@ragestudio/scylla-odm 0.22.2 → 0.22.4
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.
- package/batch/index.d.ts +3 -3
- package/batch/index.d.ts.map +1 -1
- package/client.d.ts +6 -5
- package/client.d.ts.map +1 -1
- package/client.js +11 -12
- package/client.js.map +1 -1
- package/cql_gen/create_table.d.ts +1 -1
- package/cql_gen/create_table.d.ts.map +1 -1
- package/document/index.d.ts +3 -3
- package/document/index.d.ts.map +1 -1
- package/driver/LICENSE.txt +177 -0
- package/driver/NOTICE.txt +67 -0
- package/driver/auth/index.d.ts +37 -0
- package/driver/auth/index.js +37 -0
- package/driver/auth/no-auth-provider.js +73 -0
- package/driver/auth/plain-text-auth-provider.js +81 -0
- package/driver/auth/provider.js +77 -0
- package/driver/client-options.js +442 -0
- package/driver/client.js +1267 -0
- package/driver/concurrent/index.d.ts +49 -0
- package/driver/concurrent/index.js +366 -0
- package/driver/connection.js +1034 -0
- package/driver/control-connection.js +1282 -0
- package/driver/encoder.js +2316 -0
- package/driver/errors.js +223 -0
- package/driver/execution-options.js +612 -0
- package/driver/execution-profile.js +274 -0
- package/driver/host-connection-pool.js +587 -0
- package/driver/host.js +699 -0
- package/driver/index.d.ts +387 -0
- package/driver/index.js +81 -0
- package/driver/mapping/cache.js +214 -0
- package/driver/mapping/doc-info-adapter.js +171 -0
- package/driver/mapping/index.d.ts +219 -0
- package/driver/mapping/index.js +57 -0
- package/driver/mapping/mapper.js +225 -0
- package/driver/mapping/mapping-handler.js +641 -0
- package/driver/mapping/model-batch-item.js +215 -0
- package/driver/mapping/model-batch-mapper.js +141 -0
- package/driver/mapping/model-mapper.js +315 -0
- package/driver/mapping/model-mapping-info.js +225 -0
- package/driver/mapping/object-selector.js +417 -0
- package/driver/mapping/q.js +156 -0
- package/driver/mapping/query-generator.js +556 -0
- package/driver/mapping/result-mapper.js +123 -0
- package/driver/mapping/result.js +139 -0
- package/driver/mapping/table-mappings.js +133 -0
- package/driver/mapping/tree.js +160 -0
- package/driver/metadata/aggregate.js +79 -0
- package/driver/metadata/client-state.js +119 -0
- package/driver/metadata/data-collection.js +182 -0
- package/driver/metadata/event-debouncer.js +174 -0
- package/driver/metadata/index.d.ts +276 -0
- package/driver/metadata/index.js +1156 -0
- package/driver/metadata/materialized-view.js +49 -0
- package/driver/metadata/schema-function.js +98 -0
- package/driver/metadata/schema-index.js +166 -0
- package/driver/metadata/schema-parser.js +1399 -0
- package/driver/metadata/table-metadata.js +77 -0
- package/driver/operation-state.js +206 -0
- package/driver/policies/address-resolution.js +145 -0
- package/driver/policies/index.d.ts +241 -0
- package/driver/policies/index.js +110 -0
- package/driver/policies/load-balancing.js +970 -0
- package/driver/policies/reconnection.js +166 -0
- package/driver/policies/retry.js +326 -0
- package/driver/policies/speculative-execution.js +150 -0
- package/driver/policies/timestamp-generation.js +176 -0
- package/driver/prepare-handler.js +347 -0
- package/driver/promise-utils.js +191 -0
- package/driver/readers.js +624 -0
- package/driver/request-execution.js +644 -0
- package/driver/request-handler.js +332 -0
- package/driver/requests.js +618 -0
- package/driver/stream-id-stack.js +209 -0
- package/driver/streams.js +745 -0
- package/driver/token.js +325 -0
- package/driver/tokenizer.js +631 -0
- package/driver/types/big-decimal.js +282 -0
- package/driver/types/duration.js +576 -0
- package/driver/types/index.d.ts +486 -0
- package/driver/types/index.js +733 -0
- package/driver/types/inet-address.js +262 -0
- package/driver/types/integer.js +818 -0
- package/driver/types/local-date.js +280 -0
- package/driver/types/local-time.js +299 -0
- package/driver/types/mutable-long.js +385 -0
- package/driver/types/protocol-version.js +391 -0
- package/driver/types/result-set.js +287 -0
- package/driver/types/result-stream.js +164 -0
- package/driver/types/row.js +85 -0
- package/driver/types/time-uuid.js +414 -0
- package/driver/types/tuple.js +103 -0
- package/driver/types/uuid.js +160 -0
- package/driver/types/vector.js +130 -0
- package/driver/types/version-number.js +153 -0
- package/driver/utils.js +1485 -0
- package/driver/writers.js +350 -0
- package/global.d.ts +1 -1
- package/global.d.ts.map +1 -1
- package/index.d.ts +6 -6
- package/index.d.ts.map +1 -1
- package/index.js +6 -6
- package/index.js.map +1 -1
- package/migrate/index.d.ts +1 -1
- package/migrate/index.d.ts.map +1 -1
- package/migrate/index.js +1 -1
- package/migrate/index.js.map +1 -1
- package/model/index.d.ts +6 -6
- package/model/index.d.ts.map +1 -1
- package/model/index.js +10 -10
- package/model/index.js.map +1 -1
- package/operations/countAll.d.ts +1 -1
- package/operations/countAll.d.ts.map +1 -1
- package/operations/delete.d.ts +3 -4
- package/operations/delete.d.ts.map +1 -1
- package/operations/delete.js +1 -1
- package/operations/delete.js.map +1 -1
- package/operations/find.d.ts +2 -2
- package/operations/find.d.ts.map +1 -1
- package/operations/find.js +1 -1
- package/operations/find.js.map +1 -1
- package/operations/findOne.d.ts +2 -2
- package/operations/findOne.d.ts.map +1 -1
- package/operations/findOne.js +1 -1
- package/operations/findOne.js.map +1 -1
- package/operations/insert.d.ts +3 -3
- package/operations/insert.d.ts.map +1 -1
- package/operations/insert.js +2 -2
- package/operations/insert.js.map +1 -1
- package/operations/sync.d.ts +1 -1
- package/operations/sync.d.ts.map +1 -1
- package/operations/sync.js +1 -1
- package/operations/sync.js.map +1 -1
- package/operations/tableExists.d.ts +1 -1
- package/operations/tableExists.d.ts.map +1 -1
- package/operations/update.d.ts +3 -3
- package/operations/update.d.ts.map +1 -1
- package/operations/update.js +2 -2
- package/operations/update.js.map +1 -1
- package/package.json +4 -12
- package/schema/index.d.ts +1 -1
- package/schema/index.d.ts.map +1 -1
- package/types.d.ts +4 -4
- package/types.d.ts.map +1 -1
- package/utils/queryParser.d.ts +1 -1
- package/utils/queryParser.d.ts.map +1 -1
- package/utils/queryParser.js +1 -1
- package/utils/queryParser.js.map +1 -1
- package/utils/typeChecker.d.ts +1 -1
- package/utils/typeChecker.d.ts.map +1 -1
- package/utils/typeChecker.js +1 -1
- 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 }
|