Add procedure introspection
This commit is contained in:
parent
ff6f568b34
commit
6a1a83eb62
1 changed files with 133 additions and 3 deletions
136
src/index.ts
136
src/index.ts
|
@ -22,8 +22,26 @@ interface OraColumn {
|
|||
NULLABLE: 'Y' | 'N'
|
||||
}
|
||||
|
||||
interface OraProc {
|
||||
OWNER: string
|
||||
OBJECT_NAME: string
|
||||
PROCEDURE_NAME: string | null
|
||||
}
|
||||
|
||||
interface OraArg {
|
||||
OWNER: string
|
||||
OBJECT_NAME: string
|
||||
PACKAGE_NAME: string
|
||||
POSITION: number
|
||||
DATA_TYPE: string
|
||||
DEFAULTED: string
|
||||
DEFAULT_VALUE: string | null
|
||||
IN_OUT: 'IN' | 'OUT'
|
||||
}
|
||||
|
||||
interface Schema {
|
||||
tables: Table[]
|
||||
procedures: Procedure[]
|
||||
}
|
||||
|
||||
interface Table {
|
||||
|
@ -33,12 +51,25 @@ interface Table {
|
|||
columns: Column[]
|
||||
}
|
||||
|
||||
interface Procedure {
|
||||
owner: string
|
||||
package: string
|
||||
name: string
|
||||
arguments: Argument[]
|
||||
}
|
||||
|
||||
interface Column {
|
||||
name: string
|
||||
datatype: string
|
||||
identity: boolean
|
||||
nullable: boolean
|
||||
default?: string
|
||||
default?: string | null
|
||||
}
|
||||
|
||||
interface Argument {
|
||||
inout: string
|
||||
datatype: string
|
||||
default?: string | null
|
||||
}
|
||||
|
||||
function getStringParts(str: string, offset: number) {
|
||||
|
@ -106,9 +137,17 @@ async function main() {
|
|||
}
|
||||
}
|
||||
|
||||
for (const proc of schema.procedures) {
|
||||
if (proc.owner) {
|
||||
completions.push(`${proc.owner}.${proc.package}.${proc.name}`)
|
||||
} else {
|
||||
completions.push(`${proc.package}.${proc.name}`)
|
||||
}
|
||||
}
|
||||
|
||||
completions.sort()
|
||||
|
||||
const suffix = linePartial.match(/[a-zA-Z0-9_$]*.$/)?.[0] ?? ''
|
||||
const suffix = linePartial.match(/[a-zA-Z0-9_.$]*.$/)?.[0] ?? ''
|
||||
const upper = suffix.toUpperCase()
|
||||
const prefix = linePartial.substring(0, linePartial.length - suffix.length)
|
||||
|
||||
|
@ -135,7 +174,7 @@ async function main() {
|
|||
})
|
||||
}
|
||||
|
||||
const schema: Schema = { tables: [] }
|
||||
const schema: Schema = { tables: [], procedures: [] }
|
||||
|
||||
async function refresh() {
|
||||
const tables = await execute<OraTable>(
|
||||
|
@ -155,9 +194,28 @@ async function main() {
|
|||
`,
|
||||
)
|
||||
|
||||
const procedures = await execute<OraProc>(
|
||||
`
|
||||
select owner, object_name, procedure_name from all_procedures
|
||||
union all select '' owner, object_name, procedure_name from user_procedures
|
||||
`,
|
||||
)
|
||||
|
||||
const procargs = await execute<OraArg>(
|
||||
`
|
||||
select owner, package_name, object_name, position, in_out, data_type, defaulted, default_value from all_arguments
|
||||
union all select '' owner, package_name, object_name, position, in_out, data_type, defaulted, default_value from user_arguments
|
||||
`,
|
||||
)
|
||||
|
||||
if (procargs.rows) {
|
||||
procargs.rows.sort((a, b) => a.POSITION - b.POSITION)
|
||||
}
|
||||
|
||||
const self = await execute<{ USER: string }>('select user from dual')
|
||||
|
||||
schema.tables.length = 0
|
||||
schema.procedures.length = 0
|
||||
|
||||
for (const table of tables.rows ?? []) {
|
||||
schema.tables.push({
|
||||
|
@ -180,6 +238,30 @@ async function main() {
|
|||
})),
|
||||
})
|
||||
}
|
||||
|
||||
for (const proc of procedures.rows ?? []) {
|
||||
if (proc.PROCEDURE_NAME === null) {
|
||||
continue
|
||||
}
|
||||
|
||||
schema.procedures.push({
|
||||
owner: proc.OWNER ?? '',
|
||||
package: proc.OBJECT_NAME,
|
||||
name: proc.PROCEDURE_NAME,
|
||||
arguments: (procargs.rows ?? [])
|
||||
.filter(
|
||||
(arg) =>
|
||||
arg.OWNER === (proc.OWNER || self.rows![0].USER) &&
|
||||
arg.PACKAGE_NAME === proc.OBJECT_NAME &&
|
||||
arg.OBJECT_NAME === proc.PROCEDURE_NAME,
|
||||
)
|
||||
.map((arg) => ({
|
||||
inout: arg.IN_OUT ?? '?',
|
||||
datatype: arg.DATA_TYPE,
|
||||
default: arg.DEFAULTED === 'Y' ? arg.DEFAULT_VALUE : undefined,
|
||||
})),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
await refresh()
|
||||
|
@ -202,6 +284,27 @@ async function main() {
|
|||
process.exit(0)
|
||||
} else if (query === '\\r') {
|
||||
await refresh()
|
||||
} else if (query.match(/^\\[dD]p($|\s)/)) {
|
||||
const procs = schema.procedures.filter((proc) => {
|
||||
if (query.startsWith('\\Dp')) {
|
||||
return proc.owner
|
||||
} else if (query.startsWith('\\dp')) {
|
||||
return !proc.owner
|
||||
} else {
|
||||
return true
|
||||
}
|
||||
})
|
||||
|
||||
const [_, name] = query.split(/\s+/).map((part) => part.trim())
|
||||
const upper = (name ?? '').toUpperCase()
|
||||
|
||||
console.table(
|
||||
procs.filter((proc) => {
|
||||
const fullName = `${proc.owner}.${proc.package}.${proc.name}`
|
||||
return fullName.toUpperCase().includes(upper)
|
||||
}),
|
||||
['owner', 'package', 'name'],
|
||||
)
|
||||
} else if (query.match(/^\\[dD][tv]?($|\s)/)) {
|
||||
const tables = schema.tables.filter((tab) => {
|
||||
if (query.startsWith('\\dt')) {
|
||||
|
@ -254,6 +357,33 @@ async function main() {
|
|||
default: col.default,
|
||||
}))
|
||||
|
||||
if (displayMode === 'table') {
|
||||
console.table(rows)
|
||||
} else {
|
||||
console.dir(rows, { depth: null })
|
||||
}
|
||||
} else {
|
||||
console.log('Did not find', name)
|
||||
}
|
||||
} else if (query.match(/^\\[dD]p?\+?\s+\S+/)) {
|
||||
const [_, name] = query.split(/\s+/).map((part) => part.trim())
|
||||
const upper = name.toUpperCase()
|
||||
|
||||
const procedure = schema.procedures.find((proc) => {
|
||||
const fullName = proc.owner
|
||||
? `${proc.owner}.${proc.package}.${proc.name}`
|
||||
: `${proc.package}.${proc.name}`
|
||||
|
||||
return fullName === name || fullName === upper
|
||||
})
|
||||
|
||||
if (procedure) {
|
||||
const rows = procedure.arguments.map((arg) => ({
|
||||
inout: arg.inout,
|
||||
type: arg.datatype,
|
||||
default: arg.default,
|
||||
}))
|
||||
|
||||
if (displayMode === 'table') {
|
||||
console.table(rows)
|
||||
} else {
|
||||
|
|
Loading…
Reference in a new issue