Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion advanced/hana.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ Steps to match the signature of a database object in a facade entity:
* For a view, table function, or calculation view with parameters, check that the parameter names and types match, too.
Functions with table-like input parameters are not supported.

> Note: If a field of that entity is defined as `not null` and you want to disable its runtime check, you can add [`@assert.notNull: false`](../guides/providing-services#assert-notnull). This is important if you want to use, for example [SAP HANA history tables](https://help.sap.com/docs/SAP_HANA_PLATFORM/6b94445c94ae495c83a19646e7c3fd56/d0b2c5142a19405fb912f71782cd0a84.html).
> Note: If a field of that entity is defined as `not null` and you want to disable its runtime check, you can add `@assert.notNull: false`. This is important if you want to use, for example [SAP HANA history tables](https://help.sap.com/docs/SAP_HANA_PLATFORM/6b94445c94ae495c83a19646e7c3fd56/d0b2c5142a19405fb912f71782cd0a84.html).


As a result, the database name is defined by the name of the entity or its elements, after applying the SQL name mapping.
Expand Down
6 changes: 0 additions & 6 deletions cds/annotations.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,6 @@ uacp: Used as link target from Help Portal at https://help.sap.com/products/BTP/

| Annotation | Description |
|---------------|------------------------------------------------------------------|
| `@readonly` | see [Input Validation](../guides/providing-services#readonly) |
| `@insertonly` | see [Generic Handlers](../guides/providing-services) |
| `@restrict` | see [Authorization](../guides/security/authorization#restrict-annotation) |
| `@requires` | see [Authorization](../guides/security/authorization#requires) |

Expand All @@ -40,12 +38,9 @@ uacp: Used as link target from Help Portal at https://help.sap.com/products/BTP/
|---------------------|----------------------------------------------------------------------|
| `@readonly ` | see [Input Validation](../guides/providing-services#readonly) |
| `@mandatory` | see [Input Validation](../guides/providing-services#mandatory) |
| `@assert.unique` | see [Input Validation](../guides/providing-services#assert-unique) |
| `@assert.integrity` | see [Input Validation](../guides/databases#database-constraints) |
| `@assert.target` | see [Input Validation](../guides/providing-services#assert-target) |
| `@assert.format` | see [Input Validation](../guides/providing-services#assert-format) |
| `@assert.range` | see [Input Validation](../guides/providing-services#assert-range) |
| `@assert.notNull` | see [Input Validation](../guides/providing-services#assert-notnull) |



Expand Down Expand Up @@ -102,4 +97,3 @@ Intrinsically supported OData Annotations:
| `@Core.IsMediaType` | see [Media Data](../guides/providing-services#serving-media-data) |
| `@Core.IsUrl` | see [Media Data](../guides/providing-services#serving-media-data) |
| `@Capabilities...` | see [Fiori](../advanced/fiori) |
| `@Common.FieldControl` | see [Input Validation](../guides/providing-services#common-fieldcontrol) |
102 changes: 71 additions & 31 deletions guides/databases.md
Original file line number Diff line number Diff line change
Expand Up @@ -784,6 +784,46 @@ Find here a collection of resources on selected databases and their reference do

## Database Constraints

### Not Null

You can specify that a column's value must not be `NULL` by adding the [`not null` constraint](../cds/cdl#null-values) to the element, for example:

```cds
entity Books {
key ID: Integer;
title: String not null;
}
```

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we mention that key elements are implicitly not null?

If an element is defined in a view as not null this is not enforced by the DB, I think. Same applies to calculated elements on write.

Moreover not null is a guarantee that the element is never null.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If an element is defined in a view as not null

How does one achieve this? Is that even possible?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I checked and have the impression that it's not possible.

But then, the question arises: How do I express that a virtual/calculated/@CoreComputed element is guaranteed to not be null?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have double-checked with @stewsk - you can actually define a virtual element, which is not null:

type NotNullableString : String not null;

view V as select from E { *, 'x' as nns : NotNullableString };

this is not possible, however:

view V as select from E { *, 'x' as nns : String not null };


### Unique

Annotate an entity with `@assert.unique.<constraintName>`, specifying one or more element combinations to enforce uniqueness checks on all CREATE and UPDATE operations. For example:

```cds
@assert.unique: {
locale: [ parent, locale ],
timeslice: [ parent, validFrom ],
}
entity LocalizedTemporalData {
key record_ID : UUID; // technical primary key
parent : Association to Data;
locale : String;
validFrom : Date;
validTo : Date;
}
```
{.indent}

The value of the annotation is an array of paths referring to elements in the entity. These elements may be of a scalar type, structs, or managed associations. Individual foreign keys or unmanaged associations are not supported.

If structured elements are specified, the unique constraint will contain all columns stemming from it. If the path points to a managed association, the unique constraint will contain all foreign key columns stemming from it.
::: tip
You don't need to specify `@assert.unique` constraints for the primary key elements of an entity as these are automatically secured by a SQL `PRIMARY KEY` constraint.
:::

### Foreign Keys

The information about foreign key relations contained in the associations of CDS models can be used to generate foreign key constraints on the database tables. Within CAP, referential consistency is established only at commit. The ["deferred" concept for foreign key constraints](https://www.sqlite.org/foreignkeys.html) in SQL databases allows the constraints to be checked and enforced at the time of the [COMMIT statement within a transaction](https://www.sqlite.org/lang_transaction.html) rather than immediately when the data is modified, providing more flexibility in maintaining data integrity.

Enable generation of foreign key constraints on the database with:
Expand Down Expand Up @@ -906,7 +946,7 @@ TODO: remove the above with cds9
The `@sap/cds-compiler` and all CAP Node.js database services come with out of the box support for common OData functions.

::: warning Case Sensitivity
The OData function mappings are case-sensitive and must be written as in the list below.
The OData function mappings are case-sensitive and must be written as in the list below.
:::

Assuming you have the following entity definition:
Expand Down Expand Up @@ -940,58 +980,58 @@ For example, `startsWith` instead of `startswith` will be passed as-is to the da

#### String Functions

- `concat(x, y, ...)`
- `concat(x, y, ...)`
Concatenates the given strings or numbers.

- `trim(x)`
- `trim(x)`
Removes leading and trailing whitespaces.

- `contains(x, y)`
- `contains(x, y)`
Checks whether `y` is contained in `x` (case-sensitive).

- `startswith(x, y)`
- `startswith(x, y)`
Checks whether `y` starts with `x` (case-sensitive).

- `endswith(x, y)`
- `endswith(x, y)`
Checks whether `y` ends with `x` (case-sensitive).

- `matchespattern(x, y)`
- `matchespattern(x, y)`
Checks whether `x` matches the regular expression `y`.

- `indexof(x, y)` <sup>1</sup>
- `indexof(x, y)` <sup>1</sup>
Returns the index of the first occurrence of `y` in `x` (case-sensitive).

- `substring(x, i, n?)` <sup>1</sup>
- `substring(x, i, n?)` <sup>1</sup>
Extracts a substring from `x` starting at index `i` (0-based) with an optional length `n`.

| Parameter | Positive | Negative | Omitted
| --- | --- | --- | -- |
| `i` | starts at index `i` | starts `i` positions before the end |
| `n` | extracts `n` characters | invalid | extracts until the end of the string

- `length(x)`
- `length(x)`
Returns the length of the string `x`.

- `tolower(x)`
- `tolower(x)`
Converts all characters in `x` to lowercase.

- `toupper(x)`
- `toupper(x)`
Converts all characters in `x` to uppercase.

> <sup>1</sup> These functions work zero-based. For example, `substring('abcdef', 1, 3)` returns 'bcd'

#### Numeric Functions

- `ceiling(x)`
- `ceiling(x)`
Rounds the numeric parameter up to the nearest integer.

- `floor(x)`
- `floor(x)`
Rounds the numeric parameter down to the nearest integer.

- `round(x)`
Rounds the numeric parameter to the nearest integer.
- `round(x)`
Rounds the numeric parameter to the nearest integer.
The midpoint between two integers is rounded away from zero (e.g., `0.5` → `1` and `-0.5` → `-1`).

::: warning `round` function with more than one argument
Note that most databases support `round` functions with multiple arguments,
the second parameter being the precision. SAP HANA even has a third argument which is the rounding mode.
Expand All @@ -1000,24 +1040,24 @@ For example, `startsWith` instead of `startswith` will be passed as-is to the da

#### Date and Time Functions

- `year(x)`, `month(x)`, `day(x)`, `hour(x)`, `minute(x)`, `second(x)`
- `year(x)`, `month(x)`, `day(x)`, `hour(x)`, `minute(x)`, `second(x)`
Extracts and returns specific date / time parts as integer value from a given `cds.DateTime`, `cds.Date`, or `cds.Time`.

- `time(x)`, `date(x)`
- `time(x)`, `date(x)`
Extracts and returns a time or date from a given `cds.DateTime`, `cds.Date`, or `cds.Time`.

- `fractionalseconds(x)`
- `fractionalseconds(x)`
Returns a `Decimal` representing the fractional seconds for a given `cds.Timestamp`.

- `maxdatetime()`
- `maxdatetime()`
Returns the latest possible point in time: `'9999-12-31T23:59:59.999Z'`.

- `mindatetime()`
- `mindatetime()`
Returns the earliest possible point in time: `'0001-01-01T00:00:00.000Z'`.

#### Aggregate Functions

- `min(x)`, `max(x)`, `sum(x)`, `average(x)`, `count(x)`, `countdistinct(x)`
- `min(x)`, `max(x)`, `sum(x)`, `average(x)`, `count(x)`, `countdistinct(x)`
Standard aggregate functions used to calculate minimum, maximum, sum, average, count, and distinct count of values.


Expand All @@ -1030,30 +1070,30 @@ out of the box support for some common SAP HANA functions, to further increase t
For the SAP HANA functions, both usages are allowed: all-lowercase as given above, as well as all-uppercase.
:::

- `years_between`
- `years_between`
Computes the number of years between two specified dates. ([link](https://help.sap.com/docs/hana-cloud-database/sap-hana-cloud-sap-hana-database-sql-reference-guide/years-between-function-datetime?locale=en-US))
- `months_between`
- `months_between`
Computes the number of months between two specified dates. ([link](https://help.sap.com/docs/hana-cloud-database/sap-hana-cloud-sap-hana-database-sql-reference-guide/months-between-function-datetime?locale=en-US))
- `days_between`
- `days_between`
Computes the number of days between two specified dates. ([link](https://help.sap.com/docs/hana-cloud-database/sap-hana-cloud-sap-hana-database-sql-reference-guide/days-between-function-datetime?locale=en-US))
- `seconds_between`
- `seconds_between`
Computes the number of seconds between two specified dates. ([link](https://help.sap.com/docs/hana-cloud-database/sap-hana-cloud-sap-hana-database-sql-reference-guide/seconds-between-function-datetime?locale=en-US))
- `nano100_between`
- `nano100_between`
Computes the time difference between two dates to the precision of 0.1 microseconds. ([link](https://help.sap.com/docs/hana-cloud-database/sap-hana-cloud-sap-hana-database-sql-reference-guide/nano100-between-function-datetime?locale=en-US))

### Special Runtime Functions

In addition to the OData and SAP HANA standard functions, the **CAP runtimes** provides special functions that are only available for runtime queries:

- `search(x, y)`
- `search(x, y)`
Checks whether `y` is contained in any element of `x` (fuzzy matching may apply).
See [Searching Data](../guides/providing-services#searching-data) for more details.

- `session_context(<var>)`
- `session_context(<var>)`
Utilizes standard variable names to maintain session context.
Refer to [Session Variables](#session-variables) for additional information.

- `now()`
- `now()`
Returns the current timestamp.

## Using Native Features { #native-db-functions}
Expand Down
110 changes: 38 additions & 72 deletions guides/providing-services.md
Original file line number Diff line number Diff line change
Expand Up @@ -736,7 +736,7 @@ The records are locked until the end of the transaction by commit or rollback st
Here's an overview table:

| State | Select Without Lock | Select With Shared Lock | Select With Exclusive Lock/Update |
| --------------- | ----------------------- | -------------------------- | ------------------------------------- |
| --------------- | ----------------------- | -------------------------- | ------------------------------------- |
| not locked | passes | passes | passes |
| shared lock | passes | passes | waits |
| exclusive lock | passes | waits | waits |
Expand Down Expand Up @@ -772,6 +772,7 @@ Do not use the `@readonly` annotation on keys in all variants.

<div id="readonlywithexpressions"/>


### `@mandatory`

Elements marked with `@mandatory` are checked for nonempty input: `null` and (trimmed) empty strings are rejected.
Expand All @@ -795,42 +796,49 @@ In addition to server-side input validation as introduced above, this adds a cor

<div id="mandatorywithexpressions"/>

### `@Common.FieldControl`
{#common-fieldcontrol}

The input validation for `@Common.FieldControl: #Mandatory` and `@Common.FieldControl: #ReadOnly` is done from the CAP runtimes automatically.
::: warning
Custom validations are required when using static or dynamic numeric values, for example, `@Common.FieldControl: 1` or `@Common.FieldControl: integer_field`.
:::
### `@assert .format`

Allows you to specify a regular expression string (in ECMA 262 format in CAP Node.js and java.util.regex.Pattern format in CAP Java) that all string input must match.

```cds
entity Foo {
bar : String @assert.format: '[a-z]ear';
}
```

### `@assert .unique`

Annotate an entity with `@assert.unique.<constraintName>`, specifying one or more element combinations to enforce uniqueness checks on all CREATE and UPDATE operations. For example:
### `@assert .range`

Allows you to specify `[ min, max ]` ranges for elements with ordinal types &mdash; that is, numeric or date/time types. For `enum` elements, `true` can be specified to restrict all input to the defined enum values.

```cds
@assert.unique: {
locale: [ parent, locale ],
timeslice: [ parent, validFrom ],
}
entity LocalizedTemporalData {
key record_ID : UUID; // technical primary key
parent : Association to Data;
locale : String;
validFrom : Date; validTo : Date;
entity Foo {
bar : Integer @assert.range: [ 0, 3 ];
boo : Decimal @assert.range: [ 2.1, 10.25 ];
car : DateTime @assert.range: ['2018-10-31', '2019-01-15'];
zoo : String @assert.range enum { high; medium; low; };
}
```
{.indent}
#### ... with open intervals

This annotation is applicable to entities, which result in tables in SQL databases only.
By default, specified `[min,max]` ranges are interpreted as closed intervals, that means, the performed checks are `min ≤ input ≤ max`. You can also specify open intervals by wrapping the *min* and/or *max* values into parenthesis like that:

The value of the annotation is an array of paths referring to elements in the entity. These elements may be of a scalar type, structs, or managed associations. Individual foreign keys or unmanaged associations are not supported.
<!-- cds-mode: ignore; duplicate annotations -->
```cds
@assert.range: [(0),100] // 0 < input ≤ 100
@assert.range: [0,(100)] // 0 ≤ input < 100
@assert.range: [(0),(100)] // 0 < input < 100
```
In addition, you can use an underscore `_` to represent *Infinity* like that:
<!-- cds-mode: ignore; duplicate annotations -->
```cds
@assert.range: [(0),_] // positive numbers only, _ means +Infinity here
@assert.range: [_,(0)] // negative number only, _ means -Infinity here
```
> Basically values wrapped in parentheses _`(x)`_ can be read as _excluding `x`_ for *min* or *max*. Note that the underscore `_` doesn't have to be wrapped into parenthesis, as by definition no number can be equal to *Infinity* .

If structured elements are specified, the unique constraint will contain all columns stemming from it. If the path points to a managed association, the unique constraint will contain all foreign key columns stemming from it.
::: tip
You don't need to specify `@assert.unique` constraints for the primary key elements of an entity as these are automatically secured by a SQL `PRIMARY KEY` constraint.
:::
Support for open intervals and infinity is available for CAP Node.js since `@sap/cds` version **8.5** and in CAP Java since version **3.5.0**.



Expand Down Expand Up @@ -911,59 +919,15 @@ Cross-service checks are not supported. It is expected that the associated entit
The `@assert.target` check constraint relies on database locks to ensure accurate results in concurrent scenarios. However, locking is a database-specific feature, and some databases don't permit to lock certain kinds of objects. On SAP HANA, for example, views with joins or unions can't be locked. Do not use `@assert.target` on such artifacts/entities.
:::

### `@assert .format`

Allows you to specify a regular expression string (in ECMA 262 format in CAP Node.js and java.util.regex.Pattern format in CAP Java) that all string input must match.

```cds
entity Foo {
bar : String @assert.format: '[a-z]ear';
}
```

### `@assert .range`

Allows you to specify `[ min, max ]` ranges for elements with ordinal types &mdash; that is, numeric or date/time types. For `enum` elements, `true` can be specified to restrict all input to the defined enum values.

```cds
entity Foo {
bar : Integer @assert.range: [ 0, 3 ];
boo : Decimal @assert.range: [ 2.1, 10.25 ];
car : DateTime @assert.range: ['2018-10-31', '2019-01-15'];
zoo : String @assert.range enum { high; medium; low; };
}
```
#### ... with open intervals

By default, specified `[min,max]` ranges are interpreted as closed intervals, that means, the performed checks are `min ≤ input ≤ max`. You can also specify open intervals by wrapping the *min* and/or *max* values into parenthesis like that:

<!-- cds-mode: ignore; duplicate annotations -->
```cds
@assert.range: [(0),100] // 0 < input ≤ 100
@assert.range: [0,(100)] // 0 ≤ input < 100
@assert.range: [(0),(100)] // 0 < input < 100
```
In addition, you can use an underscore `_` to represent *Infinity* like that:
<!-- cds-mode: ignore; duplicate annotations -->
```cds
@assert.range: [(0),_] // positive numbers only, _ means +Infinity here
@assert.range: [_,(0)] // negative number only, _ means -Infinity here
```
> Basically values wrapped in parentheses _`(x)`_ can be read as _excluding `x`_ for *min* or *max*. Note that the underscore `_` doesn't have to be wrapped into parenthesis, as by definition no number can be equal to *Infinity* .
<div id="assertconstraints" />

Support for open intervals and infinity is available for CAP Node.js since `@sap/cds` version **8.5** and in CAP Java since version **3.5.0**.

### `@assert .notNull`
### Database Constraints

Annotate a property with `@assert.notNull: false` to have it ignored during the generic not null check, for example if your persistence fills it automatically.
Next to input validation, you can add [database constraints](databases#database-constraints) to prevent invalid data from being persisted.

```cds
entity Foo {
bar : String not null @assert.notNull: false;
}
```

<div id="assertconstraints" />

## Custom Logic

Expand Down Expand Up @@ -1089,6 +1053,8 @@ service Sue {
action order (x:Integer) returns Integer;
//bound to the collection and not a specific instance of Foo
action customCreate (in: many $self, x: String) returns Foo;
// All parameters are optional by default, unless marked with `not null`:
action discard (reason: String not null);
}
}
```
Expand Down