@cap-js/postgres 1.13.0 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +28 -0
- package/lib/PostgresService.js +58 -35
- package/lib/cql-functions.js +1 -1
- package/package.json +6 -6
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,34 @@
|
|
|
4
4
|
- The format is based on [Keep a Changelog](http://keepachangelog.com/).
|
|
5
5
|
- This project adheres to [Semantic Versioning](http://semver.org/).
|
|
6
6
|
|
|
7
|
+
## [2.0.0](https://github.com/cap-js/cds-dbs/compare/postgres-v1.14.0...postgres-v2.0.0) (2025-05-07)
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
### ⚠ BREAKING CHANGES
|
|
11
|
+
|
|
12
|
+
* update peer dependency to @sap/cds@9 ([#1178](https://github.com/cap-js/cds-dbs/issues/1178))
|
|
13
|
+
* update dependency to @cap-js/db-service@2 ([#1178](https://github.com/cap-js/cds-dbs/issues/1178))
|
|
14
|
+
* Unfiltered db constraint errors ([#1165](https://github.com/cap-js/cds-dbs/issues/1165))
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
### Added
|
|
18
|
+
|
|
19
|
+
* Support for hierarchical queries ([#1093](https://github.com/cap-js/cds-dbs/issues/1093)) ([246e0b3](https://github.com/cap-js/cds-dbs/commit/246e0b38840f7e132ea49cae335b6be7a55354b3))
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
### Changed
|
|
23
|
+
|
|
24
|
+
* Unfiltered db constraint errors ([#1165](https://github.com/cap-js/cds-dbs/issues/1165)) ([ff39e22](https://github.com/cap-js/cds-dbs/commit/ff39e22ac6cd3f20c98bc31c1a6bb828aa009796))
|
|
25
|
+
* update peer dependency to @sap/cds@9 ([#1178](https://github.com/cap-js/cds-dbs/issues/1178)) ([#1178](https://github.com/cap-js/cds-dbs/issues/1178)) ([0507edd](https://github.com/cap-js/cds-dbs/commit/0507edd4e1dcb98983b1fb65ade1344d978b7524))
|
|
26
|
+
* update dependency to @cap-js/db-service@2 ([#1178](https://github.com/cap-js/cds-dbs/issues/1178)) ([#1178](https://github.com/cap-js/cds-dbs/issues/1178)) ([0507edd](https://github.com/cap-js/cds-dbs/commit/0507edd4e1dcb98983b1fb65ade1344d978b7524))
|
|
27
|
+
|
|
28
|
+
## [1.14.0](https://github.com/cap-js/cds-dbs/compare/postgres-v1.13.0...postgres-v1.14.0) (2025-04-17)
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
### Added
|
|
32
|
+
|
|
33
|
+
* Result set streaming ([#702](https://github.com/cap-js/cds-dbs/issues/702)) ([2fe02ea](https://github.com/cap-js/cds-dbs/commit/2fe02eafd02993e5697efbdab90ad997fb2c9e00))
|
|
34
|
+
|
|
7
35
|
## [1.13.0](https://github.com/cap-js/cds-dbs/compare/postgres-v1.12.0...postgres-v1.13.0) (2025-03-31)
|
|
8
36
|
|
|
9
37
|
|
package/lib/PostgresService.js
CHANGED
|
@@ -4,7 +4,6 @@ const cds = require('@sap/cds')
|
|
|
4
4
|
const crypto = require('crypto')
|
|
5
5
|
const { Writable, Readable } = require('stream')
|
|
6
6
|
const sessionVariableMap = require('./session.json')
|
|
7
|
-
const SANITIZE_VALUES = process.env.NODE_ENV === 'production' && cds.env.log.sanitize_values !== false
|
|
8
7
|
|
|
9
8
|
class PostgresService extends SQLService {
|
|
10
9
|
init() {
|
|
@@ -178,9 +177,9 @@ GROUP BY k
|
|
|
178
177
|
throw enhanceError(e, sql)
|
|
179
178
|
}
|
|
180
179
|
},
|
|
181
|
-
stream: async (values, one) => {
|
|
180
|
+
stream: async (values, one, objectMode) => {
|
|
182
181
|
try {
|
|
183
|
-
const streamQuery = new QueryStream({ ...query, values: this._getValues(values) }, one)
|
|
182
|
+
const streamQuery = new QueryStream({ ...query, values: this._getValues(values) }, one, objectMode)
|
|
184
183
|
return await this.dbc.query(streamQuery)
|
|
185
184
|
} catch (e) {
|
|
186
185
|
throw enhanceError(e, sql)
|
|
@@ -305,7 +304,8 @@ GROUP BY k
|
|
|
305
304
|
}
|
|
306
305
|
}
|
|
307
306
|
|
|
308
|
-
async onSELECT(
|
|
307
|
+
async onSELECT(req) {
|
|
308
|
+
const { query, data } = req
|
|
309
309
|
// workaround for chunking odata streaming
|
|
310
310
|
if (query.SELECT?.columns?.find(col => col.as === '$mediaContentType')) {
|
|
311
311
|
const columns = query.SELECT.columns
|
|
@@ -323,26 +323,50 @@ GROUP BY k
|
|
|
323
323
|
res[this.class.CQN2SQL.prototype.column_name(binary[0])] = stream
|
|
324
324
|
return res
|
|
325
325
|
}
|
|
326
|
-
return super.onSELECT(
|
|
326
|
+
return super.onSELECT(req)
|
|
327
327
|
}
|
|
328
328
|
|
|
329
|
-
async onINSERT(req) {
|
|
330
|
-
try {
|
|
331
|
-
return await super.onINSERT(req)
|
|
332
|
-
} catch (err) {
|
|
333
|
-
throw _not_unique(err, 'ENTITY_ALREADY_EXISTS', req.data)
|
|
334
|
-
}
|
|
335
|
-
}
|
|
336
329
|
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
330
|
+
static CQN2SQL = class CQN2Postgres extends SQLService.CQN2SQL {
|
|
331
|
+
|
|
332
|
+
render_with() {
|
|
333
|
+
const sql = this.sql
|
|
334
|
+
let recursive = false
|
|
335
|
+
const prefix = this._with.map(q => {
|
|
336
|
+
let sql
|
|
337
|
+
if ('SELECT' in q) sql = `${this.quote(q.as)} AS (${this.SELECT(q)})`
|
|
338
|
+
else if ('SET' in q) {
|
|
339
|
+
recursive = true
|
|
340
|
+
const { SET } = q
|
|
341
|
+
const isDepthFirst = SET.orderBy?.length && (SET.orderBy[0].sort?.toLowerCase() === 'desc' || SET.orderBy[0].sort === -1)
|
|
342
|
+
let alias = q.as
|
|
343
|
+
if (isDepthFirst) {
|
|
344
|
+
alias = alias + '_depth_first'
|
|
345
|
+
SET.args[1].SELECT.from.args.forEach(r => { if (r.ref[0] === q.as) r.ref[0] = alias })
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
sql = `${this.quote(alias)}(${SET.args[0].SELECT.columns?.map(c => this.quote(this.column_name(c))) || ''}) AS (${
|
|
349
|
+
// Root select
|
|
350
|
+
this.SELECT(SET.args[0])} ${
|
|
351
|
+
// Union clause
|
|
352
|
+
SET.op?.toUpperCase() || 'UNION'} ${SET.all ? 'ALL' : ''} ${
|
|
353
|
+
// Repeated join query
|
|
354
|
+
this.SELECT(SET.args[1])
|
|
355
|
+
})${
|
|
356
|
+
// Leverage Postgres specific depth first syntax
|
|
357
|
+
SET.orderBy?.length
|
|
358
|
+
? ` SEARCH DEPTH FIRST BY ${SET.orderBy.map(r => this.ref(r))} SET "$DEPTH$"`
|
|
359
|
+
: ''
|
|
360
|
+
}`
|
|
361
|
+
|
|
362
|
+
// Enforce depth sorting for consuming queries
|
|
363
|
+
if (isDepthFirst) sql += `,${this.quote(q.as)} AS (SELECT * FROM ${this.quote(q.as + '_depth_first')} ORDER BY "$DEPTH$")`
|
|
364
|
+
}
|
|
365
|
+
return { sql }
|
|
366
|
+
})
|
|
367
|
+
this.sql = `WITH${recursive ? ' RECURSIVE' : ''} ${prefix.map(p => p.sql)} ${sql}`
|
|
342
368
|
}
|
|
343
|
-
}
|
|
344
369
|
|
|
345
|
-
static CQN2SQL = class CQN2Postgres extends SQLService.CQN2SQL {
|
|
346
370
|
_orderBy(orderBy, localized, locale) {
|
|
347
371
|
return orderBy.map(c => {
|
|
348
372
|
const nulls = c.nulls || (c.sort?.toLowerCase() === 'desc' || c.sort === -1 ? 'LAST' : 'FIRST')
|
|
@@ -574,7 +598,6 @@ GROUP BY k
|
|
|
574
598
|
|
|
575
599
|
// Convert ST types back to WKT format
|
|
576
600
|
'cds.hana.ST_POINT': expr => `ST_AsText(${expr})`,
|
|
577
|
-
'cds.hana.ST_POINT': expr => `ST_AsText(${expr})`,
|
|
578
601
|
}
|
|
579
602
|
}
|
|
580
603
|
|
|
@@ -667,7 +690,7 @@ GROUP BY k
|
|
|
667
690
|
}
|
|
668
691
|
|
|
669
692
|
class QueryStream extends Query {
|
|
670
|
-
constructor(config, one) {
|
|
693
|
+
constructor(config, one, objectMode) {
|
|
671
694
|
// REVISIT: currently when setting the row chunk size
|
|
672
695
|
// it results in an inconsistent connection state
|
|
673
696
|
// if (!one) config.rows = 1000
|
|
@@ -676,6 +699,7 @@ class QueryStream extends Query {
|
|
|
676
699
|
this._one = one || config.one
|
|
677
700
|
|
|
678
701
|
this.stream = new Readable({
|
|
702
|
+
objectMode,
|
|
679
703
|
read: this.rows
|
|
680
704
|
? () => {
|
|
681
705
|
this.stream.pause()
|
|
@@ -693,7 +717,7 @@ class QueryStream extends Query {
|
|
|
693
717
|
this._prom = new Promise((resolve, reject) => {
|
|
694
718
|
this.once('error', reject)
|
|
695
719
|
this.once('end', () => {
|
|
696
|
-
if (!this._one) this.push(this.constructor.close)
|
|
720
|
+
if (!objectMode && !this._one) this.push(this.constructor.close)
|
|
697
721
|
this.push(null)
|
|
698
722
|
if (this.stream.isPaused()) this.stream.resume()
|
|
699
723
|
resolve(null)
|
|
@@ -736,10 +760,15 @@ class QueryStream extends Query {
|
|
|
736
760
|
} else {
|
|
737
761
|
this.handleDataRow = msg => {
|
|
738
762
|
const val = msg.fields[0]
|
|
739
|
-
|
|
763
|
+
const objectMode = this.stream.readableObjectMode
|
|
764
|
+
if (!objectMode && !this._one && val !== null) this.push(this.constructor.open)
|
|
740
765
|
this.emit('row', val)
|
|
741
|
-
this.push(val)
|
|
766
|
+
this.push(objectMode ? JSON.parse(val) : val)
|
|
767
|
+
|
|
742
768
|
delete this.handleDataRow
|
|
769
|
+
if (objectMode) {
|
|
770
|
+
this.handleDataRow = this.handleDataRowObjectMode
|
|
771
|
+
}
|
|
743
772
|
}
|
|
744
773
|
}
|
|
745
774
|
return super.handleRowDescription(msg)
|
|
@@ -751,6 +780,11 @@ class QueryStream extends Query {
|
|
|
751
780
|
this.push(msg.fields[0])
|
|
752
781
|
}
|
|
753
782
|
|
|
783
|
+
// Called when a new row is received
|
|
784
|
+
handleDataRowObjectMode(msg) {
|
|
785
|
+
this.push(JSON.parse(msg.fields[0]))
|
|
786
|
+
}
|
|
787
|
+
|
|
754
788
|
// Called when a new binary row is received
|
|
755
789
|
handleBinaryRow(msg) {
|
|
756
790
|
const val = msg.fields[0] === null ? null : this._result._parsers[0](msg.fields[0])
|
|
@@ -866,15 +900,4 @@ class ParameterStream extends Writable {
|
|
|
866
900
|
}
|
|
867
901
|
}
|
|
868
902
|
|
|
869
|
-
function _not_unique(err, code, data) {
|
|
870
|
-
if (err.code === '23505')
|
|
871
|
-
return Object.assign(err, {
|
|
872
|
-
originalMessage: err.message, // FIXME: required because of next line
|
|
873
|
-
message: code, // FIXME: misusing message as code
|
|
874
|
-
code: 400, // FIXME: misusing code as (http) status
|
|
875
|
-
})
|
|
876
|
-
if (data) err.values = SANITIZE_VALUES ? ['***'] : data
|
|
877
|
-
return err
|
|
878
|
-
}
|
|
879
|
-
|
|
880
903
|
module.exports = PostgresService
|
package/lib/cql-functions.js
CHANGED
package/package.json
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cap-js/postgres",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2.0.0",
|
|
4
4
|
"description": "CDS database service for Postgres",
|
|
5
5
|
"homepage": "https://github.com/cap-js/cds-dbs/tree/main/postgres#cds-database-service-for-postgres",
|
|
6
6
|
"repository": {
|
|
7
7
|
"type": "git",
|
|
8
|
-
"url": "https://github.com/cap-js/cds-dbs"
|
|
8
|
+
"url": "git+https://github.com/cap-js/cds-dbs.git"
|
|
9
9
|
},
|
|
10
10
|
"bugs": {
|
|
11
11
|
"url": "https://github.com/cap-js/cds-dbs/issues"
|
|
@@ -27,12 +27,12 @@
|
|
|
27
27
|
"start": "docker compose -f pg-stack.yml up -d"
|
|
28
28
|
},
|
|
29
29
|
"dependencies": {
|
|
30
|
-
"@cap-js/db-service": "^
|
|
30
|
+
"@cap-js/db-service": "^2",
|
|
31
31
|
"pg": "^8"
|
|
32
32
|
},
|
|
33
33
|
"peerDependencies": {
|
|
34
|
-
"@sap/cds": ">=
|
|
35
|
-
"@sap/cds-dk": ">=
|
|
34
|
+
"@sap/cds": ">=9",
|
|
35
|
+
"@sap/cds-dk": ">=9"
|
|
36
36
|
},
|
|
37
37
|
"peerDependenciesMeta": {
|
|
38
38
|
"@sap/cds-dk": {
|
|
@@ -76,5 +76,5 @@
|
|
|
76
76
|
}
|
|
77
77
|
}
|
|
78
78
|
},
|
|
79
|
-
"license": "
|
|
79
|
+
"license": "Apache-2.0"
|
|
80
80
|
}
|