@mcpher/gas-fakes 2.3.4 → 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.
- package/README.md +1 -0
- package/appsscript.json +1 -0
- package/main.js +3 -5
- package/package.json +4 -1
- package/skills-lock.json +10 -0
- package/src/cli/app.js +18 -0
- package/src/cli/setup.js +47 -4
- package/src/cli/utils.js +3 -4
- package/src/index.js +2 -0
- package/src/services/jdbc/app.js +9 -0
- 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 +66 -0
- package/src/services/jdbc/fakejdbcdatabasemetadata.js +492 -0
- package/src/services/jdbc/fakejdbcpreparedstatement.js +130 -0
- package/src/services/jdbc/fakejdbcresultset.js +292 -0
- package/src/services/jdbc/fakejdbcresultsetmetadata.js +47 -0
- package/src/services/jdbc/fakejdbcservice.js +255 -0
- package/src/services/jdbc/fakejdbcstatement.js +185 -0
- package/src/support/fakeinputstream.js +42 -0
- package/src/support/metadata.js +16 -0
- package/src/support/sxauth.js +26 -0
- package/src/support/sxjdbc.js +234 -0
- package/src/support/syncit.js +47 -1
- package/src/support/workersync/sxfunctions.js +1 -0
package/README.md
CHANGED
|
@@ -196,6 +196,7 @@ As I mentioned earlier, to take this further, I'm going to need a lot of help to
|
|
|
196
196
|
- [running gas-fakes on Amazon AWS lambda](https://github.com/brucemcpherson/gas-fakes-containers)
|
|
197
197
|
- [running gas-fakes on Azure ACA](https://github.com/brucemcpherson/gas-fakes-containers)
|
|
198
198
|
- [running gas-fakes on Github actions](https://github.com/brucemcpherson/gas-fakes-containers)
|
|
199
|
+
- [jdbc notes](jdbc-notes.md)
|
|
199
200
|
- [Yes – you can run native apps script code on Azure ACA as well!](https://ramblings.mcpher.com/yes-you-can-run-native-apps-script-code-on-azure-aca-as-well/)
|
|
200
201
|
- [Yes – you can run native apps script code on AWS Lambda!](https://ramblings.mcpher.com/apps-script-on-aws-lambda/)
|
|
201
202
|
- [initial idea and thoughts](https://ramblings.mcpher.com/a-proof-of-concept-implementation-of-apps-script-environment-on-node/)
|
package/appsscript.json
CHANGED
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
"oauthScopes": [
|
|
6
6
|
"https://www.googleapis.com/auth/cloud-platform",
|
|
7
7
|
"https://www.googleapis.com/auth/drive",
|
|
8
|
+
"https://www.googleapis.com/auth/sqlservice",
|
|
8
9
|
"https://www.googleapis.com/auth/script.external_request",
|
|
9
10
|
"https://www.googleapis.com/auth/spreadsheets",
|
|
10
11
|
"https://www.googleapis.com/auth/userinfo.email",
|
package/main.js
CHANGED
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
// testing locally
|
|
2
2
|
// sync the version with gas fakes code since they share a package.json
|
|
3
|
-
import {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
const VERSION = pjson.version;
|
|
7
|
-
console.log (`...gas-fakes version ${VERSION}`)
|
|
3
|
+
import { initMetadata } from './src/support/metadata.js';
|
|
4
|
+
initMetadata();
|
|
5
|
+
console.log (`...gas-fakes version ${globalThis.GasFakes.metadata.version}`)
|
|
8
6
|
import './src/index.js'
|
package/package.json
CHANGED
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
"@sindresorhus/is": "^7.2.0",
|
|
12
12
|
"acorn": "^8.16.0",
|
|
13
13
|
"adm-zip": "^0.5.16",
|
|
14
|
+
"child_process": "^1.0.2",
|
|
14
15
|
"commander": "^14.0.3",
|
|
15
16
|
"dotenv": "^17.3.1",
|
|
16
17
|
"fast-xml-parser": "^5.5.9",
|
|
@@ -22,6 +23,8 @@
|
|
|
22
23
|
"keyv": "^5.6.0",
|
|
23
24
|
"keyv-file": "^5.3.3",
|
|
24
25
|
"mime": "^4.1.0",
|
|
26
|
+
"mysql2": "^3.20.0",
|
|
27
|
+
"pg": "^8.20.0",
|
|
25
28
|
"prompts": "^2.4.2",
|
|
26
29
|
"sleep-synchronously": "^2.0.0",
|
|
27
30
|
"zod": "^4.3.6"
|
|
@@ -35,7 +38,7 @@
|
|
|
35
38
|
},
|
|
36
39
|
"name": "@mcpher/gas-fakes",
|
|
37
40
|
"author": "bruce mcpherson",
|
|
38
|
-
"version": "2.3.
|
|
41
|
+
"version": "2.3.6",
|
|
39
42
|
"license": "MIT",
|
|
40
43
|
"main": "main.js",
|
|
41
44
|
"description": "An implementation of the Google Workspace Apps Script runtime: Run native App Script Code on Node and Cloud Run",
|
package/skills-lock.json
ADDED
package/src/cli/app.js
CHANGED
|
@@ -175,6 +175,24 @@ export async function main() {
|
|
|
175
175
|
.option("-t, --tools <string>", "Path to custom tools file.")
|
|
176
176
|
.action(startMcpServer);
|
|
177
177
|
|
|
178
|
+
// --- JDBC Command ---
|
|
179
|
+
program
|
|
180
|
+
.command("jdbc")
|
|
181
|
+
.description("Parse a JDBC connection string and output configurations for App Script and Local environments.")
|
|
182
|
+
.requiredOption("-c, --connection-string <string>", "The JDBC connection string to parse.")
|
|
183
|
+
.action(async (options) => {
|
|
184
|
+
try {
|
|
185
|
+
const { newFakeJdbcService } = await import("../services/jdbc/fakejdbcservice.js");
|
|
186
|
+
const service = newFakeJdbcService();
|
|
187
|
+
const config = service.__normalConnection(options.connectionString);
|
|
188
|
+
process.stdout.write(JSON.stringify(config, null, 2) + "\n");
|
|
189
|
+
process.exit(0);
|
|
190
|
+
} catch (err) {
|
|
191
|
+
process.stderr.write(`Error parsing connection string: ${err.message}\n`);
|
|
192
|
+
process.exit(1);
|
|
193
|
+
}
|
|
194
|
+
});
|
|
195
|
+
|
|
178
196
|
program.showHelpAfterError("(add --help for additional information)");
|
|
179
197
|
|
|
180
198
|
await program.parseAsync(process.argv);
|
package/src/cli/setup.js
CHANGED
|
@@ -5,9 +5,8 @@ import path from "path";
|
|
|
5
5
|
import os from "os";
|
|
6
6
|
import { randomUUID } from "node:crypto";
|
|
7
7
|
import { execSync } from "child_process";
|
|
8
|
-
import { checkForGcloudCli,
|
|
8
|
+
import { checkForGcloudCli, runCommandSync } from "./utils.js";
|
|
9
9
|
import { getMsGraphToken, mapGasScopesToMsGraph } from "../support/msgraph/msauth.js";
|
|
10
|
-
import { Platforms, PlatformDefaults } from "../services/enums/platformenums.js";
|
|
11
10
|
|
|
12
11
|
// --- Utility Functions ---
|
|
13
12
|
|
|
@@ -242,16 +241,60 @@ export async function initializeConfiguration(options = {}) {
|
|
|
242
241
|
// Discover Scopes from appsscript.json (Shared across backends)
|
|
243
242
|
const manifestPath = path.resolve(process.cwd(), responses.GF_MANIFEST_PATH);
|
|
244
243
|
let manifestScopes = [];
|
|
244
|
+
let manifestHasCloudSql = false;
|
|
245
245
|
if (fs.existsSync(manifestPath)) {
|
|
246
246
|
try {
|
|
247
247
|
const manifest = JSON.parse(fs.readFileSync(manifestPath, "utf8"));
|
|
248
248
|
manifestScopes = manifest.oauthScopes || [];
|
|
249
249
|
console.log(`...discovered ${manifestScopes.length} scopes in ${responses.GF_MANIFEST_PATH}`);
|
|
250
|
+
|
|
251
|
+
// Check for JDBC or Cloud SQL usage (simplified check)
|
|
252
|
+
const manifestContent = fs.readFileSync(manifestPath, "utf8");
|
|
253
|
+
if (manifestContent.includes("jdbc:google:") || manifestContent.includes("Jdbc.")) {
|
|
254
|
+
manifestHasCloudSql = true;
|
|
255
|
+
}
|
|
250
256
|
} catch (err) {
|
|
251
257
|
console.warn(`...warning: failed to parse ${responses.GF_MANIFEST_PATH}.`);
|
|
252
258
|
}
|
|
253
259
|
}
|
|
254
260
|
|
|
261
|
+
// --- Step 2.5: Database & Cloud SQL Proxy Configuration ---
|
|
262
|
+
const hasJdbc = manifestHasCloudSql || Object.keys(existingConfig).some(k => k.includes("DATABASE_URL"));
|
|
263
|
+
if (hasJdbc) {
|
|
264
|
+
console.log("\n--- Configuring Database & Cloud SQL Auth Proxy ---");
|
|
265
|
+
|
|
266
|
+
// Check if proxy is installed
|
|
267
|
+
let proxyInstalled = false;
|
|
268
|
+
try {
|
|
269
|
+
execSync("cloud-sql-proxy --version", { stdio: "ignore" });
|
|
270
|
+
proxyInstalled = true;
|
|
271
|
+
} catch (e) {
|
|
272
|
+
// not installed or not in path
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
if (!proxyInstalled) {
|
|
276
|
+
console.log("\x1b[1;33mNotice: Cloud SQL Auth Proxy is not installed or not in your PATH.\x1b[0m");
|
|
277
|
+
console.log("If you plan to test Google Cloud SQL locally, please install it:");
|
|
278
|
+
console.log(" - macOS (Homebrew): brew install google-cloud-sdk");
|
|
279
|
+
console.log(" then: gcloud components install cloud-sql-proxy");
|
|
280
|
+
console.log(" - Other: https://cloud.google.com/sql/docs/postgres/sql-proxy#install\n");
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
const dbQuestions = [
|
|
284
|
+
{
|
|
285
|
+
type: "toggle",
|
|
286
|
+
name: "GF_USE_CLOUD_PG_SQL_PROXY",
|
|
287
|
+
message: "Use Cloud SQL Auth Proxy for local database connections?",
|
|
288
|
+
initial: existingConfig.GF_USE_CLOUD_PG_SQL_PROXY === "true",
|
|
289
|
+
active: "yes",
|
|
290
|
+
inactive: "no"
|
|
291
|
+
}
|
|
292
|
+
];
|
|
293
|
+
|
|
294
|
+
const dbResponses = await prompts(dbQuestions);
|
|
295
|
+
Object.assign(responses, dbResponses);
|
|
296
|
+
}
|
|
297
|
+
|
|
255
298
|
// --- Step 3: Google Workspace Configuration ---
|
|
256
299
|
if (platforms.includes("google")) {
|
|
257
300
|
console.log("\n--- Configuring Google Workspace backend ---");
|
|
@@ -756,8 +799,8 @@ export async function authenticateUser(options = {}) {
|
|
|
756
799
|
runCommandSync(`gcloud iam service-accounts add-iam-policy-binding "${sa_email}" --member="user:${current_user}" --role="roles/iam.serviceAccountTokenCreator" --quiet`, true);
|
|
757
800
|
|
|
758
801
|
const saUniqueId = execSync(`gcloud iam service-accounts describe "${sa_email}" --format="value(uniqueId)"`, { shell: true }).toString().trim();
|
|
759
|
-
console.log(`\n\x1b[1;33m
|
|
760
|
-
console.log(`IMPORTANT:
|
|
802
|
+
console.log(`\n\x1b[1;33m*************************************************************************************************`);
|
|
803
|
+
console.log(`IMPORTANT: If you haven't already done it, add this to Admin Console (Domain-Wide Delegation):`);
|
|
761
804
|
console.log(`************************************************************************\x1b[0m`);
|
|
762
805
|
console.log(`URL: https://admin.google.com/ac/owl/domainwidedelegation`);
|
|
763
806
|
console.log(`Client ID: ${saUniqueId}\nScopes: ${scopes}`);
|
package/src/cli/utils.js
CHANGED
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
import { spawn, execSync } from "child_process";
|
|
2
|
-
import {
|
|
3
|
-
const
|
|
4
|
-
const pjson = require("../../package.json");
|
|
2
|
+
import { initMetadata } from "../support/metadata.js";
|
|
3
|
+
const GasFakes = initMetadata();
|
|
5
4
|
|
|
6
|
-
export const VERSION =
|
|
5
|
+
export const VERSION = GasFakes.metadata.version;
|
|
7
6
|
export const CLI_VERSION = "0.0.20";
|
|
8
7
|
export const MCP_VERSION = "0.0.7";
|
|
9
8
|
|
package/src/index.js
CHANGED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* the idea here is to create an empty global entry for the singleton
|
|
3
|
+
* but only load it when it is actually used.
|
|
4
|
+
*/
|
|
5
|
+
import { newFakeJdbcService as maker } from './fakejdbcservice.js';
|
|
6
|
+
import { lazyLoaderApp } from '../common/lazyloader.js'
|
|
7
|
+
|
|
8
|
+
let _app = null;
|
|
9
|
+
_app = lazyLoaderApp(_app, 'Jdbc', maker);
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { Proxies } from '../../support/proxies.js';
|
|
2
|
+
|
|
3
|
+
class FakeJdbcArray {
|
|
4
|
+
constructor(data, baseType, baseTypeName) {
|
|
5
|
+
this.__fakeObjectType = 'JdbcArray';
|
|
6
|
+
this._data = Array.isArray(data) ? data : [data];
|
|
7
|
+
this._baseType = baseType || 12; // VARCHAR
|
|
8
|
+
this._baseTypeName = baseTypeName || 'VARCHAR';
|
|
9
|
+
this._isClosed = false;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
getArray() {
|
|
13
|
+
if (this._isClosed) throw new Error('Array is closed.');
|
|
14
|
+
return this._data;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
getBaseType() {
|
|
18
|
+
return this._baseType;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
getBaseTypeName() {
|
|
22
|
+
return this._baseTypeName;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
getResultSet() {
|
|
26
|
+
// In actual JDBC this returns a result set with columns INDEX and VALUE
|
|
27
|
+
// For now, returning null as it's rarely used in Apps Script JDBC context
|
|
28
|
+
return null;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
free() {
|
|
32
|
+
this._isClosed = true;
|
|
33
|
+
this._data = null;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export const newFakeJdbcArray = (...args) => Proxies.guard(new FakeJdbcArray(...args));
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Fake implementation of Google Apps Script Jdbc's BigDecimal (java.math.BigDecimal proxy)
|
|
3
|
+
*/
|
|
4
|
+
export class FakeJdbcBigDecimal {
|
|
5
|
+
constructor(val) {
|
|
6
|
+
this.val = String(val);
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Returns the string representation of this BigDecimal.
|
|
11
|
+
* This is how it's typically used in Apps Script for comparison or output.
|
|
12
|
+
*/
|
|
13
|
+
toString() {
|
|
14
|
+
return this.val;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Returns the value as a JavaScript number.
|
|
19
|
+
* Corresponds to Java's doubleValue()
|
|
20
|
+
*/
|
|
21
|
+
doubleValue() {
|
|
22
|
+
return Number(this.val);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Returns the value as an integer.
|
|
27
|
+
* Corresponds to Java's intValue()
|
|
28
|
+
*/
|
|
29
|
+
intValue() {
|
|
30
|
+
return Math.floor(Number(this.val));
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Returns the value as a long integer.
|
|
35
|
+
* Corresponds to Java's longValue()
|
|
36
|
+
*/
|
|
37
|
+
longValue() {
|
|
38
|
+
return Math.floor(Number(this.val));
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Returns the string representation without scientific notation.
|
|
43
|
+
* Corresponds to Java's toPlainString()
|
|
44
|
+
*/
|
|
45
|
+
toPlainString() {
|
|
46
|
+
return this.val;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Returns the scale of this BigDecimal.
|
|
51
|
+
*/
|
|
52
|
+
scale() {
|
|
53
|
+
const parts = this.val.split('.');
|
|
54
|
+
return parts.length > 1 ? parts[1].length : 0;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Returns the precision of this BigDecimal.
|
|
59
|
+
*/
|
|
60
|
+
precision() {
|
|
61
|
+
return this.val.replace('.', '').replace('-', '').length;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { Proxies } from '../../support/proxies.js';
|
|
2
|
+
import { newFakeInputStream } from '../../support/fakeinputstream.js';
|
|
3
|
+
|
|
4
|
+
class FakeJdbcBlob {
|
|
5
|
+
constructor(data) {
|
|
6
|
+
this.__fakeObjectType = 'JdbcBlob';
|
|
7
|
+
// Ensure we have a Buffer/Uint8Array
|
|
8
|
+
this._data = (data instanceof Uint8Array || Buffer.isBuffer(data))
|
|
9
|
+
? data
|
|
10
|
+
: Buffer.from(data || '');
|
|
11
|
+
this._isClosed = false;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
getBinaryStream() {
|
|
15
|
+
if (this._isClosed) throw new Error('Blob is closed.');
|
|
16
|
+
return newFakeInputStream(this._data);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
getBytes(pos, len) {
|
|
20
|
+
if (this._isClosed) throw new Error('Blob is closed.');
|
|
21
|
+
// pos is 1-indexed in JDBC
|
|
22
|
+
const start = pos - 1;
|
|
23
|
+
const end = start + len;
|
|
24
|
+
return Array.from(this._data.slice(start, end));
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
length() {
|
|
28
|
+
if (this._isClosed) throw new Error('Blob is closed.');
|
|
29
|
+
return this._data.length;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
position(pattern, start) {
|
|
33
|
+
if (this._isClosed) throw new Error('Blob is closed.');
|
|
34
|
+
// Simple implementation for parity
|
|
35
|
+
const patternBuffer = (pattern instanceof FakeJdbcBlob) ? pattern._data : Buffer.from(pattern);
|
|
36
|
+
const index = this._data.indexOf(patternBuffer, start - 1);
|
|
37
|
+
return index === -1 ? -1 : index + 1;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
setBinaryStream(pos) {
|
|
41
|
+
// Not fully supported in fake, but allows setting direction
|
|
42
|
+
return this.getBinaryStream();
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
setBytes(pos, bytes) {
|
|
46
|
+
if (this._isClosed) throw new Error('Blob is closed.');
|
|
47
|
+
const start = pos - 1;
|
|
48
|
+
const newBytes = Buffer.from(bytes);
|
|
49
|
+
const newData = Buffer.alloc(Math.max(this._data.length, start + newBytes.length));
|
|
50
|
+
this._data.copy(newData);
|
|
51
|
+
newBytes.copy(newData, start);
|
|
52
|
+
this._data = newData;
|
|
53
|
+
return newBytes.length;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
truncate(len) {
|
|
57
|
+
if (this._isClosed) throw new Error('Blob is closed.');
|
|
58
|
+
this._data = this._data.slice(0, len);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
free() {
|
|
62
|
+
this._isClosed = true;
|
|
63
|
+
this._data = null;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export const newFakeJdbcBlob = (...args) => Proxies.guard(new FakeJdbcBlob(...args));
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { Proxies } from '../../support/proxies.js';
|
|
2
|
+
import { newFakeInputStream, newFakeReader } from '../../support/fakeinputstream.js';
|
|
3
|
+
|
|
4
|
+
class FakeJdbcClob {
|
|
5
|
+
constructor(data) {
|
|
6
|
+
this.__fakeObjectType = 'JdbcClob';
|
|
7
|
+
this._data = String(data || '');
|
|
8
|
+
this._isClosed = false;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
getAsciiStream() {
|
|
12
|
+
if (this._isClosed) throw new Error('Clob is closed.');
|
|
13
|
+
return newFakeInputStream(Buffer.from(this._data, 'ascii'));
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
getCharacterStream() {
|
|
17
|
+
if (this._isClosed) throw new Error('Clob is closed.');
|
|
18
|
+
return newFakeReader(this._data);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
getSubString(pos, len) {
|
|
22
|
+
if (this._isClosed) throw new Error('Clob is closed.');
|
|
23
|
+
// pos is 1-indexed
|
|
24
|
+
return this._data.substring(pos - 1, pos - 1 + len);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
length() {
|
|
28
|
+
if (this._isClosed) throw new Error('Clob is closed.');
|
|
29
|
+
return this._data.length;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
position(pattern, start) {
|
|
33
|
+
if (this._isClosed) throw new Error('Clob is closed.');
|
|
34
|
+
const searchPattern = (pattern instanceof FakeJdbcClob) ? pattern._data : String(pattern);
|
|
35
|
+
const index = this._data.indexOf(searchPattern, start - 1);
|
|
36
|
+
return index === -1 ? -1 : index + 1;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
setAsciiStream(pos) {
|
|
40
|
+
return this.getAsciiStream();
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
setCharacterStream(pos) {
|
|
44
|
+
return this.getCharacterStream();
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
setString(pos, str) {
|
|
48
|
+
if (this._isClosed) throw new Error('Clob is closed.');
|
|
49
|
+
const start = pos - 1;
|
|
50
|
+
const prefix = this._data.substring(0, start).padEnd(start, ' ');
|
|
51
|
+
const suffix = this._data.substring(start + str.length);
|
|
52
|
+
this._data = prefix + str + suffix;
|
|
53
|
+
return str.length;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
truncate(len) {
|
|
57
|
+
if (this._isClosed) throw new Error('Clob is closed.');
|
|
58
|
+
this._data = this._data.substring(0, len);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
free() {
|
|
62
|
+
this._isClosed = true;
|
|
63
|
+
this._data = null;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export const newFakeJdbcClob = (...args) => Proxies.guard(new FakeJdbcClob(...args));
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { Proxies } from '../../support/proxies.js';
|
|
2
|
+
import { Syncit } from '../../support/syncit.js';
|
|
3
|
+
import { newFakeJdbcStatement } from './fakejdbcstatement.js';
|
|
4
|
+
import { newFakeJdbcPreparedStatement } from './fakejdbcpreparedstatement.js';
|
|
5
|
+
import { newFakeJdbcDatabaseMetaData } from './fakejdbcdatabasemetadata.js';
|
|
6
|
+
|
|
7
|
+
class FakeJdbcConnection {
|
|
8
|
+
constructor(url, user, password) {
|
|
9
|
+
this.__fakeObjectType = 'JdbcConnection';
|
|
10
|
+
this._url = url;
|
|
11
|
+
|
|
12
|
+
// Connect synchronously using the worker
|
|
13
|
+
// Only pass arguments that are provided to avoid passing null/undefined to Syncit
|
|
14
|
+
const args = [url];
|
|
15
|
+
if (user !== null && typeof user !== 'undefined') args.push(user);
|
|
16
|
+
if (password !== null && typeof password !== 'undefined') args.push(password);
|
|
17
|
+
|
|
18
|
+
const result = Syncit.fxJdbcConnect(...args);
|
|
19
|
+
this._connectionId = result.id;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// GAS overload: createStatement() or createStatement(resultSetType, resultSetConcurrency)
|
|
23
|
+
// resultSetType/Concurrency parameters are advisory in the fake since results are buffered in memory.
|
|
24
|
+
createStatement(resultSetType, resultSetConcurrency) {
|
|
25
|
+
return newFakeJdbcStatement(this, this._connectionId);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
prepareStatement(sql) {
|
|
29
|
+
return newFakeJdbcPreparedStatement(this, this._connectionId, sql);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
commit() {
|
|
33
|
+
Syncit.fxJdbcCommit(this._connectionId);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
rollback() {
|
|
37
|
+
Syncit.fxJdbcRollback(this._connectionId);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
setAutoCommit(autoCommit) {
|
|
41
|
+
Syncit.fxJdbcSetAutoCommit(this._connectionId, autoCommit);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
getAutoCommit() {
|
|
45
|
+
// We don't currently track this in the proxy, but GAS default is true
|
|
46
|
+
return true;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
getMetaData() {
|
|
50
|
+
return newFakeJdbcDatabaseMetaData(this, this._connectionId, this._url);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// To match GAS JdbcConnection basic capabilities
|
|
54
|
+
close() {
|
|
55
|
+
if (this._connectionId) {
|
|
56
|
+
Syncit.fxJdbcClose(this._connectionId);
|
|
57
|
+
this._connectionId = null;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
isClosed() {
|
|
62
|
+
return !this._connectionId;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export const newFakeJdbcConnection = (...args) => Proxies.guard(new FakeJdbcConnection(...args));
|