@cap-js/postgres 1.12.0 → 1.14.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 CHANGED
@@ -4,6 +4,27 @@
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
+ ## [1.14.0](https://github.com/cap-js/cds-dbs/compare/postgres-v1.13.0...postgres-v1.14.0) (2025-04-17)
8
+
9
+
10
+ ### Added
11
+
12
+ * Result set streaming ([#702](https://github.com/cap-js/cds-dbs/issues/702)) ([2fe02ea](https://github.com/cap-js/cds-dbs/commit/2fe02eafd02993e5697efbdab90ad997fb2c9e00))
13
+
14
+ ## [1.13.0](https://github.com/cap-js/cds-dbs/compare/postgres-v1.12.0...postgres-v1.13.0) (2025-03-31)
15
+
16
+
17
+ ### Added
18
+
19
+ * **collate:** only collate if locale is provided ([#1060](https://github.com/cap-js/cds-dbs/issues/1060)) ([dedd768](https://github.com/cap-js/cds-dbs/commit/dedd768c085aa29be0e38db11f11678ff55d5b7b))
20
+ * **forUpdate:** ignore locked ([#1074](https://github.com/cap-js/cds-dbs/issues/1074)) ([163480b](https://github.com/cap-js/cds-dbs/commit/163480b245b18a2829cd871c2f053c82bcc1abef))
21
+
22
+
23
+ ### Fixed
24
+
25
+ * consider `nulls first | last` on `orderBy` ([#1064](https://github.com/cap-js/cds-dbs/issues/1064)) ([c6bed60](https://github.com/cap-js/cds-dbs/commit/c6bed60f0d93b9f4a73c976727f30172707c60d9)), closes [#1062](https://github.com/cap-js/cds-dbs/issues/1062)
26
+ * Persist assert_integrity feature ([#1032](https://github.com/cap-js/cds-dbs/issues/1032)) ([2956279](https://github.com/cap-js/cds-dbs/commit/2956279777ac94330c98373d8bca32cf0f8e967e))
27
+
7
28
  ## [1.12.0](https://github.com/cap-js/cds-dbs/compare/postgres-v1.11.1...postgres-v1.12.0) (2025-03-04)
8
29
 
9
30
 
package/cds-plugin.js CHANGED
@@ -25,11 +25,23 @@ cds.build?.register?.('postgres', class PostgresBuildPlugin extends cds.build.Pl
25
25
  if (fs.existsSync(path.join(this.task.src, 'package.json'))) {
26
26
  promises.push(this.copy(path.join(this.task.src, 'package.json')).to('package.json'))
27
27
  } else {
28
- promises.push(
29
- this.write({
30
- dependencies: { '@sap/cds': '^8', '@cap-js/postgres': '^1' },
31
- scripts: { start: 'cds-deploy' },
32
- }).to('package.json'),
28
+ const packageJson = {
29
+ dependencies: {
30
+ '@sap/cds': '^8',
31
+ '@cap-js/postgres': '^1'
32
+ },
33
+ scripts: {
34
+ start: 'cds-deploy'
35
+ }
36
+ }
37
+ const assertIntegrity = cds.env?.features?.assert_integrity
38
+ if (assertIntegrity) {
39
+ packageJson.cds ??= {}
40
+ packageJson.cds.features ??= {}
41
+ packageJson.cds.features.assert_integrity = assertIntegrity
42
+ }
43
+ promises.push(
44
+ this.write(packageJson).to('package.json')
33
45
  )
34
46
  }
35
47
  promises.push(this.write(cds.compile.to.json(model)).to(path.join('db', 'csn.json')))
@@ -178,9 +178,9 @@ GROUP BY k
178
178
  throw enhanceError(e, sql)
179
179
  }
180
180
  },
181
- stream: async (values, one) => {
181
+ stream: async (values, one, objectMode) => {
182
182
  try {
183
- const streamQuery = new QueryStream({ ...query, values: this._getValues(values) }, one)
183
+ const streamQuery = new QueryStream({ ...query, values: this._getValues(values) }, one, objectMode)
184
184
  return await this.dbc.query(streamQuery)
185
185
  } catch (e) {
186
186
  throw enhanceError(e, sql)
@@ -305,7 +305,8 @@ GROUP BY k
305
305
  }
306
306
  }
307
307
 
308
- async onSELECT({ query, data }) {
308
+ async onSELECT(req) {
309
+ const { query, data } = req
309
310
  // workaround for chunking odata streaming
310
311
  if (query.SELECT?.columns?.find(col => col.as === '$mediaContentType')) {
311
312
  const columns = query.SELECT.columns
@@ -323,7 +324,7 @@ GROUP BY k
323
324
  res[this.class.CQN2SQL.prototype.column_name(binary[0])] = stream
324
325
  return res
325
326
  }
326
- return super.onSELECT({ query, data })
327
+ return super.onSELECT(req)
327
328
  }
328
329
 
329
330
  async onINSERT(req) {
@@ -344,14 +345,15 @@ GROUP BY k
344
345
 
345
346
  static CQN2SQL = class CQN2Postgres extends SQLService.CQN2SQL {
346
347
  _orderBy(orderBy, localized, locale) {
347
- return orderBy.map(
348
- localized
349
- ? c =>
350
- this.expr(c) +
351
- (c.element?.[this.class._localized] ? ` COLLATE "${locale}"` : '') +
352
- (c.sort?.toLowerCase() === 'desc' || c.sort === -1 ? ' DESC NULLS LAST' : ' ASC NULLS FIRST')
353
- : c => this.expr(c) + (c.sort?.toLowerCase() === 'desc' || c.sort === -1 ? ' DESC NULLS LAST' : ' ASC NULLS FIRST'),
354
- )
348
+ return orderBy.map(c => {
349
+ const nulls = c.nulls || (c.sort?.toLowerCase() === 'desc' || c.sort === -1 ? 'LAST' : 'FIRST')
350
+ const o = localized
351
+ ? this.expr(c) +
352
+ (c.element?.[this.class._localized] && locale ? ` COLLATE "${locale}"` : '') +
353
+ (c.sort?.toLowerCase() === 'desc' || c.sort === -1 ? ' DESC' : ' ASC')
354
+ : this.expr(c) + (c.sort?.toLowerCase() === 'desc' || c.sort === -1 ? ' DESC' : ' ASC')
355
+ return o + ' NULLS ' + (nulls.toLowerCase() === 'first' ? 'FIRST' : 'LAST')
356
+ })
355
357
  }
356
358
 
357
359
  orderBy(orderBy) {
@@ -465,9 +467,11 @@ GROUP BY k
465
467
  // Postgres does not support locking columns only tables which makes of unapplicable
466
468
  // Postgres does not support "wait n" it only supports "nowait"
467
469
  forUpdate(update) {
468
- const { wait } = update
469
- if (wait === 0) return 'FOR UPDATE NOWAIT'
470
- return 'FOR UPDATE'
470
+ const { wait, ignoreLocked } = update
471
+ let sql = 'FOR UPDATE'
472
+ if (wait === 0) sql += ' NOWAIT'
473
+ if (ignoreLocked) sql += ' SKIP LOCKED'
474
+ return sql
471
475
  }
472
476
 
473
477
  forShareLock(lock) {
@@ -571,7 +575,6 @@ GROUP BY k
571
575
 
572
576
  // Convert ST types back to WKT format
573
577
  'cds.hana.ST_POINT': expr => `ST_AsText(${expr})`,
574
- 'cds.hana.ST_POINT': expr => `ST_AsText(${expr})`,
575
578
  }
576
579
  }
577
580
 
@@ -664,7 +667,7 @@ GROUP BY k
664
667
  }
665
668
 
666
669
  class QueryStream extends Query {
667
- constructor(config, one) {
670
+ constructor(config, one, objectMode) {
668
671
  // REVISIT: currently when setting the row chunk size
669
672
  // it results in an inconsistent connection state
670
673
  // if (!one) config.rows = 1000
@@ -673,6 +676,7 @@ class QueryStream extends Query {
673
676
  this._one = one || config.one
674
677
 
675
678
  this.stream = new Readable({
679
+ objectMode,
676
680
  read: this.rows
677
681
  ? () => {
678
682
  this.stream.pause()
@@ -690,7 +694,7 @@ class QueryStream extends Query {
690
694
  this._prom = new Promise((resolve, reject) => {
691
695
  this.once('error', reject)
692
696
  this.once('end', () => {
693
- if (!this._one) this.push(this.constructor.close)
697
+ if (!objectMode && !this._one) this.push(this.constructor.close)
694
698
  this.push(null)
695
699
  if (this.stream.isPaused()) this.stream.resume()
696
700
  resolve(null)
@@ -733,10 +737,15 @@ class QueryStream extends Query {
733
737
  } else {
734
738
  this.handleDataRow = msg => {
735
739
  const val = msg.fields[0]
736
- if (!this._one && val !== null) this.push(this.constructor.open)
740
+ const objectMode = this.stream.readableObjectMode
741
+ if (!objectMode && !this._one && val !== null) this.push(this.constructor.open)
737
742
  this.emit('row', val)
738
- this.push(val)
743
+ this.push(objectMode ? JSON.parse(val) : val)
744
+
739
745
  delete this.handleDataRow
746
+ if (objectMode) {
747
+ this.handleDataRow = this.handleDataRowObjectMode
748
+ }
740
749
  }
741
750
  }
742
751
  return super.handleRowDescription(msg)
@@ -748,6 +757,11 @@ class QueryStream extends Query {
748
757
  this.push(msg.fields[0])
749
758
  }
750
759
 
760
+ // Called when a new row is received
761
+ handleDataRowObjectMode(msg) {
762
+ this.push(JSON.parse(msg.fields[0]))
763
+ }
764
+
751
765
  // Called when a new binary row is received
752
766
  handleBinaryRow(msg) {
753
767
  const val = msg.fields[0] === null ? null : this._result._parsers[0](msg.fields[0])
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cap-js/postgres",
3
- "version": "1.12.0",
3
+ "version": "1.14.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": {
@@ -27,7 +27,7 @@
27
27
  "start": "docker compose -f pg-stack.yml up -d"
28
28
  },
29
29
  "dependencies": {
30
- "@cap-js/db-service": "^1.18.0",
30
+ "@cap-js/db-service": "^1.20.0",
31
31
  "pg": "^8"
32
32
  },
33
33
  "peerDependencies": {