Skip to Content
We are live but in Staging 🎉

Schema

Schema evolution and database-level stats. See the Tables hub for the full RPC list.

OperationSurface
AlterTabledodil.tables.v1.Tables/AlterTable — typed AlterSpec or ALTER TABLE SQL
Schema introspectionDESCRIBE "<table>" via Execute — see Lifecycle
Database statsdodil.tables.v1.Tables/GetDatabaseStats, or DESCRIBE DATABASE <db>

DescribeTable and ListPartitions are not RPCs. Neither appears in dodil-tables/proto/api/tables.proto. Schema comes from DESCRIBE; storage, residency and WAL numbers come from GetDatabaseStats, which aggregates per database (optionally per table). There is no per-partition file-count API.

AlterTable

AlterTableRequest carries a oneof ddl — a typed AlterSpec or a raw ALTER TABLE SQL string. The typed spec only carries add_columns. Drops and retypes are reachable only through the SQL arm.

What is supported

FormBehaviour
ADD COLUMN <c> <type> [NOT NULL] [DEFAULT <expr>]Real Delta schema change. Existing rows read as NULL (or the default). Multiple in one statement work.
DROP COLUMN <c>Logical — the name is recorded on the HTAP sidecar and every schema-derived surface subtracts it. Delta data is untouched, so it is reversible and needs no rewrite.
ALTER COLUMN <c> SET DATA TYPE <type>Logical, widening only — recorded on the sidecar, read egress CASTs, writes keep the physical type.
ADD CONSTRAINT … CHECK (…)Real and enforced (v0.1.31): validated against the CHECK grammar at DDL (unenforceable forms → 0A000), stored on the table, enforced at every write (23514) from the next statement.
ADD CONSTRAINT … FOREIGN KEY / UNIQUEAccepted as an advisory no-op with a NOTICE (“NOT ENFORCED”) so Django/Rails/Alembic migrations proceed. The enforced uniqueness form is CREATE UNIQUE INDEX; referential integrity is the app’s.
DROP CONSTRAINT <name>Removes a stored CHECK by name; a never-stored name (an FK in a downgrade) is a silent no-op.

The widening set is closed: short → int, short → long, short → double, int → long, int → double, long → double, float → double. Anything else is refused with "… is not a supported WIDENING (allowed: short→int→long, int/long/float→double); narrowing or incompatible retypes need a table rewrite".

What is refused

  • RENAME COLUMN and RENAME TABLE — and every other ALTER TABLE form: "ALTER TABLE form not supported by warehouse_alter_table v1: ADD COLUMN and DROP COLUMN are implemented".
  • ALTER COLUMN other than SET DATA TYPE (SET DEFAULT, DROP NOT NULL, …) — "ALTER COLUMN form not supported … only SET DATA TYPE".
  • Dropping or retyping a primary-key column"cannot drop a PRIMARY KEY column" / "cannot retype a PRIMARY KEY column".
  • Dropping a column that does not exist, or is already dropped — both error rather than no-op.
  • An ALTER TABLE that carries nothing to do"ALTER TABLE carries nothing to do (add/drop/retype)". The one exception is a constraint-only statement, which short-circuits to success.
  • Adding a column whose DEFAULT does not type-check — validated at DDL time, before the Delta log is touched, not on the first insert.

For a rename, or a narrowing retype, recreate the table with CREATE TABLE … AS SELECT.

Request

-- add ALTER TABLE events ADD COLUMN session_id VARCHAR; ALTER TABLE events ADD COLUMN device VARCHAR DEFAULT 'unknown'; -- logical drop ALTER TABLE events DROP COLUMN legacy_flag; -- widening retype (int → long) ALTER TABLE events ALTER COLUMN retry_count SET DATA TYPE BIGINT;

Response

message AlterTableResponse { string table_name = 1; int64 version = 2; repeated string columns_added = 3; // TOTAL column count after the alter, NOT the number added. uint32 column_count = 4; }
{ "table_name": "events", "version": 43, "columns_added": ["session_id", "device"], "column_count": 7 }

A pure drop or pure retype is sidecar-only: columns_added is empty and version is the table’s unchanged Delta version.

Database stats

GetDatabaseStats is the aggregate read — the three truth-dimensions of a database (storage, residency, freshness) plus the reservation and the dead-letter count. It is short-TTL cached, so it never hammers the serving path. DESCRIBE DATABASE <db> reads the same collector.

Request

DESCRIBE DATABASE "acme--kb-prod";

Response

message DatabaseStats { string db_id = 1; // STORAGE — summed Delta add-action bytes + catalog row counts. uint64 stored_bytes = 2; uint32 table_count = 3; repeated TableStats tables = 4; // only when include_tables // RESIDENCY — summed across the readers serving this db. uint64 ram_resident_bytes = 5; uint64 disk_resident_bytes = 6; ReservationInfo reservation = 7; // WAL — the freshness truth. uint64 wal_segments = 8; uint64 wal_entries_estimate = 9; string wal_high_ulid = 10; // empty = fully drained // Dead-lettered rows awaiting attention (0 = clean). uint64 rejected_rows = 11; uint64 collected_ms_ago = 12; // 0 = just collected } message TableStats { string table = 1; int64 row_count = 2; // -1 when never drained (unknown) uint64 size_bytes = 3; int64 delta_version = 4; uint64 wal_backlog_entries = 5; bool fully_resident = 6; uint64 rejected_rows = 7; }
{ "db_id": "acme--kb-prod", "stored_bytes": "184729302", "table_count": 2, "ram_resident_bytes": "67108864", "disk_resident_bytes": "184729302", "wal_segments": "3", "wal_entries_estimate": "128", "wal_high_ulid": "01JX7QK9E4...", "rejected_rows": "0", "collected_ms_ago": "0" }

Key fields:

  • wal_high_ulidempty means fully drained, which is exactly the frontier condition under which eventual ≡ strong and the plane takes the analytical fast path. A non-empty value means reads are being served STRONG.
  • wal_segments / wal_entries_estimate — how much un-drained history sits in front of Delta. Both go to zero shortly after a Compact.
  • rejected_rows — dead-lettered rows dropped by schema validation. Non-zero means writes are silently losing data; investigate before trusting counts.
  • row_count of -1 means the table has never been drained, so the catalog has no count — not that it is empty.

See also

  • CreateCreateTable · CreateTablePipeline
  • LifecycleSHOW TABLES · DESCRIBE · TRUNCATE · DropTable
  • MaintenanceCompact drains the WAL; OptimizeTable compacts small files
  • Core Concepts → Table — type signature + the fifteen-type vocabulary
  • CLI Guidedodil data table describe