knex 0.95.15 → 1.0.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 (74) hide show
  1. package/CHANGELOG.md +53 -1
  2. package/UPGRADING.md +7 -0
  3. package/lib/client.js +14 -1
  4. package/lib/constants.js +2 -0
  5. package/lib/dialects/better-sqlite3/index.js +72 -0
  6. package/lib/dialects/cockroachdb/crdb-querycompiler.js +87 -33
  7. package/lib/dialects/cockroachdb/crdb-tablecompiler.js +19 -0
  8. package/lib/dialects/cockroachdb/index.js +13 -0
  9. package/lib/dialects/mssql/index.js +0 -11
  10. package/lib/dialects/mssql/query/mssql-querycompiler.js +122 -64
  11. package/lib/dialects/mssql/schema/mssql-columncompiler.js +41 -6
  12. package/lib/dialects/mssql/schema/mssql-tablecompiler.js +24 -9
  13. package/lib/dialects/mssql/schema/mssql-viewcompiler.js +15 -1
  14. package/lib/dialects/mysql/query/mysql-querycompiler.js +91 -5
  15. package/lib/dialects/mysql/schema/mysql-columncompiler.js +32 -5
  16. package/lib/dialects/mysql/schema/mysql-tablecompiler.js +28 -4
  17. package/lib/dialects/oracle/query/oracle-querycompiler.js +7 -6
  18. package/lib/dialects/oracle/schema/internal/trigger.js +1 -1
  19. package/lib/dialects/oracle/schema/oracle-columncompiler.js +10 -4
  20. package/lib/dialects/oracle/schema/oracle-tablecompiler.js +17 -6
  21. package/lib/dialects/oracledb/index.js +0 -4
  22. package/lib/dialects/oracledb/query/oracledb-querycompiler.js +104 -0
  23. package/lib/dialects/oracledb/schema/oracledb-columncompiler.js +23 -0
  24. package/lib/dialects/postgres/index.js +21 -6
  25. package/lib/dialects/postgres/query/pg-querybuilder.js +8 -0
  26. package/lib/dialects/postgres/query/pg-querycompiler.js +166 -5
  27. package/lib/dialects/postgres/schema/pg-columncompiler.js +24 -4
  28. package/lib/dialects/postgres/schema/pg-tablecompiler.js +55 -47
  29. package/lib/dialects/redshift/index.js +12 -0
  30. package/lib/dialects/redshift/query/redshift-querycompiler.js +62 -26
  31. package/lib/dialects/redshift/schema/redshift-columncompiler.js +2 -1
  32. package/lib/dialects/redshift/schema/redshift-tablecompiler.js +4 -1
  33. package/lib/dialects/sqlite3/index.js +18 -4
  34. package/lib/dialects/sqlite3/query/sqlite-querycompiler.js +85 -18
  35. package/lib/dialects/sqlite3/schema/ddl.js +274 -282
  36. package/lib/dialects/sqlite3/schema/internal/sqlite-ddl-operations.js +18 -8
  37. package/lib/dialects/sqlite3/schema/sqlite-columncompiler.js +20 -0
  38. package/lib/dialects/sqlite3/schema/sqlite-compiler.js +16 -12
  39. package/lib/dialects/sqlite3/schema/sqlite-tablecompiler.js +15 -5
  40. package/lib/dialects/sqlite3/schema/sqlite-viewcompiler.js +31 -2
  41. package/lib/execution/runner.js +37 -2
  42. package/lib/knex-builder/FunctionHelper.js +21 -0
  43. package/lib/migrations/common/MigrationsLoader.js +36 -0
  44. package/lib/migrations/migrate/MigrationGenerator.js +1 -1
  45. package/lib/migrations/migrate/Migrator.js +19 -22
  46. package/lib/migrations/migrate/migration-list-resolver.js +2 -5
  47. package/lib/migrations/migrate/{configuration-merger.js → migrator-configuration-merger.js} +2 -4
  48. package/lib/migrations/migrate/sources/fs-migrations.js +4 -29
  49. package/lib/migrations/migrate/stub/js.stub +8 -1
  50. package/lib/migrations/migrate/stub/knexfile-js.stub +3 -0
  51. package/lib/migrations/migrate/stub/knexfile-ts.stub +5 -2
  52. package/lib/migrations/seed/Seeder.js +25 -92
  53. package/lib/migrations/seed/seeder-configuration-merger.js +60 -0
  54. package/lib/migrations/seed/sources/fs-seeds.js +65 -0
  55. package/lib/migrations/seed/stub/js.stub +4 -1
  56. package/lib/migrations/util/import-file.js +0 -1
  57. package/lib/query/joinclause.js +24 -5
  58. package/lib/query/method-constants.js +37 -0
  59. package/lib/query/querybuilder.js +230 -5
  60. package/lib/query/querycompiler.js +269 -84
  61. package/lib/schema/columnbuilder.js +8 -0
  62. package/lib/schema/columncompiler.js +132 -5
  63. package/lib/schema/compiler.js +1 -0
  64. package/lib/schema/tablebuilder.js +41 -8
  65. package/lib/schema/tablecompiler.js +57 -0
  66. package/lib/schema/viewcompiler.js +13 -10
  67. package/package.json +30 -22
  68. package/scripts/docker-compose.yml +7 -7
  69. package/scripts/oracledb-install-driver-libs.sh +82 -0
  70. package/scripts/runkit-example.js +1 -1
  71. package/scripts/stress-test/docker-compose.yml +3 -3
  72. package/scripts/stress-test/knex-stress-test.js +1 -1
  73. package/scripts/stress-test/reconnect-test-mysql-based-drivers.js +1 -1
  74. package/types/index.d.ts +124 -20
package/CHANGELOG.md CHANGED
@@ -1,5 +1,57 @@
1
1
  # Master (Unreleased)
2
2
 
3
+ # 1.0.0 - 16 January, 2022
4
+
5
+ ### Breaking changes
6
+
7
+ - Dropped support for Node 10;
8
+ - Replaced unsupported `sqlite3` driver with `@vscode/sqlite3`;
9
+ - Changed data structure from `RETURNING` operation to be consistent with `SELECT`;
10
+ - Changed Migrator to return list of migrations as objects consistently.
11
+
12
+ ### New features:
13
+
14
+ - Support fromRaw #4781
15
+ - Support zero precision in timestamp/datetime #4784
16
+ - Support whereLike and whereILike #4779
17
+ - Add JSDoc (TS flavor) to stub files #4809
18
+ - Allow skip binding in limit and offset #4811
19
+ - Support creating a new table in the database based on another table #4821
20
+ - Accept Raw on onIn joins #4830
21
+ - Implement support for custom seed sources #4842
22
+ - Add binary uuid option #4836
23
+ - ForUpdate array parameter #4882
24
+ - Add camel case to timestamps method #4803
25
+ - Advanced JSON support #4859
26
+ - Add type to TypeScript knexfile #4909
27
+ - Checks Constraints Support #4874
28
+ - Support creating multiple PKs with increments #4903
29
+ - Enable wrapIdentifier for SQLite .hasTable #4915
30
+ - MSSQL: Add support for unique constraint #4887
31
+ - SQLite: New dialect, using better-sqlite3 driver #4871
32
+ - SQLite: Switch to @vscode/sqlite3 #4866
33
+ - SQLite: Support createViewOrReplace #4856
34
+ - SQLite: Support RETURNING statements for better-sqlite3 driver #4934
35
+ - PostgreSQL: Support JOIN and USING syntax for Delete Statement #4800
36
+
37
+ ### Bug fixes:
38
+
39
+ - Fix overzealous warning on use of whereNot with "in" or "between" #4780
40
+ - Fix Union all + first syntax error #4799
41
+ - Make view columns optional in create view like #4829
42
+ - Insert lock row fix during migration #4865
43
+ - Fix for createViewOrReplace #4856
44
+ - SQLite: Fix foreign key constraints when altering a table #4189
45
+ - MySQL: Validate connection fix #4794
46
+ - MySQL: Set comment size warning limit to 1024 #4867
47
+
48
+ ### Typings:
49
+
50
+ - Allow string indexType in index creation #4791
51
+ - Add missing ints typings #4832
52
+ - Returning method types #4881
53
+ - Improve columnInfo type #4868
54
+
3
55
  # 0.95.15 - 22 December, 2021
4
56
 
5
57
  ### Bug fixes:
@@ -18,7 +70,7 @@
18
70
  ### Bug fixes:
19
71
 
20
72
  - PostgreSQL: Support zero precision in timestamp/datetime #4784
21
-
73
+
22
74
  ### Typings:
23
75
 
24
76
  - Allow string indexType in index creation #4791
package/UPGRADING.md CHANGED
@@ -1,5 +1,12 @@
1
1
  ## Upgrading to new knex.js versions
2
2
 
3
+ ### Upgrading to version 1.0.0+
4
+
5
+ * Node.js older than 12 is no longer supported, make sure to update your environment;
6
+ * If you are using `sqlite3` driver, please replace it with `@vscode/sqlite3`;
7
+ * `RETURNING` operations now always return an object with column names;
8
+ * Migrator now returns list of migrations as objects.
9
+
3
10
  ### Upgrading to version 0.95.0+
4
11
 
5
12
  * TypeScript type exports changed significantly. While `import Knex from 'knex';` used to import the knex instantiation function, the namespace and the interface for the knex instantiation function/object, there is now a clear distinction between them:
package/lib/client.js CHANGED
@@ -30,6 +30,7 @@ const Logger = require('./logger');
30
30
  const { POOL_CONFIG_OPTIONS } = require('./constants');
31
31
  const ViewBuilder = require('./schema/viewbuilder.js');
32
32
  const ViewCompiler = require('./schema/viewcompiler.js');
33
+ const isPlainObject = require('lodash/isPlainObject');
33
34
 
34
35
  const debug = require('debug')('knex:client');
35
36
 
@@ -51,6 +52,7 @@ class Client extends EventEmitter {
51
52
  `Using 'this.dialect' to identify the client is deprecated and support for it will be removed in the future. Please use configuration option 'client' instead.`
52
53
  );
53
54
  }
55
+
54
56
  const dbClient = this.config.client || this.dialect;
55
57
  if (!dbClient) {
56
58
  throw new Error(
@@ -392,8 +394,13 @@ class Client extends EventEmitter {
392
394
  i = -1;
393
395
  while (++i < values.length) {
394
396
  if (i > 0) str += ', ';
397
+ let value = values[i];
398
+ // json columns can have object in values.
399
+ if (isPlainObject(value)) {
400
+ value = JSON.stringify(value);
401
+ }
395
402
  str += this.parameter(
396
- values[i] === undefined ? notSetValue : values[i],
403
+ value === undefined ? notSetValue : value,
397
404
  builder,
398
405
  bindingsHolder
399
406
  );
@@ -442,6 +449,12 @@ class Client extends EventEmitter {
442
449
  processPassedConnection(connection) {
443
450
  // Default implementation is noop
444
451
  }
452
+
453
+ toPathForJson(jsonPath) {
454
+ // By default, we want a json path, so if this function is not overriden,
455
+ // we return the path.
456
+ return jsonPath;
457
+ }
445
458
  }
446
459
 
447
460
  Object.assign(Client.prototype, {
package/lib/constants.js CHANGED
@@ -16,6 +16,7 @@ const SUPPORTED_CLIENTS = Object.freeze(
16
16
  'redshift',
17
17
  'sqlite3',
18
18
  'cockroachdb',
19
+ 'better-sqlite3',
19
20
  ].concat(Object.keys(CLIENT_ALIASES))
20
21
  );
21
22
 
@@ -29,6 +30,7 @@ const DRIVER_NAMES = Object.freeze({
29
30
  Redshift: 'pg-redshift',
30
31
  SQLite: 'sqlite3',
31
32
  CockroachDB: 'cockroachdb',
33
+ BetterSQLite3: 'better-sqlite3',
32
34
  });
33
35
 
34
36
  const POOL_CONFIG_OPTIONS = Object.freeze([
@@ -0,0 +1,72 @@
1
+ // better-sqlite3 Client
2
+ // -------
3
+ const Client_SQLite3 = require('../sqlite3');
4
+
5
+ class Client_BetterSQLite3 extends Client_SQLite3 {
6
+ _driver() {
7
+ return require('better-sqlite3');
8
+ }
9
+
10
+ // Get a raw connection from the database, returning a promise with the connection object.
11
+ async acquireRawConnection() {
12
+ return new this.driver(this.connectionSettings.filename);
13
+ }
14
+
15
+ // Used to explicitly close a connection, called internally by the pool when
16
+ // a connection times out or the pool is shutdown.
17
+ async destroyRawConnection(connection) {
18
+ return connection.close();
19
+ }
20
+
21
+ // Runs the query on the specified connection, providing the bindings and any
22
+ // other necessary prep work.
23
+ async _query(connection, obj) {
24
+ if (!obj.sql) throw new Error('The query is empty');
25
+
26
+ if (!connection) {
27
+ throw new Error('No connection provided');
28
+ }
29
+
30
+ const statement = connection.prepare(obj.sql);
31
+ const bindings = this._formatBindings(obj.bindings);
32
+
33
+ if (statement.reader) {
34
+ const response = await statement.all(bindings);
35
+ obj.response = response;
36
+ return obj;
37
+ }
38
+
39
+ const response = await statement.run(bindings);
40
+ obj.response = response;
41
+ obj.context = {
42
+ lastID: response.lastInsertRowid,
43
+ changes: response.changes,
44
+ };
45
+
46
+ return obj;
47
+ }
48
+
49
+ _formatBindings(bindings) {
50
+ if (!bindings) {
51
+ return [];
52
+ }
53
+ return bindings.map((binding) => {
54
+ if (binding instanceof Date) {
55
+ return binding.valueOf();
56
+ }
57
+
58
+ if (typeof binding === 'boolean') {
59
+ return String(binding);
60
+ }
61
+
62
+ return binding;
63
+ });
64
+ }
65
+ }
66
+
67
+ Object.assign(Client_BetterSQLite3.prototype, {
68
+ // The "dialect", for reference .
69
+ driverName: 'better-sqlite3',
70
+ });
71
+
72
+ module.exports = Client_BetterSQLite3;
@@ -1,6 +1,9 @@
1
1
  const QueryCompiler_PG = require('../postgres/query/pg-querycompiler');
2
- const isEmpty = require('lodash/isEmpty');
3
- const { columnize: columnize_ } = require('../../formatter/wrappingFormatter');
2
+ const {
3
+ columnize: columnize_,
4
+ wrap: wrap_,
5
+ operator: operator_,
6
+ } = require('../../formatter/wrappingFormatter');
4
7
 
5
8
  class QueryCompiler_CRDB extends QueryCompiler_PG {
6
9
  truncate() {
@@ -20,43 +23,94 @@ class QueryCompiler_CRDB extends QueryCompiler_PG {
20
23
 
21
24
  _upsert() {
22
25
  const upsertValues = this.single.upsert || [];
23
- let sql = this.with() + `upsert into ${this.tableName} `;
24
- if (Array.isArray(upsertValues)) {
25
- if (upsertValues.length === 0) return '';
26
- } else if (typeof upsertValues === 'object' && isEmpty(upsertValues)) {
27
- return sql + this._emptyInsertValue;
26
+ const sql = this.with() + `upsert into ${this.tableName} `;
27
+ const body = this._insertBody(upsertValues);
28
+ return body === '' ? '' : sql + body;
29
+ }
30
+
31
+ whereJsonPath(statement) {
32
+ let castValue = '';
33
+ if (parseInt(statement.value)) {
34
+ castValue = '::int';
35
+ } else if (parseFloat(statement.value)) {
36
+ castValue = '::float';
37
+ } else {
38
+ castValue = " #>> '{}'";
28
39
  }
40
+ return `json_extract_path(${this._columnClause(
41
+ statement
42
+ )}, ${this.client.toArrayPathFromJsonPath(
43
+ statement.jsonPath,
44
+ this.builder,
45
+ this.bindingsHolder
46
+ )})${castValue} ${operator_(
47
+ statement.operator,
48
+ this.builder,
49
+ this.client,
50
+ this.bindingsHolder
51
+ )} ${this._jsonValueClause(statement)}`;
52
+ }
29
53
 
30
- const upsertData = this._prepInsert(upsertValues);
31
- if (typeof upsertData === 'string') {
32
- sql += upsertData;
54
+ // Json common functions
55
+ _jsonExtract(nameFunction, params) {
56
+ let extractions;
57
+ if (Array.isArray(params.column)) {
58
+ extractions = params.column;
33
59
  } else {
34
- if (upsertData.columns.length) {
35
- sql += `(${columnize_(
36
- upsertData.columns,
60
+ extractions = [params];
61
+ }
62
+ return extractions
63
+ .map((extraction) => {
64
+ const jsonCol = `json_extract_path(${columnize_(
65
+ extraction.column || extraction[0],
37
66
  this.builder,
38
67
  this.client,
39
68
  this.bindingsHolder
40
- )}`;
41
- sql += ') values (';
42
- let i = -1;
43
- while (++i < upsertData.values.length) {
44
- if (i !== 0) sql += '), (';
45
- sql += this.client.parameterize(
46
- upsertData.values[i],
47
- this.client.valueForUndefined,
48
- this.builder,
49
- this.bindingsHolder
50
- );
51
- }
52
- sql += ')';
53
- } else if (upsertValues.length === 1 && upsertValues[0]) {
54
- sql += this._emptyInsertValue;
55
- } else {
56
- sql = '';
57
- }
58
- }
59
- return sql;
69
+ )}, ${this.client.toArrayPathFromJsonPath(
70
+ extraction.path || extraction[1],
71
+ this.builder,
72
+ this.bindingsHolder
73
+ )})`;
74
+ const alias = extraction.alias || extraction[2];
75
+ return alias
76
+ ? this.client.alias(jsonCol, this.formatter.wrap(alias))
77
+ : jsonCol;
78
+ })
79
+ .join(', ');
80
+ }
81
+
82
+ _onJsonPathEquals(nameJoinFunction, clause) {
83
+ return (
84
+ 'json_extract_path(' +
85
+ wrap_(
86
+ clause.columnFirst,
87
+ undefined,
88
+ this.builder,
89
+ this.client,
90
+ this.bindingsHolder
91
+ ) +
92
+ ', ' +
93
+ this.client.toArrayPathFromJsonPath(
94
+ clause.jsonPathFirst,
95
+ this.builder,
96
+ this.bindingsHolder
97
+ ) +
98
+ ') = json_extract_path(' +
99
+ wrap_(
100
+ clause.columnSecond,
101
+ undefined,
102
+ this.builder,
103
+ this.client,
104
+ this.bindingsHolder
105
+ ) +
106
+ ', ' +
107
+ this.client.toArrayPathFromJsonPath(
108
+ clause.jsonPathSecond,
109
+ this.builder,
110
+ this.bindingsHolder
111
+ ) +
112
+ ')'
113
+ );
60
114
  }
61
115
  }
62
116
 
@@ -7,6 +7,25 @@ class TableCompiler_CRDB extends TableCompiler {
7
7
  super(client, tableBuilder);
8
8
  }
9
9
 
10
+ addColumns(columns, prefix, colCompilers) {
11
+ if (prefix === this.alterColumnsPrefix) {
12
+ // alter columns
13
+ for (const col of colCompilers) {
14
+ this.client.logger.warn(
15
+ 'Experimental alter column in use, see issue: https://github.com/cockroachdb/cockroach/issues/49329'
16
+ );
17
+ this.pushQuery({
18
+ sql: 'SET enable_experimental_alter_column_type_general = true',
19
+ bindings: [],
20
+ });
21
+ super._addColumn(col);
22
+ }
23
+ } else {
24
+ // base class implementation for normal add
25
+ super.addColumns(columns, prefix);
26
+ }
27
+ }
28
+
10
29
  dropUnique(columns, indexName) {
11
30
  indexName = indexName
12
31
  ? this.formatter.wrap(indexName)
@@ -58,6 +58,19 @@ class Client_CockroachDB extends Client_PostgreSQL {
58
58
  connectionToKill.activeQuery
59
59
  );
60
60
  }
61
+
62
+ toArrayPathFromJsonPath(jsonPath, builder, bindingsHolder) {
63
+ return jsonPath
64
+ .replace(/^(\$\.)/, '') // remove the first dollar
65
+ .replace(/\[([0-9]+)]/, '.$1')
66
+ .split('.')
67
+ .map(
68
+ function (v) {
69
+ return this.parameter(v, builder, bindingsHolder);
70
+ }.bind(this)
71
+ )
72
+ .join(', ');
73
+ }
61
74
  }
62
75
 
63
76
  Object.assign(Client_CockroachDB.prototype, {
@@ -1,8 +1,6 @@
1
1
  // MSSQL Client
2
2
  // -------
3
- const flatten = require('lodash/flatten');
4
3
  const map = require('lodash/map');
5
- const values = require('lodash/values');
6
4
  const isNil = require('lodash/isNil');
7
5
 
8
6
  const Client = require('../../client');
@@ -472,15 +470,6 @@ class Client_MSSQL extends Client {
472
470
  if (query.returning === '@@rowcount') {
473
471
  return response[0][''];
474
472
  }
475
-
476
- if (
477
- (Array.isArray(query.returning) && query.returning.length > 1) ||
478
- query.returning[0] === '*'
479
- ) {
480
- return response;
481
- }
482
- // return an array with values if only one returning value was specified
483
- return flatten(map(response, values));
484
473
  }
485
474
  return response;
486
475
  default:
@@ -6,6 +6,9 @@ const compact = require('lodash/compact');
6
6
  const identity = require('lodash/identity');
7
7
  const isEmpty = require('lodash/isEmpty');
8
8
  const Raw = require('../../../raw.js');
9
+ const {
10
+ columnize: columnize_,
11
+ } = require('../../../formatter/wrappingFormatter');
9
12
 
10
13
  const components = [
11
14
  'columns',
@@ -100,40 +103,37 @@ class QueryCompiler_MSSQL extends QueryCompiler {
100
103
  returning,
101
104
  };
102
105
  }
106
+ sql += this._buildInsertData(insertValues, returningSql);
103
107
 
108
+ if (returning) {
109
+ sql += this._buildReturningSelect(returning);
110
+ }
111
+
112
+ return {
113
+ sql,
114
+ returning,
115
+ };
116
+ }
117
+
118
+ _buildInsertData(insertValues, returningSql) {
119
+ let sql = '';
104
120
  const insertData = this._prepInsert(insertValues);
105
121
  if (typeof insertData === 'string') {
106
122
  sql += insertData;
107
123
  } else {
108
124
  if (insertData.columns.length) {
109
125
  sql += `(${this.formatter.columnize(insertData.columns)}`;
110
- sql += `) ${returningSql}values (`;
111
- let i = -1;
112
- while (++i < insertData.values.length) {
113
- if (i !== 0) sql += '), (';
114
- sql += this.client.parameterize(
115
- insertData.values[i],
116
- this.client.valueForUndefined,
117
- this.builder,
118
- this.bindingsHolder
119
- );
120
- }
121
- sql += ')';
126
+ sql +=
127
+ `) ${returningSql}values (` +
128
+ this._buildInsertValues(insertData) +
129
+ ')';
122
130
  } else if (insertValues.length === 1 && insertValues[0]) {
123
131
  sql += returningSql + this._emptyInsertValue;
124
132
  } else {
125
- sql = '';
133
+ return '';
126
134
  }
127
135
  }
128
-
129
- if (returning) {
130
- sql += this._buildReturningSelect(returning);
131
- }
132
-
133
- return {
134
- sql,
135
- returning,
136
- };
136
+ return sql;
137
137
  }
138
138
 
139
139
  standardInsert() {
@@ -155,30 +155,7 @@ class QueryCompiler_MSSQL extends QueryCompiler {
155
155
  };
156
156
  }
157
157
 
158
- const insertData = this._prepInsert(insertValues);
159
- if (typeof insertData === 'string') {
160
- sql += insertData;
161
- } else {
162
- if (insertData.columns.length) {
163
- sql += `(${this.formatter.columnize(insertData.columns)}`;
164
- sql += `) ${returningSql}values (`;
165
- let i = -1;
166
- while (++i < insertData.values.length) {
167
- if (i !== 0) sql += '), (';
168
- sql += this.client.parameterize(
169
- insertData.values[i],
170
- this.client.valueForUndefined,
171
- this.builder,
172
- this.bindingsHolder
173
- );
174
- }
175
- sql += ')';
176
- } else if (insertValues.length === 1 && insertValues[0]) {
177
- sql += returningSql + this._emptyInsertValue;
178
- } else {
179
- sql = '';
180
- }
181
- }
158
+ sql += this._buildInsertData(insertValues, returningSql);
182
159
 
183
160
  return {
184
161
  sql,
@@ -354,15 +331,17 @@ class QueryCompiler_MSSQL extends QueryCompiler {
354
331
  sql.push(this.aggregateRaw(stmt));
355
332
  } else if (stmt.type === 'analytic') {
356
333
  sql.push(this.analytic(stmt));
334
+ } else if (stmt.type === 'json') {
335
+ sql.push(this.json(stmt));
357
336
  } else if (stmt.value && stmt.value.length > 0) {
358
337
  sql.push(this.formatter.columnize(stmt.value));
359
338
  }
360
339
  }
361
340
  }
362
341
  if (sql.length === 0) sql = ['*'];
363
-
342
+ const select = this.onlyJson() ? '' : 'select ';
364
343
  return (
365
- `select ${hints}${distinctClause}` +
344
+ `${select}${hints}${distinctClause}` +
366
345
  (top ? top + ' ' : '') +
367
346
  sql.join(', ') +
368
347
  (this.tableName ? ` from ${this.tableName}` : '')
@@ -502,11 +481,7 @@ class QueryCompiler_MSSQL extends QueryCompiler {
502
481
  const noLimit = !this.single.limit && this.single.limit !== 0;
503
482
  const noOffset = !this.single.offset;
504
483
  if (noLimit || !noOffset) return '';
505
- return `top (${this.client.parameter(
506
- this.single.limit,
507
- this.builder,
508
- this.bindingsHolder
509
- )})`;
484
+ return `top (${this._getValueOrParameterFromAttribute('limit')})`;
510
485
  }
511
486
 
512
487
  limit() {
@@ -518,23 +493,106 @@ class QueryCompiler_MSSQL extends QueryCompiler {
518
493
  const noOffset = !this.single.offset;
519
494
  if (noOffset) return '';
520
495
  let offset = `offset ${
521
- noOffset
522
- ? '0'
523
- : this.client.parameter(
524
- this.single.offset,
525
- this.builder,
526
- this.bindingsHolder
527
- )
496
+ noOffset ? '0' : this._getValueOrParameterFromAttribute('offset')
528
497
  } rows`;
529
498
  if (!noLimit) {
530
- offset += ` fetch next ${this.client.parameter(
531
- this.single.limit,
532
- this.builder,
533
- this.bindingsHolder
499
+ offset += ` fetch next ${this._getValueOrParameterFromAttribute(
500
+ 'limit'
534
501
  )} rows only`;
535
502
  }
536
503
  return offset;
537
504
  }
505
+
506
+ whereLike(statement) {
507
+ return `${this._columnClause(
508
+ statement
509
+ )} collate SQL_Latin1_General_CP1_CS_AS ${this._not(
510
+ statement,
511
+ 'like '
512
+ )}${this._valueClause(statement)}`;
513
+ }
514
+
515
+ whereILike(statement) {
516
+ return `${this._columnClause(
517
+ statement
518
+ )} collate SQL_Latin1_General_CP1_CI_AS ${this._not(
519
+ statement,
520
+ 'like '
521
+ )}${this._valueClause(statement)}`;
522
+ }
523
+
524
+ jsonExtract(params) {
525
+ // JSON_VALUE return NULL if we query object or array
526
+ // JSON_QUERY return NULL if we query literal/single value
527
+ return this._jsonExtract(
528
+ params.singleValue ? 'JSON_VALUE' : 'JSON_QUERY',
529
+ params
530
+ );
531
+ }
532
+
533
+ jsonSet(params) {
534
+ return this._jsonSet('JSON_MODIFY', params);
535
+ }
536
+
537
+ jsonInsert(params) {
538
+ return this._jsonSet('JSON_MODIFY', params);
539
+ }
540
+
541
+ jsonRemove(params) {
542
+ const jsonCol = `JSON_MODIFY(${columnize_(
543
+ params.column,
544
+ this.builder,
545
+ this.client,
546
+ this.bindingsHolder
547
+ )},${this.client.parameter(
548
+ params.path,
549
+ this.builder,
550
+ this.bindingsHolder
551
+ )}, NULL)`;
552
+ return params.alias
553
+ ? this.client.alias(jsonCol, this.formatter.wrap(params.alias))
554
+ : jsonCol;
555
+ }
556
+
557
+ whereJsonPath(statement) {
558
+ return this._whereJsonPath('JSON_VALUE', statement);
559
+ }
560
+
561
+ whereJsonSupersetOf(statement) {
562
+ throw new Error(
563
+ 'Json superset where clause not actually supported by MSSQL'
564
+ );
565
+ }
566
+
567
+ whereJsonSubsetOf(statement) {
568
+ throw new Error('Json subset where clause not actually supported by MSSQL');
569
+ }
570
+
571
+ _getExtracts(statement, operator) {
572
+ const column = columnize_(
573
+ statement.column,
574
+ this.builder,
575
+ this.client,
576
+ this.bindingsHolder
577
+ );
578
+ return (
579
+ Array.isArray(statement.values) ? statement.values : [statement.values]
580
+ )
581
+ .map(function (value) {
582
+ return (
583
+ 'JSON_VALUE(' +
584
+ column +
585
+ ',' +
586
+ this.client.parameter(value, this.builder, this.bindingsHolder) +
587
+ ')'
588
+ );
589
+ }, this)
590
+ .join(operator);
591
+ }
592
+
593
+ onJsonPathEquals(clause) {
594
+ return this._onJsonPathEquals('JSON_VALUE', clause);
595
+ }
538
596
  }
539
597
 
540
598
  // Set the QueryBuilder & QueryCompiler on the client object,