From 04db6ffcb4cbbb7bfbc1a81181ba54520981ac4b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Hamal=20Dvo=C5=99=C3=A1k?= Date: Tue, 19 Aug 2025 17:48:55 +0200 Subject: [PATCH] Add and improve introspection shortcuts --- src/index.ts | 89 ++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 83 insertions(+), 6 deletions(-) diff --git a/src/index.ts b/src/index.ts index d3e6ee5..8e9ed29 100644 --- a/src/index.ts +++ b/src/index.ts @@ -8,6 +8,7 @@ import { findUpSync } from 'find-up' interface OraTable { OWNER: string TABLE_NAME: string + KIND: 'table' | 'view' } interface OraColumn { @@ -16,6 +17,8 @@ interface OraColumn { COLUMN_NAME: string DATA_TYPE: string DATA_LENGTH: number | null + DATA_DEFAULT: string | null + IDENTITY_COLUMN: 'YES' | 'NO' NULLABLE: 'Y' | 'N' } @@ -26,13 +29,16 @@ interface Schema { interface Table { owner: string name: string + kind: 'table' | 'view' columns: Column[] } interface Column { name: string datatype: string + identity: boolean nullable: boolean + default?: string } function getStringParts(str: string, offset: number) { @@ -134,30 +140,43 @@ async function main() { async function refresh() { const tables = await execute( ` - select owner, table_name from all_tables - union all select owner, view_name table_name from all_views - union all select '' owner, table_name from user_tables - union all select '' owner, view_name table_name from user_views + select owner, table_name, 'table' kind from all_tables + union all select owner, view_name table_name, 'view' from all_views + union all select '' owner, table_name, 'table' from user_tables + union all select '' owner, view_name table_name, 'view' from user_views `, ) const columns = await execute( - 'select owner, table_name, column_name, data_type, data_length, nullable from all_tab_cols', + ` + select owner, table_name, column_name, data_type, data_length, nullable, + data_default, identity_column + from all_tab_cols + `, ) + const self = await execute<{ USER: string }>('select user from dual') + schema.tables.length = 0 for (const table of tables.rows ?? []) { schema.tables.push({ owner: table.OWNER ?? '', name: table.TABLE_NAME, + kind: table.KIND, columns: (columns.rows ?? []) - .filter((col) => col.OWNER === table.OWNER && col.TABLE_NAME === table.TABLE_NAME) + .filter( + (col) => + col.OWNER === (table.OWNER || self.rows![0].USER) && + col.TABLE_NAME === table.TABLE_NAME, + ) .map((col) => ({ name: col.COLUMN_NAME, datatype: col.DATA_LENGTH === null ? col.DATA_TYPE : `${col.DATA_TYPE}(${col.DATA_LENGTH})`, + identity: col.IDENTITY_COLUMN === 'YES', nullable: col.NULLABLE === 'Y', + default: col.DATA_DEFAULT ?? undefined, })), }) } @@ -181,6 +200,64 @@ async function main() { } else if (query === '\\q') { await connection.close() process.exit(0) + } else if (query === '\\r') { + await refresh() + } else if (query.match(/^\\[dD][tv]?($|\s)/)) { + const tables = schema.tables.filter((tab) => { + if (query.startsWith('\\dt')) { + return tab.kind === 'table' && !tab.owner + } else if (query.startsWith('\\dv')) { + return tab.kind === 'view' && !tab.owner + } else if (query.startsWith('\\Dt')) { + return tab.kind === 'table' && tab.owner + } else if (query.startsWith('\\Dv')) { + return tab.kind === 'view' && tab.owner + } else if (query.startsWith('\\d')) { + return !tab.owner + } else if (query.startsWith('\\D')) { + return tab.owner + } else { + return true + } + }) + + const [_, name] = query.split(/\s+/).map((part) => part.trim()) + const upper = (name ?? '').toUpperCase() + + console.table( + tables.filter((tab) => { + const fullName = `${tab.owner}.${tab.name}` + return fullName.toUpperCase().includes(upper) + }), + ['owner', 'name', 'kind'], + ) + } else if (query.match(/^\\[dD][tv]?\+?\s+\S+/)) { + const [_, name] = query.split(/\s+/).map((part) => part.trim()) + const upper = name.toUpperCase() + + const table = schema.tables.find((tab) => { + if (!tab.owner && (tab.name === name || tab.name === upper)) { + return true + } + + const fullName = `${tab.owner}.${tab.name}` + return fullName === name || fullName === upper + }) + + if (table) { + console.table( + table.columns.map((col) => ({ + name: col.name, + type: col.datatype, + flags: [col.identity ? 'ID' : '', col.nullable ? '' : 'NOT NULL'] + .filter(Boolean) + .join(' '), + default: col.default, + })), + ) + } else { + console.log('Did not find', name) + } } else if (query) { try { const result = await execute(query, [], 1024)