Skip to content

Commit

Permalink
feat: Improve procedure metadata lookup logic (#862)
Browse files Browse the repository at this point in the history
When using `@cap-js/hana` it is possible to send `SQL` statements like:

```sql
CALL PROC()
CALL SYS.PROC()
CALL "PROC"()
CALL "SYS"."PROC"()
```

With this change it is also possible to call procedures outside of the
current schema or sys schema like:

```sql
CALL SCHEMA.PROC()
CALL "SCHEMA"."PROC"()
CALL "Schema"."Proc"()
```

The logic always was case sensitive for the procedure name therefor the
same is applied to the schema name. This only applies to the metadata
lookup for improved output parameter handling. As the native HANA
drivers don't behave the same for procedure.

fixes: #859
  • Loading branch information
BobdenOs authored Oct 24, 2024
1 parent 641c3b9 commit da629d9
Showing 1 changed file with 14 additions and 12 deletions.
26 changes: 14 additions & 12 deletions hana/lib/HANAService.js
Original file line number Diff line number Diff line change
Expand Up @@ -987,31 +987,31 @@ SELECT ${mixing} FROM JSON_TABLE(SRC.JSON, '$' COLUMNS(${extraction})) AS NEW LE
list(list) {
const first = list.list[0]
// If the list only contains of lists it is replaced with a json function and a placeholder
if (this.values && first.list && !first.list.find(v => v.val == null)) {
const listMapped = []
if (this.values && first.list && !first.list.find(v => v.val == null)) {
const listMapped = []
for (let l of list.list) {
const obj ={}
for (let i = 0; i< l.list.length; i++) {
const obj = {}
for (let i = 0; i < l.list.length; i++) {
const c = l.list[i]
if (Buffer.isBuffer(c.val)) {
return super.list(list)
}
}
obj[`V${i}`] = c.val
}
listMapped.push(obj)
}
}
this.values.push(JSON.stringify(listMapped))
const extraction = first.list.map((v, i) => `"${i}" ${this.constructor.InsertTypeMap[typeof v.val]()} PATH '$.V${i}'`)
const extraction = first.list.map((v, i) => `"${i}" ${this.constructor.InsertTypeMap[typeof v.val]()} PATH '$.V${i}'`)
return `(SELECT * FROM JSON_TABLE(?, '$' COLUMNS(${extraction})))`
}
// If the list only contains of vals it is replaced with a json function and a placeholder
if (this.values && first.val != null) {
for (let c of list.list) {
if (Buffer.isBuffer(c.val)) {
return super.list(list)
}
}
}
const v = first
const v = first
const extraction = `"val" ${this.constructor.InsertTypeMap[typeof v.val]()} PATH '$.val'`
this.values.push(JSON.stringify(list.list))
return `(SELECT * FROM JSON_TABLE(?, '$' COLUMNS(${extraction})))`
Expand Down Expand Up @@ -1301,16 +1301,18 @@ SELECT ${mixing} FROM JSON_TABLE(SRC.JSON, '$' COLUMNS(${extraction})) AS NEW LE
}

async _getProcedureMetadata(name, schema) {
const query = `SELECT PARAMETER_NAME FROM SYS.PROCEDURE_PARAMETERS WHERE SCHEMA_NAME = ${schema?.toUpperCase?.() === 'SYS' ? `'SYS'` : 'CURRENT_SCHEMA'
} AND PROCEDURE_NAME = '${name}' AND PARAMETER_TYPE IN ('OUT', 'INOUT') ORDER BY POSITION`
const sqlString = this.class.CQN2SQL.prototype.string
name = typeof name === 'string' ? sqlString(name) : `'${name}'`
schema = typeof schema === 'string' ? sqlString(schema) : 'CURRENT_SCHEMA'
const query = `SELECT PARAMETER_NAME FROM SYS.PROCEDURE_PARAMETERS WHERE SCHEMA_NAME = ${schema} AND PROCEDURE_NAME = ${name} AND PARAMETER_TYPE IN ('OUT', 'INOUT') ORDER BY POSITION`
return await super.onPlainSQL({ query, data: [] })
}

_getProcedureNameAndSchema(sql) {
// name delimited with "" allows any character
const match = sql
.match(
/^\s*call \s*(("(?<schema_delimited>\w+)"\.)?("(?<delimited>.+)")|(?<schema_undelimited>\w+\.)?(?<undelimited>\w+))\s*\(/i
/^\s*call \s*(("(?<schema_delimited>\w+)"\.)?("(?<delimited>.+)")|((?<schema_undelimited>\w+)\.)?(?<undelimited>\w+))\s*\(/i
)
return (
match && {
Expand Down

0 comments on commit da629d9

Please sign in to comment.