Definition Language (CDL)
Find here a reference of all CDS concepts and features in the form of compact
examples. The examples are given in CDL, a human-readable syntax for defining
models, and CQL, an extension of SQL to write queries.
Refer also to The Nature of Models and the CSN specification to complete your
understanding of CDS.
Entity and Type Definitions
Entity Definitions — define entity
Type Definitions — define type
Predefined Types
Structured Types
Arrayed Types
Virtual Elements
Literals
Delimited Identifiers
Calculated elements
Default Values
Type References
Constraints
Enums
Entity Definitions — define entity
Entities are structured types with named and typed elements, representing sets of
(persisted) data that can be read and manipulated using usual CRUD operations.
They usually contain one or more designated primary key elements:
cds
define entity Employees {
key ID : Integer;
name : String;
jobTitle : String;
}
The define keyword is optional, that means define entity Foo is equal to entity Foo.
Type Definitions — define type
You can declare custom types to reuse later on, for example, for elements in entity
definitions. Custom-defined types can be simple, that is derived from one of the
predefined types, structure types or Associations.
cds
define type User : String(111);
define type Amount {
value : Decimal(10,3);
currency : Currency;
}
define type Currency : Association to Currencies;
The define keyword is optional, that means define type Foo is equal to type Foo.
Learn more about Definitions of Named Aspects.
Predefined Types
See list of Built-in Types
Structured Types
You can declare and use custom struct types as follows:
cds
type Amount {
value : Decimal(10,3);
currency : Currency;
}
entity Books {
price : Amount;
}
Elements can also be specified with anonymous inline struct types. For example, the
following is equivalent to the definition of Books above:
cds
entity Books {
price : {
value : Decimal(10,3);
currency : Currency;
};
}
Arrayed Types
Prefix a type specification with array of or many to signify array types.
cds
entity Foo { emails: many String; }
entity Bar { emails: many { kind:String; address:String; }; }
entity Car { emails: many EmailAddress; }
entity Car { emails: EmailAddresses; }
type EmailAddresses : many { kind:String; address:String; }
type EmailAddress : { kind:String; address:String; }
Keywords many and array of are mere syntax variants with identical semantics and
implementations.
When deployed to SQL databases, such fields are mapped to LargeString columns
and the data is stored denormalized as JSON array. With OData V4, arrayed types are
rendered as Collection in the EDM(X).
WARNING
Filter expressions, instance-based authorization and search are not supported on
arrayed elements.
Null Values
For arrayed types the null and not null constraints apply to the members of the
collections. The default is not null indicating that the collections can't hold null values.
WARNING
An empty collection is represented by an empty JSON array. A null value is invalid for an
element with arrayed type.
In the following example the collection emails may hold members that are null. It may
also hold a member where the element kind is null. The collection emails itself must
not be null!
cds
entity Bar {
emails : many {
kind : String null;
address : String not null;
} null; // -> collection emails may hold null values, overwriting default
}
Virtual Elements
An element definition can be prefixed with modifier keyword virtual. This keyword
indicates that this element isn't added to persistent artifacts, that is, tables or views in
SQL databases. Virtual elements are part of OData metadata.
By default virtual elements are annotated with @[Link]: true, not writable for
the client and will be silently ignored. This means also, that they are not accessible in
custom event handlers. If you want to make virtual elements writable for the client,
you explicitly need to annotate these elements with @[Link]: false. Still those
elements are not persisted and therefore, for example, not sortable or filterable.
cds
entity Employees {
...
virtual something : String(11);
}
Literals
Using literals in CDS models is commonly used, for example, to set default values.
The literals in the following table show you how to define these values in your CDS
source.
Kind Example
Null null
Kind Example
Boolean true, false
Numbers 11, 2.4, or 1.34e10
Strings 'foo' or `foo` or ```foo```
Dates date'2016-11-24'
Times time'[Link]Z'
Timestamp timestamp'2016-11-24T[Link].4209753Z'
DateTime '2016-11-24T16:11Z'
Records {"foo":<literal>, ...}
Arrays [<literal>, ...]
Learn more about literals and their representation in CSN.
Multiline String Literals
String literals enclosed in single ticks, for example 'string', are limited to a single line.
Use string literals enclosed in single or triple backticks for multiline strings. Within
those strings, escape sequences from JavaScript, such as \t or \u0020, are supported.
Line endings are normalized. If you don't want a line ending at that position, end a
line with a backslash (\). Only for string literals inside triple backticks, indentation is
stripped and tagging is possible.
Examples:
cds
@documentation: ```
This is a CDS multiline string.
- The indentation is stripped.
- \u{0055}nicode escape sequences are possible,
just like common escapes from JavaScript such as
\r \t \n and more!
```
@data: ```xml
<main>
The tag is ignored by the core-compiler but may be
used for syntax highlighting, similar to markdown.
</main>
```
@escaped: `OK Emoji: \u{1f197}`
entity DocumentedEntity {
// ...
}
Delimited Identifiers
Delimited identifiers allow you to use any identifier, even containing special
characters or using a keyword.
WARNING
Special characters in identifiers or keywords as identifiers should be avoided for best
interoperability.
cds
entity ![Entity] {
bar : ![Keyword];
![with space] : Integer;
}
You can escape ] by ]], for example ![L[C]]R] which will be parsed as L[C]R.
Calculated Elements (beta)
WARNING
This is a beta feature. Beta features aren't part of the officially delivered scope that SAP
guarantees for future releases. For more information, see Important Disclaimers and
Legal Information.
Elements of entities and aspects can be specified with a calculation expression, in
which you can refer to other elements of the same entity/aspect.
Today CAP CDS only supports calculated elements with a value expression. They are
read-only, no value must be provided for them in a WRITE operation. When reading
a calculated element, the result of the expression is returned.
Calculated elements with a value expression come in two variants: "on-read" and
"on-write". The difference between them is the point in time when the expression is
evaluated.
On-read (beta)
cds
entity Employees {
firstName : String;
lastName : String;
name : String = firstName || ' ' || lastName;
name_upper = upper(name);
addresses : Association to many Addresses;
city = addresses[kind='home'].city;
}
For a calculated element with "on-read" semantics, the calculation expression is
evaluated when reading an entry from the entity. Using such a calculated element in
a query or view definition is equivalent to writing the expression directly into the
query, both with respect to semantics and to performance. In CAP, it is implemented
by replacing each occurrence of a calculated element in a query by the respective
expression.
Entity using calculated elements:
cds
entity EmployeeView as select from Employees {
name,
city
};
Equivalent entity:
cds
entity EmployeeView as select from Employees {
firstName || ' ' || lastName as name : String,
addresses[kind='home'].city as city
};
Calculated elements "on-read" are a pure convenience feature. Instead of having to
write the same expression several times in queries, you can define a calculated
element once and then simply refer to it.
In the definition of a calculated element "on-read", you can use almost all expressions
that are allowed in queries. Some restrictions apply:
Subqueries are not allowed.
Nested projections (inline/expand) are not allowed.
A calculated element can't be key.
A calculated element can be used in every location where an expression can occur. A
calculated element can't be used in the following cases:
in the ON condition of an unmanaged association
as the foreign key of a managed association
in a query together with nested projections (inline/expand)
Temporary Restriction in the [Link] Runtime
Currently, an OData request or a custom query can't directly access a calculated element
in the entity where it is defined. It must always be accessed using a view/projection.
On-write (beta)
Calculated elements "on-write" (also referred to as "stored" calculated elements) are
defined by adding the keyword stored. A type specification is mandatory.
cds
entity Employees {
firstName : String;
lastName : String;
name : String = (firstName || ' ' || lastName) stored;
}
For a calculated element "on-write", the expression is already evaluated when an
entry is written into the database. The resulting value is then stored/persisted like a
regular field, and when reading from the entity, it behaves like a regular field as well.
Using a stored calculated element can improve performance, in particular when it's
used for sorting or filtering. This is paid for by higher memory consumption.
While calculated elements "on-read" are handled entirely by CAP, the "on-write"
variant is implemented by using the corresponding feature for database tables. The
previous entity definition results in the following table definition:
sql
-- SAP HANA syntax --
CREATE TABLE Employees (
firstName NVARCHAR,
lastName NVARCHAR,
name NVARCHAR GENERATED ALWAYS AS (firstName || ' ' || lastName)
);
For the definition of calculated elements on-write, all the on-read variant's
restrictions apply and referencing localized elements isn't allowed. In addition, there
are restrictions that depend on the particular database. Currently all databases
supported by CAP have a common restriction: The calculation expression may only
refer to fields of the same table row. Therefore, such an expression must not contain
subqueries, aggregate functions, or paths with associations.
No restrictons apply for reading a calculated element on-write.
Default Values
As in SQL you can specify default values to fill in upon INSERTs if no value is specified
for a given element.
cds
entity Foo {
bar : String default 'bar';
boo : Integer default 1;
}
Default values can also be specified in custom type definitions:
cds
type CreatedAt : Timestamp default $now;
type Complex {
real : Decimal default 0.0;
imag : Decimal default 0.0;
}
Type References
If you want to base an element's type on another element of the same structure, you
can use the type of operator.
cds
entity Author {
firstname : String(100);
lastname : type of firstname; // has type "String(100)"
}
For referencing elements of other artifacts, you can use the element access through :.
Element references with : don't require type of in front of them.
cds
entity Employees {
firstname: Author:firstname;
lastname: Author:lastname;
}
Constraints
Element definitions can be augmented with constraint not null as known from SQL.
cds
entity Employees {
name : String(111) not null;
}
Enums
You can specify enumeration values for a type as a semicolon-delimited list of
symbols. For string types, declaration of actual values is optional; if omitted, the
actual values are the string counterparts of the symbols.
cds
type Gender : String enum { male; female; non_binary = 'non-binary'; }
entity Order {
status : Integer enum {
submitted = 1;
fulfilled = 2;
shipped = 3;
canceled = -1;
};
}
To enforce your enum values during runtime, use the @[Link] annotation. For
localization of enum values, model them as code list.
Views and Projections
Use as select from or as projection on to derive new entities from existing ones by
projections, very much like views in SQL. When mapped to relational databases, such
entities are in fact translated to SQL views but they're frequently also used to declare
projections without any SQL views involved.
The entity signature is inferred from the projection.
The as select from Variant
The as projection on Variant
Views with Inferred Signatures
Views with Parameters
The as select from Variant
Use the as select from variant to use all possible features an underlying relational
database would support using any valid CQL query including all query clauses.
cds
entity Foo1 as SELECT from Bar; //> implicit {*}
entity Foo2 as SELECT from Employees { * };
entity Foo3 as SELECT from Employees LEFT JOIN Bar on [Link]=[Link] {
foo, bar as car, sum(boo) as moo
} where exists (
SELECT 1 as anyXY from SomeOtherEntity as soe where soe.x = y
)
group by foo, bar
order by moo asc;
The as projection on Variant
Use the as projection on variant instead of as select from to indicate that you don't use
the full power of SQL in your query. For example, having a restricted query in an
entity allows us to serve such an entity from external OData services.
cds
entity Foo as projection on Bar {...}
Currently the restrictions of as projection on compared to as select from are:
no explicit, manual JOINs
no explicit, manual UNIONs
no sub selects in from clauses
Over time, we can add additional checks depending on specific outbound protocols.
Views with Inferred Signatures
By default views inherit all properties and annotations from their primary underlying
base entity. Their elements signature is inferred from the projection on base elements.
Each element inherits all properties from the respective base element, except
the key property. The key property is only inherited if all of the following applies:
No explicit key is set in the query.
All key elements of the primary base entity are selected (for example, by
using *).
No path expression with a to-many association is used.
No union, join or similar query construct is used.
For example, the following definition:
cds
entity SomeView as SELECT from Employees {
ID,
name,
[Link] as jobTitle
};
Might result in this inferred signature:
cds
entity SomeView {
key ID: Integer;
name: String;
jobTitle: String;
};
Note: CAP does not enforce uniqueness for key elements of a view or projection.
Use a CDL cast to set an element's type, if one of the following conditions apply:
You don't want to use the inferred type.
The query column is an expression (no inferred type is computed).
cds
entity SomeView as SELECT from Employees {
ID : Integer64,
name : LargeString,
'SAP SE' as company : String
};
TIP
By using a cast, annotations and other properties are inherited from the provided type
and not the base element, see Annotation Propagation
Views with Parameters
You can equip views with parameters that are passed in whenever that view is
queried. Default values can be specified. Refer to these parameters in the view's
query using the prefix :.
cds
entity SomeView ( foo: Integer, bar: Boolean )
as SELECT * from Employees where ID=:foo;
Learn more about how to expose views with parameters in Services - Exposed
[Link] more about views with parameters for existing HANA artifacts
in Native SAP HANA Artifacts.
Associations & Compositions
Associations capture relationships between entities. They are like forward-declared
joins added to a table definition in SQL.
Unmanaged Associations
Managed Associations
To-many Associations
Many-to-many Associations
Compositions
Managed Compositions
Unmanaged Associations
Unmanaged associations specify arbitrary join conditions in their on clause, which
refer to available foreign key elements. The association's name ( address in the
following example) is used as the alias for the to-be-joined target entity.