@squadbase/vite-server 0.1.9-dev.a57a0ac → 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.
@@ -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 maybeOpenSshTunnel(params, connectionUrl, defaultDbPort) {
20323
+ async function maybeOpenSshTunnelHostPort(params, dbHost, dbPort) {
20318
20324
  const sshHost = params[sshTunnelParameters.sshHost.slug];
20319
- if (!sshHost) return NOOP_TUNNEL(connectionUrl);
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
- connectionUrl: url.toString(),
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 JDBC_PREFIX_RE = /^jdbc:(postgresql|postgres|mysql|mariadb):\/\//i;
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
- const match = JDBC_PREFIX_RE.exec(trimmed);
20422
- if (!match) {
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
- tunnel = await maybeOpenSshTunnel(
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
- if (parsed.driver === "postgresql") {
20463
- const { Pool } = await import("pg");
20464
- const pool2 = new Pool({
20465
- connectionString: tunnel.connectionUrl,
20466
- ssl: { rejectUnauthorized: false },
20467
- connectionTimeoutMillis: 1e4,
20468
- statement_timeout: 6e4
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 result = await pool2.query(sql, queryParams);
20472
- return result.rows;
20860
+ const [rows] = await pool.query(sql, queryParams);
20861
+ return Array.isArray(rows) ? rows : [];
20473
20862
  } finally {
20474
- await pool2.end();
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 pool.end();
20866
+ await tunnel.close();
20487
20867
  }
20488
20868
  } catch (err) {
20489
20869
  let msg = err instanceof Error ? err.message : String(err);
20490
- msg = msg.replaceAll(parsed.nativeUrl, redactJdbcUrl(jdbcUrl));
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 or MySQL/MariaDB). Always include LIMIT."
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\`, data sampling, analytical queries.
20700
- The connector dispatches by JDBC URL prefix to the matching driver (PostgreSQL or MySQL/MariaDB),
20701
- so use the dialect that matches the connection. Always include LIMIT.`,
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
- tunnel = await maybeOpenSshTunnel(
20730
- connectionParamsToRecord(connection2),
20731
- parsed.nativeUrl,
20732
- parsed.defaultPort
20733
- );
20734
- if (parsed.driver === "postgresql") {
20735
- const { Pool } = await import("pg");
20736
- const pool2 = new Pool({
20737
- connectionString: tunnel.connectionUrl,
20738
- ssl: { rejectUnauthorized: false },
20739
- connectionTimeoutMillis: CONNECT_TIMEOUT_MS,
20740
- statement_timeout: STATEMENT_TIMEOUT_MS
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 result = await pool2.query(sql);
20744
- const rows = result.rows;
20745
- const truncated = rows.length > MAX_ROWS;
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(rows.length, MAX_ROWS),
21188
+ rowCount: Math.min(resultRows.length, MAX_ROWS),
20749
21189
  truncated,
20750
- rows: rows.slice(0, MAX_ROWS)
21190
+ rows: resultRows.slice(0, MAX_ROWS)
20751
21191
  };
20752
21192
  } finally {
20753
- await pool2.end();
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 pool.end();
21196
+ await tunnel?.close();
20780
21197
  }
20781
21198
  } catch (err) {
20782
21199
  let msg = err instanceof Error ? err.message : String(err);
20783
- msg = msg.replaceAll(parsed.nativeUrl, redactJdbcUrl(jdbcUrl));
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, MariaDB).",
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: false, prod: false },
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 (PostgreSQL or MySQL/MariaDB), so use the dialect that matches the connection. Use this for schema exploration via \`information_schema\` and for sampling data. See the SQL Reference below.
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 dialect
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:mysql://...\` / \`jdbc:mariadb://...\` \u2192 MySQL dialect
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
- - Always include LIMIT in queries`,
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\u30C9\u30E9\u30A4\u30D0\uFF08PostgreSQL \u307E\u305F\u306F MySQL/MariaDB\uFF09\u306B\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\` \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
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 \u65B9\u8A00
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:mysql://...\` / \`jdbc:mariadb://...\` \u2192 MySQL \u65B9\u8A00
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
- - \u30AF\u30A8\u30EA\u306B\u306F\u5FC5\u305A LIMIT \u3092\u542B\u3081\u3066\u304F\u3060\u3055\u3044`
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
  });