From 4d9d6b7373302a7bde75845d5d59cfdf03fc9009 Mon Sep 17 00:00:00 2001 From: khoaHyh Date: Wed, 24 Jan 2024 17:33:51 -0500 Subject: [PATCH 1/3] docs: add raw parameter bindings scenario --- src/guide/raw.md | 157 +++++++++++++++++++++++++++++------------------ 1 file changed, 98 insertions(+), 59 deletions(-) diff --git a/src/guide/raw.md b/src/guide/raw.md index 7b4630b7..8a0e26ab 100644 --- a/src/guide/raw.md +++ b/src/guide/raw.md @@ -1,6 +1,5 @@ # Raw - Sometimes you may need to use a raw expression in a query. Raw query object may be injected pretty much anywhere you want, and using proper bindings can ensure your values are escaped properly, preventing SQL-injection attacks. ## Raw Parameter Binding @@ -8,80 +7,116 @@ Sometimes you may need to use a raw expression in a query. Raw query object may One can parameterize sql given to `knex.raw(sql, bindings)`. Parameters can be positional named. One can also choose if parameter should be treated as value or as sql identifier e.g. in case of `'TableName.ColumnName'` reference. ```js -knex('users') - .select(knex.raw('count(*) as user_count, status')) +knex("users") + .select(knex.raw("count(*) as user_count, status")) .where(knex.raw(1)) - .orWhere(knex.raw('status <> ?', [1])) - .groupBy('status') + .orWhere(knex.raw("status <> ?", [1])) + .groupBy("status"); ``` Positional bindings `?` are interpreted as values and `??` are interpreted as identifiers. ```js -knex('users').where(knex.raw('?? = ?', ['user.name', 1])) +knex("users").where(knex.raw("?? = ?", ["user.name", 1])); ``` Named bindings such as `:name` are interpreted as values and `:name:` interpreted as identifiers. Named bindings are processed so long as the value is anything other than `undefined`. ```js -const raw = ':name: = :thisGuy or :name: = :otherGuy or :name: = :undefinedBinding' - -knex('users') - .where( - knex.raw(raw, { - name: 'users.name', - thisGuy: 'Bob', - otherGuy: 'Jay', - undefinedBinding: undefined - })) +const raw = + ":name: = :thisGuy or :name: = :otherGuy or :name: = :undefinedBinding"; + +knex("users").where( + knex.raw(raw, { + name: "users.name", + thisGuy: "Bob", + otherGuy: "Jay", + undefinedBinding: undefined, + }) +); ``` For simpler queries where one only has a single binding, `.raw` can accept said binding as its second parameter. ```js -knex('users') - .where( - knex.raw('LOWER("login") = ?', 'knex') - ) - .orWhere( - knex.raw('accesslevel = ?', 1) - ) - .orWhere( - knex.raw('updtime = ?', '01-01-2016') - ) +knex("users") + .where(knex.raw('LOWER("login") = ?', "knex")) + .orWhere(knex.raw("accesslevel = ?", 1)) + .orWhere(knex.raw("updtime = ?", "01-01-2016")); ``` Since there is no unified syntax for array bindings, instead you need to treat them as multiple values by adding `?` directly in your query. ```js -const myArray = [1,2,3] -knex.raw('select * from users where id in (' + myArray.map(_ => '?').join(',') + ')', [...myArray]); - +const myArray = [1, 2, 3]; +knex.raw( + "select * from users where id in (" + myArray.map((_) => "?").join(",") + ")", + [...myArray] +); ``` + query will become: ```sql select * from users where id in (?, ?, ?) /* with bindings [1,2,3] */ ``` +For raw queries that involve combining multiple named bindings where one of the bindings is a string array, you'll need to turn the string array in to a string let knex build a query. + +```js +const names = ["Sally", "Jay", "Foobar"]; +const bindings = { + names: knex.raw( + `'${names.join("','")}'` + ) /* generates 'Sally','Jay','Foobar' */, + age: 21, + limit: 100, +}; +``` + +Pass the bindings to your raw query: + +```js +knex.raw( + ` + select * from people + where "name" in (:names) + and "age" > :age + limit :limit +`, + bindings +); +``` + +query will become: + +```sql +select * from people +where "name" in ('Sally', 'Jay', 'Foobar') +and "age" > 21 +limit 100 +``` + To prevent replacement of `?` one can use the escape sequence `\\?`. ```js -knex.select('*') - .from('users') - .where('id', '=', 1) - .whereRaw('?? \\? ?', ['jsonColumn', 'jsonKey']) +knex + .select("*") + .from("users") + .where("id", "=", 1) + .whereRaw("?? \\? ?", ["jsonColumn", "jsonKey"]); ``` To prevent replacement of named bindings one can use the escape sequence `\\:`. ```js -knex.select('*') - .from('users') +knex + .select("*") + .from("users") .whereRaw(":property: = '\\:value' OR \\:property: = :value", { - property: 'name', - value: 'Bob' - }) + property: "name", + value: "Bob", + }); ``` ## Raw Expressions @@ -89,10 +124,11 @@ knex.select('*') Raw expressions are created by using `knex.raw(sql, [bindings])` and passing this as a value for any value in the query chain. ```js -knex('users').select(knex.raw('count(*) as user_count, status')) +knex("users") + .select(knex.raw("count(*) as user_count, status")) .where(knex.raw(1)) - .orWhere(knex.raw('status <> ?', [1])) - .groupBy('status') + .orWhere(knex.raw("status <> ?", [1])) + .groupBy("status"); ``` ## Raw Queries @@ -100,8 +136,9 @@ knex('users').select(knex.raw('count(*) as user_count, status')) The `knex.raw` may also be used to build a full query and execute it, as a standard query builder query would be executed. The benefit of this is that it uses the connection pool and provides a standard interface for the different client libraries. ```js -knex.raw('select * from users where id = ?', [1]) - .then(function(resp) { /*...*/ }); +knex.raw("select * from users where id = ?", [1]).then(function (resp) { + /*...*/ +}); ``` Note that the response will be whatever the underlying sql library would typically return on a normal query, so you may need to look at the documentation for the base library the queries are executing against to determine how to handle the response. @@ -111,25 +148,27 @@ Note that the response will be whatever the underlying sql library would typical The raw query builder also comes with a `wrap` method, which allows wrapping the query in a value: ```js -const subcolumn = knex.raw( - 'select avg(salary) from employee where dept_no = e.dept_no' - ) - .wrap('(', ') avg_sal_dept'); - -knex.select('e.lastname', 'e.salary', subcolumn) - .from('employee as e') - .whereRaw('dept_no = e.dept_no') +const subcolumn = knex + .raw("select avg(salary) from employee where dept_no = e.dept_no") + .wrap("(", ") avg_sal_dept"); + +knex + .select("e.lastname", "e.salary", subcolumn) + .from("employee as e") + .whereRaw("dept_no = e.dept_no"); ``` Note that the example above be achieved more easily using the [as](/guide/query-builder#as) method. ```js -const subcolumn = knex.avg('salary') - .from('employee') - .whereRaw('dept_no = e.dept_no') - .as('avg_sal_dept'); - -knex.select('e.lastname', 'e.salary', subcolumn) - .from('employee as e') - .whereRaw('dept_no = e.dept_no') +const subcolumn = knex + .avg("salary") + .from("employee") + .whereRaw("dept_no = e.dept_no") + .as("avg_sal_dept"); + +knex + .select("e.lastname", "e.salary", subcolumn) + .from("employee as e") + .whereRaw("dept_no = e.dept_no"); ``` From b58c94e4ad65150127e8acfac3a639daa1696c1e Mon Sep 17 00:00:00 2001 From: khoaHyh Date: Wed, 24 Jan 2024 17:56:44 -0500 Subject: [PATCH 2/3] fix: undo local formatting --- src/guide/raw.md | 125 +++++++++++++++++++++++------------------------ 1 file changed, 61 insertions(+), 64 deletions(-) diff --git a/src/guide/raw.md b/src/guide/raw.md index 8a0e26ab..dbe4617f 100644 --- a/src/guide/raw.md +++ b/src/guide/raw.md @@ -1,5 +1,6 @@ # Raw + Sometimes you may need to use a raw expression in a query. Raw query object may be injected pretty much anywhere you want, and using proper bindings can ensure your values are escaped properly, preventing SQL-injection attacks. ## Raw Parameter Binding @@ -7,64 +8,66 @@ Sometimes you may need to use a raw expression in a query. Raw query object may One can parameterize sql given to `knex.raw(sql, bindings)`. Parameters can be positional named. One can also choose if parameter should be treated as value or as sql identifier e.g. in case of `'TableName.ColumnName'` reference. ```js -knex("users") - .select(knex.raw("count(*) as user_count, status")) +knex('users') + .select(knex.raw('count(*) as user_count, status')) .where(knex.raw(1)) - .orWhere(knex.raw("status <> ?", [1])) - .groupBy("status"); + .orWhere(knex.raw('status <> ?', [1])) + .groupBy('status') ``` Positional bindings `?` are interpreted as values and `??` are interpreted as identifiers. ```js -knex("users").where(knex.raw("?? = ?", ["user.name", 1])); +knex('users').where(knex.raw('?? = ?', ['user.name', 1])) ``` Named bindings such as `:name` are interpreted as values and `:name:` interpreted as identifiers. Named bindings are processed so long as the value is anything other than `undefined`. ```js -const raw = - ":name: = :thisGuy or :name: = :otherGuy or :name: = :undefinedBinding"; - -knex("users").where( - knex.raw(raw, { - name: "users.name", - thisGuy: "Bob", - otherGuy: "Jay", - undefinedBinding: undefined, - }) -); +const raw = ':name: = :thisGuy or :name: = :otherGuy or :name: = :undefinedBinding' + +knex('users') + .where( + knex.raw(raw, { + name: 'users.name', + thisGuy: 'Bob', + otherGuy: 'Jay', + undefinedBinding: undefined + })) ``` For simpler queries where one only has a single binding, `.raw` can accept said binding as its second parameter. ```js -knex("users") - .where(knex.raw('LOWER("login") = ?', "knex")) - .orWhere(knex.raw("accesslevel = ?", 1)) - .orWhere(knex.raw("updtime = ?", "01-01-2016")); +knex('users') + .where( + knex.raw('LOWER("login") = ?', 'knex') + ) + .orWhere( + knex.raw('accesslevel = ?', 1) + ) + .orWhere( + knex.raw('updtime = ?', '01-01-2016') + ) ``` Since there is no unified syntax for array bindings, instead you need to treat them as multiple values by adding `?` directly in your query. ```js -const myArray = [1, 2, 3]; -knex.raw( - "select * from users where id in (" + myArray.map((_) => "?").join(",") + ")", - [...myArray] -); -``` +const myArray = [1,2,3] +knex.raw('select * from users where id in (' + myArray.map(_ => '?').join(',') + ')', [...myArray]); +``` query will become: ```sql select * from users where id in (?, ?, ?) /* with bindings [1,2,3] */ ``` -For raw queries that involve combining multiple named bindings where one of the bindings is a string array, you'll need to turn the string array in to a string let knex build a query. +For raw queries that involve combining multiple named bindings where one of the bindings is a string array, you'll need to turn the string array in to a string and let knex build a query. ```js -const names = ["Sally", "Jay", "Foobar"]; +const names = ['Sally', 'Jay', 'Foobar']; const bindings = { names: knex.raw( `'${names.join("','")}'` @@ -100,23 +103,21 @@ limit 100 To prevent replacement of `?` one can use the escape sequence `\\?`. ```js -knex - .select("*") - .from("users") - .where("id", "=", 1) - .whereRaw("?? \\? ?", ["jsonColumn", "jsonKey"]); +knex.select('*') + .from('users') + .where('id', '=', 1) + .whereRaw('?? \\? ?', ['jsonColumn', 'jsonKey']) ``` To prevent replacement of named bindings one can use the escape sequence `\\:`. ```js -knex - .select("*") - .from("users") +knex.select('*') + .from('users') .whereRaw(":property: = '\\:value' OR \\:property: = :value", { - property: "name", - value: "Bob", - }); + property: 'name', + value: 'Bob' + }) ``` ## Raw Expressions @@ -124,11 +125,10 @@ knex Raw expressions are created by using `knex.raw(sql, [bindings])` and passing this as a value for any value in the query chain. ```js -knex("users") - .select(knex.raw("count(*) as user_count, status")) +knex('users').select(knex.raw('count(*) as user_count, status')) .where(knex.raw(1)) - .orWhere(knex.raw("status <> ?", [1])) - .groupBy("status"); + .orWhere(knex.raw('status <> ?', [1])) + .groupBy('status') ``` ## Raw Queries @@ -136,9 +136,8 @@ knex("users") The `knex.raw` may also be used to build a full query and execute it, as a standard query builder query would be executed. The benefit of this is that it uses the connection pool and provides a standard interface for the different client libraries. ```js -knex.raw("select * from users where id = ?", [1]).then(function (resp) { - /*...*/ -}); +knex.raw('select * from users where id = ?', [1]) + .then(function(resp) { /*...*/ }); ``` Note that the response will be whatever the underlying sql library would typically return on a normal query, so you may need to look at the documentation for the base library the queries are executing against to determine how to handle the response. @@ -148,27 +147,25 @@ Note that the response will be whatever the underlying sql library would typical The raw query builder also comes with a `wrap` method, which allows wrapping the query in a value: ```js -const subcolumn = knex - .raw("select avg(salary) from employee where dept_no = e.dept_no") - .wrap("(", ") avg_sal_dept"); - -knex - .select("e.lastname", "e.salary", subcolumn) - .from("employee as e") - .whereRaw("dept_no = e.dept_no"); +const subcolumn = knex.raw( + 'select avg(salary) from employee where dept_no = e.dept_no' + ) + .wrap('(', ') avg_sal_dept'); + +knex.select('e.lastname', 'e.salary', subcolumn) + .from('employee as e') + .whereRaw('dept_no = e.dept_no') ``` Note that the example above be achieved more easily using the [as](/guide/query-builder#as) method. ```js -const subcolumn = knex - .avg("salary") - .from("employee") - .whereRaw("dept_no = e.dept_no") - .as("avg_sal_dept"); - -knex - .select("e.lastname", "e.salary", subcolumn) - .from("employee as e") - .whereRaw("dept_no = e.dept_no"); +const subcolumn = knex.avg('salary') + .from('employee') + .whereRaw('dept_no = e.dept_no') + .as('avg_sal_dept'); + +knex.select('e.lastname', 'e.salary', subcolumn) + .from('employee as e') + .whereRaw('dept_no = e.dept_no') ``` From 477577c65c975854b834a466266ceec25da85a22 Mon Sep 17 00:00:00 2001 From: khoaHyh Date: Thu, 25 Jan 2024 11:43:20 -0500 Subject: [PATCH 3/3] add ANY example --- src/guide/raw.md | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/src/guide/raw.md b/src/guide/raw.md index dbe4617f..b67b7195 100644 --- a/src/guide/raw.md +++ b/src/guide/raw.md @@ -100,6 +100,34 @@ and "age" > 21 limit 100 ``` +You can also use `ANY`, which in many cases is equivalent to `WHERE IN`. +```js +const names = ['Sally', 'Jay', 'Foobar']; +const bindings = { + names, + age: 21, + limit: 100, +}; +knex.raw( + ` + select * from people + where "name" = any(:names) + and "age" > :age + limit :limit +`, + bindings +); +``` + +This evaluates to: + +```sql +select * from people +where "name" = any('{"Sally", "Jay", "Foobar"}') +and "age" > 21 +limit 100 +``` + To prevent replacement of `?` one can use the escape sequence `\\?`. ```js