UNPKG

@sap/cds-compiler

Version:

CDS (Core Data Services) compiler and backends

78 lines (69 loc) 2.85 kB
'use strict'; const { requireForeignKeyAccess } = require('../checks/onConditions'); /** * Filter expressions in an exists path must: * - only contain fk-accesses for assocs. Unmanaged traversal / non-fk access is forbidden. * - not contain a ref starting with $self * * @param {CSN.Artifact} parent * @param {string} name * @param {Array} expr */ function assertFilterOfExists( parent, name, expr ) { for (let i = 0; i < expr.length - 1; i++) { if (expr[i] === 'exists' && expr[i + 1].ref) { i++; const current = expr[i]; const { _links } = current; const assocs = _links.filter(link => link.art?.target).map(link => current.ref[link.idx]); ensureValidFilters.call(this, assocs); } } } /** * Reject: * - Unmanaged traversal / non-fk access. * - ref's starting with $self * * @param {object[]} assocs Array of refs of assocs - possibly with a .where to check */ function ensureValidFilters( assocs ) { for (const assoc of assocs) { if (assoc.where) { for (let i = 0; i < assoc.where.length; i++) { const part = assoc.where[i]; if (part._links && !(assoc.where[i - 1] && assoc.where[i - 1] === 'exists')) { if (part.$scope === '$self') this.error('ref-unexpected-self', part.$path, { '#': 'exists-filter', elemref: assoc.id, id: part.ref[0] }); for (const link of part._links) { if (link.art && link.art.target) { if (link.art.keys) { // managed - allow FK access const next = part._links[link.idx + 1]; if (next !== undefined) { // there is a next path step - check if it is a fk requireForeignKeyAccess(part, i, (errorIndex) => { const { ref } = assoc.where[part.$path[part.$path.length - 1]]; this.error('ref-expecting-foreign-key', part.$path, { alias: ref[errorIndex], id: assoc.id, name: ref[link.idx] }); }); } else { // no traversal, ends on managed this.error('ref-unexpected-assoc', part.$path, { '#': 'managed-filter', id: assoc.id, name: assoc.where[part.$path[part.$path.length - 1]].ref[link.idx] }); } } else { // unmanaged - always wrong this.error('ref-unexpected-assoc', part.$path, { '#': 'unmanaged-filter', id: assoc.id, name: assoc.where[part.$path[part.$path.length - 1]].ref[link.idx] }); } // Recursively drill down if the assoc-step has a filter if (part.ref[link.idx].where) ensureValidFilters.call(this, [ part.ref[link.idx] ]); } } } } } } } module.exports = { having: assertFilterOfExists, where: assertFilterOfExists, xpr: assertFilterOfExists, };