Skip to content
Open
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
104 changes: 67 additions & 37 deletions guides/databases/cdl-to-ddl.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,24 @@ Databases are deployed based on the entity definitions in your CDS models. This

## Using `cds compile`, ...

CDS compilation to database-specific DDLs is handled by the `cds compile` command, which is part of the [`cds` CLI](../../tools/cds-cli). When you run `cds deploy` or `cds watch`, this command is invoked automatically to generate the necessary DDL statements for your target database.
CDS compilation to database-specific DDLs is handled by the [`cds compile`](../../tools/cds-cli#cds-compile) command, which is part of the [`cds` CLI](../../tools/cds-cli). When you run `cds deploy` or `cds watch`, this command is invoked automatically to generate the necessary DDL statements for your target database.

In CAP Java, the DDL is generated by the [Maven build](../../java/developing-applications/building#maven-build-options) which uses the [CDS Maven Plugin](../../java/developing-applications/building#cds-maven-plugin)'s [cds](../../java/assets/cds-maven-plugin-site/cds-mojo.html) goal to invoke `cds deploy --dry`.

:::details
A build configuration to create the DDL specific for H2:

```xml
<execution>
<goals><goal>cds</goal></goals>
<configuration>
<commands>
<command>deploy --to h2 --dry --out "${project.basedir}/src/main/resources/schema-h2.sql"</command>
</commands>
</configuration>
</execution>
```
:::

You can also run the command manually to see the generated DDL for your models. For example, to inspect what the SQL DDL for your entire model would look like, simply run:

Expand Down Expand Up @@ -61,31 +78,9 @@ code --diff _out/c/sqlite.sql _out/c/h2.sql
> CDS models are designed to be database-agnostic, allowing you to switch between different databases with minimal changes. The `--dialect` option helps you see how your models translate to different database-specific DDLs. \


### Dialects by `cds env` Profiles

The dialect is automatically inferred from your project configuration, and the current profile, so you typically don't need to specify it explicitly. For example, if your project is configured to use SAP HANA in production and SQLite in development, the respective dialects will be applied automatically.
Try this out using the `--profile` option:

```shell
cds compile \* --to sql --profile development
cds compile \* --to sql --profile production
```

::: details Use `cds env` to check your effective configurations:
```shell
cds env requires.db --profile development
cds env requires.db --profile production
```
:::

> [!tip] Dialects are inferred from profiles automatically
> You typically don't need to specify the `--dialect` option manually, as it is derived from your project configuration and the active profile.



### Using `cds deploy`

We can use `cds deploy` to inspect the generated DDL without actually deploying it, by using the `--dry` option. This will print the ultimate DDL statements to the console instead of executing them against the database, for example:
We can use `cds deploy` to inspect the generated DDL without actually deploying it, by using the `--dry` option. This will print the DDL statements to the console instead of executing them against the database, for example:

```shell
cds deploy --dry
Expand Down Expand Up @@ -134,6 +129,24 @@ Essentially, `cds deploy` calls `cds compile --to sql` under the hood, but goe
> [!note] Ad-hoc Deployments
> Without the `--dry` option, `cds deploy` would not only compile your CDS models to DDL, but would also do an ad-hoc deployment to the target database, if available. How that works is explained in more detail in the database-specific guides for [_SAP HANA_](hana), [_SQLite_](sqlite), and [_PostgreSQL_](postgres).

### Dialects by `cds env` Profiles for CAP Node.js

TThe dialect is automatically inferred from your project configuration, and the current profile, so you typically don't need to specify it explicitly. For example, if your project is configured to use SAP HANA in production and SQLite in development, the respective dialects will be applied automatically. Try this out using the `--profile` option:

```shell
cds deploy --dry --profile development
cds deploy --dry --profile production
```

::: details Use `cds env` to check your effective configurations:
```shell
cds env requires.db --profile development
cds env requires.db --profile production
```
:::

> [!tip] Dialects are inferred from profiles automatically
> You typically don't need to specify the `--dialect` option manually, as it is derived from your project configuration and the active profile.


## CDL ⇒ DDL Translation
Expand Down Expand Up @@ -181,13 +194,16 @@ CREATE TABLE sap_capire_bookshop_Books_Details ( ... );
```
:::

> [!tip] Guaranteed & Stable Slugification
> The slugification effects are guaranteed and stable, which means that you can rely on it and use the slugified names in native SQL queries. For example, both of the following CQL queries are equivalent and will work as expected:
> [!tip] You may use slugified names in CAP Node.js
> In CAP Node.js, the slugification effects are guaranteed and stable, which means that you can rely on it and use the slugified names in _native SQL_ queries. For example, both of the following CQL queries are equivalent and will work as expected:
>
> ```js
> await cds.run `SELECT from sap.capire.bookshop.Books`
> await cds.run `SELECT from sap_capire_bookshop_Books`
> ```

```js
await cds.run `SELECT from sap.capire.bookshop.Books`
await cds.run `SELECT from sap_capire_bookshop_Books`
```
> [!warning] Don't use slugified names in CAP Java
In CAP Java, you can use fully qualified entity names with dots. The slugified names can't be used in CQL queries.

> [!tip]
> Prefer entity names like `Books.Details` over _CamelCase_ variants like `BooksDetails`. While both work equally, they show up differently in native tools of databases that don't preserve case, for example in SAP HANA: The former will show up as `BOOKS_DETAILS`, while the latter shows up as `BOOKSDETAILS`, which is harder to read.
Expand Down Expand Up @@ -276,14 +292,23 @@ CREATE TABLE Books (
```
:::

> [!tip] Guaranteed & Stable Flattening
> The flattening effects are guaranteed and stable, which means that you can rely on it and use the flattened elements in native SQL queries. For example, both of the following CQL queries are equivalent and would work as expected:
The flattening effects for structured elements are guaranteed and stable. If a CDS model is compiled for usage with OData only the flattened elements are preserved in the CDS model.

```js
await cds.run `SELECT price.amount from Books`
await cds.run `SELECT price_amount from Books`
```
> [!tip] Use flattening in runtime queries in CAP Node.js
> The flattening effects can also be used at runtime with CAP Node.js. For example, both of the following CQL queries are equivalent and would work as expected:
>
> ```js
> await cds.run `SELECT price.amount from Books`
> await cds.run `SELECT price_amount from Books`
> ```

> [!warning] No flattening in runtime queries in CAP Java
> CAP Java does not perform an automatic flattening of path expressions:
>
> ```java
> Select.from("Books").columns("price_amount"); // valid
> Select.from("Books").columns("price.amount"); // invalid
> ```

### Associations ⇒ JOINs

Expand Down Expand Up @@ -349,7 +374,7 @@ LEFT JOIN Genres as genre on genre_ID = genre.ID; -- [!code ++]
> Looking closely at the above compiled SQL code, we can regard
> associations to be like _'Forward-declared' JOINs_, along these lines:
>
> 1. Association names `a.name` appear in queries as standard _table aliases_
> 1. Association names `a.name` appear in queries as _table aliases_
> 2. _JOINs_ are added automatically as per the following construction rule:
>
> _JOIN `a.target` as `a.name` on `a.on`_
Expand Down Expand Up @@ -467,13 +492,18 @@ However, even though CAP allows this, and handles all accesses correctly, it is
> [!warning] DON'T use Database-Invalid Names!
> It's **strongly discouraged** to use names that contain non-ASCII characters, or conflict with database reserved words. Even more avoid [delimited names](../../cds/cdl#keywords-identifiers) in CDS models in the first place, as that impacts readability of your models.

> [!warning] Avoid using reserved Java keywords
> A CAP Java project usually uses generated [accessor interfaces](../../java/cds-data#generated-accessor-interfaces). Avoid using reserved Java keywords as identifiers in a CDS model as using Java keywords might cause generated interfaces that don't compile. You may use the `@cds.java.name` annotation to mitigate such conflicts.


###### reserved-words
> [!important] Lists of Reserved Words
> Check out the reserved words for the databases you are targeting: \
> [_SAP HANA_](https://help.sap.com/docs/HANA_CLOUD_DATABASE/c1d3f60099654ecfb3fe36ac93c121bb/28bcd6af3eb6437892719f7c27a8a285.html)
> , [_SQLite_](https://www.sqlite.org/lang_keywords.html)
> , [_H2_](https://www.h2database.com/html/advanced.html#keywords)
> , [_PostgreSQL_](https://www.postgresql.org/docs/current/sql-keywords-appendix.html)
> , [_Java_](https://docs.oracle.com/javase/tutorial/java/nutsandbolts/_keywords.html)



Expand Down
95 changes: 68 additions & 27 deletions guides/databases/cql-to-sql.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,36 +10,61 @@ CAP supports a number of portable functions and operators in CQL, which are auto

Most native SQL operators are supported in CQL as-is, like these from the SQL92 standard:

- Arithmetic operators: `+`, `-`, `*`, `/`, `%`
- Arithmetic operators: `+`, `-`, `*`, `/` <sup>1</sup>
- Comparison operators: `<`, `>`, `<=`, `>=`, `=`, `<>`
- Logical operators: `AND`, `OR`, `NOT`
- Other operators: `IN`, `LIKE`, `BETWEEN`, `IS NULL`, `IS NOT NULL`, etc.

In addition, CQL provides some extended operators as described below.
<sup>1</sup> CAP Node.js additionally supports `%`.

In addition, CQL provides some extended comparison operators as described below.

### Bivalent `==` and `!=` Operators

CQL supports `==` and `!=` operators as bivalent logic variants for SQL's three-valued logic `=` and `<>`. In essence, the differences are as follows:
CQL supports `==` and `!=` operators as two-valued logic variants for SQL's three-valued logic `=` and `<>`.

::: code-group
```SQL [CQL's Two-Valued Logic Operators]
SELECT 1 == null, 1 != null, null == null, null != null;
--> false, true, true, false
```
:::
::: code-group
```SQL [SQL's Three-Valued Logic]
SELECT 1 = null, 1 <> null, null = null, null <> null;
--> null, null, null, null
```
::: details
The differences are captured in these truth tables:

#### two-valued equality (`==`, CQL)

| == | 1 | 0 | `null` |
|---|---|---|---|
| 1 | `true` | `false` | `false` |
| 0 | `false` | `true` | `false` |
| `null` | `false` | `false` | `true` |

#### three-valued equality (`=`, CQL & SQL)

| = | 1 | 0 | `null` |
|---|---|---|---|
| 1 | `true` | `false` | `unknown` |
| 0 | `false` | `true` | `unknown` |
| `null` | `unknown` | `unknown` | `unknown` |

#### two-valued inequality (`!=`, CQL)

| == | 1 | 0 | `null` |
|---|---|---|---|
| 1 | `false` | `true` | `true` |
| 0 | `true` | `false` | `true` |
| `null` | `true` | `true` | `false` |

#### three-valued inequality (`<>`, CQL & SQL)

| = | 1 | 0 | `null` |
|---|---|---|---|
| 1 | `false` | `true` | `unknown` |
| 0 | `true` | `false` | `unknown` |
| `null` | `unknown` | `unknown` | `unknown` |
:::

In other words:

- CQL's `x == null` -> `true` if `x` is `null`, otherwise `false`
- CQL's `x != null` -> `false` if `x` is `null`, otherwise `true`
- SQL's `x = null` -> `null` for all `x` (even if `x` is `null`)
- SQL's `x <> null` -> `null` for all `x` (even if `x` is not `null`)
- CQL's `x == null` -> `true` if `x` is `null`
- CQL's `x != null` -> `true` if `x` is not `null`
- SQL's `x = null` -> `unknown` for all `x` (even if `x` is `null`)
- SQL's `x <> null` -> `unknown` for all `x` (even if `x` is not `null`)

A real-world example makes this clearer. Consider this CQL query:

Expand All @@ -49,11 +74,20 @@ SELECT from Books where genre.name != 'Science Fiction';

The result set includes all books where genre is not 'Science Fiction', including the ones with an unspecified genre. In contrast, using SQL's `<>` operator, the ones with unspecified genre would be excluded.

The CQL behavior is consistent with common programming languages like JavaScript and Java, as well as with OData semantics. It is implemented in database by, the translation of `!=` to `IS NOT` in SQLite, or to `IS DISTINCT FROM` in standard SQL, and to an equivalent polyfill in SAP HANA.
The behavior of CQL two-valued comparison operators `==` and `!=` is consistent with common programming languages like JavaScript and Java, as well as with the semantics of the OData operators `eq` and `ne`. On the database `==` and `!=` are translated to `IS NOT DISTINCT FROM` and `IS DISTINCT FROM` in standard SQL, to `IS` and `IS NOT` on SQLite, and to an equivalent polyfill on SAP HANA.

> [!tip] Usage Recommendation
> Prefer using `==` and `!=` in the very most cases to avoid unexpected `null` results. Only use `=` and `<>` if you _really_ want SQL's three-valued logic behavior.

> [!tip] Avoid using `null` values
> Declare elements as `not null` where applicable. Or use a non-null value to represent "initial" state.

> [!tip] Compare list values in CAP Java
> In CAP Java, you may use the comparison operators to compare [list values](../../java/working-with-cql/query-api#list-values) like in this CQL query:
> ```sql
> SELECT from Books where (year, month) > (2014, 7)
> ```

### Ternary `?:` Operator

CQL supports the ternary conditional operator `condition ? expr1 : expr2`, similar to many programming languages like JavaScript and Java. It evaluates `condition`, and returns the value of `expr1` if `condition` is true, or the value of `expr2` otherwise.
Expand Down Expand Up @@ -83,7 +117,7 @@ This operator is translated to the best-possible equivalent in the target databa

Following are portable functions guaranteed by CAP. These can safely be used in CDS view definitions as well as in runtime queries expressed in CQL, and are translated to the best-possible database-specific native SQL equivalents.

String functions:
**String functions:**

- `concat(x,y,...)`
- `length(x)`
Expand All @@ -97,23 +131,21 @@ String functions:
- `substring(x,start, length)`
- `matchespattern(x,pattern)`

**Numeric functions:**

Numeric functions:

- `ceil(x)`, `ceiling(x)`
- `ceiling(x)`<sup>1</sup>
- `floor(x)`
- `round(x)`

###### aggregate-functions
Aggregate functions:
**Aggregate functions:**

- `avg(x)`, `average(x)`
- `min(x)`, `max(x)`
- `sum(x)`
- `count(x)`
<!-- - `countdistinct(x)` -->
- `countdistinct(x)`<sup>2</sup>

Date / Time functions:
**Date/time functions:**<sup>3</sup>

- `date(x)` -> `yyyy-MM-dd`
- `time(x)` -> `HH:mm:ss`
Expand All @@ -123,11 +155,20 @@ Date / Time functions:
- `hour(x)`
- `minute(x)`
- `second(x)`

**SAP HANA date/time functions:**<sup>3</sup>

- `years_between(x,y)`
- `months_between(x,y)`
- `days_between(x,y)`
- `seconds_between(x,y)`

---

* <sup>1</sup> CAP Node.js also supports `ceil`.
* <sup>2</sup> `countdistinct` is only supported by CAP Java.
* <sup>3</sup> Date/time functions are not supported by CAP Java.

> [!note] Indexes and Substring Details
> The return value of `indexof()` as well as the `start` parameter in `substring()` are zero-based index values. If the substring is not found, `indexof()` returns `-1`. If the `start` index in `substring()` is negative, it is counted from the end of the string. If the `length` parameter is omitted, the substring to the end of the string is returned.

Expand Down
4 changes: 2 additions & 2 deletions guides/databases/index.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@

# CAP-level Database Integration

CAP application developers [focus on their domain](../../get-started/features#focus-on-domain), while CAP takes care of all aspects of database integration. This includes translating CDS models to native persistence models, schema evolution, deployment, as well as runtime querying – all of that in a database-agnostic way. [SQLite](./sqlite) <sup>1</sup> in-memory databases are automatically used in inner-loop development, while in production, [SAP HANA](./hana) <sup>2</sup> is used by default.
CAP application developers [focus on their domain](../../get-started/features#focus-on-domain), while CAP takes care of all aspects of database integration. This includes translating CDS models to native persistence models, schema evolution, deployment, as well as runtime querying – all of that in a database-agnostic way. An in-memory database<sup>1</sup> is automatically used in inner-loop development, while in production, [SAP HANA](./hana) <sup>2</sup> is used by default.
{.abstract}

> _<sup>1</sup> or [H2](./h2) in case of CAP Java-based projects_.\
> _<sup>1</sup> [SQLite](./sqlite) or [H2](./h2) (for CAP Java)_.\
> _<sup>2</sup> or [PostgreSQL](./postgres) in edge cases_.


Expand Down
Loading