kysely 0.8.0 → 0.8.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -5
- package/dist/cjs/dialect/mysql/mysql-driver.js +5 -5
- package/dist/cjs/dialect/postgres/postgres-driver.js +5 -5
- package/dist/cjs/query-builder/function-builder.js +1 -1
- package/dist/cjs/query-compiler/compiled-query.d.ts +1 -1
- package/dist/cjs/query-compiler/default-query-compiler.d.ts +2 -2
- package/dist/cjs/query-compiler/default-query-compiler.js +9 -9
- package/dist/cjs/query-creator.d.ts +8 -8
- package/dist/cjs/query-creator.js +9 -9
- package/dist/cjs/raw-builder/raw-builder.d.ts +1 -1
- package/dist/cjs/raw-builder/raw-builder.js +6 -6
- package/dist/esm/dialect/mysql/mysql-driver.js +5 -5
- package/dist/esm/dialect/postgres/postgres-driver.js +5 -5
- package/dist/esm/query-builder/function-builder.js +1 -1
- package/dist/esm/query-compiler/compiled-query.d.ts +1 -1
- package/dist/esm/query-compiler/default-query-compiler.d.ts +2 -2
- package/dist/esm/query-compiler/default-query-compiler.js +9 -9
- package/dist/esm/query-creator.d.ts +8 -8
- package/dist/esm/query-creator.js +9 -9
- package/dist/esm/raw-builder/raw-builder.d.ts +1 -1
- package/dist/esm/raw-builder/raw-builder.js +6 -6
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
# [Kysely](https://koskimas.github.io/kysely/index.html)
|
|
5
5
|
|
|
6
6
|
A type-safe and autocompletion-friendly typescript SQL query builder for node.js. Inspired by
|
|
7
|
-
[knex](http://knexjs.org/).
|
|
7
|
+
[knex](http://knexjs.org/). You can find an introduction to Kysely [here](https://www.jakso.me/blog/kysely-a-type-safe-sql-query-builder-for-typescript).
|
|
8
8
|
|
|
9
9
|

|
|
10
10
|
|
|
@@ -27,8 +27,6 @@ for more info.
|
|
|
27
27
|
Kysely is still young and some useful features are propably not yet implemented. If you start
|
|
28
28
|
using Kysely and can't find something you'd want to use, please open an issue.
|
|
29
29
|
|
|
30
|
-
[Here](https://www.jakso.me/blog/kysely-a-type-safe-sql-query-builder-for-typescript)'s an introductory blog post about Kysely.
|
|
31
|
-
|
|
32
30
|
# Table of contents
|
|
33
31
|
|
|
34
32
|
- [Installation](#installation)
|
|
@@ -114,8 +112,6 @@ async function demo() {
|
|
|
114
112
|
|
|
115
113
|
# Query examples
|
|
116
114
|
|
|
117
|
-
The [API documentation](https://koskimas.github.io/kysely/index.html) is full of examples.
|
|
118
|
-
|
|
119
115
|
## Select queries
|
|
120
116
|
|
|
121
117
|
You can find examples of select queries in the documentation of the
|
|
@@ -53,16 +53,16 @@ class MysqlDriver {
|
|
|
53
53
|
// On mysql this sets the isolation level of the next transaction.
|
|
54
54
|
await connection.executeQuery({
|
|
55
55
|
sql: `set transaction isolation level ${settings.isolationLevel}`,
|
|
56
|
-
|
|
56
|
+
parameters: [],
|
|
57
57
|
});
|
|
58
58
|
}
|
|
59
|
-
await connection.executeQuery({ sql: 'begin',
|
|
59
|
+
await connection.executeQuery({ sql: 'begin', parameters: [] });
|
|
60
60
|
}
|
|
61
61
|
async commitTransaction(connection) {
|
|
62
|
-
await connection.executeQuery({ sql: 'commit',
|
|
62
|
+
await connection.executeQuery({ sql: 'commit', parameters: [] });
|
|
63
63
|
}
|
|
64
64
|
async rollbackTransaction(connection) {
|
|
65
|
-
await connection.executeQuery({ sql: 'rollback',
|
|
65
|
+
await connection.executeQuery({ sql: 'rollback', parameters: [] });
|
|
66
66
|
}
|
|
67
67
|
async releaseConnection(connection) {
|
|
68
68
|
const mysqlConnection = connection;
|
|
@@ -131,7 +131,7 @@ class MysqlConnection {
|
|
|
131
131
|
}
|
|
132
132
|
#executeQuery(compiledQuery) {
|
|
133
133
|
return new Promise((resolve, reject) => {
|
|
134
|
-
this.#rawConnection.query(compiledQuery.sql, compiledQuery.
|
|
134
|
+
this.#rawConnection.query(compiledQuery.sql, compiledQuery.parameters, (err, result) => {
|
|
135
135
|
if (err) {
|
|
136
136
|
reject(err);
|
|
137
137
|
}
|
|
@@ -40,18 +40,18 @@ class PostgresDriver {
|
|
|
40
40
|
if (settings.isolationLevel) {
|
|
41
41
|
await connection.executeQuery({
|
|
42
42
|
sql: `start transaction isolation level ${settings.isolationLevel}`,
|
|
43
|
-
|
|
43
|
+
parameters: [],
|
|
44
44
|
});
|
|
45
45
|
}
|
|
46
46
|
else {
|
|
47
|
-
await connection.executeQuery({ sql: 'begin',
|
|
47
|
+
await connection.executeQuery({ sql: 'begin', parameters: [] });
|
|
48
48
|
}
|
|
49
49
|
}
|
|
50
50
|
async commitTransaction(connection) {
|
|
51
|
-
await connection.executeQuery({ sql: 'commit',
|
|
51
|
+
await connection.executeQuery({ sql: 'commit', parameters: [] });
|
|
52
52
|
}
|
|
53
53
|
async rollbackTransaction(connection) {
|
|
54
|
-
await connection.executeQuery({ sql: 'rollback',
|
|
54
|
+
await connection.executeQuery({ sql: 'rollback', parameters: [] });
|
|
55
55
|
}
|
|
56
56
|
async releaseConnection(connection) {
|
|
57
57
|
const pgConnection = connection;
|
|
@@ -90,7 +90,7 @@ class PostgresConnection {
|
|
|
90
90
|
}
|
|
91
91
|
async executeQuery(compiledQuery) {
|
|
92
92
|
const result = await this.#client.query(compiledQuery.sql, [
|
|
93
|
-
...compiledQuery.
|
|
93
|
+
...compiledQuery.parameters,
|
|
94
94
|
]);
|
|
95
95
|
if (result.command === 'UPDATE' || result.command === 'DELETE') {
|
|
96
96
|
return {
|
|
@@ -62,7 +62,7 @@ import { ModifyColumnNode } from '../operation-node/modify-column-node.js';
|
|
|
62
62
|
import { OnDuplicateKeyNode } from '../operation-node/on-duplicate-key-node.js';
|
|
63
63
|
export declare class DefaultQueryCompiler extends OperationNodeVisitor implements QueryCompiler {
|
|
64
64
|
#private;
|
|
65
|
-
protected get
|
|
65
|
+
protected get numParameters(): number;
|
|
66
66
|
compileQuery(node: RootOperationNode): CompiledQuery;
|
|
67
67
|
protected getSql(): string;
|
|
68
68
|
protected visitSelectQuery(node: SelectQueryNode): void;
|
|
@@ -129,6 +129,6 @@ export declare class DefaultQueryCompiler extends OperationNodeVisitor implement
|
|
|
129
129
|
protected getLeftIdentifierWrapper(): string;
|
|
130
130
|
protected getRightIdentifierWrapper(): string;
|
|
131
131
|
protected getCurrentParameterPlaceholder(): string;
|
|
132
|
-
protected
|
|
132
|
+
protected addParameter(parameter: any): void;
|
|
133
133
|
protected appendImmediateValue(value: PrimitiveValue): void;
|
|
134
134
|
}
|
|
@@ -8,17 +8,17 @@ const raw_node_js_1 = require("../operation-node/raw-node.js");
|
|
|
8
8
|
const object_utils_js_1 = require("../util/object-utils.js");
|
|
9
9
|
class DefaultQueryCompiler extends operation_node_visitor_js_1.OperationNodeVisitor {
|
|
10
10
|
#sqlFragments = [];
|
|
11
|
-
#
|
|
12
|
-
get
|
|
13
|
-
return this.#
|
|
11
|
+
#parameters = [];
|
|
12
|
+
get numParameters() {
|
|
13
|
+
return this.#parameters.length;
|
|
14
14
|
}
|
|
15
15
|
compileQuery(node) {
|
|
16
16
|
this.#sqlFragments = [];
|
|
17
|
-
this.#
|
|
17
|
+
this.#parameters = [];
|
|
18
18
|
this.visitNode(node);
|
|
19
19
|
return (0, object_utils_js_1.freeze)({
|
|
20
20
|
sql: this.getSql(),
|
|
21
|
-
|
|
21
|
+
parameters: this.#parameters,
|
|
22
22
|
});
|
|
23
23
|
}
|
|
24
24
|
getSql() {
|
|
@@ -640,7 +640,7 @@ class DefaultQueryCompiler extends operation_node_visitor_js_1.OperationNodeVisi
|
|
|
640
640
|
this.#sqlFragments.push(str);
|
|
641
641
|
}
|
|
642
642
|
appendValue(value) {
|
|
643
|
-
this.
|
|
643
|
+
this.addParameter(value);
|
|
644
644
|
this.append(this.getCurrentParameterPlaceholder());
|
|
645
645
|
}
|
|
646
646
|
getLeftIdentifierWrapper() {
|
|
@@ -650,10 +650,10 @@ class DefaultQueryCompiler extends operation_node_visitor_js_1.OperationNodeVisi
|
|
|
650
650
|
return '"';
|
|
651
651
|
}
|
|
652
652
|
getCurrentParameterPlaceholder() {
|
|
653
|
-
return '$' + this.
|
|
653
|
+
return '$' + this.numParameters;
|
|
654
654
|
}
|
|
655
|
-
|
|
656
|
-
this.#
|
|
655
|
+
addParameter(parameter) {
|
|
656
|
+
this.#parameters.push(parameter);
|
|
657
657
|
}
|
|
658
658
|
appendImmediateValue(value) {
|
|
659
659
|
if ((0, object_utils_js_1.isString)(value)) {
|
|
@@ -276,11 +276,11 @@ export declare class QueryCreator<DB> {
|
|
|
276
276
|
* Raw builder instances can be passed to pretty much anywhere: `select`, `where`,
|
|
277
277
|
* `*Join`, `groupBy`, `orderBy` etc. Just try it. If the method accepts it, it works.
|
|
278
278
|
*
|
|
279
|
-
* @param sql - The raw SQL. Special strings `?` and `??` can be used
|
|
280
|
-
*
|
|
279
|
+
* @param sql - The raw SQL. Special strings `?` and `??` can be used as parameter
|
|
280
|
+
* placeholders. `?` for values and `??` for identifiers such as column names
|
|
281
281
|
* or `column.table` references.
|
|
282
282
|
*
|
|
283
|
-
* @param params - The parameters that will be bound to the `?` and `??`
|
|
283
|
+
* @param params - The parameters that will be bound to the `?` and `??` placeholders in
|
|
284
284
|
* the sql string.
|
|
285
285
|
*
|
|
286
286
|
* @example
|
|
@@ -318,7 +318,7 @@ export declare class QueryCreator<DB> {
|
|
|
318
318
|
* the output type. In this case you need to use the `castTo` method on the query
|
|
319
319
|
* to specify a return type for the query.
|
|
320
320
|
*
|
|
321
|
-
* We could've also used `??`
|
|
321
|
+
* We could've also used a `??` placeholder to provide `first_name` and `last_name` like
|
|
322
322
|
* this:
|
|
323
323
|
*
|
|
324
324
|
* ```ts
|
|
@@ -333,9 +333,9 @@ export declare class QueryCreator<DB> {
|
|
|
333
333
|
*
|
|
334
334
|
* But it's often cleaner to just write the column names in the SQL. Again remember to
|
|
335
335
|
* never concatenate column names or any other untrusted user input to the SQL string or you
|
|
336
|
-
* are going to create an injection vulnerability. All user input should go to the
|
|
336
|
+
* are going to create an injection vulnerability. All user input should go to the parameters
|
|
337
337
|
* array, never to the SQL string directly. But if the column names or values are trusted
|
|
338
|
-
* and known at compile time, there is no reason to use
|
|
338
|
+
* and known at compile time, there is no reason to use parameters.
|
|
339
339
|
*
|
|
340
340
|
* @example
|
|
341
341
|
* Example of using `raw` in `where`:
|
|
@@ -361,7 +361,7 @@ export declare class QueryCreator<DB> {
|
|
|
361
361
|
*
|
|
362
362
|
* The function in the above example returns people that are older than the given number of
|
|
363
363
|
* years. The number of years in this example is an untrusted user input, and therefore we use
|
|
364
|
-
* a `?`
|
|
364
|
+
* a `?` placeholder for it.
|
|
365
365
|
*
|
|
366
366
|
* @example
|
|
367
367
|
* Example of creating a completely raw query from scratch:
|
|
@@ -375,7 +375,7 @@ export declare class QueryCreator<DB> {
|
|
|
375
375
|
* The result of `execute()` method is always an array. In this case the type of
|
|
376
376
|
* the `persons` variable is `Person[]`.
|
|
377
377
|
*/
|
|
378
|
-
raw<T = unknown>(sql: string,
|
|
378
|
+
raw<T = unknown>(sql: string, parameters?: any[]): RawBuilder<T>;
|
|
379
379
|
}
|
|
380
380
|
export interface QueryCreatorProps {
|
|
381
381
|
readonly executor: QueryExecutor;
|
|
@@ -218,11 +218,11 @@ class QueryCreator {
|
|
|
218
218
|
* Raw builder instances can be passed to pretty much anywhere: `select`, `where`,
|
|
219
219
|
* `*Join`, `groupBy`, `orderBy` etc. Just try it. If the method accepts it, it works.
|
|
220
220
|
*
|
|
221
|
-
* @param sql - The raw SQL. Special strings `?` and `??` can be used
|
|
222
|
-
*
|
|
221
|
+
* @param sql - The raw SQL. Special strings `?` and `??` can be used as parameter
|
|
222
|
+
* placeholders. `?` for values and `??` for identifiers such as column names
|
|
223
223
|
* or `column.table` references.
|
|
224
224
|
*
|
|
225
|
-
* @param params - The parameters that will be bound to the `?` and `??`
|
|
225
|
+
* @param params - The parameters that will be bound to the `?` and `??` placeholders in
|
|
226
226
|
* the sql string.
|
|
227
227
|
*
|
|
228
228
|
* @example
|
|
@@ -260,7 +260,7 @@ class QueryCreator {
|
|
|
260
260
|
* the output type. In this case you need to use the `castTo` method on the query
|
|
261
261
|
* to specify a return type for the query.
|
|
262
262
|
*
|
|
263
|
-
* We could've also used `??`
|
|
263
|
+
* We could've also used a `??` placeholder to provide `first_name` and `last_name` like
|
|
264
264
|
* this:
|
|
265
265
|
*
|
|
266
266
|
* ```ts
|
|
@@ -275,9 +275,9 @@ class QueryCreator {
|
|
|
275
275
|
*
|
|
276
276
|
* But it's often cleaner to just write the column names in the SQL. Again remember to
|
|
277
277
|
* never concatenate column names or any other untrusted user input to the SQL string or you
|
|
278
|
-
* are going to create an injection vulnerability. All user input should go to the
|
|
278
|
+
* are going to create an injection vulnerability. All user input should go to the parameters
|
|
279
279
|
* array, never to the SQL string directly. But if the column names or values are trusted
|
|
280
|
-
* and known at compile time, there is no reason to use
|
|
280
|
+
* and known at compile time, there is no reason to use parameters.
|
|
281
281
|
*
|
|
282
282
|
* @example
|
|
283
283
|
* Example of using `raw` in `where`:
|
|
@@ -303,7 +303,7 @@ class QueryCreator {
|
|
|
303
303
|
*
|
|
304
304
|
* The function in the above example returns people that are older than the given number of
|
|
305
305
|
* years. The number of years in this example is an untrusted user input, and therefore we use
|
|
306
|
-
* a `?`
|
|
306
|
+
* a `?` placeholder for it.
|
|
307
307
|
*
|
|
308
308
|
* @example
|
|
309
309
|
* Example of creating a completely raw query from scratch:
|
|
@@ -317,12 +317,12 @@ class QueryCreator {
|
|
|
317
317
|
* The result of `execute()` method is always an array. In this case the type of
|
|
318
318
|
* the `persons` variable is `Person[]`.
|
|
319
319
|
*/
|
|
320
|
-
raw(sql,
|
|
320
|
+
raw(sql, parameters) {
|
|
321
321
|
return new raw_builder_js_1.RawBuilder({
|
|
322
322
|
queryId: (0, query_id_js_1.createQueryId)(),
|
|
323
323
|
executor: this.#props.executor,
|
|
324
324
|
sql,
|
|
325
|
-
|
|
325
|
+
parameters,
|
|
326
326
|
});
|
|
327
327
|
}
|
|
328
328
|
}
|
|
@@ -28,24 +28,24 @@ class RawBuilder {
|
|
|
28
28
|
});
|
|
29
29
|
}
|
|
30
30
|
toOperationNode() {
|
|
31
|
-
const
|
|
31
|
+
const parameterPlaceholderRegex = /(\?\??)/g;
|
|
32
32
|
const sql = this.#props.sql;
|
|
33
|
-
const
|
|
33
|
+
const parameters = this.#props.parameters ?? [];
|
|
34
34
|
const sqlFragments = [];
|
|
35
35
|
const argNodes = [];
|
|
36
36
|
let paramIdx = 0;
|
|
37
37
|
let sqlIdx = 0;
|
|
38
38
|
let match = null;
|
|
39
|
-
while ((match =
|
|
39
|
+
while ((match = parameterPlaceholderRegex.exec(sql))) {
|
|
40
40
|
const str = match[1];
|
|
41
|
-
if (paramIdx >=
|
|
42
|
-
throw new Error(`value not provided for all
|
|
41
|
+
if (paramIdx >= parameters.length) {
|
|
42
|
+
throw new Error(`value not provided for all placeholders in string ${sql}`);
|
|
43
43
|
}
|
|
44
44
|
if (match.index > 0 && sql[match.index - 1] === '\\') {
|
|
45
45
|
continue;
|
|
46
46
|
}
|
|
47
47
|
sqlFragments.push(sql.slice(sqlIdx, match.index).replaceAll('\\?', '?'));
|
|
48
|
-
argNodes.push(parseRawArg(str,
|
|
48
|
+
argNodes.push(parseRawArg(str, parameters[paramIdx]));
|
|
49
49
|
sqlIdx = match.index + str.length;
|
|
50
50
|
++paramIdx;
|
|
51
51
|
}
|
|
@@ -50,16 +50,16 @@ export class MysqlDriver {
|
|
|
50
50
|
// On mysql this sets the isolation level of the next transaction.
|
|
51
51
|
await connection.executeQuery({
|
|
52
52
|
sql: `set transaction isolation level ${settings.isolationLevel}`,
|
|
53
|
-
|
|
53
|
+
parameters: [],
|
|
54
54
|
});
|
|
55
55
|
}
|
|
56
|
-
await connection.executeQuery({ sql: 'begin',
|
|
56
|
+
await connection.executeQuery({ sql: 'begin', parameters: [] });
|
|
57
57
|
}
|
|
58
58
|
async commitTransaction(connection) {
|
|
59
|
-
await connection.executeQuery({ sql: 'commit',
|
|
59
|
+
await connection.executeQuery({ sql: 'commit', parameters: [] });
|
|
60
60
|
}
|
|
61
61
|
async rollbackTransaction(connection) {
|
|
62
|
-
await connection.executeQuery({ sql: 'rollback',
|
|
62
|
+
await connection.executeQuery({ sql: 'rollback', parameters: [] });
|
|
63
63
|
}
|
|
64
64
|
async releaseConnection(connection) {
|
|
65
65
|
const mysqlConnection = connection;
|
|
@@ -127,7 +127,7 @@ class MysqlConnection {
|
|
|
127
127
|
}
|
|
128
128
|
#executeQuery(compiledQuery) {
|
|
129
129
|
return new Promise((resolve, reject) => {
|
|
130
|
-
this.#rawConnection.query(compiledQuery.sql, compiledQuery.
|
|
130
|
+
this.#rawConnection.query(compiledQuery.sql, compiledQuery.parameters, (err, result) => {
|
|
131
131
|
if (err) {
|
|
132
132
|
reject(err);
|
|
133
133
|
}
|
|
@@ -37,18 +37,18 @@ export class PostgresDriver {
|
|
|
37
37
|
if (settings.isolationLevel) {
|
|
38
38
|
await connection.executeQuery({
|
|
39
39
|
sql: `start transaction isolation level ${settings.isolationLevel}`,
|
|
40
|
-
|
|
40
|
+
parameters: [],
|
|
41
41
|
});
|
|
42
42
|
}
|
|
43
43
|
else {
|
|
44
|
-
await connection.executeQuery({ sql: 'begin',
|
|
44
|
+
await connection.executeQuery({ sql: 'begin', parameters: [] });
|
|
45
45
|
}
|
|
46
46
|
}
|
|
47
47
|
async commitTransaction(connection) {
|
|
48
|
-
await connection.executeQuery({ sql: 'commit',
|
|
48
|
+
await connection.executeQuery({ sql: 'commit', parameters: [] });
|
|
49
49
|
}
|
|
50
50
|
async rollbackTransaction(connection) {
|
|
51
|
-
await connection.executeQuery({ sql: 'rollback',
|
|
51
|
+
await connection.executeQuery({ sql: 'rollback', parameters: [] });
|
|
52
52
|
}
|
|
53
53
|
async releaseConnection(connection) {
|
|
54
54
|
const pgConnection = connection;
|
|
@@ -86,7 +86,7 @@ class PostgresConnection {
|
|
|
86
86
|
}
|
|
87
87
|
async executeQuery(compiledQuery) {
|
|
88
88
|
const result = await this.#client.query(compiledQuery.sql, [
|
|
89
|
-
...compiledQuery.
|
|
89
|
+
...compiledQuery.parameters,
|
|
90
90
|
]);
|
|
91
91
|
if (result.command === 'UPDATE' || result.command === 'DELETE') {
|
|
92
92
|
return {
|
|
@@ -62,7 +62,7 @@ import { ModifyColumnNode } from '../operation-node/modify-column-node.js';
|
|
|
62
62
|
import { OnDuplicateKeyNode } from '../operation-node/on-duplicate-key-node.js';
|
|
63
63
|
export declare class DefaultQueryCompiler extends OperationNodeVisitor implements QueryCompiler {
|
|
64
64
|
#private;
|
|
65
|
-
protected get
|
|
65
|
+
protected get numParameters(): number;
|
|
66
66
|
compileQuery(node: RootOperationNode): CompiledQuery;
|
|
67
67
|
protected getSql(): string;
|
|
68
68
|
protected visitSelectQuery(node: SelectQueryNode): void;
|
|
@@ -129,6 +129,6 @@ export declare class DefaultQueryCompiler extends OperationNodeVisitor implement
|
|
|
129
129
|
protected getLeftIdentifierWrapper(): string;
|
|
130
130
|
protected getRightIdentifierWrapper(): string;
|
|
131
131
|
protected getCurrentParameterPlaceholder(): string;
|
|
132
|
-
protected
|
|
132
|
+
protected addParameter(parameter: any): void;
|
|
133
133
|
protected appendImmediateValue(value: PrimitiveValue): void;
|
|
134
134
|
}
|
|
@@ -5,17 +5,17 @@ import { RawNode } from '../operation-node/raw-node.js';
|
|
|
5
5
|
import { getLast, freeze, isString, isNumber, isBoolean, isNull, isDate, isBigInt, } from '../util/object-utils.js';
|
|
6
6
|
export class DefaultQueryCompiler extends OperationNodeVisitor {
|
|
7
7
|
#sqlFragments = [];
|
|
8
|
-
#
|
|
9
|
-
get
|
|
10
|
-
return this.#
|
|
8
|
+
#parameters = [];
|
|
9
|
+
get numParameters() {
|
|
10
|
+
return this.#parameters.length;
|
|
11
11
|
}
|
|
12
12
|
compileQuery(node) {
|
|
13
13
|
this.#sqlFragments = [];
|
|
14
|
-
this.#
|
|
14
|
+
this.#parameters = [];
|
|
15
15
|
this.visitNode(node);
|
|
16
16
|
return freeze({
|
|
17
17
|
sql: this.getSql(),
|
|
18
|
-
|
|
18
|
+
parameters: this.#parameters,
|
|
19
19
|
});
|
|
20
20
|
}
|
|
21
21
|
getSql() {
|
|
@@ -637,7 +637,7 @@ export class DefaultQueryCompiler extends OperationNodeVisitor {
|
|
|
637
637
|
this.#sqlFragments.push(str);
|
|
638
638
|
}
|
|
639
639
|
appendValue(value) {
|
|
640
|
-
this.
|
|
640
|
+
this.addParameter(value);
|
|
641
641
|
this.append(this.getCurrentParameterPlaceholder());
|
|
642
642
|
}
|
|
643
643
|
getLeftIdentifierWrapper() {
|
|
@@ -647,10 +647,10 @@ export class DefaultQueryCompiler extends OperationNodeVisitor {
|
|
|
647
647
|
return '"';
|
|
648
648
|
}
|
|
649
649
|
getCurrentParameterPlaceholder() {
|
|
650
|
-
return '$' + this.
|
|
650
|
+
return '$' + this.numParameters;
|
|
651
651
|
}
|
|
652
|
-
|
|
653
|
-
this.#
|
|
652
|
+
addParameter(parameter) {
|
|
653
|
+
this.#parameters.push(parameter);
|
|
654
654
|
}
|
|
655
655
|
appendImmediateValue(value) {
|
|
656
656
|
if (isString(value)) {
|
|
@@ -276,11 +276,11 @@ export declare class QueryCreator<DB> {
|
|
|
276
276
|
* Raw builder instances can be passed to pretty much anywhere: `select`, `where`,
|
|
277
277
|
* `*Join`, `groupBy`, `orderBy` etc. Just try it. If the method accepts it, it works.
|
|
278
278
|
*
|
|
279
|
-
* @param sql - The raw SQL. Special strings `?` and `??` can be used
|
|
280
|
-
*
|
|
279
|
+
* @param sql - The raw SQL. Special strings `?` and `??` can be used as parameter
|
|
280
|
+
* placeholders. `?` for values and `??` for identifiers such as column names
|
|
281
281
|
* or `column.table` references.
|
|
282
282
|
*
|
|
283
|
-
* @param params - The parameters that will be bound to the `?` and `??`
|
|
283
|
+
* @param params - The parameters that will be bound to the `?` and `??` placeholders in
|
|
284
284
|
* the sql string.
|
|
285
285
|
*
|
|
286
286
|
* @example
|
|
@@ -318,7 +318,7 @@ export declare class QueryCreator<DB> {
|
|
|
318
318
|
* the output type. In this case you need to use the `castTo` method on the query
|
|
319
319
|
* to specify a return type for the query.
|
|
320
320
|
*
|
|
321
|
-
* We could've also used `??`
|
|
321
|
+
* We could've also used a `??` placeholder to provide `first_name` and `last_name` like
|
|
322
322
|
* this:
|
|
323
323
|
*
|
|
324
324
|
* ```ts
|
|
@@ -333,9 +333,9 @@ export declare class QueryCreator<DB> {
|
|
|
333
333
|
*
|
|
334
334
|
* But it's often cleaner to just write the column names in the SQL. Again remember to
|
|
335
335
|
* never concatenate column names or any other untrusted user input to the SQL string or you
|
|
336
|
-
* are going to create an injection vulnerability. All user input should go to the
|
|
336
|
+
* are going to create an injection vulnerability. All user input should go to the parameters
|
|
337
337
|
* array, never to the SQL string directly. But if the column names or values are trusted
|
|
338
|
-
* and known at compile time, there is no reason to use
|
|
338
|
+
* and known at compile time, there is no reason to use parameters.
|
|
339
339
|
*
|
|
340
340
|
* @example
|
|
341
341
|
* Example of using `raw` in `where`:
|
|
@@ -361,7 +361,7 @@ export declare class QueryCreator<DB> {
|
|
|
361
361
|
*
|
|
362
362
|
* The function in the above example returns people that are older than the given number of
|
|
363
363
|
* years. The number of years in this example is an untrusted user input, and therefore we use
|
|
364
|
-
* a `?`
|
|
364
|
+
* a `?` placeholder for it.
|
|
365
365
|
*
|
|
366
366
|
* @example
|
|
367
367
|
* Example of creating a completely raw query from scratch:
|
|
@@ -375,7 +375,7 @@ export declare class QueryCreator<DB> {
|
|
|
375
375
|
* The result of `execute()` method is always an array. In this case the type of
|
|
376
376
|
* the `persons` variable is `Person[]`.
|
|
377
377
|
*/
|
|
378
|
-
raw<T = unknown>(sql: string,
|
|
378
|
+
raw<T = unknown>(sql: string, parameters?: any[]): RawBuilder<T>;
|
|
379
379
|
}
|
|
380
380
|
export interface QueryCreatorProps {
|
|
381
381
|
readonly executor: QueryExecutor;
|
|
@@ -215,11 +215,11 @@ export class QueryCreator {
|
|
|
215
215
|
* Raw builder instances can be passed to pretty much anywhere: `select`, `where`,
|
|
216
216
|
* `*Join`, `groupBy`, `orderBy` etc. Just try it. If the method accepts it, it works.
|
|
217
217
|
*
|
|
218
|
-
* @param sql - The raw SQL. Special strings `?` and `??` can be used
|
|
219
|
-
*
|
|
218
|
+
* @param sql - The raw SQL. Special strings `?` and `??` can be used as parameter
|
|
219
|
+
* placeholders. `?` for values and `??` for identifiers such as column names
|
|
220
220
|
* or `column.table` references.
|
|
221
221
|
*
|
|
222
|
-
* @param params - The parameters that will be bound to the `?` and `??`
|
|
222
|
+
* @param params - The parameters that will be bound to the `?` and `??` placeholders in
|
|
223
223
|
* the sql string.
|
|
224
224
|
*
|
|
225
225
|
* @example
|
|
@@ -257,7 +257,7 @@ export class QueryCreator {
|
|
|
257
257
|
* the output type. In this case you need to use the `castTo` method on the query
|
|
258
258
|
* to specify a return type for the query.
|
|
259
259
|
*
|
|
260
|
-
* We could've also used `??`
|
|
260
|
+
* We could've also used a `??` placeholder to provide `first_name` and `last_name` like
|
|
261
261
|
* this:
|
|
262
262
|
*
|
|
263
263
|
* ```ts
|
|
@@ -272,9 +272,9 @@ export class QueryCreator {
|
|
|
272
272
|
*
|
|
273
273
|
* But it's often cleaner to just write the column names in the SQL. Again remember to
|
|
274
274
|
* never concatenate column names or any other untrusted user input to the SQL string or you
|
|
275
|
-
* are going to create an injection vulnerability. All user input should go to the
|
|
275
|
+
* are going to create an injection vulnerability. All user input should go to the parameters
|
|
276
276
|
* array, never to the SQL string directly. But if the column names or values are trusted
|
|
277
|
-
* and known at compile time, there is no reason to use
|
|
277
|
+
* and known at compile time, there is no reason to use parameters.
|
|
278
278
|
*
|
|
279
279
|
* @example
|
|
280
280
|
* Example of using `raw` in `where`:
|
|
@@ -300,7 +300,7 @@ export class QueryCreator {
|
|
|
300
300
|
*
|
|
301
301
|
* The function in the above example returns people that are older than the given number of
|
|
302
302
|
* years. The number of years in this example is an untrusted user input, and therefore we use
|
|
303
|
-
* a `?`
|
|
303
|
+
* a `?` placeholder for it.
|
|
304
304
|
*
|
|
305
305
|
* @example
|
|
306
306
|
* Example of creating a completely raw query from scratch:
|
|
@@ -314,12 +314,12 @@ export class QueryCreator {
|
|
|
314
314
|
* The result of `execute()` method is always an array. In this case the type of
|
|
315
315
|
* the `persons` variable is `Person[]`.
|
|
316
316
|
*/
|
|
317
|
-
raw(sql,
|
|
317
|
+
raw(sql, parameters) {
|
|
318
318
|
return new RawBuilder({
|
|
319
319
|
queryId: createQueryId(),
|
|
320
320
|
executor: this.#props.executor,
|
|
321
321
|
sql,
|
|
322
|
-
|
|
322
|
+
parameters,
|
|
323
323
|
});
|
|
324
324
|
}
|
|
325
325
|
}
|
|
@@ -25,24 +25,24 @@ export class RawBuilder {
|
|
|
25
25
|
});
|
|
26
26
|
}
|
|
27
27
|
toOperationNode() {
|
|
28
|
-
const
|
|
28
|
+
const parameterPlaceholderRegex = /(\?\??)/g;
|
|
29
29
|
const sql = this.#props.sql;
|
|
30
|
-
const
|
|
30
|
+
const parameters = this.#props.parameters ?? [];
|
|
31
31
|
const sqlFragments = [];
|
|
32
32
|
const argNodes = [];
|
|
33
33
|
let paramIdx = 0;
|
|
34
34
|
let sqlIdx = 0;
|
|
35
35
|
let match = null;
|
|
36
|
-
while ((match =
|
|
36
|
+
while ((match = parameterPlaceholderRegex.exec(sql))) {
|
|
37
37
|
const str = match[1];
|
|
38
|
-
if (paramIdx >=
|
|
39
|
-
throw new Error(`value not provided for all
|
|
38
|
+
if (paramIdx >= parameters.length) {
|
|
39
|
+
throw new Error(`value not provided for all placeholders in string ${sql}`);
|
|
40
40
|
}
|
|
41
41
|
if (match.index > 0 && sql[match.index - 1] === '\\') {
|
|
42
42
|
continue;
|
|
43
43
|
}
|
|
44
44
|
sqlFragments.push(sql.slice(sqlIdx, match.index).replaceAll('\\?', '?'));
|
|
45
|
-
argNodes.push(parseRawArg(str,
|
|
45
|
+
argNodes.push(parseRawArg(str, parameters[paramIdx]));
|
|
46
46
|
sqlIdx = match.index + str.length;
|
|
47
47
|
++paramIdx;
|
|
48
48
|
}
|