SQL DDL
Use controller-managed SQL DDL to create, inspect, list, and drop Pinot tables and materialized views.
Pinot supports a controller-managed SQL DDL surface for table and materialized-view metadata operations. Use it when you want a SQL alternative to the JSON-based /schemas and /tables workflows.
The controller accepts one DDL statement per request:
CREATE TABLEDROP TABLESHOW TABLESSHOW CREATE TABLECREATE MATERIALIZED VIEWSHOW MATERIALIZED VIEWSSHOW CREATE MATERIALIZED VIEWDROP MATERIALIZED VIEW
Run these statements through the controller endpoint POST /sql/ddl, not through the broker query API. SSE and MSE still execute query statements; the controller owns table and materialized-view DDL.
How it works
POST /sql/ddl compiles SQL into the same Pinot Schema and TableConfig model used by the existing controller APIs. That means:
DDL-created tables go through the same controller validation path as
POST /tables.DDL-created materialized views persist as regular
OFFLINEtables plus the same MV task config the JSON APIs use.SHOW CREATE TABLErenders a canonical SQL form of the stored schema and table config that you can review, version, or replay.SHOW CREATE MATERIALIZED VIEWrenders canonical MV DDL for the stored definition, including theAS <query>clause and MV-specific properties.dryRun=truelets you compile and validate without persisting any metadata.Existing broker query APIs still handle
SELECTstatements. The controller endpoint handles metadata changes.
Supported statement shapes
`CREATE TABLE [IF NOT EXISTS] [db.]table (...) [PRIMARY KEY (...)] TABLE_TYPE = OFFLINE
REALTIME [PROPERTIES (...)]`
`DROP TABLE [IF EXISTS] [db.]table [TYPE OFFLINE
REALTIME]`
SHOW TABLES [FROM db]
Lists tables in the selected database.
`SHOW CREATE TABLE [db.]table [TYPE OFFLINE
REALTIME]`
CREATE MATERIALIZED VIEW [IF NOT EXISTS] [db.]name [(...)] [REFRESH [INTERVAL] EVERY ...] PROPERTIES (...) AS <select>
Creates an OFFLINE MV. Omit the column list to infer the MV schema from the SELECT projection, or provide a full column list to override inferred types or roles.
SHOW MATERIALIZED VIEWS [FROM db]
Lists materialized views in the selected database using raw names without the _OFFLINE suffix.
SHOW CREATE MATERIALIZED VIEW [db.]name
Returns canonical MV DDL for the stored metadata.
DROP MATERIALIZED VIEW [IF EXISTS] [db.]name
Drops an MV. There is no TYPE clause because MVs are always backed by OFFLINE tables.
Use either a db.table qualifier or a Database header to target a database. If both are present, they must refer to the same database; otherwise Pinot returns 400 Bad Request.
Endpoint contract
Send requests to the controller:
To target a database with an HTTP header:
Use dryRun=true when you want validation without persistence:
High-level response behavior:
201 Createdfor a successfulCREATE TABLE201 Createdfor a successfulCREATE MATERIALIZED VIEW200 OKforDROP TABLE,SHOW TABLES,SHOW CREATE TABLE,SHOW MATERIALIZED VIEWS,SHOW CREATE MATERIALIZED VIEW, dry runs, and idempotentIF EXISTSorIF NOT EXISTScases400 Bad Requestfor parse errors, semantic validation errors, or oversized SQL404 Not Foundwhen a requested table or schema does not exist409 Conflictfor duplicateCREATE TABLEwithoutIF NOT EXISTS, logical-table references that block a drop, or a race with another writer
Response bodies include only fields that apply to the executed operation.
operation
all responses
One of SHOW_TABLES, SHOW_MATERIALIZED_VIEWS, CREATE_TABLE, SHOW_CREATE_TABLE, DROP_TABLE, CREATE_MATERIALIZED_VIEW, SHOW_CREATE_MATERIALIZED_VIEW, or DROP_MATERIALIZED_VIEW.
databaseName
create, drop, show create
The database scope used for the operation when available.
tableName
create, drop, show create
The resolved Pinot table name, often with _OFFLINE or _REALTIME. MV operations still target OFFLINE storage under the hood.
tableType
table create, typed table drop, table show create
OFFLINE or REALTIME. MV-specific statements do not take a TYPE clause.
schema
create
The compiled Pinot schema JSON. Returned for dry runs and persisted creates, including MV creates.
tableConfig
create
The compiled Pinot table config JSON after table config tuner processing. Returned for dry runs and persisted creates, including MV creates.
warnings
create
Non-fatal compile warnings, such as ignored DECIMAL(p,s) precision details.
dryRun
create, drop
Whether the request validated without persisting or deleting metadata.
ifNotExists
create
Whether the statement used IF NOT EXISTS.
ifExists
drop
Whether the statement used IF EXISTS.
deletedTables
drop
Typed table names removed by the drop.
tableNames
catalog listings
Tables or materialized views visible in the selected database, depending on the statement.
ddl
show create
Canonical CREATE TABLE or CREATE MATERIALIZED VIEW SQL for the stored metadata.
message
most responses
Human-readable operation summary.
Columns, types, and defaults
Every column has a Pinot data type and an optional role. If you omit the role, Pinot treats the column as a single-value dimension.
name STRING
Single-value dimension.
name STRING DIMENSION
Single-value dimension.
tags STRING DIMENSION ARRAY
Multi-value dimension.
score DOUBLE METRIC
Metric column.
ts TIMESTAMP DATETIME FORMAT 'TIMESTAMP' GRANULARITY '1:MILLISECONDS'
Date-time column.
Supported data type names:
INT, INTEGER
INT
BIGINT, LONG
LONG
FLOAT, REAL
FLOAT
DOUBLE
DOUBLE
DECIMAL, NUMERIC, BIG_DECIMAL
BIG_DECIMAL
BOOLEAN
BOOLEAN
TIMESTAMP
TIMESTAMP
VARCHAR, CHAR, STRING
STRING
VARBINARY, BINARY, BYTES
BYTES
JSON
JSON
SMALLINT and TINYINT are intentionally rejected. Use INT until Pinot exposes narrower integer types.
Use NOT NULL to set a non-nullable field and DEFAULT to set Pinot's default null value:
DEFAULT NULL is rejected. Default literals must be compatible with the declared column type. TIMESTAMP defaults are emitted by SHOW CREATE TABLE in UTC ISO-8601 form, and BYTES defaults are emitted as quoted hex strings.
Properties mapping
Use the PROPERTIES (...) clause for table config values that are not part of the column list or TABLE_TYPE clause. Property keys and values are string literals.
Pinot routes properties with these rules:
Promoted scalar table config keys
Dedicated TableConfig fields
replication, brokerTenant, serverTenant, timeColumnName, retentionTimeUnit, retentionTimeValue, loadMode, sortedColumn, nullHandlingEnabled, aggregateMetrics, segmentVersion, tags, invertedIndexColumns, noDictionaryColumns, bloomFilterColumns, rangeIndexColumns, jsonIndexColumns
JSON blob table config keys
Nested TableConfig objects
ingestionConfig, upsertConfig, dedupConfig, routingConfig, queryConfig, quotaConfig, tierConfigs, tunerConfigs, fieldConfigs, instanceAssignmentConfigMap, tagOverrideConfig, starTreeIndexConfigs, segmentPartitionConfig, jsonIndexConfigs
streamType, stream.*, realtime.*
Realtime stream configs
stream.kafka.topic.name, stream.kafka.decoder.class.name, stream.kafka.consumer.factory.class.name, realtime.segment.flush.threshold.rows
task.<taskType>.<key>
Minion task config
task.RealtimeToOfflineSegmentsTask.bucketTimePeriod
Any other key
Table custom config
owner, team.pipeline, custom.flag
List-valued promoted properties use comma-separated strings, for example 'invertedIndexColumns' = 'userId,country'. Stream and realtime properties are valid only on REALTIME tables.
Example: create an offline table
Submit that statement with POST /sql/ddl:
Example: create a realtime Kafka table
Example: create an upsert table
Use PRIMARY KEY with a realtime table and pass the upsert configuration as a JSON property:
Example: multi-value dimension
Use DIMENSION ARRAY for multi-value dimensions:
Example: indexes and ingestion config
Promoted list properties use comma-separated strings. Nested table configs use JSON strings.
Example: task config
Use task.<taskType>.<key> properties for minion task configs:
Example: create and inspect a materialized view
Use CREATE MATERIALIZED VIEW when you want the controller to persist an MV as an OFFLINE table plus MaterializedViewTask metadata. The column list is optional: omit it to infer the schema from the SELECT projection, or provide a full column list if you need to override inferred types or roles.
Use the same endpoint to list, inspect, and drop MVs:
SHOW CREATE MATERIALIZED VIEW emits canonical DDL for the stored MV definition, including an explicit column list even if the original create statement relied on inferred columns.
Example: show the stored table definition
Example response:
Canonical DDL uses deterministic ordering for properties and normalizes values where Pinot stores a canonical form. For example, a TIMESTAMP date-time column emits FORMAT 'TIMESTAMP', boolean defaults emit TRUE or FALSE, and identifiers that need quoting are double-quoted.
Example: list and drop tables
Use DROP TABLE events without TYPE to drop both the offline and realtime variants when they exist. Use DROP TABLE IF EXISTS events when a missing table should be a successful no-op.
When to keep using JSON APIs
If you already manage table metadata through POST /schemas, POST /tables, or PUT /tables/{tableName}, those APIs still work. SQL DDL is an additional interface, not a replacement for the existing controller metadata APIs. For materialized views, keep using raw JSON metadata if you need a MaterializedViewTask schedule that is not expressible as REFRESH EVERY <N> MINUTES|HOURS|DAYS or '<N>m|h|d'.
Related pages
Last updated
Was this helpful?

