spice-couchbase 2.0.63 → 2.0.65

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.
@@ -1,4 +1,5 @@
1
1
  "use strict";
2
+
2
3
  /**
3
4
  * Regression tests for the WHERE clause shared by the data and count queries in `search()`.
4
5
  *
@@ -9,19 +10,14 @@
9
10
  *
10
11
  * It survived the existing suite because nothing exercised the count statement.
11
12
  */
12
-
13
- function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { Promise.resolve(value).then(_next, _throw); } }
14
-
15
- function _asyncToGenerator(fn) { return function () { var self = this, args = arguments; return new Promise(function (resolve, reject) { var gen = fn.apply(self, args); function _next(value) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "next", value); } function _throw(err) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "throw", err); } _next(undefined); }); }; }
16
-
13
+ function asyncGeneratorStep(n, t, e, r, o, a, c) { try { var i = n[a](c), u = i.value; } catch (n) { return void e(n); } i.done ? t(u) : Promise.resolve(u).then(r, o); }
14
+ function _asyncToGenerator(n) { return function () { var t = this, e = arguments; return new Promise(function (r, o) { var a = n.apply(t, e); function _next(n) { asyncGeneratorStep(a, r, o, _next, _throw, "next", n); } function _throw(n) { asyncGeneratorStep(a, r, o, _next, _throw, "throw", n); } _next(void 0); }); }; }
17
15
  var assert = require("assert");
18
-
19
16
  var Database = require("./index");
20
-
21
17
  var passed = 0;
22
18
  var failed = 0;
23
- /** Captures the two statements `search()` produces without touching a cluster. */
24
19
 
20
+ /** Captures the two statements `search()` produces without touching a cluster. */
25
21
  function createSearchDb() {
26
22
  var statements = {
27
23
  data: null,
@@ -32,45 +28,33 @@ function createSearchDb() {
32
28
  db.bucket = "test";
33
29
  db.scope_name = "scope";
34
30
  db.collection_name = "platformtask";
35
-
36
- db.getData =
37
- /*#__PURE__*/
38
- function () {
31
+ db.getData = /*#__PURE__*/function () {
39
32
  var _ref = _asyncToGenerator(function* (statement) {
40
33
  statements.data = statement;
41
34
  return [];
42
35
  });
43
-
44
36
  return function (_x) {
45
37
  return _ref.apply(this, arguments);
46
38
  };
47
39
  }();
48
-
49
- db.getCount =
50
- /*#__PURE__*/
51
- function () {
40
+ db.getCount = /*#__PURE__*/function () {
52
41
  var _ref2 = _asyncToGenerator(function* (statement) {
53
42
  statements.count = statement;
54
43
  return 0;
55
44
  });
56
-
57
45
  return function (_x2) {
58
46
  return _ref2.apply(this, arguments);
59
47
  };
60
48
  }();
61
-
62
49
  db.optimize = () => {};
63
-
64
50
  return {
65
51
  db,
66
52
  statements
67
53
  };
68
54
  }
69
-
70
55
  function test(_x3, _x4) {
71
56
  return _test.apply(this, arguments);
72
57
  }
73
-
74
58
  function _test() {
75
59
  _test = _asyncToGenerator(function* (name, fn) {
76
60
  try {
@@ -85,69 +69,50 @@ function _test() {
85
69
  });
86
70
  return _test.apply(this, arguments);
87
71
  }
88
-
89
72
  function run() {
90
73
  return _run.apply(this, arguments);
91
74
  }
92
-
93
75
  function _run() {
94
76
  _run = _asyncToGenerator(function* () {
95
77
  console.log("\n--- count query tests ---");
96
- yield test("the count query carries the predicates, not a dangling WHERE",
97
- /*#__PURE__*/
98
- _asyncToGenerator(function* () {
99
- var {
100
- db,
101
- statements
102
- } = createSearchDb();
78
+ yield test("the count query carries the predicates, not a dangling WHERE", /*#__PURE__*/_asyncToGenerator(function* () {
79
+ var _createSearchDb = createSearchDb(),
80
+ db = _createSearchDb.db,
81
+ statements = _createSearchDb.statements;
103
82
  yield db.search("platformtask", "id", "status = \"pending\"", 1, 0, null, "true");
104
83
  assert.ok(statements.count.includes("status = \"pending\""), "count query lost its predicate: ".concat(statements.count));
105
84
  assert.ok(!/WHERE\s*$/.test(statements.count.trim()), "count query ends in a dangling WHERE: ".concat(statements.count));
106
85
  }));
107
- yield test("multiple conditions are joined with AND in the count query",
108
- /*#__PURE__*/
109
- _asyncToGenerator(function* () {
110
- var {
111
- db,
112
- statements
113
- } = createSearchDb();
86
+ yield test("multiple conditions are joined with AND in the count query", /*#__PURE__*/_asyncToGenerator(function* () {
87
+ var _createSearchDb2 = createSearchDb(),
88
+ db = _createSearchDb2.db,
89
+ statements = _createSearchDb2.statements;
114
90
  yield db.search("platformtask", "id", "status = \"pending\" AND assignee_type = \"user\"", 1, 0, null, "true");
115
91
  assert.ok(statements.count.includes("status = \"pending\""));
116
92
  assert.ok(statements.count.includes("assignee_type = \"user\""));
117
93
  }));
118
- yield test("an unfiltered count emits no WHERE at all",
119
- /*#__PURE__*/
120
- _asyncToGenerator(function* () {
121
- var {
122
- db,
123
- statements
124
- } = createSearchDb();
94
+ yield test("an unfiltered count emits no WHERE at all", /*#__PURE__*/_asyncToGenerator(function* () {
95
+ var _createSearchDb3 = createSearchDb(),
96
+ db = _createSearchDb3.db,
97
+ statements = _createSearchDb3.statements;
125
98
  yield db.search("platformtask", "id", "", 1, 0, null, "true");
126
99
  assert.ok(!/WHERE/i.test(statements.count), "unfiltered count should have no WHERE: ".concat(statements.count));
127
100
  }));
128
- yield test("the data and count queries agree on the WHERE clause",
129
- /*#__PURE__*/
130
- _asyncToGenerator(function* () {
131
- var {
132
- db,
133
- statements
134
- } = createSearchDb();
101
+ yield test("the data and count queries agree on the WHERE clause", /*#__PURE__*/_asyncToGenerator(function* () {
102
+ var _createSearchDb4 = createSearchDb(),
103
+ db = _createSearchDb4.db,
104
+ statements = _createSearchDb4.statements;
135
105
  yield db.search("platformtask", "id", "status = \"blocked\"", 1, 0, null, "true");
136
-
137
106
  var whereOf = sql => {
138
- var m = sql.match(/WHERE[\t-\r \xA0\u1680\u2000-\u200A\u2028\u2029\u202F\u205F\u3000\uFEFF]+([\s\S]*?)(?:[\t-\r \xA0\u1680\u2000-\u200A\u2028\u2029\u202F\u205F\u3000\uFEFF]+ORDER BY|[\t-\r \xA0\u1680\u2000-\u200A\u2028\u2029\u202F\u205F\u3000\uFEFF]+LIMIT|[\t-\r \xA0\u1680\u2000-\u200A\u2028\u2029\u202F\u205F\u3000\uFEFF]+OFFSET|$)/i);
107
+ var m = sql.match(/WHERE\s+([^]*?)(?:\s+ORDER BY|\s+LIMIT|\s+OFFSET|$)/i);
139
108
  return m ? m[1].trim() : null;
140
109
  };
141
-
142
110
  assert.strictEqual(whereOf(statements.data), whereOf(statements.count));
143
111
  }));
144
- yield test("no count statement is produced when do_count is off",
145
- /*#__PURE__*/
146
- _asyncToGenerator(function* () {
147
- var {
148
- db,
149
- statements
150
- } = createSearchDb();
112
+ yield test("no count statement is produced when do_count is off", /*#__PURE__*/_asyncToGenerator(function* () {
113
+ var _createSearchDb5 = createSearchDb(),
114
+ db = _createSearchDb5.db,
115
+ statements = _createSearchDb5.statements;
151
116
  yield db.search("platformtask", "id", "status = \"pending\"", 1, 0, null, "false");
152
117
  assert.strictEqual(statements.count, null);
153
118
  assert.ok(statements.data.includes("status = \"pending\""));
@@ -159,5 +124,4 @@ function _run() {
159
124
  });
160
125
  return _run.apply(this, arguments);
161
126
  }
162
-
163
127
  run();
@@ -0,0 +1,283 @@
1
+ "use strict";
2
+
3
+ function asyncGeneratorStep(n, t, e, r, o, a, c) { try { var i = n[a](c), u = i.value; } catch (n) { return void e(n); } i.done ? t(u) : Promise.resolve(u).then(r, o); }
4
+ function _asyncToGenerator(n) { return function () { var t = this, e = arguments; return new Promise(function (r, o) { var a = n.apply(t, e); function _next(n) { asyncGeneratorStep(a, r, o, _next, _throw, "next", n); } function _throw(n) { asyncGeneratorStep(a, r, o, _next, _throw, "throw", n); } _next(void 0); }); }; }
5
+ var assert = require("node:assert/strict");
6
+ var fs = require("node:fs");
7
+ var path = require("node:path");
8
+ var vm = require("node:vm");
9
+ var _require = require("node:test"),
10
+ test = _require.test;
11
+ var _require2 = require("./utility"),
12
+ statementShape = _require2.statementShape,
13
+ planIndexNames = _require2.planIndexNames,
14
+ usesManagedIndex = _require2.usesManagedIndex,
15
+ renderIndexKey = _require2.renderIndexKey,
16
+ BASE_INDEX = _require2.BASE_INDEX;
17
+
18
+ // Same isolation as index.auto-optimize.test.js: a fresh module per load, so
19
+ // the seen-set, cooldown and handler never leak between tests.
20
+ function loadDriver(durationMs) {
21
+ var env = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
22
+ var tick = BigInt(0);
23
+ var module = {
24
+ exports: {}
25
+ };
26
+ vm.runInNewContext(fs.readFileSync(path.join(__dirname, "index.js"), "utf8"), {
27
+ require,
28
+ module,
29
+ console,
30
+ process: {
31
+ env,
32
+ hrtime: {
33
+ bigint: () => {
34
+ var current = tick;
35
+ tick += BigInt(durationMs) * BigInt(1000000);
36
+ return current;
37
+ }
38
+ }
39
+ }
40
+ });
41
+ return module.exports;
42
+ }
43
+ function recordingCluster() {
44
+ var rowsFor = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : () => [];
45
+ var calls = [];
46
+ return {
47
+ calls,
48
+ query(statement, options) {
49
+ return _asyncToGenerator(function* () {
50
+ calls.push({
51
+ statement,
52
+ options
53
+ });
54
+ return {
55
+ rows: rowsFor(statement),
56
+ meta: {}
57
+ };
58
+ })();
59
+ }
60
+ };
61
+ }
62
+ function createDatabase(Database, cluster) {
63
+ var db = Object.create(Database.prototype);
64
+ db.bucket = "saturn";
65
+ db.scope_name = "test";
66
+ db.collection_name = "users";
67
+ db.db_config = {
68
+ bucket: "saturn"
69
+ };
70
+ db.auto_optimize = true;
71
+ db.cluster = cluster;
72
+ db.scope = cluster;
73
+ return db;
74
+ }
75
+ var KEYSPACE = "default:`saturn`.`test`.`users`";
76
+ test("statement shapes ignore literal values and IN list lengths", () => {
77
+ assert.equal(statementShape("SELECT * FROM `users` WHERE email = 'a@b.c' AND age > 30 AND id IN ['x', 'y']"), statementShape("SELECT * FROM `users` WHERE email = \"d@e.f\" AND age > 7 AND id IN ['z']"));
78
+ });
79
+ test("managed indexes are recognised in a plan, the base fallback is not", () => {
80
+ var plan = index => ({
81
+ "#operator": "Sequence",
82
+ "~children": [{
83
+ "#operator": "IndexScan3",
84
+ index
85
+ }]
86
+ });
87
+ assert.deepEqual(planIndexNames(plan("ix_sort_1a2b3c_9f0e")), ["ix_sort_1a2b3c_9f0e"]);
88
+ assert.equal(usesManagedIndex(plan("ix_sort_1a2b3c_9f0e")), true);
89
+ assert.equal(usesManagedIndex(plan("ix_base")), false);
90
+ assert.equal(usesManagedIndex(plan("adv_covered_1a3952f8")), false);
91
+ });
92
+ test("index keys render direction and INCLUDE MISSING", () => {
93
+ assert.deepEqual(BASE_INDEX.keys.map(renderIndexKey), ["`created_at` DESC INCLUDE MISSING", "`deleted`"]);
94
+ assert.equal(renderIndexKey("`name`"), "`name`");
95
+ assert.throws(() => renderIndexKey({}), TypeError);
96
+ });
97
+ test("createIndex is idempotent, deferred by default and fully qualified", /*#__PURE__*/_asyncToGenerator(function* () {
98
+ var cluster = recordingCluster();
99
+ var db = createDatabase(loadDriver(0), cluster);
100
+ yield db.createIndex({
101
+ name: "ix_sort_1a2b3c_9f0e",
102
+ keys: [{
103
+ expr: "`priority`",
104
+ desc: true,
105
+ include_missing: true
106
+ }, {
107
+ expr: "IFMISSINGORNULL(`due_at`, {})"
108
+ }],
109
+ where: "IFMISSING(`deleted`, false) = false",
110
+ num_replica: 1
111
+ });
112
+ assert.equal(cluster.calls[0].statement, "CREATE INDEX IF NOT EXISTS `ix_sort_1a2b3c_9f0e` ON ".concat(KEYSPACE) + "(`priority` DESC INCLUDE MISSING, IFMISSINGORNULL(`due_at`, {})) " + 'WHERE IFMISSING(`deleted`, false) = false WITH {"defer_build":true,"num_replica":1}');
113
+ }));
114
+ test("build, drop and primary drop target the collection", /*#__PURE__*/_asyncToGenerator(function* () {
115
+ var cluster = recordingCluster();
116
+ var db = createDatabase(loadDriver(0), cluster);
117
+ yield db.buildIndexes(["ix_a", "ix_b", "ix_a"]);
118
+ yield db.dropIndex("ix_a");
119
+ yield db.dropPrimaryIndex();
120
+ assert.deepEqual(cluster.calls.map(_ref3 => {
121
+ var statement = _ref3.statement;
122
+ return statement;
123
+ }), ["BUILD INDEX ON ".concat(KEYSPACE, "(`ix_a`, `ix_b`)"), "DROP INDEX IF EXISTS `ix_a` ON ".concat(KEYSPACE), "DROP PRIMARY INDEX IF EXISTS ON ".concat(KEYSPACE)]);
124
+ assert.equal(yield db.buildIndexes([]), null);
125
+ }));
126
+ test("a partial index inventory is an error, not an empty list", /*#__PURE__*/_asyncToGenerator(function* () {
127
+ var Database = loadDriver(0);
128
+ var db = createDatabase(Database, {
129
+ query() {
130
+ return _asyncToGenerator(function* () {
131
+ return {
132
+ rows: [],
133
+ meta: {
134
+ warnings: [{
135
+ msg: "partial"
136
+ }]
137
+ }
138
+ };
139
+ })();
140
+ }
141
+ });
142
+ yield assert.rejects(() => db.listIndexes(), /index inventory/);
143
+ }));
144
+ test("the client context id reaches the SDK options", /*#__PURE__*/_asyncToGenerator(function* () {
145
+ var cluster = recordingCluster();
146
+ var db = createDatabase(loadDriver(0), cluster);
147
+ yield db.query("SELECT 1", "cluster", {
148
+ client_context_id: "scheduler.due"
149
+ });
150
+ assert.equal(cluster.calls[0].options.clientContextId, "scheduler.due");
151
+ }));
152
+ function scanTimeout() {
153
+ var error = new Error("Index scan timed out");
154
+ error.cause = {
155
+ first_error_code: 12015,
156
+ first_error_message: "Index scan timed out"
157
+ };
158
+ return error;
159
+ }
160
+ test("an expired scan_wait reruns without waiting only when the caller allows it", /*#__PURE__*/_asyncToGenerator(function* () {
161
+ var Database = loadDriver(0);
162
+ var attempts = [];
163
+ var cluster = {
164
+ query(statement, options) {
165
+ return _asyncToGenerator(function* () {
166
+ attempts.push(options);
167
+ if (attempts.length === 1) throw scanTimeout();
168
+ return {
169
+ rows: [{
170
+ ok: true
171
+ }]
172
+ };
173
+ })();
174
+ }
175
+ };
176
+ var db = createDatabase(Database, cluster);
177
+ var response = yield db.query("SELECT 1", "cluster", {
178
+ scan_consistency: "request_plus",
179
+ scan_wait: 2000,
180
+ scan_wait_fallback: true
181
+ });
182
+ assert.deepEqual(response.rows, [{
183
+ ok: true
184
+ }]);
185
+ assert.equal(attempts.length, 2);
186
+ assert.equal(attempts[1].scanWait, undefined);
187
+ assert.notEqual(attempts[1].scanConsistency, attempts[0].scanConsistency);
188
+ }));
189
+ test("without the fallback an expired scan_wait is an error", /*#__PURE__*/_asyncToGenerator(function* () {
190
+ var Database = loadDriver(0);
191
+ var db = createDatabase(Database, {
192
+ query() {
193
+ return _asyncToGenerator(function* () {
194
+ throw scanTimeout();
195
+ })();
196
+ }
197
+ });
198
+ yield assert.rejects(() => db.query("SELECT 1", "cluster", {
199
+ scan_consistency: "request_plus",
200
+ scan_wait: 2000
201
+ }), /Index scan timed out/);
202
+ }));
203
+ test("with a handler installed, slow statements are handed off once per shape and never indexed here", /*#__PURE__*/_asyncToGenerator(function* () {
204
+ var Database = loadDriver(5000);
205
+ var cluster = recordingCluster(statement => statement.startsWith("EXPLAIN ") ? [{
206
+ "#operator": "IndexScan3",
207
+ index: "ix_base"
208
+ }] : []);
209
+ var db = createDatabase(Database, cluster);
210
+ var reports = [];
211
+ Database.setSlowStatementHandler(/*#__PURE__*/function () {
212
+ var _ref9 = _asyncToGenerator(function* (report) {
213
+ return reports.push(report);
214
+ });
215
+ return function (_x) {
216
+ return _ref9.apply(this, arguments);
217
+ };
218
+ }());
219
+ var legacy = 0;
220
+ db.optimize = /*#__PURE__*/_asyncToGenerator(function* () {
221
+ legacy += 1;
222
+ });
223
+ yield db.query("SELECT name FROM users WHERE email = 'a@b.c'");
224
+ yield db.query("SELECT name FROM users WHERE email = 'd@e.f'");
225
+ yield new Promise(resolve => setImmediate(resolve));
226
+ assert.equal(reports.length, 1);
227
+ assert.equal(reports[0].shape, "SELECT name FROM users WHERE email = ?");
228
+ assert.equal(reports[0].collection, "users");
229
+ assert.equal(legacy, 0);
230
+ assert.equal(cluster.calls.some(_ref1 => {
231
+ var statement = _ref1.statement;
232
+ return statement.startsWith("CREATE ");
233
+ }), false);
234
+ }));
235
+ test("a slow statement already served by a managed index is not reported", /*#__PURE__*/_asyncToGenerator(function* () {
236
+ var Database = loadDriver(5000);
237
+ var cluster = recordingCluster(statement => statement.startsWith("EXPLAIN ") ? [{
238
+ "#operator": "IndexScan3",
239
+ index: "ix_sort_1a2b3c_9f0e"
240
+ }] : []);
241
+ var db = createDatabase(Database, cluster);
242
+ var reports = [];
243
+ Database.setSlowStatementHandler(/*#__PURE__*/function () {
244
+ var _ref11 = _asyncToGenerator(function* (report) {
245
+ return reports.push(report);
246
+ });
247
+ return function (_x2) {
248
+ return _ref11.apply(this, arguments);
249
+ };
250
+ }());
251
+ yield db.query("SELECT name FROM users ORDER BY priority DESC");
252
+ yield new Promise(resolve => setImmediate(resolve));
253
+ assert.equal(reports.length, 0);
254
+ }));
255
+ test("without a handler the legacy covering-index path still runs", /*#__PURE__*/_asyncToGenerator(function* () {
256
+ var Database = loadDriver(5000);
257
+ var db = createDatabase(Database, recordingCluster());
258
+ var legacy = 0;
259
+ db.optimize = /*#__PURE__*/_asyncToGenerator(function* () {
260
+ legacy += 1;
261
+ });
262
+ yield db.query("SELECT name FROM users WHERE status = 'active'");
263
+ assert.equal(legacy, 1);
264
+ }));
265
+ test("a new collection gets the base index instead of a primary index when asked", /*#__PURE__*/_asyncToGenerator(function* () {
266
+ var Database = loadDriver(0);
267
+ var cluster = recordingCluster();
268
+ var db = createDatabase(Database, cluster);
269
+ db.db_config.primary_index = false;
270
+ db.checkGlobelConnectionObject = () => false;
271
+ db.collectionExists = /*#__PURE__*/_asyncToGenerator(function* () {
272
+ return false;
273
+ });
274
+ db.createCollection = /*#__PURE__*/_asyncToGenerator(function* () {});
275
+ db.wait = /*#__PURE__*/_asyncToGenerator(function* () {});
276
+ var primary = 0;
277
+ db.createPrimaryIndex = /*#__PURE__*/_asyncToGenerator(function* () {
278
+ primary += 1;
279
+ });
280
+ yield db.upsertCollection();
281
+ assert.equal(primary, 0);
282
+ assert.match(cluster.calls[0].statement, /^CREATE INDEX IF NOT EXISTS `ix_base` ON .*\(`created_at` DESC INCLUDE MISSING, `deleted`\) WITH \{"defer_build":false\}$/);
283
+ }));