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.


Entities, Views

Entities

Entities are structured types representing sets of (persisted) data that can be read and manipulated using CRUD operations. They usually contain primary key elements. The leading define is optional.

entity Employees {
  key ID : Integer;
  name : String;
  jobTitle : String;
}

The simplest entity definition is:

entity Foo {};

Views

Views are entities defined by projection on underlying entities/views, like views in SQL. The element signatures are inferred from the projection. The projection can be any valid query.

entity SomeView as SELECT from Employees { * };
entity Foo as SELECT from Bar; //> implicit {*}

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, a view and its inferred signature:

View

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

Signature

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

Views with Declared Signatures

You can optionally declare the expected signature explicitly. This declaration overrides the inferred signature. The implementation can check the inferred signature against the declared one.

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

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;

Abstract Entities

You can prefix an entity definition with the keyword abstract. This keyword indicates that this entity shouldn’t have instances, that is, just an entity type declaration without an entity set. When activated to a database, no persistence artifacts, that are tables and views in SQL, are created.

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

Entity as Projection On

You can use as projection on instead of as SELECT from to indicate restrictions on the allowed expressions of the following query.

entity Foo as projection on Bar {...}

This is to support custom implementations in parsing the query, which would add respective checks. The core cds parser and compiler modules themselves don’t make any assumptions nor about restrictions on allowed expressions neither about when and where as projection on is allowed instead of as SELECT from.

Elements, Types

Predefined Types

The following built-in types are provided. Mapping to ANSI SQL types and to Edm. types given for comparison.

CDS Type Arguments / Remarks SQL OData (v4)
UUID a 36-characters string varchar(36) Edm.Guid (1)
Boolean   boolean Edm.Boolean
Integer   integer Edm.Int32
Integer64   bigint Edm.Int64
Decimal ( precision, scale ) decimal Edm.Decimal
DecimalFloat   decimal Edm.Decimal
Double   double Edm.Double
Date   datetime Edm.Date (2)
Time   datetime Edm.TimeOfDay (3)
DateTime sec precision datetime Edm.DateTimeOffset (4)
Timestamp µs precision timestamp Edm.DateTimeOffset (4)
String ( length ) nvarchar Edm.String
Binary ( length ) varbinary Edm.Binary
LargeString   NCLOB Edm.String
LargeBinary   BLOB Edm.Binary

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

Custom-Defined Types

You can declare custom types to reuse later on, for example, for elements in entity definitions.

type User : String(111);
type Amount {
  value : Decimal(10,3);
  currency : Currency;
}
type Currency : Association to Currencies;

entity Order {
  buyer : User;
  price : Amount;
}

Struct Elements

Elements can be specified with anonymous inline struct types.

entity Order {
  buyer : String(111);
  price {
    value : Decimal(10,3);
    currency : Currency;
  };
}

Enums

You can specify enumeration values for a type as a semicolon-delimited list of symbols. For type String, 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;
  };
}

Calculated Fields

Elements can be specified with a calculation expression in which you can refer to other elements of the same entity.

entity Employees {
  addresses : Association of many Addresses;
  homeAddress = addresses [kind='home'];
}

Virtual Elements

An element definition can be prefixed with modifier keyword virtual. This keyword indicates that this element is not be added to persistent artifacts, that is, tables or views in SQL databases. The reason to declare virtual elements is to be able to add metadata.

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

Element Constraints

Element definitions can be augmented with constraints unique and not null as known from SQL.

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

Note: unique isn’t yet available.


Associations

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

Unmanaged

Unmanaged associations specify arbitrary join conditions in their on clause, which refer to available foreign key elements. The association’s name (address in the example below) 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 to-one

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

Managed to-many

For one-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.

Managed many-to-many

For many-to-many associations, CDS can generate requisite link tables. You can use the via parameter clause to add elements to link table reflecting attributed relationships or to use a predefined link table instead.

entity Employees {
  addresses1 : Association to many Addresses;
  addresses2 : Association to many Addresses via {
    kind: String(11);
  }
  addresses3 : Association to many Addresses via Emp2Addr;
}

For the first case, cds.compile automatically adds a link table. For the second case it automatically adds a link table with an additional element kind (→ an attributed relationship). For the third case, cds.compile uses the predefined entity Emp2Addr that is defined like that (names for source/target can be freely chosen):

entity Emp2Addr {
  key source : Association to Employees;
  key target : Association to Addresses;
}

With Default Filters

For to-many associations, you can optionally specify a default filter. That filter automatically applies to any usage of that association in queries, unless another filter is specified explicitly.

entity Products {
  localized : Association to many Product$Texts
    with default filter lang=$env.user.lang;
}
entity Product$Texts {
  key product : Association to Products;
  key lang : String(3);
  title : String(44);
  descr : String(444);
}

To Parameterized Views

If the target is a parameterized view, you can specify corresponding arguments in an Association definition as follows:

entity Products {
  assoc : Association to SomeParameterizedView (
    param1: 4711,
    param2: foo
  );
  foo : String;
}

The argument values for parameters are literals or expressions in which references are resolved within the current entity’s elements.

Compositions

Compositions are the same as associations just with the additional information that this association represents a contained-in relationship. Compositions frequently show up in to-many header-child scenarios.

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

Compositions of Inner Types

A managed syntactical sugar variant allows you to write such compositions in a (anonymous) inner child way:

entity Orders {
  Items : Composition of many OrderItems { ... }
  Items : Composition of many { ... }
}

Such a Composition is automatically unfolded to the equivalent as shown in the former example. If the inner struct is anonymous, it is constructed as OrdersItems in the example above.

Compositions of Named Types or Facets

Instead of an inner type you can also specify a named type or facet, which is unfolded the same way than anonymous inner types, as shown above:

entity Products {
  offeredQuantities : Composition of many Quantity;
}
type Quantity {
  value : Decimal(10,2);
  unit : String(22);
}


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 ’@’ sign and can be placed before a definition, after the defined name or at the end of simple definitions.

@before entity Foo @inner {
  @before simple @inner : String @after;
  @before struct @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, facets, and other struct types and elements thereof:

@before [define] (entity|type|facet|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 below.

@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 above annotations would compile to CSN as follows:

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

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

Records are 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' }
//> not yet implemented!
@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
};

The rules are:

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

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

Rule #3…unless an explicit cast in the select clause cuts them off, e.g. as for genre in our example above.


Aspects

Extend Entity

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

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

Annotate

Use annotate instead of extend if you only want to add/override annotations:

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

This example is effectively the same as the above one for extend without the extension fields added. Actually, annotate is just a shortcut with the default mode being switched to extending existing fields instead of adding new ones.

Extend View

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

extend view Foo with @title:'Foo' {
  foo as bar @car,
  <expression> as jar
}

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

Named Aspects

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

extend Foo with ManagedObject;
extend Bar with ManagedObject;
aspect ManagedObject {
  created { at: DateTime; by: User; }
}

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. You can use aspects 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 essentially 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.

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 ...;
}

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;
}

Auto-Promoted Associations

TODO

Actions / 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 / 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;
    }
}

Derived Services

You can define abstract services and inherit from it in other service definitions as in this example:

abstract service ShoppingService {
  abstract entity Articles {...}
  entity Suppliers {...}
  entity ShoppingCart {} actions {
    submitOrder();
  }
}
service Bookshop : ShoppingService {
  entity Books : ShoppingService.Articles {
    author : Association to Authors;
  }
  entity Authors {...}
}

Extend 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

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

Contexts

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

using Directives

Using directives allows to import definitions from other CDS models. As shown in line 3 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 via es6-like deconstructors:

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

Note: also in the deconstructor variant of using shown above, specify fully qualified names.

import Directives

The import directive extends the using directive to fully support syntax and semantics of import statements in ES6. In particular…

Imported names may omit the target’s namespace prefix:

import {Foo} from './base-model';

Multiple named imports via es6-like deconstructors:

import { Foo as Moo, scoped.Bar } from './base-model';
entity Boo : Moo;
entity Car : Bar;

Imports with locally chosen prefixes (independent from target namespaces):

import base from './base-model';
entity Foo : base.Foo;
entity Bar : base.scoped.Bar;

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; in short:

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