Schema Annotations with Database 19c
Why Adding Schema Annotations to Oracle 19c Is Not Straightforward
Search for a command to run...
Why Adding Schema Annotations to Oracle 19c Is Not Straightforward
No comments yet. Be the first to comment.
Technical posts that are not just about workflow, business process modeling - but instead are Oracle developer focused.
Autonomous AI Workflows. Smarter Case Management. Shipping May 2026.

Oracle introduced the APEX Approval Task in APEX 22.1, extended it to non-approval Human Tasks with Action Tasks in APEX 23.2, and has enhanced the overall functionality of APEX Human Tasks in each APEX release. Flows for APEX, the BPMN process model...

Flows for APEX v25.1 and Beyond The Flows for APEX team is currently finalizing version 25.1, which will be available this summer in both Community and Enterprise Editions. This release introduces sig

Several BPMN task types allow you to define the task using PL/SQL - these include BPMN Service Tasks, Script Tasks, Business Rule Tasks, and Send Tasks. This raises the question - ‘How do you set process variables from inside your PL/SQL script?’ Ins...

Oracle's Schema Annotations feature is one of those additions that immediately makes sense once you see it.
As AI-powered development tools become increasingly database-aware, having structured metadata attached directly to database objects is becoming far more valuable than traditional comments. Annotations provide a standard way to describe tables, views, columns, APIs, and other schema objects in a way that can be consumed by both humans and tools.
So, inspired by Jeff Smith and Martin Bach's blogs on annotating your schema, I decided to add annotations to the next release of Flows for APEX across our database schema. I assumed it would be a fairly simple exercise.
It wasn't.
For those unfamiliar with the project, Flows for APEX is an open-source BPMN 2.0 workflow engine for Oracle APEX.
We currently support:
Oracle APEX 24.1 and higher
Oracle Database 19c and higher
As part of our upcoming release we've been adding several features aimed at today's AI-powered developers, including:
Skills definitions
An XSD definition of our BPMN extensions
Scripts to create read-only schemas for MCP access
Database annotations on our schema objects
The goal was simple: provide richer metadata that can help developers, AI assistants, MCP servers, and schema analysis tools better understand the application.
Because we support both Oracle Database 19c and Oracle Database 26ai, our installation and upgrade scripts need to run successfully on both platforms.
That's where things became interesting.
Like many Oracle applications, we have two broad categories of database objects.
Tables are created on first installation and then migrated over time.
create table flow_processes (
...
);
Views, packages, package bodies and similar objects are recreated during every upgrade using CREATE OR REPLACE.
create or replace view flow_processes_v as
select ...
This means any annotation strategy needs to be:
Repeatable
Idempotent
Compatible across upgrades
Compatible across Oracle Database 19c through 26ai
The good news is that annotations themselves are easy to create.
For a table column:
alter table t1
modify (
id annotations (
add description 'Identifier'
)
);
For a view column:
alter view my_emp
modify (
empno annotations (
add description 'Employee Number'
)
);
So far, so good.
Installation scripts run once.
Upgrade scripts may run repeatedly during:
Development
Testing
CI/CD pipelines
Customer upgrades
Ideally, annotation scripts should be idempotent (they can be run multiple times without changing the result beyond the initial application).
Fortunately Oracle appears to provide exactly the syntax required:
alter table t1
modify (
id annotations (
add or replace description 'Identifier'
)
);
The intention is obvious:
Create the annotation if it doesn't exist
Replace it if it does
Perfect.
Or so I thought....
On Oracle Database 19c, 19.28 to at least 19.31, ADD OR REPLACE does not behave as expected.
Consider:
create table t1 (id number);
alter table t1
modify (
id annotations (
add description 'Identifier'
)
);
alter table t1
modify (
id annotations (
add or replace description 'Identifier2'
)
);
The second statement fails.
Initially I assumed there was a problem with the annotations implementation itself.
However, after discussions with Oracle, it turns out the issue is more specific.
The annotations engine itself is not the problem.
Both of these work correctly:
annotations (
add description 'Identifier'
)
and
annotations (
replace description 'Identifier'
)
The problem is specifically the combined syntax:
annotations (
add or replace description 'Identifier'
)
Oracle confirmed that the parser in the 19c backport does not correctly interpret the ADD OR REPLACE syntax.
Instead of recognising:
ADD OR REPLACE
annotation = description
value = Identifier
the parser effectively interprets:
ADD
annotation = OR
value = REPLACE
which explains the resulting errors.
The same behaviour occurs on both:
Tables
Views
Interestingly, the syntax works correctly on Oracle Database 23ai and 26ai, suggesting that the issue is specific to the 19c backport implementation.
At this point I wondered whether views might be simpler.
Since our views are recreated during every upgrade, perhaps recreating a view would remove any annotations attached to it?
For example:
create or replace view my_emp as
select * from emp;
alter view my_emp
modify (
empno annotations (
add description 'Employee Number'
)
);
create or replace view my_emp as
select * from emp;
If the annotation disappeared when the view was recreated, a subsequent ADD would succeed.
Unfortunately, that's not what happens.
The annotation survives the CREATE OR REPLACE VIEW.
The following statement fails because the annotation already exists:
alter view my_emp
modify (
empno annotations (
add description 'Employee Number'
)
);
This was actually a useful discovery.
While it complicates deployment scripts, it also means Oracle preserves annotation metadata across view recreation.
After discussing the issue with Oracle, they suggested different approaches for views and tables.
For views, annotations can be embedded directly into the view definition itself.
For example:
create table t (
c1 integer
);
create or replace view vw (
c1 annotations (
description 'Test Column'
)
)
as
select *
from t;
Because the annotation is part of the view definition, every CREATE OR REPLACE VIEW recreates the annotation automatically.
While this seems like a good workaround on simple views, implementing this on views at are UNIONs of several queries, which extract data from JSON, and other of our more complex views proved difficult and high risk - so we ended up deciding not to add annotations to views if the underlying database is < 23ai.
Tables cannot be recreated during upgrades, so a different approach is needed.
Oracle suggested adding a double statement:
alter table t
modify c1 annotations (
add if not exists description 'Identifier'
);
alter table t
modify c1 annotations (
replace description 'Identifier'
);
This effectively provides the same behaviour as ADD OR REPLACE.
The first statement ensures the annotation exists.
The second updates the value. It's slightly clunky, but it works.
After all this experimentation, our annotation strategy now looks like this.
No annotations.
The feature is not available.
For views:
For tables:
add if not exists
followed by
replace
Use:
add or replace
which works correctly.