node-firebird-driver 3.2.2 → 3.4.0

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 (42) hide show
  1. package/README.md +1 -1
  2. package/dist/lib/impl/attachment.d.ts +4 -4
  3. package/dist/lib/impl/attachment.js +8 -6
  4. package/dist/lib/impl/attachment.js.map +1 -1
  5. package/dist/lib/impl/blob.js.map +1 -1
  6. package/dist/lib/impl/client.js +3 -2
  7. package/dist/lib/impl/client.js.map +1 -1
  8. package/dist/lib/impl/date-time.js +2 -1
  9. package/dist/lib/impl/date-time.js.map +1 -1
  10. package/dist/lib/impl/events.js +2 -1
  11. package/dist/lib/impl/events.js.map +1 -1
  12. package/dist/lib/impl/fb-util.d.ts +2 -1
  13. package/dist/lib/impl/fb-util.js +100 -81
  14. package/dist/lib/impl/fb-util.js.map +1 -1
  15. package/dist/lib/impl/resultset.js +11 -5
  16. package/dist/lib/impl/resultset.js.map +1 -1
  17. package/dist/lib/impl/statement.d.ts +3 -1
  18. package/dist/lib/impl/statement.js +4 -2
  19. package/dist/lib/impl/statement.js.map +1 -1
  20. package/dist/lib/impl/time-zones.js +5 -3
  21. package/dist/lib/impl/time-zones.js.map +1 -1
  22. package/dist/lib/impl/transaction.js +2 -1
  23. package/dist/lib/impl/transaction.js.map +1 -1
  24. package/dist/lib/index.d.ts +27 -1
  25. package/dist/lib/index.js +25 -1
  26. package/dist/lib/index.js.map +1 -1
  27. package/dist/test/tests.js +197 -68
  28. package/dist/test/tests.js.map +1 -1
  29. package/package.json +6 -5
  30. package/src/lib/impl/attachment.ts +290 -253
  31. package/src/lib/impl/blob.ts +37 -35
  32. package/src/lib/impl/client.ts +60 -61
  33. package/src/lib/impl/date-time.ts +33 -33
  34. package/src/lib/impl/events.ts +17 -18
  35. package/src/lib/impl/fb-util.ts +552 -448
  36. package/src/lib/impl/resultset.ts +94 -86
  37. package/src/lib/impl/statement.ts +157 -127
  38. package/src/lib/impl/time-zones.ts +643 -641
  39. package/src/lib/impl/transaction.ts +37 -38
  40. package/src/lib/index.ts +331 -270
  41. package/src/test/tests.ts +1028 -860
  42. package/tsconfig.json +7 -13
package/src/test/tests.ts CHANGED
@@ -1,330 +1,401 @@
1
1
  import {
2
- Blob,
3
- BlobSeekWhence,
4
- Client,
5
- TransactionIsolation,
6
- ZonedDate,
7
- ZonedDateEx
2
+ Blob,
3
+ BlobSeekWhence,
4
+ Client,
5
+ DatabaseReadWriteMode,
6
+ StatementType,
7
+ TransactionIsolation,
8
+ ZonedDate,
9
+ ZonedDateEx,
8
10
  } from '../lib';
9
11
 
10
12
  import * as fs from 'fs-extra-promise';
11
13
  import * as tmp from 'temp-fs';
12
14
 
13
-
15
+ // eslint-disable-next-line @typescript-eslint/no-require-imports
14
16
  require('dotenv').config({ path: '../../.env' });
15
17
 
16
-
17
18
  export function runCommonTests(client: Client) {
18
- function dateToString(d: Date) {
19
- return d && `${(d.getFullYear() + '').padStart(4, '0')}-${d.getMonth() + 1}-${d.getDate()}`;
20
- }
21
-
22
- function timeToString(d: Date) {
23
- return d && `${d.getHours()}:${d.getMinutes()}:${d.getSeconds()}.${d.getMilliseconds()}`;
24
- }
25
-
26
- function timeTzToString(zd: ZonedDateEx) {
27
- if (!zd)
28
- return null;
29
-
30
- const d = new Date(zd.date.getTime() + (zd.offset * 60 * 1000));
31
-
32
- return `time '${d.getUTCHours()}:${d.getUTCMinutes()}:${d.getUTCSeconds()}.${d.getUTCMilliseconds()} ${zd.timeZone}'`;
33
- }
34
-
35
- function dateTimeToString(d: Date) {
36
- return d && `${dateToString(d)} ${timeToString(d)}`;
37
- }
38
-
39
- function dateTimeTzToString(zd: ZonedDateEx) {
40
- if (!zd)
41
- return null;
42
-
43
- const d = new Date(zd.date.getTime() + (zd.offset * 60 * 1000));
44
-
45
- return `timestamp '${(d.getUTCFullYear() + '').padStart(4, '0')}-${d.getUTCMonth() + 1}-${d.getUTCDate()} ` +
46
- `${d.getUTCHours()}:${d.getUTCMinutes()}:${d.getUTCSeconds()}.${d.getUTCMilliseconds()} ${zd.timeZone}'`;
47
- }
48
-
49
-
50
- describe('node-firebird-driver', () => {
51
- const testConfig = {
52
- username: process.env.ISC_USER,
53
- password: process.env.ISC_PASSWORD,
54
- host: process.env.NODE_FB_TEST_HOST,
55
- port: process.env.NODE_FB_TEST_PORT,
56
- tmpDir: process.env.NODE_FB_TEST_TMP_DIR
57
- };
58
-
59
- function isLocal(): boolean {
60
- return testConfig.host == undefined ||
61
- testConfig.host == 'localhost' ||
62
- testConfig.host == '127.0.0.1';
63
- }
64
-
65
- function getTempFile(name: string): string {
66
- const database = `${testConfig.tmpDir}/${name}`;
67
- return (testConfig.host ?? '') +
68
- (testConfig.host && testConfig.port ? `/${testConfig.port}` : '') +
69
- (testConfig.host ? ':' : '') +
70
- database;
71
- }
72
-
73
-
74
- jest.setTimeout(10000);
75
-
76
-
77
- beforeAll(() => {
78
- expect(client.isValid).toBeTruthy();
79
-
80
- if (isLocal() && !testConfig.tmpDir) {
81
- testConfig.tmpDir = tmp.mkdirSync().path.toString();
82
-
83
- // Important for MacOS tests with non-embedded server.
84
- fs.chmodSync(testConfig.tmpDir, 0o777);
85
- }
86
-
87
- const defaultOptions = {
88
- password: testConfig.password,
89
- username: testConfig.username
90
- };
91
-
92
- client.defaultCreateDatabaseOptions = {
93
- forcedWrite: false,
94
- ...defaultOptions
95
- };
96
-
97
- client.defaultConnectOptions = {
98
- ...defaultOptions
99
- };
100
- });
101
-
102
- afterAll(async () => {
103
- await client.dispose();
104
-
105
- expect(client.isValid).toBeFalsy();
106
-
107
- if (isLocal())
108
- fs.rmdirSync(testConfig.tmpDir!);
109
- });
110
-
111
- describe('Client', () => {
112
- test('#createDatabase()', async () => {
113
- const attachment = await client.createDatabase(getTempFile('Client-createDatabase.fdb'));
114
- await attachment.dropDatabase();
115
- });
116
-
117
- test('#connect()', async () => {
118
- const filename = getTempFile('Client-connect.fdb');
119
- const attachment1 = await client.createDatabase(filename);
120
- const attachment2 = await client.connect(filename);
121
-
122
- expect(attachment1.isValid).toBeTruthy();
123
- expect(attachment2.isValid).toBeTruthy();
124
-
125
- await attachment2.disconnect();
126
- await attachment1.dropDatabase();
127
-
128
- expect(attachment1.isValid).toBeFalsy();
129
- expect(attachment2.isValid).toBeFalsy();
130
- });
131
- });
132
-
133
- describe('Attachment', () => {
134
- test('#startTransaction()', async () => {
135
- const attachment = await client.createDatabase(getTempFile('Attachment-startTransaction.fdb'));
136
-
137
- const isolationQuery = 'select rdb$get_context(\'SYSTEM\', \'ISOLATION_LEVEL\') from rdb$database';
138
-
139
- const transaction1 = await attachment.startTransaction();
140
- expect(transaction1.isValid).toBeTruthy()
141
- expect((await attachment.executeSingleton(transaction1, isolationQuery))[0]).toBe('SNAPSHOT');
142
- await transaction1.commit();
143
- expect(transaction1.isValid).toBeFalsy()
144
-
145
- const transaction2 = await attachment.startTransaction({ isolation: TransactionIsolation.READ_COMMITTED });
146
- expect(transaction2.isValid).toBeTruthy()
147
- expect((await attachment.executeSingleton(transaction2, isolationQuery))[0]).toBe('READ COMMITTED');
148
- await transaction2.commit();
149
- expect(transaction2.isValid).toBeFalsy()
150
-
151
- const transaction3 = await attachment.startTransaction({ isolation: TransactionIsolation.CONSISTENCY });
152
- expect(transaction3.isValid).toBeTruthy()
153
- expect((await attachment.executeSingleton(transaction3, isolationQuery))[0]).toBe('CONSISTENCY');
154
- await transaction3.commit();
155
- expect(transaction3.isValid).toBeFalsy()
156
-
157
- await attachment.dropDatabase();
158
- });
159
-
160
- test('#prepare()', async () => {
161
- const attachment = await client.createDatabase(getTempFile('Attachment-prepare.fdb'));
162
- const transaction = await attachment.startTransaction();
163
-
164
- const statement = await attachment.prepare(transaction, 'create table t1 (n1 integer)');
165
- expect(statement.isValid).toBeTruthy();
166
- await statement.dispose();
167
- expect(statement.isValid).toBeFalsy();
168
-
169
- let error: Error | undefined;
170
- try {
171
- await attachment.prepare(transaction, 'create select t1 (n1 integer)');
172
- }
173
- catch (e) {
174
- error = e as Error;
175
- expect(error.message).toBe(
176
- 'Dynamic SQL Error\n' +
177
- '-SQL error code = -104\n' +
178
- '-Token unknown - line 1, column 8\n' +
179
- '-select');
180
- }
181
-
182
- expect(error).toBeTruthy();
183
-
184
- await transaction.commit();
185
- await attachment.dropDatabase();
186
- });
187
-
188
- //// TODO: #executeTransaction
189
-
190
- test('#execute()', async () => {
191
- const attachment = await client.createDatabase(getTempFile('Attachment-execute.fdb'));
192
- const transaction = await attachment.startTransaction();
193
-
194
- await attachment.execute(transaction, 'create table t1 (n1 integer)');
195
- await transaction.commitRetaining();
196
-
197
- await attachment.execute(transaction, 'insert into t1 (n1) values (1)');
198
-
199
- await transaction.commit();
200
- await attachment.dropDatabase();
201
- });
202
-
203
- test('#executeQuery()', async () => {
204
- const attachment = await client.createDatabase(getTempFile('Attachment-executeQuery.fdb'));
205
- const transaction = await attachment.startTransaction();
206
-
207
- await attachment.execute(transaction, 'create table t1 (n1 integer)');
208
- await transaction.commitRetaining();
209
-
210
- const resultSet = await attachment.executeQuery(transaction, 'select n1 from t1');
211
- expect(resultSet.isValid).toBeTruthy();
212
- await resultSet.close();
213
- expect(resultSet.isValid).toBeFalsy();
214
-
215
- await transaction.commit();
216
- await attachment.dropDatabase();
217
- });
218
-
219
- test('#executeSingleton()', async () => {
220
- const attachment = await client.createDatabase(getTempFile('Attachment-executeSingleton.fdb'));
221
- const transaction = await attachment.startTransaction();
222
-
223
- await attachment.execute(transaction, 'create table t1 (n1 integer)');
224
- await transaction.commitRetaining();
225
-
226
- const result = await attachment.executeSingleton(transaction, 'insert into t1 values (11) returning n1');
227
- expect(result.length).toBe(1);
228
- expect(result[0]).toBe(11);
229
-
230
- await transaction.commit();
231
- await attachment.dropDatabase();
232
- });
233
-
234
- test('#executeSingletonAsObject()', async () => {
235
- const attachment = await client.createDatabase(getTempFile('Attachment-executeSingletonAsObject.fdb'));
236
- const transaction = await attachment.startTransaction();
237
-
238
- await attachment.execute(transaction, 'create table t1 (n1 integer)');
239
- await transaction.commitRetaining();
240
-
241
- const output = await attachment.executeSingletonAsObject<{ N1: number }>(transaction,
242
- 'insert into t1 values (11) returning n1');
243
- expect(output.N1).toBe(11);
244
-
245
- await transaction.commit();
246
- await attachment.dropDatabase();
247
- });
248
-
249
- test('#executeReturning()', async () => {
250
- const attachment = await client.createDatabase(getTempFile('Attachment-executeReturning.fdb'));
251
- const transaction = await attachment.startTransaction();
252
-
253
- await attachment.execute(transaction, 'create table t1 (n1 integer)');
254
- await transaction.commitRetaining();
255
-
256
- const result = await attachment.executeReturning(transaction, 'insert into t1 values (11) returning n1');
257
- expect(result.length).toBe(1);
258
- expect(result[0]).toBe(11);
259
-
260
- await transaction.commit();
261
- await attachment.dropDatabase();
262
- });
263
-
264
- test('#executeReturningAsObject()', async () => {
265
- const attachment = await client.createDatabase(getTempFile('Attachment-executeReturningAsObject.fdb'));
266
- const transaction = await attachment.startTransaction();
267
-
268
- await attachment.execute(transaction, 'create table t1 (n1 integer)');
269
- await transaction.commitRetaining();
270
-
271
- const output = await attachment.executeReturningAsObject<{ N1: number }>(transaction,
272
- 'insert into t1 values (11) returning n1');
273
- expect(output.N1).toBe(11);
274
-
275
- await transaction.commit();
276
- await attachment.dropDatabase();
277
- });
278
-
279
- test('#queueEvents()', async () => {
280
- const attachment = await client.createDatabase(getTempFile('Attachment-queueEvents.fdb'));
281
-
282
- const eventNames: [string, number][] = [
283
- ['EVENT1', 16],
284
- ['EVENT2', 8]
285
- ];
286
-
287
- const eventsObj = eventNames.map(([name, expected]) => {
288
- let resolver: () => void = undefined!;
289
-
290
- const obj = {
291
- name,
292
- expected,
293
- count: 0,
294
- promise: new Promise<void>(resolve => resolver = resolve)
295
- };
296
-
297
- return { ...obj, resolver };
298
- });
299
- const eventsMap = new Map(eventsObj.map(ev => [ev.name, ev]));
300
-
301
- const eventHandler = async (counters: [string, number][]) => {
302
- counters.forEach(([name, count]) => {
303
- const obj = eventsMap.get(name)!;
304
- const newCount = obj.count + count;
305
- obj.count = newCount;
306
-
307
- if (newCount >= obj.expected)
308
- obj.resolver();
309
- });
310
-
311
- if (Array.from(eventsMap.values()).every(obj => obj.count >= obj.expected)) {
312
- if (events) {
313
- await events.cancel();
314
- expect(events.isValid).toBeFalsy();
315
- events = null!;
316
- }
317
- }
318
- };
319
-
320
- let events = await attachment.queueEvents(Array.from(eventsMap.keys()), eventHandler);
321
-
322
- const transaction = await attachment.startTransaction();
323
- try {
324
- // Iterate more times than the neccessary so that
325
- // eventHandler may have a chance to cancel the events.
326
- for (let i = 0; i < 20; ++i) {
327
- await attachment.execute(transaction, `
19
+ function dateToString(d: Date) {
20
+ return d && `${(d.getFullYear() + '').padStart(4, '0')}-${d.getMonth() + 1}-${d.getDate()}`;
21
+ }
22
+
23
+ function timeToString(d: Date) {
24
+ return d && `${d.getHours()}:${d.getMinutes()}:${d.getSeconds()}.${d.getMilliseconds()}`;
25
+ }
26
+
27
+ function timeTzToString(zd: ZonedDateEx) {
28
+ if (!zd) {
29
+ return null;
30
+ }
31
+
32
+ const d = new Date(zd.date.getTime() + zd.offset * 60 * 1000);
33
+
34
+ return `time '${d.getUTCHours()}:${d.getUTCMinutes()}:${d.getUTCSeconds()}.${d.getUTCMilliseconds()} ${zd.timeZone}'`;
35
+ }
36
+
37
+ function dateTimeToString(d: Date) {
38
+ return d && `${dateToString(d)} ${timeToString(d)}`;
39
+ }
40
+
41
+ function dateTimeTzToString(zd: ZonedDateEx) {
42
+ if (!zd) {
43
+ return null;
44
+ }
45
+
46
+ const d = new Date(zd.date.getTime() + zd.offset * 60 * 1000);
47
+
48
+ return (
49
+ `timestamp '${(d.getUTCFullYear() + '').padStart(4, '0')}-${d.getUTCMonth() + 1}-${d.getUTCDate()} ` +
50
+ `${d.getUTCHours()}:${d.getUTCMinutes()}:${d.getUTCSeconds()}.${d.getUTCMilliseconds()} ${zd.timeZone}'`
51
+ );
52
+ }
53
+
54
+ describe('node-firebird-driver', () => {
55
+ const testConfig = {
56
+ username: process.env.ISC_USER,
57
+ password: process.env.ISC_PASSWORD,
58
+ host: process.env.NODE_FB_TEST_HOST,
59
+ port: process.env.NODE_FB_TEST_PORT,
60
+ tmpDir: process.env.NODE_FB_TEST_TMP_DIR,
61
+ };
62
+
63
+ function isLocal(): boolean {
64
+ return testConfig.host == undefined || testConfig.host == 'localhost' || testConfig.host == '127.0.0.1';
65
+ }
66
+
67
+ function getTempFile(name: string): string {
68
+ const database = `${testConfig.tmpDir}/${name}`;
69
+ return (
70
+ (testConfig.host ?? '') +
71
+ (testConfig.host && testConfig.port ? `/${testConfig.port}` : '') +
72
+ (testConfig.host ? ':' : '') +
73
+ database
74
+ );
75
+ }
76
+
77
+ jest.setTimeout(10000);
78
+
79
+ beforeAll(() => {
80
+ expect(client.isValid).toBeTruthy();
81
+
82
+ if (isLocal() && !testConfig.tmpDir) {
83
+ testConfig.tmpDir = tmp.mkdirSync().path.toString();
84
+
85
+ // Important for MacOS tests with non-embedded server.
86
+ fs.chmodSync(testConfig.tmpDir, 0o777);
87
+ }
88
+
89
+ const defaultOptions = {
90
+ password: testConfig.password,
91
+ username: testConfig.username,
92
+ };
93
+
94
+ client.defaultCreateDatabaseOptions = {
95
+ forcedWrite: false,
96
+ ...defaultOptions,
97
+ };
98
+
99
+ client.defaultConnectOptions = {
100
+ ...defaultOptions,
101
+ };
102
+ });
103
+
104
+ afterAll(async () => {
105
+ await client.dispose();
106
+
107
+ expect(client.isValid).toBeFalsy();
108
+
109
+ if (isLocal()) {
110
+ fs.rmdirSync(testConfig.tmpDir!);
111
+ }
112
+ });
113
+
114
+ describe('Client', () => {
115
+ test('#createDatabase()', async () => {
116
+ const attachment = await client.createDatabase(getTempFile('Client-createDatabase.fdb'));
117
+ await attachment.dropDatabase();
118
+ });
119
+
120
+ test('#connect()', async () => {
121
+ const filename = getTempFile('Client-connect.fdb');
122
+ const attachment1 = await client.createDatabase(filename);
123
+ const attachment2 = await client.connect(filename);
124
+
125
+ expect(attachment1.isValid).toBeTruthy();
126
+ expect(attachment2.isValid).toBeTruthy();
127
+
128
+ await attachment2.disconnect();
129
+ await attachment1.dropDatabase();
130
+
131
+ expect(attachment1.isValid).toBeFalsy();
132
+ expect(attachment2.isValid).toBeFalsy();
133
+ });
134
+
135
+ test('setDatabaseReadWriteMode', async () => {
136
+ const filename = getTempFile('setDatabaseReadWriteMode.fdb');
137
+ const attachment1 = await client.createDatabase(filename);
138
+ await attachment1.disconnect();
139
+
140
+ const attachment2 = await client.connect(filename, {
141
+ setDatabaseReadWriteMode: DatabaseReadWriteMode.READ_ONLY,
142
+ });
143
+ try {
144
+ const transaction = await attachment2.startTransaction();
145
+ await expect(attachment2.execute(transaction, 'create table t1 (id integer)')).rejects.toThrow();
146
+ await transaction.rollback();
147
+ } finally {
148
+ await attachment2.disconnect();
149
+ }
150
+
151
+ const attachment3 = await client.connect(filename, {
152
+ setDatabaseReadWriteMode: DatabaseReadWriteMode.READ_WRITE,
153
+ });
154
+ try {
155
+ const transaction = await attachment3.startTransaction();
156
+ await attachment3.execute(transaction, 'create table t1 (id integer)');
157
+ await transaction.commit();
158
+ } finally {
159
+ await attachment3.disconnect();
160
+ }
161
+
162
+ const attachment4 = await client.connect(filename);
163
+ await attachment4.dropDatabase();
164
+ });
165
+ });
166
+
167
+ describe('Attachment', () => {
168
+ test('#startTransaction()', async () => {
169
+ const attachment = await client.createDatabase(getTempFile('Attachment-startTransaction.fdb'));
170
+
171
+ const isolationQuery = "select rdb$get_context('SYSTEM', 'ISOLATION_LEVEL') from rdb$database";
172
+
173
+ const transaction1 = await attachment.startTransaction();
174
+ expect(transaction1.isValid).toBeTruthy();
175
+ expect((await attachment.executeSingleton(transaction1, isolationQuery))[0]).toBe('SNAPSHOT');
176
+ await transaction1.commit();
177
+ expect(transaction1.isValid).toBeFalsy();
178
+
179
+ const transaction2 = await attachment.startTransaction({
180
+ isolation: TransactionIsolation.READ_COMMITTED,
181
+ });
182
+ expect(transaction2.isValid).toBeTruthy();
183
+ expect((await attachment.executeSingleton(transaction2, isolationQuery))[0]).toBe('READ COMMITTED');
184
+ await transaction2.commit();
185
+ expect(transaction2.isValid).toBeFalsy();
186
+
187
+ const transaction3 = await attachment.startTransaction({
188
+ isolation: TransactionIsolation.CONSISTENCY,
189
+ });
190
+ expect(transaction3.isValid).toBeTruthy();
191
+ expect((await attachment.executeSingleton(transaction3, isolationQuery))[0]).toBe('CONSISTENCY');
192
+ await transaction3.commit();
193
+ expect(transaction3.isValid).toBeFalsy();
194
+
195
+ await attachment.dropDatabase();
196
+ });
197
+
198
+ test('#prepare()', async () => {
199
+ const attachment = await client.createDatabase(getTempFile('Attachment-prepare.fdb'));
200
+ const transaction = await attachment.startTransaction();
201
+
202
+ const statement = await attachment.prepare(transaction, 'create table t1 (n1 integer)');
203
+ expect(statement.isValid).toBeTruthy();
204
+ await statement.dispose();
205
+ expect(statement.isValid).toBeFalsy();
206
+
207
+ let error: Error | undefined;
208
+ try {
209
+ await attachment.prepare(transaction, 'create select t1 (n1 integer)');
210
+ } catch (e) {
211
+ error = e as Error;
212
+ expect(error.message).toBe(
213
+ 'Dynamic SQL Error\n' + '-SQL error code = -104\n' + '-Token unknown - line 1, column 8\n' + '-select',
214
+ );
215
+ }
216
+
217
+ expect(error).toBeTruthy();
218
+
219
+ await transaction.commit();
220
+ await attachment.dropDatabase();
221
+ });
222
+
223
+ test('statement.type', async () => {
224
+ const attachment = await client.createDatabase(getTempFile('Statement-type.fdb'));
225
+ const transaction = await attachment.startTransaction();
226
+
227
+ const s1 = await attachment.prepare(transaction, 'select * from rdb$database');
228
+ expect(await s1.type).toBe(StatementType.SELECT);
229
+ await s1.dispose();
230
+
231
+ const s2 = await attachment.prepare(
232
+ transaction,
233
+ "insert into rdb$types (rdb$type_name, rdb$type, rdb$field_name) values ('A', 1, 'B')",
234
+ );
235
+ expect(await s2.type).toBe(StatementType.INSERT);
236
+ await s2.dispose();
237
+
238
+ const s3 = await attachment.prepare(transaction, 'update rdb$database set rdb$description = null');
239
+ expect(await s3.type).toBe(StatementType.UPDATE);
240
+ await s3.dispose();
241
+
242
+ const s4 = await attachment.prepare(transaction, 'delete from rdb$types where 1 = 0');
243
+ expect(await s4.type).toBe(StatementType.DELETE);
244
+ await s4.dispose();
245
+
246
+ const s5 = await attachment.prepare(transaction, 'create table t_stmt_type (n integer)');
247
+ expect(await s5.type).toBe(StatementType.DDL);
248
+ await s5.dispose();
249
+
250
+ await transaction.commit();
251
+ await attachment.dropDatabase();
252
+ });
253
+
254
+ //// TODO: #executeTransaction
255
+
256
+ test('#execute()', async () => {
257
+ const attachment = await client.createDatabase(getTempFile('Attachment-execute.fdb'));
258
+ const transaction = await attachment.startTransaction();
259
+
260
+ await attachment.execute(transaction, 'create table t1 (n1 integer)');
261
+ await transaction.commitRetaining();
262
+
263
+ await attachment.execute(transaction, 'insert into t1 (n1) values (1)');
264
+
265
+ await transaction.commit();
266
+ await attachment.dropDatabase();
267
+ });
268
+
269
+ test('#executeQuery()', async () => {
270
+ const attachment = await client.createDatabase(getTempFile('Attachment-executeQuery.fdb'));
271
+ const transaction = await attachment.startTransaction();
272
+
273
+ await attachment.execute(transaction, 'create table t1 (n1 integer)');
274
+ await transaction.commitRetaining();
275
+
276
+ const resultSet = await attachment.executeQuery(transaction, 'select n1 from t1');
277
+ expect(resultSet.isValid).toBeTruthy();
278
+ await resultSet.close();
279
+ expect(resultSet.isValid).toBeFalsy();
280
+
281
+ await transaction.commit();
282
+ await attachment.dropDatabase();
283
+ });
284
+
285
+ test('#executeSingleton()', async () => {
286
+ const attachment = await client.createDatabase(getTempFile('Attachment-executeSingleton.fdb'));
287
+ const transaction = await attachment.startTransaction();
288
+
289
+ await attachment.execute(transaction, 'create table t1 (n1 integer)');
290
+ await transaction.commitRetaining();
291
+
292
+ const result = await attachment.executeSingleton(transaction, 'insert into t1 values (11) returning n1');
293
+ expect(result.length).toBe(1);
294
+ expect(result[0]).toBe(11);
295
+
296
+ await transaction.commit();
297
+ await attachment.dropDatabase();
298
+ });
299
+
300
+ test('#executeSingletonAsObject()', async () => {
301
+ const attachment = await client.createDatabase(getTempFile('Attachment-executeSingletonAsObject.fdb'));
302
+ const transaction = await attachment.startTransaction();
303
+
304
+ await attachment.execute(transaction, 'create table t1 (n1 integer)');
305
+ await transaction.commitRetaining();
306
+
307
+ const output = await attachment.executeSingletonAsObject<{
308
+ N1: number;
309
+ }>(transaction, 'insert into t1 values (11) returning n1');
310
+ expect(output.N1).toBe(11);
311
+
312
+ await transaction.commit();
313
+ await attachment.dropDatabase();
314
+ });
315
+
316
+ test('#executeReturning()', async () => {
317
+ const attachment = await client.createDatabase(getTempFile('Attachment-executeReturning.fdb'));
318
+ const transaction = await attachment.startTransaction();
319
+
320
+ await attachment.execute(transaction, 'create table t1 (n1 integer)');
321
+ await transaction.commitRetaining();
322
+
323
+ const result = await attachment.executeReturning(transaction, 'insert into t1 values (11) returning n1');
324
+ expect(result.length).toBe(1);
325
+ expect(result[0]).toBe(11);
326
+
327
+ await transaction.commit();
328
+ await attachment.dropDatabase();
329
+ });
330
+
331
+ test('#executeReturningAsObject()', async () => {
332
+ const attachment = await client.createDatabase(getTempFile('Attachment-executeReturningAsObject.fdb'));
333
+ const transaction = await attachment.startTransaction();
334
+
335
+ await attachment.execute(transaction, 'create table t1 (n1 integer)');
336
+ await transaction.commitRetaining();
337
+
338
+ const output = await attachment.executeReturningAsObject<{
339
+ N1: number;
340
+ }>(transaction, 'insert into t1 values (11) returning n1');
341
+ expect(output.N1).toBe(11);
342
+
343
+ await transaction.commit();
344
+ await attachment.dropDatabase();
345
+ });
346
+
347
+ test('#queueEvents()', async () => {
348
+ const attachment = await client.createDatabase(getTempFile('Attachment-queueEvents.fdb'));
349
+
350
+ const eventNames: [string, number][] = [
351
+ ['EVENT1', 16],
352
+ ['EVENT2', 8],
353
+ ];
354
+
355
+ const eventsObj = eventNames.map(([name, expected]) => {
356
+ let resolver: () => void = undefined!;
357
+
358
+ const obj = {
359
+ name,
360
+ expected,
361
+ count: 0,
362
+ promise: new Promise<void>((resolve) => (resolver = resolve)),
363
+ };
364
+
365
+ return { ...obj, resolver };
366
+ });
367
+ const eventsMap = new Map(eventsObj.map((ev) => [ev.name, ev]));
368
+
369
+ const eventHandler = async (counters: [string, number][]) => {
370
+ counters.forEach(([name, count]) => {
371
+ const obj = eventsMap.get(name)!;
372
+ const newCount = obj.count + count;
373
+ obj.count = newCount;
374
+
375
+ if (newCount >= obj.expected) {
376
+ obj.resolver();
377
+ }
378
+ });
379
+
380
+ if (Array.from(eventsMap.values()).every((obj) => obj.count >= obj.expected)) {
381
+ if (events) {
382
+ await events.cancel();
383
+ expect(events.isValid).toBeFalsy();
384
+ events = null!;
385
+ }
386
+ }
387
+ };
388
+
389
+ let events = await attachment.queueEvents(Array.from(eventsMap.keys()), eventHandler);
390
+
391
+ const transaction = await attachment.startTransaction();
392
+ try {
393
+ // Iterate more times than the neccessary so that
394
+ // eventHandler may have a chance to cancel the events.
395
+ for (let i = 0; i < 20; ++i) {
396
+ await attachment.execute(
397
+ transaction,
398
+ `
328
399
  execute block as
329
400
  begin
330
401
  post_event 'EVENT1';
@@ -332,379 +403,463 @@ export function runCommonTests(client: Client) {
332
403
  post_event 'EVENT2';
333
404
  post_event 'EVENT3';
334
405
  end
335
- `);
336
-
337
- // Commit retaining to test internal event rescheduling
338
- // after each handler dispatch.
339
- await transaction.commitRetaining();
340
- expect(transaction.isValid).toBeTruthy();
341
- }
342
- }
343
- finally {
344
- await transaction.commit();
345
- expect(transaction.isValid).toBeFalsy();
346
- }
347
-
348
- await Promise.all(eventsObj.map(ev => ev.promise));
349
-
350
- if (events)
351
- await events.cancel();
352
-
353
- eventsObj.forEach(ev => expect(ev.count).toBeGreaterThanOrEqual(ev.expected));
354
-
355
- await attachment.dropDatabase();
356
- });
357
-
358
- test('#cancelOperation()', async () => {
359
- const attachment = await client.createDatabase(getTempFile('Attachment-cancelOperation.fdb'));
360
- const transaction1 = await attachment.startTransaction();
361
-
362
- await attachment.execute(transaction1, 'create table t1(n1 integer)');
363
- await transaction1.commitRetaining();
364
-
365
- await attachment.execute(transaction1, 'insert into t1 values (1)');
366
- await transaction1.commitRetaining();
367
-
368
- await attachment.execute(transaction1, 'update t1 set n1 = n1 + 1');
369
-
370
- await attachment.enableCancellation(true);
371
-
372
- const transaction2 = await attachment.startTransaction();
373
-
374
- const promise = attachment.execute(transaction2, 'update t1 set n1 = n1 - 1')
375
- .catch(e => `Error: ${e.message}`);
376
-
377
- await new Promise(resolve => setTimeout(resolve, 1000));
378
-
379
- await attachment.cancelOperation();
380
-
381
- await expect(promise).resolves.toEqual('Error: operation was cancelled');
382
-
383
- await transaction2.commit();
384
- await transaction1.commit();
385
- await attachment.dropDatabase();
386
- });
387
- });
388
-
389
- describe('Transaction', () => {
390
- test('#commit()', async () => {
391
- const attachment = await client.createDatabase(getTempFile('Transaction-commit.fdb'));
392
- const transaction = await attachment.startTransaction();
393
- await transaction.commit();
394
- await attachment.dropDatabase();
395
- });
396
-
397
- test('#commitRetaining()', async () => {
398
- const attachment = await client.createDatabase(getTempFile('Transaction-commitRetaining.fdb'));
399
- const transaction = await attachment.startTransaction();
400
- await transaction.commitRetaining();
401
- await transaction.commit();
402
- await attachment.dropDatabase();
403
- });
404
-
405
- test('#rollback()', async () => {
406
- const attachment = await client.createDatabase(getTempFile('Transaction-rollback.fdb'));
407
- const transaction = await attachment.startTransaction();
408
- await transaction.rollback();
409
- await attachment.dropDatabase();
410
- });
411
-
412
- test('#rollbackRetaining()', async () => {
413
- const attachment = await client.createDatabase(getTempFile('Transaction-rollbackRetaining.fdb'));
414
- const transaction = await attachment.startTransaction();
415
- await transaction.rollbackRetaining();
416
- await transaction.rollback();
417
- await attachment.dropDatabase();
418
- });
419
-
420
- test('transaction left opened', async () => {
421
- const attachment = await client.createDatabase(getTempFile('Transaction-left-opened.fdb'));
422
- await attachment.startTransaction();
423
- await attachment.dropDatabase();
424
- });
425
- });
426
-
427
- describe('Statement', () => {
428
- test('#execute()', async () => {
429
- const attachment = await client.createDatabase(getTempFile('Statement-execute.fdb'));
430
- const transaction = await attachment.startTransaction();
431
-
432
- const statement1 = await attachment.prepare(transaction, 'create table t1 (n1 integer)');
433
- await statement1.execute(transaction);
434
- await statement1.dispose();
435
- await transaction.commitRetaining();
436
-
437
- const statement2 = await attachment.prepare(transaction, 'insert into t1 (n1) values (?)');
438
- await statement2.execute(transaction, [1]);
439
- await statement2.execute(transaction, [null]);
440
- await statement2.execute(transaction, [10]);
441
- await statement2.execute(transaction, [100]);
442
- expect(statement2.isValid).toBeTruthy();
443
- await statement2.dispose();
444
- expect(statement2.isValid).toBeFalsy();
445
-
446
- const rs = await attachment.executeQuery(transaction,
447
- `select sum(n1) || ', ' || count(n1) || ', ' || count(*) ret from t1`);
448
- const ret = await rs.fetchAsObject<{ RET: string }>();
449
- await rs.close();
450
-
451
- expect(ret[0].RET).toStrictEqual('111, 3, 4');
406
+ `,
407
+ );
408
+
409
+ // Commit retaining to test internal event rescheduling
410
+ // after each handler dispatch.
411
+ await transaction.commitRetaining();
412
+ expect(transaction.isValid).toBeTruthy();
413
+ }
414
+ } finally {
415
+ await transaction.commit();
416
+ expect(transaction.isValid).toBeFalsy();
417
+ }
418
+
419
+ await Promise.all(eventsObj.map((ev) => ev.promise));
420
+
421
+ if (events) {
422
+ await events.cancel();
423
+ }
424
+
425
+ eventsObj.forEach((ev) => expect(ev.count).toBeGreaterThanOrEqual(ev.expected));
426
+
427
+ await attachment.dropDatabase();
428
+ });
429
+
430
+ test('#cancelOperation()', async () => {
431
+ const attachment = await client.createDatabase(getTempFile('Attachment-cancelOperation.fdb'));
432
+ const transaction1 = await attachment.startTransaction();
433
+
434
+ await attachment.execute(transaction1, 'create table t1(n1 integer)');
435
+ await transaction1.commitRetaining();
436
+
437
+ await attachment.execute(transaction1, 'insert into t1 values (1)');
438
+ await transaction1.commitRetaining();
439
+
440
+ await attachment.execute(transaction1, 'update t1 set n1 = n1 + 1');
441
+
442
+ await attachment.enableCancellation(true);
443
+
444
+ const transaction2 = await attachment.startTransaction();
445
+
446
+ const promise = attachment
447
+ .execute(transaction2, 'update t1 set n1 = n1 - 1')
448
+ .catch((e) => `Error: ${e.message}`);
449
+
450
+ await new Promise((resolve) => setTimeout(resolve, 1000));
451
+
452
+ await attachment.cancelOperation();
453
+
454
+ await expect(promise).resolves.toEqual('Error: operation was cancelled');
455
+
456
+ await transaction2.commit();
457
+ await transaction1.commit();
458
+ await attachment.dropDatabase();
459
+ });
460
+ });
461
+
462
+ describe('Transaction', () => {
463
+ test('#commit()', async () => {
464
+ const attachment = await client.createDatabase(getTempFile('Transaction-commit.fdb'));
465
+ const transaction = await attachment.startTransaction();
466
+ await transaction.commit();
467
+ await attachment.dropDatabase();
468
+ });
469
+
470
+ test('#commitRetaining()', async () => {
471
+ const attachment = await client.createDatabase(getTempFile('Transaction-commitRetaining.fdb'));
472
+ const transaction = await attachment.startTransaction();
473
+ await transaction.commitRetaining();
474
+ await transaction.commit();
475
+ await attachment.dropDatabase();
476
+ });
477
+
478
+ test('#rollback()', async () => {
479
+ const attachment = await client.createDatabase(getTempFile('Transaction-rollback.fdb'));
480
+ const transaction = await attachment.startTransaction();
481
+ await transaction.rollback();
482
+ await attachment.dropDatabase();
483
+ });
484
+
485
+ test('#rollbackRetaining()', async () => {
486
+ const attachment = await client.createDatabase(getTempFile('Transaction-rollbackRetaining.fdb'));
487
+ const transaction = await attachment.startTransaction();
488
+ await transaction.rollbackRetaining();
489
+ await transaction.rollback();
490
+ await attachment.dropDatabase();
491
+ });
492
+
493
+ test('transaction left opened', async () => {
494
+ const attachment = await client.createDatabase(getTempFile('Transaction-left-opened.fdb'));
495
+ await attachment.startTransaction();
496
+ await attachment.dropDatabase();
497
+ });
498
+ });
499
+
500
+ describe('Statement', () => {
501
+ test('#execute()', async () => {
502
+ const attachment = await client.createDatabase(getTempFile('Statement-execute.fdb'));
503
+ const transaction = await attachment.startTransaction();
504
+
505
+ const statement1 = await attachment.prepare(transaction, 'create table t1 (n1 integer)');
506
+ await statement1.execute(transaction);
507
+ await statement1.dispose();
508
+ await transaction.commitRetaining();
509
+
510
+ const statement2 = await attachment.prepare(transaction, 'insert into t1 (n1) values (?)');
511
+ await statement2.execute(transaction, [1]);
512
+ await statement2.execute(transaction, [null]);
513
+ await statement2.execute(transaction, [10]);
514
+ await statement2.execute(transaction, [100]);
515
+ expect(statement2.isValid).toBeTruthy();
516
+ await statement2.dispose();
517
+ expect(statement2.isValid).toBeFalsy();
452
518
 
453
- await transaction.commit();
454
- await attachment.dropDatabase();
455
- });
456
-
457
- test('#executeQuery()', async () => {
458
- const attachment = await client.createDatabase(getTempFile('Statement-executeQuery.fdb'));
459
- const transaction = await attachment.startTransaction();
460
-
461
- const statement1 = await attachment.prepare(transaction, 'create table t1 (n1 integer)');
462
- await statement1.execute(transaction);
463
- await statement1.dispose();
464
- await transaction.commitRetaining();
465
-
466
- const statement2 = await attachment.prepare(transaction, 'select n1 from t1');
467
- const resultSet2 = await statement2.executeQuery(transaction);
468
- await resultSet2.close();
469
- await statement2.dispose();
470
-
471
- await transaction.commit();
472
- await attachment.dropDatabase();
473
- });
474
-
475
- test('#executeSingleton()', async () => {
476
- const attachment = await client.createDatabase(getTempFile('Attachment-executeSingleton.fdb'));
477
- const transaction = await attachment.startTransaction();
478
-
479
- await attachment.execute(transaction, 'create table t1 (n1 integer)');
480
- await transaction.commitRetaining();
481
-
482
- const statement = await attachment.prepare(transaction, 'insert into t1 values (11) returning n1, n1 * 2');
483
-
484
- const result = await statement.executeSingleton(transaction);
485
- expect(result.length).toBe(2);
486
- expect(result[0]).toBe(11);
487
- expect(result[1]).toBe(11 * 2);
488
-
489
- await statement.dispose();
490
-
491
- await transaction.commit();
492
- await attachment.dropDatabase();
493
- });
494
-
495
- test('#executeReturning()', async () => {
496
- const attachment = await client.createDatabase(getTempFile('Attachment-executeReturning.fdb'));
497
- const transaction = await attachment.startTransaction();
498
-
499
- await attachment.execute(transaction, 'create table t1 (n1 integer)');
500
- await transaction.commitRetaining();
501
-
502
- const statement = await attachment.prepare(transaction, 'insert into t1 values (11) returning n1, n1 * 2');
503
-
504
- const result = await statement.executeReturning(transaction);
505
- expect(result.length).toBe(2);
506
- expect(result[0]).toBe(11);
507
- expect(result[1]).toBe(11 * 2);
508
-
509
- await statement.dispose();
510
-
511
- await transaction.commit();
512
- await attachment.dropDatabase();
513
- });
514
-
515
- test('#columnLabels()', async () => {
516
- const attachment = await client.createDatabase(getTempFile('Statement-columnLabels.fdb'));
517
- const transaction = await attachment.startTransaction();
518
-
519
- const statement1 = await attachment.prepare(transaction, 'create table t1 (n1 integer)');
520
- expect(await statement1.columnLabels).toStrictEqual([]);
521
- await statement1.execute(transaction);
522
- await statement1.dispose();
523
- await transaction.commitRetaining();
524
-
525
- const statement2 = await attachment.prepare(transaction, 'select n1, n1 x from t1');
526
- expect(await statement2.columnLabels).toStrictEqual(['N1', 'X']);
527
- await statement2.dispose();
528
-
529
- await transaction.commit();
530
- await attachment.dropDatabase();
531
- });
532
-
533
- test('#hasResultSet()', async () => {
534
- const attachment = await client.createDatabase(getTempFile('Statement-hasResultSet.fdb'));
535
- const transaction = await attachment.startTransaction();
536
-
537
- const statement1 = await attachment.prepare(transaction, 'create table t1 (n1 integer)');
538
- expect(statement1.hasResultSet).toBe(false);
539
- await statement1.execute(transaction);
540
- await statement1.dispose();
541
-
542
- await transaction.commitRetaining();
543
-
544
- const statement2 = await attachment.prepare(transaction, 'insert into t1 values (1)');
545
- expect(statement2.hasResultSet).toBe(false);
546
- await statement2.dispose();
547
-
548
- const statement3 = await attachment.prepare(transaction, 'insert into t1 values (1) returning *');
549
- expect(statement3.hasResultSet).toBe(false);
550
- await statement3.dispose();
551
-
552
- const statement4 = await attachment.prepare(transaction, 'execute block as begin end');
553
- expect(statement4.hasResultSet).toBe(false);
554
- await statement4.dispose();
555
-
556
- const statement5 = await attachment.prepare(transaction, 'select * from t1');
557
- expect(statement5.hasResultSet).toBe(true);
558
- await statement5.dispose();
559
-
560
- const statement6 = await attachment.prepare(transaction, 'execute block returns (n integer) as begin suspend; end');
561
- expect(statement6.hasResultSet).toBe(true);
562
- await statement6.dispose();
563
-
564
- const statement7 = await attachment.prepare(transaction, 'execute block returns (n integer) as begin end');
565
- expect(statement7.hasResultSet).toBe(true);
566
- await statement7.dispose();
567
-
568
- await transaction.commit();
569
- await attachment.dropDatabase();
570
- });
571
- });
572
-
573
- describe('ResultSet', () => {
574
- test('#fetch()', async () => {
575
- const attachment = await client.createDatabase(getTempFile('ResultSet-fetch.fdb'));
576
-
577
- let transaction = await attachment.startTransaction();
578
-
579
- const blobBuffer = Buffer.alloc(11, '12345678á9');
580
-
581
- const fields = [
582
- { name: 'x_short', type: 'numeric(2)', valToStr: (v: any) => v },
583
- { name: 'x_int', type: 'integer', valToStr: (v: any) => v },
584
- { name: 'x_int_scale', type: 'numeric(5, 2)', valToStr: (v: any) => v },
585
- { name: 'x_bigint', type: 'bigint', valToStr: (v: any) => v },
586
- { name: 'x_bigint_scale', type: 'numeric(15, 2)', valToStr: (v: any) => v },
587
- { name: 'x_int128', type: 'int128', valToStr: (v: any) => v },
588
- { name: 'x_int128_scale', type: 'numeric(20, 2)', valToStr: (v: any) => v },
589
- { name: 'x_dec16', type: 'decfloat(16)', valToStr: (v: any) => v },
590
- { name: 'x_dec34', type: 'decfloat(34)', valToStr: (v: any) => v },
591
- { name: 'x_double', type: 'double precision', valToStr: (v: any) => v },
592
- { name: 'x_date1', type: 'date', valToStr: (v: any) => `date '${dateToString(v)}'` },
593
- { name: 'x_date2', type: 'date', valToStr: (v: any) => `date '${dateToString(v)}'` },
594
- { name: 'x_date3', type: 'date', valToStr: (v: any) => `date '${dateToString(v)}'` },
595
- { name: 'x_time', type: 'time', valToStr: (v: any) => `time '${timeToString(v)}'` },
596
- {
597
- name: 'x_time_tz1',
598
- type: 'time with time zone',
599
- valToStr: (v: ZonedDate) =>
600
- `${timeTzToString({ date: v.date, timeZone: 'GMT', offset: 0 })} at time zone '${v.timeZone}'`
601
- },
602
- {
603
- name: 'x_time_tz2',
604
- type: 'time with time zone',
605
- valToStr: (v: ZonedDate) =>
606
- `${timeTzToString({ date: v.date, timeZone: 'GMT', offset: 0 })} at time zone '${v.timeZone}'`
607
- },
608
- { name: 'x_timestamp1', type: 'timestamp', valToStr: (v: any) => `timestamp '${dateTimeToString(v)}'` },
609
- { name: 'x_timestamp2', type: 'timestamp', valToStr: (v: any) => `timestamp '${dateTimeToString(v)}'` },
610
- { name: 'x_timestamp3', type: 'timestamp', valToStr: (v: any) => `timestamp '${dateTimeToString(v)}'` },
611
- {
612
- name: 'x_timestamp_tz1',
613
- type: 'timestamp with time zone',
614
- valToStr: (v: ZonedDate) =>
615
- `${dateTimeTzToString({ date: v.date, timeZone: 'GMT', offset: 0 })} at time zone '${v.timeZone}'`
616
- },
617
- {
618
- name: 'x_timestamp_tz2',
619
- type: 'timestamp with time zone',
620
- valToStr: (v: ZonedDate) =>
621
- `${dateTimeTzToString({ date: v.date, timeZone: 'GMT', offset: 0 })} at time zone '${v.timeZone}'`
622
- },
623
- { name: 'x_boolean', type: 'boolean', valToStr: (v: any) => v },
624
- { name: 'x_varchar', type: 'varchar(10) character set utf8', valToStr: (v: any) => `'${v}'` },
625
- { name: 'x_char', type: 'char(10) character set utf8', valToStr: (v: any) => `'${v}'` },
626
- { name: 'x_blob1', type: 'blob', valToStr: (v: Buffer) => `'${v.toString()}'` },
627
- { name: 'x_blob2', type: 'blob', valToStr: () => `'${blobBuffer.toString()}'` }
628
- ];
629
-
630
- const statement1 = await attachment.prepare(transaction,
631
- `create table t1 (${fields.map(f => `${f.name} ${f.type}`).join(', ')})`);
632
- await statement1.execute(transaction);
633
- await statement1.dispose();
634
- await transaction.commitRetaining();
635
-
636
- const recordCount = 5;
637
- let parameters: any[];
638
-
639
- { // scope
640
- const statement2a = await attachment.prepare(transaction,
641
- `insert into t1 (${fields.map(f => f.name).join(', ')}) values (${fields.map(() => '?').join(', ')})`);
642
-
643
- // Test execution in a new transaction, after the one used in prepare was committed.
644
- await transaction.commit();
645
- transaction = await attachment.startTransaction();
646
-
647
- const blob = await attachment.createBlob(transaction);
648
- expect(blob.isValid).toBeTruthy();
649
- await blob.write(blobBuffer);
650
- expect(blob.isValid).toBeTruthy();
651
- await blob.close();
652
- expect(blob.isValid).toBeFalsy();
653
-
654
- parameters = [
655
- -1,
656
- -2,
657
- -3.45,
658
- -2,
659
- -3.45,
660
- '-45699999999999999999999999999999999876',
661
- '-45699999999999999999999999999999999.87',
662
- '-456999999999876',
663
- '-456999999999999999999999999999.87',
664
- -4.567,
665
- new Date(2017, 3 - 1, 26),
666
- new Date(new Date(2000, 3 - 1, 26).setFullYear(50)),
667
- new Date(9999, 3 - 1, 26),
668
- new Date(2020, 1 - 1, 1, 11, 56, 32, 123),
669
- {
670
- date: new Date(Date.UTC(2020, 1 - 1, 1, 11, 56, 32, 123)),
671
- timeZone: 'America/New_York'
672
- } as ZonedDate,
673
- {
674
- date: new Date(Date.UTC(2020, 1 - 1, 1, 11, 56, 32, 123)),
675
- timeZone: 'America/Sao_Paulo'
676
- } as ZonedDate,
677
- new Date(2017, 3 - 1, 26, 11, 56, 32, 123),
678
- new Date(new Date(2000, 3 - 1, 26, 11, 56, 32, 123).setFullYear(50)),
679
- new Date(9999, 3 - 1, 26, 11, 56, 32, 123),
680
- { date: new Date(Date.UTC(2021, 6 - 1, 7, 11, 56, 32, 123)), timeZone: 'America/New_York' } as ZonedDate,
681
- { date: new Date(Date.UTC(2021, 6 - 1, 7, 11, 56, 32, 123)), timeZone: 'America/Sao_Paulo' } as ZonedDate,
682
- true,
683
- '123áé4567',
684
- '123áé4567',
685
- blobBuffer,
686
- blob
687
- ];
688
-
689
- for (let i = 0; i < recordCount; ++i)
690
- await statement2a.execute(transaction, parameters);
691
- await statement2a.dispose();
692
- }
693
-
694
- { // scope
695
- const statement2b = await attachment.prepare(transaction,
696
- `insert into t1 (${fields.map(f => f.name).join(', ')}) ` +
697
- `values (${parameters.map((val, index) => fields[index].valToStr(val)).join(', ')})`);
698
-
699
- for (let i = 0; i < recordCount; ++i)
700
- await statement2b.execute(transaction);
701
- await statement2b.dispose();
702
- }
703
-
704
- await transaction.commitRetaining();
705
-
706
- const statement3 = await attachment.prepare(transaction,
707
- `select x_short,
519
+ const rs = await attachment.executeQuery(
520
+ transaction,
521
+ `select sum(n1) || ', ' || count(n1) || ', ' || count(*) ret from t1`,
522
+ );
523
+ const ret = await rs.fetchAsObject<{ RET: string }>();
524
+ await rs.close();
525
+
526
+ expect(ret[0].RET).toStrictEqual('111, 3, 4');
527
+
528
+ await transaction.commit();
529
+ await attachment.dropDatabase();
530
+ });
531
+
532
+ test('#executeQuery()', async () => {
533
+ const attachment = await client.createDatabase(getTempFile('Statement-executeQuery.fdb'));
534
+ const transaction = await attachment.startTransaction();
535
+
536
+ const statement1 = await attachment.prepare(transaction, 'create table t1 (n1 integer)');
537
+ await statement1.execute(transaction);
538
+ await statement1.dispose();
539
+ await transaction.commitRetaining();
540
+
541
+ const statement2 = await attachment.prepare(transaction, 'select n1 from t1');
542
+ const resultSet2 = await statement2.executeQuery(transaction);
543
+ await resultSet2.close();
544
+ await statement2.dispose();
545
+
546
+ await transaction.commit();
547
+ await attachment.dropDatabase();
548
+ });
549
+
550
+ test('#executeSingleton()', async () => {
551
+ const attachment = await client.createDatabase(getTempFile('Attachment-executeSingleton.fdb'));
552
+ const transaction = await attachment.startTransaction();
553
+
554
+ await attachment.execute(transaction, 'create table t1 (n1 integer)');
555
+ await transaction.commitRetaining();
556
+
557
+ const statement = await attachment.prepare(transaction, 'insert into t1 values (11) returning n1, n1 * 2');
558
+
559
+ const result = await statement.executeSingleton(transaction);
560
+ expect(result.length).toBe(2);
561
+ expect(result[0]).toBe(11);
562
+ expect(result[1]).toBe(11 * 2);
563
+
564
+ await statement.dispose();
565
+
566
+ await transaction.commit();
567
+ await attachment.dropDatabase();
568
+ });
569
+
570
+ test('#executeReturning()', async () => {
571
+ const attachment = await client.createDatabase(getTempFile('Attachment-executeReturning.fdb'));
572
+ const transaction = await attachment.startTransaction();
573
+
574
+ await attachment.execute(transaction, 'create table t1 (n1 integer)');
575
+ await transaction.commitRetaining();
576
+
577
+ const statement = await attachment.prepare(transaction, 'insert into t1 values (11) returning n1, n1 * 2');
578
+
579
+ const result = await statement.executeReturning(transaction);
580
+ expect(result.length).toBe(2);
581
+ expect(result[0]).toBe(11);
582
+ expect(result[1]).toBe(11 * 2);
583
+
584
+ await statement.dispose();
585
+
586
+ await transaction.commit();
587
+ await attachment.dropDatabase();
588
+ });
589
+
590
+ test('#columnLabels()', async () => {
591
+ const attachment = await client.createDatabase(getTempFile('Statement-columnLabels.fdb'));
592
+ const transaction = await attachment.startTransaction();
593
+
594
+ const statement1 = await attachment.prepare(transaction, 'create table t1 (n1 integer)');
595
+ expect(await statement1.columnLabels).toStrictEqual([]);
596
+ await statement1.execute(transaction);
597
+ await statement1.dispose();
598
+ await transaction.commitRetaining();
599
+
600
+ const statement2 = await attachment.prepare(transaction, 'select n1, n1 x from t1');
601
+ expect(await statement2.columnLabels).toStrictEqual(['N1', 'X']);
602
+ await statement2.dispose();
603
+
604
+ await transaction.commit();
605
+ await attachment.dropDatabase();
606
+ });
607
+
608
+ test('#hasResultSet()', async () => {
609
+ const attachment = await client.createDatabase(getTempFile('Statement-hasResultSet.fdb'));
610
+ const transaction = await attachment.startTransaction();
611
+
612
+ const statement1 = await attachment.prepare(transaction, 'create table t1 (n1 integer)');
613
+ expect(statement1.hasResultSet).toBe(false);
614
+ await statement1.execute(transaction);
615
+ await statement1.dispose();
616
+
617
+ await transaction.commitRetaining();
618
+
619
+ const statement2 = await attachment.prepare(transaction, 'insert into t1 values (1)');
620
+ expect(statement2.hasResultSet).toBe(false);
621
+ await statement2.dispose();
622
+
623
+ const statement3 = await attachment.prepare(transaction, 'insert into t1 values (1) returning *');
624
+ expect(statement3.hasResultSet).toBe(false);
625
+ await statement3.dispose();
626
+
627
+ const statement4 = await attachment.prepare(transaction, 'execute block as begin end');
628
+ expect(statement4.hasResultSet).toBe(false);
629
+ await statement4.dispose();
630
+
631
+ const statement5 = await attachment.prepare(transaction, 'select * from t1');
632
+ expect(statement5.hasResultSet).toBe(true);
633
+ await statement5.dispose();
634
+
635
+ const statement6 = await attachment.prepare(
636
+ transaction,
637
+ 'execute block returns (n integer) as begin suspend; end',
638
+ );
639
+ expect(statement6.hasResultSet).toBe(true);
640
+ await statement6.dispose();
641
+
642
+ const statement7 = await attachment.prepare(transaction, 'execute block returns (n integer) as begin end');
643
+ expect(statement7.hasResultSet).toBe(true);
644
+ await statement7.dispose();
645
+
646
+ await transaction.commit();
647
+ await attachment.dropDatabase();
648
+ });
649
+ });
650
+
651
+ describe('ResultSet', () => {
652
+ test('#fetch()', async () => {
653
+ const attachment = await client.createDatabase(getTempFile('ResultSet-fetch.fdb'));
654
+
655
+ let transaction = await attachment.startTransaction();
656
+
657
+ const blobBuffer = Buffer.alloc(11, '12345678á9');
658
+
659
+ const fields = [
660
+ { name: 'x_short', type: 'numeric(2)', valToStr: (v: any) => v },
661
+ { name: 'x_int', type: 'integer', valToStr: (v: any) => v },
662
+ {
663
+ name: 'x_int_scale',
664
+ type: 'numeric(5, 2)',
665
+ valToStr: (v: any) => v,
666
+ },
667
+ { name: 'x_bigint', type: 'bigint', valToStr: (v: any) => v },
668
+ {
669
+ name: 'x_bigint_scale',
670
+ type: 'numeric(15, 2)',
671
+ valToStr: (v: any) => v,
672
+ },
673
+ { name: 'x_int128', type: 'int128', valToStr: (v: any) => v },
674
+ {
675
+ name: 'x_int128_scale',
676
+ type: 'numeric(20, 2)',
677
+ valToStr: (v: any) => v,
678
+ },
679
+ { name: 'x_dec16', type: 'decfloat(16)', valToStr: (v: any) => v },
680
+ { name: 'x_dec34', type: 'decfloat(34)', valToStr: (v: any) => v },
681
+ {
682
+ name: 'x_double',
683
+ type: 'double precision',
684
+ valToStr: (v: any) => v,
685
+ },
686
+ {
687
+ name: 'x_date1',
688
+ type: 'date',
689
+ valToStr: (v: any) => `date '${dateToString(v)}'`,
690
+ },
691
+ {
692
+ name: 'x_date2',
693
+ type: 'date',
694
+ valToStr: (v: any) => `date '${dateToString(v)}'`,
695
+ },
696
+ {
697
+ name: 'x_date3',
698
+ type: 'date',
699
+ valToStr: (v: any) => `date '${dateToString(v)}'`,
700
+ },
701
+ {
702
+ name: 'x_time',
703
+ type: 'time',
704
+ valToStr: (v: any) => `time '${timeToString(v)}'`,
705
+ },
706
+ {
707
+ name: 'x_time_tz1',
708
+ type: 'time with time zone',
709
+ valToStr: (v: ZonedDate) =>
710
+ `${timeTzToString({ date: v.date, timeZone: 'GMT', offset: 0 })} at time zone '${v.timeZone}'`,
711
+ },
712
+ {
713
+ name: 'x_time_tz2',
714
+ type: 'time with time zone',
715
+ valToStr: (v: ZonedDate) =>
716
+ `${timeTzToString({ date: v.date, timeZone: 'GMT', offset: 0 })} at time zone '${v.timeZone}'`,
717
+ },
718
+ {
719
+ name: 'x_timestamp1',
720
+ type: 'timestamp',
721
+ valToStr: (v: any) => `timestamp '${dateTimeToString(v)}'`,
722
+ },
723
+ {
724
+ name: 'x_timestamp2',
725
+ type: 'timestamp',
726
+ valToStr: (v: any) => `timestamp '${dateTimeToString(v)}'`,
727
+ },
728
+ {
729
+ name: 'x_timestamp3',
730
+ type: 'timestamp',
731
+ valToStr: (v: any) => `timestamp '${dateTimeToString(v)}'`,
732
+ },
733
+ {
734
+ name: 'x_timestamp_tz1',
735
+ type: 'timestamp with time zone',
736
+ valToStr: (v: ZonedDate) =>
737
+ `${dateTimeTzToString({ date: v.date, timeZone: 'GMT', offset: 0 })} at time zone '${v.timeZone}'`,
738
+ },
739
+ {
740
+ name: 'x_timestamp_tz2',
741
+ type: 'timestamp with time zone',
742
+ valToStr: (v: ZonedDate) =>
743
+ `${dateTimeTzToString({ date: v.date, timeZone: 'GMT', offset: 0 })} at time zone '${v.timeZone}'`,
744
+ },
745
+ { name: 'x_boolean', type: 'boolean', valToStr: (v: any) => v },
746
+ {
747
+ name: 'x_varchar',
748
+ type: 'varchar(10) character set utf8',
749
+ valToStr: (v: any) => `'${v}'`,
750
+ },
751
+ {
752
+ name: 'x_char',
753
+ type: 'char(10) character set utf8',
754
+ valToStr: (v: any) => `'${v}'`,
755
+ },
756
+ {
757
+ name: 'x_blob1',
758
+ type: 'blob',
759
+ valToStr: (v: Buffer) => `'${v.toString()}'`,
760
+ },
761
+ {
762
+ name: 'x_blob2',
763
+ type: 'blob',
764
+ valToStr: () => `'${blobBuffer.toString()}'`,
765
+ },
766
+ ];
767
+
768
+ const statement1 = await attachment.prepare(
769
+ transaction,
770
+ `create table t1 (${fields.map((f) => `${f.name} ${f.type}`).join(', ')})`,
771
+ );
772
+ await statement1.execute(transaction);
773
+ await statement1.dispose();
774
+ await transaction.commitRetaining();
775
+
776
+ const recordCount = 5;
777
+ let parameters: any[];
778
+
779
+ {
780
+ // scope
781
+ const statement2a = await attachment.prepare(
782
+ transaction,
783
+ `insert into t1 (${fields.map((f) => f.name).join(', ')}) values (${fields.map(() => '?').join(', ')})`,
784
+ );
785
+
786
+ // Test execution in a new transaction, after the one used in prepare was committed.
787
+ await transaction.commit();
788
+ transaction = await attachment.startTransaction();
789
+
790
+ const blob = await attachment.createBlob(transaction);
791
+ expect(blob.isValid).toBeTruthy();
792
+ await blob.write(blobBuffer);
793
+ expect(blob.isValid).toBeTruthy();
794
+ await blob.close();
795
+ expect(blob.isValid).toBeFalsy();
796
+
797
+ parameters = [
798
+ -1,
799
+ -2,
800
+ -3.45,
801
+ -2,
802
+ -3.45,
803
+ '-45699999999999999999999999999999999876',
804
+ '-45699999999999999999999999999999999.87',
805
+ '-456999999999876',
806
+ '-456999999999999999999999999999.87',
807
+ -4.567,
808
+ new Date(2017, 3 - 1, 26),
809
+ new Date(new Date(2000, 3 - 1, 26).setFullYear(50)),
810
+ new Date(9999, 3 - 1, 26),
811
+ new Date(2020, 1 - 1, 1, 11, 56, 32, 123),
812
+ {
813
+ date: new Date(Date.UTC(2020, 1 - 1, 1, 11, 56, 32, 123)),
814
+ timeZone: 'America/New_York',
815
+ } as ZonedDate,
816
+ {
817
+ date: new Date(Date.UTC(2020, 1 - 1, 1, 11, 56, 32, 123)),
818
+ timeZone: 'America/Sao_Paulo',
819
+ } as ZonedDate,
820
+ new Date(2017, 3 - 1, 26, 11, 56, 32, 123),
821
+ new Date(new Date(2000, 3 - 1, 26, 11, 56, 32, 123).setFullYear(50)),
822
+ new Date(9999, 3 - 1, 26, 11, 56, 32, 123),
823
+ {
824
+ date: new Date(Date.UTC(2021, 6 - 1, 7, 11, 56, 32, 123)),
825
+ timeZone: 'America/New_York',
826
+ } as ZonedDate,
827
+ {
828
+ date: new Date(Date.UTC(2021, 6 - 1, 7, 11, 56, 32, 123)),
829
+ timeZone: 'America/Sao_Paulo',
830
+ } as ZonedDate,
831
+ true,
832
+ '123áé4567',
833
+ '123áé4567',
834
+ blobBuffer,
835
+ blob,
836
+ ];
837
+
838
+ for (let i = 0; i < recordCount; ++i) {
839
+ await statement2a.execute(transaction, parameters);
840
+ }
841
+ await statement2a.dispose();
842
+ }
843
+
844
+ {
845
+ // scope
846
+ const statement2b = await attachment.prepare(
847
+ transaction,
848
+ `insert into t1 (${fields.map((f) => f.name).join(', ')}) ` +
849
+ `values (${parameters.map((val, index) => fields[index].valToStr(val)).join(', ')})`,
850
+ );
851
+
852
+ for (let i = 0; i < recordCount; ++i) {
853
+ await statement2b.execute(transaction);
854
+ }
855
+ await statement2b.dispose();
856
+ }
857
+
858
+ await transaction.commitRetaining();
859
+
860
+ const statement3 = await attachment.prepare(
861
+ transaction,
862
+ `select x_short,
708
863
  x_int,
709
864
  x_int_scale,
710
865
  x_bigint,
@@ -736,92 +891,98 @@ export function runCommonTests(client: Client) {
736
891
  x_char || null,
737
892
  x_blob1,
738
893
  x_blob2
739
- from t1`);
740
- const resultSet3 = await statement3.executeQuery(transaction);
741
-
742
- const data = await resultSet3.fetch();
743
- expect(data.length).toBe(recordCount * 2);
744
-
745
- for (const columns of data) {
746
- let n = 0;
747
- expect(columns[n++]).toBe(-1);
748
- expect(columns[n++]).toBe(-2);
749
- expect(columns[n++]).toBe(-3.45);
750
- expect(columns[n++]).toBe(-2);
751
- expect(columns[n++]).toBe(-3.45);
752
- expect(columns[n++]).toBe('-45699999999999999999999999999999999876');
753
- expect(columns[n++]).toBe('-45699999999999999999999999999999999.87');
754
- expect(columns[n++]).toBe('-456999999999876');
755
- expect(columns[n++]).toBe('-456999999999999999999999999999.87');
756
- expect(columns[n++]).toBe(-4.567);
757
- expect(dateTimeToString(columns[n++])).toBe('2017-3-26 0:0:0.0');
758
- expect(dateTimeToString(columns[n++])).toBe('0050-3-26 0:0:0.0');
759
- expect(dateTimeToString(columns[n++])).toBe('9999-3-26 0:0:0.0');
760
- expect(timeToString(columns[n++])).toBe('11:56:32.123');
761
- expect(timeTzToString(columns[n++])).toBe(`time '6:56:32.123 America/New_York'`);
762
- expect(timeTzToString(columns[n++])).toBe(`time '8:56:32.123 America/Sao_Paulo'`);
763
- expect(dateTimeToString(columns[n++])).toBe('2017-3-26 11:56:32.123');
764
- expect(dateTimeToString(columns[n++])).toBe('0050-3-26 11:56:32.123');
765
- expect(dateTimeToString(columns[n++])).toBe('9999-3-26 11:56:32.123');
766
- expect(dateTimeTzToString(columns[n++])).toBe(`timestamp '2021-6-7 7:56:32.123 America/New_York'`);
767
- expect(dateTimeTzToString(columns[n++])).toBe(`timestamp '2021-6-7 8:56:32.123 America/Sao_Paulo'`);
768
- expect(columns[n++]).toBe(true);
769
- expect(columns[n++]).toBe('123áé4567');
770
- expect(columns[n++]).toBe(9);
771
- expect(columns[n++]).toBe(11);
772
- expect(columns[n++]).toBe('123áé4567 ');
773
- expect(columns[n++]).toBe(10);
774
- expect(columns[n++]).toBe(12);
775
- expect(columns[n++]).toBeNull();
776
- expect(columns[n++]).toBeNull();
777
-
778
- for (const i = n + 2; n < i; ++n) {
779
- const blob = columns[n] as Blob;
780
- expect(blob.isValid).toBeTruthy();
781
- const blobStream = await attachment.openBlob(transaction, blob);
782
- const buffer = Buffer.alloc(await blobStream.length);
783
- expect(await blobStream.read(buffer)).toBe(buffer.length);
784
- expect(await blobStream.read(buffer)).toBe(-1);
785
-
786
- await blobStream.close();
787
-
788
- expect(buffer.toString()).toBe('12345678á9');
789
- }
790
-
791
- expect(columns.length).toBe(n);
792
- }
793
-
794
- expect((await resultSet3.fetch()).length).toBe(0);
795
- expect((await resultSet3.fetch()).length).toBe(0);
796
-
797
- await resultSet3.close();
798
- await statement3.dispose();
799
-
800
- await transaction.commit();
801
- await attachment.dropDatabase();
802
- });
803
-
804
- test('#fetchAsObject()', async () => {
805
- const attachment = await client.createDatabase(getTempFile('ResultSet-fetchAsObject.fdb'));
806
- const transaction = await attachment.startTransaction();
807
- const resultSet = await attachment.executeQuery(transaction, 'select 1 as a, 2 as b from rdb$database');
808
- const output = await resultSet.fetchAsObject<{ A: number; B: number }>();
809
- expect(output[0].A).toBe(1);
810
- expect(output[0].B).toBe(2);
811
- await resultSet.close();
812
-
813
- await transaction.commit();
814
- await attachment.dropDatabase();
815
- });
816
-
817
- test('#fetch() with fetchSize', async () => {
818
- const attachment = await client.createDatabase(getTempFile('ResultSet-fetch-with-fetchSize.fdb'));
819
- const transaction = await attachment.startTransaction();
820
-
821
- await attachment.execute(transaction, 'create table t1 (n1 integer)');
822
- await transaction.commitRetaining();
823
-
824
- await attachment.execute(transaction, `
894
+ from t1`,
895
+ );
896
+ const resultSet3 = await statement3.executeQuery(transaction);
897
+
898
+ const data = await resultSet3.fetch();
899
+ expect(data.length).toBe(recordCount * 2);
900
+
901
+ for (const columns of data) {
902
+ let n = 0;
903
+ expect(columns[n++]).toBe(-1);
904
+ expect(columns[n++]).toBe(-2);
905
+ expect(columns[n++]).toBe(-3.45);
906
+ expect(columns[n++]).toBe(-2);
907
+ expect(columns[n++]).toBe(-3.45);
908
+ expect(columns[n++]).toBe('-45699999999999999999999999999999999876');
909
+ expect(columns[n++]).toBe('-45699999999999999999999999999999999.87');
910
+ expect(columns[n++]).toBe('-456999999999876');
911
+ expect(columns[n++]).toBe('-456999999999999999999999999999.87');
912
+ expect(columns[n++]).toBe(-4.567);
913
+ expect(dateTimeToString(columns[n++])).toBe('2017-3-26 0:0:0.0');
914
+ expect(dateTimeToString(columns[n++])).toBe('0050-3-26 0:0:0.0');
915
+ expect(dateTimeToString(columns[n++])).toBe('9999-3-26 0:0:0.0');
916
+ expect(timeToString(columns[n++])).toBe('11:56:32.123');
917
+ expect(timeTzToString(columns[n++])).toBe(`time '6:56:32.123 America/New_York'`);
918
+ expect(timeTzToString(columns[n++])).toBe(`time '8:56:32.123 America/Sao_Paulo'`);
919
+ expect(dateTimeToString(columns[n++])).toBe('2017-3-26 11:56:32.123');
920
+ expect(dateTimeToString(columns[n++])).toBe('0050-3-26 11:56:32.123');
921
+ expect(dateTimeToString(columns[n++])).toBe('9999-3-26 11:56:32.123');
922
+ expect(dateTimeTzToString(columns[n++])).toBe(`timestamp '2021-6-7 7:56:32.123 America/New_York'`);
923
+ expect(dateTimeTzToString(columns[n++])).toBe(`timestamp '2021-6-7 8:56:32.123 America/Sao_Paulo'`);
924
+ expect(columns[n++]).toBe(true);
925
+ expect(columns[n++]).toBe('123áé4567');
926
+ expect(columns[n++]).toBe(9);
927
+ expect(columns[n++]).toBe(11);
928
+ expect(columns[n++]).toBe('123áé4567 ');
929
+ expect(columns[n++]).toBe(10);
930
+ expect(columns[n++]).toBe(12);
931
+ expect(columns[n++]).toBeNull();
932
+ expect(columns[n++]).toBeNull();
933
+
934
+ for (const i = n + 2; n < i; ++n) {
935
+ const blob = columns[n] as Blob;
936
+ expect(blob.isValid).toBeTruthy();
937
+ const blobStream = await attachment.openBlob(transaction, blob);
938
+ const buffer = Buffer.alloc(await blobStream.length);
939
+ expect(await blobStream.read(buffer)).toBe(buffer.length);
940
+ expect(await blobStream.read(buffer)).toBe(-1);
941
+
942
+ await blobStream.close();
943
+
944
+ expect(buffer.toString()).toBe('12345678á9');
945
+ }
946
+
947
+ expect(columns.length).toBe(n);
948
+ }
949
+
950
+ expect((await resultSet3.fetch()).length).toBe(0);
951
+ expect((await resultSet3.fetch()).length).toBe(0);
952
+
953
+ await resultSet3.close();
954
+ await statement3.dispose();
955
+
956
+ await transaction.commit();
957
+ await attachment.dropDatabase();
958
+ });
959
+
960
+ test('#fetchAsObject()', async () => {
961
+ const attachment = await client.createDatabase(getTempFile('ResultSet-fetchAsObject.fdb'));
962
+ const transaction = await attachment.startTransaction();
963
+ const resultSet = await attachment.executeQuery(transaction, 'select 1 as a, 2 as b from rdb$database');
964
+ const output = await resultSet.fetchAsObject<{
965
+ A: number;
966
+ B: number;
967
+ }>();
968
+ expect(output[0].A).toBe(1);
969
+ expect(output[0].B).toBe(2);
970
+ await resultSet.close();
971
+
972
+ await transaction.commit();
973
+ await attachment.dropDatabase();
974
+ });
975
+
976
+ test('#fetch() with fetchSize', async () => {
977
+ const attachment = await client.createDatabase(getTempFile('ResultSet-fetch-with-fetchSize.fdb'));
978
+ const transaction = await attachment.startTransaction();
979
+
980
+ await attachment.execute(transaction, 'create table t1 (n1 integer)');
981
+ await transaction.commitRetaining();
982
+
983
+ await attachment.execute(
984
+ transaction,
985
+ `
825
986
  execute block
826
987
  as
827
988
  declare n integer = 0;
@@ -832,32 +993,35 @@ export function runCommonTests(client: Client) {
832
993
  n = n + 1;
833
994
  end
834
995
  end
835
- `);
996
+ `,
997
+ );
836
998
 
837
- const rs = await attachment.executeQuery(transaction, 'select n1 from t1 order by n1');
838
- rs.defaultFetchOptions = { fetchSize: 5 };
999
+ const rs = await attachment.executeQuery(transaction, 'select n1 from t1 order by n1');
1000
+ rs.defaultFetchOptions = { fetchSize: 5 };
839
1001
 
840
- expect((await rs.fetch()).length).toBe(5);
841
- expect((await rs.fetch({ fetchSize: 2 })).length).toBe(2);
842
- expect((await rs.fetch()).length).toBe(5);
843
- expect((await rs.fetch({ fetchSize: 36 })).length).toBe(36);
844
- expect((await rs.fetch()).length).toBe(2);
845
- expect((await rs.fetch()).length).toBe(0);
1002
+ expect((await rs.fetch()).length).toBe(5);
1003
+ expect((await rs.fetch({ fetchSize: 2 })).length).toBe(2);
1004
+ expect((await rs.fetch()).length).toBe(5);
1005
+ expect((await rs.fetch({ fetchSize: 36 })).length).toBe(36);
1006
+ expect((await rs.fetch()).length).toBe(2);
1007
+ expect((await rs.fetch()).length).toBe(0);
846
1008
 
847
- await rs.close();
1009
+ await rs.close();
848
1010
 
849
- await transaction.commit();
850
- await attachment.dropDatabase();
851
- });
1011
+ await transaction.commit();
1012
+ await attachment.dropDatabase();
1013
+ });
852
1014
 
853
- test('#fetch() with fetchSize and exception', async () => {
854
- const attachment = await client.createDatabase(getTempFile('ResultSet-fetch-with-fetchSize.fdb'));
855
- const transaction = await attachment.startTransaction();
1015
+ test('#fetch() with fetchSize and exception', async () => {
1016
+ const attachment = await client.createDatabase(getTempFile('ResultSet-fetch-with-fetchSize.fdb'));
1017
+ const transaction = await attachment.startTransaction();
856
1018
 
857
- await attachment.execute(transaction, 'create exception e1 \'e1\'');
858
- await transaction.commitRetaining();
1019
+ await attachment.execute(transaction, "create exception e1 'e1'");
1020
+ await transaction.commitRetaining();
859
1021
 
860
- const rs = await attachment.executeQuery(transaction, `
1022
+ const rs = await attachment.executeQuery(
1023
+ transaction,
1024
+ `
861
1025
  execute block returns (n integer)
862
1026
  as
863
1027
  begin
@@ -869,91 +1033,95 @@ export function runCommonTests(client: Client) {
869
1033
  n = 3;
870
1034
  suspend;
871
1035
  end
872
- `);
873
- rs.defaultFetchOptions = { fetchSize: 5 };
1036
+ `,
1037
+ );
1038
+ rs.defaultFetchOptions = { fetchSize: 5 };
874
1039
 
875
- expect((await rs.fetch()).length).toBe(2);
876
- expect(rs.fetch()).rejects.toBeTruthy();
1040
+ expect((await rs.fetch()).length).toBe(2);
1041
+ expect(rs.fetch()).rejects.toBeTruthy();
877
1042
 
878
- await rs.close();
1043
+ await rs.close();
879
1044
 
880
- await transaction.commit();
881
- await attachment.dropDatabase();
882
- });
1045
+ await transaction.commit();
1046
+ await attachment.dropDatabase();
1047
+ });
883
1048
 
884
- test('#fetch() with large blob', async () => {
885
- const attachment = await client.createDatabase(getTempFile('ResultSet-fetch-with-large-blob.fdb'));
886
- let transaction = await attachment.startTransaction();
887
- await attachment.execute(transaction, `create table t1 (x_blob blob)`);
1049
+ test('#fetch() with large blob', async () => {
1050
+ const attachment = await client.createDatabase(getTempFile('ResultSet-fetch-with-large-blob.fdb'));
1051
+ let transaction = await attachment.startTransaction();
1052
+ await attachment.execute(transaction, `create table t1 (x_blob blob)`);
888
1053
 
889
- await transaction.commit();
890
- transaction = await attachment.startTransaction();
891
- const buffer = Buffer.from('123'.repeat(60000));
1054
+ await transaction.commit();
1055
+ transaction = await attachment.startTransaction();
1056
+ const buffer = Buffer.from('123'.repeat(60000));
892
1057
 
893
- await attachment.execute(transaction, `insert into t1 (x_blob) values (?)`, [buffer]);
1058
+ await attachment.execute(transaction, `insert into t1 (x_blob) values (?)`, [buffer]);
894
1059
 
895
- await transaction.commit();
896
- transaction = await attachment.startTransaction();
1060
+ await transaction.commit();
1061
+ transaction = await attachment.startTransaction();
897
1062
 
898
- const resultSet = await attachment.executeQuery(transaction, `select x_blob from t1`);
899
- const result = await resultSet.fetch();
900
- const readStream = await attachment.openBlob(transaction, result[0][0]);
1063
+ const resultSet = await attachment.executeQuery(transaction, `select x_blob from t1`);
1064
+ const result = await resultSet.fetch();
1065
+ const readStream = await attachment.openBlob(transaction, result[0][0]);
901
1066
 
902
- const blobLength = await readStream.length;
903
- const resultBuffer = Buffer.alloc(blobLength);
1067
+ const blobLength = await readStream.length;
1068
+ const resultBuffer = Buffer.alloc(blobLength);
904
1069
 
905
- let size = 0;
906
- let n: number;
907
- while (size < blobLength && (n = await readStream.read(resultBuffer.slice(size))) > 0)
908
- size += n;
1070
+ let size = 0;
1071
+ let n: number;
1072
+ while (size < blobLength && (n = await readStream.read(resultBuffer.slice(size))) > 0) {
1073
+ size += n;
1074
+ }
909
1075
 
910
- await readStream.close();
911
- expect(resultBuffer.toString().length).toEqual(buffer.toString().length);
912
- expect(resultBuffer.toString()).toEqual(buffer.toString());
1076
+ await readStream.close();
1077
+ expect(resultBuffer.toString().length).toEqual(buffer.toString().length);
1078
+ expect(resultBuffer.toString()).toEqual(buffer.toString());
913
1079
 
914
- await resultSet.close();
915
- await transaction.commit();
916
- await attachment.dropDatabase();
917
- });
918
- });
1080
+ await resultSet.close();
1081
+ await transaction.commit();
1082
+ await attachment.dropDatabase();
1083
+ });
1084
+ });
919
1085
 
920
- describe('BlobStream', () => {
921
- test('#seek()', async () => {
922
- const attachment = await client.createDatabase(getTempFile('BlobStream-seek.fdb'));
923
- const transaction = await attachment.startTransaction();
1086
+ describe('BlobStream', () => {
1087
+ test('#seek()', async () => {
1088
+ const attachment = await client.createDatabase(getTempFile('BlobStream-seek.fdb'));
1089
+ const transaction = await attachment.startTransaction();
924
1090
 
925
- await attachment.execute(transaction, 'create table t1 (b blob)');
926
- await transaction.commitRetaining();
1091
+ await attachment.execute(transaction, 'create table t1 (b blob)');
1092
+ await transaction.commitRetaining();
927
1093
 
928
- const blobStream = await attachment.createBlob(transaction, { type: 'STREAM' });
929
- await blobStream.write(Buffer.alloc(10, '1234567890'));
930
- await blobStream.close();
931
- await attachment.execute(transaction, 'insert into t1 (b) values (?)', [blobStream.blob]);
1094
+ const blobStream = await attachment.createBlob(transaction, {
1095
+ type: 'STREAM',
1096
+ });
1097
+ await blobStream.write(Buffer.alloc(10, '1234567890'));
1098
+ await blobStream.close();
1099
+ await attachment.execute(transaction, 'insert into t1 (b) values (?)', [blobStream.blob]);
932
1100
 
933
- const blob = (await attachment.executeSingleton(transaction, 'select b from t1'))[0] as Blob;
934
- const readBlobStream = await attachment.openBlob(transaction, blob);
1101
+ const blob = (await attachment.executeSingleton(transaction, 'select b from t1'))[0] as Blob;
1102
+ const readBlobStream = await attachment.openBlob(transaction, blob);
935
1103
 
936
- const buffer = Buffer.alloc(3);
1104
+ const buffer = Buffer.alloc(3);
937
1105
 
938
- expect(await readBlobStream.seek(2)).toBe(2);
939
- expect(await readBlobStream.read(buffer)).toBe(3);
940
- expect(buffer.toString()).toBe('345');
1106
+ expect(await readBlobStream.seek(2)).toBe(2);
1107
+ expect(await readBlobStream.read(buffer)).toBe(3);
1108
+ expect(buffer.toString()).toBe('345');
941
1109
 
942
- expect(await readBlobStream.seek(-1, BlobSeekWhence.CURRENT)).toBe(4);
943
- expect(await readBlobStream.read(buffer)).toBe(3);
944
- expect(buffer.toString()).toBe('567');
1110
+ expect(await readBlobStream.seek(-1, BlobSeekWhence.CURRENT)).toBe(4);
1111
+ expect(await readBlobStream.read(buffer)).toBe(3);
1112
+ expect(buffer.toString()).toBe('567');
945
1113
 
946
- expect(await readBlobStream.seek(1, BlobSeekWhence.START)).toBe(1);
947
- expect(await readBlobStream.read(buffer)).toBe(3);
948
- expect(buffer.toString()).toBe('234');
1114
+ expect(await readBlobStream.seek(1, BlobSeekWhence.START)).toBe(1);
1115
+ expect(await readBlobStream.read(buffer)).toBe(3);
1116
+ expect(buffer.toString()).toBe('234');
949
1117
 
950
- expect(await readBlobStream.seek(-2, BlobSeekWhence.END)).toBe(8);
951
- expect(await readBlobStream.read(buffer)).toBe(2);
952
- expect(buffer.slice(0, 2).toString()).toBe('90');
1118
+ expect(await readBlobStream.seek(-2, BlobSeekWhence.END)).toBe(8);
1119
+ expect(await readBlobStream.read(buffer)).toBe(2);
1120
+ expect(buffer.slice(0, 2).toString()).toBe('90');
953
1121
 
954
- await transaction.commit();
955
- await attachment.dropDatabase();
956
- });
957
- });
958
- });
1122
+ await transaction.commit();
1123
+ await attachment.dropDatabase();
1124
+ });
1125
+ });
1126
+ });
959
1127
  }