In previous releases of Oracle Database, there was always the possibility of adding comments to tables and columns, among other elements. As a developer, I have always appreciated finding such comments from those who worked on the code before me. They provide valuable insights into the meaning of each table and column, especially in older versions where object names were limited to 30 bytes.
In version 12.2 and beyond, this limit has increased to 128 bytes. Occasionally, these comments were utilized to define how a column should be displayed, such as in an APEX application.
Oracle Database 23c introduces a more flexible way of commenting on tables, views, materialized views, columns, and indexes: Annotations. These annotations, which are name-value pairs, can be added directly during the creation of the object. Post-creation, you can drop, add, or replace annotations.
Annotations follow the normal naming rules for objects in Oracle Database, using only ASCII letters, numbers, and underscores. They must begin with a letter. Similar to object names, annotation names are case-insensitive, unless enclosed in double quotes (") for case-sensitive names, unsupported characters, or reserved keywords. The value must be a character string literal, always enclosed in single quotes (').
Annotation names have a maximum length of 1024 characters, and annotation values can have a maximum length of 4000 characters. As annotations are stored in separate tables, there is no limit to the number of annotations.
Let’s create a table with some annotations:
create table if not exists drivers( driverid number generated by default on null as identity annotations ( primarykey , display 'false'), driver_number number ( 2 ) annotations ( "Permanent driver number" , caption 'Driver number' , display 'true' , optional ), code varchar2( 3 ) annotations ( "Driver code e.g. 'VER'" , caption 'Driver code' , display 'true' , non_unique ), driver_name varchar2( 32 ) not null annotations ( "Driver full name" , caption 'Driver full name' , display 'true' , mandatory ) , dob date not null annotations ( "Driver date of birth" , caption 'Driver date of birth' , display 'true' , mandatory ), nationality varchar2( 16 ) annotations ( "Driver nationality" , caption 'Driver nationality' , display 'true' ))annotations ( "access" 'Public' )/
After creating the object, you can drop, add, or replace annotations. To prevent script breakage, annotations also support the `if [not] exists` construction:alter table driversmodify nationality annotations ( add if not exists display 'true' )
/
This follows the same rules as any other `if not exists` statement. If the annotation name already exists, nothing happens. The `add or replace` syntax is also an option. This also always completes successfully but the value is (re)placed in the annotations.
alter table driversannotations ( add or replace "access" 'Restricted' )/
Dictionary Views
Oracle Database 23c introduces several new dictionary views to track all annotations and their usage across objects in a schema. You can query `USER_ANNOTATIONS` to find all unique annotations used in your schema:
select annotation_namefrom user_annotations/
ANNOTATION_NAME--------------------------DISPLAYPRIMARYKEYCAPTIONOPTIONALPermanent driver numberDriver code e.g. 'VER'NON_UNIQUEDriver full nameMANDATORYDriver date of birthDriver nationalityaccess
12 rows selected.
To find all unique name-value pairs, query `USER_ANNOTATION_VALUES`:
select annotation_name, annotation_valuefrom user_annotation_values/ANNOTATION_NAME ANNOTATION_VALUE------------------------- -------------------------access RestrictedPRIMARYKEYDISPLAY falsePermanent driver numberCAPTION Driver numberDISPLAY trueOPTIONALDriver code e.g. 'VER'CAPTION Driver codeNON_UNIQUEDriver full nameCAPTION Driver full nameMANDATORYDriver date of birthCAPTION Driver date of birthDriver nationalityCAPTION Driver nationality
17 rows selected.
Another view, `USER_ANNOTATION_USAGE`, shows which annotation name-value pairs are used for which objects:
SQL> select object_name 2 , object_type 3 , column_name 4 , annotation_name 5 , annotation_value 6 from user_annotations_usage 7 /OBJECT_NAME OBJECT_TYPE COLUMN_NAME ANNOTATION_NAME ANNOTATION_VALUE----------- ----------- ------------- ----------------------- --------------------DRIVERS TABLE access RestrictedDRIVERS TABLE DRIVERID PRIMARYKEYDRIVERS TABLE DRIVERID DISPLAY falseDRIVERS TABLE DRIVER_NUMBER Permanent driver numberDRIVERS TABLE DRIVER_NUMBER CAPTION Driver numberDRIVERS TABLE DRIVER_NUMBER DISPLAY trueDRIVERS TABLE DRIVER_NUMBER OPTIONALDRIVERS TABLE CODE Driver code e.g. 'VER'DRIVERS TABLE CODE CAPTION Driver codeDRIVERS TABLE CODE DISPLAY trueDRIVERS TABLE CODE NON_UNIQUEDRIVERS TABLE DRIVER_NAME Driver full nameDRIVERS TABLE DRIVER_NAME CAPTION Driver full nameDRIVERS TABLE DRIVER_NAME DISPLAY trueDRIVERS TABLE DRIVER_NAME MANDATORYDRIVERS TABLE DOB Driver date of birthDRIVERS TABLE DOB CAPTION Driver date of birthDRIVERS TABLE DOB DISPLAY trueDRIVERS TABLE DOB MANDATORYDRIVERS TABLE NATIONALITY Driver nationalityDRIVERS TABLE NATIONALITY CAPTION Driver nationalityDRIVERS TABLE NATIONALITY DISPLAY false
22 rows selected.
For all these views, the `ALL_` and `DBA_` views are also available. The `ALL_%` views include an extra column with the schema name where the annotation is defined. The `DBA_%` view is only available with sufficient access rights.
Querying these views can potentially help generate parts of your application code or even influence the behavior of your APEX application. For example, retrieving all annotations for a specific column in JSON format:
select object_type , object_name , column_name , json_serialize( json_arrayagg( json_object( annotation_name , annotation_value ) ) pretty ) in_jsonformatfrom user_annotations_usagewhere column_name = 'DRIVER_NUMBER'group by object_type , object_name , column_name/
OBJECT_TYPE OBJECT_NAME COLUMN_NAME IN_JSONFORMAT----------- ----------- ------------- ---------------------------------------------------TABLE DRIVERS DRIVER_NUMBER [ { "ANNOTATION_NAME" : "Permanent driver number", "ANNOTATION_VALUE" : null }, { "ANNOTATION_NAME" : "OPTIONAL", "ANNOTATION_VALUE" : null }, { "ANNOTATION_NAME" : "DISPLAY", "ANNOTATION_VALUE" : "true" }, { "ANNOTATION_NAME" : "CAPTION", "ANNOTATION_VALUE" : "Driver number" } ]
Adapting to these changes might take some getting used to, but by consistently using annotations in all projects, future developers will undoubtedly appreciate the clarity and insight they provide.
The source code used in this article can be found at:
https://github.com/Qualogy-Solutions/OracleDatabase23c/blob/main/annotations.sql
Also read my previous blogs about Oracle Database 23:
- Introduction blog series Oracle Database 23
- Developer Role
- Group by Alias
- If [Not] Exists
- Table values constructor
- The Boolean data type
- New PL/SQL iterator constructs
And my next blog: