@cap-js/db-service
Advanced tools
Comparing version 1.0.0 to 1.0.1
@@ -7,4 +7,13 @@ # Change Log | ||
## Version 1.0.1 - 2023-07-03 | ||
### Fixed | ||
- Paths addressing a column of the query via `$self.<column>` in `group by` / `order by`, `having` or `where` | ||
are now correctly substituted. | ||
- Mapping for OData `average` function to ANSI SQL compliant `avg` function. | ||
## Version 1.0.0 - 2023-06-23 | ||
- Initial Release |
@@ -6,2 +6,3 @@ const StandardFunctions = { | ||
// length : (x) => `length(${x})`, | ||
average: x => `avg(${x})`, | ||
search: function (ref, arg) { | ||
@@ -8,0 +9,0 @@ if (!('val' in arg)) throw `SQLite only supports single value arguments for $search` |
@@ -278,3 +278,3 @@ 'use strict' | ||
let wildcardSelect = false | ||
const refs = [] | ||
const dollarSelfRefs = [] | ||
columns.forEach(col => { | ||
@@ -298,3 +298,11 @@ if (col === '*') { | ||
} else if (col.ref) { | ||
refs.push(col) | ||
const firstStepIsTableAlias = | ||
(col.ref.length > 1 && col.ref[0] in sources) || | ||
// nested projection on table alias | ||
(col.ref.length === 1 && col.ref[0] in sources && col.inline) | ||
const firstStepIsSelf = | ||
!firstStepIsTableAlias && col.ref.length > 1 && ['$self', '$projection'].includes(col.ref[0]) | ||
// we must handle $self references after the query elements have been calculated | ||
if (firstStepIsSelf) dollarSelfRefs.push(col) | ||
else handleRef(col) | ||
} else if (col.expand) { | ||
@@ -306,13 +314,5 @@ inferQueryElement(col) | ||
}) | ||
refs.forEach(col => { | ||
inferQueryElement(col) | ||
const { definition } = col.$refLinks[col.$refLinks.length - 1] | ||
if (col.cast) | ||
// final type overwritten -> element not visible anymore | ||
setElementOnColumns(col, getElementForCast(col)) | ||
else if ((col.ref.length === 1) & (col.ref[0] === '$user')) | ||
// shortcut to $user.id | ||
setElementOnColumns(col, queryElements[col.as || '$user']) | ||
else setElementOnColumns(col, definition) | ||
}) | ||
if (dollarSelfRefs.length) inferDollarSelfRefs(dollarSelfRefs) | ||
if (wildcardSelect) inferElementsFromWildCard(aliases) | ||
@@ -366,2 +366,50 @@ } | ||
/** | ||
* Processes references starting with `$self`, which are intended to target other query elements. | ||
* These `$self` paths must be handled after processing the "regular" columns since they are dependent on other query elements. | ||
* | ||
* This function checks for `$self` references that may target other `$self` columns, and delays their processing. | ||
* `$self` references not targeting other `$self` references are handled by the generic `handleRef` function immediately. | ||
* | ||
* @param {array} dollarSelfColumns - An array of column objects containing `$self` references. | ||
*/ | ||
function inferDollarSelfRefs(dollarSelfColumns) { | ||
do { | ||
const unprocessedColumns = [] | ||
for (const currentDollarSelfColumn of dollarSelfColumns) { | ||
const { ref } = currentDollarSelfColumn | ||
const stepToFind = ref[1] | ||
const referencesOtherDollarSelfColumn = dollarSelfColumns.find( | ||
otherDollarSelfCol => | ||
otherDollarSelfCol !== currentDollarSelfColumn && | ||
(otherDollarSelfCol.as | ||
? stepToFind === otherDollarSelfCol.as | ||
: stepToFind === otherDollarSelfCol.ref?.[otherDollarSelfCol.ref.length - 1]), | ||
) | ||
if (referencesOtherDollarSelfColumn) { | ||
unprocessedColumns.push(currentDollarSelfColumn) | ||
} else { | ||
handleRef(currentDollarSelfColumn) | ||
} | ||
} | ||
dollarSelfColumns = unprocessedColumns | ||
} while (dollarSelfColumns.length > 0) | ||
} | ||
function handleRef(col) { | ||
inferQueryElement(col) | ||
const { definition } = col.$refLinks[col.$refLinks.length - 1] | ||
if (col.cast) | ||
// final type overwritten -> element not visible anymore | ||
setElementOnColumns(col, getElementForCast(col)) | ||
else if ((col.ref.length === 1) & (col.ref[0] === '$user')) | ||
// shortcut to $user.id | ||
setElementOnColumns(col, queryElements[col.as || '$user']) | ||
else setElementOnColumns(col, definition) | ||
} | ||
/** | ||
* This function is responsible for inferring a query element based on a provided column. | ||
@@ -484,3 +532,13 @@ * It initializes and attaches a non-enumerable `$refLinks` property to the column, | ||
const elements = definition.elements || definition._target?.elements | ||
if (elements && id in elements) { | ||
const element = elements?.[id] | ||
if (firstStepIsSelf && element?.isAssociation) { | ||
throw cds.error( | ||
`Paths starting with “$self” must not contain steps of type “cds.Association”: ref: [ ${column.ref.map( | ||
idOnly, | ||
)} ]`, | ||
) | ||
} | ||
if (element) { | ||
const $refLink = { definition: elements[id], target: column.$refLinks[i - 1].target } | ||
@@ -512,3 +570,5 @@ column.$refLinks.push($refLink) | ||
else if (skipAliasedFkSegmentsOfNameStack[0] === id) skipAliasedFkSegmentsOfNameStack.shift() | ||
else nameSegments.push(id) | ||
else { | ||
nameSegments.push(firstStepIsSelf && i === 1 ? element.__proto__.name : id) | ||
} | ||
} | ||
@@ -595,3 +655,3 @@ | ||
// check if we need to merge the column `ref` into the join tree of the query | ||
if (!inExists && !virtual && isColumnJoinRelevant(column)) { | ||
if (!inExists && !virtual && isColumnJoinRelevant(column, firstStepIsSelf)) { | ||
Object.defineProperty(column, 'isJoinRelevant', { value: true }) | ||
@@ -770,3 +830,3 @@ joinTree.mergeColumn(column) | ||
if (fkAccess) return false | ||
else return true | ||
return true | ||
} | ||
@@ -773,0 +833,0 @@ |
{ | ||
"name": "@cap-js/db-service", | ||
"version": "1.0.0", | ||
"version": "1.0.1", | ||
"description": "CDS base database service", | ||
@@ -5,0 +5,0 @@ "homepage": "https://cap.cloud.sap/", |
Sorry, the diff of this file is too big to display
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
202497
4341