@sap/cds 6.8.4 → 7.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 +58 -5
- package/README.md +0 -1
- package/bin/cds-serve.js +50 -3
- package/bin/deploy/to-hana.js +1 -0
- package/bin/serve.js +16 -20
- package/lib/auth/basic-auth.js +6 -4
- package/lib/auth/index.js +4 -3
- package/lib/auth/jwt-auth.js +2 -5
- package/lib/compile/cds-compile.js +34 -89
- package/lib/compile/cdsc.js +11 -0
- package/lib/compile/etc/properties.js +2 -2
- package/lib/compile/for/lean_drafts.js +36 -69
- package/lib/compile/for/nodejs.js +2 -1
- package/lib/compile/load.js +1 -1
- package/lib/compile/minify.js +2 -0
- package/lib/compile/to/csn.js +74 -0
- package/{bin/build/provider/hana/2tabledata.js → lib/compile/to/hdbtabledata.js} +4 -6
- package/lib/compile/to/json.js +1 -1
- package/lib/compile/to/sql.js +8 -6
- package/lib/dbs/cds-deploy.js +174 -114
- package/lib/env/cds-env.js +64 -79
- package/lib/env/cds-requires.js +11 -28
- package/lib/env/defaults.js +13 -3
- package/lib/env/plugins.js +1 -12
- package/lib/env/presets.js +25 -21
- package/lib/index.js +121 -147
- package/lib/{core/reflect.js → linked/models.js} +2 -2
- package/lib/{core/infer.js → linked/queries.js} +2 -0
- package/lib/{core/index.js → linked/types.js} +2 -1
- package/lib/log/cds-error.js +13 -7
- package/lib/log/format/cf.js +1 -1
- package/lib/plugins.js +49 -0
- package/lib/ql/Query.js +0 -9
- package/lib/ql/STREAM.js +0 -1
- package/lib/req/context.js +2 -7
- package/lib/req/request.js +6 -2
- package/lib/req/response.js +23 -10
- package/lib/srv/middlewares/ctx-model.js +1 -1
- package/lib/srv/middlewares/errors.js +1 -1
- package/lib/srv/protocols/_legacy.js +1 -0
- package/lib/srv/protocols/graphql.js +7 -16
- package/lib/srv/protocols/index.js +59 -45
- package/lib/srv/protocols/odata-v2-proxy.js +2 -70
- package/lib/srv/srv-api.js +9 -3
- package/lib/srv/srv-dispatch.js +12 -9
- package/lib/srv/srv-models.js +4 -21
- package/lib/srv/srv-tx.js +15 -12
- package/lib/utils/cds-test.js +14 -9
- package/lib/utils/cds-utils.js +2 -12
- package/lib/utils/check-version.js +17 -0
- package/{bin/build → lib/utils}/csv-reader.js +23 -24
- package/libx/_runtime/auth/index.js +27 -23
- package/libx/_runtime/cds-services/adapter/odata-v4/ODataRequest.js +15 -72
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/create.js +1 -1
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/metadata.js +0 -2
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/read.js +33 -63
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/request.js +14 -18
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/update.js +15 -5
- package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/ExpressionToCQN.js +5 -4
- package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/readToCQN.js +37 -40
- package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/updateToCQN.js +7 -1
- package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/utils.js +101 -38
- package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-commons/errors/AbstractError.js +5 -1
- package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-commons/utils/ValueConverter.js +2 -1
- package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-server/deserializer/ResourceJsonDeserializer.js +9 -8
- package/libx/_runtime/cds-services/adapter/odata-v4/to.js +1 -1
- package/libx/_runtime/cds-services/adapter/odata-v4/utils/data.js +15 -11
- package/libx/_runtime/cds-services/adapter/odata-v4/utils/readAfterWrite.js +4 -0
- package/libx/_runtime/cds-services/adapter/odata-v4/utils/result.js +5 -2
- package/libx/_runtime/cds-services/adapter/odata-v4/utils/stream.js +1 -123
- package/libx/_runtime/cds-services/services/Service.js +79 -107
- package/libx/_runtime/cds-services/services/utils/columns.js +23 -19
- package/libx/_runtime/cds-services/services/utils/compareJson.js +11 -1
- package/libx/_runtime/cds-services/services/utils/differ.js +7 -2
- package/libx/_runtime/cds-services/util/assert.js +65 -2
- package/libx/_runtime/common/composition/data.js +1 -0
- package/libx/_runtime/common/generic/auth/expand.js +1 -1
- package/libx/_runtime/common/generic/auth/restrict.js +5 -10
- package/libx/_runtime/common/generic/auth/restrictions.js +40 -0
- package/libx/_runtime/common/generic/auth/utils.js +1 -2
- package/libx/_runtime/common/generic/crud.js +32 -16
- package/libx/_runtime/common/generic/etag.js +133 -104
- package/libx/_runtime/common/generic/input.js +6 -21
- package/libx/_runtime/common/generic/put.js +1 -1
- package/libx/_runtime/common/generic/stream.js +52 -0
- package/libx/_runtime/common/generic/temporal.js +25 -8
- package/libx/_runtime/common/i18n/messages.properties +0 -2
- package/libx/_runtime/common/utils/cqn.js +1 -1
- package/libx/_runtime/common/utils/cqn2cqn4sql.js +5 -2
- package/libx/_runtime/common/utils/csn.js +0 -51
- package/libx/_runtime/common/utils/etag.js +30 -0
- package/libx/_runtime/common/utils/keys.js +1 -1
- package/libx/_runtime/common/utils/normalizeTimestamp.js +25 -0
- package/libx/_runtime/common/utils/path.js +1 -1
- package/libx/_runtime/common/utils/resolveView.js +2 -1
- package/libx/_runtime/common/utils/rewriteAsterisks.js +6 -4
- package/libx/_runtime/common/utils/search2cqn4sql.js +12 -16
- package/libx/_runtime/common/utils/stream.js +140 -0
- package/libx/_runtime/common/utils/streamProp.js +29 -12
- package/libx/_runtime/common/utils/templateProcessorPathSerializer.js +0 -2
- package/libx/_runtime/db/generic/index.js +0 -2
- package/libx/_runtime/db/query/delete.js +2 -2
- package/libx/_runtime/db/query/insert.js +2 -2
- package/libx/_runtime/db/query/read.js +2 -2
- package/libx/_runtime/db/query/run.js +2 -2
- package/libx/_runtime/db/query/update.js +2 -2
- package/libx/_runtime/db/sql-builder/BaseBuilder.js +0 -6
- package/libx/_runtime/db/sql-builder/ExpressionBuilder.js +23 -12
- package/libx/_runtime/db/sql-builder/FunctionBuilder.js +18 -6
- package/libx/_runtime/db/sql-builder/InsertBuilder.js +1 -0
- package/libx/_runtime/db/sql-builder/SelectBuilder.js +3 -7
- package/libx/_runtime/db/sql-builder/UpsertBuilder.js +1 -0
- package/libx/_runtime/db/utils/normalizeTimeData.js +7 -3
- package/libx/_runtime/fiori/draft.js +2 -0
- package/libx/_runtime/fiori/generic/activate.js +8 -9
- package/libx/_runtime/fiori/generic/before.js +30 -20
- package/libx/_runtime/fiori/generic/cancel.js +5 -3
- package/libx/_runtime/fiori/generic/delete.js +5 -3
- package/libx/_runtime/fiori/generic/edit.js +7 -7
- package/libx/_runtime/fiori/generic/index.js +10 -16
- package/libx/_runtime/fiori/generic/new.js +5 -3
- package/libx/_runtime/fiori/generic/patch.js +11 -8
- package/libx/_runtime/fiori/generic/prepare.js +13 -6
- package/libx/_runtime/fiori/generic/read.js +12 -6
- package/libx/_runtime/fiori/lean-draft.js +207 -152
- package/libx/_runtime/fiori/utils/delete.js +10 -5
- package/libx/_runtime/fiori/utils/req.js +17 -5
- package/libx/_runtime/fiori/utils/stream.js +36 -0
- package/libx/_runtime/hana/Service.js +12 -9
- package/libx/_runtime/hana/conversion.js +10 -15
- package/libx/_runtime/hana/driver.js +2 -0
- package/libx/_runtime/hana/execute.js +28 -6
- package/libx/_runtime/hana/pool.js +36 -122
- package/libx/_runtime/hana/search2cqn4sql.js +34 -36
- package/libx/_runtime/messaging/enterprise-messaging-utils/getTenantInfo.js +2 -6
- package/libx/_runtime/messaging/enterprise-messaging-utils/registerEndpoints.js +3 -1
- package/libx/_runtime/messaging/enterprise-messaging.js +10 -58
- package/libx/_runtime/messaging/outbox/utils.js +1 -1
- package/libx/_runtime/remote/Service.js +20 -1
- package/libx/_runtime/remote/utils/client.js +3 -5
- package/libx/_runtime/sqlite/Service.js +4 -6
- package/libx/_runtime/sqlite/conversion.js +3 -13
- package/libx/_runtime/sqlite/customBuilder/CustomFunctionBuilder.js +9 -6
- package/libx/_runtime/sqlite/customBuilder/CustomUpsertBuilder.js +6 -1
- package/libx/_runtime/sqlite/execute.js +5 -16
- package/libx/odata/afterburner.js +22 -6
- package/libx/odata/grammar.pegjs +6 -1
- package/libx/odata/parser.js +1 -1
- package/libx/rest/RestAdapter.js +16 -9
- package/libx/rest/RestRequest.js +1 -1
- package/libx/rest/middleware/input.js +2 -1
- package/libx/rest/middleware/operation.js +1 -0
- package/libx/rest/middleware/parse.js +3 -2
- package/libx/rest/middleware/payload.js +9 -8
- package/libx/rest/middleware/read.js +1 -0
- package/package.json +9 -16
- package/app/fiori/preview.js +0 -270
- package/app/fiori/routes.js +0 -59
- package/bin/build/buildTaskEngine.js +0 -360
- package/bin/build/buildTaskFactory.js +0 -283
- package/bin/build/buildTaskHandler.js +0 -241
- package/bin/build/buildTaskProvider.js +0 -22
- package/bin/build/buildTaskProviderFactory.js +0 -175
- package/bin/build/cds.js +0 -5
- package/bin/build/constants.js +0 -66
- package/bin/build/index.js +0 -58
- package/bin/build/provider/buildTaskHandlerEdmx.js +0 -82
- package/bin/build/provider/buildTaskHandlerFeatureToggles.js +0 -131
- package/bin/build/provider/buildTaskHandlerInternal.js +0 -254
- package/bin/build/provider/buildTaskProviderInternal.js +0 -383
- package/bin/build/provider/fiori/index.js +0 -171
- package/bin/build/provider/hana/2migration.js +0 -179
- package/bin/build/provider/hana/index.js +0 -505
- package/bin/build/provider/hana/migrationtable.js +0 -472
- package/bin/build/provider/hana/template/.hdiconfig-haas +0 -163
- package/bin/build/provider/hana/template/.hdiconfig-hanacloud +0 -137
- package/bin/build/provider/hana/template/.hdinamespace +0 -4
- package/bin/build/provider/hana/template/package.json +0 -12
- package/bin/build/provider/hana/template/undeploy.json +0 -5
- package/bin/build/provider/java/index.js +0 -111
- package/bin/build/provider/java-cf/index.js +0 -1
- package/bin/build/provider/mtx/index.js +0 -268
- package/bin/build/provider/mtx/resourcesTarBuilder.js +0 -95
- package/bin/build/provider/mtx-extension/index.js +0 -131
- package/bin/build/provider/mtx-sidecar/index.js +0 -137
- package/bin/build/provider/node-cf/index.js +0 -1
- package/bin/build/provider/nodejs/index.js +0 -192
- package/bin/build/util.js +0 -299
- package/bin/cds.js +0 -125
- package/bin/deploy/to-hana/cfUtil.js +0 -355
- package/bin/deploy/to-hana/gitUtil.js +0 -57
- package/bin/deploy/to-hana/hana.js +0 -306
- package/bin/deploy/to-hana/hdiDeployUtil.js +0 -153
- package/bin/deploy/to-hana/index.js +0 -16
- package/bin/deploy/to-hana/mtaUtil.js +0 -170
- package/bin/mtx/in-cds.js +0 -17
- package/bin/plugins.js +0 -32
- package/bin/run.js +0 -24
- package/bin/utils/log.js +0 -24
- package/bin/version.js +0 -178
- package/libx/_runtime/audit/Service.js +0 -222
- package/libx/_runtime/audit/generic/personal/access.js +0 -61
- package/libx/_runtime/audit/generic/personal/index.js +0 -56
- package/libx/_runtime/audit/generic/personal/modification.js +0 -132
- package/libx/_runtime/audit/generic/personal/utils.js +0 -186
- package/libx/_runtime/audit/utils/log.js +0 -23
- package/libx/_runtime/audit/utils/v2.js +0 -176
- package/libx/_runtime/db/data-conversion/timestamp.js +0 -9
- package/libx/_runtime/db/generic/integrity.js +0 -455
- package/srv/audit-log.cds +0 -87
- package/srv/mtx.cds +0 -2
- package/srv/mtx.js +0 -8
- /package/lib/{core → linked}/classes.js +0 -0
- /package/lib/{core → linked}/entities.js +0 -0
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
const { isAsteriskColumn } = require('./rewriteAsterisks')
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Recursively adds etag columns if a manual list of columns is specified.
|
|
5
|
+
* If asterisk columns or no columns are given, the database layer will
|
|
6
|
+
* add the etag columns anyway.
|
|
7
|
+
*/
|
|
8
|
+
const addEtagColumns = (columns, entity) => {
|
|
9
|
+
if (!columns || !Array.isArray(columns)) return
|
|
10
|
+
if (
|
|
11
|
+
entity._etag &&
|
|
12
|
+
!columns.some(c => isAsteriskColumn(c)) &&
|
|
13
|
+
!(columns.length === 1 && columns[0].func === 'count') &&
|
|
14
|
+
!columns.some(c => c.ref && c.ref[c.ref.length - 1] === entity._etag.name)
|
|
15
|
+
) {
|
|
16
|
+
columns.push({ ref: [entity._etag.name] })
|
|
17
|
+
}
|
|
18
|
+
const expands = columns.filter(c => c.expand)
|
|
19
|
+
for (const expand of expands) {
|
|
20
|
+
const refName = expand.ref[expand.ref.length - 1]
|
|
21
|
+
const targetEntity = refName && entity.elements[refName] && entity.elements[refName]._target
|
|
22
|
+
if (targetEntity) {
|
|
23
|
+
addEtagColumns(expand.expand, targetEntity)
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
module.exports = {
|
|
29
|
+
addEtagColumns
|
|
30
|
+
}
|
|
@@ -92,7 +92,7 @@ function _getWhereFromUpdate(query, target, model) {
|
|
|
92
92
|
|
|
93
93
|
const where = query.UPDATE.where || []
|
|
94
94
|
if (query.UPDATE.entity.ref?.length === 1 && query.UPDATE.entity.ref[0].where)
|
|
95
|
-
return _mergeWhere(query.UPDATE.entity.ref[0].where, where)
|
|
95
|
+
return _mergeWhere(where.length ? [...query.UPDATE.entity.ref[0].where] : query.UPDATE.entity.ref[0].where, where)
|
|
96
96
|
return where
|
|
97
97
|
}
|
|
98
98
|
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
const cds = require('../../cds')
|
|
2
|
+
const PRECISION = cds.env.features.precise_timestamps ? 7 : 3
|
|
3
|
+
|
|
4
|
+
const TZ_REGEX = new RegExp(/(Z|[+-][01]\d:?[0-5]\d)$/)
|
|
5
|
+
const NON_DIGIT_REGEX = new RegExp(/\D/, 'g')
|
|
6
|
+
|
|
7
|
+
const _lengthIfNotFoundIndex = (index, length) => (index > -1 ? index : length)
|
|
8
|
+
|
|
9
|
+
module.exports = value => {
|
|
10
|
+
if (value instanceof Date) value = value.toISOString()
|
|
11
|
+
if (typeof value === 'number') value = new Date(value).toISOString()
|
|
12
|
+
|
|
13
|
+
const decimalPointIndex = _lengthIfNotFoundIndex(value.lastIndexOf('.'), value.length)
|
|
14
|
+
const tzRegexMatch = TZ_REGEX.exec(value)
|
|
15
|
+
const tz = tzRegexMatch?.[0] || ''
|
|
16
|
+
const tzIndex = _lengthIfNotFoundIndex(tzRegexMatch?.index, value.length)
|
|
17
|
+
const dateEndIndex = Math.min(decimalPointIndex, tzIndex)
|
|
18
|
+
const dateNoMillisNoTZ = new Date(value.slice(0, dateEndIndex) + tz).toISOString().slice(0, 19)
|
|
19
|
+
const normalizedFractionalDigits = value
|
|
20
|
+
.slice(dateEndIndex + 1, tzIndex)
|
|
21
|
+
.replace(NON_DIGIT_REGEX, '')
|
|
22
|
+
.padEnd(PRECISION, '0')
|
|
23
|
+
.slice(0, PRECISION)
|
|
24
|
+
return dateNoMillisNoTZ + (normalizedFractionalDigits ? '.' + normalizedFractionalDigits : '') + 'Z'
|
|
25
|
+
}
|
|
@@ -9,7 +9,7 @@ const getEntityFromPath = (path, def) => {
|
|
|
9
9
|
let id
|
|
10
10
|
for (const segment of path.ref) {
|
|
11
11
|
id = ensureNoDraftsSuffix(segment.id || segment)
|
|
12
|
-
current = current
|
|
12
|
+
current = current?.elements[id] || current
|
|
13
13
|
if (current && current.target) current = current._target
|
|
14
14
|
}
|
|
15
15
|
return current
|
|
@@ -217,6 +217,7 @@ const _newInsertColumns = (columns = [], transition) => {
|
|
|
217
217
|
return newColumns
|
|
218
218
|
}
|
|
219
219
|
|
|
220
|
+
// REVISIT: this hard-coding on ref indexes does not support path expressions
|
|
220
221
|
const _newWhereRef = (newWhereElement, transition, alias, tableName, isSubSelect) => {
|
|
221
222
|
const newRef = Array.isArray(newWhereElement.ref) ? [...newWhereElement.ref] : [newWhereElement.ref]
|
|
222
223
|
if (newRef[0] === alias) {
|
|
@@ -249,7 +250,7 @@ const _newWhere = (where = [], transition, tableName, alias, isSubselect = false
|
|
|
249
250
|
|
|
250
251
|
const newWhereElement = { ...whereElement }
|
|
251
252
|
if (!whereElement.ref && !whereElement.SELECT && !whereElement.func) return whereElement
|
|
252
|
-
if (whereElement.SELECT && whereElement.SELECT.where) {
|
|
253
|
+
if (whereElement.SELECT && whereElement.SELECT.where && !whereElement._doNotResolve) {
|
|
253
254
|
newWhereElement.SELECT.where = _newWhere(whereElement.SELECT.where, transition, tableName, alias, true)
|
|
254
255
|
return newWhereElement
|
|
255
256
|
} else {
|
|
@@ -6,8 +6,9 @@ const cds = require('../../cds')
|
|
|
6
6
|
|
|
7
7
|
const isAsteriskColumn = col => col === '*' || (col.ref && col.ref[0] === '*' && !col.expand)
|
|
8
8
|
|
|
9
|
-
const
|
|
10
|
-
if (newColumn.as
|
|
9
|
+
const isDuplicate = newColumn => column => {
|
|
10
|
+
if (newColumn.as && column.as) return column.as === newColumn.as
|
|
11
|
+
if ((newColumn.as && !column.as) || (!newColumn.as && column.as)) return
|
|
11
12
|
if (!column.ref) return
|
|
12
13
|
if (Array.isArray(newColumn)) newColumn = { ref: newColumn }
|
|
13
14
|
return newColumn.ref ? newColumn.ref.join('_') === column.ref.join('_') : newColumn === column.ref.join('_')
|
|
@@ -50,7 +51,7 @@ const _rewriteAsterisk = (columns, target, _4db, isRoot) => {
|
|
|
50
51
|
1,
|
|
51
52
|
...getColumns(target, { _4db })
|
|
52
53
|
.map(c => ({ ref: [c.name] }))
|
|
53
|
-
.filter(c => !columns.find(
|
|
54
|
+
.filter(c => !columns.find(isDuplicate(c)) && (isRoot || c.ref[0] !== 'DraftAdministrativeData_DraftUUID'))
|
|
54
55
|
)
|
|
55
56
|
}
|
|
56
57
|
}
|
|
@@ -136,5 +137,6 @@ const rewriteAsterisks = (query, model, options) => {
|
|
|
136
137
|
module.exports = {
|
|
137
138
|
rewriteAsterisks,
|
|
138
139
|
isAsteriskColumn,
|
|
139
|
-
rewriteExpandAsterisk
|
|
140
|
+
rewriteExpandAsterisk,
|
|
141
|
+
isDuplicate
|
|
140
142
|
}
|
|
@@ -12,30 +12,26 @@ const _targetFrom = (cqn, options) => {
|
|
|
12
12
|
const search2cqn4sql = (query, model, options = {}) => {
|
|
13
13
|
const cqnSearchPhrase = query.SELECT.search
|
|
14
14
|
if (!cqnSearchPhrase) return
|
|
15
|
+
|
|
15
16
|
const { search2cqn4sql } = options
|
|
16
17
|
const { entityName, alias } = _targetFrom(query.SELECT.from, options)
|
|
17
18
|
const entity = model.definitions[entityName]
|
|
18
|
-
const
|
|
19
|
+
const aggregated = query._aggregated || /* new parser */ query.SELECT.groupBy
|
|
20
|
+
|
|
19
21
|
// Call custom (optimized search to cqn for sql implementation) that tries
|
|
20
22
|
// to optimize the search behavior for a specific database service.
|
|
21
|
-
|
|
22
|
-
if (
|
|
23
|
-
typeof search2cqn4sql === 'function' &&
|
|
24
|
-
!query.SELECT.count &&
|
|
25
|
-
localizedAssociation &&
|
|
26
|
-
!(query._aggregated || /* new parser */ query.SELECT.groupBy)
|
|
27
|
-
) {
|
|
23
|
+
if (typeof search2cqn4sql === 'function' && entity.associations?.localized && !aggregated) {
|
|
28
24
|
const search2cqnOptions = { columns: computeColumnsToBeSearched(query, entity), locale: options.locale }
|
|
29
25
|
return search2cqn4sql(query, entity, search2cqnOptions)
|
|
30
|
-
} else {
|
|
31
|
-
const columnsToBeSearched = computeColumnsToBeSearched(query, entity, alias)
|
|
32
|
-
const expression = columnsToBeSearched?.length
|
|
33
|
-
? searchToLike(cqnSearchPhrase, columnsToBeSearched)
|
|
34
|
-
: [{ val: 0 }, '=', { val: 1 }]
|
|
35
|
-
|
|
36
|
-
// REVISIT: find out here if where or having must be used
|
|
37
|
-
query._aggregated || /* if new parser */ query.SELECT.groupBy ? query.having(expression) : query.where(expression)
|
|
38
26
|
}
|
|
27
|
+
|
|
28
|
+
const columnsToBeSearched = computeColumnsToBeSearched(query, entity, alias)
|
|
29
|
+
const expression = columnsToBeSearched?.length
|
|
30
|
+
? searchToLike(cqnSearchPhrase, columnsToBeSearched)
|
|
31
|
+
: [{ val: 0 }, '=', { val: 1 }]
|
|
32
|
+
|
|
33
|
+
// REVISIT: find out here if where or having must be used
|
|
34
|
+
aggregated ? query.having(expression) : query.where(expression)
|
|
39
35
|
}
|
|
40
36
|
|
|
41
37
|
module.exports = search2cqn4sql
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
const cds = require('../../cds')
|
|
2
|
+
const LOG = cds.log('odata')
|
|
3
|
+
const { SELECT } = cds.ql
|
|
4
|
+
const { deepCopyArray } = require('./copy')
|
|
5
|
+
const { getTransition } = require('./resolveView')
|
|
6
|
+
const { cqn2cqn4sql } = require('./cqn2cqn4sql')
|
|
7
|
+
const getTemplate = require('./template')
|
|
8
|
+
const templateProcessor = require('./templateProcessor')
|
|
9
|
+
const { adaptStreamCQN } = require('../../fiori/utils/stream.js')
|
|
10
|
+
const { isPathToDraft } = require('./cqn')
|
|
11
|
+
|
|
12
|
+
// eslint-disable-next-line complexity
|
|
13
|
+
const _getStreamProperties = (req, query, model) => {
|
|
14
|
+
// new odata parser sets streaming property in SELECT.from
|
|
15
|
+
const ref = query.SELECT
|
|
16
|
+
? (query.SELECT.columns && query.SELECT.columns[0].ref) || query.SELECT.from.ref
|
|
17
|
+
: [query.STREAM.column]
|
|
18
|
+
const propertyName = ref[ref.length - 1]
|
|
19
|
+
let mediaTypeProperty
|
|
20
|
+
for (let key in req.target.elements) {
|
|
21
|
+
const val = req.target.elements[key]
|
|
22
|
+
if (val['@Core.MediaType'] && val.name === propertyName) {
|
|
23
|
+
mediaTypeProperty = val
|
|
24
|
+
break
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
let contentType, contentDispositionFilename
|
|
29
|
+
const columns = []
|
|
30
|
+
if (typeof mediaTypeProperty['@Core.MediaType'] === 'object') {
|
|
31
|
+
let contentTypeProperty = mediaTypeProperty['@Core.MediaType']['=']
|
|
32
|
+
if (!req.target.elements[contentTypeProperty]) {
|
|
33
|
+
LOG._warn &&
|
|
34
|
+
LOG.warn(
|
|
35
|
+
`@Core.MediaType in entity "${req.target.name}" points to property "${contentTypeProperty}" which was renamed or is not part of the projection. You must update the annotation value.`
|
|
36
|
+
)
|
|
37
|
+
const mapping = getTransition(req.target, cds.db).mapping
|
|
38
|
+
const key = [...mapping.entries()].find(({ 1: val }) => val.ref[0] === contentTypeProperty)
|
|
39
|
+
contentTypeProperty = key && key.length && key[0]
|
|
40
|
+
}
|
|
41
|
+
if (!req.target.elements[contentTypeProperty]) {
|
|
42
|
+
LOG._warn && LOG.warn(`MediaType ${contentTypeProperty} not found in entity "${req.target.name}".`)
|
|
43
|
+
} else {
|
|
44
|
+
columns.push({ ref: [contentTypeProperty], as: 'contentType' })
|
|
45
|
+
}
|
|
46
|
+
} else {
|
|
47
|
+
contentType = mediaTypeProperty['@Core.MediaType']
|
|
48
|
+
}
|
|
49
|
+
if (mediaTypeProperty['@Core.ContentDisposition.Filename']) {
|
|
50
|
+
if (typeof mediaTypeProperty['@Core.ContentDisposition.Filename'] === 'object') {
|
|
51
|
+
let contentDispositionProperty = mediaTypeProperty['@Core.ContentDisposition.Filename']['=']
|
|
52
|
+
if (!req.target.elements[contentDispositionProperty]) {
|
|
53
|
+
LOG._warn &&
|
|
54
|
+
LOG.warn(
|
|
55
|
+
`@Core.ContentDisposition.Filename in entity "${req.target.name}" points to property "${contentDispositionProperty}" which was renamed or is not part of the projection. You must update the annotation value.`
|
|
56
|
+
)
|
|
57
|
+
const mapping = getTransition(req.target, cds.db).mapping
|
|
58
|
+
const key = [...mapping.entries()].find(({ 1: val }) => val.ref[0] === contentDispositionProperty)
|
|
59
|
+
contentDispositionProperty = key && key.length && key[0]
|
|
60
|
+
}
|
|
61
|
+
if (!req.target.elements[contentDispositionProperty]) {
|
|
62
|
+
LOG._warn &&
|
|
63
|
+
LOG.warn(`ContentDisposition ${contentDispositionProperty} not found in entity "${req.target.name}".`)
|
|
64
|
+
} else {
|
|
65
|
+
columns.push({ ref: [contentDispositionProperty], as: 'contentDispositionFilename' })
|
|
66
|
+
}
|
|
67
|
+
} else {
|
|
68
|
+
contentDispositionFilename = mediaTypeProperty['@Core.ContentDisposition.Filename']
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
const contentDispositionType = mediaTypeProperty['@Core.ContentDisposition.Type']
|
|
72
|
+
|
|
73
|
+
if (columns.length && cds.db && !req.target._hasPersistenceSkip) {
|
|
74
|
+
// used cloned path
|
|
75
|
+
const ref = query.SELECT ? query.SELECT.from.ref : query.STREAM.from.ref
|
|
76
|
+
const as = query.SELECT ? query.SELECT.from.as : query.STREAM.from.as
|
|
77
|
+
let select = SELECT.one.from({ ref: deepCopyArray(ref), as }).columns(columns)
|
|
78
|
+
const where = query.SELECT ? query.SELECT.where : query.STREAM.where
|
|
79
|
+
if (where?.length) select.SELECT.where = where
|
|
80
|
+
if (!(isNewStream() || cds.env.fiori.lean_draft)) {
|
|
81
|
+
const draft = req.target._isDraftEnabled && isPathToDraft(select.SELECT.from.ref, model)
|
|
82
|
+
if (draft) {
|
|
83
|
+
select = cqn2cqn4sql(select, model)
|
|
84
|
+
adaptStreamCQN(select, draft)
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
return cds
|
|
89
|
+
.tx(req)
|
|
90
|
+
.run(select)
|
|
91
|
+
.then(res => ({
|
|
92
|
+
contentType: (res && res.contentType) || contentType,
|
|
93
|
+
contentDispositionFilename: (res && res.contentDispositionFilename) || contentDispositionFilename,
|
|
94
|
+
contentDispositionType
|
|
95
|
+
}))
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
return Promise.resolve({ contentType, contentDispositionFilename, contentDispositionType })
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
const enhanceStreamResult = async (req, query, result, model) => {
|
|
102
|
+
if (!result) return
|
|
103
|
+
if (result.$mediaContentType || result.$mediaContentDispositionFilename || result.$mediaContentDispositionType) return
|
|
104
|
+
|
|
105
|
+
const { contentType, contentDispositionFilename, contentDispositionType } = await _getStreamProperties(
|
|
106
|
+
req,
|
|
107
|
+
query,
|
|
108
|
+
model
|
|
109
|
+
)
|
|
110
|
+
if (contentType) result.$mediaContentType = contentType
|
|
111
|
+
if (contentDispositionFilename) {
|
|
112
|
+
result.$mediaContentDispositionFilename = contentDispositionFilename
|
|
113
|
+
if (contentDispositionType) result.$mediaContentDispositionType = contentDispositionType
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
const pick = element => {
|
|
118
|
+
return element['@Core.IsURL']
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
const processFn = ({ row, key }) => {
|
|
122
|
+
row[`${key}@odata.mediaReadLink`] = row[key]
|
|
123
|
+
delete row[key]
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
function transformRedirectProperties(req, service, result) {
|
|
127
|
+
if (!Array.isArray(result)) result = [result]
|
|
128
|
+
if (result.length === 0) return
|
|
129
|
+
|
|
130
|
+
const template = getTemplate('redirect-properties', service, req.target, { pick })
|
|
131
|
+
if (template && template.elements.size) {
|
|
132
|
+
for (const row of result) {
|
|
133
|
+
templateProcessor({ processFn, row, template })
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
const isNewStream = () => cds.env.features.new_stream && (!cds.db || !!cds.db.stream('foo').STREAM)
|
|
139
|
+
|
|
140
|
+
module.exports = { isNewStream, enhanceStreamResult, transformRedirectProperties }
|
|
@@ -1,21 +1,38 @@
|
|
|
1
1
|
const { ensureNoDraftsSuffix, ensureUnlocalized } = require('../../fiori/utils/handler')
|
|
2
|
+
const { isDuplicate } = require('./rewriteAsterisks')
|
|
3
|
+
|
|
4
|
+
const _addColumn = (name, type, columns) => {
|
|
5
|
+
if (typeof type === 'object') {
|
|
6
|
+
const ref = {
|
|
7
|
+
ref: [type['=']],
|
|
8
|
+
as: `${name}@odata.mediaContentType`
|
|
9
|
+
}
|
|
10
|
+
if (!columns.find(isDuplicate(ref))) columns.push(ref)
|
|
11
|
+
} else {
|
|
12
|
+
const val = { val: type, as: `${name}@odata.mediaContentType` }
|
|
13
|
+
if (!columns.find(isDuplicate(val))) columns.push(val)
|
|
14
|
+
}
|
|
15
|
+
}
|
|
2
16
|
|
|
3
17
|
const _changeStreamProperties = (target, columns, model) => {
|
|
4
|
-
|
|
18
|
+
let index = columns.length
|
|
19
|
+
while (index--) {
|
|
5
20
|
const col = columns[index]
|
|
6
21
|
const name = col.ref && col.ref[col.ref.length - 1]
|
|
7
22
|
const element = name && target.elements[name]
|
|
8
|
-
const type = element &&
|
|
23
|
+
const type = element && element['@Core.MediaType']
|
|
9
24
|
|
|
10
|
-
if (col
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
25
|
+
if (col === '*') {
|
|
26
|
+
for (const k in target.elements) {
|
|
27
|
+
const el = target.elements[k]
|
|
28
|
+
if (el['@Core.MediaType']) {
|
|
29
|
+
const type = el['@Core.MediaType']
|
|
30
|
+
_addColumn(el.name, type, columns)
|
|
15
31
|
}
|
|
16
|
-
} else {
|
|
17
|
-
columns[index] = { val: type, as: `${name}@odata.mediaContentType` }
|
|
18
32
|
}
|
|
33
|
+
} else if (col.ref && type) {
|
|
34
|
+
_addColumn(name, type, columns)
|
|
35
|
+
if (!element['@Core.IsURL']) columns.splice(index, 1)
|
|
19
36
|
} else if (col.expand && col.ref) {
|
|
20
37
|
const tgt = target.elements[col.ref] && target.elements[col.ref].target
|
|
21
38
|
tgt && _changeStreamProperties(model.definitions[ensureUnlocalized(ensureNoDraftsSuffix(tgt))], col.expand, model)
|
|
@@ -23,10 +40,10 @@ const _changeStreamProperties = (target, columns, model) => {
|
|
|
23
40
|
}
|
|
24
41
|
}
|
|
25
42
|
|
|
26
|
-
const handleStreamProperties = (target, select, model) => {
|
|
27
|
-
const columns = select.SELECT
|
|
43
|
+
const handleStreamProperties = (target, select, model, _4odata) => {
|
|
44
|
+
const columns = select.SELECT?.columns
|
|
28
45
|
if (!columns || !target || !model) return
|
|
29
|
-
if (!select.SELECT._4odata) return
|
|
46
|
+
if (!_4odata && !select.SELECT._4odata) return
|
|
30
47
|
if (select._streaming) return
|
|
31
48
|
|
|
32
49
|
_changeStreamProperties(target, columns, model)
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
// before
|
|
2
2
|
const rewrite = require('./rewrite')
|
|
3
3
|
const input = require('./input')
|
|
4
|
-
const integrity = require('./integrity')
|
|
5
4
|
const { convertVirtuals: virtual } = require('./virtual')
|
|
6
5
|
// on
|
|
7
6
|
const CREATE = require('./create')
|
|
@@ -18,7 +17,6 @@ module.exports = {
|
|
|
18
17
|
rewrite,
|
|
19
18
|
virtual,
|
|
20
19
|
input,
|
|
21
|
-
integrity,
|
|
22
20
|
CREATE,
|
|
23
21
|
READ,
|
|
24
22
|
UPDATE,
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
const { getFlatArray, processCQNs } = require('../utils/deep')
|
|
2
|
-
const
|
|
2
|
+
const normalizeTimestamp = require('../../common/utils/normalizeTimestamp')
|
|
3
3
|
const { hasDeepDelete, getDeepDeleteCQNs, getSetNullParentForeignKeyCQNs } = require('../../common/composition')
|
|
4
4
|
|
|
5
5
|
const deleteFn = (executeDeleteCQN, executeUpdateCQN) => async (model, dbc, query, req) => {
|
|
6
6
|
const { user, locale, timestamp } = req
|
|
7
|
-
const isoTs =
|
|
7
|
+
const isoTs = normalizeTimestamp(timestamp)
|
|
8
8
|
|
|
9
9
|
let result
|
|
10
10
|
if (model && hasDeepDelete(model, query)) {
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
const { hasDeepInsert, getDeepInsertCQNs } = require('../../common/composition')
|
|
2
2
|
const { getFlatArray, processCQNs } = require('../utils/deep')
|
|
3
|
-
const
|
|
3
|
+
const normalizeTimestamp = require('../../common/utils/normalizeTimestamp')
|
|
4
4
|
|
|
5
5
|
const insert = executeInsertCQN => async (model, dbc, query, req) => {
|
|
6
6
|
const { user, locale, timestamp } = req
|
|
7
|
-
const isoTs =
|
|
7
|
+
const isoTs = normalizeTimestamp(timestamp)
|
|
8
8
|
|
|
9
9
|
if (model && hasDeepInsert(model, query)) {
|
|
10
10
|
const cqns = getFlatArray(getDeepInsertCQNs(model, query))
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
const
|
|
1
|
+
const normalizeTimestamp = require('../../common/utils/normalizeTimestamp')
|
|
2
2
|
const { deepCopyObject } = require('../../common/utils/copy')
|
|
3
3
|
const getError = require('../../common/error')
|
|
4
4
|
|
|
@@ -39,7 +39,7 @@ const countValue = countResults => {
|
|
|
39
39
|
|
|
40
40
|
const read = (executeSelectCQN, executeStreamCQN) => (model, dbc, query, req) => {
|
|
41
41
|
const { user, locale, timestamp } = req
|
|
42
|
-
const isoTs =
|
|
42
|
+
const isoTs = normalizeTimestamp(timestamp)
|
|
43
43
|
|
|
44
44
|
if (query._streaming) {
|
|
45
45
|
if (!query.SELECT || (query.SELECT && !query.SELECT.columns)) {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
const
|
|
1
|
+
const normalizeTimestamp = require('../../common/utils/normalizeTimestamp')
|
|
2
2
|
|
|
3
3
|
const run = (insert, read, update, deleet, cqn, sql) => (model, dbc, query, req, values) => {
|
|
4
4
|
if (typeof query === 'string') {
|
|
@@ -22,7 +22,7 @@ const run = (insert, read, update, deleet, cqn, sql) => (model, dbc, query, req,
|
|
|
22
22
|
}
|
|
23
23
|
|
|
24
24
|
const { user, locale, timestamp } = req
|
|
25
|
-
const isoTs =
|
|
25
|
+
const isoTs = normalizeTimestamp(timestamp)
|
|
26
26
|
|
|
27
27
|
return cqn(model, dbc, query, user, locale, isoTs)
|
|
28
28
|
}
|
|
@@ -2,7 +2,7 @@ const cds = require('../../cds')
|
|
|
2
2
|
|
|
3
3
|
const { hasDeepUpdate, getDeepUpdateCQNs, selectDeepUpdateData } = require('../../common/composition')
|
|
4
4
|
const { getFlatArray, processCQNs } = require('../utils/deep')
|
|
5
|
-
const
|
|
5
|
+
const normalizeTimestamp = require('../../common/utils/normalizeTimestamp')
|
|
6
6
|
|
|
7
7
|
const _includesCompositionTarget = (cqns, target) => {
|
|
8
8
|
return cqns.find(cqn => {
|
|
@@ -47,7 +47,7 @@ const _getFilteredCqns = (cqns, model) => {
|
|
|
47
47
|
|
|
48
48
|
const update = executeUpdateCQN => async (model, dbc, req) => {
|
|
49
49
|
const { query, user, locale, timestamp } = req
|
|
50
|
-
const isoTs =
|
|
50
|
+
const isoTs = normalizeTimestamp(timestamp)
|
|
51
51
|
|
|
52
52
|
if (model && hasDeepUpdate(model, query)) {
|
|
53
53
|
// REVISIT: _activeData gets set in case of draftActivate for performance, but this is a layer violation
|
|
@@ -35,12 +35,6 @@ class BaseBuilder {
|
|
|
35
35
|
this._quotingStyle = cds.env.sql.names || 'plain'
|
|
36
36
|
this._quoteElement = quotingStyles[this._quotingStyle]
|
|
37
37
|
this._validateQuotingStyle()
|
|
38
|
-
|
|
39
|
-
// NOTE: unofficial feature flag!
|
|
40
|
-
this._parameterizedNumbers =
|
|
41
|
-
'parameterized_numbers' in this._options
|
|
42
|
-
? this._options.parameterized_numbers
|
|
43
|
-
: cds.env && cds.env.features && cds.env.features.parameterized_numbers
|
|
44
38
|
}
|
|
45
39
|
|
|
46
40
|
getDefaultOptions() {
|
|
@@ -4,6 +4,7 @@ const BaseBuilder = require('./BaseBuilder')
|
|
|
4
4
|
const { flattenStructuredWhereHaving } = require('../../common/utils/structured')
|
|
5
5
|
|
|
6
6
|
const SQLITE_DATETIME_FUNCTIONS = new Set(['year', 'month', 'day', 'second', 'hour', 'minute'])
|
|
7
|
+
const HANA_DATETIME_FUNCTIONS = new Set(['year', 'month', 'dayofmonth', 'second', 'hour', 'minute'])
|
|
7
8
|
const OPERATORS = new Set(['=', '!=', '<>', '<', '>', '<=', '>='])
|
|
8
9
|
|
|
9
10
|
function _fillAfterDot(val) {
|
|
@@ -96,6 +97,11 @@ class ExpressionBuilder extends BaseBuilder {
|
|
|
96
97
|
if (_valButNoBuffer(op1) && comp === '=' && op2.ref) return true
|
|
97
98
|
}
|
|
98
99
|
|
|
100
|
+
_hasValToValComparision(op1, comp, op2) {
|
|
101
|
+
const _operations = ['=', '!=']
|
|
102
|
+
return typeof op1.val === 'number' && _operations.includes(comp) && typeof op2.val === 'number'
|
|
103
|
+
}
|
|
104
|
+
|
|
99
105
|
_expressionObjectsToSQL(objects) {
|
|
100
106
|
const length = objects.length
|
|
101
107
|
let i = 0
|
|
@@ -117,6 +123,14 @@ class ExpressionBuilder extends BaseBuilder {
|
|
|
117
123
|
this._expressionObjectsToSQL(flattenedStructExpression)
|
|
118
124
|
i += 3
|
|
119
125
|
continue
|
|
126
|
+
} else if (this._hasValToValComparision(objects[i], objects[i + 1], objects[i + 2])) {
|
|
127
|
+
// Avoid utilizing placeholders to represent numerical values being (not) equal to other numbers due to an issue related to HDB.
|
|
128
|
+
const _outputObj = this._outputObj
|
|
129
|
+
_outputObj.sql.push(objects[i].val)
|
|
130
|
+
_outputObj.sql.push(objects[i + 1])
|
|
131
|
+
_outputObj.sql.push(objects[i + 2].val)
|
|
132
|
+
i += 3
|
|
133
|
+
continue
|
|
120
134
|
}
|
|
121
135
|
|
|
122
136
|
this._expressionElementToSQL(objects[i])
|
|
@@ -147,17 +161,17 @@ class ExpressionBuilder extends BaseBuilder {
|
|
|
147
161
|
// sqlite requires leading 0 for numbers in datetime functions
|
|
148
162
|
const f = objects[i].func ? i : OPERATORS.has(objects[i + 1]) ? i + 2 : i - 2
|
|
149
163
|
const v = objects[i].val ? i : OPERATORS.has(objects[i + 1]) ? i + 2 : i - 2
|
|
150
|
-
if (
|
|
164
|
+
if (
|
|
165
|
+
objects[f] &&
|
|
166
|
+
cds.db &&
|
|
167
|
+
((SQLITE_DATETIME_FUNCTIONS.has(objects[f].func) && cds.db.kind === 'sqlite') ||
|
|
168
|
+
(HANA_DATETIME_FUNCTIONS.has(objects[f].func) && cds.db.kind === 'hana'))
|
|
169
|
+
) {
|
|
151
170
|
if (objects[v] && objects[v].val !== undefined && typeof objects[v].val === 'number') {
|
|
152
171
|
objects[v] = { val: `${objects[v].val < 10 ? 0 : ''}${objects[v].val}` }
|
|
153
172
|
if (objects[f].func === 'second') objects[v].val = _fillAfterDot(objects[v].val)
|
|
154
173
|
}
|
|
155
174
|
}
|
|
156
|
-
// odata indexof function returns the zero-based character position of the first occurrence
|
|
157
|
-
if (this._options._4odata && objects[i].func && objects[i].func === 'indexof') {
|
|
158
|
-
if (objects[i + 2] && objects[i + 2].val !== undefined) objects[i + 2].val++
|
|
159
|
-
else if (objects[i - 2] && objects[i - 2].val !== undefined) this._outputObj.sql[i - 2]++
|
|
160
|
-
}
|
|
161
175
|
return 0
|
|
162
176
|
}
|
|
163
177
|
|
|
@@ -343,12 +357,9 @@ class ExpressionBuilder extends BaseBuilder {
|
|
|
343
357
|
* @private
|
|
344
358
|
*/
|
|
345
359
|
_valOutputFromElement(element) {
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
this._outputObj.sql.push(this._options.placeholder)
|
|
350
|
-
this._outputObj.values.push(element.val)
|
|
351
|
-
}
|
|
360
|
+
const _outputObj = this._outputObj
|
|
361
|
+
_outputObj.sql.push(this._options.placeholder)
|
|
362
|
+
_outputObj.values.push(element.val)
|
|
352
363
|
}
|
|
353
364
|
|
|
354
365
|
_addToOutputObj({ sql, values }, addBrackets) {
|
|
@@ -69,6 +69,11 @@ class FunctionBuilder extends BaseBuilder {
|
|
|
69
69
|
const functionName = this._functionName()
|
|
70
70
|
const args = this._functionArgs()
|
|
71
71
|
|
|
72
|
+
if (this._obj.func === 'indexof') {
|
|
73
|
+
this._handleIndexof(args, functionName)
|
|
74
|
+
return
|
|
75
|
+
}
|
|
76
|
+
|
|
72
77
|
if (!args) {
|
|
73
78
|
// > arg-less func such as current_date
|
|
74
79
|
this._outputObj.sql.push(functionName)
|
|
@@ -114,6 +119,17 @@ class FunctionBuilder extends BaseBuilder {
|
|
|
114
119
|
this._handleLikewiseFunc(args)
|
|
115
120
|
}
|
|
116
121
|
|
|
122
|
+
_handleIndexof(args, funcName) {
|
|
123
|
+
this._outputObj.sql.push('(')
|
|
124
|
+
this._outputObj.sql.push(funcName, '(')
|
|
125
|
+
if (typeof args === 'string') this._outputObj.sql.push(args)
|
|
126
|
+
else this._addFunctionArgs(args)
|
|
127
|
+
this._outputObj.sql.push(')')
|
|
128
|
+
this._outputObj.sql.push('-')
|
|
129
|
+
this._outputObj.sql.push('1')
|
|
130
|
+
this._outputObj.sql.push(')')
|
|
131
|
+
}
|
|
132
|
+
|
|
117
133
|
_handleLikewiseFunc(args) {
|
|
118
134
|
const functionName = this._functionName()
|
|
119
135
|
const not = functionName.startsWith('not') ? 'NOT ' : ''
|
|
@@ -193,12 +209,8 @@ class FunctionBuilder extends BaseBuilder {
|
|
|
193
209
|
res.push(sql)
|
|
194
210
|
this._outputObj.values.push(...values)
|
|
195
211
|
} else if (Object.prototype.hasOwnProperty.call(arg, 'val')) {
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
} else {
|
|
199
|
-
res.push(this._options.placeholder)
|
|
200
|
-
this._outputObj.values.push(arg.val)
|
|
201
|
-
}
|
|
212
|
+
res.push(this._options.placeholder)
|
|
213
|
+
this._outputObj.values.push(arg.val)
|
|
202
214
|
} else if (arg.list) {
|
|
203
215
|
this._addFunctionArgs(arg.list, true)
|
|
204
216
|
// _addFunctionArgs adds the arguments list already to the output object
|
|
@@ -259,6 +259,7 @@ class InsertBuilder extends BaseBuilder {
|
|
|
259
259
|
_getValue(column, { entry, flattenColumn, insertAnnotatedColumns }) {
|
|
260
260
|
let val = entry
|
|
261
261
|
if (!flattenColumn && this.uuidKeys.includes(column)) {
|
|
262
|
+
if (this._UPSERT) return undefined
|
|
262
263
|
val = cds.utils.uuid()
|
|
263
264
|
} else {
|
|
264
265
|
for (const key of flattenColumn) {
|
|
@@ -413,7 +413,7 @@ class SelectBuilder extends BaseBuilder {
|
|
|
413
413
|
_addRows() {
|
|
414
414
|
if (this._obj.SELECT.limit) {
|
|
415
415
|
if (this._obj.SELECT.limit.rows !== undefined) {
|
|
416
|
-
// limit (no placeholder for statement caching)
|
|
416
|
+
// limit (no placeholder for statement caching) - hana does not know how to optimize
|
|
417
417
|
this._outputObj.sql.push('LIMIT', this._obj.SELECT.limit.rows.val)
|
|
418
418
|
} else {
|
|
419
419
|
// rows parameter is mandatory for SQL
|
|
@@ -429,12 +429,8 @@ class SelectBuilder extends BaseBuilder {
|
|
|
429
429
|
_addOffset() {
|
|
430
430
|
// offset
|
|
431
431
|
if (this._obj.SELECT.limit && this._obj.SELECT.limit.offset !== undefined) {
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
} else {
|
|
435
|
-
this._outputObj.sql.push('OFFSET', '?')
|
|
436
|
-
this._outputObj.values.push(this._obj.SELECT.limit.offset.val)
|
|
437
|
-
}
|
|
432
|
+
this._outputObj.sql.push('OFFSET', '?')
|
|
433
|
+
this._outputObj.values.push(this._obj.SELECT.limit.offset.val)
|
|
438
434
|
}
|
|
439
435
|
}
|
|
440
436
|
|