@sap/cds 5.4.6 → 5.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 +208 -2
- package/apis/ql.d.ts +17 -15
- package/app/index.js +1 -1
- package/bin/build/buildTaskEngine.js +26 -42
- package/bin/build/buildTaskFactory.js +6 -10
- package/bin/build/buildTaskHandler.js +2 -4
- package/bin/build/buildTaskProvider.js +3 -1
- package/bin/build/buildTaskProviderFactory.js +9 -15
- package/bin/build/constants.js +15 -3
- package/bin/build/index.js +5 -4
- package/bin/build/mtaUtil.js +8 -11
- package/bin/build/provider/buildTaskHandlerEdmx.js +63 -6
- package/bin/build/provider/buildTaskHandlerInternal.js +2 -34
- package/bin/build/provider/buildTaskProviderInternal.js +16 -42
- package/bin/build/provider/fiori/index.js +13 -24
- package/bin/build/provider/hana/2migration.js +17 -15
- package/bin/build/provider/hana/2tabledata.js +52 -48
- package/bin/build/provider/hana/index.js +27 -25
- package/bin/build/provider/hana/migrationtable.js +91 -67
- package/bin/build/provider/java-cf/index.js +14 -24
- package/bin/build/provider/mtx/index.js +12 -14
- package/bin/build/provider/node-cf/index.js +18 -32
- package/bin/cds.js +5 -5
- package/bin/serve.js +29 -23
- package/bin/version.js +0 -1
- package/lib/compile/etc/_localized.js +4 -9
- package/lib/compile/for/sql.js +5 -2
- package/lib/compile/parse.js +25 -17
- package/lib/compile/to/srvinfo.js +2 -1
- package/lib/connect/bindings.js +2 -1
- package/lib/connect/index.js +48 -49
- package/lib/core/classes.js +1 -1
- package/lib/core/reflect.js +10 -2
- package/lib/deploy.js +26 -23
- package/lib/env/defaults.js +13 -6
- package/lib/env/index.js +73 -78
- package/lib/env/requires.js +38 -19
- package/lib/index.js +9 -10
- package/lib/lazy.js +2 -2
- package/lib/log/index.js +33 -45
- package/lib/log/service/index.js +2 -2
- package/lib/ql/CREATE.js +14 -9
- package/lib/ql/DELETE.js +6 -5
- package/lib/ql/DROP.js +12 -9
- package/lib/ql/INSERT.js +40 -16
- package/lib/ql/Query.js +67 -40
- package/lib/ql/SELECT.js +162 -127
- package/lib/ql/UPDATE.js +74 -42
- package/lib/ql/Whereable.js +77 -87
- package/lib/ql/index.js +36 -24
- package/lib/ql/parse.js +35 -0
- package/lib/req/context.js +44 -8
- package/lib/req/locale.js +7 -7
- package/lib/serve/Service-api.js +21 -14
- package/lib/serve/Service-dispatch.js +28 -12
- package/lib/serve/Transaction.js +22 -10
- package/lib/serve/index.js +16 -11
- package/lib/utils/axios.js +23 -16
- package/lib/utils/data.js +35 -0
- package/lib/utils/tests.js +27 -18
- package/libx/_runtime/audit/generic/personal/access.js +81 -0
- package/libx/_runtime/audit/generic/personal/constants.js +4 -0
- package/libx/_runtime/audit/generic/personal/index.js +50 -0
- package/libx/_runtime/audit/generic/personal/modification.js +138 -0
- package/libx/_runtime/audit/generic/personal/utils.js +186 -0
- package/libx/_runtime/audit/utils/v2.js +10 -4
- package/libx/_runtime/cds-services/adapter/odata-v4/Dispatcher.js +5 -5
- package/libx/_runtime/cds-services/adapter/odata-v4/OData.js +6 -7
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/action.js +5 -7
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/create.js +5 -7
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/delete.js +2 -3
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/error.js +4 -0
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/metadata.js +7 -4
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/read.js +59 -8
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/request.js +11 -1
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/update.js +6 -10
- package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/ExpressionToCQN.js +3 -46
- package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/applyToCQN.js +2 -5
- package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/createToCQN.js +2 -2
- package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/deleteToCQN.js +4 -3
- package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/expandToCQN.js +1 -2
- package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/index.js +0 -1
- package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/selectHelper.js +1 -1
- package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/updateToCQN.js +2 -2
- package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/utils.js +16 -18
- package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-commons/edm/EdmEntityType.js +6 -3
- package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-commons/format/RepresentationKind.js +4 -1
- package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-server/core/OdataRequest.js +1 -0
- package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-server/serializer/SerializerFactory.js +15 -2
- package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-server/validator/OperationValidator.js +1 -0
- package/libx/_runtime/cds-services/adapter/odata-v4/to.js +1 -1
- package/libx/_runtime/cds-services/adapter/odata-v4/utils/data.js +8 -1
- package/libx/_runtime/cds-services/adapter/odata-v4/utils/handlerUtils.js +6 -1
- package/libx/_runtime/cds-services/adapter/odata-v4/utils/omitValues.js +12 -5
- package/libx/_runtime/cds-services/adapter/odata-v4/utils/readAfterWrite.js +1 -7
- package/libx/_runtime/cds-services/adapter/odata-v4/utils/request.js +7 -7
- package/libx/_runtime/cds-services/adapter/odata-v4/utils/result.js +14 -18
- package/libx/_runtime/cds-services/adapter/odata-v4/utils/stream.js +13 -13
- package/libx/_runtime/cds-services/adapter/rest/handlers/create.js +0 -1
- package/libx/_runtime/cds-services/adapter/rest/handlers/operation.js +2 -1
- package/libx/_runtime/cds-services/adapter/rest/handlers/read.js +2 -2
- package/libx/_runtime/cds-services/adapter/rest/rest-to-cqn/index.js +2 -4
- package/libx/_runtime/cds-services/adapter/rest/utils/result.js +4 -2
- package/libx/_runtime/cds-services/services/Service.js +40 -5
- package/libx/_runtime/cds-services/services/utils/columns.js +13 -7
- package/libx/_runtime/cds-services/services/utils/compareJson.js +88 -4
- package/libx/_runtime/cds-services/services/utils/differ.js +24 -6
- package/libx/_runtime/cds-services/services/utils/handlerUtils.js +2 -2
- package/libx/_runtime/common/composition/data.js +44 -55
- package/libx/_runtime/common/composition/delete.js +97 -71
- package/libx/_runtime/common/composition/index.js +2 -1
- package/libx/_runtime/common/composition/insert.js +34 -11
- package/libx/_runtime/common/composition/tree.js +119 -92
- package/libx/_runtime/common/composition/update.js +4 -1
- package/libx/_runtime/common/composition/utils.js +1 -3
- package/libx/_runtime/common/constants/draft.js +12 -1
- package/libx/_runtime/common/generic/auth.js +6 -22
- package/libx/_runtime/common/generic/crud.js +14 -13
- package/libx/_runtime/common/generic/input.js +23 -26
- package/libx/_runtime/common/generic/put.js +1 -1
- package/libx/_runtime/common/generic/sorting.js +16 -16
- package/libx/_runtime/common/i18n/index.js +1 -1
- package/libx/_runtime/common/i18n/messages.properties +4 -0
- package/libx/_runtime/common/utils/backlinks.js +12 -5
- package/libx/_runtime/common/utils/cqn.js +6 -1
- package/libx/_runtime/common/utils/cqn2cqn4sql.js +102 -101
- package/libx/_runtime/common/utils/csn.js +47 -4
- package/libx/_runtime/common/utils/data.js +0 -37
- package/libx/_runtime/common/utils/enrichWithKeysFromWhere.js +1 -1
- package/libx/_runtime/common/utils/entityFromCqn.js +7 -24
- package/libx/_runtime/common/utils/foreignKeyPropagations.js +39 -7
- package/libx/_runtime/common/utils/generateOnCond.js +11 -12
- package/libx/_runtime/common/utils/onlyKeysRemain.js +10 -0
- package/libx/_runtime/common/utils/path.js +35 -0
- package/libx/_runtime/common/utils/postProcessing.js +86 -0
- package/libx/_runtime/common/utils/quotingStyles.js +37 -26
- package/libx/_runtime/common/utils/resolveView.js +223 -171
- package/libx/_runtime/common/utils/rewriteAsterisk.js +46 -26
- package/libx/_runtime/common/utils/structured.js +6 -12
- package/libx/_runtime/common/utils/template.js +10 -5
- package/libx/_runtime/common/utils/templateDelimiter.js +1 -0
- package/libx/_runtime/common/utils/templateProcessor.js +22 -30
- package/libx/_runtime/common/utils/union.js +31 -0
- package/libx/_runtime/common/utils/unionCqnTemplate.js +184 -0
- package/libx/_runtime/db/Service.js +1 -1
- package/libx/_runtime/db/data-conversion/timestamp.js +2 -9
- package/libx/_runtime/db/expand/expandCQNToJoin.js +204 -297
- package/libx/_runtime/db/expand/index.js +3 -3
- package/libx/_runtime/db/expand/rawToExpanded.js +36 -7
- package/libx/_runtime/db/generic/index.js +1 -1
- package/libx/_runtime/db/generic/input.js +5 -7
- package/libx/_runtime/db/generic/integrity.js +1 -1
- package/libx/_runtime/db/generic/rewrite.js +2 -10
- package/libx/_runtime/db/generic/update.js +13 -5
- package/libx/_runtime/db/generic/virtual.js +22 -58
- package/libx/_runtime/db/query/delete.js +7 -4
- package/libx/_runtime/db/query/insert.js +6 -4
- package/libx/_runtime/db/query/read.js +13 -20
- package/libx/_runtime/db/query/run.js +4 -1
- package/libx/_runtime/db/query/update.js +5 -4
- package/libx/_runtime/db/sql-builder/ExpressionBuilder.js +35 -2
- package/libx/_runtime/db/sql-builder/FunctionBuilder.js +17 -2
- package/libx/_runtime/db/sql-builder/InsertBuilder.js +6 -5
- package/libx/_runtime/db/sql-builder/ReferenceBuilder.js +10 -0
- package/libx/_runtime/db/sql-builder/SelectBuilder.js +35 -24
- package/libx/_runtime/db/sql-builder/UpdateBuilder.js +14 -4
- package/libx/_runtime/db/sql-builder/arrayed.js +4 -0
- package/libx/_runtime/db/utils/deep.js +8 -0
- package/libx/_runtime/db/utils/generateAliases.js +2 -1
- package/libx/_runtime/fiori/generic/activate.js +19 -15
- package/libx/_runtime/fiori/generic/before.js +3 -11
- package/libx/_runtime/fiori/generic/cancel.js +1 -1
- package/libx/_runtime/fiori/generic/delete.js +3 -1
- package/libx/_runtime/fiori/generic/edit.js +12 -2
- package/libx/_runtime/fiori/generic/new.js +5 -5
- package/libx/_runtime/fiori/generic/patch.js +0 -18
- package/libx/_runtime/fiori/generic/read.js +241 -189
- package/libx/_runtime/fiori/utils/delete.js +36 -7
- package/libx/_runtime/fiori/utils/handler.js +43 -44
- package/libx/_runtime/fiori/utils/where.js +30 -15
- package/libx/_runtime/hana/customBuilder/CustomSelectBuilder.js +4 -6
- package/libx/_runtime/hana/execute.js +2 -2
- package/libx/_runtime/hana/localized.js +4 -4
- package/libx/_runtime/hana/pool.js +29 -14
- package/libx/_runtime/hana/search2cqn4sql.js +2 -1
- package/libx/_runtime/hana/searchToContains.js +18 -14
- package/libx/_runtime/index.js +0 -5
- package/libx/_runtime/messaging/AMQPWebhookMessaging.js +13 -5
- package/libx/_runtime/messaging/common-utils/naming-conventions.js +4 -1
- package/libx/_runtime/messaging/enterprise-messaging-utils/EMManagement.js +31 -19
- package/libx/_runtime/messaging/enterprise-messaging-utils/registerEndpoints.js +1 -2
- package/libx/_runtime/messaging/enterprise-messaging.js +6 -4
- package/libx/_runtime/messaging/service.js +7 -6
- package/libx/_runtime/odata/cqn2odata.js +110 -43
- package/libx/_runtime/odata/index.js +26 -48
- package/libx/_runtime/odata/odata2cqn.js +1 -6154
- package/libx/_runtime/odata/odata2cqn.pegjs +559 -0
- package/libx/_runtime/odata/readToCqn.js +94 -64
- package/libx/_runtime/remote/Service.js +74 -21
- package/libx/_runtime/remote/cqn2odata/index.js +1 -5
- package/libx/_runtime/remote/utils/client.js +24 -101
- package/libx/_runtime/remote/utils/dataConversion.js +27 -12
- package/libx/_runtime/sqlite/Service.js +3 -5
- package/libx/_runtime/sqlite/execute.js +23 -24
- package/libx/_runtime/sqlite/localized.js +12 -7
- package/libx/_runtime/types/api.js +10 -0
- package/package.json +1 -1
- package/server.js +16 -2
- package/lib/ql/grammar.pegjs +0 -208
- package/lib/ql/parser.js +0 -1
- package/lib/ql/rt/DELETE.js +0 -29
- package/lib/ql/rt/INSERT.js +0 -23
- package/lib/ql/rt/Query.js +0 -84
- package/lib/ql/rt/SELECT.js +0 -174
- package/lib/ql/rt/UPDATE.js +0 -119
- package/lib/ql/rt/_helpers.js +0 -91
- package/lib/ql/rt/index.js +0 -32
- package/libx/_runtime/audit/generic/personal.js +0 -260
- package/libx/_runtime/cds-services/statements/BaseStatement.js +0 -72
- package/libx/_runtime/cds-services/statements/Create.js +0 -57
- package/libx/_runtime/cds-services/statements/Delete.js +0 -33
- package/libx/_runtime/cds-services/statements/Drop.js +0 -42
- package/libx/_runtime/cds-services/statements/Insert.js +0 -201
- package/libx/_runtime/cds-services/statements/Select.js +0 -826
- package/libx/_runtime/cds-services/statements/Update.js +0 -181
- package/libx/_runtime/cds-services/statements/Where.js +0 -726
- package/libx/_runtime/cds-services/statements/index.js +0 -25
- package/libx/_runtime/common/generic/resolve-mock.js +0 -9
|
@@ -4,6 +4,7 @@ const BaseBuilder = require('./BaseBuilder')
|
|
|
4
4
|
const SelectBuilder = require('./SelectBuilder')
|
|
5
5
|
const getAnnotatedColumns = require('./annotations')
|
|
6
6
|
const dollar = require('./dollar')
|
|
7
|
+
const { stringifyIfArrayedElement, isArrayedElement } = require('./arrayed')
|
|
7
8
|
|
|
8
9
|
/**
|
|
9
10
|
* InsertBuilder is used to take a CQN object as an input and to build an object representing an insert operation
|
|
@@ -185,13 +186,13 @@ class InsertBuilder extends BaseBuilder {
|
|
|
185
186
|
const annotatedInsertColumnValues = annotatedColumns ? this._getAnnotatedInsertColumnValues(annotatedColumns) : []
|
|
186
187
|
|
|
187
188
|
if (this._obj.INSERT.values) {
|
|
188
|
-
this._outputObj.values = this._obj.INSERT.values
|
|
189
|
+
this._outputObj.values = this._obj.INSERT.values.map(stringifyIfArrayedElement)
|
|
189
190
|
|
|
190
191
|
placeholderNum = this._outputObj.values.length
|
|
191
192
|
|
|
192
193
|
this._valuesAnnotatedValues(annotatedInsertColumnValues, this._outputObj.values)
|
|
193
194
|
} else {
|
|
194
|
-
this._outputObj.values = this._obj.INSERT.rows
|
|
195
|
+
this._outputObj.values = this._obj.INSERT.rows.map(r => r.map(stringifyIfArrayedElement))
|
|
195
196
|
|
|
196
197
|
placeholderNum = this._outputObj.values[0].length
|
|
197
198
|
|
|
@@ -261,7 +262,7 @@ class InsertBuilder extends BaseBuilder {
|
|
|
261
262
|
for (const column of columns) {
|
|
262
263
|
const flattenColumn = flattenColumnMap.get(column)
|
|
263
264
|
const val = this._getValue(column, { entry, flattenColumn, insertAnnotatedColumns })
|
|
264
|
-
values.push(val)
|
|
265
|
+
values.push(isArrayedElement(val) ? JSON.stringify(val) : val)
|
|
265
266
|
}
|
|
266
267
|
|
|
267
268
|
// insert values for insert annotated columns
|
|
@@ -370,7 +371,7 @@ class InsertBuilder extends BaseBuilder {
|
|
|
370
371
|
_getFlattenEntryColumnsRecursion(res, entry, prefix) {
|
|
371
372
|
Object.keys(entry).forEach(key => {
|
|
372
373
|
const prefixKey = prefix ? `${prefix}_${key}` : key
|
|
373
|
-
if (this._isSubObject(key, entry)) {
|
|
374
|
+
if (this._isSubObject(key, entry) && !isArrayedElement(entry[key])) {
|
|
374
375
|
this._getFlattenEntryColumnsRecursion(res, entry[key], prefixKey)
|
|
375
376
|
} else {
|
|
376
377
|
res.push(prefixKey)
|
|
@@ -384,7 +385,7 @@ class InsertBuilder extends BaseBuilder {
|
|
|
384
385
|
entries.forEach(entry => {
|
|
385
386
|
Object.keys(entry).forEach(key => {
|
|
386
387
|
const prefixKey = prefix ? `${prefix}_${key}` : key
|
|
387
|
-
if (this._isSubObject(key, entry)) {
|
|
388
|
+
if (this._isSubObject(key, entry) && !isArrayedElement(entry[key])) {
|
|
388
389
|
const resInternal = this._getFlattenColumnMap([entry[key]], { prefix: prefixKey, annotatedColumns })
|
|
389
390
|
|
|
390
391
|
Array.from(resInternal.keys()).forEach(keyInternal => {
|
|
@@ -45,6 +45,16 @@ class ReferenceBuilder extends BaseBuilder {
|
|
|
45
45
|
this._outputObj.values.push(...values)
|
|
46
46
|
} else if (this._obj.ref) {
|
|
47
47
|
// reference
|
|
48
|
+
|
|
49
|
+
// REVISIT: compat for legacy ref: "['foo as bar']" -> remove with cds^6
|
|
50
|
+
if (this._obj.ref.length === 1 && typeof this._obj.ref[0] === 'string') {
|
|
51
|
+
const matched = this._obj.ref[0].match(/(\S*)\s{1,}as\s{1,}(\S*)/i)
|
|
52
|
+
if (matched) {
|
|
53
|
+
this._obj.ref = [matched[1]]
|
|
54
|
+
this._obj.as = matched[2].replace(/^"/, '').replace(/"$/, '')
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
48
58
|
if (this._obj.param) {
|
|
49
59
|
this._parseParamReference(this._obj.ref)
|
|
50
60
|
} else {
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
const cds = require('../../cds')
|
|
2
|
+
|
|
1
3
|
const BaseBuilder = require('./BaseBuilder')
|
|
2
4
|
const { DRAFT_COLUMNS } = require('../../common/constants/draft')
|
|
3
5
|
|
|
@@ -67,6 +69,8 @@ class SelectBuilder extends BaseBuilder {
|
|
|
67
69
|
* SQL string for prepared statement and array of values to replace the placeholders.
|
|
68
70
|
*/
|
|
69
71
|
build(noQuoting = false) {
|
|
72
|
+
if (this._obj.SELECT._4odata) this._options._4odata = true
|
|
73
|
+
|
|
70
74
|
this._outputObj = {
|
|
71
75
|
sql: ['SELECT'],
|
|
72
76
|
values: []
|
|
@@ -250,8 +254,7 @@ class SelectBuilder extends BaseBuilder {
|
|
|
250
254
|
res.sql = `( ${res.sql} )`
|
|
251
255
|
} else {
|
|
252
256
|
// val
|
|
253
|
-
res
|
|
254
|
-
res.values = []
|
|
257
|
+
res = this._val(col)
|
|
255
258
|
}
|
|
256
259
|
|
|
257
260
|
if (col.as) {
|
|
@@ -288,7 +291,7 @@ class SelectBuilder extends BaseBuilder {
|
|
|
288
291
|
const aliases = columns.map(col => this._getAlias(col)).filter(element => element !== undefined)
|
|
289
292
|
const findDuplicates = aliases.filter((item, index) => aliases.indexOf(item) !== index)
|
|
290
293
|
|
|
291
|
-
if (findDuplicates.length
|
|
294
|
+
if (findDuplicates.length > 0) {
|
|
292
295
|
throw new Error(
|
|
293
296
|
`Duplicate column names ${JSON.stringify(findDuplicates)} detected in SELECT statement. Please use aliases.`
|
|
294
297
|
)
|
|
@@ -296,11 +299,8 @@ class SelectBuilder extends BaseBuilder {
|
|
|
296
299
|
}
|
|
297
300
|
|
|
298
301
|
_getAlias(col) {
|
|
299
|
-
if (col.as)
|
|
300
|
-
|
|
301
|
-
} else if (col.ref) {
|
|
302
|
-
return col.ref[col.ref.length - 1]
|
|
303
|
-
}
|
|
302
|
+
if (col.as) return col.as
|
|
303
|
+
if (col.ref) return col.ref[col.ref.length - 1]
|
|
304
304
|
}
|
|
305
305
|
|
|
306
306
|
_columns(noQuoting) {
|
|
@@ -368,16 +368,29 @@ class SelectBuilder extends BaseBuilder {
|
|
|
368
368
|
}
|
|
369
369
|
|
|
370
370
|
_having() {
|
|
371
|
-
const
|
|
372
|
-
this._outputObj.sql.push('HAVING',
|
|
373
|
-
this._outputObj.values.push(...
|
|
371
|
+
const { sql, values } = new this.ExpressionBuilder(this._obj.SELECT.having, this._options, this._csn).build()
|
|
372
|
+
this._outputObj.sql.push('HAVING', sql)
|
|
373
|
+
this._outputObj.values.push(...values)
|
|
374
374
|
}
|
|
375
375
|
|
|
376
376
|
_orderBy() {
|
|
377
377
|
const sqls = []
|
|
378
378
|
this._outputObj.sql.push('ORDER BY')
|
|
379
379
|
for (const element of this._obj.SELECT.orderBy) {
|
|
380
|
-
|
|
380
|
+
// REVISIT: should not be necessary
|
|
381
|
+
// if columns doesn't have matching ref but matching as, then use that as
|
|
382
|
+
const { columns } = this._obj.SELECT
|
|
383
|
+
if (columns) {
|
|
384
|
+
const serialized = JSON.stringify(element.ref)
|
|
385
|
+
if (!columns.find(c => JSON.stringify(c.ref) === serialized)) {
|
|
386
|
+
const toMatch = element.as || (element.ref && element.ref.length === 1 && element.ref[0])
|
|
387
|
+
if (toMatch && columns.find(c => c.as === toMatch)) {
|
|
388
|
+
sqls.push(this._quote(toMatch) + ' ' + (element.sort || 'asc').toUpperCase())
|
|
389
|
+
continue
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
|
|
381
394
|
const { sql, values } = new this.ReferenceBuilder(element, this._options, this._csn).build()
|
|
382
395
|
sqls.push(sql)
|
|
383
396
|
this._outputObj.values.push(...values)
|
|
@@ -394,17 +407,8 @@ class SelectBuilder extends BaseBuilder {
|
|
|
394
407
|
* offset will still use placeholders, as it'll change during the paging queries.
|
|
395
408
|
*/
|
|
396
409
|
_limit() {
|
|
397
|
-
// limit
|
|
398
|
-
|
|
399
|
-
this._outputObj.sql.push('LIMIT', 1)
|
|
400
|
-
} else {
|
|
401
|
-
if (typeof this._obj.SELECT.limit.rows.val === 'number' && !this._parameterizedNumbers) {
|
|
402
|
-
this._outputObj.sql.push('LIMIT', this._obj.SELECT.limit.rows.val)
|
|
403
|
-
} else {
|
|
404
|
-
this._outputObj.sql.push('LIMIT', '?')
|
|
405
|
-
this._outputObj.values.push(this._obj.SELECT.limit.rows.val)
|
|
406
|
-
}
|
|
407
|
-
}
|
|
410
|
+
// limit (no placeholder for statement caching)
|
|
411
|
+
this._outputObj.sql.push('LIMIT', this._obj.SELECT.one ? 1 : this._obj.SELECT.limit.rows.val)
|
|
408
412
|
// offset
|
|
409
413
|
if (this._obj.SELECT.limit && this._obj.SELECT.limit.offset) {
|
|
410
414
|
if (typeof this._obj.SELECT.limit.offset.val === 'number' && !this._parameterizedNumbers) {
|
|
@@ -425,7 +429,14 @@ class SelectBuilder extends BaseBuilder {
|
|
|
425
429
|
}
|
|
426
430
|
|
|
427
431
|
_val(obj) {
|
|
428
|
-
|
|
432
|
+
let _val = obj.val
|
|
433
|
+
if (typeof _val === 'number') return { sql: _val, values: [] }
|
|
434
|
+
// REVISIT: remove with cds^6
|
|
435
|
+
if (typeof _val === 'string' && cds.env.features.extract_vals !== false) {
|
|
436
|
+
const match = _val.match(/^'(.*)'$/)
|
|
437
|
+
if (match) _val = match[1]
|
|
438
|
+
}
|
|
439
|
+
return { sql: '?', values: [_val] }
|
|
429
440
|
}
|
|
430
441
|
|
|
431
442
|
getDefaultOptions() {
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
const BaseBuilder = require('./BaseBuilder')
|
|
2
2
|
const getAnnotatedColumns = require('./annotations')
|
|
3
3
|
const dollar = require('./dollar')
|
|
4
|
+
const { stringifyIfArrayedElement, isArrayedElement } = require('./arrayed')
|
|
4
5
|
|
|
5
6
|
/**
|
|
6
7
|
* UpdateBuilder is used to take a CQN object as an input and to build an object representing an update operation
|
|
@@ -69,7 +70,8 @@ class UpdateBuilder extends BaseBuilder {
|
|
|
69
70
|
const entityName = this._entity()
|
|
70
71
|
this._options.entityName = entityName
|
|
71
72
|
|
|
72
|
-
this.
|
|
73
|
+
const entity = this._csn && this._csn.definitions && this._csn.definitions[entityName]
|
|
74
|
+
this._data(getAnnotatedColumns(entityName, this._csn), entity)
|
|
73
75
|
if (Array.isArray(this._obj.UPDATE.where) && this._obj.UPDATE.where.length > 0) {
|
|
74
76
|
this._where()
|
|
75
77
|
}
|
|
@@ -101,7 +103,7 @@ class UpdateBuilder extends BaseBuilder {
|
|
|
101
103
|
}
|
|
102
104
|
}
|
|
103
105
|
|
|
104
|
-
_data(annotatedColumns) {
|
|
106
|
+
_data(annotatedColumns, entity) {
|
|
105
107
|
const sql = []
|
|
106
108
|
const data = this._obj.UPDATE.data || {}
|
|
107
109
|
const withObj = this._obj.UPDATE.with || {}
|
|
@@ -111,13 +113,19 @@ class UpdateBuilder extends BaseBuilder {
|
|
|
111
113
|
|
|
112
114
|
this._addAnnotatedUpdateColumns(resMap, annotatedColumns)
|
|
113
115
|
|
|
116
|
+
if (entity && entity.keys) {
|
|
117
|
+
resMap.forEach((value, key, map) => {
|
|
118
|
+
if (key in entity.keys) map.delete(key)
|
|
119
|
+
})
|
|
120
|
+
}
|
|
121
|
+
|
|
114
122
|
resMap.forEach((value, key, map) => {
|
|
115
123
|
if (value && value.sql) {
|
|
116
124
|
sql.push(`${this._quoteElement(key)} = ${value.sql}`)
|
|
117
125
|
this._outputObj.values.push(...value.values)
|
|
118
126
|
} else {
|
|
119
127
|
sql.push(`${this._quoteElement(key)} = ?`)
|
|
120
|
-
this._outputObj.values.push(value)
|
|
128
|
+
this._outputObj.values.push(stringifyIfArrayedElement(value))
|
|
121
129
|
}
|
|
122
130
|
})
|
|
123
131
|
|
|
@@ -142,7 +150,9 @@ class UpdateBuilder extends BaseBuilder {
|
|
|
142
150
|
const prefixKey = prefix ? `${prefix}_${key}` : key
|
|
143
151
|
const value = data[key]
|
|
144
152
|
if (typeof value === 'object' && !Buffer.isBuffer(value) && value !== null && typeof value.pipe !== 'function') {
|
|
145
|
-
if (
|
|
153
|
+
if (isArrayedElement(value)) {
|
|
154
|
+
res.set(key, JSON.stringify(value))
|
|
155
|
+
} else if ('xpr' in value && Array.isArray(value.xpr)) {
|
|
146
156
|
const xpr = new this.ExpressionBuilder(value.xpr, this._options, this._csn).build()
|
|
147
157
|
res.set(key, xpr)
|
|
148
158
|
} else if ('ref' in value && Array.isArray(value.ref)) {
|
|
@@ -30,12 +30,20 @@ async function processCQNs(processFn, cqns, model, dbc, user, locale, ts, chunks
|
|
|
30
30
|
const results = new Array(cqns.length)
|
|
31
31
|
|
|
32
32
|
const deletes = []
|
|
33
|
+
const updatesBeforeDelete = []
|
|
33
34
|
const others = []
|
|
34
35
|
for (let i = 0; i < cqns.length; i++) {
|
|
35
36
|
if (cqns[i].DELETE) deletes.push(i)
|
|
37
|
+
else if (cqns[i].UPDATE && cqns[i].UPDATE._beforeDelete) updatesBeforeDelete.push(i)
|
|
36
38
|
else others.push(i)
|
|
37
39
|
}
|
|
38
40
|
|
|
41
|
+
// UPDATEs to SET null parent's foreign keys of one compositions
|
|
42
|
+
// which are otherwise violate foreign key constraints
|
|
43
|
+
if (updatesBeforeDelete.length) {
|
|
44
|
+
await _processChunk(processFn, model, dbc, cqns, user, locale, ts, updatesBeforeDelete, results)
|
|
45
|
+
}
|
|
46
|
+
|
|
39
47
|
if (deletes.length > 0) {
|
|
40
48
|
if (chunks) {
|
|
41
49
|
let offset = 0
|
|
@@ -52,6 +52,7 @@ const _generateAliases = (partialCqn, aliasMap = new Map()) => {
|
|
|
52
52
|
_redirectXpr(partialCqn.SELECT.where, selectMap)
|
|
53
53
|
_redirectXpr(partialCqn.SELECT.having, selectMap)
|
|
54
54
|
_redirectXpr(partialCqn.SELECT.columns, selectMap)
|
|
55
|
+
_redirectXpr(partialCqn.SELECT.groupBy, selectMap)
|
|
55
56
|
return
|
|
56
57
|
}
|
|
57
58
|
|
|
@@ -65,7 +66,7 @@ const _generateAliases = (partialCqn, aliasMap = new Map()) => {
|
|
|
65
66
|
return
|
|
66
67
|
}
|
|
67
68
|
|
|
68
|
-
if (partialCqn
|
|
69
|
+
if (Object.prototype.hasOwnProperty.call(partialCqn, 'join')) {
|
|
69
70
|
partialCqn.args.forEach(arg => {
|
|
70
71
|
if (arg.ref) {
|
|
71
72
|
_redirectRef(arg, aliasMap)
|
|
@@ -1,19 +1,19 @@
|
|
|
1
1
|
const cds = require('../../cds')
|
|
2
2
|
const { INSERT, SELECT, UPDATE, DELETE } = cds.ql
|
|
3
3
|
|
|
4
|
-
const {
|
|
4
|
+
const {
|
|
5
|
+
ensureNoDraftsSuffix,
|
|
6
|
+
ensureDraftsSuffix,
|
|
7
|
+
filterKeys,
|
|
8
|
+
getDeleteDraftAdminCqn,
|
|
9
|
+
getCompositionTargets
|
|
10
|
+
} = require('../utils/handler')
|
|
5
11
|
const { readAndDeleteKeywords, isActiveEntityRequested, getKeyData } = require('../utils/where')
|
|
6
12
|
const { isDraftRootEntity } = require('../../fiori/utils/csn')
|
|
7
|
-
const { getVirtuals, postProcessVirtuals } = require('../../db/generic/virtual')
|
|
8
13
|
const { getColumns } = require('../../cds-services/services/utils/columns')
|
|
9
14
|
|
|
10
15
|
const { DRAFT_COLUMNS } = require('../../common/constants/draft')
|
|
11
16
|
|
|
12
|
-
const _getDeleteDraftAdminCqn = draftUUID =>
|
|
13
|
-
DELETE.from('DRAFT.DraftAdministrativeData').where([{ ref: ['DraftUUID'] }, '=', { val: draftUUID }])
|
|
14
|
-
|
|
15
|
-
const _getDeleteRootDraftCqn = (targetName, rootWhere) => DELETE.from(targetName).where(rootWhere)
|
|
16
|
-
|
|
17
17
|
const _getRootCQN = (context, requestActiveData) => {
|
|
18
18
|
const keys = filterKeys(context.target.keys)
|
|
19
19
|
const keyData = getKeyData(keys, context.query.SELECT.from.ref[0].where)
|
|
@@ -147,12 +147,10 @@ const _handler = async function (req) {
|
|
|
147
147
|
r.getUrlObject = () => req.getUrlObject()
|
|
148
148
|
r._.params = req.params
|
|
149
149
|
|
|
150
|
-
const virtuals = getVirtuals(req, this.model)
|
|
151
150
|
// use finally to preserve r.messages in success or error case
|
|
152
151
|
let result
|
|
153
152
|
try {
|
|
154
153
|
result = await this.dispatch(r)
|
|
155
|
-
postProcessVirtuals(virtuals, result)
|
|
156
154
|
} finally {
|
|
157
155
|
// REVISIT: should not be necessary
|
|
158
156
|
if (r.messages) for (const m of r.messages) req.info(m)
|
|
@@ -161,12 +159,18 @@ const _handler = async function (req) {
|
|
|
161
159
|
/*
|
|
162
160
|
* delete draft data
|
|
163
161
|
*/
|
|
164
|
-
const deleteDraftAdminCqn =
|
|
165
|
-
const
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
)
|
|
169
|
-
|
|
162
|
+
const deleteDraftAdminCqn = getDeleteDraftAdminCqn(adminData.DraftUUID)
|
|
163
|
+
const draftTablesToDeleteFrom = [req.target.name + '_drafts']
|
|
164
|
+
for (const [entity] of getCompositionTargets(req.target, this).entries())
|
|
165
|
+
draftTablesToDeleteFrom.push(entity + '_drafts')
|
|
166
|
+
await cds.db.tx(req).run([
|
|
167
|
+
deleteDraftAdminCqn,
|
|
168
|
+
...draftTablesToDeleteFrom.map(dt => {
|
|
169
|
+
const d = DELETE.from(dt).where({ DraftAdministrativeData_DraftUUID: adminData.DraftUUID })
|
|
170
|
+
d._suppressDeepDelete = true // hidden flag to tell db layer that no deep delete is required
|
|
171
|
+
return d
|
|
172
|
+
})
|
|
173
|
+
])
|
|
170
174
|
|
|
171
175
|
return result
|
|
172
176
|
}
|
|
@@ -3,8 +3,8 @@ const cds = require('../../cds')
|
|
|
3
3
|
const { SELECT } = cds.ql
|
|
4
4
|
|
|
5
5
|
const { isNavigationToMany } = require('../utils/req')
|
|
6
|
-
const {
|
|
7
|
-
const { ensureNoDraftsSuffix, ensureDraftsSuffix, draftIsLocked } = require('../utils/handler')
|
|
6
|
+
const { getKeysCondition } = require('../utils/where')
|
|
7
|
+
const { isDraftActivateAction, ensureNoDraftsSuffix, ensureDraftsSuffix, draftIsLocked } = require('../utils/handler')
|
|
8
8
|
|
|
9
9
|
const { isCustomOperation } = require('../../cds-services/adapter/odata-v4/utils/request')
|
|
10
10
|
|
|
@@ -128,15 +128,7 @@ const _addDraftDataFromExistingDraft = async (req, service) => {
|
|
|
128
128
|
}
|
|
129
129
|
|
|
130
130
|
if (!parent) {
|
|
131
|
-
const
|
|
132
|
-
const rootWhere = keys.reduce((res, key) => {
|
|
133
|
-
if (key === 'IsActiveEntity') {
|
|
134
|
-
return res
|
|
135
|
-
}
|
|
136
|
-
res[key] = req.data[key]
|
|
137
|
-
return res
|
|
138
|
-
}, {})
|
|
139
|
-
|
|
131
|
+
const rootWhere = getKeysCondition(req.target, req.data)
|
|
140
132
|
result = await cds.tx(req).run(_getSelectDraftDataCqn(ensureNoDraftsSuffix(req.target.name), rootWhere))
|
|
141
133
|
if (result && result.length > 0) {
|
|
142
134
|
_addDraftDataToContext(req, result)
|
|
@@ -8,7 +8,9 @@ const { deleteDraft } = require('../utils/delete')
|
|
|
8
8
|
* @param req
|
|
9
9
|
*/
|
|
10
10
|
const _handler = function (req) {
|
|
11
|
-
|
|
11
|
+
// we should call deleteDraft only for draft tables and call next
|
|
12
|
+
// to pass delete of active tables to our general implementation
|
|
13
|
+
return deleteDraft(req, this, true)
|
|
12
14
|
}
|
|
13
15
|
|
|
14
16
|
module.exports = function () {
|
|
@@ -92,7 +92,9 @@ const _handler = async function (req) {
|
|
|
92
92
|
res[key] = data[key]
|
|
93
93
|
return res
|
|
94
94
|
}, {})
|
|
95
|
-
|
|
95
|
+
|
|
96
|
+
// cds.db and not "this" as we want to resolve as db here
|
|
97
|
+
const transition = getTransition(req.target, cds.db)
|
|
96
98
|
const lockWhere = _getLockWhere(rootWhere, transition.mapping)
|
|
97
99
|
|
|
98
100
|
// gets the underlying target entity, as record locking can't be
|
|
@@ -110,13 +112,21 @@ const _handler = async function (req) {
|
|
|
110
112
|
const rootCQN = SELECT.from(req.target, columnNames).where(rootWhere)
|
|
111
113
|
const subCQNs = getSubCQNs({
|
|
112
114
|
definitions,
|
|
113
|
-
context: req,
|
|
114
115
|
rootCQN,
|
|
115
116
|
compositionTree: getCompositionTree({ definitions, rootEntityName: ensureNoDraftsSuffix(req.target.name) })
|
|
116
117
|
})
|
|
117
118
|
const rootDraftName = ensureDraftsSuffix(req.target.name)
|
|
118
119
|
const draftExistsCQN = SELECT.from(rootDraftName, ['DraftAdministrativeData_DraftUUID as DraftUUID']).where(rootWhere)
|
|
119
120
|
const selectCQNs = [rootCQN, ...subCQNs.map(obj => obj.cqn)]
|
|
121
|
+
|
|
122
|
+
// fetch unlocalized data if not a texts entity
|
|
123
|
+
for (const q of selectCQNs) {
|
|
124
|
+
const entity = definitions[q.SELECT.from.ref[0]]
|
|
125
|
+
if (entity && !entity.name.match(/\.texts$/)) {
|
|
126
|
+
Object.defineProperty(q, '_suppressLocalization', { value: true })
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
120
130
|
const lockAndSelectCQNs = [lockRecordCQN, draftExistsCQN, ...selectCQNs]
|
|
121
131
|
|
|
122
132
|
const dbtx = cds.tx(req)
|
|
@@ -6,7 +6,6 @@ const { isNavigationToMany } = require('../utils/req')
|
|
|
6
6
|
const { getKeysCondition } = require('../utils/where')
|
|
7
7
|
const { removeDraftUUIDIfNecessary, ensureDraftsSuffix } = require('../utils/handler')
|
|
8
8
|
const { DRAFT_COLUMNS } = require('../../common/constants/draft')
|
|
9
|
-
const { getVirtuals, postProcessVirtuals } = require('../../db/generic/virtual')
|
|
10
9
|
|
|
11
10
|
const _getUpdateDraftAdminCQN = ({ user, timestamp }, draftUUID) => {
|
|
12
11
|
return UPDATE('DRAFT.DraftAdministrativeData')
|
|
@@ -76,10 +75,13 @@ const _handler = async function (req, next) {
|
|
|
76
75
|
const insertDataCQN = _getInsertDataCQN(req, req.data.DraftAdministrativeData_DraftUUID)
|
|
77
76
|
|
|
78
77
|
// read data as on db and return
|
|
79
|
-
const
|
|
78
|
+
const columns = Object.keys(req.target.elements)
|
|
79
|
+
.map(e => req.target.elements[e])
|
|
80
|
+
.filter(e => !e.isAssociation)
|
|
81
|
+
.map(e => e.name)
|
|
82
|
+
const readInsertDataCQN = SELECT.from(insertDataCQN.INSERT.into).columns(columns)
|
|
80
83
|
readInsertDataCQN.where(getKeysCondition(req.target, req.data))
|
|
81
84
|
|
|
82
|
-
const virtuals = getVirtuals({ query: readInsertDataCQN, target: req.target }, this.model)
|
|
83
85
|
const dbtx = cds.tx(req)
|
|
84
86
|
|
|
85
87
|
await Promise.all([dbtx.run(adminDataCQN), dbtx.run(insertDataCQN)])
|
|
@@ -89,8 +91,6 @@ const _handler = async function (req, next) {
|
|
|
89
91
|
req.reject(404)
|
|
90
92
|
}
|
|
91
93
|
|
|
92
|
-
postProcessVirtuals(virtuals, result)
|
|
93
|
-
|
|
94
94
|
removeDraftUUIDIfNecessary(result[0], req)
|
|
95
95
|
|
|
96
96
|
return result[0]
|
|
@@ -53,21 +53,6 @@ const _getUpdateDraftCQN = ({ query, target: { name } }, keysCondition) => {
|
|
|
53
53
|
return UPDATE(ensureDraftsSuffix(name)).data(set).where(keysCondition)
|
|
54
54
|
}
|
|
55
55
|
|
|
56
|
-
const _deleteDraftAdminProperties = oldData => {
|
|
57
|
-
for (const toBeDeletedProperty of [
|
|
58
|
-
'DraftIsCreatedByMe',
|
|
59
|
-
'DraftIsProcessedByMe',
|
|
60
|
-
'InProcessByUser',
|
|
61
|
-
'LastChangeDateTime',
|
|
62
|
-
'LastChangedByUser',
|
|
63
|
-
'CreatedByUser',
|
|
64
|
-
'CreationDateTime',
|
|
65
|
-
'DraftUUID'
|
|
66
|
-
]) {
|
|
67
|
-
delete oldData[toBeDeletedProperty]
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
|
|
71
56
|
/**
|
|
72
57
|
* Generic Handler for PATCH requests in the context of draft.
|
|
73
58
|
* In case of success it returns the updated entry.
|
|
@@ -91,9 +76,6 @@ const _handler = async function (req) {
|
|
|
91
76
|
req.reject(403)
|
|
92
77
|
}
|
|
93
78
|
|
|
94
|
-
_deleteDraftAdminProperties(result[0])
|
|
95
|
-
req._oldData = result[0]
|
|
96
|
-
|
|
97
79
|
const updateDraftCQN = _getUpdateDraftCQN(req, keysCondition)
|
|
98
80
|
const updateDraftAdminCQN = getUpdateDraftAdminCQN(req, result[0].DraftAdministrativeData_DraftUUID)
|
|
99
81
|
|