@sap/cds-dk 10.0.7 → 10.1.1
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 +106 -0
- package/bin/add.js +7 -23
- package/bin/bind.js +14 -0
- package/bin/build.js +21 -34
- package/bin/cds.js +1 -1
- package/bin/compile/index.js +16 -41
- package/bin/completion/completionFs.js +11 -8
- package/bin/completion/index.js +603 -80
- package/bin/completion/scripts/cds.fish +620 -0
- package/bin/completion/scripts/cds.ps1 +403 -0
- package/bin/completion/scripts/cds.sh +327 -0
- package/bin/completion/scripts/cds.zsh +296 -0
- package/bin/completion/templates/cds-tmpl.fish +65 -0
- package/bin/completion/templates/cds-tmpl.ps1 +217 -0
- package/bin/completion/templates/cds-tmpl.sh +93 -0
- package/bin/completion/templates/cds-tmpl.zsh +74 -0
- package/bin/deploy/index.js +11 -1
- package/bin/deploy/options.js +25 -0
- package/{lib → bin}/deploy/to-hana/hana.js +8 -8
- package/{lib → bin}/deploy/to-hana/hdiDeployUtil.js +1 -1
- package/bin/deploy/{to-hana.js → to-hana/index.js} +1 -1
- package/{lib → bin}/deploy/to-hana/mtaUtil.js +1 -1
- package/bin/deploy/to-sql.js +0 -2
- package/bin/env.js +4 -1
- package/bin/export.js +44 -6
- package/bin/help/export.md +3 -0
- package/bin/help.js +1 -5
- package/bin/import.js +9 -0
- package/bin/init.js +6 -24
- package/bin/inspect.js +8 -0
- package/bin/lint.js +5 -3
- package/bin/login.js +26 -20
- package/bin/minify.js +6 -1
- package/bin/pull.js +19 -6
- package/bin/push.js +19 -6
- package/bin/repl.js +11 -2
- package/bin/run.js +12 -1
- package/bin/serve.js +13 -2
- package/bin/up.js +17 -11
- package/{lib → bin}/upgrade/constants.js +1 -1
- package/bin/{upgrade.js → upgrade/index.js} +2 -2
- package/{lib → bin}/upgrade/preflight/consistency.js +1 -1
- package/{lib → bin}/upgrade/preflight/index.js +1 -1
- package/{lib → bin}/upgrade/steps/analyze/index.js +1 -1
- package/{lib → bin}/upgrade/steps/install.js +1 -1
- package/{lib → bin}/upgrade/steps/openrewrite.js +1 -1
- package/{lib → bin}/upgrade/steps/removals.js +1 -1
- package/{lib → bin}/upgrade/steps/rewrite.js +1 -1
- package/{lib → bin}/upgrade/steps/verify.js +1 -1
- package/{lib/upgrade/index.js → bin/upgrade/upgrade.js} +2 -2
- package/{lib → bin}/upgrade/util/pom.js +2 -2
- package/{lib → bin}/upgrade/util/prompt.js +1 -1
- package/bin/util/containerize.js +10 -4
- package/bin/version.js +10 -1
- package/{lib → bin}/watch/hot-reload.js +1 -1
- package/bin/{watch.js → watch/index.js} +24 -9
- package/{lib → bin}/watch/livereload-connect.js +1 -1
- package/{lib → bin}/watch/vite-watch.js +1 -1
- package/{lib → bin}/watch/watched.js +2 -2
- package/lib/build/constants.js +1 -1
- package/lib/build/plugins/fiori/index.js +31 -2
- package/lib/build/plugins/internal.js +4 -2
- package/lib/build/plugins/mtx-sidecar/index.js +7 -8
- package/lib/build/plugins/nodejs/index.js +64 -7
- package/lib/build/util.js +2 -4
- package/lib/compile/to_asyncapi.js +1 -1
- package/lib/env/schema.js +16 -0
- package/lib/env/schemas/cds-rc.js +1 -76
- package/lib/import/importapi.js +2 -3
- package/lib/import/message.js +0 -2
- package/lib/init/constants.js +2 -1
- package/lib/init/index.js +2 -2
- package/lib/init/merge.js +123 -280
- package/lib/init/plugin.js +3 -1
- package/lib/init/projectReader.js +22 -6
- package/lib/init/registries/xs-app.js +3 -5
- package/lib/init/template/ai/files/mta.yaml.hbs +1 -1
- package/lib/init/template/ai/index.js +14 -2
- package/lib/init/template/ams/index.js +2 -2
- package/lib/init/template/app-frontend/files/containerize.yaml.hbs +2 -2
- package/lib/init/template/app-frontend/index.js +4 -4
- package/lib/init/template/application-logging/index.js +2 -2
- package/lib/init/template/approuter/files/package.json +1 -1
- package/lib/init/template/approuter/index.js +8 -3
- package/lib/init/template/attachments/files/package.json.hbs +1 -1
- package/lib/init/template/attachments/index.js +2 -2
- package/lib/init/template/audit-logging/index.js +2 -2
- package/lib/init/template/cloud-logging/files/package.json.hbs +4 -4
- package/lib/init/template/cloud-logging/index.js +2 -2
- package/lib/init/template/completion/completionSetup.js +36 -39
- package/lib/init/template/connectivity/index.js +2 -2
- package/lib/init/template/data/as-csv.js +15 -5
- package/lib/init/template/data/as-json.js +117 -42
- package/lib/init/template/data/index.js +16 -2
- package/lib/init/template/destination/index.js +2 -2
- package/lib/init/template/dynatrace/files/package.json.hbs +3 -3
- package/lib/init/template/dynatrace/index.js +2 -2
- package/lib/init/template/enterprise-messaging/index.js +2 -2
- package/lib/init/template/enterprise-messaging-shared/index.js +2 -2
- package/lib/init/template/extensibility/files/mta.yaml.hbs +11 -0
- package/lib/init/template/extensibility/files/xs-security.json.hbs +1 -1
- package/lib/init/template/extensibility/index.js +3 -3
- package/lib/init/template/github-actions/index.js +2 -2
- package/lib/init/template/hana/index.js +3 -2
- package/lib/init/template/hana-serverless/files/db/package.json +14 -0
- package/lib/init/template/hana-serverless/files/identity.values.yaml.hbs +10 -0
- package/lib/init/template/hana-serverless/files/mta.yaml.hbs +64 -0
- package/lib/init/template/hana-serverless/files/package.json.hbs +35 -0
- package/lib/init/template/hana-serverless/files/src/.hdiconfig +139 -0
- package/lib/init/template/hana-serverless/files/undeploy.json +7 -0
- package/lib/init/template/hana-serverless/files/values.yaml.hbs +18 -0
- package/lib/init/template/hana-serverless/index.js +184 -0
- package/lib/init/template/html5-repo/files/containerize.yaml.hbs +2 -2
- package/lib/init/template/html5-repo/files/mta.yaml.hbs +5 -0
- package/lib/init/template/html5-repo/index.js +18 -14
- package/lib/init/template/http/index.js +123 -26
- package/lib/init/template/ias/index.js +2 -2
- package/lib/init/template/kafka/index.js +2 -2
- package/lib/init/template/kyma/index.js +7 -5
- package/lib/init/template/malware-scanner/index.js +2 -2
- package/lib/init/template/mta/files/mta.yaml.hbs +1 -1
- package/lib/init/template/multitenancy/index.js +18 -3
- package/lib/init/template/notifications/index.js +2 -2
- package/lib/init/template/portal/files/mta.yaml.hbs +4 -0
- package/lib/init/template/portal/index.js +13 -8
- package/lib/init/template/postgres/index.js +2 -2
- package/lib/init/template/redis/index.js +2 -2
- package/lib/init/template/sample/files/db/currencies.cds +2 -0
- package/lib/init/template/sample/files/db/data/sap.capire.bookshop-Authors.csv +1 -1
- package/lib/init/template/tiny-sample/files/nodejs/db/data/CatalogService.Books.csv +2 -2
- package/lib/init/template/workzone-standard/index.js +5 -5
- package/lib/init/template/xsuaa/index.js +20 -2
- package/lib/lint/eslintHelp.json +1 -1
- package/lib/mtx/auth_manager.js +103 -37
- package/lib/mtx/params.js +3 -2
- package/lib/mtx/settings_manager.js +45 -28
- package/lib/mtx/tenant_action.js +0 -1
- package/lib/mtx/util/credentials.js +62 -0
- package/node_modules/@cap-js/db-service/CHANGELOG.md +17 -0
- package/node_modules/@cap-js/db-service/lib/common/generic-pool.js +4 -7
- package/node_modules/@cap-js/db-service/lib/cqn2pql.js +1 -1
- package/node_modules/@cap-js/db-service/lib/cqn2sql.js +15 -11
- package/node_modules/@cap-js/db-service/lib/cqn4sql.js +119 -19
- package/node_modules/@cap-js/db-service/lib/infer/index.js +27 -16
- package/node_modules/@cap-js/db-service/lib/recursion-guard.js +40 -0
- package/node_modules/@cap-js/db-service/lib/search.js +1 -1
- package/node_modules/@cap-js/db-service/package.json +1 -1
- package/node_modules/@cap-js/sqlite/CHANGELOG.md +19 -0
- package/node_modules/@cap-js/sqlite/lib/SQLiteService.js +110 -14
- package/node_modules/@cap-js/sqlite/package.json +3 -3
- package/node_modules/@sap/cds/CHANGELOG.md +57 -0
- package/node_modules/@sap/cds/app/index.js +5 -4
- package/node_modules/@sap/cds/eslint.config.mjs +1 -0
- package/node_modules/@sap/cds/lib/compile/minify.js +14 -10
- package/node_modules/@sap/cds/lib/env/cds-env.js +1 -1
- package/node_modules/@sap/cds/lib/env/cds-requires.js +22 -11
- package/node_modules/@sap/cds/lib/env/defaults.js +4 -1
- package/node_modules/@sap/cds/lib/env/schemas/cds-rc.js +5 -2
- package/node_modules/@sap/cds/lib/plugins.js +9 -5
- package/node_modules/@sap/cds/lib/req/spawn.js +1 -1
- package/node_modules/@sap/cds/lib/srv/srv-dispatch.js +1 -1
- package/node_modules/@sap/cds/lib/srv/srv-models.js +1 -1
- package/node_modules/@sap/cds/lib/utils/cds-utils.js +2 -1
- package/node_modules/@sap/cds/libx/_runtime/common/generic/assert.js +2 -3
- package/node_modules/@sap/cds/libx/_runtime/common/generic/auth/restrict.js +8 -1
- package/node_modules/@sap/cds/libx/_runtime/common/generic/crud.js +18 -8
- package/node_modules/@sap/cds/libx/_runtime/fiori/lean-draft.js +25 -17
- package/node_modules/@sap/cds/libx/_runtime/messaging/enterprise-messaging-utils/registerEndpoints.js +9 -3
- package/node_modules/@sap/cds/libx/_runtime/remote/Service.js +4 -19
- package/node_modules/@sap/cds/libx/_runtime/remote/utils/LruCache.js +42 -0
- package/node_modules/@sap/cds/libx/_runtime/remote/utils/client.js +14 -4
- package/node_modules/@sap/cds/libx/_runtime/remote/utils/cloudSdkProvider.js +2 -9
- package/node_modules/@sap/cds/libx/_runtime/remote/utils/destinationClient.js +150 -0
- package/node_modules/@sap/cds/libx/_runtime/remote/utils/error.js +13 -0
- package/node_modules/@sap/cds/libx/_runtime/remote/utils/fetchClient.js +26 -18
- package/node_modules/@sap/cds/libx/_runtime/remote/utils/query.js +34 -23
- package/node_modules/@sap/cds/libx/_runtime/remote/utils/token.js +82 -0
- package/node_modules/@sap/cds/libx/_runtime/ucl/Service.js +36 -189
- package/node_modules/@sap/cds/libx/common/utils/streaming.js +16 -16
- package/node_modules/@sap/cds/libx/http/location.js +3 -3
- package/node_modules/@sap/cds/libx/odata/middleware/batch.js +16 -10
- package/node_modules/@sap/cds/libx/odata/middleware/metadata.js +9 -6
- package/node_modules/@sap/cds/libx/odata/middleware/service-document.js +1 -1
- package/node_modules/@sap/cds/libx/odata/parse/afterburner.js +19 -3
- package/node_modules/@sap/cds/libx/odata/parse/cqn2odata.js +83 -86
- package/node_modules/@sap/cds/libx/queue/SchedulingService.js +17 -2
- package/node_modules/@sap/cds/libx/queue/processing.js +1 -3
- package/node_modules/@sap/cds/libx/rest/RestAdapter.js +3 -7
- package/node_modules/@sap/cds/libx/rest/middleware/delete.js +3 -3
- package/node_modules/@sap/cds/libx/rest/middleware/parse.js +13 -8
- package/node_modules/@sap/cds/libx/rest/middleware/read.js +3 -3
- package/node_modules/@sap/cds/libx/rest/middleware/upsert.js +2 -2
- package/node_modules/@sap/cds/package.json +1 -1
- package/node_modules/@sap/cds-compiler/CHANGELOG.md +75 -8
- package/node_modules/@sap/cds-compiler/bin/cdsc.js +2 -2
- package/node_modules/@sap/cds-compiler/doc/IncompatibleChanges_v7.md +46 -19
- package/node_modules/@sap/cds-compiler/lib/api/main.js +10 -7
- package/node_modules/@sap/cds-compiler/lib/api/options.js +8 -2
- package/node_modules/@sap/cds-compiler/lib/base/csnRefs.js +5 -2
- package/node_modules/@sap/cds-compiler/lib/base/message-registry.js +4 -0
- package/node_modules/@sap/cds-compiler/lib/base/optionProcessor.js +12 -0
- package/node_modules/@sap/cds-compiler/lib/checks/enricher.js +1 -0
- package/node_modules/@sap/cds-compiler/lib/compiler/assert-consistency.js +8 -7
- package/node_modules/@sap/cds-compiler/lib/compiler/checks.js +3 -3
- package/node_modules/@sap/cds-compiler/lib/compiler/define.js +301 -75
- package/node_modules/@sap/cds-compiler/lib/compiler/dictionaries.js +2 -0
- package/node_modules/@sap/cds-compiler/lib/compiler/extend.js +40 -26
- package/node_modules/@sap/cds-compiler/lib/compiler/populate.js +45 -60
- package/node_modules/@sap/cds-compiler/lib/compiler/propagator.js +16 -7
- package/node_modules/@sap/cds-compiler/lib/compiler/resolve.js +48 -18
- package/node_modules/@sap/cds-compiler/lib/compiler/shared.js +7 -6
- package/node_modules/@sap/cds-compiler/lib/compiler/tweak-assocs.js +62 -50
- package/node_modules/@sap/cds-compiler/lib/compiler/utils.js +29 -14
- package/node_modules/@sap/cds-compiler/lib/edm/edmAnnoPreprocessor.js +6 -5
- package/node_modules/@sap/cds-compiler/lib/gen/CdlGrammar.checksum +1 -1
- package/node_modules/@sap/cds-compiler/lib/gen/CdlParser.js +1119 -1109
- package/node_modules/@sap/cds-compiler/lib/gen/Dictionary.json +19 -3
- package/node_modules/@sap/cds-compiler/lib/json/from-csn.js +10 -16
- package/node_modules/@sap/cds-compiler/lib/json/to-csn.js +58 -39
- package/node_modules/@sap/cds-compiler/lib/main.d.ts +8 -0
- package/node_modules/@sap/cds-compiler/lib/main.js +1 -0
- package/node_modules/@sap/cds-compiler/lib/model/csnUtils.js +4 -0
- package/node_modules/@sap/cds-compiler/lib/parsers/AstBuildingParser.js +2 -1
- package/node_modules/@sap/cds-compiler/lib/parsers/XprTree.js +8 -0
- package/node_modules/@sap/cds-compiler/lib/render/toCdl.js +22 -1
- package/node_modules/@sap/cds-compiler/lib/render/toSql.js +17 -2
- package/node_modules/@sap/cds-compiler/lib/render/utils/common.js +6 -1
- package/node_modules/@sap/cds-compiler/lib/render/utils/operators.js +27 -0
- package/node_modules/@sap/cds-compiler/lib/render/utils/standardDatabaseFunctions.js +125 -3
- package/node_modules/@sap/cds-compiler/lib/tool-lib/revealInternalProperties.js +2 -1
- package/node_modules/@sap/cds-compiler/lib/transform/db/applyTransformations.js +15 -0
- package/node_modules/@sap/cds-compiler/lib/transform/db/associations.js +10 -2
- package/node_modules/@sap/cds-compiler/lib/transform/db/backlinks.js +22 -6
- package/node_modules/@sap/cds-compiler/lib/transform/db/constraints.js +62 -1
- package/node_modules/@sap/cds-compiler/lib/transform/db/flattening.js +4 -3
- package/node_modules/@sap/cds-compiler/lib/transform/effective/annotations.js +40 -0
- package/node_modules/@sap/cds-compiler/lib/transform/effective/flattening.js +17 -1
- package/node_modules/@sap/cds-compiler/lib/transform/effective/main.js +20 -0
- package/node_modules/@sap/cds-compiler/lib/transform/fioriTreeViews.js +12 -1
- package/node_modules/@sap/cds-compiler/lib/transform/forRelationalDB.js +7 -2
- package/node_modules/@sap/cds-compiler/lib/transform/odata/adaptAnnotationRefs.js +11 -5
- package/node_modules/@sap/cds-compiler/lib/transform/translateAssocsToJoins.js +48 -10
- package/node_modules/@sap/cds-compiler/package.json +1 -1
- package/node_modules/@sap/cds-mtxs/CHANGELOG.md +49 -0
- package/node_modules/@sap/cds-mtxs/bin/cds-mtx-migrate.js +26 -5
- package/node_modules/@sap/cds-mtxs/bin/cds-mtx.js +37 -16
- package/node_modules/@sap/cds-mtxs/lib/utils.js +23 -7
- package/node_modules/@sap/cds-mtxs/package.json +1 -1
- package/node_modules/@sap/cds-mtxs/srv/cf/abstract-provisioning-service.js +63 -8
- package/node_modules/@sap/cds-mtxs/srv/cf/saas-provisioning-service.js +1 -1
- package/node_modules/@sap/cds-mtxs/srv/cf/sms-provisioning-service.js +23 -3
- package/node_modules/@sap/cds-mtxs/srv/extensibility/compat/update.js +15 -0
- package/node_modules/@sap/cds-mtxs/srv/extensibility/linter/annotations.js +2 -2
- package/node_modules/@sap/cds-mtxs/srv/extensibility/linter/config.js +2 -0
- package/node_modules/@sap/cds-mtxs/srv/extensibility/linter/index.js +2 -0
- package/node_modules/@sap/cds-mtxs/srv/extensibility/linter/protected-namespace.js +45 -0
- package/node_modules/@sap/cds-mtxs/srv/extensibility-service.js +30 -16
- package/node_modules/@sap/cds-mtxs/srv/jobs-service.js +49 -10
- package/node_modules/@sap/cds-mtxs/srv/model-provider.js +28 -4
- package/node_modules/@sap/cds-mtxs/srv/plugins/common.js +19 -12
- package/node_modules/@sap/cds-mtxs/srv/plugins/hana/ctnr-mgr-base.js +1 -1
- package/node_modules/@sap/cds-mtxs/srv/plugins/hana/hdi-ctnr-mgr.js +6 -3
- package/node_modules/@sap/cds-mtxs/srv/plugins/hana/serverless.js +271 -0
- package/node_modules/@sap/cds-mtxs/srv/plugins/hana.js +7 -10
- package/node_modules/@sap/cds-mtxs/srv/worker/job-worker-pool.js +212 -0
- package/node_modules/@sap/cds-mtxs/srv/worker/job-worker.js +61 -0
- package/node_modules/@sap/xsenv/CHANGELOG.md +7 -0
- package/node_modules/@sap/xsenv/package.json +10 -7
- package/node_modules/body-parser/node_modules/content-type/README.md +2 -0
- package/node_modules/body-parser/node_modules/content-type/dist/index.d.ts +20 -0
- package/node_modules/body-parser/node_modules/content-type/dist/index.js +20 -14
- package/node_modules/body-parser/node_modules/content-type/dist/index.js.map +1 -1
- package/node_modules/body-parser/node_modules/content-type/package.json +1 -1
- package/node_modules/hdb/cfg/VERSION +1 -1
- package/node_modules/hdb/lib/protocol/ClientInfo.js +16 -3
- package/node_modules/hdb/lib/protocol/Connection.js +1 -0
- package/node_modules/hdb/package.json +3 -2
- package/node_modules/negotiator/lib/accept.js +54 -0
- package/node_modules/negotiator/lib/charset.js +10 -34
- package/node_modules/negotiator/lib/encoding.js +9 -30
- package/node_modules/negotiator/lib/language.js +15 -36
- package/node_modules/negotiator/lib/mediaType.js +16 -134
- package/node_modules/negotiator/node_modules/content-type/LICENSE +22 -0
- package/node_modules/negotiator/node_modules/content-type/README.md +71 -0
- package/node_modules/negotiator/node_modules/content-type/dist/index.d.ts +46 -0
- package/node_modules/negotiator/node_modules/content-type/dist/index.js +176 -0
- package/node_modules/negotiator/node_modules/content-type/dist/index.js.map +1 -0
- package/node_modules/negotiator/node_modules/content-type/package.json +52 -0
- package/node_modules/negotiator/package.json +16 -10
- package/node_modules/qs/.github/THREAT_MODEL.md +3 -3
- package/node_modules/qs/CHANGELOG.md +15 -0
- package/node_modules/qs/README.md +19 -1
- package/node_modules/qs/dist/qs.js +21 -21
- package/node_modules/qs/eslint.config.mjs +1 -1
- package/node_modules/qs/lib/parse.js +3 -4
- package/node_modules/qs/lib/stringify.js +23 -8
- package/node_modules/qs/lib/utils.js +10 -3
- package/node_modules/qs/package.json +4 -4
- package/node_modules/qs/test/parse.js +192 -3
- package/node_modules/qs/test/stringify.js +317 -0
- package/node_modules/qs/test/utils.js +12 -0
- package/node_modules/type-is/node_modules/content-type/README.md +2 -0
- package/node_modules/type-is/node_modules/content-type/dist/index.d.ts +20 -0
- package/node_modules/type-is/node_modules/content-type/dist/index.js +20 -14
- package/node_modules/type-is/node_modules/content-type/dist/index.js.map +1 -1
- package/node_modules/type-is/node_modules/content-type/package.json +1 -1
- package/node_modules/ws/lib/permessage-deflate.js +3 -1
- package/node_modules/ws/package.json +1 -1
- package/package.json +2 -2
- package/bin/completion/scripts/cds-bash.sh +0 -20
- package/bin/completion/scripts/cds-fish.fish +0 -13
- package/bin/completion/scripts/cds-ps.ps1 +0 -19
- package/bin/completion/scripts/cds-zsh.sh +0 -13
- package/lib/deploy/index.js +0 -25
- package/lib/import/asyncapi/importAsyncAPI.js +0 -33
- package/lib/import/asyncapi/index.js +0 -18
- package/lib/import/asyncapi/parserContext.js +0 -454
- package/lib/import/asyncapi/utils.js +0 -49
- package/node_modules/@sap/cds/libx/_runtime/ucl/queries.js +0 -61
- package/node_modules/negotiator/HISTORY.md +0 -114
- /package/{lib → bin}/upgrade/context.js +0 -0
- /package/{lib → bin}/upgrade/migrations/cds10/migrations.json +0 -0
- /package/{lib → bin}/upgrade/preflight/cds-version.js +0 -0
- /package/{lib → bin}/upgrade/preflight/project-type.js +0 -0
- /package/{lib → bin}/upgrade/steps/analyze/classification.js +0 -0
- /package/{lib → bin}/upgrade/steps/analyze/detector.js +0 -0
- /package/{lib → bin}/upgrade/steps/analyze/evaluator.js +0 -0
- /package/{lib → bin}/upgrade/steps/analyze/reporter.js +0 -0
- /package/{lib → bin}/upgrade/steps/analyze/setup.js +0 -0
- /package/{lib → bin}/upgrade/steps/analyze/utils/detection.js +0 -0
- /package/{lib → bin}/upgrade/steps/analyze/utils/files.js +0 -0
- /package/{lib → bin}/upgrade/steps/analyze/utils/grep.js +0 -0
- /package/{lib → bin}/upgrade/steps/analyze/utils/yaml.js +0 -0
- /package/{lib → bin}/upgrade/util/workspaces.js +0 -0
- /package/{lib → bin}/watch/esm-resolve-registration.js +0 -0
- /package/{lib → bin}/watch/livereload-server.js +0 -0
- /package/{lib → bin}/watch/node-watch.js +0 -0
- /package/{lib → bin}/watch/open.js +0 -0
- /package/{lib → bin}/watch/watcher.js +0 -0
- /package/node_modules/@sap/cds-mtxs/srv/worker/{pool.js → model-worker-pool.js} +0 -0
- /package/node_modules/@sap/cds-mtxs/srv/worker/{load.js → model-worker.js} +0 -0
|
@@ -33,6 +33,9 @@ const notSupportedOps = [['>'], ['<'], ['>='], ['<='], ['*'], ['+'], ['-'], ['/'
|
|
|
33
33
|
|
|
34
34
|
const allOps = eqOps.concat(eqOps).concat(notEqOps).concat(notSupportedOps)
|
|
35
35
|
|
|
36
|
+
|
|
37
|
+
const { depthGuarded, guardEntry } = require('./recursion-guard')
|
|
38
|
+
|
|
36
39
|
const { pseudos } = require('./infer/pseudos')
|
|
37
40
|
/**
|
|
38
41
|
* Transforms a CDL style query into SQL-Like CQN:
|
|
@@ -55,7 +58,17 @@ const { pseudos } = require('./infer/pseudos')
|
|
|
55
58
|
* @returns {object} transformedQuery the transformed query
|
|
56
59
|
*/
|
|
57
60
|
function cqn4sql(originalQuery, model, useTechnicalAlias = true) {
|
|
61
|
+
// guardEntry bounds subquery nesting and resets the depth budget per top-level call
|
|
62
|
+
return guardEntry(_cqn4sql)(originalQuery, model, useTechnicalAlias)
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function _cqn4sql(originalQuery, model, useTechnicalAlias = true) {
|
|
58
66
|
const getImplicitAlias = str => _getImplicitAlias(str, useTechnicalAlias)
|
|
67
|
+
// guard xpr and inline/expand-on-struct nesting
|
|
68
|
+
// eslint-disable-next-line no-func-assign
|
|
69
|
+
getTransformedTokenStream = depthGuarded(getTransformedTokenStream)
|
|
70
|
+
// eslint-disable-next-line no-func-assign
|
|
71
|
+
nestedProjectionOnStructure = depthGuarded(nestedProjectionOnStructure)
|
|
59
72
|
let inferred = typeof originalQuery === 'string' ? cds.parse.cql(originalQuery) : cds.ql.clone(originalQuery)
|
|
60
73
|
const hasCustomJoins =
|
|
61
74
|
originalQuery.SELECT?.from.args && (!originalQuery.joinTree || originalQuery.joinTree.isInitial)
|
|
@@ -69,6 +82,9 @@ function cqn4sql(originalQuery, model, useTechnicalAlias = true) {
|
|
|
69
82
|
const { where, having } = transformSearch(searchTerm)
|
|
70
83
|
if (where) inferred.SELECT.where = where
|
|
71
84
|
else if (having) inferred.SELECT.having = having
|
|
85
|
+
// Defer the ranking ORDER BY to after infer(), where the outer table alias is known and the
|
|
86
|
+
// deep-search sub-select can be correlated to the outer row (see buildSearchRankOrderBy).
|
|
87
|
+
defineProperty(inferred, '$searchRank', searchTerm)
|
|
72
88
|
}
|
|
73
89
|
}
|
|
74
90
|
// query modifiers can also be defined in from ref leaf infix filter
|
|
@@ -182,7 +198,7 @@ function cqn4sql(originalQuery, model, useTechnicalAlias = true) {
|
|
|
182
198
|
/**
|
|
183
199
|
* If the target entity is annotated with persistence skip and has an underlying db entity,
|
|
184
200
|
* we treat it as a runtime view and transform it into a CTE.
|
|
185
|
-
*
|
|
201
|
+
*
|
|
186
202
|
* @param {object} transformedQuery - The query object to be transformed.
|
|
187
203
|
* @param {string} model - The data model used for inference and transformation.
|
|
188
204
|
*/
|
|
@@ -198,10 +214,10 @@ function cqn4sql(originalQuery, model, useTechnicalAlias = true) {
|
|
|
198
214
|
}
|
|
199
215
|
|
|
200
216
|
/**
|
|
201
|
-
* Recursively call cqn4sql for all nested runtime views to calculate cte and
|
|
217
|
+
* Recursively call cqn4sql for all nested runtime views to calculate cte and
|
|
202
218
|
* add it as a with clause to the transformed query.
|
|
203
219
|
* Alias the runtime view with a unique alias and update all references to the runtime view to point to the alias.
|
|
204
|
-
*
|
|
220
|
+
*
|
|
205
221
|
* @param {object} rootDefinition - The root definition of the query. This is used to recursively process nested runtime views.
|
|
206
222
|
* @param {object} transformedQuery - The query object to be transformed.
|
|
207
223
|
* @param {string} model - The data model used for infer and cqn4sql.
|
|
@@ -317,9 +333,29 @@ function cqn4sql(originalQuery, model, useTechnicalAlias = true) {
|
|
|
317
333
|
|
|
318
334
|
// Since all the expressions in the SELECT part of the query have been computed,
|
|
319
335
|
// one can reference aliases of the queries columns in the orderBy clause.
|
|
320
|
-
|
|
321
|
-
|
|
336
|
+
let effectiveOrderBy = orderBy
|
|
337
|
+
// Rank by $search relevance — only when the score exists (HANA fuzzy, not opted out); else
|
|
338
|
+
// it would sort by a constant boolean.
|
|
339
|
+
const ranksSearch =
|
|
340
|
+
cds.db?.kind === 'hana' && cds.env.hana?.fuzzy !== false && cds.env.hana?.fuzzy?.ranked_search !== false
|
|
341
|
+
// count queries do not need ranked search
|
|
342
|
+
const isCountQuery = columns?.length === 1 && columns[0].func === 'count'
|
|
343
|
+
const searchRank = ranksSearch && !isCountQuery && inferred.$searchRank && buildSearchRankOrderBy(inferred.$searchRank)
|
|
344
|
+
if (searchRank) {
|
|
345
|
+
// precedence: user ordering, then rank, then the runtime's implicit key ordering
|
|
346
|
+
const implicitAt = (orderBy || []).findIndex(o => o.implicit)
|
|
347
|
+
const at = implicitAt === -1 ? (orderBy?.length ?? 0) : implicitAt
|
|
348
|
+
effectiveOrderBy = [...(orderBy || [])]
|
|
349
|
+
effectiveOrderBy.splice(at, 0, searchRank)
|
|
350
|
+
}
|
|
351
|
+
if (effectiveOrderBy) {
|
|
352
|
+
const transformedOrderBy = getTransformedOrderByGroupBy(effectiveOrderBy, true)
|
|
322
353
|
if (transformedOrderBy.length) {
|
|
354
|
+
// the rank is the only order-by entry that is a correlated sub-select
|
|
355
|
+
if (searchRank?.$searchRank) {
|
|
356
|
+
const rank = transformedOrderBy.find(o => o.SELECT)
|
|
357
|
+
if (rank) correlateSearchRank(rank, transformedFrom.as)
|
|
358
|
+
}
|
|
323
359
|
transformedQuery.SELECT.orderBy = transformedOrderBy
|
|
324
360
|
}
|
|
325
361
|
}
|
|
@@ -383,6 +419,8 @@ function cqn4sql(originalQuery, model, useTechnicalAlias = true) {
|
|
|
383
419
|
*/
|
|
384
420
|
function translateAssocsToJoins() {
|
|
385
421
|
let from
|
|
422
|
+
// guard association-path length
|
|
423
|
+
const joinForBranch = depthGuarded(_joinForBranch)
|
|
386
424
|
/**
|
|
387
425
|
* remember already seen aliases, do not create a join for them again
|
|
388
426
|
*/
|
|
@@ -406,7 +444,7 @@ function cqn4sql(originalQuery, model, useTechnicalAlias = true) {
|
|
|
406
444
|
})
|
|
407
445
|
return from.args.length > 1 ? from : from.args[0]
|
|
408
446
|
|
|
409
|
-
function
|
|
447
|
+
function _joinForBranch(lhs, node) {
|
|
410
448
|
const nextAssoc = inferred.joinTree.findNextAssoc(node)
|
|
411
449
|
if (!nextAssoc || alreadySeen.has(nextAssoc.$refLink.alias)) return lhs.args.length > 1 ? lhs : lhs.args[0]
|
|
412
450
|
|
|
@@ -643,7 +681,7 @@ function cqn4sql(originalQuery, model, useTechnicalAlias = true) {
|
|
|
643
681
|
if (col.cast) ret.cast = resolveEnumCastType(col.cast)
|
|
644
682
|
return ret
|
|
645
683
|
}
|
|
646
|
-
return
|
|
684
|
+
return { ...col }
|
|
647
685
|
}
|
|
648
686
|
|
|
649
687
|
function handleEmptyColumns(columns) {
|
|
@@ -995,11 +1033,11 @@ function cqn4sql(originalQuery, model, useTechnicalAlias = true) {
|
|
|
995
1033
|
const keyName = k.as || k.ref.join('_')
|
|
996
1034
|
const fkName = `${elemName}_${keyName}` // e.g., 'head_id'
|
|
997
1035
|
const fkFullName = `${columnAlias}_${fkName}` // e.g., 'department_head_id'
|
|
998
|
-
|
|
1036
|
+
|
|
999
1037
|
// Check if this FK is excluded
|
|
1000
1038
|
if (exclude.some(e => (e.ref?.at(-1) || e.as || e) === fkName)) continue
|
|
1001
1039
|
if (exclude.some(e => (e.ref?.at(-1) || e.as || e) === fkFullName)) continue
|
|
1002
|
-
|
|
1040
|
+
|
|
1003
1041
|
const flatColumn = {
|
|
1004
1042
|
ref: [joinAlias, fkName],
|
|
1005
1043
|
as: fkFullName,
|
|
@@ -1017,7 +1055,7 @@ function cqn4sql(originalQuery, model, useTechnicalAlias = true) {
|
|
|
1017
1055
|
calcElement.as = fullName
|
|
1018
1056
|
}
|
|
1019
1057
|
res.push(calcElement)
|
|
1020
|
-
}
|
|
1058
|
+
}
|
|
1021
1059
|
else {
|
|
1022
1060
|
// Scalar element
|
|
1023
1061
|
const flatColumn = {
|
|
@@ -1322,7 +1360,7 @@ function cqn4sql(originalQuery, model, useTechnicalAlias = true) {
|
|
|
1322
1360
|
else if (col.xpr) transformedColumn = { xpr: getTransformedTokenStream(col.xpr) }
|
|
1323
1361
|
else if (col.func) transformedColumn = { args: getTransformedFunctionArgs(col.args), func: col.func }
|
|
1324
1362
|
// val
|
|
1325
|
-
else transformedColumn =
|
|
1363
|
+
else transformedColumn = { ...col }
|
|
1326
1364
|
if (col.sort) transformedColumn.sort = col.sort
|
|
1327
1365
|
if (col.nulls) transformedColumn.nulls = col.nulls
|
|
1328
1366
|
res.push(transformedColumn)
|
|
@@ -1397,8 +1435,13 @@ function cqn4sql(originalQuery, model, useTechnicalAlias = true) {
|
|
|
1397
1435
|
if (!exclude.includes(k)) {
|
|
1398
1436
|
const { index, tableAlias } = inferred.$combinedElements[k][0]
|
|
1399
1437
|
const element = tableAlias.elements[k]
|
|
1400
|
-
// ignore FK for odata csn (but not for subquery sources where FK is not a separate element) / ignore blobs from wildcard expansion
|
|
1401
|
-
if (
|
|
1438
|
+
// ignore FK for odata csn (but not for subquery sources where FK is not a separate element) / ignore blobs and vectors from wildcard expansion
|
|
1439
|
+
if (
|
|
1440
|
+
(!tableAlias.SELECT && isManagedAssocInFlatMode(element)) ||
|
|
1441
|
+
element.type === 'cds.LargeBinary' ||
|
|
1442
|
+
element.type === 'cds.Vector'
|
|
1443
|
+
)
|
|
1444
|
+
continue
|
|
1402
1445
|
// for wildcard on subquery in from, just reference the elements
|
|
1403
1446
|
if (tableAlias.SELECT && !element.elements && !element.target) {
|
|
1404
1447
|
wildcardColumns.push(index ? { ref: [index, k] } : { ref: [k] })
|
|
@@ -1461,7 +1504,7 @@ function cqn4sql(originalQuery, model, useTechnicalAlias = true) {
|
|
|
1461
1504
|
* - `tableAlias` — table alias prepended to the column ref.
|
|
1462
1505
|
* @param {string[]} [csnPath=[]] - Accumulated CSN element path (used for `_csnPath` metadata on leaf columns).
|
|
1463
1506
|
* @param {{ exclude?: Array, replace?: Array }} [excludeAndReplace] - Columns to exclude or replace during wildcard expansion.
|
|
1464
|
-
* @param {boolean} [isWildcard=false] - Whether this expansion originates from a wildcard; filters out LargeBinary.
|
|
1507
|
+
* @param {boolean} [isWildcard=false] - Whether this expansion originates from a wildcard; filters out LargeBinary and Vector.
|
|
1465
1508
|
* @returns {object[]} Flat column(s) for the given element.
|
|
1466
1509
|
*/
|
|
1467
1510
|
function getFlatColumnsFor(column, names, csnPath = [], excludeAndReplace, isWildcard = false) {
|
|
@@ -1474,7 +1517,7 @@ function cqn4sql(originalQuery, model, useTechnicalAlias = true) {
|
|
|
1474
1517
|
const { $refLinks, flatName, isJoinRelevant } = column
|
|
1475
1518
|
let firstNonJoinRelevantAssoc, stepAfterAssoc
|
|
1476
1519
|
let element = $refLinks ? $refLinks[$refLinks.length - 1].definition : column
|
|
1477
|
-
if (isWildcard && element.type === 'cds.LargeBinary') return []
|
|
1520
|
+
if (isWildcard && (element.type === 'cds.LargeBinary' || element.type === 'cds.Vector')) return []
|
|
1478
1521
|
if (element.on && !element.keys) return [] // unmanaged doesn't make it into columns
|
|
1479
1522
|
if (element.virtual === true) return []
|
|
1480
1523
|
|
|
@@ -1812,8 +1855,7 @@ function cqn4sql(originalQuery, model, useTechnicalAlias = true) {
|
|
|
1812
1855
|
// reject virtual elements in expressions as they will lead to a sql error down the line
|
|
1813
1856
|
if (lhsDef?.virtual) throw new Error(`Virtual elements are not allowed in expressions`)
|
|
1814
1857
|
|
|
1815
|
-
let result = is_regexp(token?.val) ? token :
|
|
1816
|
-
// REVISIT: required because we copy the token above and lose the not enumerable "param: false"
|
|
1858
|
+
let result = typeof token !== 'object' || is_regexp(token?.val) ? token : { ...token }
|
|
1817
1859
|
if (typeof token === 'object' && 'val' in token && 'param' in token) Object.defineProperty(result, 'param', { value: token.param })
|
|
1818
1860
|
if (token.ref) {
|
|
1819
1861
|
const { definition } = token.$refLinks.at(-1)
|
|
@@ -1830,7 +1872,6 @@ function cqn4sql(originalQuery, model, useTechnicalAlias = true) {
|
|
|
1830
1872
|
const stepToFind = token.ref[1]?.id || token.ref[1]
|
|
1831
1873
|
const outerAlias = outerQuery.$combinedElements?.[stepToFind]?.[0].index
|
|
1832
1874
|
if (outerAlias) {
|
|
1833
|
-
let result = copy(token)
|
|
1834
1875
|
result.ref = [outerAlias, token.flatName]
|
|
1835
1876
|
transformedTokenStream.push(result)
|
|
1836
1877
|
continue
|
|
@@ -2016,7 +2057,7 @@ function cqn4sql(originalQuery, model, useTechnicalAlias = true) {
|
|
|
2016
2057
|
*/
|
|
2017
2058
|
function getTransformedFrom(from, existingWhere = []) {
|
|
2018
2059
|
const transformedWhere = []
|
|
2019
|
-
let transformedFrom =
|
|
2060
|
+
let transformedFrom = { ...from }
|
|
2020
2061
|
if (from.$refLinks) defineProperty(transformedFrom, '$refLinks', [...from.$refLinks])
|
|
2021
2062
|
if (from.args) {
|
|
2022
2063
|
transformedFrom.args = []
|
|
@@ -2579,6 +2620,65 @@ function cqn4sql(originalQuery, model, useTechnicalAlias = true) {
|
|
|
2579
2620
|
return { xpr: [matchColumns.length === 1 ? matchColumns[0] : { list: matchColumns }, 'in', subquery] }
|
|
2580
2621
|
}
|
|
2581
2622
|
|
|
2623
|
+
/**
|
|
2624
|
+
* Builds the ORDER BY entry ranking rows by $search relevance, sorted desc.
|
|
2625
|
+
*
|
|
2626
|
+
* Flat search: the score is on the outer row.
|
|
2627
|
+
* Deep search: the score lives in a semi-join, so emit a correlated scalar sub-select
|
|
2628
|
+
* (SELECT search(…, true) FROM <same source> WHERE innerKey = <outerAlias>.key) DESC.
|
|
2629
|
+
* Key comparisons are seeded unqualified (infer() binds them to the sub-select's own source);
|
|
2630
|
+
* correlateSearchRank redirects the rhs to the outer alias afterwards.
|
|
2631
|
+
*
|
|
2632
|
+
* @param {object} searchTerm the search term as returned by getSearch (func or xpr shape)
|
|
2633
|
+
* @returns {object|null} an orderBy entry, or null if there is nothing to rank by
|
|
2634
|
+
*/
|
|
2635
|
+
function buildSearchRankOrderBy(searchTerm) {
|
|
2636
|
+
if (searchTerm.func) return { func: searchTerm.func, args: [...searchTerm.args, { val: true }], sort: 'desc' }
|
|
2637
|
+
if (!searchTerm.xpr) return null
|
|
2638
|
+
|
|
2639
|
+
const searchSelect = searchTerm.xpr[2]
|
|
2640
|
+
const searchFunc = searchSelect.SELECT.where[0]
|
|
2641
|
+
const innerKeys = searchSelect.SELECT.columns // unqualified pk refs, e.g. [{ ref: ['ID'] }]
|
|
2642
|
+
|
|
2643
|
+
const where = []
|
|
2644
|
+
for (let i = 0; i < innerKeys.length; i++) {
|
|
2645
|
+
if (i) where.push('and')
|
|
2646
|
+
// seeded unqualified on both sides; correlateSearchRank redirects the rhs to the outer row
|
|
2647
|
+
where.push({ ref: [...innerKeys[i].ref] }, '=', { ref: [...innerKeys[i].ref] })
|
|
2648
|
+
}
|
|
2649
|
+
|
|
2650
|
+
const entry = {
|
|
2651
|
+
__proto__: SELECT.from(searchSelect.SELECT.from)
|
|
2652
|
+
// the correlated outer row fans out to many child rows -> MAX makes it a single score
|
|
2653
|
+
.columns({ func: 'max', args: [{ func: searchFunc.func, args: [...searchFunc.args, { val: true }] }] })
|
|
2654
|
+
.where(where),
|
|
2655
|
+
sort: 'desc',
|
|
2656
|
+
}
|
|
2657
|
+
defineProperty(entry, '$searchRank', true)
|
|
2658
|
+
return entry
|
|
2659
|
+
}
|
|
2660
|
+
|
|
2661
|
+
/**
|
|
2662
|
+
* Correlates the (transformed) deep-search ranking sub-select to the outer row.
|
|
2663
|
+
*
|
|
2664
|
+
* buildSearchRankOrderBy seeds its WHERE as `ref = ref` comparisons on the sub-select's own
|
|
2665
|
+
* alias; this rewrites each rhs to `<outerAlias>.<key>`. Driven off the `=` operator (not a
|
|
2666
|
+
* fixed stride) so it holds for any key count.
|
|
2667
|
+
*
|
|
2668
|
+
* @param {object} entry the transformed orderBy entry produced from a `$searchRank` sub-select
|
|
2669
|
+
* @param {string} outerAlias the final table alias of the outer query source
|
|
2670
|
+
*/
|
|
2671
|
+
function correlateSearchRank(entry, outerAlias) {
|
|
2672
|
+
const where = entry.SELECT.where
|
|
2673
|
+
for (let i = 1; i < where.length; i++) {
|
|
2674
|
+
// seeded comparisons are exactly `<ref> = <ref>`; rewrite the rhs ref to the outer row
|
|
2675
|
+
if (where[i] === '=' && where[i - 1]?.ref && where[i + 1]?.ref) {
|
|
2676
|
+
const rhs = where[i + 1]
|
|
2677
|
+
rhs.ref = [outerAlias, ...rhs.ref.slice(1)]
|
|
2678
|
+
}
|
|
2679
|
+
}
|
|
2680
|
+
}
|
|
2681
|
+
|
|
2582
2682
|
/**
|
|
2583
2683
|
* Calculates the name of the source which can be used to address the given node.
|
|
2584
2684
|
*
|
|
@@ -5,15 +5,19 @@ const cds = require('@sap/cds')
|
|
|
5
5
|
const JoinTree = require('./join-tree')
|
|
6
6
|
const { pseudos } = require('./pseudos')
|
|
7
7
|
const { isCalculatedOnRead, getImplicitAlias, getModelUtils, defineProperty, hasOwnSkip, isRuntimeView } = require('../utils')
|
|
8
|
+
const { depthGuarded, guardEntry } = require('../recursion-guard')
|
|
8
9
|
const cdsTypes = cds.builtin.types
|
|
9
10
|
/**
|
|
10
11
|
* @param {import('@sap/cds/apis/cqn').Query|string} originalQuery
|
|
11
12
|
* @param {import('@sap/cds/apis/csn').CSN} [model]
|
|
12
13
|
* @returns {import('./cqn').Query} = q with .target and .elements
|
|
13
14
|
*/
|
|
14
|
-
function
|
|
15
|
+
function _infer(originalQuery, model, useTechnicalAlias = true) {
|
|
15
16
|
if (!model) throw new Error('Please specify a model')
|
|
16
17
|
const inferred = originalQuery
|
|
18
|
+
// guard func args / lists / xpr / exists nesting
|
|
19
|
+
// eslint-disable-next-line no-func-assign
|
|
20
|
+
inferArg = depthGuarded(inferArg)
|
|
17
21
|
|
|
18
22
|
const { getDefinition } = getModelUtils(model, originalQuery)
|
|
19
23
|
|
|
@@ -291,19 +295,23 @@ function infer(originalQuery, model, useTechnicalAlias = true) {
|
|
|
291
295
|
*/
|
|
292
296
|
function walkTokenStream(tokenStream, inXpr = false) {
|
|
293
297
|
let skipJoins
|
|
294
|
-
const processToken =
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
298
|
+
const processToken = depthGuarded(walker())
|
|
299
|
+
tokenStream.forEach(processToken)
|
|
300
|
+
|
|
301
|
+
function walker() {
|
|
302
|
+
return t => {
|
|
303
|
+
if (t === 'exists') {
|
|
304
|
+
// no joins for infix filters along `exists <path>`
|
|
305
|
+
skipJoins = true
|
|
306
|
+
} else if (t.xpr) {
|
|
307
|
+
// don't miss an exists within an expression
|
|
308
|
+
t.xpr.forEach(processToken)
|
|
309
|
+
} else {
|
|
310
|
+
inferArg(t, queryElements, null, { inExists: skipJoins, inXpr, inQueryModifier: true })
|
|
311
|
+
skipJoins = false
|
|
312
|
+
}
|
|
304
313
|
}
|
|
305
314
|
}
|
|
306
|
-
tokenStream.forEach(processToken)
|
|
307
315
|
}
|
|
308
316
|
/**
|
|
309
317
|
* Processes references starting with `$self`, which are intended to target other query elements.
|
|
@@ -631,7 +639,7 @@ function infer(originalQuery, model, useTechnicalAlias = true) {
|
|
|
631
639
|
} else if (arg.inline && queryElements) {
|
|
632
640
|
const elements = resolveInline(arg)
|
|
633
641
|
for (const elName in elements) {
|
|
634
|
-
if (queryElements[elName] !== undefined) rejectDuplicatedElement(elName)
|
|
642
|
+
if (queryElements[elName] !== undefined) rejectDuplicatedElement(elName)
|
|
635
643
|
}
|
|
636
644
|
Object.assign(queryElements, elements)
|
|
637
645
|
} else {
|
|
@@ -667,7 +675,7 @@ function infer(originalQuery, model, useTechnicalAlias = true) {
|
|
|
667
675
|
// ignore whole expand if target of assoc along path has ”@cds.persistence.skip”
|
|
668
676
|
if (arg.expand) {
|
|
669
677
|
const { $refLinks } = arg
|
|
670
|
-
|
|
678
|
+
|
|
671
679
|
const skip = $refLinks.some(link => {
|
|
672
680
|
const def = getDefinition(link.definition.target)
|
|
673
681
|
return hasOwnSkip(def) && !isRuntimeView(def)
|
|
@@ -1095,7 +1103,7 @@ function infer(originalQuery, model, useTechnicalAlias = true) {
|
|
|
1095
1103
|
for (const k in elements) {
|
|
1096
1104
|
if (!exclude(k)) {
|
|
1097
1105
|
const element = elements[k]
|
|
1098
|
-
if (element.type !== 'cds.LargeBinary') {
|
|
1106
|
+
if (element.type !== 'cds.LargeBinary' && element.type !== 'cds.Vector') {
|
|
1099
1107
|
queryElements[k] = element
|
|
1100
1108
|
}
|
|
1101
1109
|
// only relevant if we actually select the calculated element
|
|
@@ -1115,7 +1123,7 @@ function infer(originalQuery, model, useTechnicalAlias = true) {
|
|
|
1115
1123
|
}
|
|
1116
1124
|
if (exclude(name) || name in queryElements) return true
|
|
1117
1125
|
const element = tableAliases[0].tableAlias.elements[name]
|
|
1118
|
-
if (element.type !== 'cds.LargeBinary') queryElements[name] = element
|
|
1126
|
+
if (element.type !== 'cds.LargeBinary' && element.type !== 'cds.Vector') queryElements[name] = element
|
|
1119
1127
|
if (isCalculatedOnRead(element)) {
|
|
1120
1128
|
linkCalculatedElement(element)
|
|
1121
1129
|
}
|
|
@@ -1287,4 +1295,7 @@ function getMainAlias (query) {
|
|
|
1287
1295
|
return mainAlias
|
|
1288
1296
|
}
|
|
1289
1297
|
|
|
1298
|
+
// guardEntry bounds nested-subquery re-entry (expand, from, set)
|
|
1299
|
+
const infer = guardEntry(_infer)
|
|
1300
|
+
|
|
1290
1301
|
module.exports = infer
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const cds = require('@sap/cds')
|
|
4
|
+
|
|
5
|
+
// shared across cqn4sql and infer to guard against stack overflow from user-controlled nesting depth
|
|
6
|
+
const MAX_RECURSION_DEPTH = 100
|
|
7
|
+
|
|
8
|
+
let recursionDepth = 0
|
|
9
|
+
|
|
10
|
+
function enterRecursion() {
|
|
11
|
+
if (++recursionDepth > MAX_RECURSION_DEPTH) throw cds.error(400, `Query exceeds the maximum nesting depth of ${MAX_RECURSION_DEPTH}`)
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function depthGuarded(fn) {
|
|
15
|
+
return function (...args) {
|
|
16
|
+
enterRecursion()
|
|
17
|
+
try {
|
|
18
|
+
return fn.apply(this, args)
|
|
19
|
+
} finally {
|
|
20
|
+
recursionDepth--
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// entry points (infer / cqn4sql): outermost call resets the counter, so a thrown
|
|
26
|
+
// depth error can't leave it polluted for the next request
|
|
27
|
+
function guardEntry(fn) {
|
|
28
|
+
return function (...args) {
|
|
29
|
+
const outermost = recursionDepth === 0
|
|
30
|
+
enterRecursion()
|
|
31
|
+
try {
|
|
32
|
+
return fn.apply(this, args)
|
|
33
|
+
} finally {
|
|
34
|
+
if (outermost) recursionDepth = 0
|
|
35
|
+
else recursionDepth--
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
module.exports = { depthGuarded, guardEntry }
|
|
@@ -166,7 +166,7 @@ const computeColumnsToBeSearched = (cqn, entity = { __searchableColumns: [] }) =
|
|
|
166
166
|
if (
|
|
167
167
|
cqn.SELECT.columns.length === 1 &&
|
|
168
168
|
column.func === 'count' &&
|
|
169
|
-
|
|
169
|
+
column.as === '$count'
|
|
170
170
|
) {
|
|
171
171
|
return
|
|
172
172
|
}
|
|
@@ -4,6 +4,25 @@
|
|
|
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
|
+
## [3.1.0](https://github.com/cap-js/cds-dbs/compare/sqlite-v3.0.2...sqlite-v3.1.0) (2026-09-07)
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
|
|
12
|
+
* Add support for `cds.Vector` on SQLite and Postgres ([#1676](https://github.com/cap-js/cds-dbs/issues/1676)) ([f0d2203](https://github.com/cap-js/cds-dbs/commit/f0d220324e8a1c7489966545000476bfd33f6b17))
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
### Fixed
|
|
16
|
+
|
|
17
|
+
* INSERT.from(SELECT) used non-existent `value` ([#1565](https://github.com/cap-js/cds-dbs/issues/1565)) ([fd98844](https://github.com/cap-js/cds-dbs/commit/fd98844c83a78d6cf2b0b71344d3ee1d88528106))
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
### Dependencies
|
|
21
|
+
|
|
22
|
+
* The following workspace dependencies were updated
|
|
23
|
+
* dependencies
|
|
24
|
+
* @cap-js/db-service bumped from ^3.0.1 to ^3.1.0
|
|
25
|
+
|
|
7
26
|
## [3.0.2](https://github.com/cap-js/cds-dbs/compare/sqlite-v3.0.1...sqlite-v3.0.2) (2026-07-01)
|
|
8
27
|
|
|
9
28
|
|
|
@@ -14,16 +14,6 @@ const sqliteKeywords = keywords.reduce((prev, curr) => {
|
|
|
14
14
|
return prev
|
|
15
15
|
}, {})
|
|
16
16
|
|
|
17
|
-
// define date and time functions in js to allow for throwing errors
|
|
18
|
-
const isTime = /^\d{1,2}:\d{1,2}:\d{1,2}$/
|
|
19
|
-
const hasTimezone = /([+-]\d{1,2}:?\d{0,2}|Z)$/
|
|
20
|
-
const toDate = (d, allowTime = false) => {
|
|
21
|
-
const date = new Date(allowTime && isTime.test(d) ? `1970-01-01T${d}Z` : hasTimezone.test(d) ? d : d + 'Z')
|
|
22
|
-
if (Number.isNaN(date.getTime())) throw new Error(`Value does not contain a valid ${allowTime ? 'time' : 'date'} "${d}"`)
|
|
23
|
-
return date
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
|
|
27
17
|
class SQLiteService extends SQLService {
|
|
28
18
|
|
|
29
19
|
get factory() {
|
|
@@ -46,6 +36,10 @@ class SQLiteService extends SQLService {
|
|
|
46
36
|
dbc.function('hour', deterministic, d => d === null ? null : toDate(d, true).getUTCHours())
|
|
47
37
|
dbc.function('minute', deterministic, d => d === null ? null : toDate(d, true).getUTCMinutes())
|
|
48
38
|
dbc.function('second', deterministic, d => d === null ? null : toDate(d, true).getUTCSeconds())
|
|
39
|
+
dbc.function('COSINE_SIMILARITY', deterministic, (a, b) => cosineSimilarity(toFloatArray(a), toFloatArray(b)))
|
|
40
|
+
dbc.function('L2DISTANCE', deterministic, (a, b) => l2Distance(toFloatArray(a), toFloatArray(b)))
|
|
41
|
+
dbc.function('L2NORMALIZE', deterministic, v => v == null ? null : fromFloatArray(l2Normalize(toFloatArray(v)), v))
|
|
42
|
+
dbc.function('VECTOR_EMBEDDING', deterministic, (input, text_type, model_and_version) => input == null ? null : JSON.stringify(hashEmbedding(String(input))))
|
|
49
43
|
if (database !== ':memory:') dbc.pragma?.('journal_mode = WAL') || dbc.exec('PRAGMA journal_mode = WAL')
|
|
50
44
|
return dbc
|
|
51
45
|
} catch (err) {
|
|
@@ -184,7 +178,7 @@ class SQLiteService extends SQLService {
|
|
|
184
178
|
let ps = await this.prepare(sql)
|
|
185
179
|
const vals = await this._prepareStreams(values)
|
|
186
180
|
const { changes } = await ps.run(vals)
|
|
187
|
-
return this._return_affected
|
|
181
|
+
return this._return_affected(changes)
|
|
188
182
|
}
|
|
189
183
|
|
|
190
184
|
onPlainSQL({ query, data }, next) {
|
|
@@ -273,9 +267,9 @@ class SQLiteService extends SQLService {
|
|
|
273
267
|
// Reading decimal as string to not loose precision
|
|
274
268
|
Decimal: cds.env.features.ieee754compatible
|
|
275
269
|
? (expr, elem) =>
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
270
|
+
elem?.scale
|
|
271
|
+
? `CASE WHEN ${expr} IS NULL THEN NULL ELSE format('%.${elem.scale}f', ${expr}) END`
|
|
272
|
+
: `CASE WHEN ${expr} IS NULL THEN NULL ELSE rtrim(rtrim(format('%.999f', ${expr}), '0'), '.') END`
|
|
279
273
|
: undefined,
|
|
280
274
|
// Binary is not allowed in json objects
|
|
281
275
|
Binary: expr => `${expr} || ''`,
|
|
@@ -321,4 +315,106 @@ function loadSQLite(driver) {
|
|
|
321
315
|
}
|
|
322
316
|
}
|
|
323
317
|
|
|
318
|
+
// define date and time functions in js to allow for throwing errors
|
|
319
|
+
const isTime = d => /^\d{1,2}:\d{1,2}:\d{1,2}$/.test(d)
|
|
320
|
+
const hasTimezone = d => /([+-]\d{1,2}:?\d{0,2}|Z)$/.test(d)
|
|
321
|
+
const toDate = (d, allowTime = false) => {
|
|
322
|
+
const date = new Date(allowTime && isTime(d) ? `1970-01-01T${d}Z` : hasTimezone(d) ? d : d + 'Z')
|
|
323
|
+
if (Number.isNaN(date.getTime())) throw new Error(`Value does not contain a valid ${allowTime ? 'time' : 'date'} "${d}"`)
|
|
324
|
+
return date
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
// Vector functions implemented in JavaScript for SQLite (registered as custom SQL functions)
|
|
328
|
+
function cosineSimilarity(a, b) {
|
|
329
|
+
if (a == null || b == null) return null
|
|
330
|
+
let dot = 0, normA = 0, normB = 0
|
|
331
|
+
for (let i = 0; i < a.length; i++) {
|
|
332
|
+
dot += a[i] * b[i]
|
|
333
|
+
normA += a[i] * a[i]
|
|
334
|
+
normB += b[i] * b[i]
|
|
335
|
+
}
|
|
336
|
+
const denom = Math.sqrt(normA) * Math.sqrt(normB)
|
|
337
|
+
return denom === 0 ? 0 : dot / denom
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
function l2Distance(a, b) {
|
|
341
|
+
if (a == null || b == null) return null
|
|
342
|
+
let sum = 0
|
|
343
|
+
for (let i = 0; i < a.length; i++) {
|
|
344
|
+
const diff = a[i] - b[i]
|
|
345
|
+
sum += diff * diff
|
|
346
|
+
}
|
|
347
|
+
return Math.sqrt(sum)
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
function l2Normalize(v) {
|
|
351
|
+
if (v == null) return null
|
|
352
|
+
let norm = 0
|
|
353
|
+
for (let i = 0; i < v.length; i++) norm += v[i] * v[i]
|
|
354
|
+
if (norm === 0) return v
|
|
355
|
+
norm = Math.sqrt(norm)
|
|
356
|
+
for (let i = 0; i < v.length; i++) v[i] /= norm
|
|
357
|
+
return v
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
/**
|
|
361
|
+
* Deterministic synchronous hash-based embedding function for SQLite.
|
|
362
|
+
* NOTE: If a synchronous embedding library becomes available for Node.js,
|
|
363
|
+
* it can be integrated here to replace the hash-based implementation.
|
|
364
|
+
*/
|
|
365
|
+
function hashEmbedding(text, dimensions = 384) {
|
|
366
|
+
if (text == null) return null
|
|
367
|
+
const vector = new Float32Array(dimensions)
|
|
368
|
+
const normalized = text.toLowerCase()
|
|
369
|
+
const ngramSize = 3
|
|
370
|
+
|
|
371
|
+
if (normalized.length >= ngramSize) {
|
|
372
|
+
for (let i = 0; i <= normalized.length - ngramSize; i++)
|
|
373
|
+
project(ngramHash(normalized, i, ngramSize), vector, dimensions)
|
|
374
|
+
} else {
|
|
375
|
+
for (let i = 0; i < normalized.length; i++)
|
|
376
|
+
project(normalized.charCodeAt(i), vector, dimensions)
|
|
377
|
+
}
|
|
378
|
+
return Array.from(l2Normalize(vector))
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
function ngramHash(text, start, len) {
|
|
382
|
+
let hash = 0x811c9dc5
|
|
383
|
+
for (let i = start; i < start + len; i++) {
|
|
384
|
+
hash ^= text.charCodeAt(i)
|
|
385
|
+
hash = Math.imul(hash, 0x01000193)
|
|
386
|
+
}
|
|
387
|
+
return hash
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
function project(hash, vector, dimensions) {
|
|
391
|
+
for (let band = 0; band < 4; band++) {
|
|
392
|
+
const h = rehash(hash, band)
|
|
393
|
+
vector[Math.abs(h % dimensions)] += ((h >>> 16) & 1) === 0 ? 1.0 : -1.0
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
function rehash(hash, band) {
|
|
398
|
+
let h = hash ^ Math.imul(band, 0x9e3779b9)
|
|
399
|
+
h ^= h >>> 16
|
|
400
|
+
h = Math.imul(h, 0x45d9f3b)
|
|
401
|
+
h ^= h >>> 16
|
|
402
|
+
return h
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
// Vector type conversion helpers for SQLite - handle various input formats (Buffer, string, array, typed arrays)
|
|
406
|
+
function toFloatArray(vector) {
|
|
407
|
+
if (vector == null) return null
|
|
408
|
+
if (vector instanceof Float32Array) return Array.from(vector)
|
|
409
|
+
if (Buffer.isBuffer(vector)) return JSON.parse(vector.toString('utf8'))
|
|
410
|
+
if (vector instanceof Uint8Array) return JSON.parse(Buffer.from(vector).toString('utf8'))
|
|
411
|
+
if (typeof vector === 'string') return JSON.parse(vector)
|
|
412
|
+
if (Array.isArray(vector)) return vector
|
|
413
|
+
throw new Error(`Unsupported vector type: ${typeof vector}`)
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
function fromFloatArray(arr, original) {
|
|
417
|
+
return original instanceof Float32Array ? new Float32Array(arr) : JSON.stringify(arr)
|
|
418
|
+
}
|
|
419
|
+
|
|
324
420
|
module.exports = SQLiteService
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cap-js/sqlite",
|
|
3
|
-
"version": "3.0
|
|
3
|
+
"version": "3.1.0",
|
|
4
4
|
"description": "CDS database service for SQLite",
|
|
5
5
|
"homepage": "https://github.com/cap-js/cds-dbs/tree/main/sqlite#cds-database-service-for-sqlite",
|
|
6
6
|
"repository": {
|
|
@@ -26,12 +26,12 @@
|
|
|
26
26
|
"test": "cds-test"
|
|
27
27
|
},
|
|
28
28
|
"dependencies": {
|
|
29
|
-
"@cap-js/db-service": "^3.0
|
|
29
|
+
"@cap-js/db-service": "^3.1.0"
|
|
30
30
|
},
|
|
31
31
|
"peerDependencies": {
|
|
32
32
|
"@sap/cds": "^10",
|
|
33
33
|
"sql.js": "^1.13.0",
|
|
34
|
-
"better-sqlite3": "^12.0.0"
|
|
34
|
+
"better-sqlite3": "^12.0.0 || ^13.0.0"
|
|
35
35
|
},
|
|
36
36
|
"peerDependenciesMeta": {
|
|
37
37
|
"sql.js": {
|