@cap-js/postgres 1.5.0 → 1.6.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.6.0](https://github.com/cap-js/cds-dbs/compare/postgres-v1.5.1...postgres-v1.6.0) (2024-03-22)
8
+
9
+
10
+ ### Added
11
+
12
+ * also support lowercase matchespattern function ([#528](https://github.com/cap-js/cds-dbs/issues/528)) ([6ea574e](https://github.com/cap-js/cds-dbs/commit/6ea574ee67ef5e42e4f8ccbe4fe91b46097de129))
13
+ * forUpdate and forShareLock ([#148](https://github.com/cap-js/cds-dbs/issues/148)) ([99a1170](https://github.com/cap-js/cds-dbs/commit/99a1170e61de4fd0c505834c25a9c03fc34da85b))
14
+
15
+
16
+ ### Changed
17
+
18
+ * use new cds build API @sap/cds-dk >= 7.5.0 ([#508](https://github.com/cap-js/cds-dbs/issues/508)) ([ef22ebe](https://github.com/cap-js/cds-dbs/commit/ef22ebe68c6a554d4042a0a19bae3b2e1d56cb01))
19
+ * this package now requires `@cap-js/db-service >= v1.7.0` ([#545](https://github.com/cap-js/cds-dbs/issues/545)) ([2cec27d](https://github.com/cap-js/cds-dbs/commit/2cec27d91402804c3b2da25cc7169f0d81a7406a))
20
+
21
+ ## [1.5.1](https://github.com/cap-js/cds-dbs/compare/postgres-v1.5.0...postgres-v1.5.1) (2024-02-16)
22
+
23
+
24
+ ### Fixed
25
+
26
+ * **`sqlite`:** Retain Error object for unique constraint violation ([#446](https://github.com/cap-js/cds-dbs/issues/446)) ([d27ee79](https://github.com/cap-js/cds-dbs/commit/d27ee79b4c4eea8522bf5dd2a288638f54029567))
27
+
7
28
  ## [1.5.0](https://github.com/cap-js/cds-dbs/compare/postgres-v1.4.1...postgres-v1.5.0) (2024-02-02)
8
29
 
9
30
 
package/cds-plugin.js CHANGED
@@ -1,11 +1,48 @@
1
1
  const cds = require('@sap/cds')
2
+ const { fs, path } = cds.utils
2
3
 
3
4
  if (!cds.env.fiori.lean_draft) {
4
5
  throw new Error('"@cap-js/postgres" only works if cds.fiori.lean_draft is enabled. Please adapt your configuration.')
5
6
  }
6
7
 
7
- // requires @sap/cds-dk version >= 7.3.2
8
- cds.build?.register?.('postgres', {
9
- impl: '@cap-js/postgres/lib/build.js',
10
- taskDefaults: { src: cds.env.folders.db }
8
+ // requires @sap/cds-dk version >= 7.5.0
9
+ cds.build?.register?.('postgres', class PostgresBuildPlugin extends cds.build.Plugin {
10
+
11
+ static taskDefaults = { src: cds.env.folders.db }
12
+
13
+ static hasTask() { return cds.requires.db?.kind === 'postgres' }
14
+
15
+ init() {
16
+ // different from the default build output structure
17
+ this.task.dest = path.join(cds.root, cds.env.build.target !== '.' ? cds.env.build.target : 'gen', 'pg')
18
+ }
19
+
20
+ async build() {
21
+ const model = await this.model()
22
+ if (!model) return
23
+
24
+ const promises = []
25
+ if (fs.existsSync(path.join(this.task.src, 'package.json'))) {
26
+ promises.push(this.copy(path.join(this.task.src, 'package.json')).to('package.json'))
27
+ } else {
28
+ promises.push(
29
+ this.write({
30
+ dependencies: { '@sap/cds': '^7', '@cap-js/postgres': '^1' },
31
+ scripts: { start: 'cds-deploy' },
32
+ }).to('package.json'),
33
+ )
34
+ }
35
+ promises.push(this.write(cds.compile.to.json(model)).to(path.join('db', 'csn.json')))
36
+
37
+ let data
38
+ if (fs.existsSync(path.join(this.task.src, 'data'))) {
39
+ data = 'data'
40
+ } else if (fs.existsSync(path.join(this.task.src, 'csv'))) {
41
+ data = 'csv'
42
+ }
43
+ if (data) {
44
+ promises.push(this.copy(data).to(path.join('db', 'data')))
45
+ }
46
+ return Promise.all(promises)
47
+ }
11
48
  })
@@ -308,14 +308,30 @@ GROUP BY k
308
308
  return super.onSELECT({ query, data })
309
309
  }
310
310
 
311
+ async onINSERT(req) {
312
+ try {
313
+ return await super.onINSERT(req)
314
+ } catch (err) {
315
+ throw _not_unique(err, 'ENTITY_ALREADY_EXISTS')
316
+ }
317
+ }
318
+
319
+ async onUPDATE(req) {
320
+ try {
321
+ return await super.onUPDATE(req)
322
+ } catch (err) {
323
+ throw _not_unique(err, 'UNIQUE_CONSTRAINT_VIOLATION')
324
+ }
325
+ }
326
+
311
327
  static CQN2SQL = class CQN2Postgres extends SQLService.CQN2SQL {
312
328
  _orderBy(orderBy, localized, locale) {
313
329
  return orderBy.map(
314
330
  localized
315
331
  ? c =>
316
- this.expr(c) +
317
- (c.element?.[this.class._localized] ? ` COLLATE "${locale}"` : '') +
318
- (c.sort === 'desc' || c.sort === -1 ? ' DESC' : ' ASC')
332
+ this.expr(c) +
333
+ (c.element?.[this.class._localized] ? ` COLLATE "${locale}"` : '') +
334
+ (c.sort === 'desc' || c.sort === -1 ? ' DESC' : ' ASC')
319
335
  : c => this.expr(c) + (c.sort === 'desc' || c.sort === -1 ? ' DESC' : ' ASC'),
320
336
  )
321
337
  }
@@ -365,9 +381,8 @@ GROUP BY k
365
381
  })
366
382
  // REVISIT: Remove SELECT ${cols} by adjusting SELECT_columns
367
383
  let obj = `to_jsonb(${queryAlias}.*)`
368
- return `SELECT ${
369
- SELECT.one || SELECT.expand === 'root' ? obj : `coalesce(jsonb_agg (${obj}),'[]'::jsonb)`
370
- } as _json_ FROM (SELECT ${cols} FROM (${sql}) as ${queryAlias}) as ${queryAlias}`
384
+ return `SELECT ${SELECT.one || SELECT.expand === 'root' ? obj : `coalesce(jsonb_agg (${obj}),'[]'::jsonb)`
385
+ } as _json_ FROM (SELECT ${cols} FROM (${sql}) as ${queryAlias}) as ${queryAlias}`
371
386
  }
372
387
 
373
388
  doubleQuote(name) {
@@ -402,6 +417,20 @@ GROUP BY k
402
417
  else return super.operator(x, i, xpr)
403
418
  }
404
419
 
420
+ // Postgres does not support locking columns only tables which makes of unapplicable
421
+ // Postgres does not support "wait n" it only supports "nowait"
422
+ forUpdate(update) {
423
+ const { wait } = update
424
+ if (wait === 0) return 'FOR UPDATE NOWAIT'
425
+ return 'FOR UPDATE'
426
+ }
427
+
428
+ forShareLock(lock) {
429
+ const { wait } = lock
430
+ if (wait === 0) return 'FOR SHARE NOWAIT'
431
+ return 'FOR SHARE'
432
+ }
433
+
405
434
  defaultValue(defaultValue = this.context.timestamp.toISOString()) {
406
435
  return this.string(`${defaultValue}`)
407
436
  }
@@ -455,6 +484,7 @@ GROUP BY k
455
484
  Timestamp: e => `to_char(${e}, 'YYYY-MM-DD"T"HH24:MI:SS.FF3"Z"')`,
456
485
  UTCDateTime: e => `to_char(${e}, 'YYYY-MM-DD"T"HH24:MI:SS"Z"')`,
457
486
  UTCTimestamp: e => `to_char(${e}, 'YYYY-MM-DD"T"HH24:MI:SS.FF3"Z"')`,
487
+ Association: e => `jsonb(${e})`,
458
488
  struct: e => `jsonb(${e})`,
459
489
  array: e => `jsonb(${e})`,
460
490
  }
@@ -551,14 +581,14 @@ class QueryStream extends Query {
551
581
  this.stream = new Readable({
552
582
  read: this.rows
553
583
  ? () => {
554
- this.stream.pause()
555
- // Request more rows
556
- this.connection.execute({
557
- portal: this.portal,
558
- rows: this.rows,
559
- })
560
- this.connection.flush()
561
- }
584
+ this.stream.pause()
585
+ // Request more rows
586
+ this.connection.execute({
587
+ portal: this.portal,
588
+ rows: this.rows,
589
+ })
590
+ this.connection.flush()
591
+ }
562
592
  : () => {},
563
593
  })
564
594
  this.push = this.stream.push.bind(this.stream)
@@ -739,4 +769,14 @@ class ParameterStream extends Writable {
739
769
  }
740
770
  }
741
771
 
772
+ function _not_unique(err, code) {
773
+ if (err.code === '23505')
774
+ return Object.assign(err, {
775
+ originalMessage: err.message, // FIXME: required because of next line
776
+ message: code, // FIXME: misusing message as code
777
+ code: 400, // FIXME: misusing code as (http) status
778
+ })
779
+ return err
780
+ }
781
+
742
782
  module.exports = PostgresService
package/lib/func.js CHANGED
@@ -11,6 +11,8 @@ const StandardFunctions = {
11
11
  indexof: (x, y) => `strpos(${x},${y}) - 1`, // sqlite instr is 1 indexed
12
12
  startswith: (x, y) => `strpos(${x},${y}) = 1`, // sqlite instr is 1 indexed
13
13
  endswith: (x, y) => `substr(${x},length(${x}) + 1 - length(${y})) = ${y}`,
14
+ matchesPattern: (x, y) => `regexp_like(${x}, ${y})`,
15
+ matchespattern: (x, y) => `regexp_like(${x}, ${y})`,
14
16
 
15
17
  // Date and Time Functions
16
18
  year: x => `date_part('year', ${castVal(x)})`,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cap-js/postgres",
3
- "version": "1.5.0",
3
+ "version": "1.6.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": {
@@ -31,12 +31,12 @@
31
31
  "start": "docker-compose -f pg-stack.yml up -d"
32
32
  },
33
33
  "dependencies": {
34
- "@cap-js/db-service": "^1.6.0",
34
+ "@cap-js/db-service": "^1.7.0",
35
35
  "pg": "^8"
36
36
  },
37
37
  "peerDependencies": {
38
38
  "@sap/cds": ">=7.6",
39
- "@sap/cds-dk": ">=7"
39
+ "@sap/cds-dk": ">=7.5"
40
40
  },
41
41
  "peerDependenciesMeta": {
42
42
  "@sap/cds-dk": {
package/lib/build.js DELETED
@@ -1,42 +0,0 @@
1
- const cds = require('@sap/cds')
2
- const { fs, path } = cds.utils
3
-
4
- module.exports = class PostgresBuildPlugin extends cds.build.BuildPlugin {
5
- static hasTask() {
6
- return cds.requires.db?.kind === 'postgres'
7
- }
8
-
9
- init() {
10
- // different from the default build output structure
11
- this.task.dest = path.join(cds.root, cds.env.build.target !== '.' ? cds.env.build.target : 'gen', 'pg')
12
- }
13
-
14
- async build() {
15
- const model = await this.model()
16
- if (!model) return
17
-
18
- const promises = []
19
- if (fs.existsSync(path.join(this.task.src, 'package.json'))) {
20
- promises.push(this.copy(path.join(this.task.src, 'package.json')).to('package.json'))
21
- } else {
22
- promises.push(
23
- this.write({
24
- dependencies: { '@sap/cds': '^7', '@cap-js/postgres': '^1' },
25
- scripts: { start: 'cds-deploy' },
26
- }).to('package.json'),
27
- )
28
- }
29
- promises.push(this.write(cds.compile.to.json(model)).to(path.join('db', 'csn.json')))
30
-
31
- let data
32
- if (fs.existsSync(path.join(this.task.src, 'data'))) {
33
- data = 'data'
34
- } else if (fs.existsSync(path.join(this.task.src, 'csv'))) {
35
- data = 'csv'
36
- }
37
- if (data) {
38
- promises.push(this.copy(data).to(path.join('db', 'data')))
39
- }
40
- return Promise.all(promises)
41
- }
42
- }