Skip to Content
We are live but in Staging 🎉
API ReferenceTableService API

TableService API

Package: dodil.k3.tables.v1

TableService owns table engine lifecycle, table CRUD/schema, SQL/data operations, and table maintenance.

What It Is For

  • Enable per-bucket table engine.
  • Create manual tables or pipeline-generated tables.
  • Execute SQL and DML-like operations.
  • Optimize, vacuum, restore, compact, and inspect table history.

Endpoint Map

Engine

gRPC methodHTTP route
EnableEnginePOST /:bucket/tables/_engine
GetEngineGET /:bucket/tables/_engine
DisableEngineDELETE /:bucket/tables/_engine

Tables and Schema

gRPC methodHTTP route
CreateTablePOST /:bucket/tables
CreateTablePipelinePOST /:bucket/tables/pipelines
ListTablesGET /:bucket/tables
GetTableGET /:bucket/tables/:table_name
DeleteTableDELETE /:bucket/tables/:table_name
AlterTablePATCH /:bucket/tables/:table_name/schema
DescribeTableGET /:bucket/tables/:table_name/describe
ListPartitionsGET /:bucket/tables/:table_name/partitions

Data and SQL

gRPC methodHTTP route
QueryPOST /:bucket/tables/_query
ExecutePOST /:bucket/tables/_execute
InsertPOST /:bucket/tables/:table_name/insert
MergePOST /:bucket/tables/:table_name/merge
UpdatePOST /:bucket/tables/:table_name/update
DeleteRowsPOST /:bucket/tables/:table_name/delete-rows

Maintenance and Materialization

gRPC methodHTTP route
OptimizePOST /:bucket/tables/:table_name/optimize
VacuumPOST /:bucket/tables/:table_name/vacuum
RestorePOST /:bucket/tables/:table_name/restore
HistoryGET /:bucket/tables/:table_name/history
CompactPOST /:bucket/tables/:table_name/_compact
MaterializePOST /:bucket/tables/:source_table/materialize
ListTemplatesGET /:bucket/tables/_templates

Key Arguments

Create manual table

FieldTypeRequiredPurpose
bucketstringyesBucket scope
namestringyesTable name
descriptionstringnoTable description
columnsColumn[]yesColumn definitions
partition_columnsstring[]noPartition keys
merge_keysstring[]noKeys for merge semantics

Column core fields:

  • name
  • type (STRING, INT, LONG, FLOAT, DOUBLE, BOOLEAN, DATE, TIMESTAMP, BINARY, JSON)
  • nullable
  • optional default_expression

Create pipeline-generated table

FieldTypeRequiredPurpose
bucketstringyesBucket scope
namestringyesTarget table name
template_idstringyesAnalytics/extraction template
folder_prefixstringnoScope auto-generated ingest rule

Query and execute

QueryRequest fields:

FieldTypeRequiredPurpose
bucketstringyesBucket scope
table_namestringyesTarget table
sqlstringyesSQL (supports {table} placeholder)
limitint64noMax rows
lookup_tablesmap<string,string>noAlias-to-table joins
freshnessenumnoEVENTUAL, STRONG, OLTP_ONLY

ExecuteRequest fields:

  • bucket
  • sql
  • optional freshness for read plans

DuckDB SQL Dialect and Query Patterns

TableService SQL paths use a DuckDB-oriented SQL dialect for analytical reads.

Dialect profile

  1. ANSI-style SQL core (SELECT, JOIN, GROUP BY, ORDER BY, CTEs, subqueries).
  2. DuckDB analytical functions (window functions, percentile/quantile functions, rich date/time functions).
  3. JSON-friendly querying through DuckDB JSON functions when columns store JSON payloads.
  4. Fast analytical scans and aggregations through vectorized execution.
Query typeExample SQLWhy use it
Aggregate metricsSELECT kind, COUNT(*) AS cnt FROM {table} GROUP BY kind ORDER BY cnt DESCFast rollups for dashboards and ops reporting
Time-bucket analysisSELECT date_trunc('hour', created_at) AS hour_bucket, COUNT(*) FROM {table} GROUP BY 1 ORDER BY 1Reliable time-series summaries without extra ETL
Window ranking/dedupSELECT id, kind, created_at, ROW_NUMBER() OVER (PARTITION BY kind ORDER BY created_at DESC) AS rn FROM {table}Ranking, latest-record extraction, and dedup patterns
JSON field extractionSELECT json_extract_string(payload, '$.customer_tier') AS tier, COUNT(*) FROM {table} GROUP BY tierQuery nested attributes without flattening first
Percentile/SLO viewsSELECT quantile_cont(duration_ms, 0.95) AS p95_ms FROM {table}Useful for latency and quality SLO monitoring
Multi-table joinsSELECT e.id, d.owner FROM {table} e LEFT JOIN dim_owners d ON e.owner_id = d.owner_idEnrich fact data with dimensions for analysis

Query endpoint usage guidance

  1. Use Query when you want a rowset response.
  2. Use Execute for statement execution flows where row materialization is not required, or when routing through the CLI query path.
  3. For data mutation semantics, prefer dedicated APIs (Insert, Merge, Update, DeleteRows) instead of ad-hoc SQL mutations.

Insert/merge/update/delete

OperationKey request fields
Insertrows (JSON-encoded row strings), mode (append/overwrite)
Mergerows, match_columns, when_matched, when_not_matched
Updateupdates (protobuf struct map), predicate
DeleteRowspredicate

Maintenance

OperationKey request fields
Optimizetarget_file_size_mb, z_order_columns
Vacuumretention_hours, dry_run, disable_retention_check
Restoreversion or restore_timestamp
Historylimit, before_version
Compactbatch_size

Examples

Enable table engine

curl -sS -X POST "https://k3.dev.dodil.io/kb-prod/tables/_engine" \ -H "Authorization: Bearer $K3_TOKEN" \ -H "x-organization-id: $K3_ORG" \ -H "Content-Type: application/json" \ -d '{"bucket":"kb-prod"}'

Create a manual table

curl -sS -X POST "https://k3.dev.dodil.io/kb-prod/tables" \ -H "Authorization: Bearer $K3_TOKEN" \ -H "x-organization-id: $K3_ORG" \ -H "Content-Type: application/json" \ -d '{ "bucket": "kb-prod", "name": "events", "columns": [ {"name": "id", "type": "COLUMN_TYPE_LONG", "nullable": false}, {"name": "kind", "type": "COLUMN_TYPE_STRING", "nullable": true}, {"name": "created_at", "type": "COLUMN_TYPE_TIMESTAMP", "nullable": false} ], "partition_columns": ["kind"], "merge_keys": ["id"] }'

Insert rows

curl -sS -X POST "https://k3.dev.dodil.io/kb-prod/tables/events/insert" \ -H "Authorization: Bearer $K3_TOKEN" \ -H "x-organization-id: $K3_ORG" \ -H "Content-Type: application/json" \ -d '{ "bucket": "kb-prod", "table_name": "events", "rows": [ "{\"id\":1,\"kind\":\"deploy\",\"created_at\":\"2026-05-14T09:00:00Z\"}", "{\"id\":2,\"kind\":\"incident\",\"created_at\":\"2026-05-14T09:05:00Z\"}" ], "mode": "append" }'

Run SQL query

curl -sS -X POST "https://k3.dev.dodil.io/kb-prod/tables/_query" \ -H "Authorization: Bearer $K3_TOKEN" \ -H "x-organization-id: $K3_ORG" \ -H "Content-Type: application/json" \ -d '{ "bucket": "kb-prod", "table_name": "events", "sql": "SELECT kind, COUNT(*) AS cnt FROM {table} GROUP BY kind", "freshness": "FRESHNESS_EVENTUAL" }'

Create a pipeline-generated table

curl -sS -X POST "https://k3.dev.dodil.io/kb-prod/tables/pipelines" \ -H "Authorization: Bearer $K3_TOKEN" \ -H "x-organization-id: $K3_ORG" \ -H "Content-Type: application/json" \ -d '{ "bucket": "kb-prod", "name": "lease_extracts", "template_id": "lease_extraction", "folder_prefix": "leases" }'

Run a compaction tick

curl -sS -X POST "https://k3.dev.dodil.io/kb-prod/tables/events/_compact" \ -H "Authorization: Bearer $K3_TOKEN" \ -H "x-organization-id: $K3_ORG" \ -H "Content-Type: application/json" \ -d '{"bucket":"kb-prod","table_name":"events","batch_size":10000}'

Common Use Cases

  1. Build analytics tables directly on object-derived data.
  2. Maintain mixed OLTP/OLAP workloads with freshness-aware reads.
  3. Run operator maintenance jobs (optimize, vacuum, _compact) for predictable performance.

Back to API index