Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion docs/config/server-options.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ title: "Server Options"
sqlite:///var/lib/data/mydb.db

# Windows
sqlite:C:/Users/YourName/data/database.db
sqlite:///C:/Users/YourName/data/database.db

# In-memory
sqlite:///:memory:
Expand Down
111 changes: 110 additions & 1 deletion src/connectors/__tests__/sqlite.integration.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -400,9 +400,118 @@ describe('SQLite Connector Integration Tests', () => {
'SELECT * FROM users ORDER BY id',
{}
);

// Should return all users (at least the original 3 plus any added in previous tests)
expect(result.rows.length).toBeGreaterThanOrEqual(3);
});
});

describe('DSN Path Parsing', () => {
it('should parse absolute paths correctly', async () => {
const connector = new SQLiteConnector();
const tempDir = os.tmpdir();
const fileName = `test_${Date.now()}.db`;
const dbPath = path.join(tempDir, fileName);

try {
// Test platform-native absolute paths
// On Unix: /tmp/test.db, On Windows: C:\Users\...\test.db
const dsn = `sqlite://${dbPath}`;

// Should successfully connect without errors
await connector.connect(dsn);

// Verify we can execute queries
await connector.executeSQL('CREATE TABLE test (id INTEGER PRIMARY KEY)', {});
const result = await connector.executeSQL('SELECT * FROM test', {});
expect(result.rows).toEqual([]);

await connector.disconnect();
} finally {
// Cleanup
if (fs.existsSync(dbPath)) {
try {
await new Promise(resolve => setTimeout(resolve, 10));
fs.unlinkSync(dbPath);
} catch (error) {
console.warn(`Failed to cleanup test database: ${error}`);
}
}
}
});

it('should parse relative paths correctly', async () => {
const connector = new SQLiteConnector();
// Use tmpdir to ensure the directory exists
const tempDir = os.tmpdir();
const fileName = `test_relative_${Date.now()}.db`;
const fullPath = path.join(tempDir, fileName);

// Create a relative path from current working directory
const relativePath = path.relative(process.cwd(), fullPath);

try {
const dsn = `sqlite://${relativePath}`;

// Should successfully connect without errors
await connector.connect(dsn);

// Verify we can execute queries
await connector.executeSQL('CREATE TABLE test (id INTEGER PRIMARY KEY)', {});
const result = await connector.executeSQL('SELECT * FROM test', {});
expect(result.rows).toEqual([]);

await connector.disconnect();
} finally {
// Cleanup
if (fs.existsSync(fullPath)) {
try {
await new Promise(resolve => setTimeout(resolve, 10));
fs.unlinkSync(fullPath);
} catch (error) {
console.warn(`Failed to cleanup test database: ${error}`);
}
}
}
});

it('should parse :memory: database correctly', async () => {
const connector = new SQLiteConnector();
const dsn = 'sqlite:///:memory:';

// Should successfully connect without errors
await connector.connect(dsn);

// Verify we can execute queries
await connector.executeSQL('CREATE TABLE test (id INTEGER PRIMARY KEY)', {});
const result = await connector.executeSQL('SELECT * FROM test', {});
expect(result.rows).toEqual([]);

await connector.disconnect();
});

it('should parse Windows drive letter paths correctly', async () => {
const connector = new SQLiteConnector();
const parser = connector.dsnParser;

// This test explicitly validates Windows DSN format parsing
// It tests the fix for issue #137 by ensuring drive letter paths
// like C:/, D:/ are correctly parsed regardless of platform

// Test lowercase drive letter
const result1 = await parser.parse('sqlite:///c:/temp/test.db');
expect(result1.dbPath).toBe('c:/temp/test.db');

// Test uppercase drive letter
const result2 = await parser.parse('sqlite:///C:/temp/test.db');
expect(result2.dbPath).toBe('C:/temp/test.db');

// Test different drive letters
const result3 = await parser.parse('sqlite:///D:/path/to/db.db');
expect(result3.dbPath).toBe('D:/path/to/db.db');

const result4 = await parser.parse('sqlite:///E:/another/path.db');
expect(result4.dbPath).toBe('E:/another/path.db');
});
});
});
6 changes: 5 additions & 1 deletion src/connectors/sqlite/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,8 +56,12 @@ class SQLiteDSNParser implements DSNParser {
else {
// Get the path part, handling both relative and absolute paths
if (url.pathname.startsWith("//")) {
// Absolute path: sqlite:///path/to/db.sqlite
// Unix absolute path: sqlite:///path/to/db.sqlite
dbPath = url.pathname.substring(2); // Remove leading //
} else if (url.pathname.match(/^\/[A-Za-z]:\//)) {
Comment on lines 58 to +61
Copy link

Copilot AI Nov 24, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The condition order creates a logic error: Windows UNC paths like //server/share would incorrectly match the Unix absolute path check first. The Windows drive letter check should come before the // check, or add an additional condition to exclude Windows drive patterns from the Unix path logic (e.g., url.pathname.startsWith('//') && !url.pathname.match(/^\/\/[A-Za-z]:\//)).

Copilot uses AI. Check for mistakes.
// Windows absolute path: sqlite:///C:/path/to/db.sqlite
Comment on lines +61 to +62
Copy link

Copilot AI Nov 24, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The regex pattern only matches forward slashes after the drive letter, but Windows paths can use backslashes. The pattern should be /^\/[A-Za-z]:[\/\\]/ to handle both C:/ and C:\ formats. Although the URL parser typically normalizes to forward slashes, this would make the code more robust.

Suggested change
} else if (url.pathname.match(/^\/[A-Za-z]:\//)) {
// Windows absolute path: sqlite:///C:/path/to/db.sqlite
} else if (url.pathname.match(/^\/[A-Za-z]:[\/\\]/)) {
// Windows absolute path: sqlite:///C:/path/to/db.sqlite or sqlite:///C:\path\to\db.sqlite

Copilot uses AI. Check for mistakes.
// URL parser adds leading slash to drive letter paths, so strip it
dbPath = url.pathname.substring(1);
} else {
// Relative path: sqlite://./path/to/db.sqlite
dbPath = url.pathname;
Expand Down
Loading