knex 2.4.1 → 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 (40) hide show
  1. package/CHANGELOG.md +59 -17
  2. package/README.md +0 -1
  3. package/bin/cli.js +7 -6
  4. package/knex.mjs +11 -0
  5. package/lib/client.js +20 -0
  6. package/lib/dialects/better-sqlite3/index.js +6 -1
  7. package/lib/dialects/index.js +33 -33
  8. package/lib/dialects/mssql/index.js +5 -0
  9. package/lib/dialects/mssql/query/mssql-querycompiler.js +1 -0
  10. package/lib/dialects/mysql2/index.js +20 -0
  11. package/lib/dialects/oracle/query/oracle-querycompiler.js +1 -0
  12. package/lib/dialects/oracledb/index.js +5 -5
  13. package/lib/dialects/oracledb/schema/oracledb-columncompiler.js +10 -4
  14. package/lib/dialects/postgres/execution/pg-transaction.js +10 -3
  15. package/lib/dialects/postgres/index.js +8 -8
  16. package/lib/dialects/postgres/query/pg-querybuilder.js +5 -0
  17. package/lib/dialects/postgres/query/pg-querycompiler.js +6 -1
  18. package/lib/dialects/postgres/schema/pg-compiler.js +3 -1
  19. package/lib/dialects/postgres/schema/pg-tablecompiler.js +9 -4
  20. package/lib/dialects/redshift/transaction.js +10 -3
  21. package/lib/dialects/sqlite3/execution/sqlite-transaction.js +7 -0
  22. package/lib/dialects/sqlite3/schema/internal/sqlite-ddl-operations.js +1 -1
  23. package/lib/execution/runner.js +18 -0
  24. package/lib/execution/transaction.js +15 -7
  25. package/lib/knex-builder/FunctionHelper.js +26 -0
  26. package/lib/knex-builder/Knex.js +1 -1
  27. package/lib/knex-builder/make-knex.js +5 -0
  28. package/lib/migrations/migrate/MigrationGenerator.js +3 -1
  29. package/lib/query/method-constants.js +1 -0
  30. package/lib/query/querybuilder.js +24 -25
  31. package/lib/query/querycompiler.js +11 -0
  32. package/lib/schema/builder.js +3 -2
  33. package/lib/schema/columnbuilder.js +3 -2
  34. package/lib/schema/tablebuilder.js +3 -2
  35. package/lib/schema/viewbuilder.js +0 -1
  36. package/lib/util/security.js +26 -0
  37. package/package.json +16 -12
  38. package/scripts/clean.js +18 -16
  39. package/scripts/update_gitignore_for_tsc_output.js +36 -32
  40. package/types/index.d.ts +51 -11
package/CHANGELOG.md CHANGED
@@ -1,12 +1,54 @@
1
1
  # Master (Unreleased)
2
2
 
3
- # 2.4.1 - 18 January, 2022
3
+ # 2.5.0 - 08 July, 2023
4
+
5
+ ### New features
6
+
7
+ - Add uuid helper function #5617
8
+ - Add `nativeBindings` option to `better-sqlite3` options #5461
9
+ - Add QueryBuilder#updateFrom #5386
10
+ - Add readonly transaction access mode #5445
11
+ - Add readonly option to Better-SQLite3 #5530
12
+ - Add EXCEPT as a valid keyword #5357
13
+ - Add ability to prepend query comments #5289
14
+ - Add fetchAsString option #5484
15
+
16
+ ### Bug fixes
17
+
18
+ - Avoid password leaks on query logs #5559
19
+ - Add knex.mjs to files in package.json #5518
20
+ - Handle numeric array elements in .orderBy() #5551
21
+ - Attach error handler early enough #5552
22
+ - Fix Returning * in Oracle #5598
23
+ - Fix indexType option in `Postgres` #5601
24
+ - Add mjs extension type #5616
25
+ - Use implicit check on json fields for OracleDB #5478
26
+ - Fix when manually close source stream #5466
27
+ - Fix case sensitive issue with get table #5509
28
+
29
+ ### Typings
30
+
31
+ - Add Object syntax overload to increment method #5512
32
+ - Add object syntax overload to decrement method #5555
33
+ - Fix typing for toSql #5594
34
+ - Add ResolveTableType for `.merge()` #5605
35
+ - Add missing types for havingNull and havingNotNull #5529
36
+ - Add collate to the columnbuilder interface #5568
37
+ - TableBuilder methods return the SchemaBuilder. #5486
38
+
39
+ # 2.4.2 - 22 January, 2023
40
+
41
+ ### Bug fixes
42
+
43
+ - CLI: Fix incorrent EOL causing errors on Linux #5455
44
+
45
+ # 2.4.1 - 18 January, 2023
4
46
 
5
47
  ### Bug fixes
6
48
 
7
49
  - PostgreSQL: Fix Malformed array literal 2.4.0 Regression #5439
8
50
 
9
- # 2.4.0 - 06 January, 2022
51
+ # 2.4.0 - 06 January, 2023
10
52
 
11
53
  ### New features:
12
54
 
@@ -1361,7 +1403,7 @@ Note: there are many breaking changes in this version, particularly in TypeScrip
1361
1403
 
1362
1404
  ### New features:
1363
1405
 
1364
- - Support passing explicit connection to query builder (#2817)
1406
+ - Support passing explicit connection to query builder #2817
1365
1407
  - Introduced abstraction for getting migrations to make migration bundling easier #2775
1366
1408
  - Allow timestamp with timezone on mssql databases #2724
1367
1409
  - Allow specifying multiple migration directories #2735
@@ -1418,7 +1460,7 @@ Note: there are many breaking changes in this version, particularly in TypeScrip
1418
1460
 
1419
1461
  ### Changes:
1420
1462
 
1421
- - THIS RELEASE WAS UNPUBLISHED FROM NPM BECAUSE IT HAD BROKEN MIGRATIONS USING `postprocessResponse` FEATURE (#2644)
1463
+ - THIS RELEASE WAS UNPUBLISHED FROM NPM BECAUSE IT HAD BROKEN MIGRATIONS USING `postprocessResponse` FEATURE #2644
1422
1464
 
1423
1465
  # 0.15.2 - 19 Jul, 2018
1424
1466
 
@@ -1479,7 +1521,7 @@ Note: there are many breaking changes in this version, particularly in TypeScrip
1479
1521
 
1480
1522
  ### Bug fixes:
1481
1523
 
1482
- - Restored functionality of query event #2566 (#2549)
1524
+ - Restored functionality of query event #2566 #2549
1483
1525
 
1484
1526
  # 0.14.5 - 8 Apr, 2018
1485
1527
 
@@ -1507,23 +1549,23 @@ Note: there are many breaking changes in this version, particularly in TypeScrip
1507
1549
 
1508
1550
  ### Bug fixes:
1509
1551
 
1510
- - containsUndefined only validate plain objects. Fixes #1898 (#2468)
1511
- - Add warning when using .returning() in sqlite3. Fixes #1660 (#2471)
1512
- - Throw an error if .update() results in an empty sql (#2472)
1513
- - Removed unnecessary createTableIfNotExist and replaced with createTable (#2473)
1552
+ - containsUndefined only validate plain objects. Fixes #1898 #2468
1553
+ - Add warning when using .returning() in sqlite3. Fixes #1660 #2471
1554
+ - Throw an error if .update() results in an empty sql #2472
1555
+ - Removed unnecessary createTableIfNotExist and replaced with createTable #2473
1514
1556
 
1515
1557
  ### New Features:
1516
1558
 
1517
- - Allow calling lock procedures (such as forUpdate) outside of transaction. Fixes #2403. (#2475)
1518
- - Added test and documentation for Event 'start' (#2488)
1559
+ - Allow calling lock procedures (such as forUpdate) outside of transaction. Fixes #2403. #2475
1560
+ - Added test and documentation for Event 'start' #2488
1519
1561
 
1520
1562
  ### Test / internal changes:
1521
1563
 
1522
1564
  - Added stress test, which uses TCP proxy to simulate flaky connection #2460
1523
- - Removed old docker tests, new stress test setup (#2474)
1524
- - Removed unused property \_\_cid on the base client (#2481)
1525
- - Changed rm to rimraf in 'npm run dev' (#2483)
1526
- - Changed babel preset and use latest node as target when running dev (#2484)
1565
+ - Removed old docker tests, new stress test setup #2474
1566
+ - Removed unused property \_\_cid on the base client #2481
1567
+ - Changed rm to rimraf in 'npm run dev' #2483
1568
+ - Changed babel preset and use latest node as target when running dev #2484
1527
1569
 
1528
1570
  # 0.14.3 - 8 Feb, 2018
1529
1571
 
@@ -1674,8 +1716,8 @@ Note: there are many breaking changes in this version, particularly in TypeScrip
1674
1716
 
1675
1717
  # 0.12.6 - 19 Oct, 2016
1676
1718
 
1677
- - Address warnings mentioned in #1388 (#1740)
1678
- - Remove postinstall script (#1746)
1719
+ - Address warnings mentioned in #1388 #1740
1720
+ - Remove postinstall script #1746
1679
1721
 
1680
1722
  # 0.12.5 - 12 Oct, 2016
1681
1723
 
package/README.md CHANGED
@@ -6,7 +6,6 @@
6
6
  [![Coverage Status](https://coveralls.io/repos/knex/knex/badge.svg?branch=master)](https://coveralls.io/r/knex/knex?branch=master)
7
7
  [![Dependencies Status](https://img.shields.io/librariesio/github/knex/knex)](https://libraries.io/npm/knex)
8
8
  [![Gitter chat](https://badges.gitter.im/tgriesser/knex.svg)](https://gitter.im/tgriesser/knex)
9
- [![Language Grade: JavaScript](https://img.shields.io/lgtm/grade/javascript/g/knex/knex.svg?logo=lgtm&logoWidth=18)](https://lgtm.com/projects/g/knex/knex/context:javascript)
10
9
 
11
10
  > **A SQL query builder that is _flexible_, _portable_, and _fun_ to use!**
12
11
 
package/bin/cli.js CHANGED
@@ -85,7 +85,7 @@ async function initKnex(env, opts, useDefaultClientIfNotSpecified) {
85
85
  }
86
86
 
87
87
  function invoke() {
88
- const filetypes = ['js', 'coffee', 'ts', 'eg', 'ls'];
88
+ const filetypes = ['js', 'mjs', 'coffee', 'ts', 'eg', 'ls'];
89
89
 
90
90
  const cwd = argv.knexfile
91
91
  ? path.dirname(path.resolve(argv.knexfile))
@@ -218,7 +218,7 @@ function invoke() {
218
218
  .action(async (name) => {
219
219
  try {
220
220
  const opts = commander.opts();
221
- const instance = await initKnex(env, opts, true); // Skip config check, we don't really care about client when creating migrations
221
+ const instance = await initKnex(env, opts, true); // Skip config check, we don't really care about client when creating migrations
222
222
  const ext = getMigrationExtension(env, opts);
223
223
  const configOverrides = { extension: ext };
224
224
 
@@ -233,7 +233,7 @@ function invoke() {
233
233
  success(color.green(`Created Migration: ${name}`));
234
234
  })
235
235
  .catch(exit);
236
- } catch(err) {
236
+ } catch (err) {
237
237
  exit(err);
238
238
  }
239
239
  });
@@ -397,7 +397,8 @@ function invoke() {
397
397
  }
398
398
 
399
399
  if (opts.timestampFilenamePrefix) {
400
- configOverrides.timestampFilenamePrefix = opts.timestampFilenamePrefix;
400
+ configOverrides.timestampFilenamePrefix =
401
+ opts.timestampFilenamePrefix;
401
402
  }
402
403
 
403
404
  instance.seed
@@ -406,9 +407,9 @@ function invoke() {
406
407
  success(color.green(`Created seed file: ${name}`));
407
408
  })
408
409
  .catch(exit);
409
- } catch(err) {
410
+ } catch (err) {
410
411
  exit(err);
411
- }
412
+ }
412
413
  });
413
414
 
414
415
  commander
package/knex.mjs ADDED
@@ -0,0 +1,11 @@
1
+ // Knex.js
2
+ // --------------
3
+ // (c) 2013-present Tim Griesser
4
+ // Knex may be freely distributed under the MIT license.
5
+ // For details and documentation:
6
+ // http://knexjs.org
7
+
8
+ import knex from './lib/index.js';
9
+
10
+ export { knex }
11
+ export default knex
package/lib/client.js CHANGED
@@ -31,6 +31,7 @@ const { POOL_CONFIG_OPTIONS } = require('./constants');
31
31
  const ViewBuilder = require('./schema/viewbuilder.js');
32
32
  const ViewCompiler = require('./schema/viewcompiler.js');
33
33
  const isPlainObject = require('lodash/isPlainObject');
34
+ const { setHiddenProperty } = require('./util/security.js');
34
35
 
35
36
  const debug = require('debug')('knex:client');
36
37
 
@@ -43,6 +44,10 @@ class Client extends EventEmitter {
43
44
  this.config = config;
44
45
  this.logger = new Logger(config);
45
46
 
47
+ if (this.config.connection && this.config.connection.password) {
48
+ setHiddenProperty(this.config.connection);
49
+ }
50
+
46
51
  //Client is a required field, so throw error if it's not supplied.
47
52
  //If 'this.dialect' is set, then this is a 'super()' call, in which case
48
53
  //'client' does not have to be set as it's already assigned on the client prototype.
@@ -69,6 +74,9 @@ class Client extends EventEmitter {
69
74
  this.connectionConfigExpirationChecker = () => true; // causes the provider to be called on first use
70
75
  } else {
71
76
  this.connectionSettings = cloneDeep(config.connection || {});
77
+ if (config.connection && config.connection.password) {
78
+ setHiddenProperty(this.connectionSettings, config.connection);
79
+ }
72
80
  this.connectionConfigExpirationChecker = null;
73
81
  }
74
82
  if (this.driverName && config.connection) {
@@ -305,6 +313,18 @@ class Client extends EventEmitter {
305
313
  try {
306
314
  const connection = await this.pool.acquire().promise;
307
315
  debug('acquired connection from pool: %s', connection.__knexUid);
316
+ if (connection.config) {
317
+ if (connection.config.password) {
318
+ setHiddenProperty(connection.config);
319
+ }
320
+ if (
321
+ connection.config.authentication &&
322
+ connection.config.authentication.options &&
323
+ connection.config.authentication.options.password
324
+ ) {
325
+ setHiddenProperty(connection.config.authentication.options);
326
+ }
327
+ }
308
328
  return connection;
309
329
  } catch (error) {
310
330
  let convertedError = error;
@@ -9,7 +9,12 @@ class Client_BetterSQLite3 extends Client_SQLite3 {
9
9
 
10
10
  // Get a raw connection from the database, returning a promise with the connection object.
11
11
  async acquireRawConnection() {
12
- return new this.driver(this.connectionSettings.filename);
12
+ const options = this.connectionSettings.options || {};
13
+
14
+ return new this.driver(this.connectionSettings.filename, {
15
+ nativeBinding: options.nativeBinding,
16
+ readonly: !!options.readonly,
17
+ });
13
18
  }
14
19
 
15
20
  // Used to explicitly close a connection, called internally by the pool when
@@ -1,34 +1,34 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.getDialectByNameOrAlias = void 0;
4
- const { resolveClientNameWithAliases } = require('../util/helpers');
5
- const dbNameToDialectLoader = Object.freeze({
6
- 'better-sqlite3': () => require('./better-sqlite3'),
7
- cockroachdb: () => require('./cockroachdb'),
8
- mssql: () => require('./mssql'),
9
- mysql: () => require('./mysql'),
10
- mysql2: () => require('./mysql2'),
11
- oracle: () => require('./oracle'),
12
- oracledb: () => require('./oracledb'),
13
- pgnative: () => require('./pgnative'),
14
- postgres: () => require('./postgres'),
15
- redshift: () => require('./redshift'),
16
- sqlite3: () => require('./sqlite3'),
17
- });
18
- /**
19
- * Gets the Dialect object with the given client name or throw an
20
- * error if not found.
21
- *
22
- * NOTE: This is a replacement for prior practice of doing dynamic
23
- * string construction for imports of Dialect objects.
24
- */
25
- function getDialectByNameOrAlias(clientName) {
26
- const resolvedClientName = resolveClientNameWithAliases(clientName);
27
- const dialectLoader = dbNameToDialectLoader[resolvedClientName];
28
- if (!dialectLoader) {
29
- throw new Error(`Invalid clientName given: ${clientName}`);
30
- }
31
- return dialectLoader();
32
- }
33
- exports.getDialectByNameOrAlias = getDialectByNameOrAlias;
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getDialectByNameOrAlias = void 0;
4
+ const { resolveClientNameWithAliases } = require('../util/helpers');
5
+ const dbNameToDialectLoader = Object.freeze({
6
+ 'better-sqlite3': () => require('./better-sqlite3'),
7
+ cockroachdb: () => require('./cockroachdb'),
8
+ mssql: () => require('./mssql'),
9
+ mysql: () => require('./mysql'),
10
+ mysql2: () => require('./mysql2'),
11
+ oracle: () => require('./oracle'),
12
+ oracledb: () => require('./oracledb'),
13
+ pgnative: () => require('./pgnative'),
14
+ postgres: () => require('./postgres'),
15
+ redshift: () => require('./redshift'),
16
+ sqlite3: () => require('./sqlite3'),
17
+ });
18
+ /**
19
+ * Gets the Dialect object with the given client name or throw an
20
+ * error if not found.
21
+ *
22
+ * NOTE: This is a replacement for prior practice of doing dynamic
23
+ * string construction for imports of Dialect objects.
24
+ */
25
+ function getDialectByNameOrAlias(clientName) {
26
+ const resolvedClientName = resolveClientNameWithAliases(clientName);
27
+ const dialectLoader = dbNameToDialectLoader[resolvedClientName];
28
+ if (!dialectLoader) {
29
+ throw new Error(`Invalid clientName given: ${clientName}`);
30
+ }
31
+ return dialectLoader();
32
+ }
33
+ exports.getDialectByNameOrAlias = getDialectByNameOrAlias;
34
34
  //# sourceMappingURL=index.js.map
@@ -12,6 +12,7 @@ const TableCompiler = require('./schema/mssql-tablecompiler');
12
12
  const ViewCompiler = require('./schema/mssql-viewcompiler');
13
13
  const ColumnCompiler = require('./schema/mssql-columncompiler');
14
14
  const QueryBuilder = require('../../query/querybuilder');
15
+ const { setHiddenProperty } = require('../../util/security');
15
16
 
16
17
  const debug = require('debug')('knex:mssql');
17
18
 
@@ -66,6 +67,10 @@ class Client_MSSQL extends Client {
66
67
  },
67
68
  };
68
69
 
70
+ if (cfg.authentication.options.password) {
71
+ setHiddenProperty(cfg.authentication.options);
72
+ }
73
+
69
74
  // tedious always connect via tcp when port is specified
70
75
  if (cfg.options.instanceName) delete cfg.options.port;
71
76
 
@@ -11,6 +11,7 @@ const {
11
11
  } = require('../../../formatter/wrappingFormatter');
12
12
 
13
13
  const components = [
14
+ 'comments',
14
15
  'columns',
15
16
  'join',
16
17
  'lock',
@@ -14,6 +14,26 @@ class Client_MySQL2 extends Client_MySQL {
14
14
  _driver() {
15
15
  return require('mysql2');
16
16
  }
17
+
18
+ initializeDriver() {
19
+ try {
20
+ this.driver = this._driver();
21
+ } catch (e) {
22
+ let message = `Knex: run\n$ npm install ${this.driverName}`;
23
+
24
+ const nodeMajorVersion = process.version.replace(/^v/, '').split('.')[0];
25
+ if (nodeMajorVersion <= 12) {
26
+ message += `@3.2.0`;
27
+ this.logger.error(
28
+ 'Mysql2 version 3.2.0 is the latest version to support Node.js 12 or lower.'
29
+ );
30
+ }
31
+ message += ` --save`;
32
+ this.logger.error(`${message}\n${e.message}\n${e.stack}`);
33
+ throw new Error(`${message}\n${e.message}`);
34
+ }
35
+ }
36
+
17
37
  validateConnection(connection) {
18
38
  return (
19
39
  connection &&
@@ -12,6 +12,7 @@ const { ReturningHelper } = require('../utils');
12
12
  const { isString } = require('../../../util/is');
13
13
 
14
14
  const components = [
15
+ 'comments',
15
16
  'columns',
16
17
  'join',
17
18
  'where',
@@ -254,10 +254,6 @@ class Client_Oracledb extends Client_Oracle {
254
254
  });
255
255
  }
256
256
  }
257
- if (connection.isTransaction) {
258
- return obj;
259
- }
260
- await connection.commitAsync();
261
257
  if (obj.returningSql) {
262
258
  const response = await connection.executeAsync(
263
259
  obj.returningSql(),
@@ -266,6 +262,10 @@ class Client_Oracledb extends Client_Oracle {
266
262
  );
267
263
  obj.response = response.rows;
268
264
  }
265
+ if (connection.isTransaction) {
266
+ return obj;
267
+ }
268
+ await connection.commitAsync();
269
269
  return obj;
270
270
  });
271
271
  }
@@ -287,7 +287,7 @@ class Client_Oracledb extends Client_Oracle {
287
287
  case 'del':
288
288
  case 'update':
289
289
  case 'counter':
290
- if (obj.returning && !isEmpty(obj.returning)) {
290
+ if ((obj.returning && !isEmpty(obj.returning)) || obj.returningSql) {
291
291
  return response;
292
292
  } else if (obj.rowsAffected !== undefined) {
293
293
  return obj.rowsAffected;
@@ -4,7 +4,7 @@ const { isObject } = require('../../../util/is');
4
4
  class ColumnCompiler_Oracledb extends ColumnCompiler_Oracle {
5
5
  constructor() {
6
6
  super(...arguments);
7
- this.modifiers = ['defaultTo', 'nullable', 'comment'];
7
+ this.modifiers = ['defaultTo', 'nullable', 'comment', 'checkJson'];
8
8
  this._addCheckModifiers();
9
9
  }
10
10
 
@@ -38,14 +38,20 @@ class ColumnCompiler_Oracledb extends ColumnCompiler_Oracle {
38
38
  }
39
39
 
40
40
  json() {
41
- return `varchar2(4000) check (${this.formatter.columnize(
42
- this.getColumnName()
43
- )} is json)`;
41
+ // implicitly add the check for json
42
+ this.columnBuilder._modifiers.checkJson = [
43
+ this.formatter.columnize(this.getColumnName()),
44
+ ];
45
+ return 'varchar2(4000)';
44
46
  }
45
47
 
46
48
  jsonb() {
47
49
  return this.json();
48
50
  }
51
+
52
+ checkJson(column) {
53
+ return `check (${column} is json)`;
54
+ }
49
55
  }
50
56
 
51
57
  ColumnCompiler_Oracledb.prototype.time = 'timestamp with local time zone';
@@ -2,10 +2,17 @@ const Transaction = require('../../../execution/transaction');
2
2
 
3
3
  class Transaction_PG extends Transaction {
4
4
  begin(conn) {
5
- if (this.isolationLevel) {
6
- return this.query(conn, `BEGIN ISOLATION LEVEL ${this.isolationLevel};`);
5
+ const trxMode = [
6
+ this.isolationLevel ? `ISOLATION LEVEL ${this.isolationLevel}` : '',
7
+ this.readOnly ? 'READ ONLY' : '',
8
+ ]
9
+ .join(' ')
10
+ .trim();
11
+
12
+ if (trxMode.length === 0) {
13
+ return this.query(conn, 'BEGIN;');
7
14
  }
8
- return this.query(conn, 'BEGIN;');
15
+ return this.query(conn, `BEGIN TRANSACTION ${trxMode};`);
9
16
  }
10
17
  }
11
18
 
@@ -80,6 +80,14 @@ class Client_PG extends Client {
80
80
  _acquireOnlyConnection() {
81
81
  const connection = new this.driver.Client(this.connectionSettings);
82
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
+
83
91
  return connection.connect().then(() => connection);
84
92
  }
85
93
 
@@ -90,14 +98,6 @@ class Client_PG extends Client {
90
98
 
91
99
  return this._acquireOnlyConnection()
92
100
  .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
101
  if (!client.version) {
102
102
  return client.checkVersion(connection).then(function (version) {
103
103
  client.version = version;
@@ -1,6 +1,11 @@
1
1
  const QueryBuilder = require('../../../query/querybuilder.js');
2
2
 
3
3
  module.exports = class QueryBuilder_PostgreSQL extends QueryBuilder {
4
+ updateFrom(name) {
5
+ this._single.updateFrom = name;
6
+ return this;
7
+ }
8
+
4
9
  using(tables) {
5
10
  this._single.using = tables;
6
11
  return this;
@@ -50,12 +50,13 @@ class QueryCompiler_PG extends QueryCompiler {
50
50
  const withSQL = this.with();
51
51
  const updateData = this._prepUpdate(this.single.update);
52
52
  const wheres = this.where();
53
- const { returning } = this.single;
53
+ const { returning, updateFrom } = this.single;
54
54
  return {
55
55
  sql:
56
56
  withSQL +
57
57
  `update ${this.single.only ? 'only ' : ''}${this.tableName} ` +
58
58
  `set ${updateData.join(', ')}` +
59
+ this._updateFrom(updateFrom) +
59
60
  (wheres ? ` ${wheres}` : '') +
60
61
  this._returning(returning),
61
62
  returning,
@@ -141,6 +142,10 @@ class QueryCompiler_PG extends QueryCompiler {
141
142
  return value ? ` returning ${this.formatter.columnize(value)}` : '';
142
143
  }
143
144
 
145
+ _updateFrom(name) {
146
+ return name ? ` from ${this.formatter.wrap(name)}` : '';
147
+ }
148
+
144
149
  _ignore(columns) {
145
150
  if (columns === true) {
146
151
  return ' on conflict do nothing';
@@ -120,7 +120,9 @@ class SchemaCompiler_PG extends SchemaCompiler {
120
120
 
121
121
  refreshMaterializedView(viewName, concurrently = false) {
122
122
  this.pushQuery({
123
- sql: `refresh materialized view${concurrently ? " concurrently" : ""} ${this.formatter.wrap(viewName)}`,
123
+ sql: `refresh materialized view${
124
+ concurrently ? ' concurrently' : ''
125
+ } ${this.formatter.wrap(viewName)}`,
124
126
  });
125
127
  }
126
128
 
@@ -235,12 +235,13 @@ class TableCompiler_PG extends TableCompiler {
235
235
  : this._indexCommand('index', this.tableNameRaw, columns);
236
236
 
237
237
  let predicate;
238
+ let storageEngineIndexType;
238
239
  let indexType;
239
240
 
240
241
  if (isString(options)) {
241
- indexType = options;
242
+ storageEngineIndexType = options;
242
243
  } else if (isObject(options)) {
243
- ({ indexType, predicate } = options);
244
+ ({ indexType, storageEngineIndexType, predicate } = options);
244
245
  }
245
246
 
246
247
  const predicateQuery = predicate
@@ -248,8 +249,12 @@ class TableCompiler_PG extends TableCompiler {
248
249
  : '';
249
250
 
250
251
  this.pushQuery(
251
- `create index ${indexName} on ${this.tableName()}${
252
- (indexType && ` using ${indexType}`) || ''
252
+ `create${
253
+ typeof indexType === 'string' && indexType.toLowerCase() === 'unique'
254
+ ? ' unique'
255
+ : ''
256
+ } index ${indexName} on ${this.tableName()}${
257
+ (storageEngineIndexType && ` using ${storageEngineIndexType}`) || ''
253
258
  }` +
254
259
  ' (' +
255
260
  this.formatter.columnize(columns) +
@@ -2,10 +2,17 @@ const Transaction = require('../../execution/transaction');
2
2
 
3
3
  module.exports = class Redshift_Transaction extends Transaction {
4
4
  begin(conn) {
5
- if (this.isolationLevel) {
6
- return this.query(conn, `BEGIN ISOLATION LEVEL ${this.isolationLevel};`);
5
+ const trxMode = [
6
+ this.isolationLevel ? `ISOLATION LEVEL ${this.isolationLevel}` : '',
7
+ this.readOnly ? 'READ ONLY' : '',
8
+ ]
9
+ .join(' ')
10
+ .trim();
11
+
12
+ if (trxMode.length === 0) {
13
+ return this.query(conn, 'BEGIN;');
7
14
  }
8
- return this.query(conn, 'BEGIN;');
15
+ return this.query(conn, `BEGIN ${trxMode};`);
9
16
  }
10
17
 
11
18
  savepoint(conn) {
@@ -11,6 +11,13 @@ class Transaction_Sqlite extends Transaction {
11
11
  'sqlite3 only supports serializable transactions, ignoring the isolation level param'
12
12
  );
13
13
  }
14
+ // SQLite infers read vs write transactions from the statement operation
15
+ // https://www.sqlite.org/lang_transaction.html#read_transactions_versus_write_transactions
16
+ if (this.readOnly) {
17
+ this.client.logger.warn(
18
+ 'sqlite3 implicitly handles read vs write transactions'
19
+ );
20
+ }
14
21
  return this.query(conn, 'BEGIN;');
15
22
  }
16
23
  }
@@ -15,7 +15,7 @@ function renameTable(tableName, alteredName) {
15
15
  }
16
16
 
17
17
  function getTableSql(tableName) {
18
- return `SELECT type, sql FROM sqlite_master WHERE (type='table' OR (type='index' AND sql IS NOT NULL)) AND tbl_name='${tableName}'`;
18
+ return `SELECT type, sql FROM sqlite_master WHERE (type='table' OR (type='index' AND sql IS NOT NULL)) AND lower(tbl_name)='${tableName.toLowerCase()}'`;
19
19
  }
20
20
 
21
21
  function isForeignCheckEnabled() {
@@ -72,6 +72,24 @@ class Runner {
72
72
  this.client.releaseConnection(this.connection);
73
73
  });
74
74
 
75
+ // If the stream is manually destroyed, the close event is not
76
+ // propagated to the top of the pipe chain. We need to manually verify
77
+ // that the source stream is closed and if not, manually destroy it.
78
+ stream.on('pipe', (sourceStream) => {
79
+ const cleanSourceStream = () => {
80
+ if (!sourceStream.closed) {
81
+ sourceStream.destroy();
82
+ }
83
+ };
84
+
85
+ // Stream already closed, cleanup immediately
86
+ if (stream.closed) {
87
+ cleanSourceStream();
88
+ } else {
89
+ stream.on('close', cleanSourceStream);
90
+ }
91
+ });
92
+
75
93
  const connectionAcquirePromise = this.ensureConnection(
76
94
  ensureConnectionStreamCallback,
77
95
  {