@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,1267 @@
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
+ import events from "events"
19
+ import util from "util"
20
+
21
+ import utils from "./utils.js"
22
+ import errors from "./errors.js"
23
+ import types from "./types/index.js"
24
+ import { ProfileManager } from "./execution-profile.js"
25
+ import requests from "./requests.js"
26
+ import clientOptions from "./client-options.js"
27
+ import ClientState from "./metadata/client-state.js"
28
+ import { DefaultExecutionOptions } from "./execution-options.js"
29
+ import ControlConnection from "./control-connection.js"
30
+ import RequestHandler from "./request-handler.js"
31
+ import PrepareHandler from "./prepare-handler.js"
32
+ import promiseUtils from "./promise-utils.js"
33
+ import { description, version } from "../../package.json" with { type: "json" }
34
+
35
+ /**
36
+ * Max amount of pools being warmup in parallel, when warmup is enabled
37
+ * @private
38
+ */
39
+ const warmupLimit = 32
40
+
41
+ /**
42
+ * Client options.
43
+ * <p>While the driver provides lots of extensibility points and configurability, few client options are required.</p>
44
+ * <p>Default values for all settings are designed to be suitable for the majority of use cases, you should avoid
45
+ * fine tuning it when not needed.</p>
46
+ * <p>See [Client constructor]{@link Client} documentation for recommended options.</p>
47
+ * @typedef {Object} ClientOptions
48
+ * @property {Array.<string>} contactPoints
49
+ * Array of addresses or host names of the nodes to add as contact points.
50
+ * <p>
51
+ * Contact points are addresses of Cassandra nodes that the driver uses to discover the cluster topology.
52
+ * </p>
53
+ * <p>
54
+ * Only one contact point is required (the driver will retrieve the address of the other nodes automatically),
55
+ * but it is usually a good idea to provide more than one contact point, because if that single contact point is
56
+ * unavailable, the driver will not be able to initialize correctly.
57
+ * </p>
58
+ * @property {String} [localDataCenter] The local data center to use.
59
+ * <p>
60
+ * If using DCAwareRoundRobinPolicy (default), this option is required and only hosts from this data center are
61
+ * connected to and used in query plans.
62
+ * </p>
63
+ * @property {String} [keyspace] The logged keyspace for all the connections created within the {@link Client} instance.
64
+ * @property {Object} [credentials] An object containing the username and password for plain-text authentication.
65
+ * It configures the authentication provider to be used against Apache Cassandra's PasswordAuthenticator or DSE's
66
+ * DseAuthenticator, when default auth scheme is plain-text.
67
+ * <p>
68
+ * Note that you should configure either <code>credentials</code> or <code>authProvider</code> to connect to an
69
+ * auth-enabled cluster, but not both.
70
+ * </p>
71
+ * @property {String} [credentials.username] The username to use for plain-text authentication.
72
+ * @property {String} [credentials.password] The password to use for plain-text authentication.
73
+ * @property {Uuid} [id] A unique identifier assigned to a {@link Client} object, that will be communicated to the
74
+ * server (DSE 6.0+) to identify the client instance created with this options. When not defined, the driver will
75
+ * generate a random identifier.
76
+ * @property {String} [applicationName] An optional setting identifying the name of the application using
77
+ * the {@link Client} instance.
78
+ * <p>This value is passed to DSE and is useful as metadata for describing a client connection on the server side.</p>
79
+ * @property {String} [applicationVersion] An optional setting identifying the version of the application using
80
+ * the {@link Client} instance.
81
+ * <p>This value is passed to DSE and is useful as metadata for describing a client connection on the server side.</p>
82
+ * @property {Object} [monitorReporting] Options for reporting mechanism from the client to the DSE server, for
83
+ * versions that support it.
84
+ * @property {Boolean} [monitorReporting.enabled=true] Determines whether the reporting mechanism is enabled.
85
+ * Defaults to <code>true</code>.
86
+ * @property {Number} [refreshSchemaDelay] The default window size in milliseconds used to debounce node list and schema
87
+ * refresh metadata requests. Default: 1000.
88
+ * @property {Boolean} [isMetadataSyncEnabled] Determines whether client-side schema metadata retrieval and update is
89
+ * enabled.
90
+ * <p>Setting this value to <code>false</code> will cause keyspace information not to be automatically loaded, affecting
91
+ * replica calculation per token in the different keyspaces. When disabling metadata synchronization, use
92
+ * [Metadata.refreshKeyspaces()]{@link module:metadata~Metadata#refreshKeyspaces} to keep keyspace information up to
93
+ * date or token-awareness will not work correctly.</p>
94
+ * Default: <code>true</code>.
95
+ * @property {Boolean} [prepareOnAllHosts] Determines if the driver should prepare queries on all hosts in the cluster.
96
+ * Default: <code>true</code>.
97
+ * @property {Boolean} [rePrepareOnUp] Determines if the driver should re-prepare all cached prepared queries on a
98
+ * host when it marks it back up.
99
+ * Default: <code>true</code>.
100
+ * @property {Number} [maxPrepared] Determines the maximum amount of different prepared queries before evicting items
101
+ * from the internal cache. Reaching a high threshold hints that the queries are not being reused, like when
102
+ * hard-coding parameter values inside the queries.
103
+ * Default: <code>500</code>.
104
+ * @property {Object} [policies]
105
+ * @property {LoadBalancingPolicy} [policies.loadBalancing] The load balancing policy instance to be used to determine
106
+ * the coordinator per query.
107
+ * @property {RetryPolicy} [policies.retry] The retry policy.
108
+ * @property {ReconnectionPolicy} [policies.reconnection] The reconnection policy to be used.
109
+ * @property {AddressTranslator} [policies.addressResolution] The address resolution policy.
110
+ * @property {SpeculativeExecutionPolicy} [policies.speculativeExecution] The <code>SpeculativeExecutionPolicy</code>
111
+ * instance to be used to determine if the client should send speculative queries when the selected host takes more
112
+ * time than expected.
113
+ * <p>
114
+ * Default: <code>[NoSpeculativeExecutionPolicy]{@link
115
+ * module:policies/speculativeExecution~NoSpeculativeExecutionPolicy}</code>
116
+ * </p>
117
+ * @property {TimestampGenerator} [policies.timestampGeneration] The client-side
118
+ * [query timestamp generator]{@link module:policies/timestampGeneration~TimestampGenerator}.
119
+ * <p>
120
+ * Default: <code>[MonotonicTimestampGenerator]{@link module:policies/timestampGeneration~MonotonicTimestampGenerator}
121
+ * </code>
122
+ * </p>
123
+ * <p>Use <code>null</code> to disable client-side timestamp generation.</p>
124
+ * @property {QueryOptions} [queryOptions] Default options for all queries.
125
+ * @property {Object} [pooling] Pooling options.
126
+ * @property {Number} [pooling.heartBeatInterval] The amount of idle time in milliseconds that has to pass before the
127
+ * driver issues a request on an active connection to avoid idle time disconnections. Default: 30000.
128
+ * @property {Object} [pooling.coreConnectionsPerHost] Associative array containing amount of connections per host
129
+ * distance.
130
+ * @property {Number} [pooling.maxRequestsPerConnection] The maximum number of requests per connection. The default
131
+ * value is:
132
+ * <ul>
133
+ * <li>For modern protocol versions (v3 and above): 2048</li>
134
+ * <li>For older protocol versions (v1 and v2): 128</li>
135
+ * </ul>
136
+ * @property {Boolean} [pooling.warmup] Determines if all connections to hosts in the local datacenter must be opened on
137
+ * connect. Default: true.
138
+ * @property {Object} [protocolOptions]
139
+ * @property {Number} [protocolOptions.port] The port to use to connect to the Cassandra host. If not set through this
140
+ * method, the default port (9042) will be used instead.
141
+ * @property {Number} [protocolOptions.maxSchemaAgreementWaitSeconds] The maximum time in seconds to wait for schema
142
+ * agreement between nodes before returning from a DDL query. Default: 10.
143
+ * @property {Number} [protocolOptions.maxVersion] When set, it limits the maximum protocol version used to connect to
144
+ * the nodes.
145
+ * Useful for using the driver against a cluster that contains nodes with different major/minor versions of Cassandra.
146
+ * @property {Boolean} [protocolOptions.noCompact] When set to true, enables the NO_COMPACT startup option.
147
+ * <p>
148
+ * When this option is supplied <code>SELECT</code>, <code>UPDATE</code>, <code>DELETE</code>, and <code>BATCH</code>
149
+ * statements on <code>COMPACT STORAGE</code> tables function in "compatibility" mode which allows seeing these tables
150
+ * as if they were "regular" CQL tables.
151
+ * </p>
152
+ * <p>
153
+ * This option only effects interactions with interactions with tables using <code>COMPACT STORAGE</code> and is only
154
+ * supported by C* 3.0.16+, 3.11.2+, 4.0+ and DSE 6.0+.
155
+ * </p>
156
+ * @property {Object} [socketOptions]
157
+ * @property {Number} [socketOptions.connectTimeout] Connection timeout in milliseconds. Default: 5000.
158
+ * @property {Number} [socketOptions.defunctReadTimeoutThreshold] Determines the amount of requests that simultaneously
159
+ * have to timeout before closing the connection. Default: 64.
160
+ * @property {Boolean} [socketOptions.keepAlive] Whether to enable TCP keep-alive on the socket. Default: true.
161
+ * @property {Number} [socketOptions.keepAliveDelay] TCP keep-alive delay in milliseconds. Default: 0.
162
+ * @property {Number} [socketOptions.readTimeout] Per-host read timeout in milliseconds.
163
+ * <p>
164
+ * Please note that this is not the maximum time a call to {@link Client#execute} may have to wait;
165
+ * this is the maximum time that call will wait for one particular Cassandra host, but other hosts will be tried if
166
+ * one of them timeout. In other words, a {@link Client#execute} call may theoretically wait up to
167
+ * <code>readTimeout * number_of_cassandra_hosts</code> (though the total number of hosts tried for a given query also
168
+ * depends on the LoadBalancingPolicy in use).
169
+ * <p>When setting this value, keep in mind the following:</p>
170
+ * <ul>
171
+ * <li>the timeout settings used on the Cassandra side (*_request_timeout_in_ms in cassandra.yaml) should be taken
172
+ * into account when picking a value for this read timeout. You should pick a value a couple of seconds greater than
173
+ * the Cassandra timeout settings.
174
+ * </li>
175
+ * <li>
176
+ * the read timeout is only approximate and only control the timeout to one Cassandra host, not the full query.
177
+ * </li>
178
+ * </ul>
179
+ * Setting a value of 0 disables read timeouts. Default: <code>12000</code>.
180
+ * @property {Boolean} [socketOptions.tcpNoDelay] When set to true, it disables the Nagle algorithm. Default: true.
181
+ * @property {Number} [socketOptions.coalescingThreshold] Buffer length in bytes use by the write queue before flushing
182
+ * the frames. Default: 8000.
183
+ * @property {AuthProvider} [authProvider] Provider to be used to authenticate to an auth-enabled cluster.
184
+ * @property {RequestTracker} [requestTracker] The instance of RequestTracker used to monitor or log requests executed
185
+ * with this instance.
186
+ * @property {Object} [sslOptions] Client-to-node ssl options. When set the driver will use the secure layer.
187
+ * You can specify cert, ca, ... options named after the Node <code>tls.connect()</code> options.
188
+ * <p>
189
+ * It uses the same default values as Node <code>tls.connect()</code> except for <code>rejectUnauthorized</code>
190
+ * which is set to <code>false</code> by default (for historical reasons). This setting is likely to change
191
+ * in upcoming versions to enable validation by default.
192
+ * </p>
193
+ * @property {Object} [encoding] Encoding options.
194
+ * @property {Function} [encoding.map] Map constructor to use for Cassandra map<k,v> type encoding and decoding.
195
+ * If not set, it will default to Javascript Object with map keys as property names.
196
+ * @property {Function} [encoding.set] Set constructor to use for Cassandra set<k> type encoding and decoding.
197
+ * If not set, it will default to Javascript Array.
198
+ * @property {Boolean} [encoding.copyBuffer] Determines if the network buffer should be copied for buffer based data
199
+ * types (blob, uuid, timeuuid and inet).
200
+ * <p>
201
+ * Setting it to true will cause that the network buffer is copied for each row value of those types,
202
+ * causing additional allocations but freeing the network buffer to be reused.
203
+ * Setting it to true is a good choice for cases where the Row and ResultSet returned by the queries are long-lived
204
+ * objects.
205
+ * </p>
206
+ * <p>
207
+ * Setting it to false will cause less overhead and the reference of the network buffer to be maintained until the row
208
+ * / result set are de-referenced.
209
+ * Default: true.
210
+ * </p>
211
+ * @property {Boolean} [encoding.useUndefinedAsUnset] Valid for Cassandra 2.2 and above. Determines that, if a parameter
212
+ * is set to
213
+ * <code>undefined</code> it should be encoded as <code>unset</code>.
214
+ * <p>
215
+ * By default, ECMAScript <code>undefined</code> is encoded as <code>null</code> in the driver. Cassandra 2.2
216
+ * introduced the concept of unset.
217
+ * At driver level, you can set a parameter to unset using the field <code>types.unset</code>. Setting this flag to
218
+ * true allows you to use ECMAScript undefined as Cassandra <code>unset</code>.
219
+ * </p>
220
+ * <p>
221
+ * Default: true.
222
+ * </p>
223
+ * @property {Boolean} [encoding.useBigIntAsLong] Use [BigInt ECMAScript type](https://tc39.github.io/proposal-bigint/)
224
+ * to represent CQL bigint and counter data types.
225
+ * @property {Boolean} [encoding.useBigIntAsVarint] Use [BigInt ECMAScript
226
+ * type](https://tc39.github.io/proposal-bigint/) to represent CQL varint data type.
227
+ * @property {Array.<ExecutionProfile>} [profiles] The array of [execution profiles]{@link ExecutionProfile}.
228
+ * @property {Function} [promiseFactory] Function to be used to create a <code>Promise</code> from a
229
+ * callback-style function.
230
+ * <p>
231
+ * Promise libraries often provide different methods to create a promise. For example, you can use Bluebird's
232
+ * <code>Promise.fromCallback()</code> method.
233
+ * </p>
234
+ * <p>
235
+ * By default, the driver will use the
236
+ * [Promise constructor]{@link https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Promise}.
237
+ * </p>
238
+ */
239
+
240
+ /**
241
+ * Query options
242
+ * @typedef {Object} QueryOptions
243
+ * @property {Boolean} [autoPage] Determines if the driver must retrieve the following result pages automatically.
244
+ * <p>
245
+ * This setting is only considered by the [Client#eachRow()]{@link Client#eachRow} method. For more information,
246
+ * check the
247
+ * [paging results documentation]{@link https://docs.datastax.com/en/developer/nodejs-driver/latest/features/paging/}.
248
+ * </p>
249
+ * @property {Boolean} [captureStackTrace] Determines if the stack trace before the query execution should be
250
+ * maintained.
251
+ * <p>
252
+ * Useful for debugging purposes, it should be set to <code>false</code> under production environment as it adds an
253
+ * unnecessary overhead to each execution.
254
+ * </p>
255
+ * Default: false.
256
+ * @property {Number} [consistency] [Consistency level]{@link module:types~consistencies}.
257
+ * <p>
258
+ * Defaults to <code>localOne</code> for Apache Cassandra and DSE deployments.
259
+ * For DataStax Astra, it defaults to <code>localQuorum</code>.
260
+ * </p>
261
+ * @property {Object} [customPayload] Key-value payload to be passed to the server. On the Cassandra side,
262
+ * implementations of QueryHandler can use this data.
263
+ * @property {String} [executeAs] The user or role name to act as when executing this statement.
264
+ * <p>When set, it executes as a different user/role than the one currently authenticated (a.k.a. proxy execution).</p>
265
+ * <p>This feature is only available in DSE 5.1+.</p>
266
+ * @property {String|ExecutionProfile} [executionProfile] Name or instance of the [profile]{@link ExecutionProfile} to
267
+ * be used for this execution. If not set, it will the use "default" execution profile.
268
+ * @property {Number} [fetchSize] Amount of rows to retrieve per page.
269
+ * @property {Array|Array<Array>} [hints] Type hints for parameters given in the query, ordered as for the parameters.
270
+ * <p>For batch queries, an array of such arrays, ordered as with the queries in the batch.</p>
271
+ * @property {Host} [host] The host that should handle the query.
272
+ * <p>
273
+ * Use of this option is <em>heavily discouraged</em> and should only be used in the following cases:
274
+ * </p>
275
+ * <ol>
276
+ * <li>
277
+ * Querying node-local tables, such as tables in the <code>system</code> and <code>system_views</code>
278
+ * keyspaces.
279
+ * </li>
280
+ * <li>
281
+ * Applying a series of schema changes, where it may be advantageous to execute schema changes in sequence on the
282
+ * same node.
283
+ * </li>
284
+ * </ol>
285
+ * <p>
286
+ * Configuring a specific host causes the configured
287
+ * [LoadBalancingPolicy]{@link module:policies/loadBalancing~LoadBalancingPolicy} to be completely bypassed.
288
+ * However, if the load balancing policy dictates that the host is at a
289
+ * [distance of ignored]{@link module:types~distance} or there is no active connectivity to the host, the request will
290
+ * fail with a [NoHostAvailableError]{@link module:errors~NoHostAvailableError}.
291
+ * </p>
292
+ * @property {Boolean} [isIdempotent] Defines whether the query can be applied multiple times without changing the result
293
+ * beyond the initial application.
294
+ * <p>
295
+ * The query execution idempotence can be used at [RetryPolicy]{@link module:policies/retry~RetryPolicy} level to
296
+ * determine if an statement can be retried in case of request error or write timeout.
297
+ * </p>
298
+ * <p>Default: <code>false</code>.</p>
299
+ * @property {String} [keyspace] Specifies the keyspace for the query. It is used for the following:
300
+ * <ol>
301
+ * <li>To indicate what keyspace the statement is applicable to (protocol V5+ only). This is useful when the
302
+ * query does not provide an explicit keyspace and you want to override the current {@link Client#keyspace}.</li>
303
+ * <li>For query routing when the query operates on a different keyspace than the current {@link Client#keyspace}.</li>
304
+ * </ol>
305
+ * @property {Boolean} [logged] Determines if the batch should be written to the batchlog. Only valid for
306
+ * [Client#batch()]{@link Client#batch}, it will be ignored by other methods. Default: true.
307
+ * @property {Boolean} [counter] Determines if its a counter batch. Only valid for
308
+ * [Client#batch()]{@link Client#batch}, it will be ignored by other methods. Default: false.
309
+ * @property {Buffer|String} [pageState] Buffer or string token representing the paging state.
310
+ * <p>Useful for manual paging, if provided, the query will be executed starting from a given paging state.</p>
311
+ * @property {Boolean} [prepare] Determines if the query must be executed as a prepared statement.
312
+ * @property {Number} [readTimeout] When defined, it overrides the default read timeout
313
+ * (<code>socketOptions.readTimeout</code>) in milliseconds for this execution per coordinator.
314
+ * <p>
315
+ * Suitable for statements for which the coordinator may allow a longer server-side timeout, for example aggregation
316
+ * queries.
317
+ * </p>
318
+ * <p>
319
+ * A value of <code>0</code> disables client side read timeout for the execution. Default: <code>undefined</code>.
320
+ * </p>
321
+ * @property {RetryPolicy} [retry] Retry policy for the query.
322
+ * <p>
323
+ * This property can be used to specify a different [retry policy]{@link module:policies/retry} to the one specified
324
+ * in the {@link ClientOptions}.policies.
325
+ * </p>
326
+ * @property {Array} [routingIndexes] Index of the parameters that are part of the partition key to determine
327
+ * the routing.
328
+ * @property {Buffer|Array} [routingKey] Partition key(s) to determine which coordinator should be used for the query.
329
+ * @property {Array} [routingNames] Array of the parameters names that are part of the partition key to determine the
330
+ * routing. Only valid for non-prepared requests, it's recommended that you use the prepare flag instead.
331
+ * @property {Number} [serialConsistency] Serial consistency is the consistency level for the serial phase of
332
+ * conditional updates.
333
+ * This option will be ignored for anything else that a conditional update/insert.
334
+ * @property {Number|Long} [timestamp] The default timestamp for the query in microseconds from the unix epoch
335
+ * (00:00:00, January 1st, 1970).
336
+ * <p>If provided, this will replace the server side assigned timestamp as default timestamp.</p>
337
+ * <p>Use [generateTimestamp()]{@link module:types~generateTimestamp} utility method to generate a valid timestamp
338
+ * based on a Date and microseconds parts.</p>
339
+ * @property {Boolean} [traceQuery] Enable query tracing for the execution. Use query tracing to diagnose performance
340
+ * problems related to query executions. Default: false.
341
+ * <p>To retrieve trace, you can call [Metadata.getTrace()]{@link module:metadata~Metadata#getTrace} method.</p>
342
+ * @property {Object} [graphOptions] Default options for graph query executions.
343
+ * <p>
344
+ * These options are meant to provide defaults for all graph query executions. Consider using
345
+ * [execution profiles]{@link ExecutionProfile} if you plan to reuse different set of options across different
346
+ * query executions.
347
+ * </p>
348
+ * @property {String} [graphOptions.language] The graph language to use in graph queries. Default:
349
+ * <code>'gremlin-groovy'</code>.
350
+ * @property {String} [graphOptions.name] The graph name to be used in all graph queries.
351
+ * <p>
352
+ * This property is required but there is no default value for it. This value can be overridden at query level.
353
+ * </p>
354
+ * @property {Number} [graphOptions.readConsistency] Overrides the
355
+ * [consistency level]{@link module:types~consistencies}
356
+ * defined in the query options for graph read queries.
357
+ * @property {Number} [graphOptions.readTimeout] Overrides the default per-host read timeout (in milliseconds) for all
358
+ * graph queries. Default: <code>0</code>.
359
+ * <p>
360
+ * Use <code>null</code> to reset the value and use the default on <code>socketOptions.readTimeout</code> .
361
+ * </p>
362
+ * @property {String} [graphOptions.source] The graph traversal source name to use in graph queries. Default:
363
+ * <code>'g'</code>.
364
+ * @property {Number} [graphOptions.writeConsistency] Overrides the [consistency
365
+ * level]{@link module:types~consistencies} defined in the query options for graph write queries.
366
+ */
367
+
368
+ /**
369
+ * Creates a new instance of {@link Client}.
370
+ * @classdesc
371
+ * Represents a database client that maintains multiple connections to the cluster nodes, providing methods to
372
+ * execute CQL statements.
373
+ * <p>
374
+ * The <code>Client</code> uses [policies]{@link module:policies} to decide which nodes to connect to, which node
375
+ * to use per each query execution, when it should retry failed or timed-out executions and how reconnection to down
376
+ * nodes should be made.
377
+ * </p>
378
+ * @extends EventEmitter
379
+ * @param {ClientOptions} options The options for this instance.
380
+ * @example <caption>Creating a new client instance</caption>
381
+ * const client = new Client({
382
+ * contactPoints: ['10.0.1.101', '10.0.1.102'],
383
+ * localDataCenter: 'datacenter1'
384
+ * });
385
+ * @example <caption>Executing a query</caption>
386
+ * const result = await client.connect();
387
+ * console.log(`Connected to ${client.hosts.length} nodes in the cluster: ${client.hosts.keys().join(', ')}`);
388
+ * @example <caption>Executing a query</caption>
389
+ * const result = await client.execute('SELECT key FROM system.local');
390
+ * const row = result.first();
391
+ * console.log(row['key']);
392
+ * @constructor
393
+ */
394
+ function Client(options) {
395
+ events.EventEmitter.call(this)
396
+ this.options = clientOptions.extend(
397
+ { logEmitter: this.emit.bind(this), id: types.Uuid.random() },
398
+ options,
399
+ )
400
+ Object.defineProperty(this, "profileManager", {
401
+ value: new ProfileManager(this.options),
402
+ })
403
+ Object.defineProperty(this, "controlConnection", {
404
+ value: new ControlConnection(this.options, this.profileManager),
405
+ writable: true,
406
+ })
407
+ //Unlimited amount of listeners for internal event queues by default
408
+ this.setMaxListeners(0)
409
+ this.connected = false
410
+ this.isShuttingDown = false
411
+ /**
412
+ * Gets the name of the active keyspace.
413
+ * @type {String}
414
+ */
415
+ this.keyspace = options.keyspace
416
+ /**
417
+ * Gets the schema and cluster metadata information.
418
+ * @type {Metadata}
419
+ */
420
+ this.metadata = this.controlConnection.metadata
421
+ /**
422
+ * Gets an associative array of cluster hosts.
423
+ * @type {HostMap}
424
+ */
425
+ this.hosts = this.controlConnection.hosts
426
+
427
+ // metrics are not available in this build (noop stub from client-options)
428
+ this.metrics = this.options.metrics
429
+ }
430
+
431
+ util.inherits(Client, events.EventEmitter)
432
+
433
+ /**
434
+ * Emitted when a new host is added to the cluster.
435
+ * <ul>
436
+ * <li>{@link Host} The host being added.</li>
437
+ * </ul>
438
+ * @event Client#hostAdd
439
+ */
440
+ /**
441
+ * Emitted when a host is removed from the cluster
442
+ * <ul>
443
+ * <li>{@link Host} The host being removed.</li>
444
+ * </ul>
445
+ * @event Client#hostRemove
446
+ */
447
+ /**
448
+ * Emitted when a host in the cluster changed status from down to up.
449
+ * <ul>
450
+ * <li>{@link Host host} The host that changed the status.</li>
451
+ * </ul>
452
+ * @event Client#hostUp
453
+ */
454
+ /**
455
+ * Emitted when a host in the cluster changed status from up to down.
456
+ * <ul>
457
+ * <li>{@link Host host} The host that changed the status.</li>
458
+ * </ul>
459
+ * @event Client#hostDown
460
+ */
461
+
462
+ /**
463
+ * Attempts to connect to one of the [contactPoints]{@link ClientOptions} and discovers the rest the nodes of the
464
+ * cluster.
465
+ * <p>When the {@link Client} is already connected, it resolves immediately.</p>
466
+ * <p>It returns a <code>Promise</code> when a <code>callback</code> is not provided.</p>
467
+ * @param {function} [callback] The optional callback that is invoked when the pool is connected or it failed to
468
+ * connect.
469
+ * @example <caption>Usage example</caption>
470
+ * await client.connect();
471
+ */
472
+ Client.prototype.connect = function (callback) {
473
+ if (this.connected && callback) {
474
+ // Avoid creating Promise to immediately resolve them
475
+ return callback()
476
+ }
477
+
478
+ return promiseUtils.optionalCallback(this._connect(), callback)
479
+ }
480
+
481
+ /**
482
+ * Async-only version of {@link Client#connect()}.
483
+ * @private
484
+ */
485
+ Client.prototype._connect = async function () {
486
+ if (this.connected) {
487
+ return
488
+ }
489
+
490
+ if (this.isShuttingDown) {
491
+ //it is being shutdown, don't allow further calls to connect()
492
+ throw new errors.NoHostAvailableError(
493
+ null,
494
+ "Connecting after shutdown is not supported",
495
+ )
496
+ }
497
+
498
+ if (this.connecting) {
499
+ return promiseUtils.fromEvent(this, "connected")
500
+ }
501
+
502
+ this.connecting = true
503
+ this.log(
504
+ "info",
505
+ util.format(
506
+ "Connecting to cluster using '%s' version %s",
507
+ description,
508
+ version,
509
+ ),
510
+ )
511
+
512
+ try {
513
+ await this.controlConnection.init()
514
+ this.hosts = this.controlConnection.hosts
515
+ await this.profileManager.init(this, this.hosts)
516
+
517
+ if (this.keyspace) {
518
+ await RequestHandler.setKeyspace(this)
519
+ }
520
+
521
+ clientOptions.setMetadataDependent(this)
522
+
523
+ await this._warmup()
524
+ } catch (err) {
525
+ // We should close the pools (if any) and reset the state to allow successive calls to connect()
526
+ await this.controlConnection.reset()
527
+ this.connected = false
528
+ this.connecting = false
529
+ this.emit("connected", err)
530
+ throw err
531
+ }
532
+
533
+ this._setHostListeners()
534
+
535
+ // Set the distance of the control connection host relatively to this instance
536
+ this.profileManager.getDistance(this.controlConnection.host)
537
+ this.connected = true
538
+ this.connecting = false
539
+ this.emit("connected")
540
+ }
541
+
542
+ /**
543
+ * Executes a query on an available connection.
544
+ * <p>The query can be prepared (recommended) or not depending on the [prepare]{@linkcode QueryOptions} flag.</p>
545
+ * <p>
546
+ * Some execution failures can be handled transparently by the driver, according to the
547
+ * [RetryPolicy]{@linkcode module:policies/retry~RetryPolicy} or the
548
+ * [SpeculativeExecutionPolicy]{@linkcode module:policies/speculativeExecution} used.
549
+ * </p>
550
+ * <p>It returns a <code>Promise</code> when a <code>callback</code> is not provided.</p>
551
+ * @param {String} query The query to execute.
552
+ * @param {Array|Object} [params] Array of parameter values or an associative array (object) containing parameter names
553
+ * as keys and its value.
554
+ * @param {QueryOptions} [options] The query options for the execution.
555
+ * @param {ResultCallback} [callback] Executes callback(err, result) when execution completed. When not defined, the
556
+ * method will return a promise.
557
+ * @example <caption>Promise-based API, using async/await</caption>
558
+ * const query = 'SELECT name, email FROM users WHERE id = ?';
559
+ * const result = await client.execute(query, [ id ], { prepare: true });
560
+ * const row = result.first();
561
+ * console.log('%s: %s', row['name'], row['email']);
562
+ * @example <caption>Callback-based API</caption>
563
+ * const query = 'SELECT name, email FROM users WHERE id = ?';
564
+ * client.execute(query, [ id ], { prepare: true }, function (err, result) {
565
+ * assert.ifError(err);
566
+ * const row = result.first();
567
+ * console.log('%s: %s', row['name'], row['email']);
568
+ * });
569
+ * @see {@link ExecutionProfile} to reuse a set of options across different query executions.
570
+ */
571
+ Client.prototype.execute = function (query, params, options, callback) {
572
+ // This method acts as a wrapper for the async method _execute()
573
+
574
+ if (!callback) {
575
+ // Set default argument values for optional parameters
576
+ if (typeof options === "function") {
577
+ callback = options
578
+ options = null
579
+ } else if (typeof params === "function") {
580
+ callback = params
581
+ params = null
582
+ }
583
+ }
584
+
585
+ try {
586
+ const execOptions = DefaultExecutionOptions.create(options, this)
587
+ return promiseUtils.optionalCallback(
588
+ this._execute(query, params, execOptions),
589
+ callback,
590
+ )
591
+ } catch (err) {
592
+ // There was an error when parsing the user options
593
+ if (callback) {
594
+ return callback(err)
595
+ }
596
+
597
+ return Promise.reject(err)
598
+ }
599
+ }
600
+
601
+ /**
602
+ * Executes a graph query.
603
+ * <p>It returns a <code>Promise</code> when a <code>callback</code> is not provided.</p>
604
+ * @param {String} query The gremlin query.
605
+ * @param {Object|null} [parameters] An associative array containing the key and values of the parameters.
606
+ * @param {GraphQueryOptions|null} [options] The graph query options.
607
+ * @param {Function} [callback] Function to execute when the response is retrieved, taking two arguments:
608
+ * <code>err</code> and <code>result</code>. When not defined, the method will return a promise.
609
+ * @example <caption>Promise-based API, using async/await</caption>
610
+ * const result = await client.executeGraph('g.V()');
611
+ * // Get the first item (vertex, edge, scalar value, ...)
612
+ * const vertex = result.first();
613
+ * console.log(vertex.label);
614
+ * @example <caption>Callback-based API</caption>
615
+ * client.executeGraph('g.V()', (err, result) => {
616
+ * const vertex = result.first();
617
+ * console.log(vertex.label);
618
+ * });
619
+ * @example <caption>Iterating through the results</caption>
620
+ * const result = await client.executeGraph('g.E()');
621
+ * for (let edge of result) {
622
+ * console.log(edge.label); // created
623
+ * });
624
+ * @example <caption>Using result.forEach()</caption>
625
+ * const result = await client.executeGraph('g.V().hasLabel("person")');
626
+ * result.forEach(function(vertex) {
627
+ * console.log(vertex.type); // vertex
628
+ * console.log(vertex.label); // person
629
+ * });
630
+ * @see {@link ExecutionProfile} to reuse a set of options across different query executions.
631
+ */
632
+ Client.prototype.executeGraph = function () {
633
+ throw new Error("Graph queries are not supported in this build")
634
+ }
635
+
636
+ /**
637
+ * Executes the query and calls <code>rowCallback</code> for each row as soon as they are received. Calls the final
638
+ * <code>callback</code> after all rows have been sent, or when there is an error.
639
+ * <p>
640
+ * The query can be prepared (recommended) or not depending on the [prepare]{@linkcode QueryOptions} flag.
641
+ * </p>
642
+ * @param {String} query The query to execute
643
+ * @param {Array|Object} [params] Array of parameter values or an associative array (object) containing parameter names
644
+ * as keys and its value.
645
+ * @param {QueryOptions} [options] The query options.
646
+ * @param {function} rowCallback Executes <code>rowCallback(n, row)</code> per each row received, where n is the row
647
+ * index and row is the current Row.
648
+ * @param {function} [callback] Executes <code>callback(err, result)</code> after all rows have been received.
649
+ * <p>
650
+ * When dealing with paged results, [ResultSet#nextPage()]{@link module:types~ResultSet#nextPage} method can be used
651
+ * to retrieve the following page. In that case, <code>rowCallback()</code> will be again called for each row and
652
+ * the final callback will be invoked when all rows in the following page has been retrieved.
653
+ * </p>
654
+ * @example <caption>Using per-row callback and arrow functions</caption>
655
+ * client.eachRow(query, params, { prepare: true }, (n, row) => console.log(n, row), err => console.error(err));
656
+ * @example <caption>Overloads</caption>
657
+ * client.eachRow(query, rowCallback);
658
+ * client.eachRow(query, params, rowCallback);
659
+ * client.eachRow(query, params, options, rowCallback);
660
+ * client.eachRow(query, params, rowCallback, callback);
661
+ * client.eachRow(query, params, options, rowCallback, callback);
662
+ */
663
+ Client.prototype.eachRow = function (
664
+ query,
665
+ params,
666
+ options,
667
+ rowCallback,
668
+ callback,
669
+ ) {
670
+ if (!callback && rowCallback && typeof options === "function") {
671
+ callback = utils.validateFn(rowCallback, "rowCallback")
672
+ rowCallback = options
673
+ } else {
674
+ callback = callback || utils.noop
675
+ rowCallback = utils.validateFn(
676
+ rowCallback || options || params,
677
+ "rowCallback",
678
+ )
679
+ }
680
+
681
+ params = typeof params !== "function" ? params : null
682
+
683
+ let execOptions
684
+ try {
685
+ execOptions = DefaultExecutionOptions.create(options, this, rowCallback)
686
+ } catch (e) {
687
+ return callback(e)
688
+ }
689
+
690
+ let rowLength = 0
691
+
692
+ const nextPage = () =>
693
+ promiseUtils.toCallback(
694
+ this._execute(query, params, execOptions),
695
+ pageCallback,
696
+ )
697
+
698
+ function pageCallback(err, result) {
699
+ if (err) {
700
+ return callback(err)
701
+ }
702
+ // Next requests in case paging (auto or explicit) is used
703
+ rowLength += result.rowLength
704
+
705
+ if (result.rawPageState !== undefined) {
706
+ // Use new page state as next request page state
707
+ execOptions.setPageState(result.rawPageState)
708
+ if (execOptions.isAutoPage()) {
709
+ // Issue next request for the next page
710
+ return nextPage()
711
+ }
712
+ // Allows for explicit (manual) paging, in case the caller needs it
713
+ result.nextPage = nextPage
714
+ }
715
+
716
+ // Finished auto-paging
717
+ result.rowLength = rowLength
718
+ callback(null, result)
719
+ }
720
+
721
+ promiseUtils.toCallback(
722
+ this._execute(query, params, execOptions),
723
+ pageCallback,
724
+ )
725
+ }
726
+
727
+ /**
728
+ * Executes the query and pushes the rows to the result stream as soon as they received.
729
+ * <p>
730
+ * The stream is a [ReadableStream]{@linkcode https://nodejs.org/api/stream.html#stream_class_stream_readable} object
731
+ * that emits rows.
732
+ * It can be piped downstream and provides automatic pause/resume logic (it buffers when not read).
733
+ * </p>
734
+ * <p>
735
+ * The query can be prepared (recommended) or not depending on {@link QueryOptions}.prepare flag. Retries on multiple
736
+ * hosts if needed.
737
+ * </p>
738
+ * @param {String} query The query to prepare and execute.
739
+ * @param {Array|Object} [params] Array of parameter values or an associative array (object) containing parameter names
740
+ * as keys and its value
741
+ * @param {QueryOptions} [options] The query options.
742
+ * @param {function} [callback] executes callback(err) after all rows have been received or if there is an error
743
+ * @returns {ResultStream}
744
+ */
745
+ Client.prototype.stream = function (query, params, options, callback) {
746
+ callback = callback || utils.noop
747
+ // NOTE: the nodejs stream maintains yet another internal buffer
748
+ // we rely on the default stream implementation to keep memory
749
+ // usage reasonable.
750
+ const resultStream = new types.ResultStream({ objectMode: 1 })
751
+ function onFinish(err, result) {
752
+ if (err) {
753
+ resultStream.emit("error", err)
754
+ }
755
+ if (result && result.nextPage) {
756
+ // allows for throttling as per the
757
+ // default nodejs stream implementation
758
+ resultStream._valve(function pageValve() {
759
+ try {
760
+ result.nextPage()
761
+ } catch (ex) {
762
+ resultStream.emit("error", ex)
763
+ }
764
+ })
765
+ return
766
+ }
767
+ // Explicitly dropping the valve (closure)
768
+ resultStream._valve(null)
769
+ resultStream.add(null)
770
+ callback(err)
771
+ }
772
+ let sync = true
773
+ this.eachRow(
774
+ query,
775
+ params,
776
+ options,
777
+ function rowCallback(n, row) {
778
+ resultStream.add(row)
779
+ },
780
+ function eachRowFinished(err, result) {
781
+ if (sync) {
782
+ // Prevent sync callback
783
+ return setImmediate(function eachRowFinishedImmediate() {
784
+ onFinish(err, result)
785
+ })
786
+ }
787
+ onFinish(err, result)
788
+ },
789
+ )
790
+ sync = false
791
+ return resultStream
792
+ }
793
+
794
+ /**
795
+ * Executes batch of queries on an available connection to a host.
796
+ * <p>It returns a <code>Promise</code> when a <code>callback</code> is not provided.</p>
797
+ * @param {Array.<string>|Array.<{query, params}>} queries The queries to execute as an Array of strings or as an array
798
+ * of object containing the query and params
799
+ * @param {QueryOptions} [options] The query options.
800
+ * @param {ResultCallback} [callback] Executes callback(err, result) when the batch was executed
801
+ */
802
+ Client.prototype.batch = function (queries, options, callback) {
803
+ if (!callback && typeof options === "function") {
804
+ callback = options
805
+ options = null
806
+ }
807
+
808
+ return promiseUtils.optionalCallback(
809
+ this._batch(queries, options),
810
+ callback,
811
+ )
812
+ }
813
+
814
+ /**
815
+ * Async-only version of {@link Client#batch()} .
816
+ * @param {Array.<string>|Array.<{query, params}>}queries
817
+ * @param {QueryOptions} options
818
+ * @returns {Promise<ResultSet>}
819
+ * @private
820
+ */
821
+ Client.prototype._batch = async function (queries, options) {
822
+ if (!Array.isArray(queries)) {
823
+ throw new errors.ArgumentError("Queries should be an Array")
824
+ }
825
+
826
+ if (queries.length === 0) {
827
+ throw new errors.ArgumentError("Queries array should not be empty")
828
+ }
829
+
830
+ await this._connect()
831
+
832
+ const execOptions = DefaultExecutionOptions.create(options, this)
833
+ let queryItems
834
+
835
+ if (execOptions.isPrepared()) {
836
+ // use keyspace from query options if protocol supports per-query keyspace, otherwise use connection keyspace.
837
+ const version = this.controlConnection.protocolVersion
838
+ const queryKeyspace =
839
+ (types.protocolVersion.supportsKeyspaceInRequest(version) &&
840
+ options.keyspace) ||
841
+ this.keyspace
842
+ queryItems = await PrepareHandler.getPreparedMultiple(
843
+ this,
844
+ execOptions.getLoadBalancingPolicy(),
845
+ queries,
846
+ queryKeyspace,
847
+ )
848
+ } else {
849
+ queryItems = new Array(queries.length)
850
+
851
+ for (let i = 0; i < queries.length; i++) {
852
+ const item = queries[i]
853
+ if (!item) {
854
+ throw new errors.ArgumentError(`Invalid query at index ${i}`)
855
+ }
856
+
857
+ const query = typeof item === "string" ? item : item.query
858
+ if (!query) {
859
+ throw new errors.ArgumentError(`Invalid query at index ${i}`)
860
+ }
861
+
862
+ queryItems[i] = { query, params: item.params }
863
+ }
864
+ }
865
+
866
+ const request = await this._createBatchRequest(queryItems, execOptions)
867
+ return await RequestHandler.send(request, execOptions, this)
868
+ }
869
+
870
+ /**
871
+ * Gets the host that are replicas of a given token.
872
+ * @param {String} keyspace
873
+ * @param {Buffer} token
874
+ * @returns {Array<Host>}
875
+ */
876
+ Client.prototype.getReplicas = function (keyspace, token) {
877
+ return this.metadata.getReplicas(keyspace, token)
878
+ }
879
+
880
+ /**
881
+ * Gets a snapshot containing information on the connections pools held by this Client at the current time.
882
+ * <p>
883
+ * The information provided in the returned object only represents the state at the moment this method was called and
884
+ * it's not maintained in sync with the driver metadata.
885
+ * </p>
886
+ * @returns {ClientState} A [ClientState]{@linkcode module:metadata~ClientState} instance.
887
+ */
888
+ Client.prototype.getState = function () {
889
+ return ClientState.from(this)
890
+ }
891
+
892
+ Client.prototype.log = utils.log
893
+
894
+ /**
895
+ * Closes all connections to all hosts.
896
+ * <p>It returns a <code>Promise</code> when a <code>callback</code> is not provided.</p>
897
+ * @param {Function} [callback] Optional callback to be invoked when finished closing all connections.
898
+ */
899
+ Client.prototype.shutdown = function (callback) {
900
+ return promiseUtils.optionalCallback(this._shutdown(), callback)
901
+ }
902
+
903
+ /** @private */
904
+ Client.prototype._shutdown = async function () {
905
+ this.log("info", "Shutting down")
906
+
907
+ if (!this.hosts || !this.connected) {
908
+ // not initialized
909
+ this.connected = false
910
+ return
911
+ }
912
+
913
+ if (this.connecting) {
914
+ this.log("warning", "Shutting down while connecting")
915
+ // wait until finish connecting for easier troubleshooting
916
+ await promiseUtils.fromEvent(this, "connected")
917
+ }
918
+
919
+ this.connected = false
920
+ this.isShuttingDown = true
921
+ const hosts = this.hosts.values()
922
+
923
+ // Shutdown the ControlConnection before shutting down the pools
924
+ this.controlConnection.shutdown()
925
+ this.options.policies.speculativeExecution.shutdown()
926
+
927
+ if (this.options.requestTracker) {
928
+ this.options.requestTracker.shutdown()
929
+ }
930
+
931
+ // go through all the host and shut down their pools
932
+ await Promise.all(hosts.map((h) => h.shutdown(false)))
933
+ }
934
+
935
+ /**
936
+ * Waits until that the schema version in all nodes is the same or the waiting time passed.
937
+ * @param {Connection} connection
938
+ * @returns {Promise<boolean>}
939
+ * @ignore
940
+ */
941
+ Client.prototype._waitForSchemaAgreement = async function (connection) {
942
+ if (this.hosts.length === 1) {
943
+ return true
944
+ }
945
+
946
+ const start = process.hrtime()
947
+ const maxWaitSeconds =
948
+ this.options.protocolOptions.maxSchemaAgreementWaitSeconds
949
+
950
+ this.log("info", "Waiting for schema agreement")
951
+
952
+ let versionsMatch
953
+
954
+ while (!versionsMatch && process.hrtime(start)[0] < maxWaitSeconds) {
955
+ versionsMatch = await this.metadata.compareSchemaVersions(connection)
956
+
957
+ if (versionsMatch) {
958
+ this.log("info", "Schema versions match")
959
+ break
960
+ }
961
+
962
+ // Let some time pass before the next check
963
+ await promiseUtils.delay(500)
964
+ }
965
+
966
+ return versionsMatch
967
+ }
968
+
969
+ /**
970
+ * Waits for schema agreements and schedules schema metadata refresh.
971
+ * @param {Connection} connection
972
+ * @param event
973
+ * @returns {Promise<boolean>}
974
+ * @ignore
975
+ * @internal
976
+ */
977
+ Client.prototype.handleSchemaAgreementAndRefresh = async function (
978
+ connection,
979
+ event,
980
+ ) {
981
+ let agreement = false
982
+
983
+ try {
984
+ agreement = await this._waitForSchemaAgreement(connection)
985
+ } catch (err) {
986
+ //we issue a warning but we continue with the normal flow
987
+ this.log(
988
+ "warning",
989
+ "There was an error while waiting for the schema agreement between nodes",
990
+ err,
991
+ )
992
+ }
993
+
994
+ if (!this.options.isMetadataSyncEnabled) {
995
+ return agreement
996
+ }
997
+
998
+ // Refresh metadata immediately
999
+ try {
1000
+ await this.controlConnection.handleSchemaChange(event, true)
1001
+ } catch (err) {
1002
+ this.log(
1003
+ "warning",
1004
+ "There was an error while handling schema change",
1005
+ err,
1006
+ )
1007
+ }
1008
+
1009
+ return agreement
1010
+ }
1011
+
1012
+ /**
1013
+ * Connects and handles the execution of prepared and simple statements.
1014
+ * @param {string} query
1015
+ * @param {Array} params
1016
+ * @param {ExecutionOptions} execOptions
1017
+ * @returns {Promise<ResultSet>}
1018
+ * @private
1019
+ */
1020
+ Client.prototype._execute = async function (query, params, execOptions) {
1021
+ const version = this.controlConnection.protocolVersion
1022
+
1023
+ if (
1024
+ !execOptions.isPrepared() &&
1025
+ params &&
1026
+ !Array.isArray(params) &&
1027
+ !types.protocolVersion.supportsNamedParameters(version)
1028
+ ) {
1029
+ // Only Cassandra 2.1 and above supports named parameters
1030
+ throw new errors.ArgumentError(
1031
+ "Named parameters for simple statements are not supported, use prepare flag",
1032
+ )
1033
+ }
1034
+
1035
+ let request
1036
+
1037
+ if (!this.connected) {
1038
+ // Micro optimization to avoid an async execution for a simple check
1039
+ await this._connect()
1040
+ }
1041
+
1042
+ if (!execOptions.isPrepared()) {
1043
+ request = await this._createQueryRequest(query, execOptions, params)
1044
+ } else {
1045
+ const lbp = execOptions.getLoadBalancingPolicy()
1046
+
1047
+ // Use keyspace from query options if protocol supports per-query keyspace, otherwise use connection keyspace.
1048
+ const queryKeyspace =
1049
+ (types.protocolVersion.supportsKeyspaceInRequest(version) &&
1050
+ execOptions.getKeyspace()) ||
1051
+ this.keyspace
1052
+
1053
+ const { queryId, meta } = await PrepareHandler.getPrepared(
1054
+ this,
1055
+ lbp,
1056
+ query,
1057
+ queryKeyspace,
1058
+ )
1059
+ request = await this._createExecuteRequest(
1060
+ query,
1061
+ queryId,
1062
+ execOptions,
1063
+ params,
1064
+ meta,
1065
+ )
1066
+ }
1067
+
1068
+ return await RequestHandler.send(request, execOptions, this)
1069
+ }
1070
+
1071
+ /**
1072
+ * Sets the listeners for the nodes.
1073
+ * @private
1074
+ */
1075
+ Client.prototype._setHostListeners = function () {
1076
+ function getHostUpListener(emitter, h) {
1077
+ return () => emitter.emit("hostUp", h)
1078
+ }
1079
+
1080
+ function getHostDownListener(emitter, h) {
1081
+ return () => emitter.emit("hostDown", h)
1082
+ }
1083
+
1084
+ const self = this
1085
+
1086
+ // Add status listeners when new nodes are added and emit hostAdd
1087
+ this.hosts.on("add", function hostAddedListener(h) {
1088
+ h.on("up", getHostUpListener(self, h))
1089
+ h.on("down", getHostDownListener(self, h))
1090
+ self.emit("hostAdd", h)
1091
+ })
1092
+
1093
+ // Remove all listeners and emit hostRemove
1094
+ this.hosts.on("remove", function hostRemovedListener(h) {
1095
+ h.removeAllListeners()
1096
+ self.emit("hostRemove", h)
1097
+ })
1098
+
1099
+ // Add status listeners for existing hosts
1100
+ this.hosts.forEach(function (h) {
1101
+ h.on("up", getHostUpListener(self, h))
1102
+ h.on("down", getHostDownListener(self, h))
1103
+ })
1104
+ }
1105
+
1106
+ /**
1107
+ * Sets the distance to each host and when warmup is true, creates all connections to local hosts.
1108
+ * @returns {Promise}
1109
+ * @private
1110
+ */
1111
+ Client.prototype._warmup = function () {
1112
+ const hosts = this.hosts.values()
1113
+
1114
+ return promiseUtils.times(hosts.length, warmupLimit, async (index) => {
1115
+ const h = hosts[index]
1116
+ const distance = this.profileManager.getDistance(h)
1117
+
1118
+ if (distance === types.distance.ignored) {
1119
+ return
1120
+ }
1121
+
1122
+ if (this.options.pooling.warmup && distance === types.distance.local) {
1123
+ try {
1124
+ await h.warmupPool(this.keyspace)
1125
+ } catch (err) {
1126
+ // An error while trying to create a connection to one of the hosts.
1127
+ // Warn the user and move on.
1128
+ this.log(
1129
+ "warning",
1130
+ `Connection pool to host ${h.address} could not be created: ${err}`,
1131
+ err,
1132
+ )
1133
+ }
1134
+ } else {
1135
+ h.initializePool()
1136
+ }
1137
+ })
1138
+ }
1139
+
1140
+ /**
1141
+ * @returns {Encoder}
1142
+ * @private
1143
+ */
1144
+ Client.prototype._getEncoder = function () {
1145
+ const encoder = this.controlConnection.getEncoder()
1146
+ if (!encoder) {
1147
+ throw new errors.DriverInternalError("Encoder is not defined")
1148
+ }
1149
+ return encoder
1150
+ }
1151
+
1152
+ /**
1153
+ * Returns a BatchRequest instance and fills the routing key information in the provided options.
1154
+ * @private
1155
+ */
1156
+ Client.prototype._createBatchRequest = async function (queryItems, info) {
1157
+ const firstQuery = queryItems[0]
1158
+ if (!firstQuery.meta) {
1159
+ return new requests.BatchRequest(queryItems, info)
1160
+ }
1161
+
1162
+ await this._setRoutingInfo(info, firstQuery.params, firstQuery.meta)
1163
+ return new requests.BatchRequest(queryItems, info)
1164
+ }
1165
+
1166
+ /**
1167
+ * Returns an ExecuteRequest instance and fills the routing key information in the provided options.
1168
+ * @private
1169
+ */
1170
+ Client.prototype._createExecuteRequest = async function (
1171
+ query,
1172
+ queryId,
1173
+ info,
1174
+ params,
1175
+ meta,
1176
+ ) {
1177
+ params = utils.adaptNamedParamsPrepared(params, meta.columns)
1178
+ await this._setRoutingInfo(info, params, meta)
1179
+ return new requests.ExecuteRequest(query, queryId, params, info, meta)
1180
+ }
1181
+
1182
+ /**
1183
+ * Returns a QueryRequest instance and fills the routing key information in the provided options.
1184
+ * @private
1185
+ */
1186
+ Client.prototype._createQueryRequest = async function (
1187
+ query,
1188
+ execOptions,
1189
+ params,
1190
+ ) {
1191
+ await this.metadata.adaptUserHints(this.keyspace, execOptions.getHints())
1192
+ const paramsInfo = utils.adaptNamedParamsWithHints(params, execOptions)
1193
+ this._getEncoder().setRoutingKeyFromUser(
1194
+ paramsInfo.params,
1195
+ execOptions,
1196
+ paramsInfo.keyIndexes,
1197
+ )
1198
+
1199
+ return new requests.QueryRequest(
1200
+ query,
1201
+ paramsInfo.params,
1202
+ execOptions,
1203
+ paramsInfo.namedParameters,
1204
+ )
1205
+ }
1206
+
1207
+ /**
1208
+ * Sets the routing key based on the parameter values or the provided routing key components.
1209
+ * @param {ExecutionOptions} execOptions
1210
+ * @param {Array} params
1211
+ * @param meta
1212
+ * @private
1213
+ */
1214
+ Client.prototype._setRoutingInfo = async function (execOptions, params, meta) {
1215
+ const encoder = this._getEncoder()
1216
+
1217
+ if (!execOptions.getKeyspace() && meta.keyspace) {
1218
+ execOptions.setKeyspace(meta.keyspace)
1219
+ }
1220
+ if (execOptions.getRoutingKey()) {
1221
+ // Routing information provided by the user
1222
+ return encoder.setRoutingKeyFromUser(params, execOptions)
1223
+ }
1224
+ if (Array.isArray(meta.partitionKeys)) {
1225
+ // The partition keys are provided as part of the metadata for modern protocol versions
1226
+ execOptions.setRoutingIndexes(meta.partitionKeys)
1227
+ return encoder.setRoutingKeyFromMeta(meta, params, execOptions)
1228
+ }
1229
+
1230
+ // Older versions of the protocol (v3 and below) don't provide routing information
1231
+ try {
1232
+ const tableInfo = await this.metadata.getTable(
1233
+ meta.keyspace,
1234
+ meta.table,
1235
+ )
1236
+
1237
+ if (!tableInfo) {
1238
+ // The schema data is not there, maybe it is being recreated, avoid setting the routing information
1239
+ return
1240
+ }
1241
+
1242
+ execOptions.setRoutingIndexes(
1243
+ tableInfo.partitionKeys.map((c) => meta.columnsByName[c.name]),
1244
+ )
1245
+ // Skip parsing metadata next time
1246
+ meta.partitionKeys = execOptions.getRoutingIndexes()
1247
+ encoder.setRoutingKeyFromMeta(meta, params, execOptions)
1248
+ } catch (err) {
1249
+ this.log(
1250
+ "warning",
1251
+ util.format(
1252
+ "Table %s.%s metadata could not be retrieved",
1253
+ meta.keyspace,
1254
+ meta.table,
1255
+ ),
1256
+ )
1257
+ }
1258
+ }
1259
+
1260
+ /**
1261
+ * Callback used by execution methods.
1262
+ * @callback ResultCallback
1263
+ * @param {Error} err Error occurred in the execution of the query.
1264
+ * @param {ResultSet} [result] Result of the execution of the query.
1265
+ */
1266
+
1267
+ export default Client