@mcpher/gas-fakes 2.3.5 → 2.3.6

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.
@@ -0,0 +1,492 @@
1
+ import { Proxies } from "../../support/proxies.js";
2
+ import { Syncit } from "../../support/syncit.js";
3
+ import { newFakeJdbcResultSet } from "./fakejdbcresultset.js";
4
+
5
+ class FakeJdbcDatabaseMetaData {
6
+ constructor(connection, connectionId, url) {
7
+ this.__fakeObjectType = "JdbcDatabaseMetaData";
8
+ this._connection = connection;
9
+ this._connectionId = connectionId;
10
+ this._url = url;
11
+ this._type =
12
+ url.includes("postgres") || url.includes("cockroach")
13
+ ? "postgres"
14
+ : "mysql";
15
+ }
16
+
17
+ // ---------------------------------------------------------
18
+ // Core Info Methods
19
+ // ---------------------------------------------------------
20
+
21
+ getURL() {
22
+ return this._url;
23
+ }
24
+
25
+ getUserName() {
26
+ try {
27
+ const u = new URL(
28
+ this._url
29
+ .replace(/^jdbc:google:/, "")
30
+ .replace(/^jdbc:/, "")
31
+ .replace("cockroachdb", "http"),
32
+ );
33
+ return u.username;
34
+ } catch (e) {
35
+ return "unknown";
36
+ }
37
+ }
38
+
39
+ getDatabaseProductName() {
40
+ return this._type === "postgres" ? "PostgreSQL" : "MySQL";
41
+ }
42
+
43
+ getDatabaseProductVersion() {
44
+ return "unknown";
45
+ }
46
+
47
+ getDriverName() {
48
+ return "gas-fakes-jdbc-driver";
49
+ }
50
+ getDriverVersion() {
51
+ return globalThis.GasFakes?.metadata?.version || "unknown";
52
+ }
53
+ getDriverMajorVersion() {
54
+ return 2;
55
+ }
56
+ getDriverMinorVersion() {
57
+ return 3;
58
+ }
59
+
60
+ getIdentifierQuoteString() {
61
+ return this._type === "mysql" ? "`" : '"';
62
+ }
63
+
64
+ // ---------------------------------------------------------
65
+ // Retrieval Methods (ResultSets)
66
+ // ---------------------------------------------------------
67
+
68
+ getCatalogs() {
69
+ const sql =
70
+ this._type === "mysql"
71
+ ? "SELECT SCHEMA_NAME as TABLE_CAT FROM information_schema.SCHEMATA"
72
+ : "SELECT datname AS TABLE_CAT FROM pg_database";
73
+ const result = Syncit.fxJdbcQuery(this._connectionId, sql);
74
+ return newFakeJdbcResultSet(result);
75
+ }
76
+
77
+ getSchemas() {
78
+ const sql =
79
+ "SELECT SCHEMA_NAME as TABLE_SCHEM, CATALOG_NAME as TABLE_CATALOG FROM information_schema.SCHEMATA";
80
+ const result = Syncit.fxJdbcQuery(this._connectionId, sql);
81
+ return newFakeJdbcResultSet(result);
82
+ }
83
+
84
+ getTableTypes() {
85
+ return newFakeJdbcResultSet({
86
+ fields: [{ name: "TABLE_TYPE" }],
87
+ rows: [
88
+ { TABLE_TYPE: "TABLE" },
89
+ { TABLE_TYPE: "VIEW" },
90
+ { TABLE_TYPE: "SYSTEM TABLE" },
91
+ ],
92
+ });
93
+ }
94
+
95
+ getTables(catalog, schemaPattern, tableNamePattern, types) {
96
+ let sql = `SELECT
97
+ TABLE_CATALOG as TABLE_CAT,
98
+ TABLE_SCHEMA as TABLE_SCHEM,
99
+ TABLE_NAME,
100
+ CASE WHEN TABLE_TYPE = 'BASE TABLE' THEN 'TABLE' ELSE TABLE_TYPE END as TABLE_TYPE,
101
+ NULL as REMARKS,
102
+ NULL as TYPE_CAT,
103
+ NULL as TYPE_SCHEM,
104
+ NULL as TYPE_NAME,
105
+ NULL as SELF_REFERENCING_COL_NAME,
106
+ NULL as REF_GENERATION
107
+ FROM information_schema.tables WHERE 1=1`;
108
+
109
+ if (catalog) sql += ` AND TABLE_CATALOG = '${catalog}'`;
110
+ if (schemaPattern) sql += ` AND TABLE_SCHEMA LIKE '${schemaPattern}'`;
111
+ if (tableNamePattern) {
112
+ sql += ` AND LOWER(TABLE_NAME) LIKE LOWER('${tableNamePattern}')`;
113
+ }
114
+ if (types && types.length > 0) {
115
+ const typeList = types
116
+ .map((t) => {
117
+ if (t === "TABLE") return "'TABLE','BASE TABLE'";
118
+ return `'${t}'`;
119
+ })
120
+ .join(",");
121
+ sql += ` AND TABLE_TYPE IN (${typeList})`;
122
+ }
123
+
124
+ const result = Syncit.fxJdbcQuery(this._connectionId, sql);
125
+ return newFakeJdbcResultSet(result);
126
+ }
127
+
128
+ getColumns(catalog, schemaPattern, tableNamePattern, columnNamePattern) {
129
+ const isMysql = this._type === "mysql";
130
+ let sql = `SELECT
131
+ TABLE_CATALOG as TABLE_CAT,
132
+ TABLE_SCHEMA as TABLE_SCHEM,
133
+ TABLE_NAME,
134
+ COLUMN_NAME,
135
+ DATA_TYPE as DATA_TYPE_NAME,
136
+ CASE
137
+ WHEN DATA_TYPE LIKE '%int%' THEN 4
138
+ WHEN DATA_TYPE LIKE '%text%' THEN 12
139
+ WHEN DATA_TYPE LIKE '%varchar%' THEN 12
140
+ WHEN DATA_TYPE LIKE '%float%' THEN 7
141
+ WHEN DATA_TYPE LIKE '%double%' THEN 8
142
+ ELSE 1111
143
+ END as DATA_TYPE,
144
+ CHARACTER_MAXIMUM_LENGTH as COLUMN_SIZE,
145
+ NULL as BUFFER_LENGTH,
146
+ NUMERIC_PRECISION as DECIMAL_DIGITS,
147
+ 10 as NUM_PREC_RADIX,
148
+ CASE WHEN IS_NULLABLE = 'YES' THEN 1 ELSE 0 END as NULLABLE,
149
+ NULL as REMARKS,
150
+ COLUMN_DEFAULT as COLUMN_DEF,
151
+ NULL as SQL_DATA_TYPE,
152
+ NULL as SQL_DATETIME_SUB,
153
+ CHARACTER_OCTET_LENGTH as CHAR_OCTET_LENGTH,
154
+ ORDINAL_POSITION,
155
+ IS_NULLABLE,
156
+ NULL as SCOPE_CATALOG,
157
+ NULL as SCOPE_SCHEMA,
158
+ NULL as SCOPE_TABLE,
159
+ NULL as SOURCE_DATA_TYPE,
160
+ ${isMysql ? "CASE WHEN EXTRA LIKE '%auto_increment%' THEN 'YES' ELSE 'NO' END" : "'NO'"} as IS_AUTOINCREMENT
161
+ FROM information_schema.columns WHERE 1=1`;
162
+
163
+ if (catalog) sql += ` AND TABLE_CATALOG = '${catalog}'`;
164
+ if (schemaPattern) sql += ` AND TABLE_SCHEMA LIKE '${schemaPattern}'`;
165
+ if (tableNamePattern) {
166
+ sql += ` AND LOWER(TABLE_NAME) LIKE LOWER('${tableNamePattern}')`;
167
+ }
168
+ if (columnNamePattern) {
169
+ sql += ` AND LOWER(COLUMN_NAME) LIKE LOWER('${columnNamePattern}')`;
170
+ }
171
+
172
+ sql += " ORDER BY TABLE_NAME, ORDINAL_POSITION";
173
+
174
+ const result = Syncit.fxJdbcQuery(this._connectionId, sql);
175
+ return newFakeJdbcResultSet(result);
176
+ }
177
+
178
+ getPrimaryKeys(catalog, schema, table) {
179
+ let sql;
180
+ if (this._type === "mysql") {
181
+ sql = `SELECT
182
+ TABLE_CATALOG as TABLE_CAT,
183
+ TABLE_SCHEMA as TABLE_SCHEM,
184
+ TABLE_NAME,
185
+ COLUMN_NAME,
186
+ ORDINAL_POSITION as KEY_SEQ,
187
+ CONSTRAINT_NAME as PK_NAME
188
+ FROM information_schema.KEY_COLUMN_USAGE
189
+ WHERE CONSTRAINT_NAME = 'PRIMARY' AND LOWER(TABLE_NAME) = LOWER('${table}')`;
190
+ if (catalog) sql += ` AND TABLE_CATALOG = '${catalog}'`;
191
+ if (schema) sql += ` AND TABLE_SCHEMA = '${schema}'`;
192
+ } else {
193
+ // PostgreSQL Primary Keys via information_schema
194
+ sql = `SELECT
195
+ tc.table_catalog as TABLE_CAT,
196
+ tc.table_schema as TABLE_SCHEM,
197
+ tc.table_name as TABLE_NAME,
198
+ kcu.column_name as COLUMN_NAME,
199
+ kcu.ordinal_position as KEY_SEQ,
200
+ tc.constraint_name as PK_NAME
201
+ FROM information_schema.table_constraints tc
202
+ JOIN information_schema.key_column_usage kcu
203
+ ON tc.constraint_name = kcu.constraint_name
204
+ AND tc.table_schema = kcu.table_schema
205
+ AND tc.table_name = kcu.table_name
206
+ WHERE tc.constraint_type = 'PRIMARY KEY' AND LOWER(tc.table_name) = LOWER('${table}')`;
207
+ if (catalog) sql += ` AND tc.table_catalog = '${catalog}'`;
208
+ if (schema) sql += ` AND tc.table_schema = '${schema}'`;
209
+ }
210
+
211
+ const result = Syncit.fxJdbcQuery(this._connectionId, sql);
212
+ return newFakeJdbcResultSet(result);
213
+ }
214
+
215
+ getTypeInfo() {
216
+ // Standard type info return
217
+ return newFakeJdbcResultSet({
218
+ fields: [
219
+ { name: "TYPE_NAME" },
220
+ { name: "DATA_TYPE" },
221
+ { name: "PRECISION" },
222
+ { name: "LITERAL_PREFIX" },
223
+ { name: "LITERAL_SUFFIX" },
224
+ { name: "CREATE_PARAMS" },
225
+ { name: "NULLABLE" },
226
+ { name: "CASE_SENSITIVE" },
227
+ { name: "SEARCHABLE" },
228
+ { name: "UNSIGNED_ATTRIBUTE" },
229
+ { name: "FIXED_PREC_SCALE" },
230
+ { name: "AUTO_INCREMENT" },
231
+ { name: "LOCAL_TYPE_NAME" },
232
+ { name: "MINIMUM_SCALE" },
233
+ { name: "MAXIMUM_SCALE" },
234
+ { name: "SQL_DATA_TYPE" },
235
+ { name: "SQL_DATETIME_SUB" },
236
+ { name: "NUM_PREC_RADIX" },
237
+ ],
238
+ rows: [
239
+ { TYPE_NAME: "TEXT", DATA_TYPE: 12, SEARCHABLE: 3, NULLABLE: 1 },
240
+ { TYPE_NAME: "INT", DATA_TYPE: 4, SEARCHABLE: 3, NULLABLE: 1 },
241
+ { TYPE_NAME: "FLOAT", DATA_TYPE: 7, SEARCHABLE: 3, NULLABLE: 1 },
242
+ ],
243
+ });
244
+ }
245
+
246
+ // ---------------------------------------------------------
247
+ // Capability Methods (Static/Defaults)
248
+ // ---------------------------------------------------------
249
+
250
+ supportsTransactions() {
251
+ return true;
252
+ }
253
+ supportsBatchUpdates() {
254
+ return true;
255
+ }
256
+ supportsSavepoints() {
257
+ return this._type === "postgres";
258
+ }
259
+ supportsStoredProcedures() {
260
+ return true;
261
+ }
262
+ supportsResultSetType(type) {
263
+ return type === 1003;
264
+ } // TYPE_FORWARD_ONLY
265
+ supportsResultSetConcurrency(type, concurrency) {
266
+ return concurrency === 1007;
267
+ } // CONCUR_READ_ONLY
268
+
269
+ // High-level common capabilities
270
+ allProceduresAreCallable() {
271
+ return true;
272
+ }
273
+ allTablesAreSelectable() {
274
+ return true;
275
+ }
276
+ dataDefinitionCausesTransactionCommit() {
277
+ return this._type === "mysql";
278
+ }
279
+ dataDefinitionIgnoredInTransactions() {
280
+ return false;
281
+ }
282
+ doesMaxRowSizeIncludeBlobs() {
283
+ return true;
284
+ }
285
+ supportsCorrelatedSubqueries() {
286
+ return true;
287
+ }
288
+ supportsDataDefinitionAndDataManipulationTransactions() {
289
+ return true;
290
+ }
291
+ supportsDataManipulationTransactionsOnly() {
292
+ return false;
293
+ }
294
+ supportsDifferentTableCorrelationNames() {
295
+ return false;
296
+ }
297
+ supportsExpressionsInOrderBy() {
298
+ return true;
299
+ }
300
+ supportsExtendedSQLGrammar() {
301
+ return false;
302
+ }
303
+ supportsFullOuterJoins() {
304
+ return this._type === "postgres";
305
+ }
306
+ supportsGroupBy() {
307
+ return true;
308
+ }
309
+ supportsGroupByBeyondSelect() {
310
+ return true;
311
+ }
312
+ supportsGroupByUnrelated() {
313
+ return true;
314
+ }
315
+ supportsIntegrityEnhancementFacility() {
316
+ return false;
317
+ }
318
+ supportsLikeEscapeClause() {
319
+ return true;
320
+ }
321
+ supportsLimitedOuterJoins() {
322
+ return true;
323
+ }
324
+ supportsMinimumSQLGrammar() {
325
+ return true;
326
+ }
327
+ supportsMixedCaseIdentifiers() {
328
+ return false;
329
+ }
330
+ supportsMixedCaseQuotedIdentifiers() {
331
+ return true;
332
+ }
333
+ supportsMultipleResultSets() {
334
+ return false;
335
+ }
336
+ supportsMultipleTransactions() {
337
+ return true;
338
+ }
339
+ supportsNamedParameters() {
340
+ return false;
341
+ }
342
+ supportsNonNullableColumns() {
343
+ return true;
344
+ }
345
+ supportsOpenCursorsAcrossCommit() {
346
+ return false;
347
+ }
348
+ supportsOpenCursorsAcrossRollback() {
349
+ return false;
350
+ }
351
+ supportsOpenStatementsAcrossCommit() {
352
+ return false;
353
+ }
354
+ supportsOpenStatementsAcrossRollback() {
355
+ return false;
356
+ }
357
+ supportsOrderByUnrelated() {
358
+ return true;
359
+ }
360
+ supportsOuterJoins() {
361
+ return true;
362
+ }
363
+ supportsPositionedDelete() {
364
+ return false;
365
+ }
366
+ supportsPositionedUpdate() {
367
+ return false;
368
+ }
369
+ supportsSchemasInDataManipulation() {
370
+ return true;
371
+ }
372
+ supportsSchemasInIndexDefinitions() {
373
+ return true;
374
+ }
375
+ supportsSchemasInPrivilegeDefinitions() {
376
+ return true;
377
+ }
378
+ supportsSchemasInProcedureCalls() {
379
+ return true;
380
+ }
381
+ supportsSchemasInTableDefinitions() {
382
+ return true;
383
+ }
384
+ supportsSelectForUpdate() {
385
+ return true;
386
+ }
387
+ supportsStatementPooling() {
388
+ return false;
389
+ }
390
+ supportsSubqueriesInComparisons() {
391
+ return true;
392
+ }
393
+ supportsSubqueriesInExists() {
394
+ return true;
395
+ }
396
+ supportsSubqueriesInIns() {
397
+ return true;
398
+ }
399
+ supportsSubqueriesInQuantifieds() {
400
+ return true;
401
+ }
402
+ supportsTableCorrelationNames() {
403
+ return true;
404
+ }
405
+ supportsUnion() {
406
+ return true;
407
+ }
408
+ supportsUnionAll() {
409
+ return true;
410
+ }
411
+ usesLocalFilePerTable() {
412
+ return false;
413
+ }
414
+ usesLocalFiles() {
415
+ return false;
416
+ }
417
+
418
+ // Default numeric constraints
419
+ getMaxBinaryLiteralLength() {
420
+ return 0;
421
+ }
422
+ getMaxCatalogNameLength() {
423
+ return 0;
424
+ }
425
+ getMaxCharLiteralLength() {
426
+ return 0;
427
+ }
428
+ getMaxColumnNameLength() {
429
+ return 0;
430
+ }
431
+ getMaxColumnsInGroupBy() {
432
+ return 0;
433
+ }
434
+ getMaxColumnsInIndex() {
435
+ return 0;
436
+ }
437
+ getMaxColumnsInOrderBy() {
438
+ return 0;
439
+ }
440
+ getMaxColumnsInSelect() {
441
+ return 0;
442
+ }
443
+ getMaxColumnsInTable() {
444
+ return 0;
445
+ }
446
+ getMaxConnections() {
447
+ return 0;
448
+ }
449
+ getMaxCursorNameLength() {
450
+ return 0;
451
+ }
452
+ getMaxIndexLength() {
453
+ return 0;
454
+ }
455
+ getMaxProcedureNameLength() {
456
+ return 0;
457
+ }
458
+ getMaxRowSize() {
459
+ return 0;
460
+ }
461
+ getMaxSchemaNameLength() {
462
+ return 0;
463
+ }
464
+ getMaxStatementLength() {
465
+ return 0;
466
+ }
467
+ getMaxStatements() {
468
+ return 0;
469
+ }
470
+ getMaxTableNameLength() {
471
+ return 0;
472
+ }
473
+ getMaxTablesInSelect() {
474
+ return 0;
475
+ }
476
+ getMaxUserNameLength() {
477
+ return 0;
478
+ }
479
+
480
+ getDefaultTransactionIsolation() {
481
+ return 2;
482
+ } // TRANSACTION_READ_COMMITTED
483
+ getResultSetHoldability() {
484
+ return 1;
485
+ } // HOLD_CURSORS_OVER_COMMIT
486
+ getSQLStateType() {
487
+ return 2;
488
+ } // sqlStateSQL
489
+ }
490
+
491
+ export const newFakeJdbcDatabaseMetaData = (...args) =>
492
+ Proxies.guard(new FakeJdbcDatabaseMetaData(...args));
@@ -0,0 +1,130 @@
1
+ import { Proxies } from '../../support/proxies.js';
2
+ import { Syncit } from '../../support/syncit.js';
3
+ import { newFakeJdbcResultSet } from './fakejdbcresultset.js';
4
+
5
+ class FakeJdbcPreparedStatement {
6
+ constructor(connection, connectionId, sql) {
7
+ this.__fakeObjectType = 'JdbcPreparedStatement';
8
+ this._connection = connection;
9
+ this._connectionId = connectionId;
10
+ this._sql = sql;
11
+ this._params = [];
12
+ this._batch = [];
13
+ this._isClosed = false;
14
+ this._fetchSize = 0;
15
+ this._maxRows = 0;
16
+ this._queryTimeout = 0;
17
+ this._maxFieldSize = 0;
18
+ this._fetchDirection = 1000; // FETCH_FORWARD
19
+ this._isPoolable = false;
20
+ this._lastUpdateCount = -1;
21
+ }
22
+
23
+ cancel() { }
24
+ clearWarnings() { }
25
+ getConnection() { return this._connection; }
26
+ getFetchDirection() { return this._fetchDirection; }
27
+ getFetchSize() { return this._fetchSize; }
28
+ getGeneratedKeys() { return newFakeJdbcResultSet({ fields: [], rows: [] }, this); }
29
+ getMaxFieldSize() { return this._maxFieldSize; }
30
+ getMaxRows() { return this._maxRows; }
31
+ getMoreResults(current) { return false; }
32
+ getQueryTimeout() { return this._queryTimeout; }
33
+ getResultSetConcurrency() { return 1007; }
34
+ getResultSetHoldability() { return 1; }
35
+ getResultSetType() { return 1003; }
36
+ getWarnings() { return null; }
37
+ isPoolable() { return this._isPoolable; }
38
+ setCursorName(name) { }
39
+ setEscapeProcessing(enable) { }
40
+ setFetchDirection(direction) { this._fetchDirection = direction; }
41
+ setFetchSize(rows) { this._fetchSize = rows; }
42
+ setMaxFieldSize(max) { this._maxFieldSize = max; }
43
+ setMaxRows(max) { this._maxRows = max; }
44
+ setPoolable(poolable) { this._isPoolable = poolable; }
45
+ setQueryTimeout(seconds) { this._queryTimeout = seconds; }
46
+
47
+ getMetaData() {
48
+ return this._lastResultSet ? this._lastResultSet.getMetaData() : null;
49
+ }
50
+
51
+ addBatch() {
52
+ if (this._isClosed) throw new Error('Statement is closed.');
53
+ this._batch.push([...this._params]);
54
+ }
55
+
56
+ executeBatch() {
57
+ if (this._isClosed) throw new Error('Statement is closed.');
58
+ const results = [];
59
+ for (const params of this._batch) {
60
+ const result = Syncit.fxJdbcExecutePrepared(this._connectionId, this._sql, params);
61
+ results.push(result.rowCount || 0);
62
+ }
63
+ this.clearBatch();
64
+ return results;
65
+ }
66
+
67
+ clearBatch() {
68
+ this._batch = [];
69
+ }
70
+
71
+ executeQuery() {
72
+ if (this._isClosed) throw new Error('Statement is closed.');
73
+ const result = Syncit.fxJdbcExecutePrepared(this._connectionId, this._sql, this._params);
74
+ this._lastUpdateCount = -1;
75
+ this._lastResultSet = newFakeJdbcResultSet(result, this);
76
+ return this._lastResultSet;
77
+ }
78
+
79
+ executeUpdate() {
80
+ if (this._isClosed) throw new Error('Statement is closed.');
81
+ const result = Syncit.fxJdbcExecutePrepared(this._connectionId, this._sql, this._params);
82
+ this._lastUpdateCount = result.rowCount;
83
+ this._lastResultSet = null;
84
+ return result.rowCount;
85
+ }
86
+
87
+ execute() {
88
+ if (this._isClosed) throw new Error('Statement is closed.');
89
+ const result = Syncit.fxJdbcExecutePrepared(this._connectionId, this._sql, this._params);
90
+ this._lastUpdateCount = result.rowCount;
91
+ if (result.fields && result.fields.length > 0) {
92
+ this._lastResultSet = newFakeJdbcResultSet(result, this);
93
+ return true;
94
+ } else {
95
+ this._lastResultSet = null;
96
+ return false;
97
+ }
98
+ }
99
+
100
+ setBoolean(index, value) { this._params[index - 1] = Boolean(value); }
101
+ setDouble(index, value) { this._params[index - 1] = Number(value); }
102
+ setFloat(index, value) { this._params[index - 1] = Number(value); }
103
+ setInt(index, value) { this._params[index - 1] = parseInt(value, 10); }
104
+ setLong(index, value) { this._params[index - 1] = parseInt(value, 10); }
105
+ setString(index, value) { this._params[index - 1] = String(value); }
106
+ setBigDecimal(index, value) {
107
+ this._params[index - 1] = value !== null && value !== undefined ? String(value) : null;
108
+ }
109
+ setObject(index, value) { this._params[index - 1] = value; }
110
+ setNull(index, type) { this._params[index - 1] = null; }
111
+ clearParameters() { this._params = []; }
112
+ setTimestamp(index, value) {
113
+ // GAS uses JdbcTimestamp, but we'll use standard Date/String for fake
114
+ this._params[index - 1] = value instanceof Date ? value.toISOString() : String(value);
115
+ }
116
+
117
+ getUpdateCount() { return this._lastUpdateCount || 0; }
118
+ getResultSet() { return this._lastResultSet; }
119
+
120
+ close() {
121
+ this._isClosed = true;
122
+ this._lastResultSet = null;
123
+ }
124
+
125
+ isClosed() {
126
+ return this._isClosed;
127
+ }
128
+ }
129
+
130
+ export const newFakeJdbcPreparedStatement = (...args) => Proxies.guard(new FakeJdbcPreparedStatement(...args));