Skip to content

Commit 55203cc

Browse files
authored
SQL related improvements (#940)
* Inactive connection originated uuid is now client's uuid * fix tests * Change is row set logic * API doc changes * Rename variable * Add comment * Fix test name * Fix close test * Add @ignore to empty modules, adjust comments * Delete useless internal labels
1 parent 758d1e9 commit 55203cc

File tree

10 files changed

+72
-38
lines changed

10 files changed

+72
-38
lines changed

src/sql/SqlError.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
* See the License for the specific language governing permissions and
1414
* limitations under the License.
1515
*/
16+
/** @ignore *//** */
1617

1718
import {UUID} from '../core';
1819

src/sql/SqlErrorCode.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
* See the License for the specific language governing permissions and
1414
* limitations under the License.
1515
*/
16+
/** @ignore *//** */
1617

1718
/**
1819
* Error codes used in Hazelcast SQL.

src/sql/SqlPage.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
* See the License for the specific language governing permissions and
1414
* limitations under the License.
1515
*/
16+
/** @ignore *//** */
1617

1718
import {SqlColumnType} from './SqlColumnMetadata';
1819

src/sql/SqlQueryId.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
import {UUID} from '../core';
1818
import {UuidUtil} from '../util/UuidUtil';
1919
import * as Long from 'long';
20+
/** @ignore *//** */
2021

2122
/**
2223
* @internal

src/sql/SqlResult.ts

Lines changed: 23 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -211,7 +211,7 @@ export class SqlResultImpl implements SqlResult {
211211

212212
isRowSet(): Promise<boolean> {
213213
return this.executeDeferred.promise.then(() => {
214-
return this.updateCount === Long.fromInt(-1);
214+
return this.rowMetadata !== null;
215215
});
216216
}
217217

@@ -242,6 +242,7 @@ export class SqlResultImpl implements SqlResult {
242242
// Prevent ongoing/future fetch requests
243243
if (!this.fetchDeferred) {
244244
this.fetchDeferred = deferredPromise<SqlPage>();
245+
this.fetchDeferred.promise.catch(() => {});
245246
}
246247
this.fetchDeferred.reject(error);
247248
// Send the close request.
@@ -255,7 +256,10 @@ export class SqlResultImpl implements SqlResult {
255256
return this.closeDeferred.promise;
256257
}
257258

258-
/** Called when next page of the result is received. */
259+
/**
260+
* Called when next page of the result is received.
261+
* @param page
262+
*/
259263
private onNextPage(page: SqlPage) {
260264
this.currentPage = page;
261265
this.currentRowCount = page.getRowCount();
@@ -267,7 +271,10 @@ export class SqlResultImpl implements SqlResult {
267271
}
268272
}
269273

270-
/** Called when an error is occurred during SQL execute */
274+
/**
275+
* Called when an error is occurred during SQL execution.
276+
* @param error The wrapped error that can be propagated to the user through executeDeferred.
277+
*/
271278
onExecuteError(error: HazelcastSqlException): void {
272279
// Ignore the error if SQL result is closed.
273280
if (this.closed) {
@@ -303,18 +310,23 @@ export class SqlResultImpl implements SqlResult {
303310
}
304311
}
305312

306-
/** Called when a execute response is received. */
307-
onExecuteResponse(rowMetadata: SqlRowMetadata | null, rowPage: SqlPage, updateCount: Long) {
308-
// Ignore the response if SQL result is closed.
313+
/**
314+
* Called when a execute response is received.
315+
* @param rowMetadata The row metadata. It is null if the response only contains the update count.
316+
* @param rowPage The first page of the result. It is null if the response only contains the update count.
317+
* @param updateCount The update count.
318+
*/
319+
onExecuteResponse(rowMetadata: SqlRowMetadata | null, rowPage: SqlPage | null, updateCount: Long) {
320+
// Ignore the response if the SQL result is closed.
309321
if (this.closed) {
310322
return;
311323
}
312324

313-
if (rowMetadata !== null) { // Result that including rows
325+
if (rowMetadata !== null) { // Result that includes rows
314326
this.rowMetadata = rowMetadata;
315327
this.onNextPage(rowPage);
316328
this.updateCount = Long.fromInt(-1);
317-
} else { // Result that including update count
329+
} else { // Result that includes update count
318330
this.updateCount = updateCount;
319331
this.closed = true;
320332
}
@@ -333,13 +345,13 @@ export class SqlResultImpl implements SqlResult {
333345
}
334346
// Do not start a fetch if the result is already closed
335347
if (this.closed) {
336-
return Promise.reject('Cannot fetch, the result is already closed');
348+
return Promise.reject(new IllegalStateError('Cannot fetch, the result is already closed'));
337349
}
338350

339351
this.fetchDeferred = deferredPromise<SqlPage>();
340352

341-
this.sqlService.fetch(this.connection, this.queryId, this.cursorBufferSize).then(value => {
342-
this.fetchDeferred.resolve(value);
353+
this.sqlService.fetch(this.connection, this.queryId, this.cursorBufferSize).then(sqlPage => {
354+
this.fetchDeferred.resolve(sqlPage);
343355
this.fetchDeferred = undefined; // Set fetchDeferred to undefined to be able to fetch again
344356
}).catch(err => {
345357
this.fetchDeferred.reject(this.sqlService.toHazelcastSqlException(err, this.connection));
@@ -350,7 +362,6 @@ export class SqlResultImpl implements SqlResult {
350362

351363
/**
352364
* Checks if there are rows to iterate in a recursive manner, similar to a non-blocking while block
353-
* @internal
354365
*/
355366
private checkHasNext(): Promise<boolean> {
356367
if (this.currentPosition === this.currentRowCount) {

src/sql/SqlService.ts

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -200,7 +200,6 @@ export class SqlServiceImpl implements SqlService {
200200
*
201201
* @param sqlStatement
202202
* @throws RangeError if validation is not successful
203-
* @internal
204203
*/
205204
private static validateSqlStatement(sqlStatement: SqlStatement | null): void {
206205
if (sqlStatement === null) {
@@ -223,7 +222,6 @@ export class SqlServiceImpl implements SqlService {
223222
*
224223
* @param sqlStatementOptions
225224
* @throws RangeError if validation is not successful
226-
* @internal
227225
*/
228226
private static validateSqlStatementOptions(sqlStatementOptions: SqlStatementOptions): void {
229227
if (sqlStatementOptions.hasOwnProperty('schema')) {
@@ -253,15 +251,15 @@ export class SqlServiceImpl implements SqlService {
253251

254252
/**
255253
* Converts an error to HazelcastSqlException and returns it. Used by execute, close and fetch
256-
* @internal
257254
* @param err
258255
* @param connection
259256
* @returns {@link HazelcastSqlException}
260257
*/
261258
toHazelcastSqlException(err: any, connection: Connection) : HazelcastSqlException {
262259
if (!connection.isAlive()) {
263260
return new HazelcastSqlException(
264-
connection.getRemoteUuid(), SqlErrorCode.CONNECTION_PROBLEM,
261+
this.connectionManager.getClientUuid(),
262+
SqlErrorCode.CONNECTION_PROBLEM,
265263
'Cluster topology changed while a query was executed:' +
266264
`Member cannot be reached: ${connection.getRemoteAddress()}`,
267265
err
@@ -291,6 +289,7 @@ export class SqlServiceImpl implements SqlService {
291289

292290
const connection = this.connectionRegistry.getRandomConnection(true);
293291
if (connection === null) {
292+
// Either the client is not connected to the cluster, or there are no data members in the cluster.
294293
throw new HazelcastSqlException(
295294
this.connectionManager.getClientUuid(),
296295
SqlErrorCode.CONNECTION_PROBLEM,
@@ -378,11 +377,22 @@ export class SqlServiceImpl implements SqlService {
378377
return this.executeStatement(sqlStatement);
379378
}
380379

380+
/**
381+
* Sends a close request on a connection for an SQL result using its query id.
382+
* @param connection The connection the request will be sent to
383+
* @param queryId The query id that defines the SQL result
384+
*/
381385
close(connection: Connection, queryId: SqlQueryId): Promise<ClientMessage> {
382386
const requestMessage = SqlCloseCodec.encodeRequest(queryId);
383387
return this.invocationService.invokeOnConnection(connection, requestMessage);
384388
}
385389

390+
/**
391+
* Sends a fetch request on a connection for an SQL result using its query id.
392+
* @param connection The connection the request will be sent to
393+
* @param queryId The query id that defines the SQL result
394+
* @param cursorBufferSize The cursor buffer size associated with SQL fetch request, i.e its page size
395+
*/
386396
fetch(connection: Connection, queryId: SqlQueryId, cursorBufferSize: number): Promise<SqlPage> {
387397
const requestMessage = SqlFetchCodec.encodeRequest(queryId, cursorBufferSize);
388398
return this.invocationService.invokeOnConnection(connection, requestMessage).then(clientMessage => {

src/util/DatetimeUtil.ts

Lines changed: 19 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -13,16 +13,16 @@
1313
* See the License for the specific language governing permissions and
1414
* limitations under the License.
1515
*/
16-
16+
/** @ignore *//** */
1717

1818
/**
19-
Constructs and returns timezone for ISO string from offsetSeconds
20-
@internal
21-
22-
@param offsetSeconds Offset in seconds, can be negative or positive. must be in valid timezone range [-64800, 64800]. If out of
23-
this range, the limit values are assumed.
24-
@throws RangeError if offset seconds is not number
25-
@return Timezone string, can be 'Z', +hh:mm or -hh:mm
19+
* Constructs and returns timezone for ISO string from offsetSeconds
20+
* @internal
21+
*
22+
* @param offsetSeconds Offset in seconds, can be negative or positive. must be in valid timezone range [-64800, 64800]. If out of
23+
* this range, the limit values are assumed.
24+
* @throws RangeError if offset seconds is not number
25+
* @return Timezone string, can be 'Z', +hh:mm or -hh:mm
2626
*/
2727
export function getTimezoneOffsetFromSeconds(offsetSeconds: number): string {
2828
if (!Number.isInteger(offsetSeconds)) {
@@ -57,12 +57,12 @@ export function getTimezoneOffsetFromSeconds(offsetSeconds: number): string {
5757
}
5858

5959
/**
60-
Parses timezone string and returns offset in seconds
61-
@internal
62-
63-
@param timezoneString string, can be 'Z', +hh:mm or -hh:mm
64-
@throws RangeError If timezoneString is invalid
65-
@return Timezone Offset in seconds, can be negative or positive. must be in valid timezone range [-64800, 64800]
60+
* Parses timezone string and returns offset in seconds
61+
* @internal
62+
*
63+
* @param timezoneString string, can be 'Z', +hh:mm or -hh:mm
64+
* @throws RangeError If timezoneString is invalid
65+
* @return Timezone Offset in seconds, can be negative or positive. must be in valid timezone range [-64800, 64800]
6666
*/
6767
export function getOffsetSecondsFromTimezoneString(timezoneString: string): number {
6868
if (typeof timezoneString !== 'string') {
@@ -100,8 +100,9 @@ export function getOffsetSecondsFromTimezoneString(timezoneString: string): numb
100100
}
101101

102102
/**
103-
* Give this function integer and it will zero pad to the given length.
104103
* @internal
104+
* Give this function integer and it will zero pad to the given length.
105+
*
105106
* @param value
106107
* @param length total length after padding
107108
* @returns Zero padded string
@@ -130,7 +131,9 @@ enum Months {
130131
}
131132

132133
/**
134+
* @internal
133135
* Returns the string representation of a local date.
136+
*
134137
* @param year Must be between -999999999-999999999
135138
* @param month Must be between 1-12
136139
* @param date Must be between 1-31 depending on year and month
@@ -187,6 +190,7 @@ export function getLocalDateString(year: number, month: number, date: number): s
187190
}
188191

189192
/**
193+
* @internal
190194
* Returns the string representation of a local time.
191195
*
192196
* @param hour The hour-of-day to represent, from 0 to 23

test/integration/backward_compatible/sql/ExecuteTest.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -512,7 +512,7 @@ describe('SqlExecuteTest', function () {
512512
});
513513
error1.should.be.instanceof(getHazelcastSqlException());
514514
error1.code.should.be.eq(getSqlErrorCode().CONNECTION_PROBLEM);
515-
error1.originatingMemberId.toString().should.be.eq(member.uuid);
515+
error1.originatingMemberId.should.be.eq(client.connectionManager.getClientUuid());
516516
});
517517

518518
it('should return an error if connection lost - statement', async function () {
@@ -538,7 +538,7 @@ describe('SqlExecuteTest', function () {
538538
});
539539
error1.should.be.instanceof(getHazelcastSqlException());
540540
error1.code.should.be.eq(getSqlErrorCode().CONNECTION_PROBLEM);
541-
error1.originatingMemberId.toString().should.be.eq(member.uuid);
541+
error1.originatingMemberId.should.be.eq(client.connectionManager.getClientUuid());
542542
});
543543

544544
it('should return an error if sql is invalid', async function () {

test/integration/backward_compatible/sql/ResultTest.js

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -61,8 +61,8 @@ describe('SqlResultTest', function () {
6161
await someMap.put(0, 1);
6262
await someMap.put(1, 2);
6363
await someMap.put(2, 3);
64-
65-
result = client.getSqlService().execute(`SELECT * FROM ${mapName} WHERE this > ?`, [1]);
64+
await someMap.put(3, 4);
65+
await someMap.put(4, 5);
6666
});
6767

6868
after(async function () {
@@ -75,11 +75,15 @@ describe('SqlResultTest', function () {
7575
});
7676

7777
it('should reject iteration after close()', async function () {
78-
await result.close();
79-
78+
result = client.getSqlService().execute(`SELECT * FROM ${mapName} WHERE this > ?`, [1], {cursorBufferSize: 1});
8079
const error = await TestUtil.getRejectionReasonOrThrow(async () => {
80+
let counter = 0;
8181
// eslint-disable-next-line no-empty,no-unused-vars
8282
for await (const row of result) {
83+
counter++;
84+
if (counter === 2) {
85+
await result.close();
86+
}
8387
}
8488
});
8589
error.should.be.instanceof(getHazelcastSqlException());
@@ -89,6 +93,7 @@ describe('SqlResultTest', function () {
8993
});
9094

9195
it('getters should work', async function () {
96+
result = client.getSqlService().execute(`SELECT * FROM ${mapName} WHERE this > ?`, [1]);
9297
const rowMetadata = await result.getRowMetadata();
9398
rowMetadata.should.be.instanceof(getSqlRowMetadataImpl());
9499
rowMetadata.getColumnCount().should.be.eq(2);

test/unit/sql/SqlResult.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -302,7 +302,7 @@ describe('SqlResultTest', function () {
302302
}, 100);
303303
});
304304

305-
it('should not call onExecuteError and change properties after an error is received', function (done) {
305+
it('should call onExecuteError and change properties after an error is received', function (done) {
306306
const sqlResult = new SqlResultImpl(fakeSqlService, {}, {}, {}, 4096);
307307
const onExecuteErrorFake = sandbox.replace(sqlResult, 'onExecuteError', sandbox.fake(sqlResult.onExecuteError));
308308
// simulate a response then call close()

0 commit comments

Comments
 (0)