Articles in this section

Celigo Storage query SQL reference

Celigo SQL is the SQL dialect you use to query Celigo Storage files. It's a curated subset of standard SQL, PostgreSQL-compatible where syntax is otherwise ambiguous, with the exceptions documented in this article. This reference covers the supported statements, clauses, and functions, the behaviors that differ from PostgreSQL, and what Celigo rejects before a query runs.

For an introduction to querying, see About querying Celigo Storage files. To run a query end to end, see Query files in Celigo Storage.

Supported vs. unsupported SQL

Celigo SQL is a curated surface, not a passthrough to the underlying engine. Three tiers apply:

  • Documented core — everything in this article. Supported and tested.
  • Undocumented syntax — some SQL that isn't listed here executes anyway. It isn't supported or tested against Celigo's regression suite, and it may stop working after a platform upgrade. Don't build on it.
  • Rejected — statements, functions, and references that fail validation before the query runs. See What Celigo rejects.

Statements

A request carries exactly one statement. A second statement is rejected with STORAGE_QUERY_MULTIPLE_STATEMENTS. A single trailing semicolon is fine.

Statement Description
SELECT Retrieve and transform rows. Includes the VALUES and TABLE shorthands.
WITH Define common table expressions, including WITH RECURSIVE.
DESCRIBE <query> Return the column names and inferred types a query would produce, one row per column. Runs as a normal query job — there is no separate schema endpoint.
SUMMARIZE <query> Return per-column statistics for the rows a query would produce: minimum, maximum, approximate distinct count, average, standard deviation, quartiles, count, and null percentage. Runs as a normal query job.

No other statement type executes. Data definition (CREATE, ALTER, DROP), data manipulation (INSERT, UPDATE, DELETE), COPY in either direction, SET, PRAGMA, INSTALL, LOAD, ATTACH, EXPORT, CALL, EXPLAIN, SHOW forms that list engine state, and every other statement family are rejected with STORAGE_QUERY_NOT_SELECT. This is one of the two enforcement points that make the query API read-only; the other is the worker's read-only credentials.

Clauses and expressions

Feature Supported forms
Projection SELECT, SELECT DISTINCT, column aliases
Filtering WHERE with comparison operators, IN, BETWEEN, LIKE, IS NULL
Grouping GROUP BY, HAVING
Ordering ORDER BY with ASC and DESC, NULLS FIRST and NULLS LAST
Paging LIMIT, OFFSET
Joins INNER JOIN, LEFT JOIN, RIGHT JOIN, FULL OUTER JOIN, CROSS JOIN
Set operations UNION, UNION ALL, INTERSECT, EXCEPT
Subqueries Scalar, IN, EXISTS, and derived tables
Common table expressions WITH, including RECURSIVE
Window functions OVER (PARTITION BY … ORDER BY …), row_number, rank, lag, lead, running and moving aggregates
Conditional logic CASE … WHEN … THEN … ELSE … END
Inline tables FROM (VALUES (…), (…)) AS alias(col1, col2)
Aggregates COUNT, SUM, AVG, MIN, MAX, including COUNT(DISTINCT …) and the FILTER clause
Row generators generate_series and range with whole-number literal arguments

Inline mapping tables

An inline VALUES table gives you a lookup table without a file:

SELECT o.region_code, m.region_name, SUM(o.total) AS revenue
FROM '/staging/orders/' AS o
JOIN (VALUES ('NA', 'North America'), ('EU', 'Europe')) AS m(code, region_name)
  ON o.region_code = m.code
GROUP BY o.region_code, m.region_name

Window functions

Window functions attach an aggregate computed over the whole dataset to every row, without collapsing rows:

SELECT order_id, customer_id, total,
       SUM(total) OVER (PARTITION BY customer_id) AS customer_lifetime_total,
       row_number() OVER (PARTITION BY customer_id ORDER BY order_date DESC) AS recency_rank
FROM '/staging/orders/'

Row generators

generate_series and range produce rows without a file, for example to build a date spine. Three rules apply, each enforced before the query runs:

  • Every argument must be a whole-number literal, so that Celigo can check the row count in advance. An expression, a column, or a parameter fails with STORAGE_QUERY_GENERATOR_ARGUMENT_NOT_LITERAL.
  • Celigo multiplies the row counts of every generate_series and range call in the statement, including calls in separate UNION ALL branches, and the product must be at most 1,000,000. Exceeding that fails with STORAGE_QUERY_GENERATOR_TOO_LARGE.
  • A statement that generates rows, through a generator, a recursive common table expression, or unnest of a literal list, must also read at least one storage file. A generator with no file reference fails with STORAGE_QUERY_NO_STORAGE_REFERENCE. A statement of constant expressions alone, such as SELECT 1, is allowed.

Nested data

JSON files produce nested structures, and Celigo SQL reads them directly. Column types are inferred automatically.

Access Syntax Example
Struct field Dot notation customer.region
List element Bracket indexing items[1].sku
Explode a list into rows UNNEST UNNEST(items)
SELECT customer.region, item.sku, item.quantity
FROM '/staging/orders.json',
     UNNEST(items) AS t(item)
WHERE customer.region = 'EU'

In results, STRUCT values arrive as JSON objects and LIST values as arrays. A key whose values have different types across records is typed as JSON, arrives as a string containing JSON text, and is reported as a VARCHAR column. Nesting is typed at any depth unless you set maximum_depth on the JSON reader, in which case nesting below that depth is typed as JSON too. To work with a JSON-typed value as data, extract the fields you need with json_extract and cast them. See Celigo storage query read options and format behavior.

Type conversion

TRY_CAST returns NULL instead of failing when a value can't be converted. Use it on data you don't control:

SELECT order_id, TRY_CAST(total AS DECIMAL(10,2)) AS total_amount
FROM '/staging/orders/'
WHERE TRY_CAST(total AS DECIMAL(10,2)) IS NOT NULL

Behaviors that differ from PostgreSQL

These are the deviations to know before you port a query.

Behavior Celigo SQL Notes
Division 7 / 2 returns 3.5 / always produces a floating-point result. Use // for integer division: 7 // 2 returns 3. This is the largest deviation from PostgreSQL.
Division by zero x / 0 and x // 0 don't raise an error They produce non-finite values, which arrive as null in results, because infinity and NaN aren't valid JSON.
64-bit integers and decimals Arrive as strings 64-bit integers, unsigned 64-bit integers, 128-bit integers, and decimals arrive as strings so that no digits are lost, and every whole-number column inferred from a CSV or JSON file is typed as a 64-bit integer. COUNT, SUM over integers, and // follow the same rule; AVG and / return numbers. Declare the column's type in the reader, or cast the expression, to receive a number.
Mixed-type JSON field Typed as JSON, arrives as a string of JSON text A key missing from some objects reads as NULL.
Multi-file schema drift Columns are combined by name by default Every column across all files in a queried folder appears; a file missing a column produces NULL for it. Set union_by_name = false on the reader to keep only the first file's columns. See Celigo storage query read options and format behavior.
Empty file Returns zero rows A zero-byte file queried directly completes with zero rows and one placeholder column. Inside a folder, it adds no rows.
random(), now() Allowed, nondeterministic Each run evaluates them again and may produce different values. Avoid them where you need repeatable results.

What Celigo rejects

Validation runs before execution. A rejected query returns a specific error naming what was rejected, and no query runs. Four checks apply:

  1. Statement type — anything outside the SELECT family, and any request carrying more than one statement.
  2. Functions — only allowlisted functions may appear. The allowlist is the engine's own function library minus the functions that report engine or host state, such as version(), current_setting(), and getenv(). A function outside the allowlist fails with STORAGE_QUERY_FUNCTION_NOT_ALLOWED, and the error names the function. A misspelled function name produces the same error.
  3. Table references — every base-table reference must resolve to a common table expression defined in the same statement, or to a Celigo Storage file or folder path. Schema-qualified and catalog-qualified references such as information_schema.tables are rejected, as are wildcard patterns — * and ? aren't valid path characters.
  4. Statement size — the SQL text is capped at 16 KB.

Rejected function families

Family Why it's rejected
Dynamic SQL Runs SQL assembled at runtime, which would bypass validation entirely.
Arbitrary filesystem and network readers Read bytes outside Celigo Storage, list the filesystem, or reach the network — for example, glob, read_text, and any reader called with a raw s3:// or https:// URL.
Engine and secret introspection Expose stored credentials, engine configuration, and internals — for example, version(), current_setting(), and getenv().
Engine state mutation Change engine state from inside a query.

Allowed table functions

The only table functions allowed are the file readers (read_csv, read_json, read_ndjson, read_parquet, and their _auto variants), unnest, generate_series, and range. Every other table function is rejected with STORAGE_QUERY_TABLE_FUNCTION_NOT_ALLOWED, including glob, read_text, and query. Each reader's first argument must be a literal path or a literal list of paths, and it's validated exactly like a normal file reference, so no reader can read outside your permitted folders.

Learn more