diff --git a/cds/assets/cxl/binary-operator.drawio.svg b/cds/assets/cxl/binary-operator.drawio.svg
new file mode 100644
index 0000000000..50beb04e94
--- /dev/null
+++ b/cds/assets/cxl/binary-operator.drawio.svg
@@ -0,0 +1,300 @@
+
\ No newline at end of file
diff --git a/cds/assets/cxl/expr.drawio.svg b/cds/assets/cxl/expr.drawio.svg
new file mode 100644
index 0000000000..b67261bc8f
--- /dev/null
+++ b/cds/assets/cxl/expr.drawio.svg
@@ -0,0 +1,868 @@
+
\ No newline at end of file
diff --git a/cds/assets/cxl/function-def.drawio.svg b/cds/assets/cxl/function-def.drawio.svg
new file mode 100644
index 0000000000..2db3c1d36a
--- /dev/null
+++ b/cds/assets/cxl/function-def.drawio.svg
@@ -0,0 +1,112 @@
+
\ No newline at end of file
diff --git a/cds/assets/cxl/infix-filter.drawio.svg b/cds/assets/cxl/infix-filter.drawio.svg
new file mode 100644
index 0000000000..cff966b280
--- /dev/null
+++ b/cds/assets/cxl/infix-filter.drawio.svg
@@ -0,0 +1,110 @@
+
\ No newline at end of file
diff --git a/cds/assets/cxl/intro.drawio.svg b/cds/assets/cxl/intro.drawio.svg
new file mode 100644
index 0000000000..b4ab6afc5f
--- /dev/null
+++ b/cds/assets/cxl/intro.drawio.svg
@@ -0,0 +1,196 @@
+
\ No newline at end of file
diff --git a/cds/assets/cxl/literal-value.drawio.svg b/cds/assets/cxl/literal-value.drawio.svg
new file mode 100644
index 0000000000..105b37cea3
--- /dev/null
+++ b/cds/assets/cxl/literal-value.drawio.svg
@@ -0,0 +1,155 @@
+
\ No newline at end of file
diff --git a/cds/assets/cxl/ref.drawio.svg b/cds/assets/cxl/ref.drawio.svg
new file mode 100644
index 0000000000..cfb78f48bc
--- /dev/null
+++ b/cds/assets/cxl/ref.drawio.svg
@@ -0,0 +1,106 @@
+
\ No newline at end of file
diff --git a/cds/assets/cxl/unary-operator.drawio.svg b/cds/assets/cxl/unary-operator.drawio.svg
new file mode 100644
index 0000000000..df933fe1f1
--- /dev/null
+++ b/cds/assets/cxl/unary-operator.drawio.svg
@@ -0,0 +1,65 @@
+
\ No newline at end of file
diff --git a/cds/cxl.md b/cds/cxl.md
index f9734ff3d9..8865502da7 100644
--- a/cds/cxl.md
+++ b/cds/cxl.md
@@ -1,12 +1,834 @@
---
+# layout: cds-ref
+shorty: Expressions
+synopsis: >
+ Specification of the CDS Expression Language (CXL) used to capture expressions in CDS.
status: released
---
-# CDS Expression Language (CXL)
+
-This document provides an overview of the CDS Expression Language (CXL) used in CDS models and queries.
-CXL is essentially a standard subset of SQL expressions enhanced by [Path Expressions](../cds/cql#path-expressions) with [Infix Filters](../cds/cql#with-infix-filters).
-The documentation is still **under construction** and will be released soon.
+# CDS Expression Language (CXL) { #expressions }
+The CDS Expression Language (`CXL`) is a language to express calculations, conditions,
+and other expressions in the context of CDS models and queries.
+**`CXL` is based on the SQL expression language**, so many syntax elements from SQL are also available in `CXL`.
+
+`CXL` can be used in various places:
+- In [CQL](./cql#path-expressions) (select list, where clause, …)
+- In [CDL](./cdl)
+ + In [calculated elements](./cdl#calculated-elements)
+ + In [annotations](./cdl.md#expressions-as-annotation-values)
+
+::: tip Expressions in CAP are materialized in the context of queries
+No matter where `CXL` is used, it always manifests in queries.
+For example, [a calculated element](./cdl#calculated-elements) defined in an entity will be resolved
+to the respective calculation in the generated query when the entity is queried.
+:::
+
+
+## How to read this guide { #how-to }
+
+
+In the following chapters we illustrate the `CXL` syntax based on simple and more complex examples.
+For a complete reference of the syntax, there are clickable [syntax diagrams](https://en.wikipedia.org/wiki/Syntax_diagram) (aka railroad diagrams) for each language construct.
+
+### samples
+
+To try the samples by yourself, create a simple CAP app:
+
+```sh
+cds init bookshop --add sample && cd bookshop
+```
+
+We encourage you to play around with the snippets.
+Just create the sample app as described above and start a repl session within the newly created app by running:
+
+```sh
+cds repl --run .
+```
+
+:::info All of the example expressions follow the same pattern:
+1. A **`CXL`** is shown in the context of a query.
+2. The resulting **`SQL`** is shown.
+
+:::code-group
+```js [CQL]
+> await cds.ql`SELECT from Books { title }` // [!code focus]
+[
+ { title: 'Wuthering Heights' },
+ { title: 'Jane Eyre' },
+ { title: 'The Raven' },
+ { title: 'Eleonora' },
+ { title: 'Catweazle' }
+]
+```
+
+```sql [SQL]
+SELECT title FROM sap_capire_bookshop_Books as Books
+```
+:::
+
+### syntax diagrams
+
+Each language construct is illustrated by a clickable [syntax diagram](https://en.wikipedia.org/wiki/Syntax_diagram).
+
+They show the syntax of CAPs expression language as a sequence of building blocks.
+By clicking on the individual blocks, you can get more information about the respective building block.
+
+The following diagram illustrates how to read the diagrams:
+
+
+
+
+
+
+
+## expr { #expr }
+
+An expression can hold various elements, such as references, literals, function calls, operators, and more. A few examples, in the context of a select list:
+```cds
+select from Books {
+ 42 as answer, // literal
+ title, // reference ("ref")
+ price * quantity as totalPrice, // binary operator
+ substring(title, 1, 3) as shortTitle, // function call
+ author.name as authorName, // ref with path expression
+ chapters[number < 3] as earlyChapters, // ref with infix filter
+ exists chapters as hasChapters, // exists
+ count(chapters) as chapterCount, // aggregate function
+}
+```
+
+This syntax diagram describes the possible expressions:
+
+
+
+
+
+
+
+::: tip
+An expression can be used in various places, in the following sections we will give a brief overview of _some_ use cases.
+:::
+
+### in model definitions
+
+Expressions can be used to define calculated elements.
+Typically, this is done on the select list of a query. CAP
+also allows to define calculated elements directly in the model:
+
+```cds
+extend Books with {
+ total = price * stock;
+}
+```
+
+In this example, we define a calculated element `total` in the `Books` entity
+that calculates the total value of all books in stock by multiplying the `price` with the `stock`.
+
+
+:::code-group
+```js [CQL]
+> await cds.ql`SELECT title, total from Books` // [!code focus]
+[
+ { title: 'Wuthering Heights', total: 133.32 },
+ { title: 'Jane Eyre', total: 135.74 },
+ { title: 'The Raven', total: 4372.29 },
+ { title: 'Eleonora', total: 7770 },
+ { title: 'Catweazle', total: 3300 }
+]
+```
+
+```sql [SQL]
+SELECT
+ Books.title,
+ Books.price * Books.stock as total -- calculated element is resolved
+FROM sap_capire_bookshop_Books as Books
+```
+:::
+
+[Learn more about calculated elements](./cdl.md#calculated-elements){ .learn-more }
+
+### in queries
+
+Expressions can be used in various parts of a query, e.g., on the select list, in the where clause, in order by clauses, and more:
+
+:::code-group
+```js [CQL]
+> await cds.ql`
+ SELECT from Books {
+ title,
+ stock,
+ price,
+ price * stock as total } where price > 10` // [!code focus]
+[
+ { title: 'Wuthering Heights', stock: 12, price: 11.11, total: 133.32},
+ { title: 'Jane Eyre', stock: 11, price: 12.34, total: 135.74 },
+ { title: 'The Raven', stock: 333, price: 13.13, total: 4372.29 },
+ { title: 'Eleonora', stock: 555, price: 14, total: 7770 },
+ { title: 'Catweazle', stock: 22, price: 150, total: 3300 }
+]
+```
+
+Compared to the previous example, we now use the expression directly in the query
+to calculate the total value of all books in stock.
+
+```sql [SQL]
+SELECT
+ Books.title,
+ Books.stock,
+ Books.price,
+ Books.price * Books.stock as total
+FROM sap_capire_bookshop_Books as Books
+WHERE Books.price > 10
+```
+:::
+
+
+### in annotations
+
+Annotations can [contain expressions](./cdl.md#expressions-as-annotation-values) as their value.
+The meaning and effect of the expression depend on the specific annotation being used.
+
+For example, the [`@assert` annotation](../guides/services/constraints.md#assert-constraint) lets us declaratively define input validation constraints.
+In this example, we want to make sure that no Books with negative stocks are created:
+
+
+```cds
+annotate AdminService.Books:stock with @assert: (case
+ when stock < 0 then 'Enter a positive number'
+end);
+```
+
+Upon insert, the expression is evaluated against the updated data:
+
+:::code-group
+```js [cds repl]
+> const { Books } = AdminService.entities
+> const insert = INSERT.into(Books).entries({ // [!code focus]
+ ID: 277,
+ author_ID: 101,
+ title: 'Lord of the Rings',
+ stock: -2, // [!code focus]
+ })
+> await AdminService.run(insert)
+
+Uncaught:
+{
+ status: 400, // [!code focus]
+ code: 'ASSERT', // [!code focus]
+ target: 'stock', // [!code focus]
+ numericSeverity: 4,
+ '@Common.numericSeverity': 4,
+ message: 'Enter a positive number' // [!code focus]
+}
+```
+
+```sql [sql log]
+BEGIN
+
+-- sql statement for the insert:
+INSERT INTO sap_capire_bookshop_Books (createdAt,createdBy,modifiedAt,modifiedBy,ID,author_ID,title,descr,genre_ID,stock,price,currency_code) SELECT (CASE WHEN json_type(value,'$."createdAt"') IS NULL THEN ISO(session_context('$now')) ELSE ISO(value->>'$."createdAt"') END),(CASE WHEN json_type(value,'$."createdBy"') IS NULL THEN session_context('$user.id') ELSE value->>'$."createdBy"' END),(CASE WHEN json_type(value,'$."modifiedAt"') IS NULL THEN ISO(session_context('$now')) ELSE ISO(value->>'$."modifiedAt"') END),(CASE WHEN json_type(value,'$."modifiedBy"') IS NULL THEN session_context('$user.id') ELSE value->>'$."modifiedBy"' END),value->>'$."ID"',value->>'$."author_ID"',value->>'$."title"',value->>'$."descr"',value->>'$."genre_ID"',value->>'$."stock"',value->>'$."price"',value->>'$."currency_code"' FROM json_each(?) [
+ [
+ [
+ {
+ ID: 277,
+ author_ID: 101,
+ title: 'Lord of the Rings',
+ stock: -2
+ }
+ ]
+ ]
+]
+
+-- assert expressions are evaluated:
+SELECT json_insert('{}','$."ID"',ID,'$."@assert:stock"',"@assert:stock") as _json_
+FROM (
+ SELECT
+ "$B".ID,
+ case when "$B".stock < ? then ? end as "@assert:stock"
+ FROM AdminService_Books as "$B"
+ WHERE ("$B".ID) in ((?))
+) [ 0, 'Enter a positive number', 277 ]
+
+-- result of evaluation contains violated constraints,
+-- which leads to a rollback:
+ROLLBACK
+```
+:::
+
+
+::: tip What-not-how!
+The `@assert` annotation lets you capture the intent via an expression, without having to deal with the technical details.
+This conforms to the core principle [what-not-how](../guides/domain/index#capture-intent-—-what-not-how) of CAP.
+:::
+
+## literal value { #literal-value }
+
+Literal values represent constant data embedded directly in an expression.
+They are independent of model elements and evaluate to the same value.
+
+
+
+::: info A unary operator is an operator that operates on exactly one operand.
+
+E.g. in the expression `-price`, the `-` operator is a unary operator
+that operates on the single operand `price`. It negates the value of `price`.
+:::
+
+### binary operator { #binary-operator }
+
+
+
+
+
+
+::: info A binary operator is an operator that operates on two operands.
+E.g. in the expression `price * quantity`, the `*` operator is a binary operator
+that multiplies the two factors `price` and `quantity`.
+:::
+
+## function { #function }
+
+
+
+
+
+
+
+
+CAP supports a set of [portable functions](../guides/databases/cql-to-sql.md#portable-functions) that can be used in all expressions. Those functions are passed through to the underlying database, allowing you to leverage the same functions for different databases, which greatly enhances portability.
+
+## ref (path expression) { #ref }
+
+A `ref` (short for reference) is used to refer to an element within the model.
+It can be used to navigate along path segments. Such a navigation is often
+referred to as a **path expression**.
+
+
+
+
+
+
+::: info Leaf elements
+Leaf elements as opposed to associations and structured elements represent scalar values, such as strings, numbers, dates, as well as the array and map types.
+They typically manifest as columns in database tables.
+:::
+
+### simple element reference
+
+In its simplest form, a `ref` can be used to reference an element:
+
+:::code-group
+```js [CQL] {1}
+> await cds.ql`SELECT from Books { title }` // [!code focus]
+[
+ { title: 'Wuthering Heights' },
+ { title: 'Jane Eyre' },
+ { title: 'The Raven' },
+ { title: 'Eleonora' },
+ { title: 'Catweazle' }
+]
+```
+
+```sql [SQL]
+SELECT title FROM sap_capire_bookshop_Books as Books
+```
+:::
+
+In this example, we select the `title` element from the `Books` entity.
+
+### path navigation {#path-navigation}
+
+A path expression can be used to navigate to any element of the associations target:
+
+:::code-group
+```js [CQL]
+> await cds.ql`SELECT from Books { title, author.name as author }` // [!code focus]
+[
+ { title: 'Wuthering Heights', author: 'Emily Brontë' },
+ { title: 'Jane Eyre', author: 'Charlotte Brontë' },
+ { title: 'The Raven', author: 'Edgar Allen Poe' },
+ { title: 'Eleonora', author: 'Edgar Allen Poe' },
+ { title: 'Catweazle', author: 'Richard Carpenter' }
+]
+```
+
+```sql [SQL]
+SELECT
+ title,
+ author.name AS author
+FROM
+ sap_capire_bookshop_Books AS Books
+ LEFT JOIN sap_capire_bookshop_Authors AS author -- The table alias for association 'author'
+ ON author.ID = Books.author_ID;
+```
+:::
+
+In this example, we select all books together with the name of their author.
+The association `author` defined in the `Books` entity relates a book to it's author.
+
+::: warning Flattening of to-many associations
+When navigating along a to-many association to a leaf element, the result is flattened:
+
+:::code-group
+```js [CQL]
+> await cds.ql `SELECT from Authors { books.title as title, name as author }` // [!code focus]
+[
+ { title: 'Wuthering Heights', author: 'Emily Brontë' },
+ { title: 'Jane Eyre', author: 'Charlotte Brontë' },
+ { title: 'Eleonora', author: 'Edgar Allen Poe' }, // [!code focus]
+ { title: 'The Raven', author: 'Edgar Allen Poe' }, // [!code focus]
+ { title: 'Catweazle', author: 'Richard Carpenter' }
+]
+```
+
+```sql [SQL]
+SELECT
+ books.title as title,
+ name as author
+FROM sap_capire_bookshop_Authors as Authors
+ LEFT JOIN sap_capire_bookshop_Books as books
+ ON books.author_ID = Authors.ID
+```
+
+In this example, we select the book titles together with each author.
+Since books is a to-many association, we get a _joined_ result that repeats every author (name) for every associated book.
+:::
+
+Use expand to read to-many associations as structured result:
+
+::: code-group
+```js [CQL]
+> await cds.ql`SELECT from Authors {
+ name as author,
+ books { title } }` // [!code focus]
+
+[
+ { author: 'Emily Brontë', books: [ { title: 'Wuthering Heights' } ] },
+ { author: 'Charlotte Brontë', books: [ { title: 'Jane Eyre' } ] },
+ { // [!code focus]
+ author: 'Edgar Allen Poe', // [!code focus]
+ books: [ { title: 'The Raven' }, { title: 'Eleonora' } ] // [!code focus]
+ }, // [!code focus]
+ { author: 'Richard Carpenter', books: [ { title: 'Catweazle' } ] }
+]
+```
+```sql [SQL]
+SELECT Authors.name as author,
+(
+ SELECT jsonb_group_array(
+ jsonb_insert('{}', '$."title"', title, '$."genre"', genre->'$')
+ ) as _json_
+ FROM (
+ SELECT books.title, (
+ SELECT json_insert('{}', '$."name"', name) as _json_
+ FROM (
+ SELECT genre.name
+ FROM sap_capire_bookshop_Genres as genre
+ WHERE books.genre_ID = genre.ID
+ LIMIT ?
+ )
+ ) as genre
+ FROM sap_capire_bookshop_Books as books
+ WHERE Authors.ID = books.author_ID
+ )) as books
+FROM sap_capire_bookshop_Authors as Authors
+
+```
+:::
+
+::: warning Annotation expressions expect single-valued results
+When writing annotation expressions, it's often important to ensure that the result yields a single value for each entry in the annotated entity.
+To achieve this, use the [exists](#in-exists-predicate) predicate.
+:::
+
+### in `exists` predicate
+
+Path expressions can also be used after the `exists` keyword to check whether the set referenced by the path is empty.
+This is especially useful for to-many relations.
+
+E.g., to select all authors that have written **at least** one book:
+
+:::code-group
+```js [CQL]
+> await cds.ql`SELECT from Authors { name } where exists books` // [!code focus]
+
+[
+ { name: 'Emily Brontë' },
+ { name: 'Charlotte Brontë' },
+ { name: 'Edgar Allen Poe' },
+ { name: 'Richard Carpenter' }
+]
+```
+
+```sql [SQL] {3-7}
+SELECT Authors.name
+FROM sap_capire_bookshop_Authors as Authors
+WHERE exists (
+ SELECT 1
+ FROM sap_capire_bookshop_Books as books
+ WHERE books.author_ID = Authors.ID
+ )
+```
+:::
+
+[Learn more about the `exists` predicate.](./cql.md#exists-predicate){.learn-more}
+
+The `exists` predicate can be further enhanced by [combining it with infix filters](#exists-infix-filter).
+This allows you to specify conditions on subsets of associated entities, enabling more precise and expressive queries.
+
+## infix filter { #infix-filter }
+
+An infix in linguistics refer to a letter or group of letters that are added in the middle of a word to make a new word.
+
+If we apply this terminology to [path-expressions](#ref), an infix filter condition is an expression
+that is applied to a path-segment of a [path-expression](#ref).
+This allows to filter the target of an association based on certain criteria.
+
+