Search

Core Expression Notation

Expressions in CDS definitions and queries can be one of

expr = // one of...
  val    |   // [literal values]: #literal-values
  ref    |   // references or functions
  xpr    |   // operator expressions
  func   |   // function calls
  param  |   // binding parameters
  sym    |   // enum symbol
  SELECT     // subqueries

Literal Values

Literal values are represented as {val:...} with property val holding the actual literal value as specified in JSON.

val = {val:literal}
literal = string | number | true | false | null

Examples:

cds.parse.expr(`'a string'`)  == {val:'a string'}
cds.parse.expr(`11`)  == {val:11}
cds.parse.expr(`true`)  == {val:true}
cds.parse.expr(`null`)  == {val:null}

References

A reference is represented as {ref:...} with property ref holding an array of reference segments as plain identifier strings, or in case of infix filters and/or arguments an object {id:identifier, ...} as follows:

ref = {ref:[..._segment]}
_segment = string | { id:string, args:_named, where:expr }
_named = { ... <name>:expr }

Examples:

let cqn4 = cds.parse.expr
cqn4(`foo.bar`) == {ref:['foo','bar']}
cqn4(`foo[9].bar`) == {ref:[{ id:'foo', where:[{val:9}] }, 'bar' ]}
cqn4(`foo(p:x).bar`) == {ref:[{ id:'foo', args:{p:{ref:['x']}} }, 'bar' ]}

Functions Calls

Function calls are represented as follows:

func = { func:string, args: _positional | _named }
_positional = [ ...expr ]
_named = { ... <name>:expr }

Examples:

let cqn4 = cds.parse.expr
cqn4(`foo(p=>x)`) == {func:'foo', args:{p:{ref:['x']}}}
cqn4(`sum(x)`)   == {func:'sum', args:[{ref:['x']}]}
cqn4(`count(*)`) == {func:'count', args:['*']}

Operator Expressions

Operators join one or more expressions into complex ones, represented as {xpr:...}. The property xpr holds a sequence of operators and operands.

xpr = {xpr:_xpr}
_xpr = [...( _operand | _operator )]
_operand = expr
_operator = string

Examples:

cds.parse.expr(`x<9`)  ==//> returns:
{xpr:[ {ref:['x']}, '<', {val:9} ]}

cds.parse.expr(`x<9 and (y=1 or z=2)`)  ==//> returns:
{xpr:[
  {ref:['x']}, '<', {val:9}, 'and', '(',
    {ref:['y']}, '=', {val:1}, 'or', {ref:['z']}, '=', {val:2}
  ')'
]}

CQN intentionally doesn’t aim to understand the individual operators and related expressions. It captures them as arbitrary sequences, in the same lexical structure and order they’re written in the source. This ‘ignorance’ allows us to stay open to any kind of operators and keywords. For example, we can easily express native extensions of underlying database dialects.

Binding Parameters

Binding parameters for prepared statements are represented as {ref:..., param:true} with values for ref as follows.

param = { ref:[ '?' | number | name ], param:true }

Examples:

cds.parse.expr(`x=:1`) == [{ref:['x']}, '=', {ref:[1], param:true}]
cds.parse.expr(`x=:y`) == [{ref:['x']}, '=', {ref:['y'], param:true}]
cds.parse.expr(`x=?`)  == [{ref:['x']}, '=', {ref:['?'], param:true}]