@cap-js/postgres 1.4.0 → 1.5.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 +21 -0
- package/lib/PostgresService.js +40 -12
- package/lib/build.js +17 -7
- package/package.json +3 -3
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.5.0](https://github.com/cap-js/cds-dbs/compare/postgres-v1.4.1...postgres-v1.5.0) (2024-02-02)
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
|
|
12
|
+
* SELECT returns LargeBinaries as streams unless feature flag "stream_compat" is set ([#251](https://github.com/cap-js/cds-dbs/issues/251)) ([8165a4a](https://github.com/cap-js/cds-dbs/commit/8165a4a3f6bb21c970668c8873f9d9c662b43780))
|
|
13
|
+
* Support Readable Streams inside INSERT.entries ([#343](https://github.com/cap-js/cds-dbs/issues/343)) ([f6faf89](https://github.com/cap-js/cds-dbs/commit/f6faf8955b7888479c66f1727ade65b382611c2f))
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
### Fixed
|
|
17
|
+
|
|
18
|
+
* switch Postgres from json to jsonb ([#402](https://github.com/cap-js/cds-dbs/issues/402)) ([c98a964](https://github.com/cap-js/cds-dbs/commit/c98a964a34232267aece337dc6f6bedf03e9891a))
|
|
19
|
+
* UPSERT for @cap-js/hana for entities with multiple keys ([#418](https://github.com/cap-js/cds-dbs/issues/418)) ([9bbac6e](https://github.com/cap-js/cds-dbs/commit/9bbac6ebbbddfa2f620833ce195eedeb0a79f43e))
|
|
20
|
+
|
|
21
|
+
## [1.4.1](https://github.com/cap-js/cds-dbs/compare/postgres-v1.4.0...postgres-v1.4.1) (2023-11-24)
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
### Fixed
|
|
25
|
+
|
|
26
|
+
* `cds build` now generates the correct output folder structure for Node.js and Java apps. ([#353](https://github.com/cap-js/cds-dbs/issues/353)) ([875aca4](https://github.com/cap-js/cds-dbs/commit/875aca4f5a0ee71bcfbb13be47d4349970b40605))
|
|
27
|
+
|
|
7
28
|
## [1.4.0](https://github.com/cap-js/cds-dbs/compare/postgres-v1.3.1...postgres-v1.4.0) (2023-11-20)
|
|
8
29
|
|
|
9
30
|
|
package/lib/PostgresService.js
CHANGED
|
@@ -13,6 +13,7 @@ class PostgresService extends SQLService {
|
|
|
13
13
|
cds.options.dialect = 'postgres'
|
|
14
14
|
}
|
|
15
15
|
this.kind = 'postgres'
|
|
16
|
+
this._queryCache = {}
|
|
16
17
|
return super.init(...arguments)
|
|
17
18
|
}
|
|
18
19
|
|
|
@@ -78,7 +79,7 @@ class PostgresService extends SQLService {
|
|
|
78
79
|
}
|
|
79
80
|
|
|
80
81
|
return Promise.all([
|
|
81
|
-
(await this.prepare(`SELECT set_config(key::text,$1->>key,false) FROM
|
|
82
|
+
(await this.prepare(`SELECT set_config(key::text,$1->>key,false) FROM jsonb_each($1);`)).run([
|
|
82
83
|
JSON.stringify(env),
|
|
83
84
|
]),
|
|
84
85
|
...(this.options?.credentials?.schema
|
|
@@ -135,12 +136,13 @@ GROUP BY k
|
|
|
135
136
|
}
|
|
136
137
|
|
|
137
138
|
prepare(sql) {
|
|
138
|
-
|
|
139
|
+
// Track queries name for postgres referencing prepare statements
|
|
140
|
+
// sha1 as it needs to be less then 63 character
|
|
141
|
+
const sha = crypto.createHash('sha1').update(sql).digest('hex')
|
|
142
|
+
const query = this._queryCache[sha] = this._queryCache[sha] || {
|
|
139
143
|
_streams: 0,
|
|
140
144
|
text: sql,
|
|
141
|
-
|
|
142
|
-
// sha1 as it needs to be less then 63 characters
|
|
143
|
-
name: crypto.createHash('sha1').update(sql).digest('hex'),
|
|
145
|
+
name: sha,
|
|
144
146
|
}
|
|
145
147
|
return {
|
|
146
148
|
run: async values => {
|
|
@@ -194,7 +196,7 @@ GROUP BY k
|
|
|
194
196
|
values.forEach((value, i) => {
|
|
195
197
|
if (value instanceof Readable) {
|
|
196
198
|
const streamID = query._streams++
|
|
197
|
-
const isBinary = value.type
|
|
199
|
+
const isBinary = value.type !== 'json'
|
|
198
200
|
const paramStream = new ParameterStream(query.name, streamID)
|
|
199
201
|
if (isBinary) value.setEncoding('base64')
|
|
200
202
|
value.pipe(paramStream)
|
|
@@ -281,6 +283,31 @@ GROUP BY k
|
|
|
281
283
|
return super.onPlainSQL(req, next)
|
|
282
284
|
}
|
|
283
285
|
|
|
286
|
+
async onSELECT({ query, data }) {
|
|
287
|
+
// workaround for chunking odata streaming
|
|
288
|
+
if (query.SELECT?.columns?.find(col => col.as === '$mediaContentType')) {
|
|
289
|
+
const columns = query.SELECT.columns
|
|
290
|
+
const index = columns.findIndex(col => query.elements[col.ref?.[col.ref.length - 1]].type === 'cds.LargeBinary')
|
|
291
|
+
const binary = columns[index]
|
|
292
|
+
// SELECT without binary column
|
|
293
|
+
columns.splice(index, 1)
|
|
294
|
+
const { sql, values } = this.cqn2sql(query, data)
|
|
295
|
+
let ps = this.prepare(sql)
|
|
296
|
+
let res = await ps.all(values)
|
|
297
|
+
if (res.length === 0) return
|
|
298
|
+
res = res.map(r => (typeof r._json_ === 'string' ? JSON.parse(r._json_) : r._json_ || r))[0]
|
|
299
|
+
// SELECT only binary column
|
|
300
|
+
query.SELECT.columns = [binary]
|
|
301
|
+
const { sql: streamSql, values: valuesStream } = this.cqn2sql(query, data)
|
|
302
|
+
ps = this.prepare(streamSql)
|
|
303
|
+
const stream = await ps.stream(valuesStream, true)
|
|
304
|
+
// merge results
|
|
305
|
+
res[binary.as || binary.ref[binary.ref.length - 1]] = stream
|
|
306
|
+
return res
|
|
307
|
+
}
|
|
308
|
+
return super.onSELECT({ query, data })
|
|
309
|
+
}
|
|
310
|
+
|
|
284
311
|
static CQN2SQL = class CQN2Postgres extends SQLService.CQN2SQL {
|
|
285
312
|
_orderBy(orderBy, localized, locale) {
|
|
286
313
|
return orderBy.map(
|
|
@@ -337,9 +364,9 @@ GROUP BY k
|
|
|
337
364
|
return col
|
|
338
365
|
})
|
|
339
366
|
// REVISIT: Remove SELECT ${cols} by adjusting SELECT_columns
|
|
340
|
-
let obj = `
|
|
367
|
+
let obj = `to_jsonb(${queryAlias}.*)`
|
|
341
368
|
return `SELECT ${
|
|
342
|
-
SELECT.one || SELECT.expand === 'root' ? obj : `coalesce(
|
|
369
|
+
SELECT.one || SELECT.expand === 'root' ? obj : `coalesce(jsonb_agg (${obj}),'[]'::jsonb)`
|
|
343
370
|
} as _json_ FROM (SELECT ${cols} FROM (${sql}) as ${queryAlias}) as ${queryAlias}`
|
|
344
371
|
}
|
|
345
372
|
|
|
@@ -355,8 +382,8 @@ GROUP BY k
|
|
|
355
382
|
// Adjusts json path expressions to be postgres specific
|
|
356
383
|
.replace(/->>'\$(?:(?:\."(.*?)")|(?:\[(\d*)\]))'/g, (a, b, c) => (b ? `->>'${b}'` : `->>${c}`))
|
|
357
384
|
// Adjusts json function to be postgres specific
|
|
358
|
-
.replace('json_each(?)', '
|
|
359
|
-
.replace(/json_type\((\w+),'\$\."(\w+)"'\)/g, (_a, b, c) => `
|
|
385
|
+
.replace('json_each(?)', 'jsonb_array_elements($1::jsonb)')
|
|
386
|
+
.replace(/json_type\((\w+),'\$\."(\w+)"'\)/g, (_a, b, c) => `jsonb_typeof(${b}->'${c}')`))
|
|
360
387
|
}
|
|
361
388
|
|
|
362
389
|
param({ ref }) {
|
|
@@ -428,8 +455,8 @@ GROUP BY k
|
|
|
428
455
|
Timestamp: e => `to_char(${e}, 'YYYY-MM-DD"T"HH24:MI:SS.FF3"Z"')`,
|
|
429
456
|
UTCDateTime: e => `to_char(${e}, 'YYYY-MM-DD"T"HH24:MI:SS"Z"')`,
|
|
430
457
|
UTCTimestamp: e => `to_char(${e}, 'YYYY-MM-DD"T"HH24:MI:SS.FF3"Z"')`,
|
|
431
|
-
struct: e => `
|
|
432
|
-
array: e => `
|
|
458
|
+
struct: e => `jsonb(${e})`,
|
|
459
|
+
array: e => `jsonb(${e})`,
|
|
433
460
|
}
|
|
434
461
|
}
|
|
435
462
|
|
|
@@ -457,6 +484,7 @@ GROUP BY k
|
|
|
457
484
|
DROP USER IF EXISTS "${creds.user}";
|
|
458
485
|
CREATE GROUP "${creds.usergroup}";
|
|
459
486
|
CREATE USER "${creds.user}" WITH CREATEROLE IN GROUP "${creds.usergroup}" PASSWORD '${creds.user}';
|
|
487
|
+
GRANT "${creds.usergroup}" TO "${creds.user}" WITH ADMIN OPTION;
|
|
460
488
|
`)
|
|
461
489
|
await this.exec(`CREATE DATABASE "${creds.database}" OWNER="${creds.user}" TEMPLATE=template0`)
|
|
462
490
|
} catch (e) {
|
package/lib/build.js
CHANGED
|
@@ -2,21 +2,31 @@ const cds = require('@sap/cds')
|
|
|
2
2
|
const { fs, path } = cds.utils
|
|
3
3
|
|
|
4
4
|
module.exports = class PostgresBuildPlugin extends cds.build.BuildPlugin {
|
|
5
|
-
|
|
6
5
|
static hasTask() {
|
|
7
6
|
return cds.requires.db?.kind === 'postgres'
|
|
8
7
|
}
|
|
9
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
|
+
|
|
10
14
|
async build() {
|
|
11
15
|
const model = await this.model()
|
|
12
16
|
if (!model) return
|
|
13
17
|
|
|
14
18
|
const promises = []
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
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')))
|
|
20
30
|
|
|
21
31
|
let data
|
|
22
32
|
if (fs.existsSync(path.join(this.task.src, 'data'))) {
|
|
@@ -25,7 +35,7 @@ module.exports = class PostgresBuildPlugin extends cds.build.BuildPlugin {
|
|
|
25
35
|
data = 'csv'
|
|
26
36
|
}
|
|
27
37
|
if (data) {
|
|
28
|
-
promises.push(this.copy(data).to(path.join('
|
|
38
|
+
promises.push(this.copy(data).to(path.join('db', 'data')))
|
|
29
39
|
}
|
|
30
40
|
return Promise.all(promises)
|
|
31
41
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cap-js/postgres",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.5.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,11 +31,11 @@
|
|
|
31
31
|
"start": "docker-compose -f pg-stack.yml up -d"
|
|
32
32
|
},
|
|
33
33
|
"dependencies": {
|
|
34
|
-
"@cap-js/db-service": "^1.
|
|
34
|
+
"@cap-js/db-service": "^1.6.0",
|
|
35
35
|
"pg": "^8"
|
|
36
36
|
},
|
|
37
37
|
"peerDependencies": {
|
|
38
|
-
"@sap/cds": ">=7",
|
|
38
|
+
"@sap/cds": ">=7.6",
|
|
39
39
|
"@sap/cds-dk": ">=7"
|
|
40
40
|
},
|
|
41
41
|
"peerDependenciesMeta": {
|