@mcpher/gas-fakes 2.3.5 → 2.3.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/appsscript.json +3 -3
- package/main.js +3 -5
- package/package.json +1 -1
- package/src/cli/setup.js +4 -5
- package/src/cli/utils.js +24 -4
- package/src/services/jdbc/fakejdbcarray.js +37 -0
- package/src/services/jdbc/fakejdbcbigdecimal.js +63 -0
- package/src/services/jdbc/fakejdbcblob.js +67 -0
- package/src/services/jdbc/fakejdbcclob.js +67 -0
- package/src/services/jdbc/fakejdbcconnection.js +28 -7
- package/src/services/jdbc/fakejdbcdatabasemetadata.js +492 -0
- package/src/services/jdbc/fakejdbcpreparedstatement.js +130 -0
- package/src/services/jdbc/fakejdbcresultset.js +227 -5
- package/src/services/jdbc/fakejdbcresultsetmetadata.js +8 -0
- package/src/services/jdbc/fakejdbcservice.js +39 -29
- package/src/services/jdbc/fakejdbcstatement.js +153 -7
- package/src/support/fakeinputstream.js +42 -0
- package/src/support/metadata.js +16 -0
- package/src/support/sxjdbc.js +74 -6
- package/src/support/syncit.js +30 -2
|
@@ -1,13 +1,24 @@
|
|
|
1
1
|
import { Proxies } from '../../support/proxies.js';
|
|
2
2
|
import { newFakeJdbcResultSetMetaData } from './fakejdbcresultsetmetadata.js';
|
|
3
|
+
import { FakeJdbcBigDecimal } from './fakejdbcbigdecimal.js';
|
|
4
|
+
import { newFakeJdbcBlob } from './fakejdbcblob.js';
|
|
5
|
+
import { newFakeJdbcClob } from './fakejdbcclob.js';
|
|
6
|
+
import { newFakeJdbcArray } from './fakejdbcarray.js';
|
|
7
|
+
import { newFakeInputStream, newFakeReader } from '../../support/fakeinputstream.js';
|
|
3
8
|
|
|
4
9
|
class FakeJdbcResultSet {
|
|
5
|
-
constructor(result) {
|
|
10
|
+
constructor(result, statement) {
|
|
6
11
|
this.__fakeObjectType = 'JdbcResultSet';
|
|
7
12
|
this._rows = result.rows || [];
|
|
8
13
|
this._fields = result.fields || [];
|
|
9
14
|
this._currentIndex = -1;
|
|
10
15
|
this._isClosed = false;
|
|
16
|
+
this._statement = statement;
|
|
17
|
+
this._lastValue = null;
|
|
18
|
+
this._fetchSize = 0;
|
|
19
|
+
this._fetchDirection = 1000; // FETCH_FORWARD
|
|
20
|
+
this._type = 1003; // TYPE_FORWARD_ONLY
|
|
21
|
+
this._concurrency = 1007; // CONCUR_READ_ONLY
|
|
11
22
|
}
|
|
12
23
|
|
|
13
24
|
next() {
|
|
@@ -16,15 +27,96 @@ class FakeJdbcResultSet {
|
|
|
16
27
|
return this._currentIndex < this._rows.length;
|
|
17
28
|
}
|
|
18
29
|
|
|
19
|
-
_getValue(
|
|
30
|
+
_getValue(columnIdentifier) {
|
|
20
31
|
if (this._isClosed) throw new Error('ResultSet is closed.');
|
|
21
32
|
if (this._currentIndex < 0 || this._currentIndex >= this._rows.length) {
|
|
22
33
|
throw new Error('No current row.');
|
|
23
34
|
}
|
|
35
|
+
|
|
36
|
+
let index;
|
|
37
|
+
if (typeof columnIdentifier === 'number') {
|
|
38
|
+
index = columnIdentifier;
|
|
39
|
+
} else {
|
|
40
|
+
index = this.findColumn(columnIdentifier);
|
|
41
|
+
}
|
|
42
|
+
|
|
24
43
|
// JDBC columns are 1-indexed
|
|
25
|
-
const field = this._fields[
|
|
26
|
-
if (!field) throw new Error(`Invalid column
|
|
27
|
-
|
|
44
|
+
const field = this._fields[index - 1];
|
|
45
|
+
if (!field) throw new Error(`Invalid column identifier: ${columnIdentifier}`);
|
|
46
|
+
const val = this._rows[this._currentIndex][field.name];
|
|
47
|
+
this._lastValue = val;
|
|
48
|
+
return val;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
wasNull() {
|
|
52
|
+
return this._lastValue === null || this._lastValue === undefined;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
absolute(row) {
|
|
56
|
+
if (this._isClosed) throw new Error('ResultSet is closed.');
|
|
57
|
+
if (row === 0) throw new Error('Rows are 1-indexed.');
|
|
58
|
+
if (row > 0) {
|
|
59
|
+
this._currentIndex = row - 1;
|
|
60
|
+
} else {
|
|
61
|
+
this._currentIndex = this._rows.length + row;
|
|
62
|
+
}
|
|
63
|
+
return this._currentIndex >= 0 && this._currentIndex < this._rows.length;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
afterLast() {
|
|
67
|
+
if (this._isClosed) throw new Error('ResultSet is closed.');
|
|
68
|
+
this._currentIndex = this._rows.length;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
beforeFirst() {
|
|
72
|
+
if (this._isClosed) throw new Error('ResultSet is closed.');
|
|
73
|
+
this._currentIndex = -1;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
first() {
|
|
77
|
+
return this.absolute(1);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
last() {
|
|
81
|
+
return this.absolute(-1);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
previous() {
|
|
85
|
+
if (this._isClosed) throw new Error('ResultSet is closed.');
|
|
86
|
+
if (this._currentIndex > -1) {
|
|
87
|
+
this._currentIndex--;
|
|
88
|
+
}
|
|
89
|
+
return this._currentIndex >= 0 && this._currentIndex < this._rows.length;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
relative(rows) {
|
|
93
|
+
return this.absolute(this._currentIndex + 1 + rows);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
isAfterLast() {
|
|
97
|
+
if (this._isClosed) throw new Error('ResultSet is closed.');
|
|
98
|
+
return this._currentIndex >= this._rows.length && this._rows.length > 0;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
isBeforeFirst() {
|
|
102
|
+
if (this._isClosed) throw new Error('ResultSet is closed.');
|
|
103
|
+
return this._currentIndex < 0 && this._rows.length > 0;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
isFirst() {
|
|
107
|
+
if (this._isClosed) throw new Error('ResultSet is closed.');
|
|
108
|
+
return this._currentIndex === 0 && this._rows.length > 0;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
isLast() {
|
|
112
|
+
if (this._isClosed) throw new Error('ResultSet is closed.');
|
|
113
|
+
return this._currentIndex === this._rows.length - 1 && this._rows.length > 0;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
getRow() {
|
|
117
|
+
if (this._isClosed) throw new Error('ResultSet is closed.');
|
|
118
|
+
if (this._currentIndex < 0 || this._currentIndex >= this._rows.length) return 0;
|
|
119
|
+
return this._currentIndex + 1;
|
|
28
120
|
}
|
|
29
121
|
|
|
30
122
|
getString(columnIndex) {
|
|
@@ -37,20 +129,102 @@ class FakeJdbcResultSet {
|
|
|
37
129
|
return val !== null && val !== undefined ? parseInt(val, 10) : 0;
|
|
38
130
|
}
|
|
39
131
|
|
|
132
|
+
getLong(columnIndex) {
|
|
133
|
+
return this.getInt(columnIndex);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
getShort(columnIndex) {
|
|
137
|
+
return this.getInt(columnIndex);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
getBoolean(columnIndex) {
|
|
141
|
+
const val = this._getValue(columnIndex);
|
|
142
|
+
return Boolean(val);
|
|
143
|
+
}
|
|
144
|
+
|
|
40
145
|
getFloat(columnIndex) {
|
|
146
|
+
const val = this._getValue(columnIndex);
|
|
147
|
+
// GAS getFloat() returns IEEE 754 single-precision (32-bit float)
|
|
148
|
+
return val !== null && val !== undefined ? Math.fround(parseFloat(val)) : 0.0;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
getDouble(columnIndex) {
|
|
152
|
+
// getDouble() returns full 64-bit precision
|
|
41
153
|
const val = this._getValue(columnIndex);
|
|
42
154
|
return val !== null && val !== undefined ? parseFloat(val) : 0.0;
|
|
43
155
|
}
|
|
156
|
+
|
|
157
|
+
getBigDecimal(columnIndex) {
|
|
158
|
+
const val = this._getValue(columnIndex);
|
|
159
|
+
return val !== null && val !== undefined ? new FakeJdbcBigDecimal(val) : null;
|
|
160
|
+
}
|
|
44
161
|
|
|
45
162
|
getDate(columnIndex) {
|
|
46
163
|
const val = this._getValue(columnIndex);
|
|
47
164
|
return val ? new Date(val) : null;
|
|
48
165
|
}
|
|
49
166
|
|
|
167
|
+
getTimestamp(columnIndex) {
|
|
168
|
+
return this.getDate(columnIndex);
|
|
169
|
+
}
|
|
170
|
+
|
|
50
171
|
getObject(columnIndex) {
|
|
51
172
|
return this._getValue(columnIndex);
|
|
52
173
|
}
|
|
53
174
|
|
|
175
|
+
getBytes(columnIndex) {
|
|
176
|
+
const val = this._getValue(columnIndex);
|
|
177
|
+
if (val === null || val === undefined) return null;
|
|
178
|
+
return Array.from(Buffer.from(val));
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
getBlob(columnIndex) {
|
|
182
|
+
const val = this._getValue(columnIndex);
|
|
183
|
+
return val !== null && val !== undefined ? newFakeJdbcBlob(val) : null;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
getClob(columnIndex) {
|
|
187
|
+
const val = this._getValue(columnIndex);
|
|
188
|
+
return val !== null && val !== undefined ? newFakeJdbcClob(val) : null;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
getArray(columnIndex) {
|
|
192
|
+
const val = this._getValue(columnIndex);
|
|
193
|
+
return val !== null && val !== undefined ? newFakeJdbcArray(val) : null;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
getUnicodeStream(columnIndex) {
|
|
197
|
+
const val = this._getValue(columnIndex);
|
|
198
|
+
return val !== null && val !== undefined ? newFakeInputStream(Buffer.from(String(val), 'utf8')) : null;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
getAsciiStream(columnIndex) {
|
|
202
|
+
const val = this._getValue(columnIndex);
|
|
203
|
+
return val !== null && val !== undefined ? newFakeInputStream(Buffer.from(String(val), 'ascii')) : null;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
getBinaryStream(columnIndex) {
|
|
207
|
+
const val = this._getValue(columnIndex);
|
|
208
|
+
return val !== null && val !== undefined ? newFakeInputStream(Buffer.from(val)) : null;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
getCharacterStream(columnIndex) {
|
|
212
|
+
const val = this._getValue(columnIndex);
|
|
213
|
+
return val !== null && val !== undefined ? newFakeReader(String(val)) : null;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
getCursorName() {
|
|
217
|
+
if (this._isClosed) throw new Error('ResultSet is closed.');
|
|
218
|
+
return 'fake-cursor';
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
findColumn(columnLabel) {
|
|
222
|
+
if (this._isClosed) throw new Error('ResultSet is closed.');
|
|
223
|
+
const index = this._fields.findIndex(f => f.name.toLowerCase() === columnLabel.toLowerCase());
|
|
224
|
+
if (index === -1) throw new Error(`Column not found: ${columnLabel}`);
|
|
225
|
+
return index + 1;
|
|
226
|
+
}
|
|
227
|
+
|
|
54
228
|
getMetaData() {
|
|
55
229
|
if (this._isClosed) throw new Error('ResultSet is closed.');
|
|
56
230
|
return newFakeJdbcResultSetMetaData(this._fields);
|
|
@@ -62,6 +236,54 @@ class FakeJdbcResultSet {
|
|
|
62
236
|
this._fields = [];
|
|
63
237
|
}
|
|
64
238
|
|
|
239
|
+
getByte(columnIndex) {
|
|
240
|
+
return this.getInt(columnIndex);
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
getTime(columnIndex) {
|
|
244
|
+
return this.getDate(columnIndex);
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
getURL(columnIndex) {
|
|
248
|
+
return this.getString(columnIndex);
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
getFetchDirection() {
|
|
252
|
+
return this._fetchDirection;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
getFetchSize() {
|
|
256
|
+
return this._fetchSize;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
getType() {
|
|
260
|
+
return this._type;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
getConcurrency() {
|
|
264
|
+
return this._concurrency;
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
setFetchDirection(direction) {
|
|
268
|
+
this._fetchDirection = direction;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
setFetchSize(rows) {
|
|
272
|
+
this._fetchSize = rows;
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
getStatement() {
|
|
276
|
+
return this._statement;
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
getWarnings() {
|
|
280
|
+
return null;
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
clearWarnings() {
|
|
284
|
+
// No-op for fake
|
|
285
|
+
}
|
|
286
|
+
|
|
65
287
|
isClosed() {
|
|
66
288
|
return this._isClosed;
|
|
67
289
|
}
|
|
@@ -34,6 +34,14 @@ class FakeJdbcResultSetMetaData {
|
|
|
34
34
|
if (!field) throw new Error(`Invalid column index: ${column}`);
|
|
35
35
|
return `TYPE_${field.dataTypeID}`; // basic fallback
|
|
36
36
|
}
|
|
37
|
+
|
|
38
|
+
getTableName(column) {
|
|
39
|
+
return "";
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
getSchemaName(column) {
|
|
43
|
+
return "";
|
|
44
|
+
}
|
|
37
45
|
}
|
|
38
46
|
|
|
39
47
|
export const newFakeJdbcResultSetMetaData = (...args) => Proxies.guard(new FakeJdbcResultSetMetaData(...args));
|
|
@@ -7,12 +7,12 @@ class FakeJdbcService {
|
|
|
7
7
|
this.__fakeObjectType = 'Jdbc';
|
|
8
8
|
}
|
|
9
9
|
|
|
10
|
-
|
|
10
|
+
/**
|
|
11
11
|
* Connects to a JDBC database.
|
|
12
12
|
* In gas-fakes, we primarily support postgres via pg module, mapping jdbc:postgresql to it.
|
|
13
13
|
*
|
|
14
14
|
* @param {string} url The URL of the database to connect to.
|
|
15
|
-
* @param {string} user (optional) The user name to connect as.
|
|
15
|
+
* @param {string|object} user (optional) The user name to connect as, or an object of property/value pairs.
|
|
16
16
|
* @param {string} password (optional) The password for the user.
|
|
17
17
|
* @returns {JdbcConnection} A JDBC connection object.
|
|
18
18
|
*/
|
|
@@ -24,39 +24,49 @@ class FakeJdbcService {
|
|
|
24
24
|
}
|
|
25
25
|
|
|
26
26
|
// Explicitly merge user/password into the URL structure for gas-fakes processing
|
|
27
|
-
if (user
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
// Restore the prefix
|
|
35
|
-
let prefix = "jdbc:";
|
|
36
|
-
if (finalUrl.startsWith("jdbc:google:")) prefix = "jdbc:google:";
|
|
37
|
-
finalUrl = prefix + urlObj.toString();
|
|
38
|
-
} catch (e) {
|
|
39
|
-
// Fallback for malformed URLs
|
|
27
|
+
if (typeof user === 'object' && user !== null) {
|
|
28
|
+
// Handling info object
|
|
29
|
+
const info = user;
|
|
30
|
+
const u = info.user || info.userName;
|
|
31
|
+
const p = info.password;
|
|
32
|
+
if (u && p) {
|
|
33
|
+
finalUrl = this._mergeCredentials(finalUrl, u, p);
|
|
40
34
|
}
|
|
35
|
+
// Note: other info properties aren't currently merged into the URL string,
|
|
36
|
+
// but Syncit.fxJdbcConnect will receive the info object anyway.
|
|
37
|
+
} else if (user !== undefined && user !== null && password !== undefined && password !== null) {
|
|
38
|
+
finalUrl = this._mergeCredentials(finalUrl, user, password);
|
|
41
39
|
}
|
|
42
40
|
|
|
43
|
-
//
|
|
44
|
-
return newFakeJdbcConnection(finalUrl,
|
|
41
|
+
// Pass user (which might be the info object) and password to the connection
|
|
42
|
+
return newFakeJdbcConnection(finalUrl, user, password);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
_mergeCredentials(url, user, password) {
|
|
46
|
+
try {
|
|
47
|
+
const cleanUrl = url.replace(/^jdbc:google:/, '').replace(/^jdbc:/, '');
|
|
48
|
+
const urlObj = new URL(cleanUrl);
|
|
49
|
+
urlObj.username = encodeURIComponent(String(user));
|
|
50
|
+
urlObj.password = encodeURIComponent(String(password));
|
|
51
|
+
|
|
52
|
+
// Restore the prefix
|
|
53
|
+
let prefix = "jdbc:";
|
|
54
|
+
if (url.startsWith("jdbc:google:")) prefix = "jdbc:google:";
|
|
55
|
+
return prefix + urlObj.toString();
|
|
56
|
+
} catch (e) {
|
|
57
|
+
return url; // Fallback for malformed URLs
|
|
58
|
+
}
|
|
45
59
|
}
|
|
46
60
|
|
|
47
61
|
/**
|
|
48
|
-
* Connects to a Google Cloud SQL instance
|
|
62
|
+
* Connects to a Google Cloud SQL instance.
|
|
49
63
|
* @param {string} url The URL of the database to connect to.
|
|
50
|
-
* @param {string} user (optional) The user name to connect as.
|
|
64
|
+
* @param {string|object} user (optional) The user name to connect as, or an object of property/value pairs.
|
|
51
65
|
* @param {string} password (optional) The password for the user.
|
|
52
66
|
* @returns {JdbcConnection} A JDBC connection object.
|
|
53
67
|
*/
|
|
54
68
|
getCloudSqlConnection(url, user, password) {
|
|
55
|
-
return this.getConnection
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
parseCsv(csv) {
|
|
59
|
-
throw new Error('Not implemented: parseCsv');
|
|
69
|
+
return this.getConnection(url, user, password);
|
|
60
70
|
}
|
|
61
71
|
|
|
62
72
|
__useProxy (val) {
|
|
@@ -90,7 +100,7 @@ class FakeJdbcService {
|
|
|
90
100
|
|
|
91
101
|
const cleanUrl = url.replace(/^jdbc:google:/, "").replace(/^jdbc:/, "");
|
|
92
102
|
|
|
93
|
-
let scheme, user, pass, host, db;
|
|
103
|
+
let scheme, user, pass, host, db, searchString = "";
|
|
94
104
|
|
|
95
105
|
try {
|
|
96
106
|
const parsed = new URL(cleanUrl);
|
|
@@ -110,7 +120,7 @@ class FakeJdbcService {
|
|
|
110
120
|
remainingParams.delete('user');
|
|
111
121
|
remainingParams.delete('password');
|
|
112
122
|
|
|
113
|
-
|
|
123
|
+
searchString = remainingParams.toString();
|
|
114
124
|
if (searchString) {
|
|
115
125
|
db = `${db}?${searchString}`;
|
|
116
126
|
}
|
|
@@ -181,7 +191,7 @@ class FakeJdbcService {
|
|
|
181
191
|
// Live Apps Script Java JDBC requires properly encoded URI components to prevent "Connection URL is malformed"
|
|
182
192
|
const gasAuthQuery = (encodedUser || encodedPass) ? `?user=${encodedUser}&password=${encodedPass}` : '';
|
|
183
193
|
|
|
184
|
-
// --- GAS ---
|
|
194
|
+
// --- GAS --- (Rule 3: Strip ALL SSL/Tunneling parameters)
|
|
185
195
|
let gasUrl, gasConnectionString, gasFullConnectionString;
|
|
186
196
|
let isCloudSqlConnection = false;
|
|
187
197
|
|
|
@@ -192,8 +202,8 @@ class FakeJdbcService {
|
|
|
192
202
|
isCloudSqlConnection = true;
|
|
193
203
|
} else {
|
|
194
204
|
gasUrl = `${protocol}${remoteHost}/${pureDb}`;
|
|
195
|
-
gasConnectionString =
|
|
196
|
-
//
|
|
205
|
+
gasConnectionString = gasUrl;
|
|
206
|
+
// Single-argument getConnection is discouraged on GAS, but if used, only append credentials
|
|
197
207
|
gasFullConnectionString = `${protocol}${remoteHost}/${pureDb}${gasAuthQuery}`;
|
|
198
208
|
}
|
|
199
209
|
|
|
@@ -3,10 +3,39 @@ import { Syncit } from '../../support/syncit.js';
|
|
|
3
3
|
import { newFakeJdbcResultSet } from './fakejdbcresultset.js';
|
|
4
4
|
|
|
5
5
|
class FakeJdbcStatement {
|
|
6
|
-
constructor(connectionId) {
|
|
6
|
+
constructor(connection, connectionId) {
|
|
7
7
|
this.__fakeObjectType = 'JdbcStatement';
|
|
8
|
+
this._connection = connection;
|
|
8
9
|
this._connectionId = connectionId;
|
|
9
10
|
this._isClosed = false;
|
|
11
|
+
this._batch = [];
|
|
12
|
+
this._fetchSize = 0;
|
|
13
|
+
this._maxRows = 0;
|
|
14
|
+
this._queryTimeout = 0;
|
|
15
|
+
this._maxFieldSize = 0;
|
|
16
|
+
this._fetchDirection = 1000; // FETCH_FORWARD
|
|
17
|
+
this._isPoolable = false;
|
|
18
|
+
this._lastUpdateCount = -1;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
addBatch(sql) {
|
|
22
|
+
if (this._isClosed) throw new Error('Statement is closed.');
|
|
23
|
+
this._batch.push(sql);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
executeBatch() {
|
|
27
|
+
if (this._isClosed) throw new Error('Statement is closed.');
|
|
28
|
+
const results = [];
|
|
29
|
+
for (const sql of this._batch) {
|
|
30
|
+
const result = Syncit.fxJdbcQuery(this._connectionId, sql);
|
|
31
|
+
results.push(result.rowCount || 0);
|
|
32
|
+
}
|
|
33
|
+
this.clearBatch();
|
|
34
|
+
return results;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
clearBatch() {
|
|
38
|
+
this._batch = [];
|
|
10
39
|
}
|
|
11
40
|
|
|
12
41
|
executeQuery(sql) {
|
|
@@ -14,21 +43,138 @@ class FakeJdbcStatement {
|
|
|
14
43
|
|
|
15
44
|
// Fetch result synchronously from worker
|
|
16
45
|
const result = Syncit.fxJdbcQuery(this._connectionId, sql);
|
|
17
|
-
|
|
18
|
-
|
|
46
|
+
this._lastUpdateCount = -1;
|
|
47
|
+
this._lastResultSet = newFakeJdbcResultSet(result, this);
|
|
48
|
+
return this._lastResultSet;
|
|
19
49
|
}
|
|
20
50
|
|
|
21
|
-
execute(sql) {
|
|
51
|
+
execute(sql, autoGeneratedKeysOrIndexesOrNames) {
|
|
22
52
|
if (this._isClosed) throw new Error('Statement is closed.');
|
|
23
53
|
|
|
24
|
-
// In actual JDBC, execute returns true if the first result is a ResultSet
|
|
25
|
-
// node-postgres gives us an array of rows or a rowCount.
|
|
26
54
|
const result = Syncit.fxJdbcQuery(this._connectionId, sql);
|
|
27
|
-
|
|
55
|
+
this._lastUpdateCount = result.rowCount || 0;
|
|
56
|
+
if (result.fields && result.fields.length > 0) {
|
|
57
|
+
this._lastResultSet = newFakeJdbcResultSet(result, this);
|
|
58
|
+
return true;
|
|
59
|
+
} else {
|
|
60
|
+
this._lastResultSet = null;
|
|
61
|
+
return false;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
executeUpdate(sql, autoGeneratedKeysOrIndexesOrNames) {
|
|
66
|
+
if (this._isClosed) throw new Error('Statement is closed.');
|
|
67
|
+
const result = Syncit.fxJdbcQuery(this._connectionId, sql);
|
|
68
|
+
this._lastUpdateCount = result.rowCount;
|
|
69
|
+
this._lastResultSet = null;
|
|
70
|
+
return result.rowCount;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
cancel() {
|
|
74
|
+
// Stub: No-op for fake
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
clearWarnings() {
|
|
78
|
+
// Stub: No-op for fake
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
getConnection() {
|
|
82
|
+
return this._connection;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
getFetchDirection() {
|
|
86
|
+
return this._fetchDirection;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
getFetchSize() {
|
|
90
|
+
return this._fetchSize;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
getGeneratedKeys() {
|
|
94
|
+
// Return empty results as we don't currently track generated auto-increment IDs
|
|
95
|
+
return newFakeJdbcResultSet({ fields: [], rows: [] }, this);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
getMaxFieldSize() {
|
|
99
|
+
return this._maxFieldSize;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
getMaxRows() {
|
|
103
|
+
return this._maxRows;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
getMoreResults(current) {
|
|
107
|
+
// We only support one result set at a time per statement in this fake
|
|
108
|
+
return false;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
getQueryTimeout() {
|
|
112
|
+
return this._queryTimeout;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
getResultSetConcurrency() {
|
|
116
|
+
return 1007; // CONCUR_READ_ONLY
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
getResultSetHoldability() {
|
|
120
|
+
return 1; // HOLD_CURSORS_OVER_COMMIT
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
getResultSetType() {
|
|
124
|
+
return 1003; // TYPE_FORWARD_ONLY
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
getWarnings() {
|
|
128
|
+
return null;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
isPoolable() {
|
|
132
|
+
return this._isPoolable;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
setCursorName(name) {
|
|
136
|
+
// Stub: No-op for fake
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
setEscapeProcessing(enable) {
|
|
140
|
+
// Stub: No-op for fake
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
setFetchDirection(direction) {
|
|
144
|
+
this._fetchDirection = direction;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
setFetchSize(rows) {
|
|
148
|
+
this._fetchSize = rows;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
setMaxFieldSize(max) {
|
|
152
|
+
this._maxFieldSize = max;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
setMaxRows(max) {
|
|
156
|
+
this._maxRows = max;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
setPoolable(poolable) {
|
|
160
|
+
this._isPoolable = poolable;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
setQueryTimeout(seconds) {
|
|
164
|
+
this._queryTimeout = seconds;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
getUpdateCount() {
|
|
168
|
+
return this._lastUpdateCount || 0;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
getResultSet() {
|
|
172
|
+
return this._lastResultSet;
|
|
28
173
|
}
|
|
29
174
|
|
|
30
175
|
close() {
|
|
31
176
|
this._isClosed = true;
|
|
177
|
+
this._lastResultSet = null;
|
|
32
178
|
}
|
|
33
179
|
|
|
34
180
|
isClosed() {
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { Proxies } from './proxies.js';
|
|
2
|
+
|
|
3
|
+
class FakeInputStream {
|
|
4
|
+
constructor(data) {
|
|
5
|
+
this.__fakeObjectType = 'InputStream';
|
|
6
|
+
this._data = data instanceof Uint8Array ? data : Buffer.from(data || '');
|
|
7
|
+
this._pos = 0;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
read() {
|
|
11
|
+
if (this._pos >= this._data.length) return -1;
|
|
12
|
+
return this._data[this._pos++];
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
available() {
|
|
16
|
+
return this._data.length - this._pos;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
close() {
|
|
20
|
+
this._pos = this._data.length;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
class FakeReader {
|
|
25
|
+
constructor(data) {
|
|
26
|
+
this.__fakeObjectType = 'Reader';
|
|
27
|
+
this._data = String(data || '');
|
|
28
|
+
this._pos = 0;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
read() {
|
|
32
|
+
if (this._pos >= this._data.length) return -1;
|
|
33
|
+
return this._data.charCodeAt(this._pos++);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
close() {
|
|
37
|
+
this._pos = this._data.length;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export const newFakeInputStream = (...args) => Proxies.guard(new FakeInputStream(...args));
|
|
42
|
+
export const newFakeReader = (...args) => Proxies.guard(new FakeReader(...args));
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { createRequire } from 'node:module';
|
|
2
|
+
const require = createRequire(import.meta.url);
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Initializes the global GasFakes metadata object.
|
|
6
|
+
* @param {string} packageJsonPath The relative path to package.json from this file.
|
|
7
|
+
* @returns {object} The GasFakes global object.
|
|
8
|
+
*/
|
|
9
|
+
export function initMetadata(packageJsonPath = '../../package.json') {
|
|
10
|
+
// add the version number to gasfakes metadata
|
|
11
|
+
if (!globalThis.GasFakes) globalThis.GasFakes = {};
|
|
12
|
+
const pjson = require(packageJsonPath);
|
|
13
|
+
if (!globalThis.GasFakes.metadata) globalThis.GasFakes.metadata = {}
|
|
14
|
+
globalThis.GasFakes.metadata.version = pjson.version
|
|
15
|
+
return globalThis.GasFakes;
|
|
16
|
+
}
|