Repository navigation
Fix: Make SQL connection string parsing more sensible - #22260
Conversation
…nd prevent env variable mixing This commit refactors Bun.SQL's options parsing to fix issue #22147 and properly separate PostgreSQL and MySQL environment variables based on the chosen adapter. Key changes: 1. Separate adapter detection from options parsing 2. Only read environment variables appropriate for the selected adapter 3. Respect proper precedence: explicit options > adapter-specific env vars > generic env vars 4. Add support for unix:// protocol with explicit adapter specification 5. Validate adapter-protocol compatibility Adapter detection logic: - If adapter is explicitly set: use it - If adapter is set to "mysql": use mysql adapter - If adapter is set to "postgres"/"postgresql": use postgres adapter - If protocol is mysql/mariadb/mysqls: use mysql adapter - If protocol is postgres/postgresql: use postgres adapter - If no protocol in URL: default to postgres adapter - For unix:// protocol: require explicit adapter Environment variable precedence by adapter: - PostgreSQL: Only reads PGHOST, PGUSER, PGPASSWORD, etc. + generic USER/USERNAME - MySQL: Only reads generic USER/USERNAME (no widely standardized MySQL env vars) - SQLite: Only reads DATABASE_URL for SQLite URLs This prevents mixing of PostgreSQL env vars when using MySQL adapter and vice versa, while ensuring explicit connection options always take precedence over environment variables. Fixes: #22147 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
The previous implementation incorrectly checked if MYSQL_URL was a SQLite URL using parseDefinitelySqliteUrl(). This fix properly checks the protocol of environment URLs to determine the correct adapter: - mysql://, mariadb://, mysqls:// → mysql adapter - postgres://, postgresql:// → postgres adapter - SQLite URLs (detected by parseDefinitelySqliteUrl) → sqlite adapter Added tests to verify MYSQL_URL and POSTGRES_URL environment variables correctly infer their respective adapters and that precedence is respected. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
Added tests to verify MySQL port defaulting behavior: - MySQL URL without port defaults to 3306 - Explicit MySQL adapter defaults to 3306 - Both URL and explicit adapter scenarios work correctly This documents the expected behavior and ensures proper port handling across all MySQL connection scenarios. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
…issing Fixed bug where MYSQL_URL=root@localhost:3306/test would incorrectly be treated as postgres instead of mysql due to missing protocol detection. Now properly infers adapter from environment variable names: - MYSQL_URL → mysql adapter (even without mysql:// protocol) - POSTGRES_URL, PGURL, PG_URL → postgres adapter (even without postgres:// protocol) - Protocol detection still takes precedence when present - Maintains proper precedence order across environment variables Added comprehensive tests to verify: - MYSQL_URL without protocol correctly infers mysql - POSTGRES_URL without protocol correctly infers postgres - All existing functionality continues to work This ensures that environment variable names provide semantic hints about the intended database adapter even when URLs lack explicit protocols. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
…le names over protocols Fixed logic where PGURL="mysql://" would incorrectly return mysql adapter instead of postgres. Environment variable names now take semantic precedence over URL protocols: - MYSQL_URL=postgres://host → mysql adapter (name wins) - PGURL=mysql://host → postgres adapter (name wins) - DATABASE_URL=mysql://host → mysql adapter (protocol detection as fallback) This is more intuitive and matches user expectations - if you set PGURL, you clearly intend PostgreSQL regardless of what protocol might be in the URL value. Simplified logic: 1. Explicit adapter (highest priority) 2. Environment variable name (semantic intent) 3. Protocol detection (fallback for DATABASE_URL only) 4. Default to postgres Added comprehensive tests to verify environment variable names override protocols in all scenarios. All 236 existing tests continue to pass. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
Added MySQL-specific environment variables with proper precedence: **New MySQL Environment Variables:** - `MYSQL_HOST` (defaults to `localhost`) - `MYSQL_PORT` (defaults to `3306`) - `MYSQL_USER` (with fallback to `$USER`) - `MYSQL_PASSWORD` (defaults to empty) - `MYSQL_DATABASE` (defaults to `mysql`) - `MYSQL_URL` (primary connection URL) - `TLS_MYSQL_DATABASE_URL` (SSL/TLS connection URL) **Enhanced TLS Support:** - Added `TLS_MYSQL_DATABASE_URL` for MySQL TLS connections - Enhanced `TLS_DATABASE_URL` to work with MySQL protocol detection - All TLS URLs automatically enable SSL mode **Environment Variable Precedence:** - MySQL-specific env vars (e.g., `$MYSQL_USER`) override generic ones (e.g., `$USER`) - Environment variable names override URL protocols for semantic intent - Proper URL precedence: `MYSQL_URL` > `DATABASE_URL` > `TLS_MYSQL_DATABASE_URL` > `TLS_DATABASE_URL` **Comprehensive Testing:** - 23 adapter precedence tests covering all MySQL env vars - Tests for TLS URL handling and protocol detection - Tests for env var name precedence over protocols - All existing tests (241 total) continue to pass This brings MySQL environment variable support in line with PostgreSQL, providing a complete and intuitive configuration experience. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
Added assertions to verify that TLS environment variables correctly set sslMode to SSLMode.require (value 2): - TLS_MYSQL_DATABASE_URL → sslMode = 2 - TLS_POSTGRES_DATABASE_URL → sslMode = 2 - TLS_DATABASE_URL → sslMode = 2 This ensures TLS URLs automatically enable SSL/TLS mode as expected. All 23 tests pass with the additional SSL mode checks. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
…ection Added comprehensive tests and validation for adapter-protocol compatibility: **New Test Coverage:** - ✅ Explicit adapter with URL without protocol (should work) - ✅ Explicit adapter with matching protocol (should work) - ✅ Adapter conflicts with protocol (should throw error) - ✅ Unix socket protocol with explicit adapter (should work) - ✅ SQLite protocol with sqlite adapter (should work) **Error Cases Added:** - `mysql` adapter + `postgres://` → throws "Protocol 'postgres' is not compatible with adapter 'mysql'" - `postgres` adapter + `mysql://` → throws "Protocol 'mysql' is not compatible with adapter 'postgres'" - `sqlite` adapter + `mysql://` → throws "Protocol 'mysql' is not compatible with adapter 'sqlite'" - `mysql` adapter + `postgres://` → throws "Protocol 'postgres' is not compatible with adapter 'mysql'" **Implementation Fix:** - Moved adapter-protocol validation earlier in parsing process (Step 2.5) - Now validates all adapters including SQLite before early return - Prevents SQLite adapter bypass of validation logic - Special handling for SQLite URL parsing in validation **Test Results:** - 8 new adapter-protocol validation tests - all pass - 31 total adapter precedence tests - all pass - 218 SQLite tests - all pass (no regressions) This ensures users get clear error messages when mixing incompatible adapters and protocols, improving the developer experience. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
- Separate adapter detection from options parsing - Fix DATABASE_URL precedence over explicit options - Add comprehensive MySQL environment variable support - Add adapter-protocol validation with proper error handling - Ensure all 33 adapter tests and 240+ SQL tests pass 🤖 Generated with Claude Code Co-Authored-By: Claude <noreply@anthropic.com>
- Fix precedence logic so explicit options (hostname, port, username, password, database) now correctly override URL parameters instead of the other way around - Add 10 comprehensive tests verifying explicit options take precedence over URL parameters - Test both PostgreSQL and MySQL adapters with various combinations of explicit options - Test alternative option names (host, user, pass, db) - Ensure explicit options still take precedence even when environment variables are present - All 43 adapter tests and 29 main SQL tests continue to pass 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
|
Updated 9:28 PM PT - Sep 8th, 2025
❌ @alii, your commit 2c85900 has 1 failures in
🧪 To try this PR locally: bunx bun-pr 22260That installs a local version of the PR into your bun-22260 --bun |
There was a problem hiding this comment.
Actionable comments posted: 0
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
src/js/internal/sql/shared.ts (1)
11-15: ReintroduceaffectedRowsor update its usages
SQLResultArray<T>no longer declarespublic affectedRows, yetsrc/js/internal/sql/mysql.tsstill assigns to it (lines 34 & 66) andtest/js/sql/sql-mysql.test.tsdestructures it at line 36.- Either add back
public affectedRows!: number | null;inshared.tsor remove/update these assignments and tests.
🧹 Nitpick comments (1)
test/js/sql/adapter-env-var-precedence.test.ts (1)
121-127: Improve error assertion specificity.The current regex
/mysql.*postgres/iis quite broad and might match unintended error messages. Consider using a more specific error pattern.- }).toThrow(/mysql.*postgres/i); + }).toThrow(/Protocol 'mysql' is not compatible with adapter 'postgres'/);
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (2)
src/js/internal/sql/shared.ts(1 hunks)test/js/sql/adapter-env-var-precedence.test.ts(1 hunks)
🧰 Additional context used
📓 Path-based instructions (8)
**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (CLAUDE.md)
Format JavaScript/TypeScript with Prettier (bun run prettier)
Files:
src/js/internal/sql/shared.tstest/js/sql/adapter-env-var-precedence.test.ts
src/js/**/*.{ts,js}
📄 CodeRabbit inference engine (CLAUDE.md)
src/js/**/*.{ts,js}: In built-in modules, require() must use string literals resolved at compile time
In built-in modules, use export default (converted to a return statement by the preprocessor)
Files:
src/js/internal/sql/shared.ts
test/**/*.test.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
test/**/*.test.{ts,tsx}: Place tests in test/ and ensure filenames end with .test.ts or .test.tsx
Always use port: 0 in tests; never hardcode port numbers or use custom random-port functions
Prefer snapshot tests using normalizeBunSnapshot(...).toMatchInlineSnapshot(...) over exact string comparisons
Never write tests that assert absence of crashes (e.g., no 'panic' or 'uncaught exception' in output)
Avoid shell commands like find/grep in tests; use Bun.Glob and built-in tools instead
Files:
test/js/sql/adapter-env-var-precedence.test.ts
test/**/*.test.ts
📄 CodeRabbit inference engine (test/CLAUDE.md)
test/**/*.test.ts: Use bun:test in files that end in *.test.ts
Prefer async/await over callbacks in tests
For single-callback flows in tests, use Promise.withResolvers()
Do not set timeouts on tests; rely on Bun’s built-in timeouts
Use tempDirWithFiles from harness to create temporary directories/files in tests
Organize tests with describe blocks
Always assert exit codes and error scenarios in tests (e.g., proc.exited, expect(...).not.toBe(0), toThrow())
Use describe.each for parameterized tests; toMatchSnapshot for snapshots; and beforeAll/afterEach/beforeEach for setup/teardown
Avoid flaky tests: never wait for arbitrary time; wait for conditions to be met
Files:
test/js/sql/adapter-env-var-precedence.test.ts
test/{**/*.test.ts,**/*-fixture.ts}
📄 CodeRabbit inference engine (test/CLAUDE.md)
test/{**/*.test.ts,**/*-fixture.ts}: When spawning Bun processes, use bunExe() and bunEnv from harness
Use using or await using for Bun APIs (e.g., Bun.spawn, Bun.listen, Bun.connect, Bun.serve) to ensure cleanup
Never hardcode port numbers; use port: 0 to get a random port
Use Buffer.alloc(count, fill).toString() instead of "A".repeat(count) for large/repetitive strings
Import testing utilities from harness (bunExe, bunEnv, tempDirWithFiles, tmpdirSync, isMacOS, isWindows, isPosix, gcTick, withoutAggressiveGC)
Files:
test/js/sql/adapter-env-var-precedence.test.ts
test/**
📄 CodeRabbit inference engine (.cursor/rules/writing-tests.mdc)
Place all tests under the test/ directory
Files:
test/js/sql/adapter-env-var-precedence.test.ts
test/js/**/*.{js,ts}
📄 CodeRabbit inference engine (.cursor/rules/writing-tests.mdc)
Place JavaScript and TypeScript tests under test/js/
Files:
test/js/sql/adapter-env-var-precedence.test.ts
test/**/*.{js,ts}
📄 CodeRabbit inference engine (.cursor/rules/writing-tests.mdc)
test/**/*.{js,ts}: Write tests in JavaScript or TypeScript using Bun’s Jest-style APIs (test, describe, expect) and run with bun test
Prefer data-driven tests (e.g., test.each) to reduce boilerplate
Use shared utilities from test/harness.ts where applicable
Files:
test/js/sql/adapter-env-var-precedence.test.ts
🧬 Code graph analysis (1)
test/js/sql/adapter-env-var-precedence.test.ts (1)
src/js/internal-for-testing.ts (1)
SQL(17-17)
🔇 Additional comments (11)
src/js/internal/sql/shared.ts (1)
23-27: Verify consistency with constructor initialization.The removal of
affectedRowsfrom the property definitions is consistent - both the public property declaration and its initialization inObject.definePropertieshave been removed. This maintains internal consistency.test/js/sql/adapter-env-var-precedence.test.ts (10)
1-3: LGTM: Proper test setup with required imports.The imports correctly use
bun:testas specified in the coding guidelines for test files ending with.test.ts, and import theSQLclass from the mainbunmodule.
4-36: LGTM: Well-structured test environment management.The test setup properly handles environment variable isolation with snapshot/restore functionality. The
cleanEnv()function comprehensively removes all SQL-related environment variables, andrestoreEnv()ensures proper cleanup.
37-52: LGTM: Correctly validates explicit options precedence.This test properly validates that explicit options take precedence over environment variables, which addresses the issue mentioned in the test name (
#22147). The test correctly verifies thatDATABASE_URLdoesn't override explicitly provided connection parameters.
347-354: LGTM: Comprehensive adapter-protocol validation.This test correctly validates the adapter-protocol mismatch error handling. The error message pattern matches the expected validation from the
parseOptionsfunction inshared.ts.
54-71: LGTM: Proper adapter-specific environment variable handling.These tests correctly validate that:
- PostgreSQL environment variables (
PGHOST,PGUSER, etc.) are only used when the adapter ispostgres- MySQL-specific environment variables take precedence when the adapter is
mysql- Adapter-specific variables have priority over generic ones
The test logic properly isolates environment variables and verifies the expected behavior.
Also applies to: 73-87, 129-140
223-243: LGTM: Environment variable name-based adapter inference.These tests correctly validate that environment variable names can override protocol-based adapter inference (e.g.,
PGURLwithmysql://protocol should result inpostgresadapter). This is an important edge case for configuration flexibility.
401-545: LGTM: Comprehensive explicit options precedence testing.This test section thoroughly validates that explicit options override URL parameters, including:
- Individual parameter overrides (hostname, port, username, password, database)
- Multiple simultaneous overrides
- Alternative option names (
host,user,pass,db)- Precedence over both URL parameters and environment variables
The test coverage is comprehensive and follows the expected precedence hierarchy.
164-175: LGTM: Correct MySQL default port handling.This test properly validates that MySQL adapter defaults to port 3306 when no port is explicitly specified, which matches the expected behavior in the
parseOptionsfunction.
281-288: LGTM: Correct PostgreSQL database naming defaults.This test properly validates that PostgreSQL uses the username as the default database name when no database is specified, which aligns with standard PostgreSQL conventions.
290-300: SSLMode.require value validated TheSSLModeenum insrc/js/internal/sql/shared.tsdefinesrequire = 2, so the test’sexpect(options.options.sslMode).toBe(2)correctly reflectsSSLMode.require.
There was a problem hiding this comment.
Actionable comments posted: 0
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
src/js/internal/sql/shared.ts (1)
475-489: Bug: unix socket path logic fails when port comes from URL (string).
Number.isSafeInteger(port)is false whenportis a string (e.g., fromurl.port), so/.s.PGSQL.${port}is never appended. This regresses socket discovery.Apply:
if (adapter === "postgres") { // add /.s.PGSQL.${port} if the unix domain socket is listening on that path - if (path && Number.isSafeInteger(port) && path?.indexOf("/.s.PGSQL.") === -1) { - const pathWithSocket = `${path}/.s.PGSQL.${port}`; + if (path && path.indexOf("/.s.PGSQL.") === -1) { + const portNum = Number(port ?? 5432); + if (Number.isSafeInteger(portNum)) { + const pathWithSocket = `${path}/.s.PGSQL.${portNum}`; // Only add the path if it actually exists. It would be better to just // always respect whatever the user passes in, but that would technically // be a breakpoint change at this point. - if (require("node:fs").existsSync(pathWithSocket)) { - path = pathWithSocket; - } + if (require("node:fs").existsSync(pathWithSocket)) { + path = pathWithSocket; + } + } } }
🧹 Nitpick comments (6)
src/js/node/fs.ts (1)
536-542: Accept PathLike in existsSync signature
Change the wrapper’s_path: stringto_path: PathLikeand addimport type { PathLike } from 'fs';to match Node.js’s API.test/integration/bun-types/fixture/sql.ts (1)
277-279: Nit: fix typo in comment.“exits” → “exists”.
-expectType(aSqlInstance.options.safeIntegers).is<boolean | undefined>(); // property exits in sqlite options +expectType(aSqlInstance.options.safeIntegers).is<boolean | undefined>(); // property exists in sqlite optionssrc/js/internal/sql/shared.ts (3)
11-28: Removal of SQLResultArray.affectedRows may break consumers.Dropping
affectedRowsis a breaking change for MySQL/MariaDB users who relied on it. If intentional, document in CHANGELOG and ensure typings/tests reflect it; otherwise, consider keeping it as a non-enumerable deprecated field.Object.defineProperties(this, { count: { value: null, writable: true }, command: { value: null, writable: true }, lastInsertRowid: { value: null, writable: true }, + // Keep for back-compat; write-only by adapters if applicable + affectedRows: { value: undefined, writable: true }, });
239-286: Env precedence cleanup and missing alias.
env.PGURLis listed twice.- Consider supporting
POSTGRESQL_URL.- env.POSTGRES_URL || - env.DATABASE_URL || - env.PGURL || - env.PG_URL || - env.PGURL || + env.POSTGRES_URL || + env.POSTGRESQL_URL || + env.PG_URL || + env.PGURL || + env.DATABASE_URL || env.MYSQL_URL || env.SQLITE_URL ||And in the postgres branch:
- url = env.POSTGRES_URL || env.DATABASE_URL || env.PG_URL || env.PGURL || env.PG_URL || null; + url = env.POSTGRES_URL || env.POSTGRESQL_URL || env.DATABASE_URL || env.PG_URL || env.PGURL || null;
350-369: Confirm protocol mapping for http/https/ftp → postgres.This is surprising; verify it matches product requirements and tests. If it’s for cloud proxies, add a brief comment for future readers.
- case "http": - case "https": - case "ftp": + case "http": // e.g. cloud/sql proxy URLs treated as postgres + case "https": + case "ftp":src/js/private.d.ts (1)
31-36: Nit: duplicate "max" in key union.Tiny cleanup only; no behavior change.
-type DefinedPostgresOrMySQLOptions = Define<Bun.SQL.PostgresOrMySQLOptions, "max" | "prepare" | "max"> & { +type DefinedPostgresOrMySQLOptions = Define<Bun.SQL.PostgresOrMySQLOptions, "max" | "prepare"> & {
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (5)
packages/bun-types/sql.d.ts(2 hunks)src/js/internal/sql/shared.ts(10 hunks)src/js/node/fs.ts(1 hunks)src/js/private.d.ts(2 hunks)test/integration/bun-types/fixture/sql.ts(1 hunks)
🧰 Additional context used
📓 Path-based instructions (5)
**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (CLAUDE.md)
Format JavaScript/TypeScript with Prettier (bun run prettier)
Files:
src/js/node/fs.tstest/integration/bun-types/fixture/sql.tssrc/js/private.d.tspackages/bun-types/sql.d.tssrc/js/internal/sql/shared.ts
src/js/**/*.{ts,js}
📄 CodeRabbit inference engine (CLAUDE.md)
src/js/**/*.{ts,js}: In built-in modules, require() must use string literals resolved at compile time
In built-in modules, use export default (converted to a return statement by the preprocessor)
Files:
src/js/node/fs.tssrc/js/private.d.tssrc/js/internal/sql/shared.ts
test/integration/**
📄 CodeRabbit inference engine (test/CLAUDE.md)
Place integration tests under /test/integration/
Files:
test/integration/bun-types/fixture/sql.ts
test/**
📄 CodeRabbit inference engine (.cursor/rules/writing-tests.mdc)
Place all tests under the test/ directory
Files:
test/integration/bun-types/fixture/sql.ts
test/**/*.{js,ts}
📄 CodeRabbit inference engine (.cursor/rules/writing-tests.mdc)
test/**/*.{js,ts}: Write tests in JavaScript or TypeScript using Bun’s Jest-style APIs (test, describe, expect) and run with bun test
Prefer data-driven tests (e.g., test.each) to reduce boilerplate
Use shared utilities from test/harness.ts where applicable
Files:
test/integration/bun-types/fixture/sql.ts
🧬 Code graph analysis (3)
test/integration/bun-types/fixture/sql.ts (1)
test/integration/bun-types/fixture/utilities.ts (1)
expectType(35-37)
packages/bun-types/sql.d.ts (1)
src/js/internal-for-testing.ts (1)
SQL(17-17)
src/js/internal/sql/shared.ts (2)
src/js/internal/sql/postgres.ts (1)
adapter(452-461)src/js/internal/sql/mysql.ts (1)
adapter(435-444)
🔇 Additional comments (6)
packages/bun-types/sql.d.ts (2)
341-349: Good public alias for merged options.
SQL.MergedOptionsimproves readability and external consumption over the internalDistributedMergetype.
536-536: LGTM: options now typed as SQL.MergedOptions.Matches new ambient type checks in tests.
src/js/internal/sql/shared.ts (3)
597-604: TLS serverName defaulting looks good.Auto-filling
serverNamewhensslmodeisn’t disable matches common PG/MySQL client expectations.
155-160: All call sites updated for new parameter order. No further instances found.
684-686: No action needed: all adapters implementisConnected()andclosed.
PostgresAdapter and MySQLAdapter both define apublic closed: boolean, SQLiteAdapter hasget closed(): boolean, and each adapter definesisConnected().src/js/private.d.ts (1)
15-20: Good: Make Define distributive over unions.This fixes several union-typing edge cases.
There was a problem hiding this comment.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
src/js/internal/sql/shared.ts (1)
146-153: SQLite query params (e.g.?mode=ro) are dropped — functional bug
parseDefinitelySqliteUrl()strips query strings, andparseConnectionDetailsFromOptionsOrEnvironment()stores the stripped path inurl. ThenparseOptions()passes that value intoparseSQLiteOptionsWithQueryParams(), so SQLitemodeis never observed for string URLs likesqlite:file.db?mode=ro.Fix: preserve the original string/URL (with query) for option parsing, and compute
filenameseparately viaparseDefinitelySqliteUrl().@@ - if (definitelySqliteUrl) { - protocol = "sqlite"; - url = definitelySqliteUrl; - } + if (definitelySqliteUrl) { + protocol = "sqlite"; + // keep original for query parsing; derive filename later + url = stringOrUrl!; + } @@ - if (options.adapter === "sqlite") { - return parseSQLiteOptionsWithQueryParams(urlFromConnectionDetails, { - ...options, - adapter: "sqlite", - filename: urlFromConnectionDetails ?? ":memory:", - }); - } + if (options.adapter === "sqlite") { + const filename = + parseDefinitelySqliteUrl(urlFromConnectionDetails) ?? + (typeof urlFromConnectionDetails === "string" ? urlFromConnectionDetails : null) ?? + ":memory:"; + return parseSQLiteOptionsWithQueryParams(urlFromConnectionDetails, { + ...options, + adapter: "sqlite", + filename, + }); + }Also applies to: 335-341, 383-389
🧹 Nitpick comments (4)
src/js/internal/sql/shared.ts (4)
11-28: Public API removal: consider a compatibility shim foraffectedRows
SQLResultArrayno longer exposesaffectedRows. If external code relies on it, this is a breaking change. Consider a non-enumerable, deprecated getter that aliases tocountto ease migration.class SQLResultArray<T> extends PublicArray<T> { public count!: number | null; public command!: string | null; public lastInsertRowid!: number | bigint | null; + /** @deprecated Use `count` instead. */ + public get affectedRows(): number | null { + return this.count; + }
239-287: Env resolution gaps and duplicates (PGURLrepeated; no MariaDB URLs)
PGURLappears twice; drop the duplicate.- No MariaDB URL support. Add
MARIADB_URLandTLS_MARIADB_DATABASE_URL(and acase "mariadb") to mirror MySQL.- Consider including
MYSQL_PWDas a password fallback for MySQL/MariaDB later inparseOptions(nice-to-have).- env.PGURL || - env.PG_URL || - env.PGURL || - env.MYSQL_URL || + env.PG_URL || + env.PGURL || + env.MYSQL_URL || + env.MARIADB_URL || env.SQLITE_URL || @@ - case "postgres": - url = env.POSTGRES_URL || env.DATABASE_URL || env.PG_URL || env.PGURL || env.PG_URL || null; + case "postgres": + url = env.POSTGRES_URL || env.DATABASE_URL || env.PG_URL || env.PGURL || null; @@ case "mysql": url = env.MYSQL_URL || env.DATABASE_URL || null; @@ return [url, sslMode]; + + case "mariadb": + url = env.MARIADB_URL || env.DATABASE_URL || null; + if (!url) { + url = env.TLS_MARIADB_DATABASE_URL || env.TLS_DATABASE_URL || null; + if (url) sslMode = SSLMode.require; + } + return [url, sslMode];
424-436: Prefer standards-based params extraction overtoJSON()
URLSearchParams#toJSON()isn’t standard across runtimes. UseObject.fromEntries(url.searchParams).- const queryObject = url.searchParams.toJSON(); + const queryObject = Object.fromEntries(url.searchParams);
288-295: Docstring typos and clarityMinor cleanup for readability.
- * url string, that you should continue to use for further options. In some - * cases the it will be a parsed URL instance, and in others a string. This is - * to save unnecessary parses in some caes. The third value is the SSL mode The last value is the options object - * resolved from the possible overloads of the Bun.SQL constructor, it may have modifications + * url string to use for further options. In some cases it will be a parsed URL instance, + * in others a string (to avoid unnecessary parses). The third value is the SSL mode. + * The last value is the options object resolved from the possible Bun.SQL constructor overloads.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (1)
src/js/internal/sql/shared.ts(10 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (CLAUDE.md)
Format JavaScript/TypeScript with Prettier (bun run prettier)
Files:
src/js/internal/sql/shared.ts
src/js/**/*.{ts,js}
📄 CodeRabbit inference engine (CLAUDE.md)
src/js/**/*.{ts,js}: In built-in modules, require() must use string literals resolved at compile time
In built-in modules, use export default (converted to a return statement by the preprocessor)
Files:
src/js/internal/sql/shared.ts
🧬 Code graph analysis (1)
src/js/internal/sql/shared.ts (2)
src/js/internal/sql/postgres.ts (1)
adapter(452-461)src/js/internal/sql/mysql.ts (1)
adapter(435-444)
🔇 Additional comments (3)
src/js/internal/sql/shared.ts (3)
304-313: Confirm options precedence when two options objects are providedMerging with
{ ...stringOrUrlOrOptions, ...definitelyOptionsButMaybeEmpty }gives precedence to the second parameter. If that’s the intended “options override” (constructornew SQL(urlOrOpts, opts)), great—otherwise swap the spread order.
443-477: Host/port defaults per adapter — LGTMFallbacks look correct and align with typical defaults and env names.
601-607: TLS serverName inference — LGTMAuto-populating
serverNamefromhostnamewhensslmodeisn’t disabled is a good default and backwards-compatible.
There was a problem hiding this comment.
Actionable comments posted: 0
🧹 Nitpick comments (3)
src/js/internal/sql/postgres.ts (3)
318-324: Async password providers supported — add a lightweight runtime guardIf a provider resolves to a non-string, Zig will still get coerced via
|| "", but being explicit avoids surprises.Apply:
if (typeof password === "function") { password = password(); } - if (password && $isPromise(password)) { + if (password && $isPromise(password)) { password = await password; } + if (password != null && typeof password !== "string") { + password = String(password); + }
851-902: close() return type clarified; tighten timeout validation to ms limitThe Promise typing and withResolvers() are correct. One small edge case: setTimeout takes a 32-bit signed millisecond delay. Validating seconds against 2^31 can still overflow after multiplying by 1000.
Apply:
- timeout = Number(timeout); - if (timeout > 2 ** 31 || timeout < 0 || timeout !== timeout) { - throw $ERR_INVALID_ARG_VALUE("options.timeout", timeout, "must be a non-negative integer less than 2^31"); - } + timeout = Number(timeout); + const maxSeconds = Math.floor(((2 ** 31) - 1) / 1000); + if (timeout > maxSeconds || timeout < 0 || timeout !== timeout) { + throw $ERR_INVALID_ARG_VALUE( + "options.timeout", + timeout, + `must be a non-negative number ≤ ${maxSeconds} seconds` + ); + }Optional follow-up: confirm call sites now treat close() as async (await or handle the returned promise) where a timeout is passed.
123-131: Nit: parameter name typo “databae”The PostgresDotZig.createConnection interface spells “database” as “databae”. Harmless but distracting; consider fixing for readability.
- databae: string, + database: string,
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (3)
src/bun.js/api/bun/socket/Listener.zig(1 hunks)src/js/internal/sql/mysql.ts(1 hunks)src/js/internal/sql/postgres.ts(9 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
- src/bun.js/api/bun/socket/Listener.zig
- src/js/internal/sql/mysql.ts
🧰 Additional context used
📓 Path-based instructions (2)
src/js/**/*.ts
📄 CodeRabbit inference engine (CLAUDE.md)
src/js/**/*.ts: In built-in modules under src/js, require() must use string literals (resolved at compile time)
In built-in modules, use export default to export the module object (converted to a return statement by the preprocessor)
Use special $-prefixed intrinsics and helpers (e.g., $Array, $newArrayWithSize, $debug, $assert) only within built-in modules
Use process.platform and process.arch for platform detection in built-in modules to enable dead-code elimination
Files:
src/js/internal/sql/postgres.ts
**/*.{ts,tsx,js}
📄 CodeRabbit inference engine (CLAUDE.md)
Run Prettier to format JS/TS files (bun run prettier)
Files:
src/js/internal/sql/postgres.ts
🧠 Learnings (1)
📚 Learning: 2025-09-03T17:10:13.486Z
Learnt from: CR
PR: oven-sh/bun#0
File: test/CLAUDE.md:0-0
Timestamp: 2025-09-03T17:10:13.486Z
Learning: Applies to test/**/*.test.ts : Prefer `async/await` in tests; for a single callback, use `Promise.withResolvers()`
Applied to files:
src/js/internal/sql/postgres.ts
🔇 Additional comments (5)
src/js/internal/sql/postgres.ts (5)
296-314: Type widening for createConnection options looks saneUsing Bun.SQL.__internal.DefinedPostgresOrMySQLOptions here is consistent; the destructured fields are in the shared surface. No functional concerns.
359-361: connectionInfo type broadenedWidening to the unified options type is fine and aligns with the adapter’s usage.
426-431: Constructor signature updatedAccepting the unified options type here matches the internal field and call sites in this file.
510-511: Public connectionInfo type updatedPublic surface matches internals; no issues.
523-527: Adapter constructor accepts unified optionsLooks good; note that code depends on
connectionInfo.maxbeing defined. If callers omit it, pool size becomes 1 due tonew Array(undefined)semantics—likely acceptable, just calling it out.
There was a problem hiding this comment.
Actionable comments posted: 0
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (3)
test/js/sql/sql-mysql.test.ts (3)
55-55: Leak: top-level SQL instance isn’t closed — likely contributor to sporadic segfaults.This instance lives for the whole describe block with no teardown. Ensure it’s ended to avoid dangling sockets/pool state between tests.
Apply this diff right after the declaration:
const sql = new SQL(options); +afterAll(async () => { + await sql.end(); +});
171-178: Avoid while(true) in tests; wait for max_lifetime close event instead.Busy-looping risks hangs/flakes if the close is delayed; rely on onClosePromise to deterministically await closure.
Apply:
- try { - while (true) { - for (let i = 0; i < 100; i++) { - await sql`select SLEEP(1)`; - } - } - } catch (e) { - error = e; - } + // Wait for the pool to enforce max_lifetime and close the connection. + const err = await onClosePromise.promise; + error = err;
897-900: Missing assertion: test returns a tuple instead of asserting result.This test won’t fail if behavior regresses.
- test("Array returns rows as arrays of columns", async () => { - await using sql = new SQL(options); - return [(await sql`select CAST(1 AS SIGNED) as x`.values())[0][0], 1]; - }); + test("Array returns rows as arrays of columns", async () => { + await using sql = new SQL(options); + expect((await sql`select CAST(1 AS SIGNED) as x`.values())[0][0]).toBe(1); + });
🧹 Nitpick comments (3)
test/js/sql/sql-mysql.test.ts (3)
91-112: Make error typing consistent with newer tests (unknown + instanceof).This block still uses any and direct property access; align with the stronger pattern used elsewhere.
- let error: any; + let error: unknown; @@ - expect(error.code).toBe(`ERR_MYSQL_CONNECTION_TIMEOUT`); - expect(error.message).toContain("Connection timeout after 4s"); + expect(error).toBeInstanceOf(SQL.SQLError); + expect(error).toBeInstanceOf(SQL.MySQLError); + expect((error as SQL.MySQLError).code).toBe(`ERR_MYSQL_CONNECTION_TIMEOUT`); + expect((error as SQL.MySQLError).message).toContain("Connection timeout after 4s");
499-505: Remove arbitrary sleep; wait for idle close event to avoid flakiness.Use onclose + Promise.withResolvers instead of Bun.sleep(1100).
- test("Idle timeout retry works", async () => { - await using sql = new SQL({ ...options, idleTimeout: 1 }); - await sql`select 1`; - await Bun.sleep(1100); // 1.1 seconds so it should retry - await sql`select 1`; - expect().pass(); - }); + test("Idle timeout retry works", async () => { + const onClosePromise = Promise.withResolvers(); + const onclose = mock(err => onClosePromise.resolve(err)); + await using sql = new SQL({ ...options, idleTimeout: 1, onclose }); + await sql`select 1`; + await onClosePromise.promise; // deterministically wait for idle close + await sql`select 1`; // should transparently reconnect + expect().pass(); + });
499-505: Minor naming consistency: choose one of idleTimeout vs idle_timeout across the file.Both are supported; using one consistently improves readability.
Also applies to: 55-55
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (3)
packages/bun-types/sql.d.ts(4 hunks)test/js/sql/adapter-env-var-precedence.test.ts(1 hunks)test/js/sql/sql-mysql.test.ts(4 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
- test/js/sql/adapter-env-var-precedence.test.ts
- packages/bun-types/sql.d.ts
🧰 Additional context used
📓 Path-based instructions (7)
test/**
📄 CodeRabbit inference engine (.cursor/rules/writing-tests.mdc)
Place all tests under the test/ directory
Files:
test/js/sql/sql-mysql.test.ts
test/js/**/*.{js,ts}
📄 CodeRabbit inference engine (.cursor/rules/writing-tests.mdc)
Place JavaScript and TypeScript tests under test/js/
Files:
test/js/sql/sql-mysql.test.ts
test/**/*.{js,ts}
📄 CodeRabbit inference engine (.cursor/rules/writing-tests.mdc)
test/**/*.{js,ts}: Write tests in JavaScript or TypeScript using Bun’s Jest-style APIs (test, describe, expect) and run with bun test
Prefer data-driven tests (e.g., test.each) to reduce boilerplate
Use shared utilities from test/harness.ts where applicable
Files:
test/js/sql/sql-mysql.test.ts
test/**/*.test.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
test/**/*.test.{ts,tsx}: Tests must be named with the suffix .test.ts or .test.tsx and live under the test/ directory
In tests, always use port: 0 and never hardcode ports or use custom random-port utilities
Prefer snapshot tests using normalizeBunSnapshot(...) over direct string equality on stdout/stderr
Do not write tests that assert absence of 'panic', 'uncaught exception', or similar strings in output
Avoid shelling out to tools like find or grep in tests; use Bun.Glob and built-in utilities
Files:
test/js/sql/sql-mysql.test.ts
**/*.{ts,tsx,js}
📄 CodeRabbit inference engine (CLAUDE.md)
Run Prettier to format JS/TS files (bun run prettier)
Files:
test/js/sql/sql-mysql.test.ts
test/**/*.test.ts
📄 CodeRabbit inference engine (test/CLAUDE.md)
test/**/*.test.ts: Name test files*.test.tsand usebun:test
Do not write flaky tests: never wait for arbitrary time; wait for conditions instead
Never hardcode port numbers in tests; useport: 0to get a random port
When spawning Bun in tests, usebunExe()andbunEnvfromharness
Preferasync/awaitin tests; for a single callback, usePromise.withResolvers()
Do not set explicit test timeouts; rely on Bun’s built-in timeouts
UsetempDir/tempDirWithFilesfromharnessfor temporary files and directories in tests
For large/repetitive strings in tests, preferBuffer.alloc(count, fill).toString()over"A".repeat(count)
Import common test utilities fromharness(e.g.,bunExe,bunEnv,tempDirWithFiles,tmpdirSync, platform checks, GC helpers)
In error tests, assert non-zero exit codes for failing processes and usetoThrowfor synchronous errors
Usedescribeblocks for grouping,describe.eachfor parameterized tests, snapshots withtoMatchSnapshot, and lifecycle hooks (beforeAll,beforeEach,afterEach); track resources for cleanup inafterEach
Useusing/await usingwith Bun resources (e.g., Bun.listen/connect/spawn/serve) to ensure cleanup in tests
Files:
test/js/sql/sql-mysql.test.ts
test/js/**
📄 CodeRabbit inference engine (test/CLAUDE.md)
Organize unit tests for specific features under
test/js/by module
Files:
test/js/sql/sql-mysql.test.ts
🧬 Code graph analysis (1)
test/js/sql/sql-mysql.test.ts (2)
src/js/internal/sql/mysql.ts (2)
err(347-376)err(378-407)src/js/internal-for-testing.ts (1)
SQL(17-17)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: Format
🔇 Additional comments (4)
test/js/sql/sql-mysql.test.ts (4)
47-55: Typed Options init looks correct (incl. TLS Bun.file).Explicit Bun.SQL.Options and conditional CA file use are good.
146-146: Compile-time shape check on result: LGTM.Using expect(…) enforces row typing without runtime cost.
150-153: Runtime type checks on error: LGTM.Both base and adapter-specific instanceof checks + code read are correct.
167-169: Switch to unknown + typed expect: LGTM.Good move toward safer error typing.
WalkthroughOptionalized many Postgres/MySQL error metadata and restructured error option interfaces; unified Postgres/MySQL option shapes (mariadb alias for mysql); overhauled URL/env parsing and SQLite in-memory handling; added TLS/BunFile support and docs deprecations; multiple test additions/updates and typing tweaks across SQL internals. Changes
Possibly related PRs
Suggested reviewers
✨ Finishing Touches
🧪 Generate unit tests
Comment |
There was a problem hiding this comment.
Actionable comments posted: 0
🧹 Nitpick comments (2)
test/harness.ts (2)
11-11: Unify child_process imports; prefer node: specifier and make ChildProcess a type-only importAvoid mixed module specifiers and reduce runtime bindings.
-import { ChildProcess, execSync, fork } from "child_process"; +import type { ChildProcess } from "node:child_process"; +import { execSync, fork } from "node:child_process";
33-35: Fix glibc detection typing and harden against missing reportUse the correct string shape and optional chaining instead of asserting boolean.
- (process.report.getReport() as { header: { glibcVersionRuntime: boolean } }).header.glibcVersionRuntime + ((process.report as any)?.getReport?.() as { header?: { glibcVersionRuntime?: string } } | undefined)?.header?.glibcVersionRuntime
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (1)
test/harness.ts(2 hunks)
🧰 Additional context used
📓 Path-based instructions (3)
test/**
📄 CodeRabbit inference engine (.cursor/rules/writing-tests.mdc)
Place all tests under the test/ directory
Files:
test/harness.ts
test/**/*.{js,ts}
📄 CodeRabbit inference engine (.cursor/rules/writing-tests.mdc)
test/**/*.{js,ts}: Write tests in JavaScript or TypeScript using Bun’s Jest-style APIs (test, describe, expect) and run with bun test
Prefer data-driven tests (e.g., test.each) to reduce boilerplate
Use shared utilities from test/harness.ts where applicable
Files:
test/harness.ts
**/*.{js,ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
Format JavaScript/TypeScript files with Prettier (bun run prettier)
Files:
test/harness.ts
There was a problem hiding this comment.
Actionable comments posted: 1
♻️ Duplicate comments (2)
src/js/internal/sql/errors.ts (2)
82-101: Fix lint: remove unsafe declaration merging for SQLiteError; declare class field insteadThis triggers Biome/typescript-eslint no-unsafe-declaration-merging and blocks CI. Declare the optional field on the class and drop the merged interface.
-// oxlint-disable-next-line typescript-eslint(no-unsafe-declaration-merging) -interface SQLiteError { - byteOffset?: number | undefined; -} - class SQLiteError extends SQLError implements Bun.SQL.SQLiteError { public readonly code: string; public readonly errno: number; + // Avoid unsafe declaration merging; preserve runtime shape (no initializer emitted) + declare readonly byteOffset?: number | undefined; constructor(message: string, options: SQLiteErrorOptions) {
109-126: Fix lint: remove unsafe declaration merging for MySQLError; declare fields on classSame issue as SQLiteError. Declare optional fields on the class and delete the merged interface.
-// oxlint-disable-next-line typescript-eslint(no-unsafe-declaration-merging) -interface MySQLError { - errno?: number | undefined; - sqlState?: string | undefined; -} +// (removed unsafe declaration merging) class MySQLError extends SQLError implements Bun.SQL.MySQLError { public readonly code: string; + // Avoid unsafe declaration merging; no emitted initializers + declare readonly errno?: number | undefined; + declare readonly sqlState?: string | undefined; constructor(message: string, options: MySQLErrorOptions) {
🧹 Nitpick comments (3)
src/js/internal/sql/errors.ts (1)
28-46: Optional: align PostgresError with the same no-merge patternFor consistency and to avoid future suppressions, declare fields on the class and drop the merged interface.
-// oxlint-disable-next-line typescript-eslint(no-unsafe-declaration-merging) -interface PostgresError { - detail?: string | undefined; - hint?: string | undefined; - severity?: string | undefined; - errno?: string | undefined; - position?: string | undefined; - internalPosition?: string | undefined; - internalQuery?: string | undefined; - where?: string | undefined; - schema?: string | undefined; - table?: string | undefined; - column?: string | undefined; - dataType?: string | undefined; - constraint?: string | undefined; - file?: string | undefined; - line?: string | undefined; - routine?: string | undefined; -} +// (removed unsafe declaration merging) class PostgresError extends SQLError implements Bun.SQL.PostgresError { public readonly code: string; + // Optional properties populated from options + declare readonly detail?: string | undefined; + declare readonly hint?: string | undefined; + declare readonly severity?: string | undefined; + declare readonly errno?: string | undefined; + declare readonly position?: string | undefined; + declare readonly internalPosition?: string | undefined; + declare readonly internalQuery?: string | undefined; + declare readonly where?: string | undefined; + declare readonly schema?: string | undefined; + declare readonly table?: string | undefined; + declare readonly column?: string | undefined; + declare readonly dataType?: string | undefined; + declare readonly constraint?: string | undefined; + declare readonly file?: string | undefined; + declare readonly line?: string | undefined; + declare readonly routine?: string | undefined;Also applies to: 49-56
src/js/internal/sql/mysql.ts (2)
105-116: Typo: databae → database in createConnection signaturePurely cosmetic but confusing.
- databae: string, + database: string,
409-413: Unify constructor type with the rest of the fileEverything else uses DefinedPostgresOrMySQLOptions; constructor still uses DefinedMySQLOptions.
- constructor(connectionInfo: Bun.SQL.__internal.DefinedMySQLOptions, adapter: MySQLAdapter) { + constructor(connectionInfo: Bun.SQL.__internal.DefinedPostgresOrMySQLOptions, adapter: MySQLAdapter) {
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (3)
src/js/internal/sql/errors.ts(4 hunks)src/js/internal/sql/mysql.ts(10 hunks)src/js/internal/sql/postgres.ts(10 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- src/js/internal/sql/postgres.ts
🧰 Additional context used
📓 Path-based instructions (4)
src/js/internal/**/*.{js,ts}
📄 CodeRabbit inference engine (src/js/CLAUDE.md)
Place internal-only modules under
internal/
Files:
src/js/internal/sql/mysql.tssrc/js/internal/sql/errors.ts
src/js/{builtins,node,bun,thirdparty,internal}/**/*.{js,ts}
📄 CodeRabbit inference engine (src/js/CLAUDE.md)
src/js/{builtins,node,bun,thirdparty,internal}/**/*.{js,ts}: Use.$calland.$applyinstead of.callor.applyto avoid user tampering
Use string-literalrequire("...")only (no dynamic or non-literal specifiers)
Author modules as CommonJS-style withrequire(...)and export viaexport default {}(no ESMimport/named exports)
Prefer JSC intrinsics and$-prefixed private APIs for performance and safety (e.g.,$Array,$newArrayWithSize,$putByIdDirectPrivate,$assert,$debug)
Validate function arguments with$isCallableand throw$ERR_INVALID_ARG_TYPEfor invalid callbacks
Use$isObjectand throw appropriate TypeErrors for constructor/initializer inputs that must be objects
Files:
src/js/internal/sql/mysql.tssrc/js/internal/sql/errors.ts
src/js/**/*.{js,ts}
📄 CodeRabbit inference engine (CLAUDE.md)
src/js/**/*.{js,ts}: In built-in modules (src/js), require() must use string literals resolved at compile time
In built-in modules (src/js), use export default (converted to a return statement by the preprocessor)
Files:
src/js/internal/sql/mysql.tssrc/js/internal/sql/errors.ts
**/*.{js,ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
Format JavaScript/TypeScript files with Prettier (bun run prettier)
Files:
src/js/internal/sql/mysql.tssrc/js/internal/sql/errors.ts
🧬 Code graph analysis (2)
src/js/internal/sql/mysql.ts (1)
src/js/internal/sql/postgres.ts (4)
err(364-393)err(395-424)connection(433-439)onClose(441-443)
src/js/internal/sql/errors.ts (1)
packages/bun-types/sqlite.d.ts (1)
SQLiteError(1273-1303)
🪛 Biome (2.1.2)
src/js/internal/sql/errors.ts
[error] 87-87: This class is unsafely merged with an interface.
The interface is declared here.
The TypeScript compiler doesn't check whether properties defined in the interface are initialized in the class.
(lint/suspicious/noUnsafeDeclarationMerging)
[error] 115-115: This class is unsafely merged with an interface.
The interface is declared here.
The TypeScript compiler doesn't check whether properties defined in the interface are initialized in the class.
(lint/suspicious/noUnsafeDeclarationMerging)
🔇 Additional comments (4)
src/js/internal/sql/errors.ts (1)
13-13: Verify Postgres errno typePostgresErrorOptions.errno is typed as string. Confirm this matches the Zig/pg source (Postgres typically exposes code as string, but “errno” is not standard in PG).
src/js/internal/sql/mysql.ts (3)
112-112: Confirm Bun.BunFile is supported for tls in Zig bindingEnsure the Zig side handles BunFile (path/FD) and that downstream conversion matches TLSOptions semantics.
846-846: LGTM: typed Promise.withResolvers usageThe explicit void generic fixes inference edge-cases.
Also applies to: 870-870
1182-1183: LGTM: export value SQLCommand in default exportKeeps runtime access while retaining type export above.
There was a problem hiding this comment.
Actionable comments posted: 0
🧹 Nitpick comments (5)
test/js/sql/tls-sql.test.ts (5)
37-40: Prefer marking the suite as skipped instead of logging; avoid printing option blobsUse a skipped test to surface the reason in runner output and keep CI logs clean.
- if (options.url === undefined) { - console.log("SKIPPING TEST", JSON.stringify(options), "BECAUSE MISSING THE URL SECRET"); - continue; - } + if (!options.url) { + test.todo(`TLS SQL suite skipped: missing URL secret for ${options.adapter}`); + continue; + }
202-209: Tighten assertion: check error class before reading errnoMakes the expectation clearer and guards against unexpected shapes.
- expect( - await sql - .begin(sql => [sql`select wat`, sql`select current_setting('bun_sql.test') as x, ${1} as a`]) - .catch(e => e.errno), - ).toBe("42703"); + const err = await sql + .begin(sql => [sql`select wat`, sql`select current_setting('bun_sql.test') as x, ${1} as a`]) + .catch(e => e); + expect(err).toBeInstanceOf(SQL.SQLError); + expect(err).toBeInstanceOf(SQL.PostgresError); + expect(err.errno).toBe("42703");
75-82: Remove redundant close when usingawait using
await usingwill dispose automatically; explicitclose()is unnecessary.await using sql = new SQL(options); const [{ one, two }] = await sql`SELECT 1 as one, '2' as two`; expect(one).toBe(1); expect(two).toBe("2"); - await sql.close();
49-56: Deduplicate expectationsThese two lines are repeated above; keep a single assertion each.
- expect(sql.distributed).toBeDefined(); - expect(sql.unsafe).toBeDefined();
65-72: Deduplicate expectations (postgres variant)Same duplicates here; remove the second pair.
- expect(postgres.distributed).toBeDefined(); - expect(postgres.unsafe).toBeDefined();
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (1)
test/js/sql/tls-sql.test.ts(2 hunks)
🧰 Additional context used
📓 Path-based instructions (7)
test/**
📄 CodeRabbit inference engine (.cursor/rules/writing-tests.mdc)
Place all tests under the test/ directory
Files:
test/js/sql/tls-sql.test.ts
test/js/**/*.{js,ts}
📄 CodeRabbit inference engine (.cursor/rules/writing-tests.mdc)
Place JavaScript and TypeScript tests under test/js/
Files:
test/js/sql/tls-sql.test.ts
test/**/*.{js,ts}
📄 CodeRabbit inference engine (.cursor/rules/writing-tests.mdc)
test/**/*.{js,ts}: Write tests in JavaScript or TypeScript using Bun’s Jest-style APIs (test, describe, expect) and run with bun test
Prefer data-driven tests (e.g., test.each) to reduce boilerplate
Use shared utilities from test/harness.ts where applicable
Files:
test/js/sql/tls-sql.test.ts
test/**/*.test.ts
📄 CodeRabbit inference engine (test/CLAUDE.md)
test/**/*.test.ts: Name test files*.test.tsand usebun:test
Do not write flaky tests: never wait for arbitrary time; wait for conditions instead
Never hardcode port numbers in tests; useport: 0to get a random port
When spawning Bun in tests, usebunExe()andbunEnvfromharness
Preferasync/awaitin tests; for a single callback, usePromise.withResolvers()
Do not set explicit test timeouts; rely on Bun’s built-in timeouts
UsetempDir/tempDirWithFilesfromharnessfor temporary files and directories in tests
For large/repetitive strings in tests, preferBuffer.alloc(count, fill).toString()over"A".repeat(count)
Import common test utilities fromharness(e.g.,bunExe,bunEnv,tempDirWithFiles,tmpdirSync, platform checks, GC helpers)
In error tests, assert non-zero exit codes for failing processes and usetoThrowfor synchronous errors
Usedescribeblocks for grouping,describe.eachfor parameterized tests, snapshots withtoMatchSnapshot, and lifecycle hooks (beforeAll,beforeEach,afterEach); track resources for cleanup inafterEach
Useusing/await usingwith Bun resources (e.g., Bun.listen/connect/spawn/serve) to ensure cleanup in tests
Files:
test/js/sql/tls-sql.test.ts
test/js/**
📄 CodeRabbit inference engine (test/CLAUDE.md)
Organize unit tests for specific features under
test/js/by module
Files:
test/js/sql/tls-sql.test.ts
test/**/*.test.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
test/**/*.test.{ts,tsx}: Test files must be placed under test/ and end with .test.ts or .test.tsx
In tests, always use port: 0; do not hardcode ports or use custom random port functions
In tests, use normalizeBunSnapshot when asserting snapshots
Never write tests that merely assert absence of "panic" or "uncaught exception" in output
Avoid shell commands (e.g., find, grep) in tests; use Bun.Glob and built-ins instead
Prefer snapshot tests over exact stdout equality assertions
Files:
test/js/sql/tls-sql.test.ts
**/*.{js,ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
Format JavaScript/TypeScript files with Prettier (bun run prettier)
Files:
test/js/sql/tls-sql.test.ts
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: Format
| }); | ||
|
|
||
| try { | ||
| const stat = require("node:fs").statSync("/tmp/mysql.sock"); |
There was a problem hiding this comment.
| const stat = require("node:fs").statSync("/tmp/mysql.sock"); |
| } | ||
|
|
||
| try { | ||
| const file = await Bun.file("/tmp/mysql.sock").arrayBuffer(); |
There was a problem hiding this comment.
| const file = await Bun.file("/tmp/mysql.sock").arrayBuffer(); |
This PR makes connection string parsing more sensible in Bun.SQL, without breaking the default fallback of postgres
Added some tests checking for connection string precedence