Skip to main content

Data types

A CamusDB column has a strong type. You declare that type in CREATE TABLE, or in ALTER TABLE ... ADD COLUMN. CamusDB uses the type for the storage, for a comparison, for an index, for a cast, for a default, and for a JSON value.

Reference of the types​

SQL typeStoresIndexableNotes
OIDA 12-byte ObjectIdYesThe native type of an identifier. SQL also accepts OBJECT_ID. An HTTP table definition names the type id.
UUIDA 128-bit UUIDYesThe native type of a UUID or a GUID. CamusDB also accepts GUID.
INT64A 64-bit signed integerYesCamusDB also accepts INT, INTEGER, and SMALLINT.
FLOAT64An IEEE-754 doubleYesCamusDB also accepts FLOAT. In a CAST, it also accepts DOUBLE.
FLOAT32An IEEE-754 singleYesCamusDB also accepts REAL. It stores and compares the value at single precision.
BOOLA booleanYesCamusDB also accepts BOOLEAN.
STRINGUTF-16 textYesIt uses the default limit on the length of a string. CamusDB also accepts CHAR, VARCHAR, and TEXT.
STRING(N)UTF-16 text, with a maximum of N charactersYesN must be a positive integer. CHAR(N) and VARCHAR(N) use the same bound.
DATEA calendar date, without a timeYesCamusDB stores it as UTC ticks, truncated to midnight.
DATETIMEAn instant in UTCYesCamusDB also accepts TIMESTAMP.
BYTESAn opaque string of bytesYesCamusDB also accepts BLOB.
BYTES(N)An opaque string of bytes, with a maximum of N bytesYesN must be a positive integer. BLOB(N) uses the same bound.
ARRAY(T)An ordered list of scalar values of type TNoT must be a scalar type. You cannot use an array in a primary key, and you cannot use one in an index.
CREATE TABLE events (
id OID PRIMARY KEY NOT NULL,
external_id UUID DEFAULT (gen_uuid_v7()),
name STRING(64) NOT NULL,
payload BYTES,
score FLOAT32,
happened_at DATETIME,
event_day DATE,
tags ARRAY(INT64)
);

Aliases of the types​

AliasCanonical type
INT, INTEGER, SMALLINTINT64
FLOATFLOAT64
REALFLOAT32
TIMESTAMPDATETIME
BLOBBYTES
CHAR, VARCHAR, TEXTSTRING
OBJECT_IDOID
GUIDUUID
BOOLEANBOOL

In SQL, a column of an ObjectId uses OID or OBJECT_ID. In an HTTP table definition, the same type has the name id. The SQL identifier id is still an ordinary column name.

Use a UUID or a GUID column for a UUID identifier. Do not store a UUID in a STRING column. CamusDB stores a native UUID as a compact 128-bit value. It also uses an index encoding of a fixed width that preserves the order. A native UUID is therefore more efficient than UUID text, in memory, on disk, and in an index.

The length of a string and of a bytes value​

STRING(N) accepts at most N UTF-16 code units. A bare STRING column uses the default maximum length of 2,621,440 characters.

BYTES(N) accepts at most N bytes. A bare BYTES column uses the default maximum length of 10,485,760 bytes, which is 10 MB.

Both bounds are maximums, not fixed widths. A BYTES(3072) column accepts a shorter value without complaint. Add a CHECK constraint when a column must hold an exact number of bytes. Vector search shows that pattern for an embedding.

CamusDB rejects a value that is too long. It does not truncate that value. An insert, an update, and a cast that exceeds the bound of the column fails with CADB0302 ValueTooLong. A NULL has no length. CamusDB therefore does not check a NULL against these bounds.

CREATE TABLE documents (
id OID PRIMARY KEY NOT NULL,
title STRING(120) NOT NULL,
body STRING,
attachment BYTES
);

Large values​

A large STRING, BYTES/BLOB, or ARRAY value can be stored compressed, or under its own key outside the row. Each column has a storage strategy: PLAIN, MAIN, EXTERNAL, or the default EXTENDED.

CREATE TABLE files (
id OID PRIMARY KEY,
name STRING,
content BYTES STORAGE EXTERNAL
);

ALTER TABLE files ALTER COLUMN content SET STORAGE PLAIN;

The strategy never changes the value returned by a query. It changes the I/O a query does and the size of each stored row version. See Large Values.

Arrays​

ARRAY(T) stores an ordered list of one type. The type of an element must be scalar:

CREATE TABLE measurements (
id OID PRIMARY KEY NOT NULL,
samples ARRAY(FLOAT64),
labels ARRAY(STRING)
);

These rules apply to an array today:

  • CamusDB rejects an array inside an array, such as ARRAY(ARRAY(INT64)).
  • You cannot use an array column in a primary key. You cannot use it in a secondary index.
  • SQL supports an inline ARRAY[...] literal, in an expression and in the values of a DML statement.
  • CamusDB rejects a literal of an array inside an array, such as ARRAY[ARRAY[1]].
  • An element of an array may be NULL.

Read one element with a subscript, tags[1]. The first element is 1, and an index outside the array gives NULL. See PostgreSQL Expression Syntax.

Use Array Functions to read an array as a whole with cardinality, array_length, and array_contains. CamusDB also supports quantified comparisons such as value = ANY(array) and value > ALL(array). See PostgreSQL Expression Syntax.

INSERT INTO measurements (id, samples, labels)
VALUES (GEN_ID(), ARRAY[1.5, 2.0, 2.5], ARRAY['alpha', 'beta']);

UPDATE measurements
SET labels = ARRAY[]
WHERE id = STR_ID('507f1f77bcf86cd799439011');

The literal formats of SQL​

TypeForm of the SQL literalExample
INT64An integer42
FLOAT64, FLOAT32A decimal number3.14
STRINGA quoted string"hello" or 'hello'
BOOLtrue or falsetrue
OIDA quoted ObjectId string of 24 characters"507f1f77bcf86cd799439011"
UUIDA quoted UUID string, with hyphens or as 32 hexadecimal digits"550e8400-e29b-41d4-a716-446655440000"
DATEA quoted string in the form yyyy-MM-dd"2026-03-15"
DATETIMEA quoted UTC string in the ISO-8601 form"2026-03-15T12:00:00Z"
BYTESHexadecimal bytes, in the form X'...'X'DEADBEEF'
ARRAY(T)ARRAY[...]ARRAY[1, 2, 3]

A numeric literal uses invariant formatting. The decimal separator is therefore ., whatever the locale of the server is.

CamusDB parses the text of a date and of a datetime as UTC. It returns the text of a UUID in the canonical form: lowercase, and with hyphens. An invalid input of a date, a datetime, a UUID, a byte value, or a cast fails with InvalidInput.

String literals​

CamusDB supports two forms of a string literal.

A plain string uses a single quotation mark, or a double one. It does not process a backslash as an escape. CamusDB stores a backslash as a normal character. Repeat the active quotation mark to escape it:

SELECT 'plain text';
SELECT 'C:\Users\data';
SELECT 'it''s ready';
SELECT "say ""hello""";

A plain string is the correct form for most values. That includes the pattern of a regular expression, and a path on Windows:

SELECT name FROM files WHERE path = 'C:\Users\data';
SELECT name FROM users WHERE email ~ '^[^@]+@example\.com$';

An escape string uses the prefix E'...' or E"...". In that form, a backslash starts an escape sequence:

EscapeMeaning
\\A backslash
\', \"A quotation mark
\n, \r, \t, \0, \a, \b, \f, \vA control character
\NNNThe character of three octal digits
\xHHThe character of two hexadecimal digits
\uHHHH, \UHHHHHHHHA Unicode code point
COMMENT ON TABLE events IS E'first line\nsecond line';

Use an escape string when the value needs a control character. Examples are a new line, a tab, a carriage return, and a NUL. Three inputs fail with InvalidInput: a numeric escape with a wrong form, a Unicode escape outside the valid range, and a surrogate escape without its pair.

An escape that CamusDB does not know keeps only the escaped character. E'\d+' therefore stores d+. Use a plain string when you want a literal backslash.

SHOW CREATE TABLE and the other renderers of a schema emit a literal that the parser accepts again. They prefer the plain form. They use E'...' only when the value holds a control character.

Bytes literals​

Use X'...' for a typed literal of bytes:

INSERT INTO documents (id, attachment)
VALUES (GEN_ID(), X'DEADBEEF');

SELECT X'4d5a';
SELECT X''; -- empty byte string

The count of the hexadecimal digits must be even. CamusDB also accepts x'...'.

0xFF stays an integer literal. It is not a literal of bytes. CamusDB can still coerce string text such as '0xDEADBEEF' to BYTES, when it knows the target type. X'...' nevertheless carries the type of bytes directly. It is the preferred literal in SQL.

Array literals​

Use ARRAY[...] for an inline value of an array. Each element can be any scalar expression:

SELECT ARRAY[1, 2, 3];
SELECT ARRAY[n::string, upper(name), n + 1] FROM measurements;

INSERT INTO measurements (id, samples)
VALUES (GEN_ID(), ARRAY[1, 2, 3]);

CamusDB infers the type of the elements from the first element that is not NULL. Every other element must be compatible with that type. CamusDB coerces an element to the declared type of the target column, where that is necessary.

-- Accepted for ARRAY(FLOAT64): integer elements widen to float64.
INSERT INTO measurements (id, samples)
VALUES (GEN_ID(), ARRAY[1, 2, 3]);

-- Empty arrays adopt the target column's element type.
INSERT INTO measurements (id, samples)
VALUES (GEN_ID(), ARRAY[]);

CamusDB rejects a literal of an array inside an array.

Casts​

CAST(value AS type) works with a scalar type. The PostgreSQL shorthand value::type is equivalent:

SELECT
CAST("2026-03-15" AS DATE) AS event_day,
CAST("2026-03-15T12:00:00Z" AS DATETIME) AS happened_at,
CAST("550e8400-e29b-41d4-a716-446655440000" AS UUID) AS external_id,
CAST(X'DEADBEEF' AS BYTES) AS payload,
CAST(score AS FLOAT32) AS compact_score
FROM events;

SELECT "42"::INT64 + 1;

See Conversion Functions for the equivalent to_* functions.

Temporal functions​

A function for a date or a time returns a typed temporal value. It does not return a string:

FunctionReturn type
NOW(), CURRENT_TIMESTAMP()DATETIME
CURRENT_DATE()DATE
DATE_ADD(temporal, amount, unit)DATETIME
DATE_TRUNC(unit, temporal)DATETIME
FROM_UNIXTIME(seconds)DATETIME
DATE_DIFF(start, end, unit)INT64
DATE_PART(unit, temporal)INT64
UNIX_TIMESTAMP([temporal])INT64

A function that accepts a temporal input can use a DATE column and a DATETIME column directly. For example, created_at < NOW() compares two DATETIME values. INSERT INTO events (created_at) VALUES (NOW()) stores a typed datetime value, and it needs no cast.

See Date/Time Functions for the units, the rules of the parser, and some examples.

Reserved keywords of the types​

CamusDB reserves these names of a type, and these aliases:

oid object_id int int64 integer smallint string char varchar text bool boolean
float float32 float64 real date datetime timestamp bytes blob uuid guid array

CamusDB reserves the exact keyword only. An identifier that merely starts with the word of a type stays valid. Three examples are internal, dates, and blob_store.