58 lines
3.2 KiB
TypeScript
58 lines
3.2 KiB
TypeScript
import type { QueryResult } from "@/types/database";
|
|
|
|
export interface OracleDatabaseLink {
|
|
name: string;
|
|
owner: string;
|
|
username: string;
|
|
host: string;
|
|
created: string;
|
|
}
|
|
|
|
// Links belong to the login user, independently of ALTER SESSION SET CURRENT_SCHEMA.
|
|
// Do not expose another user's private links when connected with catalog privileges.
|
|
export const ORACLE_DATABASE_LINKS_SQL = `SELECT OWNER, DB_LINK, USERNAME, HOST,
|
|
TO_CHAR(CREATED, 'YYYY-MM-DD HH24:MI:SS') AS CREATED
|
|
FROM ALL_DB_LINKS
|
|
WHERE OWNER IN (SYS_CONTEXT('USERENV', 'SESSION_USER'), 'PUBLIC')
|
|
ORDER BY DB_LINK, CASE WHEN OWNER = 'PUBLIC' THEN 1 ELSE 0 END`;
|
|
|
|
export function oracleDatabaseLinksFromResult(result: QueryResult): OracleDatabaseLink[] {
|
|
const indexes = new Map(result.columns.map((name, index) => [name.toUpperCase(), index]));
|
|
return result.rows
|
|
.map((row) => {
|
|
const value = (name: string) => String(row[indexes.get(name) ?? -1] ?? "");
|
|
return { name: value("DB_LINK"), owner: value("OWNER"), username: value("USERNAME"), host: value("HOST"), created: value("CREATED") };
|
|
})
|
|
.filter((link) => !!link.name && !!link.owner);
|
|
}
|
|
|
|
export function oracleDatabaseLinkName(name: string): string {
|
|
// Oracle link names are ASCII, case insensitive, and may include a domain.
|
|
// A dot is part of the link name, never a schema qualifier.
|
|
if (!/^[A-Za-z][A-Za-z0-9_$#]*(?:\.[A-Za-z0-9_$#]+)*$/.test(name) || name.length > 128) throw new Error("Invalid database link name");
|
|
return name;
|
|
}
|
|
|
|
function oracleQuotedIdentifier(value: string, fromMetadata = false): string {
|
|
if (!value || /[\0\r\n]/.test(value)) throw new Error("Invalid identifier");
|
|
const name = fromMetadata ? value : value.startsWith('"') && value.endsWith('"') ? value.slice(1, -1).replaceAll('""', '"') : value.toUpperCase();
|
|
return `"${name.replaceAll('"', '""')}"`;
|
|
}
|
|
|
|
export function createOracleDatabaseLinkSql(input: { name: string; public: boolean; username: string; password: string; host: string }): string {
|
|
if (!input.host.trim() || !input.password || /[\0\r\n"]/.test(input.password)) throw new Error("A connect string and a password without double quotes or line breaks are required");
|
|
return `CREATE ${input.public ? "PUBLIC " : ""}DATABASE LINK ${oracleDatabaseLinkName(input.name)} CONNECT TO ${oracleQuotedIdentifier(input.username)} IDENTIFIED BY "${input.password}" USING '${input.host.replaceAll("'", "''")}'`;
|
|
}
|
|
|
|
export function alterOracleDatabaseLinkSql(link: OracleDatabaseLink, password: string): string {
|
|
if (!link.username || !password || /[\0\r\n"]/.test(password)) throw new Error("A fixed-user link and a password without double quotes or line breaks are required");
|
|
return `ALTER ${link.owner === "PUBLIC" ? "PUBLIC " : ""}DATABASE LINK ${oracleDatabaseLinkName(link.name)} CONNECT TO ${oracleQuotedIdentifier(link.username, true)} IDENTIFIED BY "${password}"`;
|
|
}
|
|
|
|
export function dropOracleDatabaseLinkSql(link: OracleDatabaseLink): string {
|
|
return `DROP ${link.owner === "PUBLIC" ? "PUBLIC " : ""}DATABASE LINK ${oracleDatabaseLinkName(link.name)}`;
|
|
}
|
|
export function testOracleDatabaseLinkSql(link: OracleDatabaseLink): string {
|
|
return `SELECT 1 AS DBX_LINK_OK FROM DUAL@${oracleDatabaseLinkName(link.name)}`;
|
|
}
|