@cap-js/hana
Advanced tools
Comparing version 0.4.0 to 1.0.0
@@ -7,2 +7,20 @@ # Changelog | ||
## [1.0.0](https://github.com/cap-js/cds-dbs/compare/hana-v0.5.0...hana-v1.0.0) (2024-06-19) | ||
### Fixed | ||
* Binary columns now return as Buffer for HANAService ([#689](https://github.com/cap-js/cds-dbs/issues/689)) ([179bd92](https://github.com/cap-js/cds-dbs/commit/179bd92729d57905d63ae55cca74c6c765eae290)) | ||
* Improve error message for disconnected connections ([#678](https://github.com/cap-js/cds-dbs/issues/678)) ([eb4ef37](https://github.com/cap-js/cds-dbs/commit/eb4ef37e3ecf2fbc1080e3c8b662075eb543f313)) | ||
* insertion of arrayed elements ([#677](https://github.com/cap-js/cds-dbs/issues/677)) ([92cf307](https://github.com/cap-js/cds-dbs/commit/92cf307b57bf01f70e82b7019e9f85feac877a0a)) | ||
* insertion of large decimals ([#686](https://github.com/cap-js/cds-dbs/issues/686)) ([ae8abff](https://github.com/cap-js/cds-dbs/commit/ae8abff74511adb2df1e260673bb69ee3e834451)) | ||
* Support static conditions inside unmanaged associations ([#682](https://github.com/cap-js/cds-dbs/issues/682)) ([e17ddfd](https://github.com/cap-js/cds-dbs/commit/e17ddfd5fa0ec43277f2f5b254f3ee894cc89c07)) | ||
## [0.5.0](https://github.com/cap-js/cds-dbs/compare/hana-v0.4.0...hana-v0.5.0) (2024-05-29) | ||
### Added | ||
* simple queries ([#654](https://github.com/cap-js/cds-dbs/issues/654)) ([ba77f9e](https://github.com/cap-js/cds-dbs/commit/ba77f9e4bdee8793b9e661fc7db2fa04854d8d01)) | ||
## [0.4.0](https://github.com/cap-js/cds-dbs/compare/hana-v0.3.0...hana-v0.4.0) (2024-05-16) | ||
@@ -9,0 +27,0 @@ |
@@ -88,3 +88,3 @@ const { Readable, Stream } = require('stream') | ||
while (await next()) { | ||
const cols = stmt.getColumnInfo().map(b => b.columnName) | ||
const cols = stmt.getColumnInfo() | ||
// column 0-3 are metadata columns | ||
@@ -97,6 +97,8 @@ const values = await Promise.all([getValue(0), getValue(1), getValue(2), getValue(3)]) | ||
// column >3 are all blob columns | ||
row[col] = i > 3 ? | ||
row[col.columnName] = i > 3 ? | ||
rs.isNull(i) | ||
? null | ||
: Readable.from(streamBlob(rsStreams, rs._rowPosition, i), { objectMode: false }) | ||
: col.nativeType === 13 // return binary type as simple buffer | ||
? await getValue(i) | ||
: Readable.from(streamBlob(rsStreams, rs._rowPosition, i), { objectMode: false }) | ||
: values[i] | ||
@@ -371,3 +373,3 @@ } | ||
if (read < binaryBuffer.byteLength) { | ||
yield binaryBuffer.slice(0, read) | ||
yield binaryBuffer.subarray(0, read) | ||
break | ||
@@ -374,0 +376,0 @@ } |
@@ -99,3 +99,3 @@ const { Readable, Stream } = require('stream') | ||
row[col].createReadStream?.() | ||
|| Readable.from(echoStream(row[col]), { objectMode: false }) | ||
|| row[col] | ||
) | ||
@@ -102,0 +102,0 @@ : row[col] |
@@ -17,3 +17,3 @@ const cds = require('@sap/cds') | ||
if (dependencies['@sap/hana-client']) return module.exports['hana-client'] | ||
} catch (e) { | ||
} catch { | ||
console.trace(`WARNING! Unable to require the project's package.json at "${cds.root + '/package.json'}". Please check your project setup.`) | ||
@@ -25,3 +25,3 @@ } | ||
return module.exports.hdb | ||
} catch (e) { | ||
} catch { | ||
return module.exports['hana-client'] | ||
@@ -28,0 +28,0 @@ } |
@@ -103,3 +103,3 @@ const fs = require('fs') | ||
ensureDBC() { | ||
return this.dbc || cds.error`Database is disconnected` | ||
return this.dbc || cds.error`Database connection is ${this._done || 'disconnected'}` | ||
} | ||
@@ -121,3 +121,3 @@ | ||
if (!query.target) { | ||
try { this.infer(query) } catch (e) { /**/ } | ||
try { this.infer(query) } catch { /**/ } | ||
} | ||
@@ -138,7 +138,13 @@ if (!query.target || query.target._unresolved) { | ||
const isSimple = temporary.length + blobs.length + withclause.length === 0 | ||
// REVISIT: add prepare options when param:true is used | ||
const sqlScript = isLockQuery ? sql : this.wrapTemporary(temporary, withclause, blobs) | ||
let rows = (values?.length || blobs.length > 0) | ||
? await (await this.prepare(sqlScript, blobs.length)).all(values || []) | ||
: await this.exec(sqlScript) | ||
const sqlScript = isLockQuery || isSimple ? sql : this.wrapTemporary(temporary, withclause, blobs) | ||
let rows | ||
if (values?.length || blobs.length > 0) { | ||
const ps = await this.prepare(sqlScript, blobs.length) | ||
rows = this.ensureDBC() && await ps.all(values || []) | ||
} else { | ||
rows = await this.exec(sqlScript) | ||
} | ||
@@ -153,3 +159,3 @@ if (isLockQuery) { | ||
if (rows.length) { | ||
if (rows.length && !isSimple) { | ||
rows = this.parseRows(rows) | ||
@@ -172,5 +178,5 @@ } | ||
? HANAVERSION <= 2 | ||
? entries.reduce((l, c) => l.then(() => ps.run(c)), Promise.resolve(0)) | ||
: entries.length > 1 ? await ps.runBatch(entries) : await ps.run(entries[0]) | ||
: ps.run()) | ||
? entries.reduce((l, c) => l.then(() => this.ensureDBC() && ps.run(c)), Promise.resolve(0)) | ||
: entries.length > 1 ? this.ensureDBC() && await ps.runBatch(entries) : this.ensureDBC() && await ps.run(entries[0]) | ||
: this.ensureDBC() && ps.run()) | ||
return new this.class.InsertResults(cqn, results) | ||
@@ -235,3 +241,3 @@ } catch (err) { | ||
const data = Object.assign(JSON.parse(row._json_), expands, blobs) | ||
Object.keys(blobs).forEach(k => (data[k] = this._stream(row[k] || data[k]))) | ||
Object.keys(blobs).forEach(k => (data[k] = row[k] || data[k])) | ||
@@ -260,7 +266,9 @@ // REVISIT: try to unify with handleLevel from base driver used for streaming | ||
level.data.push?.(data) | ||
levels.push({ | ||
data: data, | ||
path: row._path_, | ||
expands, | ||
}) | ||
if (row._path_ !== level.path) { | ||
levels.push({ | ||
data: data, | ||
path: row._path_, | ||
expands, | ||
}) | ||
} | ||
break | ||
@@ -450,3 +458,3 @@ } | ||
if (expand === 'root') { | ||
if (expand === 'root' && this._outputColumns) { | ||
this.cqn = q | ||
@@ -482,8 +490,6 @@ const fromSQL = this.from({ ref: [alias] }) | ||
const parent = src | ||
let fkeys = x.element._foreignKeys | ||
if (typeof fkeys === 'function') fkeys = fkeys.call(x.element) | ||
fkeys.forEach(k => { | ||
if (!k?.parentElement?.name) return // not all associations have foreign key references | ||
if (!parent.SELECT.columns.find(c => this.column_name(c) === k.parentElement.name)) { | ||
parent.SELECT.columns.push({ ref: [parent.as, k.parentElement.name] }) | ||
this.extractForeignKeys(x.SELECT.where, parent.as, []).forEach(ref => { | ||
const columnName = this.column_name(ref) | ||
if (!parent.SELECT.columns.find(c => this.column_name(c) === columnName)) { | ||
parent.SELECT.columns.push(ref) | ||
} | ||
@@ -540,2 +546,11 @@ }) | ||
this.blobs.push(...blobColumns.filter(b => !this.blobs.includes(b))) | ||
if ( | ||
cds.env.features.sql_simple_queries && | ||
structures.length + ObjectKeys(expands).length + ObjectKeys(blobs).length === 0 && | ||
!q?.src?.SELECT?.parent && | ||
this.temporary.length === 0 | ||
) { | ||
return `${sql}` | ||
} | ||
expands = this.string(JSON.stringify(expands)) | ||
@@ -548,4 +563,4 @@ blobs = this.string(JSON.stringify(blobs)) | ||
const rawJsonColumn = sql.length | ||
? `(SELECT ${sql} FROM JSON_TABLE('[{}]', '$' COLUMNS(I FOR ORDINALITY)) FOR JSON ('format'='no', 'omitnull'='no', 'arraywrap'='no') RETURNS NVARCHAR(2147483647)) AS "_json_"` | ||
: `'{}' AS "_json_"` | ||
? `(SELECT ${sql} FROM JSON_TABLE('[{}]', '$' COLUMNS(I FOR ORDINALITY)) FOR JSON ('format'='no', 'omitnull'='no', 'arraywrap'='no') RETURNS NVARCHAR(2147483647))` | ||
: `'{}'` | ||
@@ -557,10 +572,10 @@ let jsonColumn = rawJsonColumn | ||
const structuresConcat = structures | ||
.map(x => { | ||
.map((x, i) => { | ||
const name = this.column_name(x) | ||
return `',"${name}":' || COALESCE(${this.quote(name)},'null')` | ||
return `'${i ? ',' : '{'}"${name}":' || COALESCE(${this.quote(name)},'null')` | ||
}) | ||
.join(' || ') | ||
jsonColumn = sql.length | ||
? `SUBSTRING("_json_", 1, LENGTH("_json_") - 1) || ${structuresConcat} || '}' as "_json_"` | ||
: `'{' || '${structuresConcat.substring(2)} || '}' as "_json_"` | ||
? `${structuresConcat} || ',' || SUBSTRING(${rawJsonColumn}, 2)` | ||
: `${structuresConcat} || '}'` | ||
} | ||
@@ -570,7 +585,7 @@ | ||
let outputColumns = '' | ||
outputColumns = `_path_ as "_path_",${blobs} as "_blobs_",${expands} as "_expands_",${jsonColumn}` | ||
outputColumns = `_path_ as "_path_",${blobs} as "_blobs_",${expands} as "_expands_",${jsonColumn} as "_json_"` | ||
if (blobColumns.length) | ||
outputColumns = `${outputColumns},${blobColumns.map(b => `${this.quote(b)} as "${b.replace(/"/g, '""')}"`)}` | ||
this._outputColumns = outputColumns | ||
sql = `*,${path} as _path_,${rawJsonColumn}` | ||
sql = `*,${path} as _path_` | ||
} | ||
@@ -1051,4 +1066,5 @@ return sql | ||
Binary: () => `NVARCHAR(2147483647)`, | ||
array: () => `NVARCHAR(2147483647)`, | ||
array: () => `NVARCHAR(2147483647) FORMAT JSON`, | ||
Vector: () => `NVARCHAR(2147483647)`, | ||
Decimal: () => `DECIMAL`, | ||
@@ -1094,5 +1110,4 @@ // JavaScript types | ||
Int64: expr => `TO_NVARCHAR(${expr})`, | ||
// REVISIT: always cast to string in next major | ||
// Reading decimal as string to not loose precision | ||
Decimal: cds.env.features.string_decimals ? expr => `TO_NVARCHAR(${expr})` : undefined, | ||
Decimal: expr => `TO_NVARCHAR(${expr})`, | ||
@@ -1109,3 +1124,3 @@ // HANA types | ||
let ps = await this.prepare(sql) | ||
return (await ps.run(values)).changes | ||
return (this.ensureDBC() && await ps.run(values)).changes | ||
} catch (err) { | ||
@@ -1140,3 +1155,3 @@ // Allow drop to fail when the view or table does not exist | ||
const ps = await this.prepare(query) | ||
return ps.proc(data, outParameters) | ||
return this.ensureDBC() && ps.proc(data, outParameters) | ||
} | ||
@@ -1208,3 +1223,3 @@ | ||
const stmt = await this.dbc.prepare(createContainerDatabase) | ||
const res = await stmt.run([creds.user, creds.password, creds.containerGroup, !clean]) | ||
const res = this.ensureDBC() && await stmt.run([creds.user, creds.password, creds.containerGroup, !clean]) | ||
res && DEBUG?.(res.changes.map(r => r.MESSAGE).join('\n')) | ||
@@ -1251,4 +1266,4 @@ } finally { | ||
const stmt = await this.dbc.prepare(createContainerTenant.replaceAll('{{{GROUP}}}', creds.containerGroup)) | ||
const res = await stmt.run([creds.user, creds.password, creds.schema, !clean]) | ||
res && DEBUG?.(res.changes.map(r => r.MESSAGE).join('\n')) | ||
const res = this.ensureDBC() && await stmt.run([creds.user, creds.password, creds.schema, !clean]) | ||
res && DEBUG?.(res.changes.map?.(r => r.MESSAGE).join('\n')) | ||
} finally { | ||
@@ -1288,6 +1303,2 @@ await this.dbc.disconnect() | ||
Buffer.prototype.toJSON = function () { | ||
return this.toString('hex') | ||
} | ||
function _not_unique(err, code) { | ||
@@ -1294,0 +1305,0 @@ if (err.code === 301) |
{ | ||
"name": "@cap-js/hana", | ||
"version": "0.4.0", | ||
"version": "1.0.0", | ||
"description": "CDS database service for SAP HANA", | ||
@@ -18,6 +18,2 @@ "homepage": "https://cap.cloud.sap/", | ||
], | ||
"engines": { | ||
"node": ">=16", | ||
"npm": ">=8" | ||
}, | ||
"scripts": { | ||
@@ -24,0 +20,0 @@ "test": "npm start && jest --silent", |
No v1
QualityPackage is not semver >=1. This means it is not stable and does not support ^ ranges.
Found 1 instance in 1 package
132152
2574
0