Oracle VECTOR Columns with Atlas (23ai+)
Oracle is a trademark of Oracle Corporation. Atlas is not affiliated with or endorsed by Oracle.
Oracle 23ai introduced the VECTOR column type for storing high-dimensional embeddings and enabling
similarity searches directly inside the database. Atlas lets you define, inspect, and migrate VECTOR
columns like any other schema type, so your AI-related schema changes flow through the same code review
and CI/CD pipeline as the rest of your schema.
This guide walks through the versioned workflow: each change is captured as a migration file, reviewed by your team, and applied through CI/CD.
The Oracle driver is available only to Atlas Pro users. To use it, run:
atlas login
Prerequisites
- Docker
- Atlas installed on your machine:
- macOS + Linux
- Docker
- Windows
To download and install the custom release of the Atlas CLI, simply run the following in your terminal:
curl -sSf https://atlasgo.sh | ATLAS_FLAVOR="oracle" shTo pull the Atlas image and run it as a Docker container:
docker pull arigaio/atlas:latest-extended
docker run --rm arigaio/atlas:latest-extended --helpIf the container needs access to the host network or a local directory, use the
--net=hostflag and mount the desired directory:docker run --rm --net=host \
-v $(pwd)/migrations:/migrations \
arigaio/atlas:latest-extended migrate apply \
--url "oracle://PDBADMIN:Pssw0rd0995@localhost:1521/FREEPDB1"Download the custom release and move the atlas binary to a file location on your system PATH.
- An Atlas Pro account (run
atlas loginto authenticate)
Project Setup
Start a local Oracle 23ai container. The VECTOR type requires Oracle 23ai (23.4+):
docker run --rm \
-e ORACLE_PWD=Pssw0rd0995 \
-p 1521:1521 \
--name atlas-oracle \
-d container-registry.oracle.com/database/free:latest-lite
Oracle images are larger than most and take longer to initialize. Wait until the database is ready before continuing:
docker logs -f atlas-oracle 2>&1 | grep -m1 "DATABASE IS READY TO USE"
#########################
DATABASE IS READY TO USE!
#########################
Export the connection URL:
export DATABASE_URL="oracle://PDBADMIN:Pssw0rd0995@localhost:1521/FREEPDB1"
Grant PDBADMIN the privileges Atlas needs to manage schema objects:
docker exec -i atlas-oracle sqlplus \
-S 'sys/Pssw0rd0995@localhost:1521/FREEPDB1' AS SYSDBA <<SQL
GRANT CONNECT, RESOURCE, SELECT_CATALOG_ROLE TO PDBADMIN;
GRANT CREATE USER, CREATE ANY TABLE, CREATE VIEW TO PDBADMIN;
GRANT ALTER USER, ALTER ANY TABLE TO PDBADMIN;
GRANT DROP USER, DROP ANY TABLE TO PDBADMIN;
GRANT EXECUTE ON SYS.DBMS_LOCK TO PDBADMIN;
ALTER USER PDBADMIN QUOTA UNLIMITED ON SYSAUX;
SQL
Grant succeeded.
Grant succeeded.
Grant succeeded.
Grant succeeded.
Grant succeeded.
User altered.
Create an atlas.hcl configuration file:
- Atlas DDL (HCL)
- SQL
docker "oracle" "dev" {
image = "container-registry.oracle.com/database/free:latest-lite"
database = "FREEPDB1"
baseline = <<-SQL
ALTER USER PDBADMIN DEFAULT TABLESPACE SYSAUX;
ALTER USER PDBADMIN QUOTA UNLIMITED ON SYSAUX;
SQL
}
env "local" {
url = getenv("DATABASE_URL")
dev = docker.oracle.dev.url
schema {
src = "file://schema.ora.hcl"
}
migration {
dir = "file://migrations"
}
}
docker "oracle" "dev" {
image = "container-registry.oracle.com/database/free:latest-lite"
database = "FREEPDB1"
baseline = <<-SQL
ALTER USER PDBADMIN DEFAULT TABLESPACE SYSAUX;
ALTER USER PDBADMIN QUOTA UNLIMITED ON SYSAUX;
SQL
}
env "local" {
url = getenv("DATABASE_URL")
dev = docker.oracle.dev.url
schema {
src = "file://schema.sql"
}
migration {
dir = "file://migrations"
}
}
latest-lite ships without a USERS tablespace. Oracle needs a tablespace with Automatic Segment Space
Management (ASSM) to create VECTOR columns, and in this image only SYSAUX has it. The baseline in
the docker block sets PDBADMIN's default tablespace to SYSAUX before Atlas computes any diff, which
is what makes the dev container work correctly. Atlas manages the dev container's lifecycle on its own.
Defining the Schema
Atlas supports all VECTOR variants introduced in Oracle 23ai:
| Type expression | What it stores |
|---|---|
VECTOR(1536, FLOAT32) | Fixed-dimension, single-precision (e.g., OpenAI ada-002) |
VECTOR(1536, FLOAT64) | Fixed-dimension, double-precision |
VECTOR(1536, INT8) | Fixed-dimension, 8-bit integer (quantized) |
VECTOR(1536, BINARY) | Fixed-dimension binary vector |
VECTOR(*, FLOAT32) | Flexible dimensions (any embedding model) |
VECTOR | Fully flexible (any format, any dimensions) |
VECTOR(1000, INT8, SPARSE) | Sparse vector for high-dimensional keyword models |
Model this as a DOCUMENTS table that stores AI-generated embeddings alongside source text:
- Atlas DDL (HCL)
- SQL
table "DOCUMENTS" {
schema = schema.PDBADMIN
column "ID" {
type = NUMBER(10)
null = false
}
column "CONTENT" {
type = VARCHAR2(4000)
null = true
}
// Fixed dimensions, paired with OpenAI text-embedding-ada-002
column "EMBEDDING" {
type = VECTOR(1536, FLOAT32)
null = true
}
// Flexible dimensions, works with any embedding model
column "FLEX_EMBEDDING" {
type = VECTOR("*", FLOAT32)
null = true
}
// Untyped: store any vector without dimension or format constraints
column "RAW_VECTOR" {
type = VECTOR
null = true
}
// Sparse vector for keyword / BM25-style retrieval models
column "SPARSE_KEYWORDS" {
type = VECTOR(1000, INT8, SPARSE)
null = true
}
primary_key "PK_DOCUMENTS" {
columns = [column.ID]
}
}
schema "PDBADMIN" {}
CREATE TABLE "DOCUMENTS" (
"ID" NUMBER(10) NOT NULL,
"CONTENT" VARCHAR2(4000),
"EMBEDDING" VECTOR(1536, FLOAT32),
"FLEX_EMBEDDING" VECTOR(*, FLOAT32),
"RAW_VECTOR" VECTOR,
"SPARSE_KEYWORDS" VECTOR(1000, INT8, SPARSE),
CONSTRAINT "PK_DOCUMENTS" PRIMARY KEY ("ID")
);
Generating the Initial Migration
Run atlas migrate diff to generate the first migration file by comparing the schema file against the
empty database:
atlas migrate diff create_documents --env local
Atlas writes two files to the migrations/ directory:
migrations/
├── 20260331120000_create_documents.sql
└── atlas.sum
Open the generated file to see what Atlas will run:
-- Add new table named "DOCUMENTS"
CREATE TABLE "DOCUMENTS" (
"ID" NUMBER(10) NOT NULL,
"CONTENT" VARCHAR2(4000),
"EMBEDDING" VECTOR(1536, FLOAT32),
"FLEX_EMBEDDING" VECTOR(*, FLOAT32),
"RAW_VECTOR" VECTOR,
"SPARSE_KEYWORDS" VECTOR(1000, INT8, SPARSE),
CONSTRAINT "PK_DOCUMENTS" PRIMARY KEY ("ID")
);
Atlas generates correct VECTOR DDL for every variant (fixed, flexible, untyped, and sparse) directly
from the schema definition.
Applying the Migration
Apply the pending migration to the target database:
atlas migrate apply --env local
Migrating to version 20260331120000 (1 migrations in total):
-- migrating version 20260331120000
-> CREATE TABLE "DOCUMENTS" (
"ID" NUMBER(10) NOT NULL,
"CONTENT" VARCHAR2(4000),
"EMBEDDING" VECTOR(1536, FLOAT32),
"FLEX_EMBEDDING" VECTOR(*, FLOAT32),
"RAW_VECTOR" VECTOR,
"SPARSE_KEYWORDS" VECTOR(1000, INT8, SPARSE),
CONSTRAINT "PK_DOCUMENTS" PRIMARY KEY ("ID")
)
-- ok (14.159708ms)
-------------------------
-- 1.928394166s
-- 1 migration
-- 1 sql statement
Verify the deployed schema by inspecting the live database:
atlas schema inspect --env local
The atlas_schema_revisions table Atlas uses internally for migration tracking is omitted from this output.
table "DOCUMENTS" {
schema = schema.PDBADMIN
column "ID" {
null = false
type = NUMBER(10)
}
column "CONTENT" {
null = true
type = VARCHAR2(4000)
}
column "EMBEDDING" {
null = true
type = VECTOR(1536, FLOAT32)
}
column "FLEX_EMBEDDING" {
null = true
type = VECTOR("*", FLOAT32)
}
column "RAW_VECTOR" {
null = true
type = VECTOR
}
column "SPARSE_KEYWORDS" {
null = true
type = VECTOR(1000, INT8, SPARSE)
}
primary_key "PK_DOCUMENTS" {
columns = [column.ID]
}
}
schema "PDBADMIN" {
}
VECTOR columns round-trip cleanly: inspect, modify, and re-apply without data loss or type coercion.
With Atlas Cloud, push the migration directory and deploy from any CI/CD platform:
atlas migrate push app --env local
Making Incremental Changes
When requirements change, update the schema file and run migrate diff again. Atlas generates only what
changed, leaving existing data untouched.
For example, add a high-precision FLOAT64 embedding to support a more accurate semantic model:
- Atlas DDL (HCL)
- SQL
Add to schema.ora.hcl:
table "DOCUMENTS" {
schema = schema.PDBADMIN
// ... existing columns ...
// High-precision embedding column
column "EMBEDDING_F64" {
type = VECTOR(1536, FLOAT64)
null = true
}
primary_key "PK_DOCUMENTS" {
columns = [column.ID]
}
}
Add to schema.sql:
ALTER TABLE "DOCUMENTS" ADD "EMBEDDING_F64" VECTOR(1536, FLOAT64);
Generate the incremental migration:
atlas migrate diff add_f64_embedding --env local
Atlas generates only what changed (the new column):
-- Add columns to table "DOCUMENTS"
ALTER TABLE "DOCUMENTS" ADD "EMBEDDING_F64" VECTOR(1536, FLOAT64);
The migration directory now holds both files. The sum of all migrations represents the current state of the schema:
migrations/
├── 20260331120000_create_documents.sql
├── 20260331130000_add_f64_embedding.sql
└── atlas.sum
VECTOR in Procedures and Functions
VECTOR is a valid argument and return type in procedure and function blocks. This lets Atlas manage
PL/SQL objects that work with vector data as part of the same schema definition and migration workflow.
In Oracle PL/SQL, procedure and function argument types must use the base VECTOR type without dimension
or format parameters. Fixed types like VECTOR(1536, FLOAT32) are only valid in table column definitions.
Stored Procedure
Define a procedure that uses VECTOR_DISTANCE() to find the top-K nearest neighbours:
procedure "FIND_SIMILAR" {
schema = schema.PDBADMIN
arg "P_QUERY_VEC" {
type = VECTOR
}
arg "P_TOP_K" {
type = NUMBER
}
as = <<-SQL
BEGIN
FOR r IN (
SELECT ID, CONTENT,
VECTOR_DISTANCE(EMBEDDING, P_QUERY_VEC, COSINE) AS dist
FROM PDBADMIN.DOCUMENTS
ORDER BY dist
FETCH FIRST P_TOP_K ROWS ONLY
) LOOP
DBMS_OUTPUT.PUT_LINE(r.ID || ': ' || r.CONTENT);
END LOOP;
END;
SQL
}
Function with VECTOR Return Type
Define a function that returns a vector value:
function "NORMALIZE_VEC" {
schema = schema.PDBADMIN
return = VECTOR
arg "P_INPUT" {
type = VECTOR
}
as = <<-SQL
BEGIN
RETURN P_INPUT;
END;
SQL
}
After adding either object to the schema file, generate the migration:
atlas migrate diff add_vector_procedure --env local
-- Create "NORMALIZE_VEC" function
CREATE FUNCTION "NORMALIZE_VEC" (
"P_INPUT" VECTOR
)
RETURN VECTOR AS
BEGIN
RETURN P_INPUT;
END;;
-- Create "FIND_SIMILAR" procedure
CREATE PROCEDURE "FIND_SIMILAR" (
"P_QUERY_VEC" VECTOR,
"P_TOP_K" NUMBER
) AS
BEGIN
FOR r IN (
SELECT ID, CONTENT,
VECTOR_DISTANCE(EMBEDDING, P_QUERY_VEC, COSINE) AS dist
FROM PDBADMIN.DOCUMENTS
ORDER BY dist
FETCH FIRST P_TOP_K ROWS ONLY
) LOOP
DBMS_OUTPUT.PUT_LINE(r.ID || ': ' || r.CONTENT);
END LOOP;
END;;
Apply the migration:
atlas migrate apply --env local
Migrating to version 20260331184653 from 20260331181344 (1 migrations in total):
-- migrating version 20260331184653
-> CREATE FUNCTION "NORMALIZE_VEC" (
"P_INPUT" VECTOR
)
RETURN VECTOR AS
BEGIN
RETURN P_INPUT;
END;
-> CREATE PROCEDURE "FIND_SIMILAR" (
"P_QUERY_VEC" VECTOR,
"P_TOP_K" NUMBER
) AS
BEGIN
FOR r IN (
SELECT ID, CONTENT,
VECTOR_DISTANCE(EMBEDDING, P_QUERY_VEC, COSINE) AS dist
FROM PDBADMIN.DOCUMENTS
ORDER BY dist
FETCH FIRST P_TOP_K ROWS ONLY
) LOOP
DBMS_OUTPUT.PUT_LINE(r.ID || ': ' || r.CONTENT);
END LOOP;
END;
-- ok (52.571041ms)
-------------------------
-- 168.383542ms
-- 1 migration
-- 2 sql statements
Next Steps
- Oracle schema reference: all supported Oracle column types, functions, and procedures
- Versioned migrations setup: deploy migrations to production through CI/CD
- CI integration: lint schema changes automatically on every pull request
- Dev database reference: how Atlas uses the dev container to plan diffs
- Oracle automatic migrations guide: the declarative (stateless) workflow
Have questions? Find our team on Discord or schedule a demo.