@squadbase/vite-server 0.1.9-dev.a120137 → 0.1.9-dev.a70dcaa
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/dist/cli/index.js +97059 -70750
- package/dist/connectors/aws-billing.js +45 -28875
- package/dist/connectors/azure-sql.js +20400 -17
- package/dist/connectors/cosmosdb.d.ts +5 -0
- package/dist/connectors/cosmosdb.js +685 -0
- package/dist/connectors/freshsales.js +1 -1
- package/dist/connectors/github.js +1 -1
- package/dist/connectors/google-search-console-oauth.js +39 -8
- package/dist/connectors/jdbc.js +609 -106
- package/dist/connectors/oracle.js +20433 -27
- package/dist/connectors/semrush.js +29 -16
- package/dist/connectors/sqlserver.js +20395 -17
- package/dist/connectors/supabase.js +1 -1
- package/dist/index.js +40741 -45908
- package/dist/main.js +40735 -45902
- package/dist/vite-plugin.js +40655 -45821
- package/package.json +3 -2
package/dist/connectors/jdbc.js
CHANGED
|
@@ -20307,6 +20307,12 @@ var NOOP_TUNNEL = (connectionUrl) => ({
|
|
|
20307
20307
|
close: async () => {
|
|
20308
20308
|
}
|
|
20309
20309
|
});
|
|
20310
|
+
var NOOP_TUNNEL_HOSTPORT = (host, port) => ({
|
|
20311
|
+
host,
|
|
20312
|
+
port,
|
|
20313
|
+
close: async () => {
|
|
20314
|
+
}
|
|
20315
|
+
});
|
|
20310
20316
|
function connectionParamsToRecord(connection2) {
|
|
20311
20317
|
const out = {};
|
|
20312
20318
|
for (const p of connection2.parameters) {
|
|
@@ -20314,9 +20320,9 @@ function connectionParamsToRecord(connection2) {
|
|
|
20314
20320
|
}
|
|
20315
20321
|
return out;
|
|
20316
20322
|
}
|
|
20317
|
-
async function
|
|
20323
|
+
async function maybeOpenSshTunnelHostPort(params, dbHost, dbPort) {
|
|
20318
20324
|
const sshHost = params[sshTunnelParameters.sshHost.slug];
|
|
20319
|
-
if (!sshHost) return
|
|
20325
|
+
if (!sshHost) return NOOP_TUNNEL_HOSTPORT(dbHost, dbPort);
|
|
20320
20326
|
const sshUsername = params[sshTunnelParameters.sshUsername.slug];
|
|
20321
20327
|
const sshPrivateKeyBase64 = params[sshTunnelParameters.sshPrivateKeyBase64.slug];
|
|
20322
20328
|
if (!sshUsername || !sshPrivateKeyBase64) {
|
|
@@ -20326,9 +20332,6 @@ async function maybeOpenSshTunnel(params, connectionUrl, defaultDbPort) {
|
|
|
20326
20332
|
}
|
|
20327
20333
|
const sshPort = Number(params[sshTunnelParameters.sshPort.slug] || "22") || 22;
|
|
20328
20334
|
const sshPassphrase = params[sshTunnelParameters.sshPassphrase.slug];
|
|
20329
|
-
const url = new URL(connectionUrl);
|
|
20330
|
-
const dbHost = url.hostname;
|
|
20331
|
-
const dbPort = url.port ? Number(url.port) : defaultDbPort;
|
|
20332
20335
|
const [{ Client }, net] = await Promise.all([
|
|
20333
20336
|
Promise.resolve().then(() => __toESM(require_lib3(), 1)),
|
|
20334
20337
|
import("net")
|
|
@@ -20371,16 +20374,297 @@ async function maybeOpenSshTunnel(params, connectionUrl, defaultDbPort) {
|
|
|
20371
20374
|
sshClient.end();
|
|
20372
20375
|
throw new Error("Failed to allocate local port for SSH tunnel.");
|
|
20373
20376
|
}
|
|
20374
|
-
url.hostname = "127.0.0.1";
|
|
20375
|
-
url.port = String(address.port);
|
|
20376
20377
|
return {
|
|
20377
|
-
|
|
20378
|
+
host: "127.0.0.1",
|
|
20379
|
+
port: address.port,
|
|
20378
20380
|
close: async () => {
|
|
20379
20381
|
await new Promise((resolve) => server.close(() => resolve()));
|
|
20380
20382
|
sshClient.end();
|
|
20381
20383
|
}
|
|
20382
20384
|
};
|
|
20383
20385
|
}
|
|
20386
|
+
async function maybeOpenSshTunnel(params, connectionUrl, defaultDbPort) {
|
|
20387
|
+
const sshHost = params[sshTunnelParameters.sshHost.slug];
|
|
20388
|
+
if (!sshHost) return NOOP_TUNNEL(connectionUrl);
|
|
20389
|
+
const url = new URL(connectionUrl);
|
|
20390
|
+
const dbHost = url.hostname;
|
|
20391
|
+
const dbPort = url.port ? Number(url.port) : defaultDbPort;
|
|
20392
|
+
const tunnel = await maybeOpenSshTunnelHostPort(params, dbHost, dbPort);
|
|
20393
|
+
url.hostname = tunnel.host;
|
|
20394
|
+
url.port = String(tunnel.port);
|
|
20395
|
+
return {
|
|
20396
|
+
connectionUrl: url.toString(),
|
|
20397
|
+
close: tunnel.close
|
|
20398
|
+
};
|
|
20399
|
+
}
|
|
20400
|
+
|
|
20401
|
+
// ../connectors/src/connectors/sqlserver/utils.ts
|
|
20402
|
+
var SQLSERVER_PREFIX_RE = /^(?:jdbc:)?sqlserver:\/\//i;
|
|
20403
|
+
var TRUE_VALUES = /* @__PURE__ */ new Set(["true", "1", "yes"]);
|
|
20404
|
+
var FALSE_VALUES = /* @__PURE__ */ new Set(["false", "0", "no"]);
|
|
20405
|
+
function parseBoolean(value) {
|
|
20406
|
+
if (value == null) return void 0;
|
|
20407
|
+
const lower = value.toLowerCase();
|
|
20408
|
+
if (TRUE_VALUES.has(lower)) return true;
|
|
20409
|
+
if (FALSE_VALUES.has(lower)) return false;
|
|
20410
|
+
return void 0;
|
|
20411
|
+
}
|
|
20412
|
+
function parseSqlServerJdbcUrl(jdbcUrl, options = {}) {
|
|
20413
|
+
const trimmed = jdbcUrl.trim();
|
|
20414
|
+
if (!SQLSERVER_PREFIX_RE.test(trimmed)) {
|
|
20415
|
+
throw new Error(
|
|
20416
|
+
`Unsupported SQL Server URL "${redactSqlServerUrl(trimmed)}". Expected prefix: jdbc:sqlserver:// or sqlserver://.`
|
|
20417
|
+
);
|
|
20418
|
+
}
|
|
20419
|
+
const withoutPrefix = trimmed.replace(SQLSERVER_PREFIX_RE, "");
|
|
20420
|
+
const [hostAndPath, ...propertySegments] = withoutPrefix.split(";");
|
|
20421
|
+
const props = {};
|
|
20422
|
+
for (const segment of propertySegments) {
|
|
20423
|
+
if (!segment) continue;
|
|
20424
|
+
const eqIdx = segment.indexOf("=");
|
|
20425
|
+
if (eqIdx === -1) continue;
|
|
20426
|
+
const key = segment.slice(0, eqIdx).trim().toLowerCase();
|
|
20427
|
+
const value = segment.slice(eqIdx + 1).trim();
|
|
20428
|
+
if (key) props[key] = value;
|
|
20429
|
+
}
|
|
20430
|
+
const url = new URL(`mssql://${hostAndPath}`);
|
|
20431
|
+
for (const [key, value] of url.searchParams.entries()) {
|
|
20432
|
+
if (!(key.toLowerCase() in props)) {
|
|
20433
|
+
props[key.toLowerCase()] = value;
|
|
20434
|
+
}
|
|
20435
|
+
}
|
|
20436
|
+
const server = url.hostname;
|
|
20437
|
+
const port = url.port ? Number(url.port) : 1433;
|
|
20438
|
+
const pathname = url.pathname.replace(/^\//, "");
|
|
20439
|
+
const database = pathname || props["database"] || props["databasename"];
|
|
20440
|
+
const user = props["user"] || props["username"] || props["userid"] || options.username;
|
|
20441
|
+
const password = props["password"] || options.password;
|
|
20442
|
+
return {
|
|
20443
|
+
server,
|
|
20444
|
+
port,
|
|
20445
|
+
database,
|
|
20446
|
+
user,
|
|
20447
|
+
password,
|
|
20448
|
+
options: props
|
|
20449
|
+
};
|
|
20450
|
+
}
|
|
20451
|
+
function toMssqlConfig(parsed, defaults = {}) {
|
|
20452
|
+
const encrypt = parseBoolean(parsed.options["encrypt"]) ?? defaults.encrypt ?? false;
|
|
20453
|
+
const trustServerCertificate = parseBoolean(parsed.options["trustservercertificate"]) ?? !encrypt;
|
|
20454
|
+
return {
|
|
20455
|
+
server: parsed.server,
|
|
20456
|
+
port: parsed.port,
|
|
20457
|
+
database: parsed.database,
|
|
20458
|
+
user: parsed.user,
|
|
20459
|
+
password: parsed.password,
|
|
20460
|
+
connectionTimeout: 1e4,
|
|
20461
|
+
requestTimeout: 6e4,
|
|
20462
|
+
options: {
|
|
20463
|
+
encrypt,
|
|
20464
|
+
trustServerCertificate
|
|
20465
|
+
}
|
|
20466
|
+
};
|
|
20467
|
+
}
|
|
20468
|
+
function redactSqlServerUrl(jdbcUrl) {
|
|
20469
|
+
return jdbcUrl.replace(/(:\/\/)([^@/;]+)@/, "$1***@").replace(/(password\s*=\s*)([^;]+)/gi, "$1***");
|
|
20470
|
+
}
|
|
20471
|
+
|
|
20472
|
+
// ../connectors/src/lib/mssql-runner.ts
|
|
20473
|
+
async function importMssql() {
|
|
20474
|
+
const mod = await import("mssql");
|
|
20475
|
+
return mod.default ?? mod;
|
|
20476
|
+
}
|
|
20477
|
+
async function runMssqlQuery(parsed, sql, options = {}) {
|
|
20478
|
+
const sqlMod = await importMssql();
|
|
20479
|
+
const tunnel = options.tunnelParams ? await maybeOpenSshTunnelHostPort(
|
|
20480
|
+
options.tunnelParams,
|
|
20481
|
+
parsed.server,
|
|
20482
|
+
parsed.port
|
|
20483
|
+
) : null;
|
|
20484
|
+
try {
|
|
20485
|
+
const tunneled = tunnel ? { ...parsed, server: tunnel.host, port: tunnel.port } : parsed;
|
|
20486
|
+
const config = toMssqlConfig(tunneled, {
|
|
20487
|
+
encrypt: options.forceEncrypt
|
|
20488
|
+
});
|
|
20489
|
+
const pool = new sqlMod.ConnectionPool(config);
|
|
20490
|
+
await pool.connect();
|
|
20491
|
+
try {
|
|
20492
|
+
const result = await pool.request().query(sql);
|
|
20493
|
+
const recordset = result.recordset ?? [];
|
|
20494
|
+
return { rows: recordset };
|
|
20495
|
+
} finally {
|
|
20496
|
+
await pool.close();
|
|
20497
|
+
}
|
|
20498
|
+
} finally {
|
|
20499
|
+
await tunnel?.close();
|
|
20500
|
+
}
|
|
20501
|
+
}
|
|
20502
|
+
async function checkMssqlConnection(url, credentials, options = {}) {
|
|
20503
|
+
let parsed;
|
|
20504
|
+
try {
|
|
20505
|
+
parsed = parseSqlServerJdbcUrl(url, credentials);
|
|
20506
|
+
} catch (err) {
|
|
20507
|
+
return {
|
|
20508
|
+
success: false,
|
|
20509
|
+
error: err instanceof Error ? err.message : String(err)
|
|
20510
|
+
};
|
|
20511
|
+
}
|
|
20512
|
+
try {
|
|
20513
|
+
await runMssqlQuery(parsed, "SELECT 1 AS one", options);
|
|
20514
|
+
return { success: true };
|
|
20515
|
+
} catch (err) {
|
|
20516
|
+
let msg = err instanceof Error ? err.message : String(err);
|
|
20517
|
+
msg = msg.replaceAll(url, redactSqlServerUrl(url));
|
|
20518
|
+
return { success: false, error: msg };
|
|
20519
|
+
}
|
|
20520
|
+
}
|
|
20521
|
+
|
|
20522
|
+
// ../connectors/src/connectors/oracle/utils.ts
|
|
20523
|
+
var JDBC_THIN_PREFIX_RE = /^jdbc:oracle:thin:/i;
|
|
20524
|
+
var JDBC_OCI_PREFIX_RE = /^jdbc:oracle:oci/i;
|
|
20525
|
+
var URL_PREFIX_RE = /^oracle:\/\//i;
|
|
20526
|
+
function parseOracleJdbcUrl(jdbcUrl, options = {}) {
|
|
20527
|
+
const trimmed = jdbcUrl.trim();
|
|
20528
|
+
if (JDBC_OCI_PREFIX_RE.test(trimmed)) {
|
|
20529
|
+
throw new Error(
|
|
20530
|
+
"Oracle OCI driver URLs are not supported. Use the thin driver form: jdbc:oracle:thin:@host:port/service"
|
|
20531
|
+
);
|
|
20532
|
+
}
|
|
20533
|
+
if (URL_PREFIX_RE.test(trimmed)) {
|
|
20534
|
+
const url = new URL(trimmed);
|
|
20535
|
+
const path2 = url.pathname.replace(/^\//, "");
|
|
20536
|
+
if (!url.hostname || !path2) {
|
|
20537
|
+
throw new Error(
|
|
20538
|
+
`Invalid Oracle URL "${redactOracleUrl(trimmed)}". Expected oracle://[user:password@]host:port/service`
|
|
20539
|
+
);
|
|
20540
|
+
}
|
|
20541
|
+
const port = url.port || "1521";
|
|
20542
|
+
return {
|
|
20543
|
+
connectString: `${url.hostname}:${port}/${path2}`,
|
|
20544
|
+
user: url.username ? decodeURIComponent(url.username) : options.username,
|
|
20545
|
+
password: url.password ? decodeURIComponent(url.password) : options.password
|
|
20546
|
+
};
|
|
20547
|
+
}
|
|
20548
|
+
if (!JDBC_THIN_PREFIX_RE.test(trimmed)) {
|
|
20549
|
+
throw new Error(
|
|
20550
|
+
`Unsupported Oracle URL "${redactOracleUrl(trimmed)}". Expected prefix: jdbc:oracle:thin:@ or oracle://`
|
|
20551
|
+
);
|
|
20552
|
+
}
|
|
20553
|
+
const afterPrefix = trimmed.replace(JDBC_THIN_PREFIX_RE, "");
|
|
20554
|
+
const atIdx = afterPrefix.indexOf("@");
|
|
20555
|
+
if (atIdx === -1) {
|
|
20556
|
+
throw new Error(
|
|
20557
|
+
`Invalid Oracle JDBC URL "${redactOracleUrl(trimmed)}". Expected '@' separator before host.`
|
|
20558
|
+
);
|
|
20559
|
+
}
|
|
20560
|
+
const credentialsPart = afterPrefix.slice(0, atIdx);
|
|
20561
|
+
const target = afterPrefix.slice(atIdx + 1).replace(/^\/\//, "");
|
|
20562
|
+
let user = options.username;
|
|
20563
|
+
let password = options.password;
|
|
20564
|
+
if (credentialsPart) {
|
|
20565
|
+
const slashIdx = credentialsPart.indexOf("/");
|
|
20566
|
+
if (slashIdx === -1) {
|
|
20567
|
+
user = credentialsPart || user;
|
|
20568
|
+
} else {
|
|
20569
|
+
user = credentialsPart.slice(0, slashIdx) || user;
|
|
20570
|
+
password = credentialsPart.slice(slashIdx + 1) || password;
|
|
20571
|
+
}
|
|
20572
|
+
}
|
|
20573
|
+
if (!target) {
|
|
20574
|
+
throw new Error(
|
|
20575
|
+
`Invalid Oracle JDBC URL "${redactOracleUrl(trimmed)}". Missing host portion after '@'.`
|
|
20576
|
+
);
|
|
20577
|
+
}
|
|
20578
|
+
return {
|
|
20579
|
+
connectString: target,
|
|
20580
|
+
user,
|
|
20581
|
+
password
|
|
20582
|
+
};
|
|
20583
|
+
}
|
|
20584
|
+
function redactOracleUrl(jdbcUrl) {
|
|
20585
|
+
return jdbcUrl.replace(/(:\/\/)([^@/]+)@/, "$1***@").replace(/(thin:)([^@]+)@/i, "$1***@");
|
|
20586
|
+
}
|
|
20587
|
+
function parseOracleConnectStringHostPort(connectString) {
|
|
20588
|
+
const m = /^([^:/]+):(\d+)(.*)$/.exec(connectString);
|
|
20589
|
+
if (!m) return null;
|
|
20590
|
+
return { host: m[1], port: Number(m[2]), trailing: m[3] };
|
|
20591
|
+
}
|
|
20592
|
+
function rewriteOracleConnectStringHostPort(connectString, host, port) {
|
|
20593
|
+
const parts = parseOracleConnectStringHostPort(connectString);
|
|
20594
|
+
if (!parts) return connectString;
|
|
20595
|
+
return `${host}:${port}${parts.trailing}`;
|
|
20596
|
+
}
|
|
20597
|
+
|
|
20598
|
+
// ../connectors/src/lib/oracle-runner.ts
|
|
20599
|
+
async function importOracleDb() {
|
|
20600
|
+
const mod = await import("oracledb");
|
|
20601
|
+
return mod.default ?? mod;
|
|
20602
|
+
}
|
|
20603
|
+
async function runOracleQuery(parsed, sql, options = {}) {
|
|
20604
|
+
const oracledb = await importOracleDb();
|
|
20605
|
+
let tunnel = null;
|
|
20606
|
+
if (options.tunnelParams) {
|
|
20607
|
+
const hostPort = parseOracleConnectStringHostPort(parsed.connectString);
|
|
20608
|
+
if (hostPort) {
|
|
20609
|
+
tunnel = await maybeOpenSshTunnelHostPort(
|
|
20610
|
+
options.tunnelParams,
|
|
20611
|
+
hostPort.host,
|
|
20612
|
+
hostPort.port
|
|
20613
|
+
);
|
|
20614
|
+
}
|
|
20615
|
+
}
|
|
20616
|
+
try {
|
|
20617
|
+
const connectString = tunnel ? rewriteOracleConnectStringHostPort(
|
|
20618
|
+
parsed.connectString,
|
|
20619
|
+
tunnel.host,
|
|
20620
|
+
tunnel.port
|
|
20621
|
+
) : parsed.connectString;
|
|
20622
|
+
const connection2 = await oracledb.getConnection({
|
|
20623
|
+
user: parsed.user,
|
|
20624
|
+
password: parsed.password,
|
|
20625
|
+
connectString
|
|
20626
|
+
});
|
|
20627
|
+
try {
|
|
20628
|
+
const result = await connection2.execute(
|
|
20629
|
+
sql,
|
|
20630
|
+
[],
|
|
20631
|
+
{
|
|
20632
|
+
outFormat: oracledb.OUT_FORMAT_OBJECT,
|
|
20633
|
+
// Bound by the connector's own row cap, but keep the driver from
|
|
20634
|
+
// streaming arbitrarily large result sets.
|
|
20635
|
+
maxRows: 5e3
|
|
20636
|
+
}
|
|
20637
|
+
);
|
|
20638
|
+
return { rows: result.rows ?? [] };
|
|
20639
|
+
} finally {
|
|
20640
|
+
try {
|
|
20641
|
+
await connection2.close();
|
|
20642
|
+
} catch {
|
|
20643
|
+
}
|
|
20644
|
+
}
|
|
20645
|
+
} finally {
|
|
20646
|
+
await tunnel?.close();
|
|
20647
|
+
}
|
|
20648
|
+
}
|
|
20649
|
+
async function checkOracleConnection(url, credentials, options = {}) {
|
|
20650
|
+
let parsed;
|
|
20651
|
+
try {
|
|
20652
|
+
parsed = parseOracleJdbcUrl(url, credentials);
|
|
20653
|
+
} catch (err) {
|
|
20654
|
+
return {
|
|
20655
|
+
success: false,
|
|
20656
|
+
error: err instanceof Error ? err.message : String(err)
|
|
20657
|
+
};
|
|
20658
|
+
}
|
|
20659
|
+
try {
|
|
20660
|
+
await runOracleQuery(parsed, "SELECT 1 FROM DUAL", options);
|
|
20661
|
+
return { success: true };
|
|
20662
|
+
} catch (err) {
|
|
20663
|
+
let msg = err instanceof Error ? err.message : String(err);
|
|
20664
|
+
msg = msg.replaceAll(url, redactOracleUrl(url));
|
|
20665
|
+
return { success: false, error: msg };
|
|
20666
|
+
}
|
|
20667
|
+
}
|
|
20384
20668
|
|
|
20385
20669
|
// ../connectors/src/connectors/jdbc/parameters.ts
|
|
20386
20670
|
var parameters = {
|
|
@@ -20415,20 +20699,82 @@ var parameters = {
|
|
|
20415
20699
|
};
|
|
20416
20700
|
|
|
20417
20701
|
// ../connectors/src/connectors/jdbc/utils.ts
|
|
20418
|
-
var
|
|
20702
|
+
var POSTGRES_PREFIX_RE = /^jdbc:(postgresql|postgres):\/\//i;
|
|
20703
|
+
var MYSQL_PREFIX_RE = /^jdbc:(mysql|mariadb):\/\//i;
|
|
20704
|
+
var REDSHIFT_PREFIX_RE = /^jdbc:redshift:\/\//i;
|
|
20705
|
+
var SQLSERVER_PREFIX_RE2 = /^jdbc:sqlserver:\/\//i;
|
|
20706
|
+
var ORACLE_PREFIX_RE = /^jdbc:oracle:thin:/i;
|
|
20707
|
+
var KNOWN_UNSUPPORTED = [
|
|
20708
|
+
{
|
|
20709
|
+
prefix: /^jdbc:snowflake:\/\//i,
|
|
20710
|
+
message: "Snowflake JDBC URLs are not routable through the generic `jdbc` connector. Use the dedicated `snowflake` connector."
|
|
20711
|
+
},
|
|
20712
|
+
{
|
|
20713
|
+
prefix: /^jdbc:bigquery:\/\//i,
|
|
20714
|
+
message: "BigQuery JDBC URLs are not routable through the generic `jdbc` connector. Use the dedicated `bigquery` connector."
|
|
20715
|
+
},
|
|
20716
|
+
{
|
|
20717
|
+
prefix: /^jdbc:databricks:\/\//i,
|
|
20718
|
+
message: "Databricks JDBC URLs are not routable through the generic `jdbc` connector. Use the dedicated `databricks` connector."
|
|
20719
|
+
},
|
|
20720
|
+
{
|
|
20721
|
+
prefix: /^jdbc:(trino|presto):\/\//i,
|
|
20722
|
+
message: "Trino/Presto JDBC URLs are not yet supported by the `jdbc` connector (no Node.js client bundled)."
|
|
20723
|
+
},
|
|
20724
|
+
{
|
|
20725
|
+
prefix: /^jdbc:td:\/\//i,
|
|
20726
|
+
message: "Treasure Data JDBC URLs are not yet supported by the `jdbc` connector (no Node.js client bundled). The TD JDBC driver speaks Presto under the hood; Trino/Presto support is the same prerequisite."
|
|
20727
|
+
},
|
|
20728
|
+
{
|
|
20729
|
+
prefix: /^jdbc:oracle:oci/i,
|
|
20730
|
+
message: "Oracle OCI driver URLs are not supported. Use the thin driver form: jdbc:oracle:thin:@host:port/service"
|
|
20731
|
+
},
|
|
20732
|
+
{
|
|
20733
|
+
prefix: /^jdbc:db2:\/\//i,
|
|
20734
|
+
message: "DB2 JDBC URLs are not yet supported by the `jdbc` connector."
|
|
20735
|
+
},
|
|
20736
|
+
{
|
|
20737
|
+
prefix: /^jdbc:(sybase|h2|hsqldb|derby|firebird|sqlite):/i,
|
|
20738
|
+
message: "This JDBC dialect is not supported by the `jdbc` connector."
|
|
20739
|
+
}
|
|
20740
|
+
];
|
|
20419
20741
|
function parseJdbcUrl(jdbcUrl, options = {}) {
|
|
20420
20742
|
const trimmed = jdbcUrl.trim();
|
|
20421
|
-
|
|
20422
|
-
|
|
20743
|
+
if (SQLSERVER_PREFIX_RE2.test(trimmed)) {
|
|
20744
|
+
return { driver: "sqlserver", originalUrl: trimmed };
|
|
20745
|
+
}
|
|
20746
|
+
if (ORACLE_PREFIX_RE.test(trimmed)) {
|
|
20747
|
+
return { driver: "oracle", originalUrl: trimmed };
|
|
20748
|
+
}
|
|
20749
|
+
let driver;
|
|
20750
|
+
let defaultPort;
|
|
20751
|
+
let forceSsl = false;
|
|
20752
|
+
let nativeProtocol;
|
|
20753
|
+
let withoutPrefix;
|
|
20754
|
+
if (POSTGRES_PREFIX_RE.test(trimmed)) {
|
|
20755
|
+
driver = "postgresql";
|
|
20756
|
+
defaultPort = 5432;
|
|
20757
|
+
nativeProtocol = "postgresql://";
|
|
20758
|
+
withoutPrefix = trimmed.replace(POSTGRES_PREFIX_RE, nativeProtocol);
|
|
20759
|
+
} else if (REDSHIFT_PREFIX_RE.test(trimmed)) {
|
|
20760
|
+
driver = "postgresql";
|
|
20761
|
+
defaultPort = 5439;
|
|
20762
|
+
forceSsl = true;
|
|
20763
|
+
nativeProtocol = "postgresql://";
|
|
20764
|
+
withoutPrefix = trimmed.replace(REDSHIFT_PREFIX_RE, nativeProtocol);
|
|
20765
|
+
} else if (MYSQL_PREFIX_RE.test(trimmed)) {
|
|
20766
|
+
driver = "mysql";
|
|
20767
|
+
defaultPort = 3306;
|
|
20768
|
+
nativeProtocol = "mysql://";
|
|
20769
|
+
withoutPrefix = trimmed.replace(MYSQL_PREFIX_RE, nativeProtocol);
|
|
20770
|
+
} else {
|
|
20771
|
+
for (const { prefix, message } of KNOWN_UNSUPPORTED) {
|
|
20772
|
+
if (prefix.test(trimmed)) throw new Error(message);
|
|
20773
|
+
}
|
|
20423
20774
|
throw new Error(
|
|
20424
|
-
`Unsupported JDBC URL "${redactJdbcUrl(trimmed)}". Supported prefixes: jdbc:postgresql://, jdbc:mysql://, jdbc:mariadb://.`
|
|
20775
|
+
`Unsupported JDBC URL "${redactJdbcUrl(trimmed)}". Supported prefixes: jdbc:postgresql://, jdbc:postgres://, jdbc:mysql://, jdbc:mariadb://, jdbc:sqlserver://, jdbc:oracle:thin:@, jdbc:redshift://.`
|
|
20425
20776
|
);
|
|
20426
20777
|
}
|
|
20427
|
-
const subProtocol = match[1].toLowerCase();
|
|
20428
|
-
const driver = subProtocol === "mysql" || subProtocol === "mariadb" ? "mysql" : "postgresql";
|
|
20429
|
-
const defaultPort = driver === "postgresql" ? 5432 : 3306;
|
|
20430
|
-
const nativeProtocol = driver === "postgresql" ? "postgresql://" : "mysql://";
|
|
20431
|
-
const withoutPrefix = trimmed.replace(JDBC_PREFIX_RE, nativeProtocol);
|
|
20432
20778
|
const url = new URL(withoutPrefix);
|
|
20433
20779
|
if (options.username && !url.username) {
|
|
20434
20780
|
url.username = encodeURIComponent(options.username);
|
|
@@ -20436,10 +20782,10 @@ function parseJdbcUrl(jdbcUrl, options = {}) {
|
|
|
20436
20782
|
if (options.password && !url.password) {
|
|
20437
20783
|
url.password = encodeURIComponent(options.password);
|
|
20438
20784
|
}
|
|
20439
|
-
return { driver, nativeUrl: url.toString(), defaultPort };
|
|
20785
|
+
return { driver, nativeUrl: url.toString(), defaultPort, forceSsl };
|
|
20440
20786
|
}
|
|
20441
20787
|
function redactJdbcUrl(jdbcUrl) {
|
|
20442
|
-
return jdbcUrl.replace(/(:\/\/)([^@/]+)@/, "$1***@");
|
|
20788
|
+
return jdbcUrl.replace(/(:\/\/)([^@/]+)@/, "$1***@").replace(/(password\s*=\s*)([^;&]+)/gi, "$1***");
|
|
20443
20789
|
}
|
|
20444
20790
|
|
|
20445
20791
|
// ../connectors/src/connectors/jdbc/sdk/index.ts
|
|
@@ -20452,46 +20798,80 @@ function createClient(params) {
|
|
|
20452
20798
|
const password = params[parameters.password.slug];
|
|
20453
20799
|
const parsed = parseJdbcUrl(jdbcUrl, { username, password });
|
|
20454
20800
|
async function runQuery(sql, queryParams) {
|
|
20455
|
-
let tunnel;
|
|
20456
20801
|
try {
|
|
20457
|
-
|
|
20802
|
+
if (parsed.driver === "sqlserver") {
|
|
20803
|
+
if (queryParams && queryParams.length > 0) {
|
|
20804
|
+
throw new Error(
|
|
20805
|
+
"jdbc sdk: positional parameters are not yet supported for the sqlserver route. Inline literals into the SQL."
|
|
20806
|
+
);
|
|
20807
|
+
}
|
|
20808
|
+
const mssqlParsed = parseSqlServerJdbcUrl(parsed.originalUrl, {
|
|
20809
|
+
username,
|
|
20810
|
+
password
|
|
20811
|
+
});
|
|
20812
|
+
const result = await runMssqlQuery(mssqlParsed, sql, {
|
|
20813
|
+
tunnelParams: params
|
|
20814
|
+
});
|
|
20815
|
+
return result.rows;
|
|
20816
|
+
}
|
|
20817
|
+
if (parsed.driver === "oracle") {
|
|
20818
|
+
if (queryParams && queryParams.length > 0) {
|
|
20819
|
+
throw new Error(
|
|
20820
|
+
"jdbc sdk: positional parameters are not yet supported for the oracle route. Inline literals into the SQL."
|
|
20821
|
+
);
|
|
20822
|
+
}
|
|
20823
|
+
const oracleParsed = parseOracleJdbcUrl(parsed.originalUrl, {
|
|
20824
|
+
username,
|
|
20825
|
+
password
|
|
20826
|
+
});
|
|
20827
|
+
const cleanSql = sql.replace(/;\s*$/, "");
|
|
20828
|
+
const result = await runOracleQuery(oracleParsed, cleanSql, {
|
|
20829
|
+
tunnelParams: params
|
|
20830
|
+
});
|
|
20831
|
+
return result.rows;
|
|
20832
|
+
}
|
|
20833
|
+
const tunnel = await maybeOpenSshTunnel(
|
|
20458
20834
|
params,
|
|
20459
20835
|
parsed.nativeUrl,
|
|
20460
20836
|
parsed.defaultPort
|
|
20461
20837
|
);
|
|
20462
|
-
|
|
20463
|
-
|
|
20464
|
-
|
|
20465
|
-
|
|
20466
|
-
|
|
20467
|
-
|
|
20468
|
-
|
|
20838
|
+
try {
|
|
20839
|
+
if (parsed.driver === "postgresql") {
|
|
20840
|
+
const { Pool } = await import("pg");
|
|
20841
|
+
const pool2 = new Pool({
|
|
20842
|
+
connectionString: tunnel.connectionUrl,
|
|
20843
|
+
ssl: { rejectUnauthorized: false },
|
|
20844
|
+
connectionTimeoutMillis: 1e4,
|
|
20845
|
+
statement_timeout: 6e4
|
|
20846
|
+
});
|
|
20847
|
+
try {
|
|
20848
|
+
const result = await pool2.query(sql, queryParams);
|
|
20849
|
+
return result.rows;
|
|
20850
|
+
} finally {
|
|
20851
|
+
await pool2.end();
|
|
20852
|
+
}
|
|
20853
|
+
}
|
|
20854
|
+
const mysql = await import("mysql2/promise");
|
|
20855
|
+
const pool = mysql.createPool({
|
|
20856
|
+
uri: tunnel.connectionUrl,
|
|
20857
|
+
connectTimeout: 1e4
|
|
20469
20858
|
});
|
|
20470
20859
|
try {
|
|
20471
|
-
const
|
|
20472
|
-
return
|
|
20860
|
+
const [rows] = await pool.query(sql, queryParams);
|
|
20861
|
+
return Array.isArray(rows) ? rows : [];
|
|
20473
20862
|
} finally {
|
|
20474
|
-
await
|
|
20863
|
+
await pool.end();
|
|
20475
20864
|
}
|
|
20476
|
-
}
|
|
20477
|
-
const mysql = await import("mysql2/promise");
|
|
20478
|
-
const pool = mysql.createPool({
|
|
20479
|
-
uri: tunnel.connectionUrl,
|
|
20480
|
-
connectTimeout: 1e4
|
|
20481
|
-
});
|
|
20482
|
-
try {
|
|
20483
|
-
const [rows] = await pool.query(sql, queryParams);
|
|
20484
|
-
return Array.isArray(rows) ? rows : [];
|
|
20485
20865
|
} finally {
|
|
20486
|
-
await
|
|
20866
|
+
await tunnel.close();
|
|
20487
20867
|
}
|
|
20488
20868
|
} catch (err) {
|
|
20489
20869
|
let msg = err instanceof Error ? err.message : String(err);
|
|
20490
|
-
|
|
20870
|
+
if (parsed.driver === "postgresql" || parsed.driver === "mysql") {
|
|
20871
|
+
msg = msg.replaceAll(parsed.nativeUrl, redactJdbcUrl(jdbcUrl));
|
|
20872
|
+
}
|
|
20491
20873
|
msg = msg.replaceAll(jdbcUrl, redactJdbcUrl(jdbcUrl));
|
|
20492
20874
|
throw new Error(msg);
|
|
20493
|
-
} finally {
|
|
20494
|
-
await tunnel?.close();
|
|
20495
20875
|
}
|
|
20496
20876
|
}
|
|
20497
20877
|
return {
|
|
@@ -20678,7 +21058,7 @@ var inputSchema = z.object({
|
|
|
20678
21058
|
),
|
|
20679
21059
|
connectionId: z.string().describe("ID of the JDBC connection to use"),
|
|
20680
21060
|
sql: z.string().describe(
|
|
20681
|
-
"SQL query to execute. Use the dialect that matches the JDBC URL (PostgreSQL
|
|
21061
|
+
"SQL query to execute. Use the dialect that matches the JDBC URL prefix (PostgreSQL / Redshift: LIMIT N; MySQL/MariaDB: LIMIT N; SQL Server: TOP N; Oracle: FETCH FIRST N ROWS ONLY). Always bound the row count."
|
|
20682
21062
|
)
|
|
20683
21063
|
});
|
|
20684
21064
|
var outputSchema = z.discriminatedUnion("success", [
|
|
@@ -20696,9 +21076,10 @@ var outputSchema = z.discriminatedUnion("success", [
|
|
|
20696
21076
|
var executeQueryTool = new ConnectorTool({
|
|
20697
21077
|
name: "executeQuery",
|
|
20698
21078
|
description: `Execute a SQL query through the JDBC connector. Returns up to ${MAX_ROWS} rows.
|
|
20699
|
-
Use for: schema exploration via \`information_schema
|
|
20700
|
-
The connector dispatches by JDBC URL prefix to the matching driver
|
|
20701
|
-
so use the dialect that matches the connection.
|
|
21079
|
+
Use for: schema exploration via \`information_schema\` (or USER_TABLES on Oracle), data sampling, analytical queries.
|
|
21080
|
+
The connector dispatches by JDBC URL prefix to the matching driver
|
|
21081
|
+
(PostgreSQL / Redshift / MySQL / MariaDB / SQL Server / Oracle), so use the dialect that matches the connection.
|
|
21082
|
+
Always bound results: LIMIT for PG/MySQL/Redshift, TOP for SQL Server, FETCH FIRST for Oracle.`,
|
|
20702
21083
|
inputSchema,
|
|
20703
21084
|
outputSchema,
|
|
20704
21085
|
async execute({ connectionId, sql }, connections) {
|
|
@@ -20724,87 +21105,139 @@ so use the dialect that matches the connection. Always include LIMIT.`,
|
|
|
20724
21105
|
error: err instanceof Error ? err.message : String(err)
|
|
20725
21106
|
};
|
|
20726
21107
|
}
|
|
20727
|
-
let tunnel;
|
|
20728
21108
|
try {
|
|
20729
|
-
|
|
20730
|
-
|
|
20731
|
-
parsed.
|
|
20732
|
-
|
|
20733
|
-
|
|
20734
|
-
|
|
20735
|
-
const
|
|
20736
|
-
const
|
|
20737
|
-
|
|
20738
|
-
|
|
20739
|
-
|
|
20740
|
-
|
|
21109
|
+
const tunnelParams = connectionParamsToRecord(connection2);
|
|
21110
|
+
if (parsed.driver === "sqlserver") {
|
|
21111
|
+
const mssqlParsed = parseSqlServerJdbcUrl(parsed.originalUrl, {
|
|
21112
|
+
username,
|
|
21113
|
+
password
|
|
21114
|
+
});
|
|
21115
|
+
const result = await runMssqlQuery(mssqlParsed, sql, { tunnelParams });
|
|
21116
|
+
const rows = result.rows;
|
|
21117
|
+
return {
|
|
21118
|
+
success: true,
|
|
21119
|
+
rowCount: Math.min(rows.length, MAX_ROWS),
|
|
21120
|
+
truncated: rows.length > MAX_ROWS,
|
|
21121
|
+
rows: rows.slice(0, MAX_ROWS)
|
|
21122
|
+
};
|
|
21123
|
+
}
|
|
21124
|
+
if (parsed.driver === "oracle") {
|
|
21125
|
+
const oracleParsed = parseOracleJdbcUrl(parsed.originalUrl, {
|
|
21126
|
+
username,
|
|
21127
|
+
password
|
|
21128
|
+
});
|
|
21129
|
+
const cleanSql = sql.replace(/;\s*$/, "");
|
|
21130
|
+
const result = await runOracleQuery(oracleParsed, cleanSql, {
|
|
21131
|
+
tunnelParams
|
|
21132
|
+
});
|
|
21133
|
+
const rows = result.rows;
|
|
21134
|
+
return {
|
|
21135
|
+
success: true,
|
|
21136
|
+
rowCount: Math.min(rows.length, MAX_ROWS),
|
|
21137
|
+
truncated: rows.length > MAX_ROWS,
|
|
21138
|
+
rows: rows.slice(0, MAX_ROWS)
|
|
21139
|
+
};
|
|
21140
|
+
}
|
|
21141
|
+
let tunnel;
|
|
21142
|
+
try {
|
|
21143
|
+
tunnel = await maybeOpenSshTunnel(
|
|
21144
|
+
tunnelParams,
|
|
21145
|
+
parsed.nativeUrl,
|
|
21146
|
+
parsed.defaultPort
|
|
21147
|
+
);
|
|
21148
|
+
if (parsed.driver === "postgresql") {
|
|
21149
|
+
const { Pool } = await import("pg");
|
|
21150
|
+
const pool2 = new Pool({
|
|
21151
|
+
connectionString: tunnel.connectionUrl,
|
|
21152
|
+
ssl: { rejectUnauthorized: false },
|
|
21153
|
+
connectionTimeoutMillis: CONNECT_TIMEOUT_MS,
|
|
21154
|
+
statement_timeout: STATEMENT_TIMEOUT_MS
|
|
21155
|
+
});
|
|
21156
|
+
try {
|
|
21157
|
+
const result = await pool2.query(sql);
|
|
21158
|
+
const rows = result.rows;
|
|
21159
|
+
const truncated = rows.length > MAX_ROWS;
|
|
21160
|
+
return {
|
|
21161
|
+
success: true,
|
|
21162
|
+
rowCount: Math.min(rows.length, MAX_ROWS),
|
|
21163
|
+
truncated,
|
|
21164
|
+
rows: rows.slice(0, MAX_ROWS)
|
|
21165
|
+
};
|
|
21166
|
+
} finally {
|
|
21167
|
+
await pool2.end();
|
|
21168
|
+
}
|
|
21169
|
+
}
|
|
21170
|
+
const mysql = await import("mysql2/promise");
|
|
21171
|
+
const pool = mysql.createPool({
|
|
21172
|
+
uri: tunnel.connectionUrl,
|
|
21173
|
+
connectTimeout: CONNECT_TIMEOUT_MS
|
|
20741
21174
|
});
|
|
20742
21175
|
try {
|
|
20743
|
-
const
|
|
20744
|
-
const
|
|
20745
|
-
|
|
21176
|
+
const queryPromise = pool.query(sql);
|
|
21177
|
+
const timeoutPromise = new Promise(
|
|
21178
|
+
(_, reject) => setTimeout(
|
|
21179
|
+
() => reject(new Error("Query timed out after 60 seconds")),
|
|
21180
|
+
STATEMENT_TIMEOUT_MS
|
|
21181
|
+
)
|
|
21182
|
+
);
|
|
21183
|
+
const [rows] = await Promise.race([queryPromise, timeoutPromise]);
|
|
21184
|
+
const resultRows = Array.isArray(rows) ? rows : [];
|
|
21185
|
+
const truncated = resultRows.length > MAX_ROWS;
|
|
20746
21186
|
return {
|
|
20747
21187
|
success: true,
|
|
20748
|
-
rowCount: Math.min(
|
|
21188
|
+
rowCount: Math.min(resultRows.length, MAX_ROWS),
|
|
20749
21189
|
truncated,
|
|
20750
|
-
rows:
|
|
21190
|
+
rows: resultRows.slice(0, MAX_ROWS)
|
|
20751
21191
|
};
|
|
20752
21192
|
} finally {
|
|
20753
|
-
await
|
|
21193
|
+
await pool.end();
|
|
20754
21194
|
}
|
|
20755
|
-
}
|
|
20756
|
-
const mysql = await import("mysql2/promise");
|
|
20757
|
-
const pool = mysql.createPool({
|
|
20758
|
-
uri: tunnel.connectionUrl,
|
|
20759
|
-
connectTimeout: CONNECT_TIMEOUT_MS
|
|
20760
|
-
});
|
|
20761
|
-
try {
|
|
20762
|
-
const queryPromise = pool.query(sql);
|
|
20763
|
-
const timeoutPromise = new Promise(
|
|
20764
|
-
(_, reject) => setTimeout(
|
|
20765
|
-
() => reject(new Error("Query timed out after 60 seconds")),
|
|
20766
|
-
STATEMENT_TIMEOUT_MS
|
|
20767
|
-
)
|
|
20768
|
-
);
|
|
20769
|
-
const [rows] = await Promise.race([queryPromise, timeoutPromise]);
|
|
20770
|
-
const resultRows = Array.isArray(rows) ? rows : [];
|
|
20771
|
-
const truncated = resultRows.length > MAX_ROWS;
|
|
20772
|
-
return {
|
|
20773
|
-
success: true,
|
|
20774
|
-
rowCount: Math.min(resultRows.length, MAX_ROWS),
|
|
20775
|
-
truncated,
|
|
20776
|
-
rows: resultRows.slice(0, MAX_ROWS)
|
|
20777
|
-
};
|
|
20778
21195
|
} finally {
|
|
20779
|
-
await
|
|
21196
|
+
await tunnel?.close();
|
|
20780
21197
|
}
|
|
20781
21198
|
} catch (err) {
|
|
20782
21199
|
let msg = err instanceof Error ? err.message : String(err);
|
|
20783
|
-
|
|
21200
|
+
if (parsed.driver === "postgresql" || parsed.driver === "mysql") {
|
|
21201
|
+
msg = msg.replaceAll(parsed.nativeUrl, redactJdbcUrl(jdbcUrl));
|
|
21202
|
+
}
|
|
20784
21203
|
msg = msg.replaceAll(jdbcUrl, redactJdbcUrl(jdbcUrl));
|
|
20785
21204
|
return { success: false, error: msg };
|
|
20786
|
-
} finally {
|
|
20787
|
-
await tunnel?.close();
|
|
20788
21205
|
}
|
|
20789
21206
|
}
|
|
20790
21207
|
});
|
|
20791
21208
|
|
|
21209
|
+
// ../connectors/src/lib/unwrap-sample-limit.ts
|
|
21210
|
+
var STRICT_WRAPPER_RE = /^\s*SELECT\s+\*\s+FROM\s+\(([\s\S]+)\)\s+AS\s+\w+\s+LIMIT\s+(\d+)\s*;?\s*$/i;
|
|
21211
|
+
var TRAILING_LIMIT_RE = /\s+LIMIT\s+(\d+)\s*;?\s*$/i;
|
|
21212
|
+
function unwrapSampleLimit(sql) {
|
|
21213
|
+
const strict = sql.match(STRICT_WRAPPER_RE);
|
|
21214
|
+
if (strict) {
|
|
21215
|
+
return { inner: strict[1].trim(), limit: Number.parseInt(strict[2], 10) };
|
|
21216
|
+
}
|
|
21217
|
+
const trailing = sql.match(TRAILING_LIMIT_RE);
|
|
21218
|
+
if (!trailing) return null;
|
|
21219
|
+
if (!/^\s*SELECT\s+\*\s+FROM\s*\(/i.test(sql)) return null;
|
|
21220
|
+
const limit = Number.parseInt(trailing[1], 10);
|
|
21221
|
+
const inner = sql.slice(0, sql.length - trailing[0].length).trim();
|
|
21222
|
+
return { inner, limit };
|
|
21223
|
+
}
|
|
21224
|
+
|
|
20792
21225
|
// ../connectors/src/connectors/jdbc/index.ts
|
|
20793
21226
|
var tools = { executeQuery: executeQueryTool };
|
|
20794
21227
|
var jdbcConnector = new ConnectorPlugin({
|
|
20795
21228
|
slug: "jdbc",
|
|
20796
21229
|
authType: AUTH_TYPES.USER_PASSWORD,
|
|
20797
21230
|
name: "JDBC",
|
|
20798
|
-
description: "Generic JDBC-style connection. Accepts a JDBC URL and dispatches to the matching driver (PostgreSQL, MySQL,
|
|
21231
|
+
description: "Generic JDBC-style connection. Accepts a JDBC URL and dispatches to the matching driver (PostgreSQL, MySQL/MariaDB, SQL Server, Oracle, Redshift).",
|
|
20799
21232
|
iconUrl: "https://images.ctfassets.net/9ncizv60xc5y/4Hgb5qQffrdu5nOkc49WtM/76d82ef6b108c7780e42998137c61b83/jdbc-icon.png",
|
|
20800
21233
|
parameters,
|
|
20801
|
-
releaseFlag: { dev1: true, dev2:
|
|
21234
|
+
releaseFlag: { dev1: true, dev2: true, prod: true },
|
|
20802
21235
|
categories: ["database"],
|
|
20803
21236
|
onboarding: jdbcOnboarding,
|
|
20804
21237
|
systemPrompt: {
|
|
20805
21238
|
en: `### Tools
|
|
20806
21239
|
|
|
20807
|
-
- \`jdbc_executeQuery\`: Executes a SQL query through a JDBC URL and returns rows. The connector dispatches by URL prefix to the matching driver
|
|
21240
|
+
- \`jdbc_executeQuery\`: Executes a SQL query through a JDBC URL and returns rows. The connector dispatches by URL prefix to the matching native driver, so use the dialect that matches the connection. Use this for schema exploration via \`information_schema\` (or vendor equivalent) and for sampling data. See the SQL Reference below.
|
|
20808
21241
|
|
|
20809
21242
|
### Business Logic
|
|
20810
21243
|
|
|
@@ -20814,16 +21247,29 @@ The business logic type for this connector is "sql".
|
|
|
20814
21247
|
|
|
20815
21248
|
The dialect depends on the JDBC URL prefix:
|
|
20816
21249
|
|
|
20817
|
-
- \`jdbc:postgresql://...\` \u2192 PostgreSQL
|
|
21250
|
+
- \`jdbc:postgresql://...\` / \`jdbc:postgres://...\` \u2192 PostgreSQL (driver: \`pg\`). \`LIMIT N\` is native.
|
|
20818
21251
|
- List tables: \`SELECT table_name FROM information_schema.tables WHERE table_schema = 'public'\`
|
|
20819
21252
|
- List columns: \`SELECT column_name, data_type FROM information_schema.columns WHERE table_schema = 'public' AND table_name = 'xxx'\`
|
|
20820
|
-
- \`jdbc:
|
|
21253
|
+
- \`jdbc:redshift://...\` \u2192 Amazon Redshift (driver: \`pg\` with SSL forced; default port 5439). PostgreSQL-compatible wire protocol; \`LIMIT N\` is native.
|
|
21254
|
+
- \`jdbc:mysql://...\` / \`jdbc:mariadb://...\` \u2192 MySQL / MariaDB (driver: \`mysql2\`). \`LIMIT N\` is native.
|
|
20821
21255
|
- List tables: \`SELECT TABLE_NAME FROM information_schema.TABLES WHERE TABLE_SCHEMA = DATABASE()\`
|
|
20822
21256
|
- List columns: \`SELECT COLUMN_NAME, DATA_TYPE FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'xxx'\`
|
|
20823
|
-
-
|
|
21257
|
+
- \`jdbc:sqlserver://...\` \u2192 Microsoft SQL Server / Azure SQL (driver: \`mssql\` / \`tedious\`). T-SQL dialect. **Use \`TOP N\` instead of \`LIMIT N\`** in queries you write.
|
|
21258
|
+
- List tables: \`SELECT TABLE_SCHEMA, TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_TYPE = 'BASE TABLE'\`
|
|
21259
|
+
- List columns: \`SELECT COLUMN_NAME, DATA_TYPE FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = 'xxx'\`
|
|
21260
|
+
- \`jdbc:oracle:thin:@host:port/service\` (or \`@//host:port/service\` / \`@host:port:sid\`) \u2192 Oracle Database (driver: \`oracledb\` thin). Oracle SQL. **No \`LIMIT\` keyword** \u2014 use \`FETCH FIRST N ROWS ONLY\`, \`OFFSET m ROWS FETCH NEXT N ROWS ONLY\`, or \`ROWNUM\` filters.
|
|
21261
|
+
- List tables: \`SELECT TABLE_NAME FROM USER_TABLES\`
|
|
21262
|
+
- List columns: \`SELECT COLUMN_NAME, DATA_TYPE FROM USER_TAB_COLUMNS WHERE TABLE_NAME = UPPER('xxx')\`
|
|
21263
|
+
|
|
21264
|
+
Explicitly **not** supported via this connector \u2014 use the dedicated connector or open a request:
|
|
21265
|
+
\`jdbc:snowflake://\` (use \`snowflake\`), \`jdbc:bigquery://\` (use \`bigquery\`), \`jdbc:databricks://\` (use \`databricks\`), \`jdbc:trino://\` / \`jdbc:presto://\` / \`jdbc:td://\` (Treasure Data) \u2014 no Node.js client bundled.
|
|
21266
|
+
|
|
21267
|
+
### Row-limit compatibility (server-logic schema inference)
|
|
21268
|
+
|
|
21269
|
+
The platform's server-logic schema inference may wrap your query as \`SELECT * FROM (<inner>) AS _sq LIMIT N\` (PostgreSQL/MySQL syntax). For PostgreSQL / Redshift / MySQL routes this executes natively. For \`jdbc:sqlserver://\` and \`jdbc:oracle:thin:\` routes, the connector detects this exact wrapper at \`query()\` time, executes \`<inner>\` directly via the dialect-specific driver, and slices the first N rows in JS. You do not need to handle this \u2014 but in queries **you author**, do not use \`LIMIT\` for the SQL Server / Oracle routes; use \`TOP\` / \`FETCH FIRST\` as listed above.`,
|
|
20824
21270
|
ja: `### \u30C4\u30FC\u30EB
|
|
20825
21271
|
|
|
20826
|
-
- \`jdbc_executeQuery\`: JDBC URL \u7D4C\u7531\u3067 SQL \u30AF\u30A8\u30EA\u3092\u5B9F\u884C\u3057\u3001\u884C\u30C7\u30FC\u30BF\u3092\u8FD4\u3057\u307E\u3059\u3002\u30B3\u30CD\u30AF\u30BF\u306F URL \u30D7\u30EC\u30D5\u30A3\u30C3\u30AF\u30B9\u304B\u3089\u5BFE\u5FDC\u3059\u308B\
|
|
21272
|
+
- \`jdbc_executeQuery\`: JDBC URL \u7D4C\u7531\u3067 SQL \u30AF\u30A8\u30EA\u3092\u5B9F\u884C\u3057\u3001\u884C\u30C7\u30FC\u30BF\u3092\u8FD4\u3057\u307E\u3059\u3002\u30B3\u30CD\u30AF\u30BF\u306F URL \u30D7\u30EC\u30D5\u30A3\u30C3\u30AF\u30B9\u304B\u3089\u5BFE\u5FDC\u3059\u308B\u30CD\u30A4\u30C6\u30A3\u30D6\u30C9\u30E9\u30A4\u30D0\u3078\u632F\u308A\u5206\u3051\u308B\u305F\u3081\u3001\u63A5\u7D9A\u5148\u306B\u5408\u3063\u305F\u65B9\u8A00\u3092\u4F7F\u7528\u3057\u3066\u304F\u3060\u3055\u3044\u3002\`information_schema\`\uFF08\u307E\u305F\u306F\u5404 DB \u306E\u540C\u7B49\u306E\u30E1\u30BF\u30C7\u30FC\u30BF\u30D3\u30E5\u30FC\uFF09\u3092\u4F7F\u3063\u305F\u30B9\u30AD\u30FC\u30DE\u63A2\u7D22\u3084\u30C7\u30FC\u30BF\u306E\u30B5\u30F3\u30D7\u30EA\u30F3\u30B0\u306B\u4F7F\u3044\u307E\u3059\u3002\u4E0B\u90E8\u306E\u300CSQL \u30EA\u30D5\u30A1\u30EC\u30F3\u30B9\u300D\u3092\u53C2\u7167\u3057\u3066\u304F\u3060\u3055\u3044\u3002
|
|
20827
21273
|
|
|
20828
21274
|
### Business Logic
|
|
20829
21275
|
|
|
@@ -20833,13 +21279,26 @@ The dialect depends on the JDBC URL prefix:
|
|
|
20833
21279
|
|
|
20834
21280
|
JDBC URL \u306E\u30D7\u30EC\u30D5\u30A3\u30C3\u30AF\u30B9\u306B\u3088\u308A\u65B9\u8A00\u304C\u6C7A\u307E\u308A\u307E\u3059:
|
|
20835
21281
|
|
|
20836
|
-
- \`jdbc:postgresql://...\` \u2192 PostgreSQL \
|
|
21282
|
+
- \`jdbc:postgresql://...\` / \`jdbc:postgres://...\` \u2192 PostgreSQL\uFF08\u30C9\u30E9\u30A4\u30D0: \`pg\`\uFF09\u3002\`LIMIT N\` \u304C\u30CD\u30A4\u30C6\u30A3\u30D6\u3067\u4F7F\u3048\u307E\u3059\u3002
|
|
20837
21283
|
- \u30C6\u30FC\u30D6\u30EB\u4E00\u89A7: \`SELECT table_name FROM information_schema.tables WHERE table_schema = 'public'\`
|
|
20838
21284
|
- \u30AB\u30E9\u30E0\u4E00\u89A7: \`SELECT column_name, data_type FROM information_schema.columns WHERE table_schema = 'public' AND table_name = 'xxx'\`
|
|
20839
|
-
- \`jdbc:
|
|
21285
|
+
- \`jdbc:redshift://...\` \u2192 Amazon Redshift\uFF08\u30C9\u30E9\u30A4\u30D0: \`pg\`\u3001SSL \u5F37\u5236\u3001\u30C7\u30D5\u30A9\u30EB\u30C8\u30DD\u30FC\u30C8 5439\uFF09\u3002PostgreSQL \u30EF\u30A4\u30E4\u4E92\u63DB\u306E\u305F\u3081 \`LIMIT N\` \u304C\u305D\u306E\u307E\u307E\u4F7F\u3048\u307E\u3059\u3002
|
|
21286
|
+
- \`jdbc:mysql://...\` / \`jdbc:mariadb://...\` \u2192 MySQL / MariaDB\uFF08\u30C9\u30E9\u30A4\u30D0: \`mysql2\`\uFF09\u3002\`LIMIT N\` \u304C\u30CD\u30A4\u30C6\u30A3\u30D6\u3067\u4F7F\u3048\u307E\u3059\u3002
|
|
20840
21287
|
- \u30C6\u30FC\u30D6\u30EB\u4E00\u89A7: \`SELECT TABLE_NAME FROM information_schema.TABLES WHERE TABLE_SCHEMA = DATABASE()\`
|
|
20841
21288
|
- \u30AB\u30E9\u30E0\u4E00\u89A7: \`SELECT COLUMN_NAME, DATA_TYPE FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'xxx'\`
|
|
20842
|
-
- \
|
|
21289
|
+
- \`jdbc:sqlserver://...\` \u2192 Microsoft SQL Server / Azure SQL\uFF08\u30C9\u30E9\u30A4\u30D0: \`mssql\` / \`tedious\`\uFF09\u3002T-SQL \u65B9\u8A00\u3002**\u81EA\u5206\u3067\u66F8\u304F SQL \u3067\u306F \`LIMIT N\` \u3067\u306F\u306A\u304F \`TOP N\` \u3092\u4F7F\u3046\u3053\u3068**\u3002
|
|
21290
|
+
- \u30C6\u30FC\u30D6\u30EB\u4E00\u89A7: \`SELECT TABLE_SCHEMA, TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_TYPE = 'BASE TABLE'\`
|
|
21291
|
+
- \u30AB\u30E9\u30E0\u4E00\u89A7: \`SELECT COLUMN_NAME, DATA_TYPE FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = 'xxx'\`
|
|
21292
|
+
- \`jdbc:oracle:thin:@host:port/service\`\uFF08\`@//host:port/service\` / \`@host:port:sid\` \u3082\u53EF\uFF09\u2192 Oracle Database\uFF08\u30C9\u30E9\u30A4\u30D0: \`oracledb\` thin\uFF09\u3002Oracle SQL\u3002**\`LIMIT\` \u30AD\u30FC\u30EF\u30FC\u30C9\u306F\u5B58\u5728\u3057\u307E\u305B\u3093** \u2014 \`FETCH FIRST N ROWS ONLY\` / \`OFFSET m ROWS FETCH NEXT N ROWS ONLY\` / \`ROWNUM\` \u3092\u4F7F\u7528\u3057\u3066\u304F\u3060\u3055\u3044\u3002
|
|
21293
|
+
- \u30C6\u30FC\u30D6\u30EB\u4E00\u89A7: \`SELECT TABLE_NAME FROM USER_TABLES\`
|
|
21294
|
+
- \u30AB\u30E9\u30E0\u4E00\u89A7: \`SELECT COLUMN_NAME, DATA_TYPE FROM USER_TAB_COLUMNS WHERE TABLE_NAME = UPPER('xxx')\`
|
|
21295
|
+
|
|
21296
|
+
\u3053\u306E\u30B3\u30CD\u30AF\u30BF\u3067\u306F **\u30B5\u30DD\u30FC\u30C8\u5916**\uFF08\u5C02\u7528\u30B3\u30CD\u30AF\u30BF\u3092\u4F7F\u3046\u304B\u3001\u30EA\u30AF\u30A8\u30B9\u30C8\u8FFD\u52A0\u3057\u3066\u304F\u3060\u3055\u3044\uFF09:
|
|
21297
|
+
\`jdbc:snowflake://\`\uFF08\`snowflake\` \u3092\u4F7F\u7528\uFF09\u3001\`jdbc:bigquery://\`\uFF08\`bigquery\` \u3092\u4F7F\u7528\uFF09\u3001\`jdbc:databricks://\`\uFF08\`databricks\` \u3092\u4F7F\u7528\uFF09\u3001\`jdbc:trino://\` / \`jdbc:presto://\` / \`jdbc:td://\`\uFF08Treasure Data\uFF09\u2014 Node.js \u30AF\u30E9\u30A4\u30A2\u30F3\u30C8\u3092\u540C\u68B1\u3057\u3066\u3044\u307E\u305B\u3093\u3002
|
|
21298
|
+
|
|
21299
|
+
### \u884C\u6570\u5236\u9650\u306E\u4E92\u63DB\u6027\uFF08server-logic \u30B9\u30AD\u30FC\u30DE\u63A8\u8AD6\uFF09
|
|
21300
|
+
|
|
21301
|
+
\u30D7\u30E9\u30C3\u30C8\u30D5\u30A9\u30FC\u30E0\u306E server-logic \u30B9\u30AD\u30FC\u30DE\u63A8\u8AD6\u306F\u3001\u30AF\u30A8\u30EA\u3092 \`SELECT * FROM (<inner>) AS _sq LIMIT N\`\uFF08PostgreSQL/MySQL \u69CB\u6587\uFF09\u306E\u5F62\u3067\u30E9\u30C3\u30D7\u3057\u3066\u304F\u308B\u3053\u3068\u304C\u3042\u308A\u307E\u3059\u3002PostgreSQL / Redshift / MySQL \u30EB\u30FC\u30C8\u3067\u306F\u305D\u306E\u307E\u307E\u5B9F\u884C\u3055\u308C\u307E\u3059\u3002\`jdbc:sqlserver://\` \u304A\u3088\u3073 \`jdbc:oracle:thin:\` \u30EB\u30FC\u30C8\u3067\u306F\u3001\u30B3\u30CD\u30AF\u30BF\u304C \`query()\` \u5185\u3067\u3053\u306E\u30E9\u30C3\u30D1\u3092\u691C\u51FA\u3057\u3001\`<inner>\` \u3092\u65B9\u8A00\u56FA\u6709\u306E\u30C9\u30E9\u30A4\u30D0\u3067\u76F4\u63A5\u5B9F\u884C\u3057\u3066 JS \u5074\u3067\u5148\u982D N \u884C\u306B\u5207\u308A\u8A70\u3081\u307E\u3059\u3002\u5229\u7528\u8005\u5074\u3067\u5BFE\u51E6\u3059\u308B\u5FC5\u8981\u306F\u3042\u308A\u307E\u305B\u3093\u304C\u3001**\u81EA\u5206\u3067\u66F8\u304F SQL** \u3067\u306F SQL Server / Oracle \u30EB\u30FC\u30C8\u3067 \`LIMIT\` \u3092\u4F7F\u308F\u305A\u3001\u4E0A\u8A18\u306E \`TOP\` / \`FETCH FIRST\` \u3092\u4F7F\u3063\u3066\u304F\u3060\u3055\u3044\u3002`
|
|
20843
21302
|
},
|
|
20844
21303
|
tools,
|
|
20845
21304
|
async checkConnection(params, _config) {
|
|
@@ -20855,6 +21314,20 @@ JDBC URL \u306E\u30D7\u30EC\u30D5\u30A3\u30C3\u30AF\u30B9\u306B\u3088\u308A\u65B
|
|
|
20855
21314
|
error: e instanceof Error ? e.message : String(e)
|
|
20856
21315
|
};
|
|
20857
21316
|
}
|
|
21317
|
+
if (parsed.driver === "sqlserver") {
|
|
21318
|
+
return checkMssqlConnection(
|
|
21319
|
+
parsed.originalUrl,
|
|
21320
|
+
{ username, password },
|
|
21321
|
+
{ tunnelParams: params }
|
|
21322
|
+
);
|
|
21323
|
+
}
|
|
21324
|
+
if (parsed.driver === "oracle") {
|
|
21325
|
+
return checkOracleConnection(
|
|
21326
|
+
parsed.originalUrl,
|
|
21327
|
+
{ username, password },
|
|
21328
|
+
{ tunnelParams: params }
|
|
21329
|
+
);
|
|
21330
|
+
}
|
|
20858
21331
|
const tunnel = await maybeOpenSshTunnel(
|
|
20859
21332
|
params,
|
|
20860
21333
|
parsed.nativeUrl,
|
|
@@ -20864,7 +21337,7 @@ JDBC URL \u306E\u30D7\u30EC\u30D5\u30A3\u30C3\u30AF\u30B9\u306B\u3088\u308A\u65B
|
|
|
20864
21337
|
const { Pool } = await import("pg");
|
|
20865
21338
|
const pool2 = new Pool({
|
|
20866
21339
|
connectionString: tunnel.connectionUrl,
|
|
20867
|
-
ssl: { rejectUnauthorized: false },
|
|
21340
|
+
ssl: parsed.forceSsl ? { rejectUnauthorized: false } : { rejectUnauthorized: false },
|
|
20868
21341
|
connectionTimeoutMillis: 1e4
|
|
20869
21342
|
});
|
|
20870
21343
|
try {
|
|
@@ -20903,6 +21376,36 @@ JDBC URL \u306E\u30D7\u30EC\u30D5\u30A3\u30C3\u30AF\u30B9\u306B\u3088\u308A\u65B
|
|
|
20903
21376
|
const username = params[parameters.username.slug];
|
|
20904
21377
|
const password = params[parameters.password.slug];
|
|
20905
21378
|
const parsed = parseJdbcUrl(jdbcUrl, { username, password });
|
|
21379
|
+
if (parsed.driver === "sqlserver") {
|
|
21380
|
+
const mssqlParsed = parseSqlServerJdbcUrl(parsed.originalUrl, {
|
|
21381
|
+
username,
|
|
21382
|
+
password
|
|
21383
|
+
});
|
|
21384
|
+
const sample = unwrapSampleLimit(sql);
|
|
21385
|
+
if (sample) {
|
|
21386
|
+
const result = await runMssqlQuery(mssqlParsed, sample.inner, {
|
|
21387
|
+
tunnelParams: params
|
|
21388
|
+
});
|
|
21389
|
+
return { rows: result.rows.slice(0, sample.limit) };
|
|
21390
|
+
}
|
|
21391
|
+
return runMssqlQuery(mssqlParsed, sql, { tunnelParams: params });
|
|
21392
|
+
}
|
|
21393
|
+
if (parsed.driver === "oracle") {
|
|
21394
|
+
const oracleParsed = parseOracleJdbcUrl(parsed.originalUrl, {
|
|
21395
|
+
username,
|
|
21396
|
+
password
|
|
21397
|
+
});
|
|
21398
|
+
const sample = unwrapSampleLimit(sql);
|
|
21399
|
+
if (sample) {
|
|
21400
|
+
const inner = sample.inner.replace(/;\s*$/, "");
|
|
21401
|
+
const result = await runOracleQuery(oracleParsed, inner, {
|
|
21402
|
+
tunnelParams: params
|
|
21403
|
+
});
|
|
21404
|
+
return { rows: result.rows.slice(0, sample.limit) };
|
|
21405
|
+
}
|
|
21406
|
+
const cleanSql = sql.replace(/;\s*$/, "");
|
|
21407
|
+
return runOracleQuery(oracleParsed, cleanSql, { tunnelParams: params });
|
|
21408
|
+
}
|
|
20906
21409
|
const tunnel = await maybeOpenSshTunnel(
|
|
20907
21410
|
params,
|
|
20908
21411
|
parsed.nativeUrl,
|
|
@@ -20913,7 +21416,7 @@ JDBC URL \u306E\u30D7\u30EC\u30D5\u30A3\u30C3\u30AF\u30B9\u306B\u3088\u308A\u65B
|
|
|
20913
21416
|
const cleanSql = sql.replace(/;\s*$/, "");
|
|
20914
21417
|
const pool2 = new Pool({
|
|
20915
21418
|
connectionString: tunnel.connectionUrl,
|
|
20916
|
-
ssl: { rejectUnauthorized: false },
|
|
21419
|
+
ssl: parsed.forceSsl ? { rejectUnauthorized: false } : { rejectUnauthorized: false },
|
|
20917
21420
|
connectionTimeoutMillis: 1e4,
|
|
20918
21421
|
statement_timeout: 6e4
|
|
20919
21422
|
});
|