knex 2.4.2 → 2.5.0

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.
Files changed (190) hide show
  1. package/CHANGELOG.md +54 -18
  2. package/CONTRIBUTING.md +194 -194
  3. package/LICENSE +22 -22
  4. package/README.md +147 -148
  5. package/UPGRADING.md +233 -233
  6. package/bin/cli.js +473 -473
  7. package/bin/utils/cli-config-utils.js +210 -210
  8. package/bin/utils/constants.js +7 -7
  9. package/bin/utils/migrationsLister.js +37 -37
  10. package/knex.js +23 -23
  11. package/knex.mjs +11 -0
  12. package/lib/builder-interface-augmenter.js +120 -120
  13. package/lib/client.js +495 -475
  14. package/lib/constants.js +61 -61
  15. package/lib/dialects/better-sqlite3/index.js +77 -72
  16. package/lib/dialects/cockroachdb/crdb-columncompiler.js +14 -14
  17. package/lib/dialects/cockroachdb/crdb-querybuilder.js +11 -11
  18. package/lib/dialects/cockroachdb/crdb-querycompiler.js +122 -122
  19. package/lib/dialects/cockroachdb/crdb-tablecompiler.js +37 -37
  20. package/lib/dialects/cockroachdb/crdb-viewcompiler.js +15 -15
  21. package/lib/dialects/cockroachdb/index.js +86 -86
  22. package/lib/dialects/index.js +33 -33
  23. package/lib/dialects/mssql/index.js +500 -495
  24. package/lib/dialects/mssql/mssql-formatter.js +34 -34
  25. package/lib/dialects/mssql/query/mssql-querycompiler.js +601 -600
  26. package/lib/dialects/mssql/schema/mssql-columncompiler.js +185 -185
  27. package/lib/dialects/mssql/schema/mssql-compiler.js +91 -91
  28. package/lib/dialects/mssql/schema/mssql-tablecompiler.js +378 -378
  29. package/lib/dialects/mssql/schema/mssql-viewcompiler.js +55 -55
  30. package/lib/dialects/mssql/transaction.js +176 -176
  31. package/lib/dialects/mysql/index.js +201 -201
  32. package/lib/dialects/mysql/query/mysql-querycompiler.js +274 -274
  33. package/lib/dialects/mysql/schema/mysql-columncompiler.js +193 -193
  34. package/lib/dialects/mysql/schema/mysql-compiler.js +60 -60
  35. package/lib/dialects/mysql/schema/mysql-tablecompiler.js +381 -381
  36. package/lib/dialects/mysql/schema/mysql-viewbuilder.js +21 -21
  37. package/lib/dialects/mysql/schema/mysql-viewcompiler.js +15 -15
  38. package/lib/dialects/mysql/transaction.js +46 -46
  39. package/lib/dialects/mysql2/index.js +53 -33
  40. package/lib/dialects/mysql2/transaction.js +44 -44
  41. package/lib/dialects/oracle/DEAD_CODE.md +5 -5
  42. package/lib/dialects/oracle/index.js +92 -92
  43. package/lib/dialects/oracle/query/oracle-querycompiler.js +343 -342
  44. package/lib/dialects/oracle/schema/internal/incrementUtils.js +20 -20
  45. package/lib/dialects/oracle/schema/internal/trigger.js +135 -135
  46. package/lib/dialects/oracle/schema/oracle-columnbuilder.js +17 -17
  47. package/lib/dialects/oracle/schema/oracle-columncompiler.js +126 -126
  48. package/lib/dialects/oracle/schema/oracle-compiler.js +122 -122
  49. package/lib/dialects/oracle/schema/oracle-tablecompiler.js +190 -190
  50. package/lib/dialects/oracle/utils.js +87 -87
  51. package/lib/dialects/oracledb/index.js +327 -327
  52. package/lib/dialects/oracledb/query/oracledb-querycompiler.js +481 -481
  53. package/lib/dialects/oracledb/schema/oracledb-columncompiler.js +61 -55
  54. package/lib/dialects/oracledb/schema/oracledb-tablecompiler.js +19 -19
  55. package/lib/dialects/oracledb/schema/oracledb-viewbuilder.js +13 -13
  56. package/lib/dialects/oracledb/schema/oracledb-viewcompiler.js +19 -19
  57. package/lib/dialects/oracledb/transaction.js +98 -98
  58. package/lib/dialects/oracledb/utils.js +208 -208
  59. package/lib/dialects/pgnative/index.js +60 -60
  60. package/lib/dialects/postgres/execution/pg-transaction.js +19 -12
  61. package/lib/dialects/postgres/index.js +358 -358
  62. package/lib/dialects/postgres/query/pg-querybuilder.js +43 -38
  63. package/lib/dialects/postgres/query/pg-querycompiler.js +400 -395
  64. package/lib/dialects/postgres/schema/pg-columncompiler.js +156 -156
  65. package/lib/dialects/postgres/schema/pg-compiler.js +138 -138
  66. package/lib/dialects/postgres/schema/pg-tablecompiler.js +304 -299
  67. package/lib/dialects/postgres/schema/pg-viewbuilder.js +21 -21
  68. package/lib/dialects/postgres/schema/pg-viewcompiler.js +35 -35
  69. package/lib/dialects/redshift/index.js +86 -86
  70. package/lib/dialects/redshift/query/redshift-querycompiler.js +163 -163
  71. package/lib/dialects/redshift/schema/redshift-columnbuilder.js +22 -22
  72. package/lib/dialects/redshift/schema/redshift-columncompiler.js +67 -67
  73. package/lib/dialects/redshift/schema/redshift-compiler.js +14 -14
  74. package/lib/dialects/redshift/schema/redshift-tablecompiler.js +122 -122
  75. package/lib/dialects/redshift/schema/redshift-viewcompiler.js +11 -11
  76. package/lib/dialects/redshift/transaction.js +32 -25
  77. package/lib/dialects/sqlite3/execution/sqlite-transaction.js +25 -18
  78. package/lib/dialects/sqlite3/index.js +250 -250
  79. package/lib/dialects/sqlite3/query/sqlite-querybuilder.js +33 -33
  80. package/lib/dialects/sqlite3/query/sqlite-querycompiler.js +334 -334
  81. package/lib/dialects/sqlite3/schema/ddl.js +400 -400
  82. package/lib/dialects/sqlite3/schema/internal/compiler.js +327 -327
  83. package/lib/dialects/sqlite3/schema/internal/parser-combinator.js +161 -161
  84. package/lib/dialects/sqlite3/schema/internal/parser.js +638 -638
  85. package/lib/dialects/sqlite3/schema/internal/sqlite-ddl-operations.js +41 -41
  86. package/lib/dialects/sqlite3/schema/internal/tokenizer.js +38 -38
  87. package/lib/dialects/sqlite3/schema/internal/utils.js +12 -12
  88. package/lib/dialects/sqlite3/schema/sqlite-columncompiler.js +50 -50
  89. package/lib/dialects/sqlite3/schema/sqlite-compiler.js +80 -80
  90. package/lib/dialects/sqlite3/schema/sqlite-tablecompiler.js +347 -347
  91. package/lib/dialects/sqlite3/schema/sqlite-viewcompiler.js +40 -40
  92. package/lib/execution/batch-insert.js +51 -51
  93. package/lib/execution/internal/delay.js +6 -6
  94. package/lib/execution/internal/ensure-connection-callback.js +41 -41
  95. package/lib/execution/internal/query-executioner.js +62 -62
  96. package/lib/execution/runner.js +325 -307
  97. package/lib/execution/transaction.js +409 -401
  98. package/lib/formatter/formatterUtils.js +42 -42
  99. package/lib/formatter/rawFormatter.js +84 -84
  100. package/lib/formatter/wrappingFormatter.js +250 -250
  101. package/lib/formatter.js +25 -25
  102. package/lib/index.js +3 -3
  103. package/lib/knex-builder/FunctionHelper.js +80 -54
  104. package/lib/knex-builder/Knex.js +59 -59
  105. package/lib/knex-builder/internal/config-resolver.js +57 -57
  106. package/lib/knex-builder/internal/parse-connection.js +87 -87
  107. package/lib/knex-builder/make-knex.js +345 -340
  108. package/lib/logger.js +76 -76
  109. package/lib/migrations/common/MigrationsLoader.js +36 -36
  110. package/lib/migrations/migrate/MigrationGenerator.js +84 -84
  111. package/lib/migrations/migrate/Migrator.js +598 -598
  112. package/lib/migrations/migrate/migrate-stub.js +17 -17
  113. package/lib/migrations/migrate/migration-list-resolver.js +33 -33
  114. package/lib/migrations/migrate/migrator-configuration-merger.js +58 -58
  115. package/lib/migrations/migrate/sources/fs-migrations.js +74 -74
  116. package/lib/migrations/migrate/stub/cjs.stub +15 -15
  117. package/lib/migrations/migrate/stub/coffee.stub +13 -13
  118. package/lib/migrations/migrate/stub/eg.stub +14 -14
  119. package/lib/migrations/migrate/stub/js-schema.stub +22 -22
  120. package/lib/migrations/migrate/stub/js.stub +22 -22
  121. package/lib/migrations/migrate/stub/knexfile-coffee.stub +34 -34
  122. package/lib/migrations/migrate/stub/knexfile-eg.stub +43 -43
  123. package/lib/migrations/migrate/stub/knexfile-js.stub +47 -47
  124. package/lib/migrations/migrate/stub/knexfile-ls.stub +35 -35
  125. package/lib/migrations/migrate/stub/knexfile-ts.stub +47 -47
  126. package/lib/migrations/migrate/stub/ls.stub +14 -14
  127. package/lib/migrations/migrate/stub/mjs.stub +23 -23
  128. package/lib/migrations/migrate/stub/ts-schema.stub +21 -21
  129. package/lib/migrations/migrate/stub/ts.stub +21 -21
  130. package/lib/migrations/migrate/table-creator.js +77 -77
  131. package/lib/migrations/migrate/table-resolver.js +27 -27
  132. package/lib/migrations/seed/Seeder.js +137 -137
  133. package/lib/migrations/seed/seed-stub.js +13 -13
  134. package/lib/migrations/seed/seeder-configuration-merger.js +60 -60
  135. package/lib/migrations/seed/sources/fs-seeds.js +65 -65
  136. package/lib/migrations/seed/stub/coffee.stub +9 -9
  137. package/lib/migrations/seed/stub/eg.stub +11 -11
  138. package/lib/migrations/seed/stub/js.stub +13 -13
  139. package/lib/migrations/seed/stub/ls.stub +11 -11
  140. package/lib/migrations/seed/stub/mjs.stub +12 -12
  141. package/lib/migrations/seed/stub/ts.stub +13 -13
  142. package/lib/migrations/util/fs.js +86 -86
  143. package/lib/migrations/util/import-file.js +12 -12
  144. package/lib/migrations/util/is-module-type.js +9 -9
  145. package/lib/migrations/util/template.js +52 -52
  146. package/lib/migrations/util/timestamp.js +14 -14
  147. package/lib/query/analytic.js +52 -52
  148. package/lib/query/constants.js +15 -15
  149. package/lib/query/joinclause.js +270 -270
  150. package/lib/query/method-constants.js +136 -135
  151. package/lib/query/querybuilder.js +1793 -1794
  152. package/lib/query/querycompiler.js +1591 -1580
  153. package/lib/raw.js +139 -139
  154. package/lib/ref.js +39 -39
  155. package/lib/schema/builder.js +115 -115
  156. package/lib/schema/columnbuilder.js +146 -146
  157. package/lib/schema/columncompiler.js +307 -307
  158. package/lib/schema/compiler.js +187 -187
  159. package/lib/schema/internal/helpers.js +55 -55
  160. package/lib/schema/tablebuilder.js +376 -376
  161. package/lib/schema/tablecompiler.js +433 -433
  162. package/lib/schema/viewbuilder.js +92 -92
  163. package/lib/schema/viewcompiler.js +138 -138
  164. package/lib/util/finally-mixin.js +13 -13
  165. package/lib/util/helpers.js +95 -95
  166. package/lib/util/is.js +32 -32
  167. package/lib/util/nanoid.js +40 -40
  168. package/lib/util/noop.js +1 -1
  169. package/lib/util/save-async-stack.js +14 -14
  170. package/lib/util/security.js +26 -0
  171. package/lib/util/string.js +190 -190
  172. package/lib/util/timeout.js +29 -29
  173. package/package.json +12 -10
  174. package/scripts/build.js +125 -125
  175. package/scripts/clean.js +31 -31
  176. package/scripts/docker-compose.yml +152 -152
  177. package/scripts/next-release-howto.md +24 -24
  178. package/scripts/oracledb-install-driver-libs.sh +82 -82
  179. package/scripts/release.sh +34 -34
  180. package/scripts/runkit-example.js +34 -34
  181. package/scripts/stress-test/README.txt +18 -18
  182. package/scripts/stress-test/docker-compose.yml +57 -57
  183. package/scripts/stress-test/knex-stress-test.js +208 -208
  184. package/scripts/stress-test/mysql2-random-hanging-every-now-and-then.js +145 -145
  185. package/scripts/stress-test/mysql2-sudden-exit-without-error.js +100 -100
  186. package/scripts/stress-test/reconnect-test-mysql-based-drivers.js +184 -184
  187. package/scripts/update_gitignore_for_tsc_output.js +90 -90
  188. package/types/index.d.ts +3273 -3233
  189. package/types/result.d.ts +27 -27
  190. package/types/tables.d.ts +4 -4
@@ -1,358 +1,358 @@
1
- // PostgreSQL
2
- // -------
3
- const extend = require('lodash/extend');
4
- const map = require('lodash/map');
5
- const { promisify } = require('util');
6
- const Client = require('../../client');
7
-
8
- const Transaction = require('./execution/pg-transaction');
9
- const QueryCompiler = require('./query/pg-querycompiler');
10
- const QueryBuilder = require('./query/pg-querybuilder');
11
- const ColumnCompiler = require('./schema/pg-columncompiler');
12
- const TableCompiler = require('./schema/pg-tablecompiler');
13
- const ViewCompiler = require('./schema/pg-viewcompiler');
14
- const ViewBuilder = require('./schema/pg-viewbuilder');
15
- const SchemaCompiler = require('./schema/pg-compiler');
16
- const { makeEscape } = require('../../util/string');
17
- const { isString } = require('../../util/is');
18
-
19
- class Client_PG extends Client {
20
- constructor(config) {
21
- super(config);
22
- if (config.returning) {
23
- this.defaultReturning = config.returning;
24
- }
25
-
26
- if (config.searchPath) {
27
- this.searchPath = config.searchPath;
28
- }
29
- }
30
- transaction() {
31
- return new Transaction(this, ...arguments);
32
- }
33
-
34
- queryBuilder() {
35
- return new QueryBuilder(this);
36
- }
37
-
38
- queryCompiler(builder, formatter) {
39
- return new QueryCompiler(this, builder, formatter);
40
- }
41
-
42
- columnCompiler() {
43
- return new ColumnCompiler(this, ...arguments);
44
- }
45
-
46
- schemaCompiler() {
47
- return new SchemaCompiler(this, ...arguments);
48
- }
49
-
50
- tableCompiler() {
51
- return new TableCompiler(this, ...arguments);
52
- }
53
-
54
- viewCompiler() {
55
- return new ViewCompiler(this, ...arguments);
56
- }
57
-
58
- viewBuilder() {
59
- return new ViewBuilder(this, ...arguments);
60
- }
61
-
62
- _driver() {
63
- return require('pg');
64
- }
65
-
66
- wrapIdentifierImpl(value) {
67
- if (value === '*') return value;
68
-
69
- let arrayAccessor = '';
70
- const arrayAccessorMatch = value.match(/(.*?)(\[[0-9]+\])/);
71
-
72
- if (arrayAccessorMatch) {
73
- value = arrayAccessorMatch[1];
74
- arrayAccessor = arrayAccessorMatch[2];
75
- }
76
-
77
- return `"${value.replace(/"/g, '""')}"${arrayAccessor}`;
78
- }
79
-
80
- _acquireOnlyConnection() {
81
- const connection = new this.driver.Client(this.connectionSettings);
82
-
83
- return connection.connect().then(() => connection);
84
- }
85
-
86
- // Get a raw connection, called by the `pool` whenever a new
87
- // connection needs to be added to the pool.
88
- acquireRawConnection() {
89
- const client = this;
90
-
91
- return this._acquireOnlyConnection()
92
- .then(function (connection) {
93
- connection.on('error', (err) => {
94
- connection.__knex__disposed = err;
95
- });
96
-
97
- connection.on('end', (err) => {
98
- connection.__knex__disposed = err || 'Connection ended unexpectedly';
99
- });
100
-
101
- if (!client.version) {
102
- return client.checkVersion(connection).then(function (version) {
103
- client.version = version;
104
- return connection;
105
- });
106
- }
107
-
108
- return connection;
109
- })
110
- .then(async function setSearchPath(connection) {
111
- await client.setSchemaSearchPath(connection);
112
- return connection;
113
- });
114
- }
115
-
116
- // Used to explicitly close a connection, called internally by the pool
117
- // when a connection times out or the pool is shutdown.
118
- async destroyRawConnection(connection) {
119
- const end = promisify((cb) => connection.end(cb));
120
- return end();
121
- }
122
-
123
- // In PostgreSQL, we need to do a version check to do some feature
124
- // checking on the database.
125
- checkVersion(connection) {
126
- return new Promise((resolve, reject) => {
127
- connection.query('select version();', (err, resp) => {
128
- if (err) return reject(err);
129
- resolve(this._parseVersion(resp.rows[0].version));
130
- });
131
- });
132
- }
133
-
134
- _parseVersion(versionString) {
135
- return /^PostgreSQL (.*?)( |$)/.exec(versionString)[1];
136
- }
137
-
138
- // Position the bindings for the query. The escape sequence for question mark
139
- // is \? (e.g. knex.raw("\\?") since javascript requires '\' to be escaped too...)
140
- positionBindings(sql) {
141
- let questionCount = 0;
142
- return sql.replace(/(\\*)(\?)/g, function (match, escapes) {
143
- if (escapes.length % 2) {
144
- return '?';
145
- } else {
146
- questionCount++;
147
- return `$${questionCount}`;
148
- }
149
- });
150
- }
151
-
152
- setSchemaSearchPath(connection, searchPath) {
153
- let path = searchPath || this.searchPath;
154
-
155
- if (!path) return Promise.resolve(true);
156
-
157
- if (!Array.isArray(path) && !isString(path)) {
158
- throw new TypeError(
159
- `knex: Expected searchPath to be Array/String, got: ${typeof path}`
160
- );
161
- }
162
-
163
- if (isString(path)) {
164
- if (path.includes(',')) {
165
- const parts = path.split(',');
166
- const arraySyntax = `[${parts
167
- .map((searchPath) => `'${searchPath}'`)
168
- .join(', ')}]`;
169
- this.logger.warn(
170
- `Detected comma in searchPath "${path}".` +
171
- `If you are trying to specify multiple schemas, use Array syntax: ${arraySyntax}`
172
- );
173
- }
174
- path = [path];
175
- }
176
-
177
- path = path.map((schemaName) => `"${schemaName}"`).join(',');
178
-
179
- return new Promise(function (resolver, rejecter) {
180
- connection.query(`set search_path to ${path}`, function (err) {
181
- if (err) return rejecter(err);
182
- resolver(true);
183
- });
184
- });
185
- }
186
-
187
- _stream(connection, obj, stream, options) {
188
- if (!obj.sql) throw new Error('The query is empty');
189
-
190
- const PGQueryStream = process.browser
191
- ? undefined
192
- : require('pg-query-stream');
193
- const sql = obj.sql;
194
-
195
- return new Promise(function (resolver, rejecter) {
196
- const queryStream = connection.query(
197
- new PGQueryStream(sql, obj.bindings, options)
198
- );
199
-
200
- queryStream.on('error', function (error) {
201
- rejecter(error);
202
- stream.emit('error', error);
203
- });
204
-
205
- // 'end' IS propagated by .pipe, by default
206
- stream.on('end', resolver);
207
- queryStream.pipe(stream);
208
- });
209
- }
210
-
211
- // Runs the query on the specified connection, providing the bindings
212
- // and any other necessary prep work.
213
- _query(connection, obj) {
214
- if (!obj.sql) throw new Error('The query is empty');
215
-
216
- let queryConfig = {
217
- text: obj.sql,
218
- values: obj.bindings || [],
219
- };
220
-
221
- if (obj.options) {
222
- queryConfig = extend(queryConfig, obj.options);
223
- }
224
-
225
- return new Promise(function (resolver, rejecter) {
226
- connection.query(queryConfig, function (err, response) {
227
- if (err) return rejecter(err);
228
- obj.response = response;
229
- resolver(obj);
230
- });
231
- });
232
- }
233
-
234
- // Ensures the response is returned in the same format as other clients.
235
- processResponse(obj, runner) {
236
- const resp = obj.response;
237
- if (obj.output) return obj.output.call(runner, resp);
238
- if (obj.method === 'raw') return resp;
239
- const { returning } = obj;
240
- if (resp.command === 'SELECT') {
241
- if (obj.method === 'first') return resp.rows[0];
242
- if (obj.method === 'pluck') return map(resp.rows, obj.pluck);
243
- return resp.rows;
244
- }
245
- if (returning) {
246
- const returns = [];
247
- for (let i = 0, l = resp.rows.length; i < l; i++) {
248
- const row = resp.rows[i];
249
- returns[i] = row;
250
- }
251
- return returns;
252
- }
253
- if (resp.command === 'UPDATE' || resp.command === 'DELETE') {
254
- return resp.rowCount;
255
- }
256
- return resp;
257
- }
258
-
259
- async cancelQuery(connectionToKill) {
260
- const conn = await this.acquireRawConnection();
261
-
262
- try {
263
- return await this._wrappedCancelQueryCall(conn, connectionToKill);
264
- } finally {
265
- await this.destroyRawConnection(conn).catch((err) => {
266
- this.logger.warn(`Connection Error: ${err}`);
267
- });
268
- }
269
- }
270
- _wrappedCancelQueryCall(conn, connectionToKill) {
271
- return this._query(conn, {
272
- sql: 'SELECT pg_cancel_backend($1);',
273
- bindings: [connectionToKill.processID],
274
- options: {},
275
- });
276
- }
277
-
278
- toPathForJson(jsonPath) {
279
- const PG_PATH_REGEX = /^{.*}$/;
280
- if (jsonPath.match(PG_PATH_REGEX)) {
281
- return jsonPath;
282
- }
283
- return (
284
- '{' +
285
- jsonPath
286
- .replace(/^(\$\.)/, '') // remove the first dollar
287
- .replace('.', ',')
288
- .replace(/\[([0-9]+)]/, ',$1') + // transform [number] to ,number
289
- '}'
290
- );
291
- }
292
- }
293
-
294
- Object.assign(Client_PG.prototype, {
295
- dialect: 'postgresql',
296
-
297
- driverName: 'pg',
298
- canCancelQuery: true,
299
-
300
- _escapeBinding: makeEscape({
301
- escapeArray(val, esc) {
302
- return esc(arrayString(val, esc));
303
- },
304
- escapeString(str) {
305
- let hasBackslash = false;
306
- let escaped = "'";
307
- for (let i = 0; i < str.length; i++) {
308
- const c = str[i];
309
- if (c === "'") {
310
- escaped += c + c;
311
- } else if (c === '\\') {
312
- escaped += c + c;
313
- hasBackslash = true;
314
- } else {
315
- escaped += c;
316
- }
317
- }
318
- escaped += "'";
319
- if (hasBackslash === true) {
320
- escaped = 'E' + escaped;
321
- }
322
- return escaped;
323
- },
324
- escapeObject(val, prepareValue, timezone, seen = []) {
325
- if (val && typeof val.toPostgres === 'function') {
326
- seen = seen || [];
327
- if (seen.indexOf(val) !== -1) {
328
- throw new Error(
329
- `circular reference detected while preparing "${val}" for query`
330
- );
331
- }
332
- seen.push(val);
333
- return prepareValue(val.toPostgres(prepareValue), seen);
334
- }
335
- return JSON.stringify(val);
336
- },
337
- }),
338
- });
339
-
340
- function arrayString(arr, esc) {
341
- let result = '{';
342
- for (let i = 0; i < arr.length; i++) {
343
- if (i > 0) result += ',';
344
- const val = arr[i];
345
- if (val === null || typeof val === 'undefined') {
346
- result += 'NULL';
347
- } else if (Array.isArray(val)) {
348
- result += arrayString(val, esc);
349
- } else if (typeof val === 'number') {
350
- result += val;
351
- } else {
352
- result += JSON.stringify(typeof val === 'string' ? val : esc(val));
353
- }
354
- }
355
- return result + '}';
356
- }
357
-
358
- module.exports = Client_PG;
1
+ // PostgreSQL
2
+ // -------
3
+ const extend = require('lodash/extend');
4
+ const map = require('lodash/map');
5
+ const { promisify } = require('util');
6
+ const Client = require('../../client');
7
+
8
+ const Transaction = require('./execution/pg-transaction');
9
+ const QueryCompiler = require('./query/pg-querycompiler');
10
+ const QueryBuilder = require('./query/pg-querybuilder');
11
+ const ColumnCompiler = require('./schema/pg-columncompiler');
12
+ const TableCompiler = require('./schema/pg-tablecompiler');
13
+ const ViewCompiler = require('./schema/pg-viewcompiler');
14
+ const ViewBuilder = require('./schema/pg-viewbuilder');
15
+ const SchemaCompiler = require('./schema/pg-compiler');
16
+ const { makeEscape } = require('../../util/string');
17
+ const { isString } = require('../../util/is');
18
+
19
+ class Client_PG extends Client {
20
+ constructor(config) {
21
+ super(config);
22
+ if (config.returning) {
23
+ this.defaultReturning = config.returning;
24
+ }
25
+
26
+ if (config.searchPath) {
27
+ this.searchPath = config.searchPath;
28
+ }
29
+ }
30
+ transaction() {
31
+ return new Transaction(this, ...arguments);
32
+ }
33
+
34
+ queryBuilder() {
35
+ return new QueryBuilder(this);
36
+ }
37
+
38
+ queryCompiler(builder, formatter) {
39
+ return new QueryCompiler(this, builder, formatter);
40
+ }
41
+
42
+ columnCompiler() {
43
+ return new ColumnCompiler(this, ...arguments);
44
+ }
45
+
46
+ schemaCompiler() {
47
+ return new SchemaCompiler(this, ...arguments);
48
+ }
49
+
50
+ tableCompiler() {
51
+ return new TableCompiler(this, ...arguments);
52
+ }
53
+
54
+ viewCompiler() {
55
+ return new ViewCompiler(this, ...arguments);
56
+ }
57
+
58
+ viewBuilder() {
59
+ return new ViewBuilder(this, ...arguments);
60
+ }
61
+
62
+ _driver() {
63
+ return require('pg');
64
+ }
65
+
66
+ wrapIdentifierImpl(value) {
67
+ if (value === '*') return value;
68
+
69
+ let arrayAccessor = '';
70
+ const arrayAccessorMatch = value.match(/(.*?)(\[[0-9]+\])/);
71
+
72
+ if (arrayAccessorMatch) {
73
+ value = arrayAccessorMatch[1];
74
+ arrayAccessor = arrayAccessorMatch[2];
75
+ }
76
+
77
+ return `"${value.replace(/"/g, '""')}"${arrayAccessor}`;
78
+ }
79
+
80
+ _acquireOnlyConnection() {
81
+ const connection = new this.driver.Client(this.connectionSettings);
82
+
83
+ connection.on('error', (err) => {
84
+ connection.__knex__disposed = err;
85
+ });
86
+
87
+ connection.on('end', (err) => {
88
+ connection.__knex__disposed = err || 'Connection ended unexpectedly';
89
+ });
90
+
91
+ return connection.connect().then(() => connection);
92
+ }
93
+
94
+ // Get a raw connection, called by the `pool` whenever a new
95
+ // connection needs to be added to the pool.
96
+ acquireRawConnection() {
97
+ const client = this;
98
+
99
+ return this._acquireOnlyConnection()
100
+ .then(function (connection) {
101
+ if (!client.version) {
102
+ return client.checkVersion(connection).then(function (version) {
103
+ client.version = version;
104
+ return connection;
105
+ });
106
+ }
107
+
108
+ return connection;
109
+ })
110
+ .then(async function setSearchPath(connection) {
111
+ await client.setSchemaSearchPath(connection);
112
+ return connection;
113
+ });
114
+ }
115
+
116
+ // Used to explicitly close a connection, called internally by the pool
117
+ // when a connection times out or the pool is shutdown.
118
+ async destroyRawConnection(connection) {
119
+ const end = promisify((cb) => connection.end(cb));
120
+ return end();
121
+ }
122
+
123
+ // In PostgreSQL, we need to do a version check to do some feature
124
+ // checking on the database.
125
+ checkVersion(connection) {
126
+ return new Promise((resolve, reject) => {
127
+ connection.query('select version();', (err, resp) => {
128
+ if (err) return reject(err);
129
+ resolve(this._parseVersion(resp.rows[0].version));
130
+ });
131
+ });
132
+ }
133
+
134
+ _parseVersion(versionString) {
135
+ return /^PostgreSQL (.*?)( |$)/.exec(versionString)[1];
136
+ }
137
+
138
+ // Position the bindings for the query. The escape sequence for question mark
139
+ // is \? (e.g. knex.raw("\\?") since javascript requires '\' to be escaped too...)
140
+ positionBindings(sql) {
141
+ let questionCount = 0;
142
+ return sql.replace(/(\\*)(\?)/g, function (match, escapes) {
143
+ if (escapes.length % 2) {
144
+ return '?';
145
+ } else {
146
+ questionCount++;
147
+ return `$${questionCount}`;
148
+ }
149
+ });
150
+ }
151
+
152
+ setSchemaSearchPath(connection, searchPath) {
153
+ let path = searchPath || this.searchPath;
154
+
155
+ if (!path) return Promise.resolve(true);
156
+
157
+ if (!Array.isArray(path) && !isString(path)) {
158
+ throw new TypeError(
159
+ `knex: Expected searchPath to be Array/String, got: ${typeof path}`
160
+ );
161
+ }
162
+
163
+ if (isString(path)) {
164
+ if (path.includes(',')) {
165
+ const parts = path.split(',');
166
+ const arraySyntax = `[${parts
167
+ .map((searchPath) => `'${searchPath}'`)
168
+ .join(', ')}]`;
169
+ this.logger.warn(
170
+ `Detected comma in searchPath "${path}".` +
171
+ `If you are trying to specify multiple schemas, use Array syntax: ${arraySyntax}`
172
+ );
173
+ }
174
+ path = [path];
175
+ }
176
+
177
+ path = path.map((schemaName) => `"${schemaName}"`).join(',');
178
+
179
+ return new Promise(function (resolver, rejecter) {
180
+ connection.query(`set search_path to ${path}`, function (err) {
181
+ if (err) return rejecter(err);
182
+ resolver(true);
183
+ });
184
+ });
185
+ }
186
+
187
+ _stream(connection, obj, stream, options) {
188
+ if (!obj.sql) throw new Error('The query is empty');
189
+
190
+ const PGQueryStream = process.browser
191
+ ? undefined
192
+ : require('pg-query-stream');
193
+ const sql = obj.sql;
194
+
195
+ return new Promise(function (resolver, rejecter) {
196
+ const queryStream = connection.query(
197
+ new PGQueryStream(sql, obj.bindings, options)
198
+ );
199
+
200
+ queryStream.on('error', function (error) {
201
+ rejecter(error);
202
+ stream.emit('error', error);
203
+ });
204
+
205
+ // 'end' IS propagated by .pipe, by default
206
+ stream.on('end', resolver);
207
+ queryStream.pipe(stream);
208
+ });
209
+ }
210
+
211
+ // Runs the query on the specified connection, providing the bindings
212
+ // and any other necessary prep work.
213
+ _query(connection, obj) {
214
+ if (!obj.sql) throw new Error('The query is empty');
215
+
216
+ let queryConfig = {
217
+ text: obj.sql,
218
+ values: obj.bindings || [],
219
+ };
220
+
221
+ if (obj.options) {
222
+ queryConfig = extend(queryConfig, obj.options);
223
+ }
224
+
225
+ return new Promise(function (resolver, rejecter) {
226
+ connection.query(queryConfig, function (err, response) {
227
+ if (err) return rejecter(err);
228
+ obj.response = response;
229
+ resolver(obj);
230
+ });
231
+ });
232
+ }
233
+
234
+ // Ensures the response is returned in the same format as other clients.
235
+ processResponse(obj, runner) {
236
+ const resp = obj.response;
237
+ if (obj.output) return obj.output.call(runner, resp);
238
+ if (obj.method === 'raw') return resp;
239
+ const { returning } = obj;
240
+ if (resp.command === 'SELECT') {
241
+ if (obj.method === 'first') return resp.rows[0];
242
+ if (obj.method === 'pluck') return map(resp.rows, obj.pluck);
243
+ return resp.rows;
244
+ }
245
+ if (returning) {
246
+ const returns = [];
247
+ for (let i = 0, l = resp.rows.length; i < l; i++) {
248
+ const row = resp.rows[i];
249
+ returns[i] = row;
250
+ }
251
+ return returns;
252
+ }
253
+ if (resp.command === 'UPDATE' || resp.command === 'DELETE') {
254
+ return resp.rowCount;
255
+ }
256
+ return resp;
257
+ }
258
+
259
+ async cancelQuery(connectionToKill) {
260
+ const conn = await this.acquireRawConnection();
261
+
262
+ try {
263
+ return await this._wrappedCancelQueryCall(conn, connectionToKill);
264
+ } finally {
265
+ await this.destroyRawConnection(conn).catch((err) => {
266
+ this.logger.warn(`Connection Error: ${err}`);
267
+ });
268
+ }
269
+ }
270
+ _wrappedCancelQueryCall(conn, connectionToKill) {
271
+ return this._query(conn, {
272
+ sql: 'SELECT pg_cancel_backend($1);',
273
+ bindings: [connectionToKill.processID],
274
+ options: {},
275
+ });
276
+ }
277
+
278
+ toPathForJson(jsonPath) {
279
+ const PG_PATH_REGEX = /^{.*}$/;
280
+ if (jsonPath.match(PG_PATH_REGEX)) {
281
+ return jsonPath;
282
+ }
283
+ return (
284
+ '{' +
285
+ jsonPath
286
+ .replace(/^(\$\.)/, '') // remove the first dollar
287
+ .replace('.', ',')
288
+ .replace(/\[([0-9]+)]/, ',$1') + // transform [number] to ,number
289
+ '}'
290
+ );
291
+ }
292
+ }
293
+
294
+ Object.assign(Client_PG.prototype, {
295
+ dialect: 'postgresql',
296
+
297
+ driverName: 'pg',
298
+ canCancelQuery: true,
299
+
300
+ _escapeBinding: makeEscape({
301
+ escapeArray(val, esc) {
302
+ return esc(arrayString(val, esc));
303
+ },
304
+ escapeString(str) {
305
+ let hasBackslash = false;
306
+ let escaped = "'";
307
+ for (let i = 0; i < str.length; i++) {
308
+ const c = str[i];
309
+ if (c === "'") {
310
+ escaped += c + c;
311
+ } else if (c === '\\') {
312
+ escaped += c + c;
313
+ hasBackslash = true;
314
+ } else {
315
+ escaped += c;
316
+ }
317
+ }
318
+ escaped += "'";
319
+ if (hasBackslash === true) {
320
+ escaped = 'E' + escaped;
321
+ }
322
+ return escaped;
323
+ },
324
+ escapeObject(val, prepareValue, timezone, seen = []) {
325
+ if (val && typeof val.toPostgres === 'function') {
326
+ seen = seen || [];
327
+ if (seen.indexOf(val) !== -1) {
328
+ throw new Error(
329
+ `circular reference detected while preparing "${val}" for query`
330
+ );
331
+ }
332
+ seen.push(val);
333
+ return prepareValue(val.toPostgres(prepareValue), seen);
334
+ }
335
+ return JSON.stringify(val);
336
+ },
337
+ }),
338
+ });
339
+
340
+ function arrayString(arr, esc) {
341
+ let result = '{';
342
+ for (let i = 0; i < arr.length; i++) {
343
+ if (i > 0) result += ',';
344
+ const val = arr[i];
345
+ if (val === null || typeof val === 'undefined') {
346
+ result += 'NULL';
347
+ } else if (Array.isArray(val)) {
348
+ result += arrayString(val, esc);
349
+ } else if (typeof val === 'number') {
350
+ result += val;
351
+ } else {
352
+ result += JSON.stringify(typeof val === 'string' ? val : esc(val));
353
+ }
354
+ }
355
+ return result + '}';
356
+ }
357
+
358
+ module.exports = Client_PG;