From e34ba78ac2e8ac61f5e8aada8d2ab9746a080d8f Mon Sep 17 00:00:00 2001 From: Claude Bot Date: Fri, 29 Aug 2025 22:33:15 +0000 Subject: [PATCH] Fix SQL options precedence: explicit options override URL parameters MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 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 --- src/js/internal/sql/shared.ts | 14 +- .../js/sql/adapter-env-var-precedence.test.ts | 166 ++++++++++++++++++ 2 files changed, 173 insertions(+), 7 deletions(-) diff --git a/src/js/internal/sql/shared.ts b/src/js/internal/sql/shared.ts index aea0dc590f..dc226a2129 100644 --- a/src/js/internal/sql/shared.ts +++ b/src/js/internal/sql/shared.ts @@ -657,13 +657,13 @@ function normalizeOptionsForAdapter( } } - // Apply explicit options (highest precedence) - hostname ||= options.hostname || options.host; - port ||= options.port; - username ||= options.username || options.user; - password ||= options.password || options.pass; - database ||= options.database || options.db; - path ||= (options as { path?: string }).path; + // Apply explicit options (highest precedence) - they override URL parameters + hostname = options.hostname || options.host || hostname; + port = options.port || port; + username = options.username || options.user || username; + password = options.password || options.pass || password; + database = options.database || options.db || database; + path = (options as { path?: string }).path || path; // Apply adapter-specific environment defaults (medium precedence) hostname ||= envDefaults.hostname; diff --git a/test/js/sql/adapter-env-var-precedence.test.ts b/test/js/sql/adapter-env-var-precedence.test.ts index 338e5ecf91..99c2e7c9f1 100644 --- a/test/js/sql/adapter-env-var-precedence.test.ts +++ b/test/js/sql/adapter-env-var-precedence.test.ts @@ -397,5 +397,171 @@ describe("SQL adapter environment variable precedence", () => { expect(options.options.filename).toBe("/tmp/test.db"); restoreEnv(); }); + + describe("Explicit options override URL parameters", () => { + test("explicit hostname should override URL hostname", () => { + cleanEnv(); + + const options = new SQL("postgres://urluser:urlpass@urlhost:1234/urldb", { + hostname: "explicithost", + }); + + expect(options.options.hostname).toBe("explicithost"); + expect(options.options.port).toBe(1234); // URL port should remain + expect(options.options.username).toBe("urluser"); // URL username should remain + expect(options.options.database).toBe("urldb"); // URL database should remain + restoreEnv(); + }); + + test("explicit port should override URL port", () => { + cleanEnv(); + + const options = new SQL("postgres://urluser:urlpass@urlhost:1234/urldb", { + port: 5432, + }); + + expect(options.options.hostname).toBe("urlhost"); // URL hostname should remain + expect(options.options.port).toBe(5432); + expect(options.options.username).toBe("urluser"); // URL username should remain + expect(options.options.database).toBe("urldb"); // URL database should remain + restoreEnv(); + }); + + test("explicit username should override URL username", () => { + cleanEnv(); + + const options = new SQL("postgres://urluser:urlpass@urlhost:1234/urldb", { + username: "explicituser", + }); + + expect(options.options.hostname).toBe("urlhost"); // URL hostname should remain + expect(options.options.port).toBe(1234); // URL port should remain + expect(options.options.username).toBe("explicituser"); + expect(options.options.database).toBe("urldb"); // URL database should remain + restoreEnv(); + }); + + test("explicit password should override URL password", () => { + cleanEnv(); + + const options = new SQL("postgres://urluser:urlpass@urlhost:1234/urldb", { + password: "explicitpass", + }); + + expect(options.options.hostname).toBe("urlhost"); // URL hostname should remain + expect(options.options.port).toBe(1234); // URL port should remain + expect(options.options.username).toBe("urluser"); // URL username should remain + expect(options.options.password).toBe("explicitpass"); + expect(options.options.database).toBe("urldb"); // URL database should remain + restoreEnv(); + }); + + test("explicit database should override URL database", () => { + cleanEnv(); + + const options = new SQL("postgres://urluser:urlpass@urlhost:1234/urldb", { + database: "explicitdb", + }); + + expect(options.options.hostname).toBe("urlhost"); // URL hostname should remain + expect(options.options.port).toBe(1234); // URL port should remain + expect(options.options.username).toBe("urluser"); // URL username should remain + expect(options.options.database).toBe("explicitdb"); + restoreEnv(); + }); + + test("multiple explicit options should override corresponding URL parameters", () => { + cleanEnv(); + + const options = new SQL("postgres://urluser:urlpass@urlhost:1234/urldb", { + hostname: "explicithost", + port: 5432, + username: "explicituser", + password: "explicitpass", + database: "explicitdb", + }); + + expect(options.options.hostname).toBe("explicithost"); + expect(options.options.port).toBe(5432); + expect(options.options.username).toBe("explicituser"); + expect(options.options.password).toBe("explicitpass"); + expect(options.options.database).toBe("explicitdb"); + restoreEnv(); + }); + + test("should work with MySQL URLs and explicit options", () => { + cleanEnv(); + + const options = new SQL("mysql://urluser:urlpass@urlhost:3306/urldb", { + hostname: "explicithost", + port: 3307, + username: "explicituser", + }); + + expect(options.options.adapter).toBe("mysql"); + expect(options.options.hostname).toBe("explicithost"); + expect(options.options.port).toBe(3307); + expect(options.options.username).toBe("explicituser"); + expect(options.options.password).toBe("urlpass"); // URL password should remain + expect(options.options.database).toBe("urldb"); // URL database should remain + restoreEnv(); + }); + + test("should work with alternative option names (user, pass, db, host)", () => { + cleanEnv(); + + const options = new SQL("postgres://urluser:urlpass@urlhost:1234/urldb", { + host: "explicithost", + user: "explicituser", + pass: "explicitpass", + db: "explicitdb", + }); + + expect(options.options.hostname).toBe("explicithost"); + expect(options.options.username).toBe("explicituser"); + expect(options.options.password).toBe("explicitpass"); + expect(options.options.database).toBe("explicitdb"); + restoreEnv(); + }); + + test("explicit options should override URL even when environment variables are present", () => { + cleanEnv(); + process.env.PGHOST = "envhost"; + process.env.PGPORT = "9999"; + process.env.PGUSER = "envuser"; + + const options = new SQL("postgres://urluser:urlpass@urlhost:1234/urldb", { + hostname: "explicithost", + port: 5432, + username: "explicituser", + }); + + expect(options.options.hostname).toBe("explicithost"); + expect(options.options.port).toBe(5432); + expect(options.options.username).toBe("explicituser"); + expect(options.options.password).toBe("urlpass"); // URL password should remain since no explicit password + expect(options.options.database).toBe("urldb"); // URL database should remain + restoreEnv(); + }); + + test("explicit options should have higher precedence than environment-specific variables", () => { + cleanEnv(); + process.env.MYSQL_HOST = "mysqlhost"; + process.env.MYSQL_USER = "mysqluser"; + process.env.MYSQL_PASSWORD = "mysqlpass"; + + const options = new SQL("mysql://urluser:urlpass@urlhost:3306/urldb", { + hostname: "explicithost", + username: "explicituser", + }); + + expect(options.options.adapter).toBe("mysql"); + expect(options.options.hostname).toBe("explicithost"); + expect(options.options.username).toBe("explicituser"); + expect(options.options.password).toBe("urlpass"); // URL password (not env) + expect(options.options.database).toBe("urldb"); // URL database should remain + restoreEnv(); + }); + }); }); });