Implement basic table and column name completion
This commit is contained in:
parent
f612f51e72
commit
a0509b4b55
1 changed files with 117 additions and 12 deletions
129
src/index.ts
129
src/index.ts
|
@ -5,6 +5,36 @@ import readline from 'node:readline'
|
||||||
import oracledb from 'oracledb'
|
import oracledb from 'oracledb'
|
||||||
import { findUpSync } from 'find-up'
|
import { findUpSync } from 'find-up'
|
||||||
|
|
||||||
|
interface OraTable {
|
||||||
|
OWNER: string
|
||||||
|
TABLE_NAME: string
|
||||||
|
}
|
||||||
|
|
||||||
|
interface OraColumn {
|
||||||
|
OWNER: string
|
||||||
|
TABLE_NAME: string
|
||||||
|
COLUMN_NAME: string
|
||||||
|
DATA_TYPE: string
|
||||||
|
DATA_LENGTH: number | null
|
||||||
|
NULLABLE: 'Y' | 'N'
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Schema {
|
||||||
|
tables: Table[]
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Table {
|
||||||
|
owner: string
|
||||||
|
name: string
|
||||||
|
columns: Column[]
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Column {
|
||||||
|
name: string
|
||||||
|
datatype: string
|
||||||
|
nullable: boolean
|
||||||
|
}
|
||||||
|
|
||||||
function getStringParts(str: string, offset: number) {
|
function getStringParts(str: string, offset: number) {
|
||||||
const init = str.slice(0, offset)
|
const init = str.slice(0, offset)
|
||||||
|
|
||||||
|
@ -43,10 +73,96 @@ async function main() {
|
||||||
prompt: `${user}@${connectString}> `,
|
prompt: `${user}@${connectString}> `,
|
||||||
removeHistoryDuplicates: true,
|
removeHistoryDuplicates: true,
|
||||||
history,
|
history,
|
||||||
|
completer(linePartial: string) {
|
||||||
|
const words = [...rl.line.matchAll(/[a-zA-Z0-9_.$]+/g).map((g) => g[0])]
|
||||||
|
words.push(...words.map((w) => w.toUpperCase()))
|
||||||
|
const wset = new Set(words)
|
||||||
|
|
||||||
|
const completions = ['DUAL']
|
||||||
|
const tables = schema.tables.filter((tab) => {
|
||||||
|
if (tab.owner) {
|
||||||
|
const fullName = `${tab.owner}.${tab.name}`
|
||||||
|
completions.push(fullName)
|
||||||
|
if (wset.has(fullName)) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (['', 'SYS', 'WMSYS'].includes(tab.owner)) {
|
||||||
|
completions.push(tab.name)
|
||||||
|
return wset.has(tab.name)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
for (const tab of tables) {
|
||||||
|
for (const col of tab.columns) {
|
||||||
|
completions.push(col.name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
completions.sort()
|
||||||
|
|
||||||
|
const suffix = linePartial.match(/[a-zA-Z0-9_$]*.$/)?.[0] ?? ''
|
||||||
|
const upper = suffix.toUpperCase()
|
||||||
|
const prefix = linePartial.substring(0, linePartial.length - suffix.length)
|
||||||
|
|
||||||
|
return [
|
||||||
|
completions
|
||||||
|
.filter((comp) => comp.startsWith(suffix) || comp.startsWith(upper))
|
||||||
|
.map((comp) => prefix + comp),
|
||||||
|
linePartial,
|
||||||
|
]
|
||||||
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
const connection = await oracledb.getConnection({ user, password, connectString })
|
const connection = await oracledb.getConnection({ user, password, connectString })
|
||||||
|
|
||||||
|
function execute<R>(query: string, args: any[] = [], maxRows?: number) {
|
||||||
|
return connection.execute<R>(query, args, {
|
||||||
|
dbObjectAsPojo: true,
|
||||||
|
prefetchRows: 1024,
|
||||||
|
fetchArraySize: 1024,
|
||||||
|
maxRows,
|
||||||
|
outFormat: oracledb.OUT_FORMAT_OBJECT,
|
||||||
|
resultSet: false,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const schema: Schema = { tables: [] }
|
||||||
|
|
||||||
|
async function refresh() {
|
||||||
|
const tables = await execute<OraTable>(
|
||||||
|
`
|
||||||
|
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
|
||||||
|
`,
|
||||||
|
)
|
||||||
|
|
||||||
|
const columns = await execute<OraColumn>(
|
||||||
|
'select owner, table_name, column_name, data_type, data_length, nullable from all_tab_cols',
|
||||||
|
)
|
||||||
|
|
||||||
|
schema.tables.length = 0
|
||||||
|
|
||||||
|
for (const table of tables.rows ?? []) {
|
||||||
|
schema.tables.push({
|
||||||
|
owner: table.OWNER ?? '',
|
||||||
|
name: table.TABLE_NAME,
|
||||||
|
columns: (columns.rows ?? [])
|
||||||
|
.filter((col) => col.OWNER === table.OWNER && 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})`,
|
||||||
|
nullable: col.NULLABLE === 'Y',
|
||||||
|
})),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
await refresh()
|
||||||
rl.prompt()
|
rl.prompt()
|
||||||
|
|
||||||
let displayMode: 'table' | 'list' = 'table'
|
let displayMode: 'table' | 'list' = 'table'
|
||||||
|
@ -66,18 +182,7 @@ async function main() {
|
||||||
process.exit(0)
|
process.exit(0)
|
||||||
} else if (query) {
|
} else if (query) {
|
||||||
try {
|
try {
|
||||||
const result = await connection.execute(
|
const result = await execute(query, [], 1024)
|
||||||
query,
|
|
||||||
{},
|
|
||||||
{
|
|
||||||
dbObjectAsPojo: true,
|
|
||||||
prefetchRows: 1024,
|
|
||||||
fetchArraySize: 1024,
|
|
||||||
maxRows: 1024,
|
|
||||||
outFormat: oracledb.OUT_FORMAT_OBJECT,
|
|
||||||
resultSet: false,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
if (result.rows && result.rows.length > 0) {
|
if (result.rows && result.rows.length > 0) {
|
||||||
if (displayMode === 'table') {
|
if (displayMode === 'table') {
|
||||||
console.table(result.rows)
|
console.table(result.rows)
|
||||||
|
|
Loading…
Reference in a new issue