Compare commits

...

4 Commits

Author SHA1 Message Date
Claude Bot
0ef4538339 Refactor test to use Docker container with Unix socket proxy
Updates the test to use describeWithContainer with mysql_plain image
and UnixDomainSocketProxy to work in CI environments where Docker is
available but local Unix sockets are not.

The test now:
- Only runs when Docker is enabled (via isDockerEnabled check)
- Creates a Unix socket proxy that forwards to the MySQL container
- Tests all three connection formats:
  1. Standard format with localhost
  2. Shorthand format without hostname (issue #23954)
  3. Options object with path property
2025-10-22 18:55:42 +00:00
autofix-ci[bot]
7c04111cfd [autofix.ci] apply automated fixes 2025-10-22 09:36:34 +00:00
Claude Bot
daf428a344 Update docs to show standard MySQL socket URL format
Show the standard format with localhost as the primary example,
which matches how other MySQL libraries (Prisma, mysql2) document
socket connections. The format without hostname is still supported
but shown as an alternative.

The hostname is ignored when a socket parameter is provided, but
using localhost makes the URL format valid and more standard.
2025-10-22 09:33:32 +00:00
Claude Bot
5ba94141e8 Fix MySQL Unix socket connections with socket query parameter
This fixes issue #23954 where MySQL connections using Unix sockets
failed when using the documented URL format.

Changes:
1. Handle URL format `mysql://user:pass@/database?socket=/path` by
   replacing the missing hostname with localhost before URL parsing
2. Support `socket` query parameter in addition to `path` for Unix
   socket connections (matching documented behavior)

The fix allows both formats to work:
- `mysql://user:pass@localhost/db?socket=/path` (already worked)
- `mysql://user:pass@/db?socket=/path` (now works, matches docs)

Fixes #23954

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-22 09:13:49 +00:00
3 changed files with 97 additions and 4 deletions

View File

@@ -126,7 +126,11 @@ new SQL("mysql2://user:pass@localhost:3306/database");
// With query parameters
new SQL("mysql://user:pass@localhost/db?ssl=true");
// Unix socket connection
// Unix socket connection (hostname is ignored when socket is provided)
new SQL(
"mysql://user:pass@localhost/database?socket=/var/run/mysqld/mysqld.sock",
);
// Hostname can also be omitted:
new SQL("mysql://user:pass@/database?socket=/var/run/mysqld/mysqld.sock");
```

View File

@@ -464,8 +464,16 @@ function parseConnectionDetailsFromOptionsOrEnvironment(
);
}
// unrelated error to do with url parsing, we should re-throw. This is a real user error
throw e;
// Handle special case: mysql://user:pass@/database?socket=/path
// This is a common pattern for Unix socket connections where hostname is omitted
if (typeof urlToProcess === "string" && /@\//.test(urlToProcess)) {
// Replace @/ with @localhost/ to make it a valid URL
urlToProcess = new URL(urlToProcess.replace(/@\//, "@localhost/"));
protocol = urlToProcess.protocol.replace(/:$/, "");
} else {
// unrelated error to do with url parsing, we should re-throw. This is a real user error
throw e;
}
}
} else {
// Add protocol if missing
@@ -582,7 +590,8 @@ function parseOptions(
for (const key in queryObject) {
if (key.toLowerCase() === "sslmode") {
sslMode = normalizeSSLMode(queryObject[key]);
} else if (key.toLowerCase() === "path") {
} else if (key.toLowerCase() === "path" || key.toLowerCase() === "socket") {
// Support both 'path' and 'socket' query parameters for Unix socket connections
path = queryObject[key];
} else {
// this is valid for postgres for other databases it might not be valid

View File

@@ -0,0 +1,80 @@
import { SQL } from "bun";
import { afterAll, beforeAll, expect, test } from "bun:test";
import { describeWithContainer, isDockerEnabled } from "harness";
import { UnixDomainSocketProxy } from "../../unix-domain-socket-proxy.ts";
// Test for issue #23954: Unix socket connection for MySQL
// https://github.com/oven-sh/bun/issues/23954
if (isDockerEnabled()) {
describeWithContainer(
"MySQL Unix socket connection (issue #23954)",
{
image: "mysql_plain",
concurrent: true,
},
container => {
let socketProxy: UnixDomainSocketProxy;
let socketPath: string;
beforeAll(async () => {
await container.ready;
// Create Unix socket proxy for MySQL
socketProxy = await UnixDomainSocketProxy.create("MySQL", container.host, container.port);
socketPath = socketProxy.path;
});
afterAll(() => {
if (socketProxy) {
socketProxy.stop();
}
});
test("MySQL connection via Unix socket using 'socket' query parameter with localhost", async () => {
// Standard format: hostname is required but ignored when socket is provided
const mysql = new SQL(`mysql://root:@localhost/bun_sql_test?socket=${socketPath}`);
try {
// Try a simple query
const result = await mysql`SELECT 1 as value`;
expect(result).toEqual([{ value: 1 }]);
} finally {
await mysql.close();
}
});
test("MySQL connection via Unix socket using 'socket' query parameter without hostname (docs format)", async () => {
// Shorthand format from docs: mysql://user:pass@/database?socket=/path
// The missing hostname is replaced with localhost internally
const mysql = new SQL(`mysql://root:@/bun_sql_test?socket=${socketPath}`);
try {
// Try a simple query
const result = await mysql`SELECT 1 as value`;
expect(result).toEqual([{ value: 1 }]);
} finally {
await mysql.close();
}
});
test("MySQL connection via Unix socket using path in options object", async () => {
// Using the options object with path property
const mysql = new SQL({
adapter: "mysql",
username: "root",
password: "",
database: "bun_sql_test",
path: socketPath,
});
try {
// Try a simple query
const result = await mysql`SELECT 1 as value`;
expect(result).toEqual([{ value: 1 }]);
} finally {
await mysql.close();
}
});
},
);
}