CDS pitfalls
What you'll learn
CDS cardinality is a hint to the optimizer, not a rule it enforces — and #CHECK with no DCL activates fine but dies at runtime.
- Cardinality [1..1] is a declaration to the optimizer, NOT an enforced constraint — wrong cardinality corrupts joins silently.
- @AccessControl.authorizationCheck: #CHECK without a matching DCL activates fine, then fails at runtime on first access.
- @Consumption.* annotations only take effect in projection/consumption views; on interface views they are ignored.
The most expensive CDS misconception is treating cardinality as a constraint. Writing an association as [1..1] is a *declaration* to the optimizer about expected fan-out, not an enforced rule. If the real data has more than one matching row, the activator says nothing, the join silently fans out, and you get duplicated or wrong aggregates. Cardinality drives pushdown — a [0..1] or [1..1] target can be folded into the parent read, while a wrong cardinality both misleads the optimizer and corrupts results, undetectably at activation time.
The authorization trap mirrors the RAP one. @AccessControl.authorizationCheck: #CHECK declares 'this view is access-controlled' — but the DCL role that actually grants the rows is a *separate* object. With #CHECK and no matching DCL the view activates cleanly, then throws at runtime on the first access because the framework looks for a role that isn't there. Either ship the DCL or use #NOT_REQUIRED deliberately for views that are not end-user-facing.
The third is layering. @Consumption.* annotations (value helps, filters, semantic hints) take effect only in the projection / consumption layer; placed on an interface (I_*) view they are simply ignored, so the value help you 'added' never appears. And path-expression pushdown across associations only happens when the cardinality permits it — check the EXPLAIN plan rather than assuming the database resolved the path for you.
Key points
- Cardinality [1..1] is a declaration to the optimizer, NOT an enforced constraint — wrong cardinality corrupts joins silently.
- @AccessControl.authorizationCheck: #CHECK without a matching DCL activates fine, then fails at runtime on first access.
- @Consumption.* annotations only take effect in projection/consumption views; on interface views they are ignored.
- Path-expression pushdown depends on cardinality — verify with EXPLAIN rather than assuming.
Examples
This activates cleanly; the first SELECT at runtime fails because no DCL role grants the rows.
ABAP@accesscontrol.authorizationcheck: #check
define view entity zi_order
as select from zorder_hdr { key order_id, total }A role grants select on the view; now #CHECK has something to enforce and runtime access succeeds.
ABAPdefine role zi_order_dcl {
grant select on zi_order
where ( company_code ) = aspect pfcg_auth( s_tcode, bukrs, actvt = '03' );
}Source notes: clean-core-curriculum §10.4
Ask Claude
Build a prompt from this lesson + your question and open a fresh Claude chat with it pre-filled — handy for adapting a before/after pattern to your own object.