Search

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.


Please 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

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.

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.

When mapped to relational databases, such entity definitions are translated to tables. You can prefix an entity definition with the keyword abstract to not create a table (or view) when mapped to a database:

abstract entity Foo {...}
abstract entity Foo as SELECT from Bar {...};

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.

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.

See also: Definitions of Named Aspects

Predefined Types

Mapping to ANSI SQL types and to Edm. types given for comparison.

The following built-in types are provided.

CDS Type Arguments / Remarks SQL OData (V4)
UUID a 36-characters string NVARCHAR(36) Edm.Guid (1)
Boolean   BOOLEAN Edm.Boolean
Integer   INTEGER Edm.Int32
Integer64   BIGINT Edm.Int64
Decimal ( precision, scale ) (2) DECIMAL Edm.Decimal
DecimalFloat deprecated DECIMAL Edm.Decimal
Double   DOUBLE Edm.Double
Date   DATE Edm.Date (3)
Time   TIME Edm.TimeOfDay (4)
DateTime sec precision TIMESTAMP Edm.DateTimeOffset
Timestamp µs precision TIMESTAMP Edm.DateTimeOffset
String ( length ) NVARCHAR Edm.String
Binary ( length ) VARBINARY Edm.Binary
LargeBinary   BLOB Edm.Binary
LargeString   NCLOB Edm.String

(1) Mapping can be changed with, for example, @odata.Type='Edm.String'
(2) Optional
(3) OData V2: Edm.DateTime with sap:display-format="Date"
(4) OData V2: Edm.Time

SAP HANA-Specific Data Types

The SAP HANA-specific data types are primarily intended for porting existing SAP HANA CDS models into the CAP domain if the old SAP HANA types must be preserved in the existing database tables. If you’re starting from scratch, these types shouldn’t be used but only the predefined CDS types.

CDS Type Arguments / Remarks SQL OData (V4)
hana.SMALLINT   SMALLINT Edm.Int16
hana.TINYINT   TINYINT Edm.Byte
hana.SMALLDECIMAL   SMALLDECIMAL Edm.Decimal
hana.REAL   REAL Edm.Single
hana.CHAR ( length ) CHAR Edm.String
hana.NCHAR ( length ) NCHAR Edm.String
hana.VARCHAR ( length ) VARCHAR Edm.String
hana.CLOB   CLOB Edm.String
hana.BINARY ( length ) BINARY Edm.Binary
hana.ST_POINT ( srid ) (1) ST_POINT Edm.GeometryPoint
hana.ST_GEOMETRY ( srid ) (1) ST_GEOMETRY Edm.Geometry

(1) Optional, default: 0

Structured Types

You can declare and use custom struct types as follows:

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:

entity Books {
  price : {
    value : Decimal(10,3);
    currency : Currency;
  };
}

Arrayed Types

Prefix a type specification with array of or many to signify array types.

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. With OData V4, arrayed types are rendered as Collection in the EDM(X).

Generic support in runtimes is added over time. It’s available in Node.js but not yet in Java. If not available, you need to add custom logic, for example, to serialize payloads into JSON strings and vice versa.

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.

entity Employees {
  ...
  virtual something : String(11);
}

Default Values

As in SQL you can specify default values to fill in upon INSERTs if no value is specified for a given element.

entity Foo {
  bar : String default 'bar';
  boo : Integer default 1;
}

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.

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.

entity Employees {
  firstname: type of Author:firstname;
   lastname: Author:firstname; // optional type of
}

Constraints

Element definitions can be augmented with constraint not null as known from SQL.

entity Employees {
  name : String(111) not null;
}

Enums

You can specify enumeration values for a type as a semicolon-delimited list of symbols. String and integer enums are supported. For string types, declaration of actual values is optional; if omitted, the actual values then are the string counterparts of the symbols.

type Gender : String enum { male; female; }
entity Order {
  status : Integer enum {
    submitted =  1;
    fulfilled =  2;
    shipped   =  3;
    canceled  = -1;
  };
}


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

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.

entity Foo1 as SELECT from Bar; //> implicit {*}
entity Foo2 as SELECT from Employees { * };
entity Foo3 as SELECT from Employees LEFT JOIN Bar on Employees.ID=Bar.ID {
  foo, bar as car, sum(boo) as moo
} where exists (
  SELECT 1 as anyXY from SomeOtherEntity 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.

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.

For example, the following definition:

entity SomeView as SELECT from Employees {
  ID,
  name,
  job.title as jobTitle
};

Might result in this inferred signature:

entity SomeView {
  ID: Integer;
  name: String;
  jobTitle: String;
};

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 :.

entity SomeView ( foo: Integer, bar: Boolean )
as SELECT * from Employees where ID=:foo;

See Services - Exposed Entities to learn more about how to expose views with parameters. See Native SAP HANA Artifacts to learn more about views with parameters for existing HANA artifacts.

Associations & Compositions

Associations capture relationships between entities. They are like forward-declared joins added to a table definition in SQL.

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.

entity Employees {
  address : Association to Addresses on address.ID = address_ID;
  address_ID : Integer;  //> foreign key
}
entity Addresses {
  key ID : Integer;
}

Managed Associations

For to-one associations, CDS can automatically resolve and add requisite foreign key elements from the target’s primary keys and implicitly add respective join conditions.

entity Employees {
  address : Association to Addresses;
}

This example is equivalent to the unmanaged example above, with the foreign key element address_ID being added automatically upon activation to a SQL database.

No foreign key constraints are added on database level.

To-many Associations

For to-many associations specify an on condition following the canonical expression pattern <assoc>.<backlink> = $self as in this example:

entity Employees {
  key ID : Integer;
  addresses : Association to many Addresses
    on addresses.owner = $self;
}
entity Addresses {
  owner : Association to Employees;  //> the backlink
}

The backlink can be any managed to-one association on the many side pointing back to the one side.

Many-to-many Associations

For many-to-many association, follow the common practice of resolving logical many-to-many relationships into two one-to-many associations using a link entity to connect both. For example:

entity Employees { ...
  addresses : Association to many Addresses on addresses.emp = $self;
}
entity Emp2Addr {
  key emp : Association to Employees;
  key adr : Association to Addresses;
}

See also: Managed Compositions for Many-to-many Relationships

Compositions

Compositions constitute document structures through ‘contained-in’ relationships. They frequently show up in to-many header-child scenarios.

entity Orders {
  Items : Composition of many OrderItems on Items.parent = $self;
}
entity Orders_Items {
  key parent : Association to Orders;
  product : ...;
  quantity : ...;
}

Essentially, Compositions are the same as associations, just with the additional information that this association represents a contained-in relationship so the same syntax and rules apply in their base form.

Managed Compositions

Use managed compositions variant to nicely reflect document structures in your domain models, without the need for separate entities, reverse associations, and unmanaged on conditions.

IMPORTANT: CSN representations for managed composition aren’t stable yet. — Means: While you can use Managed Compositions as shown in the following example and transparently supported by the Node.js and Java runtimes, you shouldn’t write code that relies on the current CSN output of, for example cds compile for managed compositions.

With Inline Targets

entity Orders { ...
  Items : Composition of many { ...
    product : ...;
    quantity : ...;
  }
}

Managed compositions are automatically unfolded to the unmanaged equivalent as shown above, with the name of the unfolded entity being the concatenation of the name of the parent entity, _, and the name of the composition, that is Orders_Items in the previous example.

With Named Targets

Instead of anonymous target aspects you can also specify named types or aspects, which are unfolded the same way than anonymous inner types, as shown in the previous example:

entity Orders { ...
  Items : Composition of many OrderItems;
}
aspect OrderItems { ...
  product : Association to Products;
  quantity : Integer;
}

For Many-to-many Relationships

Managed Compositions are handy for many-to-many relationships, where a link table usually is private to one side.

entity Teams { ...
  members : Composition of many { key user: Association to Users; }
}
entity Users { ... }

And here’s an example of an attributed many-to-many relationship:

entity Teams { ...
  members : Composition of many {
    key user : Association to Users;
    role : String enum { Lead; Member; Collaborator; }
  }
}
entity Users { ... }

Annotations

This chapter describes how to add Annotations to model definitions written in CDL, focused on the common syntax options, and fundamental concepts. Find additional information in the OData Annotations guide.

Annotation Syntax

Annotations in CDL are prefixed with an @ character and can be placed before a definition, after the defined name or at the end of simple definitions.

@before entity Foo @inner {
  @before simpleElement @inner : String @after;
  @before structElement @inner { /* elements */ }
}

Multiple annotations can be placed in each spot separated by whitespaces or enclosed in @(...) and separated by comma - like the following are equivalent:

entity Foo @(
  my.annotation: foo,
  another.one: 4711
) { /* elements */ }
@my.annotation:foo
@another.one: 4711
entity Foo { /* elements */ }

For an @inner annotation, only the syntax @(...) is available.

Annotation Targets

You can basically annotate any named thing in a CDS model, such as:

Contexts and services:

@before [define] (context|service) Foo @inner { ... }

Definitions and elements with simple types:

@before [define] type Foo @inner : String @after;
@before [key] anElement @inner : String @after;

Entities, aspects, and other struct types and elements thereof:

@before [define] (entity|type|aspect|annotation) Foo @inner {
  @before simple @inner : String @after;
  @before struct @inner { ...elements... };
}

Enums:

... status : String @inner enum {
  fulfilled @after;
}

Columns in a view definition’s query:

... as SELECT from Foo {
  @before expr as alias @inner : String,
  ...
}

Parameters in view definitions:

... with parameters (
  @before param @inner : String @after
) ...

Actions/functions including their parameters and result elements:

@before action doSomething @inner (
  @before param @inner : String @after
) returns {
  @before result @inner : String @after;
};

Annotation Values

Values can be literals or references. If no value is given, the default value is true as for @aFlag in the following example.

@aFlag //= true, if no value is given
@aBoolean: false
@aString: 'foo'
@anInteger: 11
@aDecimal: 11.1
@aSymbol: #foo
@aReference: foo.bar
@anArray: [ /* can contain any kind of value */ ]

As described in the CSN spec, the previously mentioned annotations would compile to CSN as follows:

{
  "@aFlag": true,
  "@aBoolean": false,
  "@aString": "foo",
  "@anInteger": 11,
  "@aDecimal": 11.1,
  "@aSymbol": {"#":"foo"},
  "@aReference": {"=":"foo.bar"},
  "@anArray": [ ... ]
}

References (and expressions in general) aren’t checked or resolved by CDS parsers or linkers. They’re interpreted and evaluated only on consumption-specific modules. For example, for SAP Fiori models, it’s the 4odata and 2edm(x) processors.

Records as Syntax Shortcuts

Annotations in CDS are flat lists of key-value pairs assigned to a target. The record syntax - that is, {key:<value>, ...} - is a shortcut notation that applies a common prefix to nested annotations. For example, the following are equivalent:

@Common.foo.bar
@Common.foo.car: 'wheels'
@Common: { foo.bar, foo.car: 'wheels' }
@Common.foo: { bar }
@Common.foo.car: 'wheels'
@Common.foo: { bar, car: 'wheels'  }

and they would show up as follows in a parsed model (→ see CSN):

{
  "@Common.foo.bar": true,
  "@Common.foo.car": "wheels",
}

Annotation Propagation

Annotations are inherited from types and base types to derived types, entities, and elements as well as from elements of underlying entities in case of views.

For examples, given this view definition:

using Books from './bookshop-model';
entity BooksList as SELECT from Books {
  ID, genre : Genre, title,
  author.name as author
};
  • BooksList would inherit annotations from Books
  • BooksList.ID would inherit from Books.ID
  • BooksList.author would inherit from Books.author.name
  • BooksList.genre would inherit from type Genre

The rules are:

  1. Entity-level properties and annotations are inherited from the primary underlying source entity — here Books.

  2. Each element that can unambiguously be traced back to a single source element, inherits that element’s properties.

  3. An explicit cast in the select clause cuts off the inheritance, for example, as for genre in our previous example.

The annotate Directive

The annotate directive allows to annotate already existing definitions, that may have been imported from other files or projects.

annotate Foo with @title:'Foo' {
  nestedStructField {
    existingField @title:'Nested Field';
  }
}
annotate Bar with @title:'Bar';

The annotate directive a variant of the extend directive. Actually, annotate is just a shortcut with the default mode being switched to extending existing fields instead of adding new ones.


Aspects

CDS’s aspects allow to flexibly extend definitions by new elements as well as overriding properties and annotations. They’re based on a mixin approach as known from Aspect-oriented Programming methods.

The extend Directive

Use extend to add extension fields or to add/override metadata to existing definitions, for example, annotations, as follows:

extend Foo with @title:'Foo' {
  newField : String;
  extend nestedStructField {
    newField : String;
    extend existingField @title:'Nested Field';
  }
}
extend Bar with @title:'Bar'; // nothing for elements

Make sure that you prepend the extend keyword to nested elements, otherwise this would mean that you want to add a new field with that name:

See also: The annotate Directive

Named Aspects — define aspect

You can use extend or annotate with predefined aspects, to apply the same extensions to multiple targets:

extend Foo with ManagedObject;
extend Bar with ManagedObject;
aspect ManagedObject {
  created { at: Timestamp; _by: User; }
}

The define keyword is optional, that means define aspect Foo is equal to aspect Foo.

If you use extend, all nested fields in the named aspect are interpreted as being extension fields. If you use annotate, the nested fields are interpreted as existing fields and the annotations are copied to the corresponding target elements.

The named extension can be anything, for example, including other types or entities. Use keyword aspect as shown in the example to declare definitions that are only meant to be used in such extensions, not as types for elements.

Shortcut Syntax :

You can use an inheritance-like syntax option to extend a definition with one or more named aspects as follows:

define entity Foo : ManagedObject, AnotherAspect {
  key ID : Integer;
  name : String;
  ...
}

This is syntactical sugar and equivalent to using a sequence of extends as follows:

define entity Foo {}
extend Foo with ManagedObject;
extend Foo with AnotherAspect;
extend Foo with {
  key ID : Integer;
  name : String;
  ...
}

You can apply this to any definition of an entity or a structured type.

Looks Like Inheritance

The :-based syntax option described before looks very much like (multiple) inheritance and in fact has very much the same effects. Yet, as mentioned in the beginning of this chapter, it isn’t based on inheritance but on mixins, which are more powerful and also avoid common problems like the infamous diamond shapes in type derivations.

When combined with persistence mapping there are a few things to note, that goes down to which strategy to choose to map inheritance to, for example, relational models. Find some details in Aspects vs Inheritance.

Extending Views and Projections

Use the extend projection variant to extend the projection of a view entity to include more elements existing in the underlying entity:

extend projection Foo with @title:'Foo' {
  foo as bar @car
}

Enhancing nested structs isn’t supported. Note also that you can use the common annotate syntax, to just add/override annotations of a view’s elements.


Services

Service Definitions

CDS allows to define service interfaces as collections of exposed entities enclosed in a service block, which essentially is and acts the same a context:

service SomeService {
  entity SomeExposedEntity ...;
  entity AnotherExposedEntity ...;
}

The endpoint of the exposed service is constructed by its name, following some conventions (the string service is dropped and kebab-case is enforced). If you want to overwrite the path, you can add the @path annotation as follows:

@path: 'myCustomServicePath'
service SomeService { ... }

Exposed Entities

The entities exposed by a service are most frequently projections on entities from underlying data models. Standard view definitions, using as SELECT from or as projection on, can be used for exposing entities.

service CatalogService {
  entity Product as projection on data.Products {
    *, created.at as since
  } excluding { created };
}
service MyOrders {
  view Order as select from data.Orders { * } where buyer=$user.id;  //> $user not yet implemented!
  entity Product as projection on CatalogService.Product;
}

You can optionally add annotations such as @readonly or @insertonly to exposed entities, which, will be enforced by the CAP runtimes in Java and Node.js.

Entities can be also exposed as views with parameters:

service MyOrders {
  view OrderWithParameter( foo: Integer ) as select from data.Orders where id=:foo;
}

A view with parameter modeled in the previous example, can be exposed as follows:

service SomeService {
  view ViewInService( p1: Integer, p2: Boolean ) as select from data.SomeView(foo: :p1, bar: :p2) {*};
}

Then the OData request for views with parameters should look like this:

GET: /OrderWithParameter(foo=5)/Set or GET: /OrderWithParameter(5)/Set
GET: /ViewInService(p1=5, p2=true)/Set

(Auto-) Redirected Associations

When exposing related entities, associations are automatically redirected. For example:

service AdminService {
  entity Books as projection on my.Books;
  entity Authors as projection on my.Authors;
  //> AdminService.Authors.books refers to AdminService.Books
}

Auto-redirection fails if a target can’t be resolved unambiguously. In that case, use redirected to to resolve the ambiguity:

service AdminService {
  entity Books1 as projection on my.Books;
  entity Books2 as projection on my.Books;
  entity Authors as projection on my.Authors { *,
    books : redirected to Books1
  };
}

Auto-Exposed Targets

Add annotation @cds.autoexpose to automatically expose these entities whenever they’re referred to by associations from other entities exposed by a service. For example, this is frequently used to auto-expose code list entities to support value helps in UIs.

@cds.autoexpose entity SomeCodeListEntity {
  key code : String(3);
  name : String;
}
service Sue {
  entity Foo { bar : Association to SomeCodeListEntity; }
  //> SomeCodeListEntity will be auto-exposed as @readonly
}

You can still expose such entities explicitly, for example, the following would be equivalent to the service definition above:

service Sue {
  entity Foo { /*...*/ }
  @readonly entity Bar as projection on SomeCodeListEntity;
}

Custom Actions and Functions

Within service definitions, you can additionally specify actions and functions. Use a comma-separated list of named and typed inbound parameters and an optional response type, which can be either a:

service MyOrders {
  entity Order { /*...*/ };
  // unbound actions / functions
  type cancelOrderRet {
    acknowledge: String enum { succeeded; failed; };
    message: String;
  }
  action cancelOrder ( orderID:Integer, reason:String ) returns cancelOrderRet;
  function countOrders() returns Integer;
  function getOpenOrders() returns array of Order;
}

The notion of actions and functions in CDS adopts that of OData; actions and functions on service-level are unbound ones.

Bound Actions and Functions

Actions and functions can also be bound to individual entities of a service, enclosed in an additional actions block as the last clause in an entity/view definition.

service CatalogService {
  entity Products as projection on data.Products { ... }
    actions {
      // bound actions/functions
      action addRating (stars: Integer);
      function getViewsCount() returns Integer;
    }
}

Extending Services

You can extend services with additional entities and actions much as you would add new entities to a context:

extend service CatalogService with {
  entity Foo {};
  function getRatings() returns Integer;
}

Similarly, you can extend entities with additional actions as you would add new elements:

extend entity CatalogService.Products with actions {
  function getRatings() returns Integer;
}

Namespaces

The namespace Directive

To prefix the names of all subsequent definitions, place a namespace directive at the top of a model. This is comparable to other languages, like Java.

namespaces.cds

namespace foo.bar;
entity Foo {}           //> foo.bar.Foo 
entity Bar : Foo {}     //> foo.bar.Bar

The context Directive

Use contexts for nested namespace sections.

contexts.cds

namespace foo.bar;
entity Foo {}           //> foo.bar.Foo
context scoped {
  entity Bar : Foo {}   //> foo.bar.scoped.Bar
  context nested {
    entity Zoo {}       //> foo.bar.scoped.nested.Zoo
  }
}

Fully Qualified Names

A model ultimately is a collection of definitions with unique, fully qualified names. For example, the second model above would compile to this CSN:

contexts.json

{"definitions":{
  "foo.bar.Foo": { "kind": "entity" },
  "foo.bar.scoped": { "kind": "context" },
  "foo.bar.scoped.Bar": { "kind": "entity",
    "includes": [ "foo.bar.Foo" ]
  },
  "foo.bar.scoped.nested": { "kind": "context" },
  "foo.bar.scoped.nested.Zoo": { "kind": "entity" }
}}


Import Directives

The using Directive

Using directives allows to import definitions from other CDS models. As shown in line three below you can specify aliases to be used subsequently. You can import single definitions as well as several ones with a common namespace prefix. Optional: Choose a local alias.

using-from.cds

using foo.bar.scoped.Bar from './contexts';
using foo.bar.scoped.nested from './contexts';
using foo.bar.scoped.nested as specified from './contexts';

entity Car : Bar {}            //> : foo.bar.scoped.Bar 
entity Moo : nested.Zoo {}     //> : foo.bar.scoped.nested.Zoo
entity Zoo : specified.Zoo {}  //> : foo.bar.scoped.nested.Zoo

Multiple named imports through es6-like deconstructors:

using { Foo as Moo, sub.Bar } from './base-model';
entity Boo : Moo { /*...*/ }
entity Car : Bar { /*...*/ }

Also in the deconstructor variant of using shown in the previous example, specify fully qualified names.

Model Resolution

Imports in cds work very much like require in node and imports in ES6. In fact, we reuse Node’s module loading mechanisms. Hence, the same rules apply:

  • Relative path resolution
    Names starting with ./ or ../ are resolved relative to the current model.
  • Resolving absolute references
    They’re fetched for in node_modules folders:
    • Files having .cds, .csn, or .json as suffixes, appended in order
    • Folders, from either the file set in cds.main in the folder’s package.json or index.<cds|csn|json> file.

To allow for loading from precompiled .json files it’s recommended to omit .cds suffixes in import statements, as shown in the provided examples.