Skip to content

Commit ec4dacb

Browse files
committed
fix: repair Oracle metadata introspection
1 parent 70e1c3a commit ec4dacb

6 files changed

Lines changed: 49 additions & 22 deletions

File tree

docker/test-dbs/integration-test.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ const RPC_AUTH_HEADERS = {
4848
};
4949

5050
const { startServer } = await import("@omni-sql/backend");
51+
const { closeBackendResources } = await import("../../packages/backend/src/handlers.ts");
5152

5253
const SIDECAR_HEALTH_URL = "http://127.0.0.1:41921/health";
5354
const SIDECAR_SCOPE_URL = "http://127.0.0.1:41921/scope/resolve";
@@ -208,6 +209,7 @@ describe("Integration — pipeline completo via JSON-RPC", () => {
208209
await new Promise<void>((resolve, reject) =>
209210
server.close((err) => (err ? reject(err) : resolve())),
210211
);
212+
await closeBackendResources();
211213
fs.rmSync(tmpDir, { recursive: true, force: true });
212214
});
213215

docker/test-dbs/smoke-test.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ const TARGETS: Record<string, Target> = {
5555
dialect: "sqlserver",
5656
endpoint: "127.0.0.1:1433/omni_test",
5757
user: "sa",
58+
options: { trustServerCertificate: true, serverName: "localhost" },
5859
},
5960
password: "Omni!2024",
6061
},
@@ -153,7 +154,7 @@ for (const [key, target] of targets) {
153154
const pub = findSchema(schemas);
154155

155156
const tables = adapter.listTables(pub.name);
156-
const tableNames = tables.map((t) => t.name);
157+
const tableNames = tables.map((t) => t.name.toLowerCase());
157158
assert.ok(tableNames.includes("customers"), `customers missing, got: ${tableNames}`);
158159
assert.ok(tableNames.includes("orders"), `orders missing, got: ${tableNames}`);
159160
assert.ok(tableNames.includes("products"), `products missing, got: ${tableNames}`);
@@ -163,7 +164,7 @@ for (const [key, target] of targets) {
163164
it("listColumns (customers)", () => {
164165
const schemas = adapter.listSchemas();
165166
const pub = findSchema(schemas);
166-
const cols = adapter.listColumns(pub.name, "customers");
167+
const cols = adapter.listColumns(pub.name, key === "oracle" ? "CUSTOMERS" : "customers");
167168
assert.ok(cols.length >= 4, `expected 4+ columns, got ${cols.length}`);
168169

169170
const idCol = cols.find((c) => c.name === "id" || c.name === "ID");
@@ -177,7 +178,7 @@ for (const [key, target] of targets) {
177178
it("listColumns (orders with FK)", () => {
178179
const schemas = adapter.listSchemas();
179180
const pub = findSchema(schemas);
180-
const cols = adapter.listColumns(pub.name, "orders");
181+
const cols = adapter.listColumns(pub.name, key === "oracle" ? "ORDERS" : "orders");
181182
const fkCol = cols.find(
182183
(c) => c.name === "customer_id" || c.name === "CUSTOMER_ID",
183184
);

docs/releases/v0.2.12.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
## 0.2.12 (2026-09-17)
2+
3+
### Bug Fixes
4+
5+
- **Oracle metadata:** qualify catalog columns in the joined column query so introspection succeeds without ambiguous-column errors.
6+
- **Oracle errors:** show structured `ORA-xxxxx` errors when schema discovery or metadata introspection fails.
7+
8+
### Tests
9+
10+
- Exercise PostgreSQL, MySQL, SQL Server, Oracle, and generic JDBC through the Docker integration suite; fix SQL Server TLS and Oracle identifier expectations in the smoke tests.
11+
- Close backend resources before removing the integration test database on Windows.
12+
13+
**Full Changelog:** [`v0.2.11...v0.2.12`](https://github.com/cccadet/omni-sql/compare/v0.2.11...v0.2.12)

packages/adapters-oracle/src/index.test.ts

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -99,9 +99,12 @@ test("introspectSchemas ignora FK incompleta e preserva FK válida", async () =>
9999
execute: async (sql: string) => {
100100
if (sql.includes("FROM all_tables")) {
101101
return { rows: [{ table_schema: "APP", table_name: "CHILD", table_type: "TABLE" }] };
102-
}
103-
if (sql.includes("FROM all_tab_columns")) {
104-
return {
102+
}
103+
if (sql.includes("FROM all_tab_columns")) {
104+
assert.match(sql, /c\.owner AS "table_schema"/);
105+
assert.match(sql, /c\.table_name AS "table_name"/);
106+
assert.match(sql, /c\.column_name AS "column_name"/);
107+
return {
105108
rows: [
106109
{ table_schema: "APP", table_name: "CHILD", column_name: "BROKEN_ID", data_type: "NUMBER", is_nullable: "Y", column_default: null, ordinal_position: 1 },
107110
{ table_schema: "APP", table_name: "CHILD", column_name: "PARENT_ID", data_type: "NUMBER", is_nullable: "N", column_default: null, ordinal_position: 2 },

packages/adapters-oracle/src/introspection.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -74,12 +74,12 @@ ORDER BY 1, 2
7474

7575
const COLUMNS_SQL = `
7676
SELECT
77-
owner AS "table_schema",
78-
table_name AS "table_name",
79-
column_name AS "column_name",
80-
data_type AS "data_type",
81-
nullable AS "is_nullable",
82-
data_default AS "column_default",
77+
c.owner AS "table_schema",
78+
c.table_name AS "table_name",
79+
c.column_name AS "column_name",
80+
c.data_type AS "data_type",
81+
c.nullable AS "is_nullable",
82+
c.data_default AS "column_default",
8383
c.column_id AS "ordinal_position",
8484
cc.comments AS "description"
8585
FROM all_tab_columns c

packages/backend/src/handlers.ts

Lines changed: 18 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -686,9 +686,12 @@ export const handlers: BackendRpcRouter = {
686686
console.log(
687687
`[omni-sql] listSchemas: adapter returned ${schemas.length} schemas ` +
688688
`in ${Date.now() - tList}ms`,
689-
);
690-
return { schemas };
691-
} finally {
689+
);
690+
return { schemas };
691+
} catch (error) {
692+
if (config.dialect === "oracle") throw safeOracleDatabaseError(error) ?? error;
693+
throw error;
694+
} finally {
692695
await adapter.close().catch((e) => console.warn(`[omni-sql] listSchemas: close failed: ${errorMessage(e)}`));
693696
}
694697
},
@@ -891,18 +894,23 @@ export const handlers: BackendRpcRouter = {
891894
return { rowsAffected };
892895
},
893896

894-
async "metadata.introspect"({ connectionId }: IntrospectParams): Promise<IntrospectResult> {
897+
async "metadata.introspect"({ connectionId }: IntrospectParams): Promise<IntrospectResult> {
895898
const s = requireSession(connectionId);
896899
console.log(
897900
`[omni-sql] introspect start: dialect=${logValue(s.config.dialect)}`,
898901
);
899902
const tConnect = Date.now();
900-
await s.adapter.connect();
901-
console.log(`[omni-sql] introspect: connected in ${Date.now() - tConnect}ms, querying metadata…`);
902-
const tIntro = Date.now();
903-
const db: Database = await refreshMetadataCache(connectionId, s);
904-
console.log(`[omni-sql] introspect: adapter.introspect() returned in ${Date.now() - tIntro}ms`);
905-
return db;
903+
try {
904+
await s.adapter.connect();
905+
console.log(`[omni-sql] introspect: connected in ${Date.now() - tConnect}ms, querying metadata…`);
906+
const tIntro = Date.now();
907+
const db: Database = await refreshMetadataCache(connectionId, s);
908+
console.log(`[omni-sql] introspect: adapter.introspect() returned in ${Date.now() - tIntro}ms`);
909+
return db;
910+
} catch (error) {
911+
if (s.config.dialect === "oracle") throw safeOracleDatabaseError(error) ?? error;
912+
throw error;
913+
}
906914
},
907915

908916
async "metadata.listRelations"({

0 commit comments

Comments
 (0)