diff --git a/db-service/lib/cqn2sql.js b/db-service/lib/cqn2sql.js index 57fa1b2a8..3df42a6ef 100644 --- a/db-service/lib/cqn2sql.js +++ b/db-service/lib/cqn2sql.js @@ -243,7 +243,7 @@ class CQN2SQLRenderer { let cols = SELECT.columns.map(x => { const name = this.column_name(x) - let col = `'${name}',${this.output_converter4(x.element, this.quote(name))}` + let col = `'$."${name}"',${this.output_converter4(x.element, this.quote(name))}` if (x.SELECT?.count) { // Return both the sub select and the count for @odata.count const qc = cds.ql.clone(x, { columns: [{ func: 'count' }], one: 1, limit: 0, orderBy: 0 }) @@ -252,21 +252,14 @@ class CQN2SQLRenderer { return col }).flat() - // Prevent SQLite from hitting function argument limit of 100 - let obj = '' + const isRoot = SELECT.expand === 'root' - if (cols.length < 50) obj = `json_object(${cols.slice(0, 50)})` - else { - const chunks = [] - for (let i = 0; i < cols.length; i += 50) { - chunks.push(`json_object(${cols.slice(i, i + 50)})`) - } - // REVISIT: json_merge is a user defined function, bad performance! - obj = `json_merge(${chunks})` + // Prevent SQLite from hitting function argument limit of 100 + let obj = "'{}'" + for (let i = 0; i < cols.length; i += 48) { + obj = `jsonb_insert(${obj},${cols.slice(i, i + 48)})` } - - - return `SELECT ${SELECT.one || SELECT.expand === 'root' ? obj : `json_group_array(${obj.includes('json_merge') ? `json_insert(${obj})` : obj})`} as _json_ FROM (${sql})` + return `SELECT ${isRoot || SELECT.one ? obj.replace('jsonb', 'json') : `jsonb_group_array(${obj})`} as _json_ FROM (${sql})` } /** @@ -695,7 +688,7 @@ class CQN2SQLRenderer { columns = columns.map(c => { if (q.elements?.[c.name]?.['@cds.extension']) return { name: 'extensions__', - sql: `json_set(extensions__,${this.string('$."' + c.name + '"')},${c.sql})`, + sql: `jsonb_set(extensions__,${this.string('$."' + c.name + '"')},${c.sql})`, } return c }) diff --git a/package-lock.json b/package-lock.json index 44c612479..f85f215ab 100644 --- a/package-lock.json +++ b/package-lock.json @@ -29,7 +29,7 @@ }, "db-service": { "name": "@cap-js/db-service", - "version": "1.4.0", + "version": "1.5.1", "license": "SEE LICENSE", "devDependencies": { "@typescript-eslint/eslint-plugin": "^6.2.0", @@ -45,9 +45,10 @@ }, "hana": { "name": "@cap-js/hana", - "version": "1.0.0", + "version": "0.0.3", "license": "SEE LICENSE", "dependencies": { + "@cap-js/db-service": "^1.3.1", "hdb": "^0.19.5" }, "engines": { @@ -5658,9 +5659,9 @@ ] }, "node_modules/better-sqlite3": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/better-sqlite3/-/better-sqlite3-9.0.0.tgz", - "integrity": "sha512-lDxQ9qg/XuUHZG6xzrQaMHkNWl37t35/LPB/VJGV8DdScSuGFNfFSqgscXEd8UIuyk/d9wU8iaMxQa4If5Wqog==", + "version": "9.3.0", + "resolved": "https://registry.npmjs.org/better-sqlite3/-/better-sqlite3-9.3.0.tgz", + "integrity": "sha512-ww73jVpQhRRdS9uMr761ixlkl4bWoXi8hMQlBGhoN6vPNlUHpIsNmw4pKN6kjknlt/wopdvXHvLk1W75BI+n0Q==", "hasInstallScript": true, "dependencies": { "bindings": "^1.5.0", diff --git a/sqlite/lib/SQLiteService.js b/sqlite/lib/SQLiteService.js index 5ecf1da38..6839dd14e 100644 --- a/sqlite/lib/SQLiteService.js +++ b/sqlite/lib/SQLiteService.js @@ -37,9 +37,6 @@ class SQLiteService extends SQLService { dbc.function('minute', deterministic, d => d === null ? null : toDate(d, true).getUTCMinutes()) dbc.function('second', deterministic, d => d === null ? null : toDate(d, true).getUTCSeconds()) - dbc.function('json_merge', { varargs: true, deterministic: true }, (...args) => - args.join('').replace(/}{/g, ','), - ) if (!dbc.memory) dbc.pragma('journal_mode = WAL') return dbc }, @@ -84,13 +81,11 @@ class SQLiteService extends SQLService { async _run(stmt, binding_params) { for (let i = 0; i < binding_params.length; i++) { const val = binding_params[i] + if (val instanceof Readable) { + binding_params[i] = await convStrm[val.type === 'json' ? 'text' : 'buffer'](val) + } if (Buffer.isBuffer(val)) { - binding_params[i] = Buffer.from(val.base64Slice()) - } else if (typeof val === 'object' && val && val.pipe) { - // REVISIT: stream.setEncoding('base64') sometimes misses the last bytes - // if (val.type === 'binary') val.setEncoding('base64') - binding_params[i] = await convStrm.buffer(val) - if (val.type === 'binary') binding_params[i] = Buffer.from(binding_params[i].toString('base64')) + binding_params[i] = Buffer.from(val.toString('base64')) } } return stmt.run(binding_params) @@ -254,7 +249,7 @@ class SQLiteService extends SQLService { } catch (err) { throw _not_unique(err, 'UNIQUE_CONSTRAINT_VIOLATION') || err } - } + } } // function _not_null (err) { diff --git a/sqlite/package.json b/sqlite/package.json index c243f6660..a50e040d1 100644 --- a/sqlite/package.json +++ b/sqlite/package.json @@ -31,7 +31,7 @@ }, "dependencies": { "@cap-js/db-service": "^1.3.1", - "better-sqlite3": "^9" + "better-sqlite3": "^9.3.0" }, "peerDependencies": { "@sap/cds": ">=7"