Search notes:

Oracle 23c: New features

Oracle claims that 23c has 300+ new features and enhancements compared to 21c. This page summarizes the features that are of particular interest to me.
Because of the importance of Artificial Intelligence technology in this release, Oracle renamed 23c to 23ai.

BOOLEAN data type in SQL

Oracle 23c introduces the boolean datatype for SQL (after it having been in PL/SQL for quite some time).
Some textual values such as true, false, y, yes will be converted to a boolean value, any numerical value that is not 0 will be converted to true, 0 values to false.
create table tq84_bool_test (val varchar2(10), flg boolean);
insert into tq84_bool_test values ('true'  , true );
insert into tq84_bool_test values ('false' , false);
insert into tq84_bool_test values ('null'  , null );
insert into tq84_bool_test values ('t'     ,'t'   );
insert into tq84_bool_test values ('yes'   ,'yes' );
insert into tq84_bool_test values ('true'  ,'true');
insert into tq84_bool_test values ('f'     ,'f'   );
insert into tq84_bool_test values ('0'     , 0    );

select val from tq84_bool_test where     flg;
select val from tq84_bool_test where not flg;
The new datatype comes with the function to_boolean(…).
The introduction of the boolean data type is rather unexpected: Some 20 years ago, Tom Kyte asserted that a boolean datatype is not needed
since
flag char(1) check (flag in ( 'Y', 'N' ))
serves the same purpose, requires the same amount of space and does the same thing.
Oracle clients earlier than 23c will display true values as 1 and false values as 0:
SQL> select true, false;
--
-- TRUE FALSE
-- ---- -----
--    1     0
Trying to use a boolean in a release earlier than 23c would result in a ORA-00902: invalid datatype error message.

Selecting an expression without FROM DUAL

The DUAL table is not needed anymore to select one or more expressions:
select
  'Hello world'     as txt,
   7 * 6            as num,
   sysdate          as now;

JavaScript Stored procedures

Stored procedures can be written in JavaScript:
create mle module tq84_js language javascript as

    export function f(p1, p2) { return p1+p2; }
   
    …

  create function f (p1 number, p2 number) return number as mle module tq84_js signature f(number, number);

  select f(20, 22) /* Ha: no more dual :-) */;
See also MLE modules.

DROP/CREATE TABLE IF [NOT] EXISTS

The if exists and if not exists clauses of the create table and drop table statements are useful to prevent the ORA-00942: table or view does not exist and ORA-00955: name is already used by an existing object errors:
drop   table IF     EXIST tq84_t1;
create table IF NOT EXIST tq84_t2 (num number, txt varchar2(10));

GROUP BY on expression aliases/position number

group by can use aliases for expressions (such as the result of a PL/SQL function):
select
   count(*),
   fnc(a, b) xyz
from
   t
group by
   xyz
having
   xyz > 100
;
Prior to Oracle 23, the previous statement would have thrown a ORA-00904: invalid identifier error message.
If the group_by_position_enabled init parameter is set to true, it's also possible to group by a position number In order to group on a position number (rather than an expression alias):
alter session set group_by_position_enabled = true;

select
   category_id,
   sum(val)
from
   t
group by
   1;

Updating tables

Tables can be updated with a condition specified in a («direct») join: (i.e. with a from clause after the set clause).
udpate    dest d
   set    d.xxx = …
  from    src  s
 where    d.abc = s.def;

JSON

JSON Schema

A JSON schema allows to validate JSON documents:
create table tq84_json_schema_test (
   obj json validate '
     {
         "num": number,
         … 
     }
   '
);

JSON Relational Duality

JSON Relational Duality Views provide a mapping between
  • the way an application consumes and represents data (i. e. JSON documents) and
  • the paradigm in which a relational database stores the data (i. e. rows).
The conversion is between these two worlds is triggered in/with GraphQL.
create or replace JSON DUALITY VIEW
from tq84_root_tab                   -- The name table sitting at the top of the hierarchy
as
   tq84_rel_dual_test
   @update @insert @delete
{
   num      :   col_num,             -- Mapping between JSON property names and table column names
   txt      :   col_txt
   children : tq84_child_tab {       -- Nesting objects
     …

   }
}

Accessing duality views

A duality view can be accessed with
  • SQL
  • traditional HTTP/REST operations, i. e. GET, PUT and POST HTTP requests. (see concurrent reading/writing access below)
  • Simple Oracle Document API (SODA)
  • Oracle Database API for MongoDB
  • ORDS

Concurrent reading/writing access

REST GET and REST PUT are stateless operations. Thus, a writing operation might overwrite the modification of another writing request («mid-air collision» problem).
In order to avoid this problem, Oracle has adapted the concept of the ETag response and If-Match request HTTP header (see also the function sys_row_etag).

Advantages over ORM

Some advantages of duality views over Object Relational Mappings (ORMs) are
  • They're language independent
  • They're optimized by the database

Describing a duality view's schema

dbms_json_schema.describe('tq84_rel_dual_test')
$dbHost=…
$schemaName=rene
curl -X GET http://$dbHost/ords/$schemaName/metadata-catalog/tq84_rel_dual_test

Links

Table value constructors

Insert multiple records at once (going by the name of «table value constructor»):
insert into t1 values
  (1, 'first record' ),
  (2, 'second record'),
  (3, 'third record' ),
  (4, 'fourth record');
Similarly in a select statement:
select * from (
  values
    (1, 'first record' ),
    (2, 'second record'),
    (3, 'third record' ),
    (4, 'fourth record')
)
tmp (num, txt);

Schema Level Privileges

Privileges can be granted on entire schemas:
grant
   select any table
on
   schema sales  
to
   rene;

SQL domains

SQL domains can optionally specify a
create domain email_addr as varchar2(99);

create domain month_year as varchar2(7)
   constraint month_fmt check (regexp_like(month_year, '^\d\d-\d\d\d\d$'))
   display 'Year: ' || substr(month_year, 4, 4) || ', Month: ' || substr(month_year, 1, 2)
   order substr(month_year, 4, 4) || substr(month_year, 1, 2)
;

create table financial_report (
   id             integer    primary key,
   corrections_to email_addr,
   rep_period     month_year,
   pdf            blob
);

insert into financial_report values
( 1, 'fred@xyz.ab', '03-2023', null ),
( 2, 'fred@xyz.ab', '08-2023', null ),
( 3, 'maya@xyz.ab', '02-2023', null ),
( 4, 'joey@xyz.ab', '06-2023', null );
The order clause of the month_year domains influences the sort order of order by so that it is sorted logically rather than by the by the displayed value:
select
   rep_period,
   corrections_to,
   id
from
   financial_report
order by
   rep_period;
--
-- REP_PER CORRECTIONS_TO         ID
-- ------- -------------- ----------
-- 02-2023 maya@xyz.ab             3
-- 03-2023 fred@xyz.ab             1
-- 06-2023 joey@xyz.ab             4
-- 08-2023 fred@xyz.ab             2
domain_display(…) applies the display rules when selecting values from a domain:
select
   domain_display(rep_period) repper,
   corrections_to
from
   financial_report
order by
   rep_period;

-- REPPER                CORRECTIONS_TO                                                                                     
-- --------------------- ---------------------------------------------------------------------------------------------------
-- Year: 2023, Month: 02 maya@xyz.ab                                                                                        
-- Year: 2023, Month: 03 fred@xyz.ab                                                                                        
-- Year: 2023, Month: 06 joey@xyz.ab                                                                                        
-- Year: 2023, Month: 08 fred@xyz.ab                                                                                        
Domains are stored in the data dictionary in dba_domains etc.
SQL functions related to domains include

Developer role

A special developer role (named db_developer_role) can be granted to facilitate developers's jobs.
begin
   dbms_developer_admin.grant_privs('rene');
end;
/
2023-04-10: Apparently, dbms_developer_admin is not present in (at least the) Oracle 23c Free edition.
SQL> grant db_developer_role to rene identified by renesSecretPassword;
grant succeeded.

SQL> connect rene/renesSecretPassword@ora23c;
connected.

SQL> select * from session_privs order by privilege;
PRIVILEGE
------------------------------
CREATE ANALYTIC VIEW
CREATE ATTRIBUTE DIMENSION
CREATE CUBE
CREATE CUBE BUILD PROCESS
CREATE CUBE DIMENSION
CREATE DIMENSION
CREATE DOMAIN
CREATE HIERARCHY
CREATE JOB
CREATE MATERIALIZED VIEW
CREATE MINING MODEL
CREATE MLE
CREATE PROCEDURE
CREATE SEQUENCE
CREATE SESSION
CREATE SYNONYM
CREATE TABLE
CREATE TRIGGER
CREATE TYPE
CREATE VIEW
DEBUG CONNECT SESSION
EXECUTE DYNAMIC MLE
FORCE TRANSACTION
ON COMMIT REFRESH

24 rows selected.
The db_developer_role is created in $ORACLE_HOME/rdbms/admin/catdevrol.sql (in which script also the corresponding privileges are granted).

Up to 4096 columns per table

Up to 4096 columns per table (requires max_columns to be set to extended and compatible to be set to 23.0.0).

Staging tables

A staging table is a heap table that is optimized for
create table (
  c1 number,
  …
)
FOR STAGING;
This feature comes with the new column STAGING in dba_tables.

Object and schema annotations

Key value pair annotations for (some) objects provide metadata for data and schema.
create table t_foo ( … )
annotations (
   expected_release 'R4.2',
   test_coverage    'no'
);
Annotations are supported for tables, views, materialized views, columns, indexes and domains.
Annotations can be queried in the data dictionary:
select * from user_annotations;
select * from user_annotation_values;
select * from user_annotations_usage;

«Asynchronous» transactions (lock-free reservations)

A numeric column that is marked reservable allows multiple transactions to concurrently update the value.
The following example creates such a table and inserts some test data:
create table tq84_async (
    id    integer     primary key,
    val   number(5,2) RESERVABLE
);

insert into tq84_async values
(  13,   18.22 ),
(  19,    7.83 ),
(  42,  100.00 ),
(  99,   68.54 );

commit;
A session updates a record in this table and then queries the value (which does not seem to change, i.e. is still 100):
SES 1> update tq84_async set val = val + 10 where id = 42;
SES 1> select * from tq84_async where id = 42;
--
--         ID        VAL
-- ---------- ----------
--         42        100
Another session also updates val (while the first session has not commited the update). This session also sees the value of 100:
SES 2> update tq84_async set val = val +  1 where id = 42;
SES 2> select * from tq84_async where id = 42;
--
--         ID        VAL
-- ---------- ----------
--         42        100
The first session then commits its transaction. It now sees its modification:
SES 1> commit;
SES 1> select * from tq84_async where id = 42;
--
--         ID        VAL
-- ---------- ----------
--         42        110
The second session also sees the update of the first session but still not its own update:
SES 2> select * from tq84_async where id = 42;
--
--         ID        VAL
-- ---------- ----------
--         42        110
Only when the 2nd session commits its transaction, its modification becomes visible also:
SES 2> commit;
SES 2> select * from tq84_async where id = 42;
--
--         ID        VAL
-- ---------- ----------
--         42        111
After the commit in the second session, the first session commits also and then selects the updated record:
SES 1> commit;
SES 1> select * from tq84_tab;
       ID        VAL
---------  ---------
       42        111
A table with a reservable column cannot be dropped (error message ORA-55764: Cannot DROP or MOVE tables with reservable columns). Thus, the column must be first set to not reservable:
alter table tq84_async modify val not reservable;
drop  table tq84_async;

See also

See also Connor McDonald's tweet and the follow up on YouTube.

Improved error messages

A program of Oracle (that is not strictly related to 23c only) tries to improve error messages:
ORA21> select sum(val), gr_1, gr_2 from T group by gr_1;
ORA-00979: not a GROUP BY expression

ORA21> connect …@ORA23

ORA23> select sum(val), gr_1, gr_2 from T group by gr_1;
ORA-00979: "GR_2": must appear in the GROUP BY clause or
                   be used in an aggregate function
Some of the messages that seem to have changed include

Misc

Here's a python script to download release 23c related Oracle ducumentation PDF files.
Three new PL/SQL packages:
Initialization parameters:
allow_legacy_reco_protocol Should the database allow the legacy RECO protocol
allow_weak_crypto Allow weak crypto usage in DBMS_CRYPTO Undocumented? Already in 21c?
auto_start_pdb_services Automatically start all PDB services on PDB Open
blockchain_table_retention_threshold maximum retention without TABLE RETENTION privilege
client_prefetch_rows Client prefetch rows value Undocumented? Already in 21c?
db_flashback_log_dest Separate creation directory for flashback database logs
db_flashback_log_dest_size Size limit of separate creation directory for flashback database logs
default_credential default credential session parameter Undocumented? Already in 21c?
drcp_connection_limit DRCP connection limit Undocumented? Already in 21c?
group_by_position_enabled enable/disable group by position
hybrid_read_only Hybrid read only mode allows CDB common user to patch the PDB Undocumented? Already in 21c?
inmemory_graph_algorithm_execution Controls the fall-back action of graph algorithm execution if in-memory execution is not possible Undocumented? Already in 21c?
iorm_limit_policy Policy used to compute Exadata IORM limit Undocumented? Already in 21c?
ipddb_enable Enable IPD/DB data collection Undocumented? Already in 21c?
json_behavior_flags control json behaviors Undocumented? Already in 21c?
json_expression_check enable/disable JSON query statement check
kafka_config_file KSR pub/sub external message bus(KGMPS) CONFIGuration file Undocumented? Already in 21c?
load_without_compile Load PL/SQL or Database objects without compilation Undocumented? Already in 21c?
main_workload_type Main workload type Undocumented? Already in 21c?
max_columns maximum number of columns[columns] allowed in a table or view Value can be standard (= 1000 columns) or extended (= 4096 columns). In order to set to extended, compatible must be set to 23.0.0.0.0 or higher.
max_saga_duration default value for max saga duration
memoptimize_write_area_size changes memoptimize write area size Undocumented? Already in 21c?
memoptimize_writes write data to IGA without memoptimize_write hint Undocumented? Already in 21c?
memory_max_size Maximum memory size Undocumented? Already in 21c?
memory_size Target memory size Undocumented? Already in 21c?
multilingual_engine Enable or disable Multilingual Engine (MLE)
native_blockchain_features Native block chain enable/disable
optimizer_cross_shard_resiliency enables resilient execution of cross shard queries
pdc_file_size size (in bytes) of the pmem direct commit file Undocumented? Already in 21c?
pkcs11_library_location PKCS#11 library location for Transparent Data Encryption Undocumented? Already in 21c?
plsql_implicit_conversion_bool PL/SQL: Implicit conversion for boolean
read_only Restrict WRITE operations in user session Undocumented? Already in 21c?
resource_manager_cpu_scope scope of CPU resource management Undocumented? Already in 21c?
result_cache_auto_blocklist whether to run the auto blocklisting algorithm
result_cache_integrity result cache deterministic PLSQL functions Undocumented? Already in 21c?
saga_hist_retention default value for retention of completed sagas
session_exit_on_package_state_error Request client to exit when PL/SQL package state is discarded Undocumented? Already in 21c?
shard_enable_raft_follower_read enable read from follower replications units in a shard Undocumented? Already in 21c?
shard_queries_restricted_by_key add shard key predicates to the query Undocumented? Already in 21c?
soda_behavior_flags control soda behaviors Undocumented? Already in 21c?
sql_error_mitigation enables automatic error mitigation
sql_history_enabled Determines if SQL history (exposed in v$sql_history) is enabled (true or false)
sql_transpiler Enable SQL transpiler
statement_redirect_service statement redirect service Undocumented? Already in 21c?
sysdate_at_dbtimezone use DB timezone while computing sysdate and systimestamp value Undocumented? Already in 21c?
time_at_dbtimezone use DB timezone when computing current time
timezone_version_upgrade_online enable/disable time zone version upgrade online
tracefile_content_classification enable output of trace record security label prefix Undocumented? Already in 21c?
true_cache Enable True Cache Undocumented? Already in 21c?
txn_auto_rollback_high_priority_wait_target Auto abort wait for high pri txns
txn_auto_rollback_medium_priority_wait_target Auto abort wait for medium pri txns
txn_auto_rollback_mode Modes for Auto Transaction Rollback feature
txn_priority Priority of a transaction in a session
xml_client_side_decoding enable/disable xml client-side decoding Undocumented? Already in 21c?
xml_handling_of_invalid_chars Handle invalid chars during xmlelement Undocumented? Already in 21c?
Static data dictionary that are new in 23c possibly include:
dba_annotations Annotations
dba_annotations_usage Usage information for annotation
dba_annotation_values Values for annotations
dba_blockchain_row_version_cols Row versioned columns in all blockchain tables.
dba_blockchain_row_version_history
dba_blockchain_table_chains
dba_blockchain_table_epochs
dba_blockchain_table_hash_col_order
dba_ddl_regs DDL notification registrations in the database.
dba_domains SQL domains
dba_domain_cols
dba_domain_constraints Constraint definitions on SQL domains
dba_hist_optimizer_env_details Optimizer environments that have been captured in the Workload Repository.
dba_hist_sagas History of all completed sagas in the database.
dba_immutable_row_version_cols Row versioned columns in all immutable tables in the database
dba_immutable_row_version_history
dba_immutable_table_columns
dba_immutable_table_epochs
dba_incomplete_sagas
dba_json_duality_views JSON relation duality views
dba_json_duality_view_links Links associated with all JSON-relational duality views in the database
dba_json_duality_view_tabs
dba_json_duality_view_tab_cols
dba_json_schema_columns
dba_kafka_applications Oracle SQL Access to Kafka (OSAK) applications
dba_kafka_clusters OSAK Clusters for which any database user has READ access.
dba_kafka_load_metrics Metrics for dbms_kafka
dba_kafka_ops Operations for all OSAK views
dba_kafka_ops_results Results of OSAK operations
dba_kafka_partitions Partitions for Kafka topics associated with SQL access
dba_mle_envs
dba_mle_env_imports
dba_mle_modules
dba_mle_procedures
dba_pg_edge_relationships Property graph relationships
dba_pg_elements Property graph element tables
dba_pg_element_labels
dba_pg_keys
dba_pg_labels
dba_pg_label_properties
dba_pg_prop_definitions
dba_property_graph List of defined property graphs in the database.
dba_sagas Active sagas
dba_saga_details Saga details
dba_saga_errors
dba_saga_finalization Pending finalization actions
dba_saga_participants
dba_saga_pending Sagas that were initialized in the current PDB.
dba_schema_privs
dba_sql_error_mitigations Automatic error mitigations for SQL statements.
dba_txeventq_migration_status Information about all migrations from AQ classic queues to Transactional Event Queues (TxEventQs).
dba_unused_schema_privs Schema privileges (without privilege grant paths) that are not used for the privilege analysis policies reported by the dbms_privilege_capture.generate_result procedure. Compare with dba_used_schema_privs
dba_unused_schema_privs_path Compare with dba_used_schema_privs_path
dba_used_schema_privs Schema privileges (without privilege grant paths) that are used for the privilege analysis policies reported by the dbms_privilege_capture.generate_result procedure. Compare with dba_unused_schema_privs_path
dba_used_schema_privs_path Compare with dba_unused_schema_privs_path
DBMS_* packages
dbms_aqmigtool
dbms_json_duality ?
dbms_json_schema ?
dbms_kafka
dbms_saga
dbms_saga_adm
dbms_search
dbms_sql_firewall
dbms_userdiag
V$VIEWS:
v$datapump_sessionwait_info
v$enabledschemaprivs
v$eq_dequeue_sessions
v$fast_start_failover_config
v$flashback_log_dest
v$ofs_threads Oracle file system (OFS) threads.
v$px_process_detail
v$px_server
v$sql_history See also sql_history_enabled
v$tdm_stats Statistics for Oracle Connection Manager in Traffic Director Mode (CMAN-TDM).
v$text_waiting_events Oracle Text index maintenance events that have been delayed due to errors or contentions.
Further improvements might (or do?) include:

Docker image

There is a docker image for the free edition of Oracle 23c:
docker pull container-registry.oracle.com/database/free
However, this image is poorly (if at all) documented, thus I had to make a few dabbles in the dark to get it running usefully.
If the container named ora23c was previously started (and stopped), it must be removed:
$ docker rm ora23c
Start a new container:
docker run  --name ora23c -d -p 1521:1521 container-registry.oracle.com/database/free
Check the status of the container until it changes from Up … seconds (health: starting) to Up 2 minutes (healthy):
docker ps
When it was running, I changed the password of sys: opened a shell in the container:
$ docker exec ora23c sh -c "echo 'alter user sys identified by IamTheDBA;' | sqlplus -S / as sysdba"

User altered.
It's also possible to open a shell in the container and run SQLPlus interactively:
$ docker exec -it ora23c bash
bash-4.4$ sqlplus / as sysdba
…
SQL>
With this password, I could then connect to the instance from SQL Developer using the service names FREE and FREEPDB1 (see /opt/oracle/product/23c/dbhomeFree/network/admin/tnsnames.ora)
It should be noted that all changes to the database disappear when the docker container is stopped.

See also

/etc/init.d/oracle-free-23c
A new error message seems to be ORA-46401: No new traditional AUDIT configuration is allowed. Traditional auditing is desupported, and you should use unified auditing in its place.

Links

Philipp Salvisberg's Twitterthread summarizes some impressions from new 23c features presented at the DOAG 2022 Conference.
That Jeff Smith's notes

Index