@ragestudio/scylla-odm 0.22.2 → 0.22.3

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 (153) hide show
  1. package/batch/index.d.ts +3 -3
  2. package/batch/index.d.ts.map +1 -1
  3. package/client.d.ts +6 -5
  4. package/client.d.ts.map +1 -1
  5. package/client.js +7 -7
  6. package/client.js.map +1 -1
  7. package/cql_gen/create_table.d.ts +1 -1
  8. package/cql_gen/create_table.d.ts.map +1 -1
  9. package/document/index.d.ts +3 -3
  10. package/document/index.d.ts.map +1 -1
  11. package/driver/LICENSE.txt +177 -0
  12. package/driver/NOTICE.txt +67 -0
  13. package/driver/auth/index.d.ts +37 -0
  14. package/driver/auth/index.js +37 -0
  15. package/driver/auth/no-auth-provider.js +73 -0
  16. package/driver/auth/plain-text-auth-provider.js +81 -0
  17. package/driver/auth/provider.js +77 -0
  18. package/driver/client-options.js +442 -0
  19. package/driver/client.js +1267 -0
  20. package/driver/concurrent/index.d.ts +49 -0
  21. package/driver/concurrent/index.js +366 -0
  22. package/driver/connection.js +1034 -0
  23. package/driver/control-connection.js +1282 -0
  24. package/driver/encoder.js +2316 -0
  25. package/driver/errors.js +223 -0
  26. package/driver/execution-options.js +612 -0
  27. package/driver/execution-profile.js +274 -0
  28. package/driver/host-connection-pool.js +587 -0
  29. package/driver/host.js +699 -0
  30. package/driver/index.d.ts +387 -0
  31. package/driver/index.js +81 -0
  32. package/driver/mapping/cache.js +214 -0
  33. package/driver/mapping/doc-info-adapter.js +171 -0
  34. package/driver/mapping/index.d.ts +219 -0
  35. package/driver/mapping/index.js +57 -0
  36. package/driver/mapping/mapper.js +225 -0
  37. package/driver/mapping/mapping-handler.js +641 -0
  38. package/driver/mapping/model-batch-item.js +215 -0
  39. package/driver/mapping/model-batch-mapper.js +141 -0
  40. package/driver/mapping/model-mapper.js +315 -0
  41. package/driver/mapping/model-mapping-info.js +225 -0
  42. package/driver/mapping/object-selector.js +417 -0
  43. package/driver/mapping/q.js +156 -0
  44. package/driver/mapping/query-generator.js +556 -0
  45. package/driver/mapping/result-mapper.js +123 -0
  46. package/driver/mapping/result.js +139 -0
  47. package/driver/mapping/table-mappings.js +133 -0
  48. package/driver/mapping/tree.js +160 -0
  49. package/driver/metadata/aggregate.js +79 -0
  50. package/driver/metadata/client-state.js +119 -0
  51. package/driver/metadata/data-collection.js +182 -0
  52. package/driver/metadata/event-debouncer.js +174 -0
  53. package/driver/metadata/index.d.ts +276 -0
  54. package/driver/metadata/index.js +1156 -0
  55. package/driver/metadata/materialized-view.js +49 -0
  56. package/driver/metadata/schema-function.js +98 -0
  57. package/driver/metadata/schema-index.js +166 -0
  58. package/driver/metadata/schema-parser.js +1399 -0
  59. package/driver/metadata/table-metadata.js +77 -0
  60. package/driver/operation-state.js +206 -0
  61. package/driver/policies/address-resolution.js +145 -0
  62. package/driver/policies/index.d.ts +241 -0
  63. package/driver/policies/index.js +110 -0
  64. package/driver/policies/load-balancing.js +970 -0
  65. package/driver/policies/reconnection.js +166 -0
  66. package/driver/policies/retry.js +326 -0
  67. package/driver/policies/speculative-execution.js +150 -0
  68. package/driver/policies/timestamp-generation.js +176 -0
  69. package/driver/prepare-handler.js +347 -0
  70. package/driver/promise-utils.js +191 -0
  71. package/driver/readers.js +624 -0
  72. package/driver/request-execution.js +644 -0
  73. package/driver/request-handler.js +332 -0
  74. package/driver/requests.js +618 -0
  75. package/driver/stream-id-stack.js +209 -0
  76. package/driver/streams.js +745 -0
  77. package/driver/token.js +325 -0
  78. package/driver/tokenizer.js +631 -0
  79. package/driver/types/big-decimal.js +282 -0
  80. package/driver/types/duration.js +576 -0
  81. package/driver/types/index.d.ts +486 -0
  82. package/driver/types/index.js +733 -0
  83. package/driver/types/inet-address.js +262 -0
  84. package/driver/types/integer.js +818 -0
  85. package/driver/types/local-date.js +280 -0
  86. package/driver/types/local-time.js +299 -0
  87. package/driver/types/mutable-long.js +385 -0
  88. package/driver/types/protocol-version.js +391 -0
  89. package/driver/types/result-set.js +287 -0
  90. package/driver/types/result-stream.js +164 -0
  91. package/driver/types/row.js +85 -0
  92. package/driver/types/time-uuid.js +414 -0
  93. package/driver/types/tuple.js +103 -0
  94. package/driver/types/uuid.js +160 -0
  95. package/driver/types/vector.js +130 -0
  96. package/driver/types/version-number.js +153 -0
  97. package/driver/utils.js +1485 -0
  98. package/driver/writers.js +350 -0
  99. package/global.d.ts +1 -1
  100. package/global.d.ts.map +1 -1
  101. package/index.d.ts +6 -6
  102. package/index.d.ts.map +1 -1
  103. package/index.js +6 -6
  104. package/index.js.map +1 -1
  105. package/migrate/index.d.ts +1 -1
  106. package/migrate/index.d.ts.map +1 -1
  107. package/migrate/index.js +1 -1
  108. package/migrate/index.js.map +1 -1
  109. package/model/index.d.ts +6 -6
  110. package/model/index.d.ts.map +1 -1
  111. package/model/index.js +10 -10
  112. package/model/index.js.map +1 -1
  113. package/operations/countAll.d.ts +1 -1
  114. package/operations/countAll.d.ts.map +1 -1
  115. package/operations/delete.d.ts +3 -4
  116. package/operations/delete.d.ts.map +1 -1
  117. package/operations/delete.js +1 -1
  118. package/operations/delete.js.map +1 -1
  119. package/operations/find.d.ts +2 -2
  120. package/operations/find.d.ts.map +1 -1
  121. package/operations/find.js +1 -1
  122. package/operations/find.js.map +1 -1
  123. package/operations/findOne.d.ts +2 -2
  124. package/operations/findOne.d.ts.map +1 -1
  125. package/operations/findOne.js +1 -1
  126. package/operations/findOne.js.map +1 -1
  127. package/operations/insert.d.ts +3 -3
  128. package/operations/insert.d.ts.map +1 -1
  129. package/operations/insert.js +2 -2
  130. package/operations/insert.js.map +1 -1
  131. package/operations/sync.d.ts +1 -1
  132. package/operations/sync.d.ts.map +1 -1
  133. package/operations/sync.js +1 -1
  134. package/operations/sync.js.map +1 -1
  135. package/operations/tableExists.d.ts +1 -1
  136. package/operations/tableExists.d.ts.map +1 -1
  137. package/operations/update.d.ts +3 -3
  138. package/operations/update.d.ts.map +1 -1
  139. package/operations/update.js +2 -2
  140. package/operations/update.js.map +1 -1
  141. package/package.json +4 -12
  142. package/schema/index.d.ts +1 -1
  143. package/schema/index.d.ts.map +1 -1
  144. package/types.d.ts +4 -4
  145. package/types.d.ts.map +1 -1
  146. package/utils/queryParser.d.ts +1 -1
  147. package/utils/queryParser.d.ts.map +1 -1
  148. package/utils/queryParser.js +1 -1
  149. package/utils/queryParser.js.map +1 -1
  150. package/utils/typeChecker.d.ts +1 -1
  151. package/utils/typeChecker.d.ts.map +1 -1
  152. package/utils/typeChecker.js +1 -1
  153. package/utils/typeChecker.js.map +1 -1
@@ -0,0 +1,1399 @@
1
+ /*
2
+ * Licensed to the Apache Software Foundation (ASF) under one
3
+ * or more contributor license agreements. See the NOTICE file
4
+ * distributed with this work for additional information
5
+ * regarding copyright ownership. The ASF licenses this file
6
+ * to you under the Apache License, Version 2.0 (the
7
+ * "License"); you may not use this file except in compliance
8
+ * with the License. You may obtain a copy of the License at
9
+ *
10
+ * http://www.apache.org/licenses/LICENSE-2.0
11
+ *
12
+ * Unless required by applicable law or agreed to in writing, software
13
+ * distributed under the License is distributed on an "AS IS" BASIS,
14
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ * See the License for the specific language governing permissions and
16
+ * limitations under the License.
17
+ */
18
+
19
+ import util from "util"
20
+ import events from "events"
21
+ import types from "../types/index.js"
22
+ import utils from "../utils.js"
23
+ import errors from "../errors.js"
24
+ import promiseUtils from "../promise-utils.js"
25
+ import TableMetadata from "./table-metadata.js"
26
+ import Aggregate from "./aggregate.js"
27
+ import SchemaFunction from "./schema-function.js"
28
+ import Index from "./schema-index.js"
29
+ import MaterializedView from "./materialized-view.js"
30
+ const { format } = util
31
+
32
+ /**
33
+ * @module metadata/schemaParser
34
+ * @ignore
35
+ */
36
+
37
+ const _selectAllKeyspacesV1 = "SELECT * FROM system.schema_keyspaces"
38
+ const _selectSingleKeyspaceV1 =
39
+ "SELECT * FROM system.schema_keyspaces where keyspace_name = '%s'"
40
+ const _selectAllKeyspacesV2 = "SELECT * FROM system_schema.keyspaces"
41
+ const _selectSingleKeyspaceV2 =
42
+ "SELECT * FROM system_schema.keyspaces where keyspace_name = '%s'"
43
+ const _selectTableV1 =
44
+ "SELECT * FROM system.schema_columnfamilies WHERE keyspace_name='%s' AND columnfamily_name='%s'"
45
+ const _selectTableV2 =
46
+ "SELECT * FROM system_schema.tables WHERE keyspace_name='%s' AND table_name='%s'"
47
+ const _selectColumnsV1 =
48
+ "SELECT * FROM system.schema_columns WHERE keyspace_name='%s' AND columnfamily_name='%s'"
49
+ const _selectColumnsV2 =
50
+ "SELECT * FROM system_schema.columns WHERE keyspace_name='%s' AND table_name='%s'"
51
+ const _selectIndexesV2 =
52
+ "SELECT * FROM system_schema.indexes WHERE keyspace_name='%s' AND table_name='%s'"
53
+ const _selectUdtV1 =
54
+ "SELECT * FROM system.schema_usertypes WHERE keyspace_name='%s' AND type_name='%s'"
55
+ const _selectUdtV2 =
56
+ "SELECT * FROM system_schema.types WHERE keyspace_name='%s' AND type_name='%s'"
57
+ const _selectFunctionsV1 =
58
+ "SELECT * FROM system.schema_functions WHERE keyspace_name = '%s' AND function_name = '%s'"
59
+ const _selectFunctionsV2 =
60
+ "SELECT * FROM system_schema.functions WHERE keyspace_name = '%s' AND function_name = '%s'"
61
+ const _selectAggregatesV1 =
62
+ "SELECT * FROM system.schema_aggregates WHERE keyspace_name = '%s' AND aggregate_name = '%s'"
63
+ const _selectAggregatesV2 =
64
+ "SELECT * FROM system_schema.aggregates WHERE keyspace_name = '%s' AND aggregate_name = '%s'"
65
+ const _selectMaterializedViewV2 =
66
+ "SELECT * FROM system_schema.views WHERE keyspace_name = '%s' AND view_name = '%s'"
67
+
68
+ const _selectAllVirtualKeyspaces =
69
+ "SELECT * FROM system_virtual_schema.keyspaces"
70
+ const _selectSingleVirtualKeyspace =
71
+ "SELECT * FROM system_virtual_schema.keyspaces where keyspace_name = '%s'"
72
+ const _selectVirtualTable =
73
+ "SELECT * FROM system_virtual_schema.tables where keyspace_name = '%s' and table_name='%s'"
74
+ const _selectVirtualColumns =
75
+ "SELECT * FROM system_virtual_schema.columns where keyspace_name = '%s' and table_name='%s'"
76
+
77
+ /**
78
+ * @abstract
79
+ * @param {ClientOptions} options The client options
80
+ * @param {ControlConnection} cc
81
+ * @constructor
82
+ * @ignore
83
+ */
84
+ class SchemaParser {
85
+ constructor(options, cc) {
86
+ this.cc = cc
87
+ this.encodingOptions = options.encoding
88
+ this.selectTable = null
89
+ this.selectColumns = null
90
+ this.selectIndexes = null
91
+ this.selectUdt = null
92
+ this.selectAggregates = null
93
+ this.selectFunctions = null
94
+ this.supportsVirtual = false
95
+ }
96
+
97
+ /**
98
+ * @param name
99
+ * @param durableWrites
100
+ * @param strategy
101
+ * @param strategyOptions
102
+ * @param virtual
103
+ * @returns {{name, durableWrites, strategy, strategyOptions, tokenToReplica, udts, tables, functions, aggregates}}
104
+ */
105
+ _createKeyspace(name, durableWrites, strategy, strategyOptions, virtual) {
106
+ return {
107
+ name,
108
+ durableWrites,
109
+ strategy,
110
+ strategyOptions,
111
+ virtual: virtual === true,
112
+ udts: {},
113
+ tables: {},
114
+ functions: {},
115
+ aggregates: {},
116
+ views: {},
117
+ tokenToReplica: getTokenToReplicaMapper(strategy, strategyOptions),
118
+ graphEngine: undefined,
119
+ }
120
+ }
121
+
122
+ /**
123
+ * @abstract
124
+ * @param {String} name
125
+ * @returns {Promise<Object>}
126
+ */
127
+ getKeyspace(name) {}
128
+
129
+ /**
130
+ * @abstract
131
+ * @param {Boolean} waitReconnect
132
+ * @returns {Promise<Object<string, Object>>}
133
+ */
134
+ getKeyspaces(waitReconnect) {}
135
+
136
+ /**
137
+ * @param {String} keyspaceName
138
+ * @param {String} name
139
+ * @param {Object} cache
140
+ * @param {Boolean} virtual
141
+ * @returns {Promise<TableMetadata|null>}
142
+ */
143
+ async getTable(keyspaceName, name, cache, virtual) {
144
+ let tableInfo = cache && cache[name]
145
+ if (!tableInfo) {
146
+ tableInfo = new TableMetadata(name)
147
+ if (cache) {
148
+ cache[name] = tableInfo
149
+ }
150
+ }
151
+ if (tableInfo.loaded) {
152
+ return tableInfo
153
+ }
154
+ if (tableInfo.loading) {
155
+ // Wait for it to emit
156
+ return promiseUtils.fromEvent(tableInfo, "load")
157
+ }
158
+ try {
159
+ // its not cached and not being retrieved
160
+ tableInfo.loading = true
161
+ let indexRows
162
+ let virtualTable = virtual
163
+ const selectTable = virtualTable
164
+ ? _selectVirtualTable
165
+ : this.selectTable
166
+ const query = util.format(selectTable, keyspaceName, name)
167
+ let tableRow = await this._getFirstRow(query)
168
+ // if we weren't sure if table was virtual or not, query virtual schema.
169
+ if (
170
+ !tableRow &&
171
+ this.supportsVirtual &&
172
+ virtualTable === undefined
173
+ ) {
174
+ const query = util.format(
175
+ _selectVirtualTable,
176
+ keyspaceName,
177
+ name,
178
+ )
179
+ try {
180
+ tableRow = await this._getFirstRow(query)
181
+ } catch (err) {
182
+ // we can't error here as we can't be sure if the node
183
+ // supports virtual tables, in this case it is adequate
184
+ // to act as if there was no matching table.
185
+ }
186
+ if (tableRow) {
187
+ // We are fetching a virtual table
188
+ virtualTable = true
189
+ }
190
+ }
191
+ if (!tableRow) {
192
+ tableInfo.loading = false
193
+ tableInfo.emit("load", null, null)
194
+ return null
195
+ }
196
+ const selectColumns = virtualTable
197
+ ? _selectVirtualColumns
198
+ : this.selectColumns
199
+ const columnRows = await this._getRows(
200
+ util.format(selectColumns, keyspaceName, name),
201
+ )
202
+ if (this.selectIndexes && !virtualTable) {
203
+ indexRows = await this._getRows(
204
+ util.format(this.selectIndexes, keyspaceName, name),
205
+ )
206
+ }
207
+ await this._parseTableOrView(
208
+ tableInfo,
209
+ tableRow,
210
+ columnRows,
211
+ indexRows,
212
+ virtualTable,
213
+ )
214
+ tableInfo.loaded = true
215
+ tableInfo.emit("load", null, tableInfo)
216
+ return tableInfo
217
+ } catch (err) {
218
+ tableInfo.emit("load", err, null)
219
+ throw err
220
+ } finally {
221
+ tableInfo.loading = false
222
+ }
223
+ }
224
+
225
+ async _getFirstRow(query) {
226
+ const rows = await this._getRows(query)
227
+ return rows[0]
228
+ }
229
+
230
+ async _getRows(query) {
231
+ const response = await this.cc.query(query)
232
+ return response.rows
233
+ }
234
+
235
+ /**
236
+ * @param {String} keyspaceName
237
+ * @param {String} name
238
+ * @param {Object} cache
239
+ * @returns {Promise<Object|null>}
240
+ */
241
+ async getUdt(keyspaceName, name, cache) {
242
+ let udtInfo = cache && cache[name]
243
+ if (!udtInfo) {
244
+ udtInfo = new events.EventEmitter()
245
+ if (cache) {
246
+ cache[name] = udtInfo
247
+ }
248
+ udtInfo.setMaxListeners(0)
249
+ udtInfo.loading = false
250
+ udtInfo.name = name
251
+ udtInfo.keyspace = keyspaceName
252
+ udtInfo.fields = null
253
+ }
254
+ if (udtInfo.fields) {
255
+ return udtInfo
256
+ }
257
+ if (udtInfo.loading) {
258
+ return promiseUtils.fromEvent(udtInfo, "load")
259
+ }
260
+ udtInfo.loading = true
261
+ const query = format(this.selectUdt, keyspaceName, name)
262
+ try {
263
+ const row = await this._getFirstRow(query)
264
+ if (!row) {
265
+ udtInfo.loading = false
266
+ udtInfo.emit("load", null, null)
267
+ return null
268
+ }
269
+ await this._parseUdt(udtInfo, row)
270
+ udtInfo.emit("load", null, udtInfo)
271
+ return udtInfo
272
+ } catch (err) {
273
+ udtInfo.emit("load", err)
274
+ throw err
275
+ } finally {
276
+ udtInfo.loading = false
277
+ }
278
+ }
279
+
280
+ /**
281
+ * Parses the udt information from the row
282
+ * @param udtInfo
283
+ * @param {Row} row
284
+ * @returns {Promise<void>}
285
+ * @abstract
286
+ */
287
+ _parseUdt(udtInfo, row) {}
288
+
289
+ /**
290
+ * Builds the metadata based on the table and column rows
291
+ * @abstract
292
+ * @param {module:metadata~TableMetadata} tableInfo
293
+ * @param {Row} tableRow
294
+ * @param {Array.<Row>} columnRows
295
+ * @param {Array.<Row>} indexRows
296
+ * @param {Boolean} virtual
297
+ * @returns {Promise<void>}
298
+ * @throws {Error}
299
+ */
300
+ async _parseTableOrView(
301
+ tableInfo,
302
+ tableRow,
303
+ columnRows,
304
+ indexRows,
305
+ virtual,
306
+ ) {}
307
+
308
+ /**
309
+ * @abstract
310
+ * @param {String} keyspaceName
311
+ * @param {String} name
312
+ * @param {Object} cache
313
+ * @returns {Promise<MaterializedView|null>}
314
+ */
315
+ getMaterializedView(keyspaceName, name, cache) {}
316
+
317
+ /**
318
+ * @param {String} keyspaceName
319
+ * @param {String} name
320
+ * @param {Boolean} aggregate
321
+ * @param {Object} cache
322
+ * @returns {Promise<Map>}
323
+ */
324
+ async getFunctions(keyspaceName, name, aggregate, cache) {
325
+ /** @type {String} */
326
+ let query = this.selectFunctions
327
+ let parser = (row) => this._parseFunction(row)
328
+ if (aggregate) {
329
+ query = this.selectAggregates
330
+ parser = (row) => this._parseAggregate(row)
331
+ }
332
+ // if it's not already loaded, get all functions with that name
333
+ // cache it by name and, within name, by signature
334
+ let functionsInfo = cache && cache[name]
335
+ if (!functionsInfo) {
336
+ functionsInfo = new events.EventEmitter()
337
+ if (cache) {
338
+ cache[name] = functionsInfo
339
+ }
340
+ functionsInfo.setMaxListeners(0)
341
+ }
342
+ if (functionsInfo.values) {
343
+ return functionsInfo.values
344
+ }
345
+ if (functionsInfo.loading) {
346
+ return promiseUtils.fromEvent(functionsInfo, "load")
347
+ }
348
+ functionsInfo.loading = true
349
+ try {
350
+ const rows = await this._getRows(format(query, keyspaceName, name))
351
+ const funcs = await Promise.all(rows.map(parser))
352
+ const result = new Map()
353
+ if (rows.length > 0) {
354
+ // Cache positive hits
355
+ functionsInfo.values = result
356
+ }
357
+
358
+ funcs.forEach((f) =>
359
+ functionsInfo.values.set(f.signature.join(","), f),
360
+ )
361
+ functionsInfo.emit("load", null, result)
362
+ return result
363
+ } catch (err) {
364
+ functionsInfo.emit("load", err)
365
+ throw err
366
+ } finally {
367
+ functionsInfo.loading = false
368
+ }
369
+ }
370
+
371
+ /**
372
+ * @abstract
373
+ * @param {Row} row
374
+ * @returns {Promise}
375
+ */
376
+ _parseAggregate(row) {}
377
+
378
+ /**
379
+ * @abstract
380
+ * @param {Row} row
381
+ * @returns {Promise}
382
+ */
383
+ _parseFunction(row) {}
384
+
385
+ /** @returns {Map} */
386
+ _asMap(obj) {
387
+ if (!obj) {
388
+ return new Map()
389
+ }
390
+ if (
391
+ this.encodingOptions.map &&
392
+ obj instanceof this.encodingOptions.map
393
+ ) {
394
+ // Its already a Map or a polyfill of a Map
395
+ return obj
396
+ }
397
+ return new Map(Object.keys(obj).map((k) => [k, obj[k]]))
398
+ }
399
+
400
+ _mapAsObject(map) {
401
+ if (!map) {
402
+ return map
403
+ }
404
+ if (
405
+ this.encodingOptions.map &&
406
+ map instanceof this.encodingOptions.map
407
+ ) {
408
+ const result = {}
409
+ map.forEach((value, key) => (result[key] = value))
410
+ return result
411
+ }
412
+ return map
413
+ }
414
+ }
415
+
416
+ /**
417
+ * Used to parse schema information for Cassandra versions 1.2.x, and 2.x
418
+ * @ignore
419
+ */
420
+ class SchemaParserV1 extends SchemaParser {
421
+ /**
422
+ * @param {ClientOptions} options
423
+ * @param {ControlConnection} cc
424
+ */
425
+ constructor(options, cc) {
426
+ super(options, cc)
427
+ this.selectTable = _selectTableV1
428
+ this.selectColumns = _selectColumnsV1
429
+ this.selectUdt = _selectUdtV1
430
+ this.selectAggregates = _selectAggregatesV1
431
+ this.selectFunctions = _selectFunctionsV1
432
+ }
433
+
434
+ async getKeyspaces(waitReconnect) {
435
+ const keyspaces = {}
436
+ const result = await this.cc.query(_selectAllKeyspacesV1, waitReconnect)
437
+ for (let i = 0; i < result.rows.length; i++) {
438
+ const row = result.rows[i]
439
+ const ksInfo = this._createKeyspace(
440
+ row["keyspace_name"],
441
+ row["durable_writes"],
442
+ row["strategy_class"],
443
+ JSON.parse(row["strategy_options"] || null),
444
+ )
445
+ keyspaces[ksInfo.name] = ksInfo
446
+ }
447
+ return keyspaces
448
+ }
449
+
450
+ async getKeyspace(name) {
451
+ const row = await this._getFirstRow(
452
+ format(_selectSingleKeyspaceV1, name),
453
+ )
454
+ if (!row) {
455
+ return null
456
+ }
457
+ return this._createKeyspace(
458
+ row["keyspace_name"],
459
+ row["durable_writes"],
460
+ row["strategy_class"],
461
+ JSON.parse(row["strategy_options"]),
462
+ )
463
+ }
464
+
465
+ // eslint-disable-next-line require-await
466
+ async _parseTableOrView(
467
+ tableInfo,
468
+ tableRow,
469
+ columnRows,
470
+ indexRows,
471
+ virtual,
472
+ ) {
473
+ // All the tableInfo parsing in V1 is sync, it uses a async function because the super class defines one
474
+ // to support other versions.
475
+ let c, name, types
476
+ const encoder = this.cc.getEncoder()
477
+ const columnsKeyed = {}
478
+ let partitionKeys = []
479
+ let clusteringKeys = []
480
+ tableInfo.bloomFilterFalsePositiveChance =
481
+ tableRow["bloom_filter_fp_chance"]
482
+ tableInfo.caching = tableRow["caching"]
483
+ tableInfo.comment = tableRow["comment"]
484
+ tableInfo.compactionClass = tableRow["compaction_strategy_class"]
485
+ tableInfo.compactionOptions = JSON.parse(
486
+ tableRow["compaction_strategy_options"],
487
+ )
488
+ tableInfo.compression = JSON.parse(tableRow["compression_parameters"])
489
+ tableInfo.gcGraceSeconds = tableRow["gc_grace_seconds"]
490
+ tableInfo.localReadRepairChance = tableRow["local_read_repair_chance"]
491
+ tableInfo.readRepairChance = tableRow["read_repair_chance"]
492
+ tableInfo.populateCacheOnFlush =
493
+ tableRow["populate_io_cache_on_flush"] ||
494
+ tableInfo.populateCacheOnFlush
495
+ tableInfo.memtableFlushPeriod =
496
+ tableRow["memtable_flush_period_in_ms"] ||
497
+ tableInfo.memtableFlushPeriod
498
+ tableInfo.defaultTtl =
499
+ tableRow["default_time_to_live"] || tableInfo.defaultTtl
500
+ tableInfo.speculativeRetry =
501
+ tableRow["speculative_retry"] || tableInfo.speculativeRetry
502
+ tableInfo.indexInterval =
503
+ tableRow["index_interval"] || tableInfo.indexInterval
504
+ if (typeof tableRow["min_index_interval"] !== "undefined") {
505
+ //Cassandra 2.1+
506
+ tableInfo.minIndexInterval =
507
+ tableRow["min_index_interval"] || tableInfo.minIndexInterval
508
+ tableInfo.maxIndexInterval =
509
+ tableRow["max_index_interval"] || tableInfo.maxIndexInterval
510
+ } else {
511
+ //set to null
512
+ tableInfo.minIndexInterval = null
513
+ tableInfo.maxIndexInterval = null
514
+ }
515
+ if (typeof tableRow["replicate_on_write"] !== "undefined") {
516
+ //leave the default otherwise
517
+ tableInfo.replicateOnWrite = tableRow["replicate_on_write"]
518
+ }
519
+ tableInfo.columns = []
520
+ for (let i = 0; i < columnRows.length; i++) {
521
+ const row = columnRows[i]
522
+ const type = encoder.parseFqTypeName(row["validator"])
523
+ c = {
524
+ name: row["column_name"],
525
+ type: type,
526
+ isStatic: false,
527
+ }
528
+ tableInfo.columns.push(c)
529
+ columnsKeyed[c.name] = c
530
+ switch (row["type"]) {
531
+ case "partition_key":
532
+ partitionKeys.push({
533
+ c: c,
534
+ index: row["component_index"] || 0,
535
+ })
536
+ break
537
+ case "clustering_key":
538
+ clusteringKeys.push({
539
+ c: c,
540
+ index: row["component_index"] || 0,
541
+ order: c.type.options.reversed ? "DESC" : "ASC",
542
+ })
543
+ break
544
+ case "static":
545
+ // C* 2.0.6+ supports static columns
546
+ c.isStatic = true
547
+ break
548
+ }
549
+ }
550
+ if (partitionKeys.length > 0) {
551
+ tableInfo.partitionKeys = partitionKeys
552
+ .sort(utils.propCompare("index"))
553
+ .map((item) => item.c)
554
+ clusteringKeys.sort(utils.propCompare("index"))
555
+ tableInfo.clusteringKeys = clusteringKeys.map((item) => item.c)
556
+ tableInfo.clusteringOrder = clusteringKeys.map((item) => item.order)
557
+ }
558
+ // In C* 1.2, keys are not stored on the schema_columns table
559
+ const keysStoredInTableRow = tableInfo.partitionKeys.length === 0
560
+ if (keysStoredInTableRow && tableRow["key_aliases"]) {
561
+ //In C* 1.2, keys are not stored on the schema_columns table
562
+ partitionKeys = JSON.parse(tableRow["key_aliases"])
563
+ types = encoder.parseKeyTypes(tableRow["key_validator"]).types
564
+ for (let i = 0; i < partitionKeys.length; i++) {
565
+ name = partitionKeys[i]
566
+ c = columnsKeyed[name]
567
+ if (!c) {
568
+ c = {
569
+ name: name,
570
+ type: types[i],
571
+ }
572
+ tableInfo.columns.push(c)
573
+ }
574
+ tableInfo.partitionKeys.push(c)
575
+ }
576
+ }
577
+ const comparator = encoder.parseKeyTypes(tableRow["comparator"])
578
+ if (keysStoredInTableRow && tableRow["column_aliases"]) {
579
+ clusteringKeys = JSON.parse(tableRow["column_aliases"])
580
+ for (let i = 0; i < clusteringKeys.length; i++) {
581
+ name = clusteringKeys[i]
582
+ c = columnsKeyed[name]
583
+ if (!c) {
584
+ c = {
585
+ name: name,
586
+ type: comparator.types[i],
587
+ }
588
+ tableInfo.columns.push(c)
589
+ }
590
+ tableInfo.clusteringKeys.push(c)
591
+ tableInfo.clusteringOrder.push(
592
+ c.type.options.reversed ? "DESC" : "ASC",
593
+ )
594
+ }
595
+ }
596
+ tableInfo.isCompact = !!tableRow["is_dense"]
597
+ if (!tableInfo.isCompact) {
598
+ //is_dense column does not exist in previous versions of Cassandra
599
+ //also, compact pk, ck and val appear as is_dense false
600
+ // clusteringKeys != comparator types - 1
601
+ // or not composite (comparator)
602
+ tableInfo.isCompact =
603
+ //clustering keys are not marked as composite
604
+ !comparator.isComposite ||
605
+ //only 1 column not part of the partition or clustering keys
606
+ (!comparator.hasCollections &&
607
+ tableInfo.clusteringKeys.length !==
608
+ comparator.types.length - 1)
609
+ }
610
+ name = tableRow["value_alias"]
611
+ if (tableInfo.isCompact && name && !columnsKeyed[name]) {
612
+ //additional column in C* 1.2 as value_alias
613
+ c = {
614
+ name: name,
615
+ type: encoder.parseFqTypeName(tableRow["default_validator"]),
616
+ }
617
+ tableInfo.columns.push(c)
618
+ columnsKeyed[name] = c
619
+ }
620
+ tableInfo.columnsByName = columnsKeyed
621
+ tableInfo.indexes = Index.fromColumnRows(
622
+ columnRows,
623
+ tableInfo.columnsByName,
624
+ )
625
+ }
626
+
627
+ getMaterializedView(keyspaceName, name, cache) {
628
+ return Promise.reject(
629
+ new errors.NotSupportedError(
630
+ "Materialized views are not supported on Cassandra versions below 3.0",
631
+ ),
632
+ )
633
+ }
634
+
635
+ // eslint-disable-next-line require-await
636
+ async _parseAggregate(row) {
637
+ const encoder = this.cc.getEncoder()
638
+ const aggregate = new Aggregate()
639
+ aggregate.name = row["aggregate_name"]
640
+ aggregate.keyspaceName = row["keyspace_name"]
641
+ aggregate.signature = row["signature"] || utils.emptyArray
642
+ aggregate.stateFunction = row["state_func"]
643
+ aggregate.finalFunction = row["final_func"]
644
+ aggregate.initConditionRaw = row["initcond"]
645
+ aggregate.argumentTypes = (
646
+ row["argument_types"] || utils.emptyArray
647
+ ).map((name) => encoder.parseFqTypeName(name))
648
+ aggregate.stateType = encoder.parseFqTypeName(row["state_type"])
649
+ const initConditionValue = encoder.decode(
650
+ aggregate.initConditionRaw,
651
+ aggregate.stateType,
652
+ )
653
+ if (
654
+ initConditionValue !== null &&
655
+ typeof initConditionValue !== "undefined"
656
+ ) {
657
+ aggregate.initCondition = initConditionValue.toString()
658
+ }
659
+ aggregate.returnType = encoder.parseFqTypeName(row["return_type"])
660
+ return aggregate
661
+ }
662
+
663
+ // eslint-disable-next-line require-await
664
+ async _parseFunction(row) {
665
+ const encoder = this.cc.getEncoder()
666
+ const func = new SchemaFunction()
667
+ func.name = row["function_name"]
668
+ func.keyspaceName = row["keyspace_name"]
669
+ func.signature = row["signature"] || utils.emptyArray
670
+ func.argumentNames = row["argument_names"] || utils.emptyArray
671
+ func.body = row["body"]
672
+ func.calledOnNullInput = row["called_on_null_input"]
673
+ func.language = row["language"]
674
+ func.argumentTypes = (row["argument_types"] || utils.emptyArray).map(
675
+ (name) => encoder.parseFqTypeName(name),
676
+ )
677
+ func.returnType = encoder.parseFqTypeName(row["return_type"])
678
+ return func
679
+ }
680
+
681
+ // eslint-disable-next-line require-await
682
+ async _parseUdt(udtInfo, row) {
683
+ const encoder = this.cc.getEncoder()
684
+ const fieldNames = row["field_names"]
685
+ const fieldTypes = row["field_types"]
686
+ const fields = new Array(fieldNames.length)
687
+ for (let i = 0; i < fieldNames.length; i++) {
688
+ fields[i] = {
689
+ name: fieldNames[i],
690
+ type: encoder.parseFqTypeName(fieldTypes[i]),
691
+ }
692
+ }
693
+ udtInfo.fields = fields
694
+ return udtInfo
695
+ }
696
+ }
697
+
698
+ /**
699
+ * Used to parse schema information for Cassandra versions 3.x and above
700
+ * @param {ClientOptions} options The client options
701
+ * @param {ControlConnection} cc The control connection to be used
702
+ * @param {Function} udtResolver The function to be used to retrieve the udts.
703
+ * @ignore
704
+ */
705
+ class SchemaParserV2 extends SchemaParser {
706
+ /**
707
+ * @param {ClientOptions} options The client options
708
+ * @param {ControlConnection} cc The control connection to be used
709
+ * @param {Function} udtResolver The function to be used to retrieve the udts.
710
+ */
711
+ constructor(options, cc, udtResolver) {
712
+ super(options, cc)
713
+ this.udtResolver = udtResolver
714
+ this.selectTable = _selectTableV2
715
+ this.selectColumns = _selectColumnsV2
716
+ this.selectUdt = _selectUdtV2
717
+ this.selectAggregates = _selectAggregatesV2
718
+ this.selectFunctions = _selectFunctionsV2
719
+ this.selectIndexes = _selectIndexesV2
720
+ }
721
+
722
+ async getKeyspaces(waitReconnect) {
723
+ const keyspaces = {}
724
+ const result = await this.cc.query(_selectAllKeyspacesV2, waitReconnect)
725
+ for (let i = 0; i < result.rows.length; i++) {
726
+ const ksInfo = this._parseKeyspace(result.rows[i])
727
+ keyspaces[ksInfo.name] = ksInfo
728
+ }
729
+ return keyspaces
730
+ }
731
+
732
+ async getKeyspace(name) {
733
+ const row = await this._getFirstRow(
734
+ format(_selectSingleKeyspaceV2, name),
735
+ )
736
+ if (!row) {
737
+ return null
738
+ }
739
+ return this._parseKeyspace(row)
740
+ }
741
+
742
+ async getMaterializedView(keyspaceName, name, cache) {
743
+ let viewInfo = cache && cache[name]
744
+ if (!viewInfo) {
745
+ viewInfo = new MaterializedView(name)
746
+ if (cache) {
747
+ cache[name] = viewInfo
748
+ }
749
+ }
750
+ if (viewInfo.loaded) {
751
+ return viewInfo
752
+ }
753
+ if (viewInfo.loading) {
754
+ return promiseUtils.fromEvent(viewInfo, "load")
755
+ }
756
+ viewInfo.loading = true
757
+ try {
758
+ const tableRow = await this._getFirstRow(
759
+ format(_selectMaterializedViewV2, keyspaceName, name),
760
+ )
761
+ if (!tableRow) {
762
+ viewInfo.emit("load", null, null)
763
+ viewInfo.loading = false
764
+ return null
765
+ }
766
+ const columnRows = await this._getRows(
767
+ format(this.selectColumns, keyspaceName, name),
768
+ )
769
+ await this._parseTableOrView(
770
+ viewInfo,
771
+ tableRow,
772
+ columnRows,
773
+ null,
774
+ false,
775
+ )
776
+ viewInfo.loaded = true
777
+ viewInfo.emit("load", null, viewInfo)
778
+ return viewInfo
779
+ } catch (err) {
780
+ viewInfo.emit("load", err)
781
+ throw err
782
+ } finally {
783
+ viewInfo.loading = false
784
+ }
785
+ }
786
+
787
+ _parseKeyspace(row, virtual) {
788
+ const replication = row["replication"]
789
+ let strategy
790
+ let strategyOptions
791
+ if (replication) {
792
+ strategy = replication["class"]
793
+ strategyOptions = {}
794
+ for (const key in replication) {
795
+ if (!replication.hasOwnProperty(key) || key === "class") {
796
+ continue
797
+ }
798
+ strategyOptions[key] = replication[key]
799
+ }
800
+ }
801
+
802
+ const ks = this._createKeyspace(
803
+ row["keyspace_name"],
804
+ row["durable_writes"],
805
+ strategy,
806
+ strategyOptions,
807
+ virtual,
808
+ )
809
+ ks.graphEngine = row["graph_engine"]
810
+ return ks
811
+ }
812
+
813
+ async _parseTableOrView(
814
+ tableInfo,
815
+ tableRow,
816
+ columnRows,
817
+ indexRows,
818
+ virtual,
819
+ ) {
820
+ const encoder = this.cc.getEncoder()
821
+ const columnsKeyed = {}
822
+ const partitionKeys = []
823
+ const clusteringKeys = []
824
+ tableInfo.columns = await Promise.all(
825
+ columnRows.map(async (row) => {
826
+ const type = await encoder.parseTypeName(
827
+ tableRow["keyspace_name"],
828
+ row["type"],
829
+ 0,
830
+ null,
831
+ this.udtResolver,
832
+ )
833
+ const c = {
834
+ name: row["column_name"],
835
+ type: type,
836
+ isStatic: false,
837
+ }
838
+ columnsKeyed[c.name] = c
839
+ switch (row["kind"]) {
840
+ case "partition_key":
841
+ partitionKeys.push({ c, index: row["position"] || 0 })
842
+ break
843
+ case "clustering":
844
+ clusteringKeys.push({
845
+ c,
846
+ index: row["position"] || 0,
847
+ order:
848
+ row["clustering_order"] === "desc"
849
+ ? "DESC"
850
+ : "ASC",
851
+ })
852
+ break
853
+ case "static":
854
+ c.isStatic = true
855
+ break
856
+ }
857
+ return c
858
+ }),
859
+ )
860
+ tableInfo.columnsByName = columnsKeyed
861
+ tableInfo.partitionKeys = partitionKeys
862
+ .sort(utils.propCompare("index"))
863
+ .map((item) => item.c)
864
+ clusteringKeys.sort(utils.propCompare("index"))
865
+ tableInfo.clusteringKeys = clusteringKeys.map((item) => item.c)
866
+ tableInfo.clusteringOrder = clusteringKeys.map((item) => item.order)
867
+ if (virtual) {
868
+ // When table is virtual, the only relevant information to parse are the columns
869
+ // as the table itself has no configuration
870
+ tableInfo.virtual = true
871
+ return
872
+ }
873
+ const isView = tableInfo instanceof MaterializedView
874
+ tableInfo.bloomFilterFalsePositiveChance =
875
+ tableRow["bloom_filter_fp_chance"]
876
+ tableInfo.caching = JSON.stringify(tableRow["caching"])
877
+ tableInfo.comment = tableRow["comment"]
878
+ // Regardless of the encoding options, use always an Object to represent an associative Array
879
+ const compaction = this._asMap(tableRow["compaction"])
880
+ if (compaction) {
881
+ // compactionOptions as an Object<String, String>
882
+ tableInfo.compactionOptions = {}
883
+ tableInfo.compactionClass = compaction.get("class")
884
+ compaction.forEach((value, key) => {
885
+ if (key === "class") {
886
+ return
887
+ }
888
+ tableInfo.compactionOptions[key] = compaction.get(key)
889
+ })
890
+ }
891
+ // Convert compression to an Object<String, String>
892
+ tableInfo.compression = this._mapAsObject(tableRow["compression"])
893
+ tableInfo.gcGraceSeconds = tableRow["gc_grace_seconds"]
894
+ tableInfo.localReadRepairChance = tableRow["dclocal_read_repair_chance"]
895
+ tableInfo.readRepairChance = tableRow["read_repair_chance"]
896
+ tableInfo.extensions = this._mapAsObject(tableRow["extensions"])
897
+ tableInfo.crcCheckChance = tableRow["crc_check_chance"]
898
+ tableInfo.memtableFlushPeriod =
899
+ tableRow["memtable_flush_period_in_ms"] ||
900
+ tableInfo.memtableFlushPeriod
901
+ tableInfo.defaultTtl =
902
+ tableRow["default_time_to_live"] || tableInfo.defaultTtl
903
+ tableInfo.speculativeRetry =
904
+ tableRow["speculative_retry"] || tableInfo.speculativeRetry
905
+ tableInfo.minIndexInterval =
906
+ tableRow["min_index_interval"] || tableInfo.minIndexInterval
907
+ tableInfo.maxIndexInterval =
908
+ tableRow["max_index_interval"] || tableInfo.maxIndexInterval
909
+ tableInfo.nodesync = tableRow["nodesync"] || tableInfo.nodesync
910
+ if (!isView) {
911
+ const cdc = tableRow["cdc"]
912
+ if (cdc !== undefined) {
913
+ tableInfo.cdc = cdc
914
+ }
915
+ }
916
+ if (isView) {
917
+ tableInfo.tableName = tableRow["base_table_name"]
918
+ tableInfo.whereClause = tableRow["where_clause"]
919
+ tableInfo.includeAllColumns = tableRow["include_all_columns"]
920
+ return
921
+ }
922
+ tableInfo.indexes = this._getIndexes(indexRows)
923
+ // flags can be an instance of Array or Set (real or polyfill)
924
+ let flags = tableRow["flags"]
925
+ if (Array.isArray(flags)) {
926
+ flags = new Set(flags)
927
+ }
928
+ const isDense = flags.has("dense")
929
+ const isSuper = flags.has("super")
930
+ const isCompound = flags.has("compound")
931
+ tableInfo.isCompact = isSuper || isDense || !isCompound
932
+ // Remove the columns related to Thrift
933
+ const isStaticCompact = !isSuper && !isDense && !isCompound
934
+ if (isStaticCompact) {
935
+ pruneStaticCompactTableColumns(tableInfo)
936
+ } else if (isDense) {
937
+ pruneDenseTableColumns(tableInfo)
938
+ }
939
+ }
940
+
941
+ _getIndexes(indexRows) {
942
+ if (!indexRows || indexRows.length === 0) {
943
+ return utils.emptyArray
944
+ }
945
+ return indexRows.map((row) => {
946
+ const options = this._mapAsObject(row["options"])
947
+ return new Index(
948
+ row["index_name"],
949
+ options["target"],
950
+ row["kind"],
951
+ options,
952
+ )
953
+ })
954
+ }
955
+
956
+ async _parseAggregate(row) {
957
+ const encoder = this.cc.getEncoder()
958
+ const aggregate = new Aggregate()
959
+ aggregate.name = row["aggregate_name"]
960
+ aggregate.keyspaceName = row["keyspace_name"]
961
+ aggregate.signature = row["argument_types"] || utils.emptyArray
962
+ aggregate.stateFunction = row["state_func"]
963
+ aggregate.finalFunction = row["final_func"]
964
+ aggregate.initConditionRaw = row["initcond"]
965
+ aggregate.initCondition = aggregate.initConditionRaw
966
+ aggregate.deterministic = row["deterministic"] || false
967
+ aggregate.argumentTypes = await Promise.all(
968
+ aggregate.signature.map((name) =>
969
+ encoder.parseTypeName(
970
+ row["keyspace_name"],
971
+ name,
972
+ 0,
973
+ null,
974
+ this.udtResolver,
975
+ ),
976
+ ),
977
+ )
978
+ aggregate.stateType = await encoder.parseTypeName(
979
+ row["keyspace_name"],
980
+ row["state_type"],
981
+ 0,
982
+ null,
983
+ this.udtResolver,
984
+ )
985
+ aggregate.returnType = await encoder.parseTypeName(
986
+ row["keyspace_name"],
987
+ row["return_type"],
988
+ 0,
989
+ null,
990
+ this.udtResolver,
991
+ )
992
+ return aggregate
993
+ }
994
+
995
+ async _parseFunction(row) {
996
+ const encoder = this.cc.getEncoder()
997
+ const func = new SchemaFunction()
998
+ func.name = row["function_name"]
999
+ func.keyspaceName = row["keyspace_name"]
1000
+ func.signature = row["argument_types"] || utils.emptyArray
1001
+ func.argumentNames = row["argument_names"] || utils.emptyArray
1002
+ func.body = row["body"]
1003
+ func.calledOnNullInput = row["called_on_null_input"]
1004
+ func.language = row["language"]
1005
+ func.deterministic = row["deterministic"] || false
1006
+ func.monotonic = row["monotonic"] || false
1007
+ func.monotonicOn = row["monotonic_on"] || utils.emptyArray
1008
+ func.argumentTypes = await Promise.all(
1009
+ func.signature.map((name) =>
1010
+ encoder.parseTypeName(
1011
+ row["keyspace_name"],
1012
+ name,
1013
+ 0,
1014
+ null,
1015
+ this.udtResolver,
1016
+ ),
1017
+ ),
1018
+ )
1019
+ func.returnType = await encoder.parseTypeName(
1020
+ row["keyspace_name"],
1021
+ row["return_type"],
1022
+ 0,
1023
+ null,
1024
+ this.udtResolver,
1025
+ )
1026
+ return func
1027
+ }
1028
+
1029
+ async _parseUdt(udtInfo, row) {
1030
+ const encoder = this.cc.getEncoder()
1031
+ const fieldTypes = row["field_types"]
1032
+ const keyspace = row["keyspace_name"]
1033
+ udtInfo.fields = await Promise.all(
1034
+ row["field_names"].map(async (name, i) => {
1035
+ const type = await encoder.parseTypeName(
1036
+ keyspace,
1037
+ fieldTypes[i],
1038
+ 0,
1039
+ null,
1040
+ this.udtResolver,
1041
+ )
1042
+ return { name, type }
1043
+ }),
1044
+ )
1045
+ return udtInfo
1046
+ }
1047
+ }
1048
+
1049
+ /**
1050
+ * Used to parse schema information for Cassandra versions 4.x and above.
1051
+ *
1052
+ * This parser similar to [SchemaParserV2] expect it also parses virtual
1053
+ * keyspaces.
1054
+ * @ignore
1055
+ */
1056
+ class SchemaParserV3 extends SchemaParserV2 {
1057
+ /**
1058
+ * @param {ClientOptions} options The client options
1059
+ * @param {ControlConnection} cc The control connection to be used
1060
+ * @param {Function} udtResolver The function to be used to retrieve the udts.
1061
+ */
1062
+ constructor(options, cc, udtResolver) {
1063
+ super(options, cc, udtResolver)
1064
+ this.supportsVirtual = true
1065
+ }
1066
+
1067
+ async getKeyspaces(waitReconnect) {
1068
+ const keyspaces = {}
1069
+ const queries = [
1070
+ { query: _selectAllKeyspacesV2, virtual: false },
1071
+ { query: _selectAllVirtualKeyspaces, virtual: true },
1072
+ ]
1073
+
1074
+ await Promise.all(
1075
+ queries.map(async (q) => {
1076
+ let result = null
1077
+ try {
1078
+ result = await this.cc.query(q.query, waitReconnect)
1079
+ } catch (err) {
1080
+ if (q.virtual) {
1081
+ // Only throw error for non-virtual query as
1082
+ // server reporting C* 4.0 may not actually implement
1083
+ // virtual tables.
1084
+ return
1085
+ }
1086
+ throw err
1087
+ }
1088
+ for (let i = 0; i < result.rows.length; i++) {
1089
+ const ksInfo = this._parseKeyspace(
1090
+ result.rows[i],
1091
+ q.virtual,
1092
+ )
1093
+ keyspaces[ksInfo.name] = ksInfo
1094
+ }
1095
+ }),
1096
+ )
1097
+ return keyspaces
1098
+ }
1099
+
1100
+ async getKeyspace(name) {
1101
+ const ks = await this._getKeyspace(_selectSingleKeyspaceV2, name, false)
1102
+ if (!ks) {
1103
+ // if not found, attempt to retrieve as virtual keyspace.
1104
+ return this._getKeyspace(_selectSingleVirtualKeyspace, name, true)
1105
+ }
1106
+ return ks
1107
+ }
1108
+
1109
+ async _getKeyspace(query, name, virtual) {
1110
+ try {
1111
+ const row = await this._getFirstRow(format(query, name))
1112
+
1113
+ if (!row) {
1114
+ return null
1115
+ }
1116
+
1117
+ return this._parseKeyspace(row, virtual)
1118
+ } catch (err) {
1119
+ if (virtual) {
1120
+ // only throw error for non-virtual query as
1121
+ // server reporting C* 4.0 may not actually implement
1122
+ // virtual tables.
1123
+ return null
1124
+ }
1125
+ throw err
1126
+ }
1127
+ }
1128
+ }
1129
+
1130
+ /**
1131
+ * Upon migration from thrift to CQL, we internally create a pair of surrogate clustering/regular columns
1132
+ * for compact static tables. These columns shouldn't be exposed to the user but are currently returned by C*.
1133
+ * We also need to remove the static keyword for all other columns in the table.
1134
+ * @param {module:metadata~TableMetadata} tableInfo
1135
+ */
1136
+ function pruneStaticCompactTableColumns(tableInfo) {
1137
+ let i
1138
+ let c
1139
+ //remove "column1 text" clustering column
1140
+ for (i = 0; i < tableInfo.clusteringKeys.length; i++) {
1141
+ c = tableInfo.clusteringKeys[i]
1142
+ const index = tableInfo.columns.indexOf(c)
1143
+ tableInfo.columns.splice(index, 1)
1144
+ delete tableInfo.columnsByName[c.name]
1145
+ }
1146
+ tableInfo.clusteringKeys = utils.emptyArray
1147
+ tableInfo.clusteringOrder = utils.emptyArray
1148
+ //remove regular columns and set the static columns to non-static
1149
+ i = tableInfo.columns.length
1150
+ while (i--) {
1151
+ c = tableInfo.columns[i]
1152
+ if (!c.isStatic && tableInfo.partitionKeys.indexOf(c) === -1) {
1153
+ // remove "value blob" regular column
1154
+ tableInfo.columns.splice(i, 1)
1155
+ delete tableInfo.columnsByName[c.name]
1156
+ continue
1157
+ }
1158
+ c.isStatic = false
1159
+ }
1160
+ }
1161
+
1162
+ /**
1163
+ * Upon migration from thrift to CQL, we internally create a surrogate column "value" of type custom.
1164
+ * This column shouldn't be exposed to the user but is currently returned by C*.
1165
+ * @param {module:metadata~TableMetadata} tableInfo
1166
+ */
1167
+ function pruneDenseTableColumns(tableInfo) {
1168
+ let i = tableInfo.columns.length
1169
+ while (i--) {
1170
+ const c = tableInfo.columns[i]
1171
+ if (
1172
+ !c.isStatic &&
1173
+ c.type.code === types.dataTypes.custom &&
1174
+ c.type.info === "empty"
1175
+ ) {
1176
+ // remove "value blob" regular column
1177
+ tableInfo.columns.splice(i, 1)
1178
+ delete tableInfo.columnsByName[c.name]
1179
+ continue
1180
+ }
1181
+ c.isStatic = false
1182
+ }
1183
+ }
1184
+
1185
+ function getTokenToReplicaMapper(strategy, strategyOptions) {
1186
+ if (/SimpleStrategy$/.test(strategy)) {
1187
+ const rf = parseInt(strategyOptions["replication_factor"], 10)
1188
+ if (rf > 1) {
1189
+ return getTokenToReplicaSimpleMapper(rf)
1190
+ }
1191
+ }
1192
+ if (/NetworkTopologyStrategy$/.test(strategy)) {
1193
+ return getTokenToReplicaNetworkMapper(strategyOptions)
1194
+ }
1195
+ //default, wrap in an Array
1196
+ return function noStrategy(tokenizer, ring, primaryReplicas) {
1197
+ const replicas = {}
1198
+ for (const key in primaryReplicas) {
1199
+ if (!primaryReplicas.hasOwnProperty(key)) {
1200
+ continue
1201
+ }
1202
+ replicas[key] = [primaryReplicas[key]]
1203
+ }
1204
+ return replicas
1205
+ }
1206
+ }
1207
+
1208
+ /**
1209
+ * @param {Number} replicationFactor
1210
+ * @returns {function}
1211
+ */
1212
+ function getTokenToReplicaSimpleMapper(replicationFactor) {
1213
+ return function tokenSimpleStrategy(
1214
+ tokenizer,
1215
+ ringTokensAsStrings,
1216
+ primaryReplicas,
1217
+ ) {
1218
+ const ringLength = ringTokensAsStrings.length
1219
+ const rf = Math.min(replicationFactor, ringLength)
1220
+ const replicas = {}
1221
+ for (let i = 0; i < ringLength; i++) {
1222
+ const key = ringTokensAsStrings[i]
1223
+ const tokenReplicas = [primaryReplicas[key]]
1224
+ for (let j = 1; j < ringLength && tokenReplicas.length < rf; j++) {
1225
+ let nextReplicaIndex = i + j
1226
+ if (nextReplicaIndex >= ringLength) {
1227
+ //circle back
1228
+ nextReplicaIndex = nextReplicaIndex % ringLength
1229
+ }
1230
+ const nextReplica =
1231
+ primaryReplicas[ringTokensAsStrings[nextReplicaIndex]]
1232
+ // In the case of vnodes, consecutive sections of the ring can be assigned to the same host.
1233
+ if (tokenReplicas.indexOf(nextReplica) === -1) {
1234
+ tokenReplicas.push(nextReplica)
1235
+ }
1236
+ }
1237
+ replicas[key] = tokenReplicas
1238
+ }
1239
+ return replicas
1240
+ }
1241
+ }
1242
+
1243
+ /**
1244
+ * @param {Object} replicationFactors
1245
+ * @returns {Function}
1246
+ * @private
1247
+ */
1248
+ function getTokenToReplicaNetworkMapper(replicationFactors) {
1249
+ // A(DC1)
1250
+ //
1251
+ // H B(DC2)
1252
+ // |
1253
+ // G --+-- C(DC1)
1254
+ // |
1255
+ // F D(DC2)
1256
+ //
1257
+ // E(DC1)
1258
+ return function tokenNetworkStrategy(
1259
+ tokenizer,
1260
+ ringTokensAsStrings,
1261
+ primaryReplicas,
1262
+ datacenters,
1263
+ ) {
1264
+ const replicas = {}
1265
+ const ringLength = ringTokensAsStrings.length
1266
+
1267
+ for (let i = 0; i < ringLength; i++) {
1268
+ const key = ringTokensAsStrings[i]
1269
+ const tokenReplicas = []
1270
+ const replicasByDc = {}
1271
+ const racksPlaced = {}
1272
+ const skippedHosts = []
1273
+ for (let j = 0; j < ringLength; j++) {
1274
+ let nextReplicaIndex = i + j
1275
+ if (nextReplicaIndex >= ringLength) {
1276
+ //circle back
1277
+ nextReplicaIndex = nextReplicaIndex % ringLength
1278
+ }
1279
+ const h = primaryReplicas[ringTokensAsStrings[nextReplicaIndex]]
1280
+ // In the case of vnodes, consecutive sections of the ring can be assigned to the same host.
1281
+ if (tokenReplicas.indexOf(h) !== -1) {
1282
+ continue
1283
+ }
1284
+ const dc = h.datacenter
1285
+ //Check if the next replica belongs to one of the targeted dcs
1286
+ let dcRf = parseInt(replicationFactors[dc], 10)
1287
+ if (!dcRf) {
1288
+ continue
1289
+ }
1290
+ dcRf = Math.min(dcRf, datacenters[dc].hostLength)
1291
+ let dcReplicas = replicasByDc[dc] || 0
1292
+ //Amount of replicas per dc is greater than rf or the amount of host in the datacenter
1293
+ if (dcReplicas >= dcRf) {
1294
+ continue
1295
+ }
1296
+ let racksPlacedInDc = racksPlaced[dc]
1297
+ if (!racksPlacedInDc) {
1298
+ racksPlacedInDc = racksPlaced[dc] = new utils.HashSet()
1299
+ }
1300
+ if (
1301
+ h.rack &&
1302
+ racksPlacedInDc.contains(h.rack) &&
1303
+ racksPlacedInDc.length < datacenters[dc].racks.length
1304
+ ) {
1305
+ // We already selected a replica for this rack
1306
+ // Skip until replicas in other racks are added
1307
+ if (skippedHosts.length < dcRf - dcReplicas) {
1308
+ skippedHosts.push(h)
1309
+ }
1310
+ continue
1311
+ }
1312
+ replicasByDc[h.datacenter] = ++dcReplicas
1313
+ tokenReplicas.push(h)
1314
+ if (
1315
+ h.rack &&
1316
+ racksPlacedInDc.add(h.rack) &&
1317
+ racksPlacedInDc.length === datacenters[dc].racks.length
1318
+ ) {
1319
+ // We finished placing all replicas for all racks in this dc
1320
+ // Add the skipped hosts
1321
+ replicasByDc[dc] += addSkippedHosts(
1322
+ dcRf,
1323
+ dcReplicas,
1324
+ tokenReplicas,
1325
+ skippedHosts,
1326
+ )
1327
+ }
1328
+ if (
1329
+ isDoneForToken(
1330
+ replicationFactors,
1331
+ datacenters,
1332
+ replicasByDc,
1333
+ )
1334
+ ) {
1335
+ break
1336
+ }
1337
+ }
1338
+ replicas[key] = tokenReplicas
1339
+ }
1340
+ return replicas
1341
+ }
1342
+ }
1343
+
1344
+ /**
1345
+ * @returns {Number} The number of skipped hosts added.
1346
+ */
1347
+ function addSkippedHosts(dcRf, dcReplicas, tokenReplicas, skippedHosts) {
1348
+ let i
1349
+ for (i = 0; i < dcRf - dcReplicas && i < skippedHosts.length; i++) {
1350
+ tokenReplicas.push(skippedHosts[i])
1351
+ }
1352
+ return i
1353
+ }
1354
+
1355
+ function isDoneForToken(replicationFactors, datacenters, replicasByDc) {
1356
+ const keys = Object.keys(replicationFactors)
1357
+ for (let i = 0; i < keys.length; i++) {
1358
+ const dcName = keys[i]
1359
+ const dc = datacenters[dcName]
1360
+ if (!dc) {
1361
+ // A DC is included in the RF but the DC does not exist in the topology
1362
+ continue
1363
+ }
1364
+ const rf = Math.min(
1365
+ parseInt(replicationFactors[dcName], 10),
1366
+ dc.hostLength,
1367
+ )
1368
+ if (rf > 0 && (!replicasByDc[dcName] || replicasByDc[dcName] < rf)) {
1369
+ return false
1370
+ }
1371
+ }
1372
+ return true
1373
+ }
1374
+
1375
+ /**
1376
+ * Creates a new instance if the currentInstance is not valid for the
1377
+ * provided Cassandra version
1378
+ * @param {ClientOptions} options The client options
1379
+ * @param {ControlConnection} cc The control connection to be used
1380
+ * @param {Function} udtResolver The function to be used to retrieve the udts.
1381
+ * @param {Array.<Number>} [version] The cassandra version
1382
+ * @param {SchemaParser} [currentInstance] The current instance
1383
+ * @returns {SchemaParser}
1384
+ */
1385
+ function getByVersion(options, cc, udtResolver, version, currentInstance) {
1386
+ let parserConstructor = SchemaParserV1
1387
+ if (version && version[0] === 3) {
1388
+ parserConstructor = SchemaParserV2
1389
+ } else if (version && version[0] >= 4) {
1390
+ parserConstructor = SchemaParserV3
1391
+ }
1392
+ if (!currentInstance || !(currentInstance instanceof parserConstructor)) {
1393
+ return new parserConstructor(options, cc, udtResolver)
1394
+ }
1395
+ return currentInstance
1396
+ }
1397
+
1398
+ export { getByVersion, isDoneForToken }
1399
+ export default { getByVersion, isDoneForToken }