Learn BigQuery feature comparison

BigQuery Feature Comparison

Production BigQuery vs Go Emulator (goccy/bigquery-emulator) vs Python Rewrite (SQLGlot + DuckDB)

Date: 2026-04-20

Legend: Y = Supported | P = Partial | N = Not Supported | = Not Applicable


Platform & Runtime

FeatureProduction BQGo EmulatorPython Rewrite
ARM64 / Apple Silicon (native)N (go-zetasql is x86-64 only, requires QEMU)Y (DuckDB native ARM64)
Single binary / containerY (Go static binary)Y (Docker w/ Python)
In-process testing (same process)Y (Go httptest)Y (Python fixture)
Persistent storageYY (SQLite file)Y (DuckDB file)
In-memory modeYYY

DDL Statements

FeatureProduction BQGo EmulatorPython Rewrite
CREATE TABLEYYY
CREATE TABLE IF NOT EXISTSYYY
CREATE OR REPLACE TABLEYYY
CREATE TEMP TABLEYYY
CREATE TABLE LIKE / COPY / CLONEYNN (planned P2)
CREATE TABLE with PARTITION BYYN (issue #152)P (DDL accepted, metadata stored; no physical partitioning)
CREATE TABLE with CLUSTER BYYN (issue #373, bug)P (DDL accepted, metadata stored)
CREATE TABLE with OPTIONSYPP (SQLGlot drops OPTIONS silently; must intercept pre-transpile)
CREATE VIEWYYY
CREATE MATERIALIZED VIEWYNN (planned P2)
CREATE EXTERNAL TABLEYNN (planned P3)
CREATE SCHEMAYNY
CREATE FUNCTION (SQL UDF)YYY (via DuckDB CREATE MACRO)
CREATE TABLE FUNCTIONYNN
CREATE PROCEDUREYNN (planned P1, requires scripting interpreter)
CREATE SNAPSHOT TABLEYNN (planned P3)
CREATE ROW ACCESS POLICYYNN (stub)
CREATE SEARCH INDEXYNN (stub)
ALTER TABLE ADD COLUMNYN (issue #412)Y
ALTER TABLE DROP COLUMNYNY
ALTER TABLE RENAMEYNY
ALTER TABLE SET OPTIONSYNP (SQLGlot truncates OPTIONS)
DROP TABLE / VIEW / FUNCTION / SCHEMAYP (TABLE, VIEW, FUNCTION only)Y

DML Statements

FeatureProduction BQGo EmulatorPython Rewrite
INSERTYYY
UPDATEYP (no UPDATE…FROM with joins, issue #310)Y
DELETEYYY
TRUNCATE TABLEYYY
MERGEYP (parser accepts, execution buggy: issues #128, #163, #299)Y (SQLGlot transpiles correctly)
EXPORT DATAYN (issue #418)N (planned P3)
LOAD DATAYNN (SQLGlot ParseError)

Query Language

FeatureProduction BQGo EmulatorPython Rewrite
SELECT * / EXCEPT / REPLACEYYY
SELECT AS STRUCT / VALUEYYY
INNER / LEFT / RIGHT / FULL / CROSS JOINYYY
Correlated subqueriesYNY
Sequential JOINsYNY
Non-recursive CTEs (WITH)YYY
Recursive CTEs (WITH RECURSIVE)YN (issue #216)Y (DuckDB native)
UNION / INTERSECT / EXCEPT [ALL]YYY
QUALIFYYYY (DuckDB native)
PIVOT / UNPIVOTYYY (DuckDB native)
TABLESAMPLEYNP (syntax differs; BERNOULLI→RESERVOIR semantic mismatch)
ORDER BY NULLS FIRST/LASTYNY (DuckDB native)
UNNEST with WITH OFFSETYYY (→ WITH ORDINALITY)
Implicit UNNESTYNN
Pipe syntax (|>)YNP (WHERE/SELECT/AGGREGATE/JOIN work; RENAME/CALL/SET/DROP/LIMIT crash)
Wildcard tables / _TABLE_SUFFIXYYN (SQLGlot quotes * as identifier; planned P1)
Named parameters (@param)YYY (→ $param)
Table decorators (@timestamp)Y (Legacy SQL)NN

Window Functions

FeatureProduction BQGo EmulatorPython Rewrite
ROW_NUMBER / RANK / DENSE_RANKYYY
LEAD / LAGYYY
FIRST_VALUE / LAST_VALUE / NTH_VALUEYYY
NTH_VALUE(x, n FROM LAST)YNN (SQLGlot ParseError)
NTILE / CUME_DIST / PERCENT_RANKYYY
PERCENTILE_CONT / PERCENTILE_DISCYP (issue #205, inconsistent results)Y
Window over NULL partitionsYN (panic, issue #351)Y

Scripting / Procedural Language

FeatureProduction BQGo EmulatorPython Rewrite
DECLARE / SETYN (issue #344)P (single statements transpile; blocks truncated by SQLGlot — requires custom interpreter)
BEGIN…ENDYYP (SQLGlot truncates after first statement)
IF / ELSEIF / ELSE / END IFYY (basic)P (SQLGlot truncates block)
LOOP / WHILE / REPEAT / FOR…INYNN (requires custom interpreter)
BREAK / CONTINUEYNN
RETURNYNN (SQLGlot ParseError)
RAISEYNN (SQLGlot ParseError)
CALL procedureYNN
EXECUTE IMMEDIATEYNN
BEGIN TRANSACTION / COMMITYYY
ROLLBACKYNY
BEGIN…EXCEPTION…ENDYNN

Data Types

TypeProduction BQGo EmulatorPython Rewrite
INT64 / FLOAT64 / BOOLYYY
STRINGYYY
BYTESYYY (→ BLOB)
NUMERIC / DECIMALYYY
BIGNUMERIC (76 digits)YYP (DuckDB DECIMAL max 38 digits)
DATE / TIME / DATETIME / TIMESTAMPYY (timezone bugs)Y (DATETIME→TIMESTAMP, TIMESTAMP→TIMESTAMPTZ)
INTERVALYYY
ARRAYYP (blob encoding; NULL/nested bugs)Y (→ LIST; must enforce no nested arrays)
STRUCT / RECORDYP (blob encoding; nested query bugs)Y (→ STRUCT; no REQUIRED/REPEATED mode enforcement)
JSONYP (type works; function bugs)Y
GEOGRAPHYYN (3 of ~60 ST_* functions)N (DuckDB spatial is planar, not geodesic)

Functions

Core Function Categories

CategoryProduction BQGo EmulatorPython Rewrite
Math functions (~30)YYY
String functions (~50)YP (CONTAINS_SUBSTR, COLLATE, EDIT_DISTANCE missing)Y (most transpile; TO_CODE_POINTS, NORMALIZE pass through)
Date/Time functions (~40)YY (edge-case bugs: DATE_TRUNC HOUR, DATETIME_DIFF off-by-one)Y (TIMESTAMP(dt,tz) has reversed semantics)
JSON functions (~15)YP (multiple bugs: #357, #379, #389, #428)Y (LAX_* functions pass through)
Array functions (~15)YP (crashes with length comparison, issue #410)Y
Aggregate functions (~20)YY (COVAR_POP bug; MAX_BY/MIN_BY missing)Y (ARRAY_AGG IGNORE NULLS silently dropped)
Approximate aggregates (~5)YYP (APPROX_TOP_COUNT return type differs; APPROX_TOP_SUM missing)
HLL_COUNT.* sketch functionsYYN (no sketch serialization in DuckDB)
Window functions (~15)YP (NULL partition crash; PERCENTILE_CONT inconsistency)Y (NTH_VALUE FROM LAST is ParseError)
Hash/UUID (MD5, SHA*, FARM_FINGERPRINT)YYY

Specialized Function Categories

CategoryProduction BQGo EmulatorPython Rewrite
NET.* (10 functions)YYN (pass through; planned P1 via Python UDFs)
Geography / ST_* (~60 functions)YN (3 only)N (DuckDB spatial is planar; planned P3)
SAFE_CASTYYY (→ TRY_CAST)
SAFE.* prefix (generic)YP (named SAFE_DIVIDE etc. exist; generic prefix unclear)N (no generic mechanism; planned P1)
SAFE_DIVIDE / SAFE_ADD / etc.YYY (SQLGlot handles these)
FORMAT() with %t/%T/%PYYN (BQ-specific specifiers pass through)
ERROR()YNP (DuckDB has error() — casing may differ)
AEAD encryption (KEYS., AEAD.)YNN

Partitioning & Clustering

FeatureProduction BQGo EmulatorPython Rewrite
Time-unit column partitioningYN (issue #152)P (DDL metadata stored; no physical partitioning)
Ingestion-time partitioningYP (issue #317)P (metadata + pseudocolumn injection)
Range partitioningYNP (metadata only)
_PARTITIONTIME / _PARTITIONDATE pseudocolumnsYNP (planned P0, injected by emulator)
Partition pruningYNP (via DuckDB zonemap push-down)
Partition expirationYNP (application-layer TTL)
Required partition filterYNP (application-layer enforcement)
CLUSTER BY (DDL)YN (bug, issue #373)P (DDL accepted, metadata stored)
Cluster pruningYNN (DuckDB has no user-declared clustering)

INFORMATION_SCHEMA

ViewProduction BQGo EmulatorPython Rewrite
TABLESYP (basic, limited schema)P (planned P0; synthesize from catalog)
COLUMNSYP (basic)P (planned P0; synthesize)
SCHEMATAYPP (planned P0)
PARTITIONSYNP (planned P0; from partition metadata)
ROUTINES / PARAMETERSYNP (planned P1)
TABLE_STORAGEYNP (planned P2)
JOBS / JOBS_BY_USERYNP (planned P2; from job log)
VIEWSYNP (planned P1)
OBJECT_PRIVILEGESYNN (stub)
SEARCH_INDEXES / VECTOR_INDEXESYNN (stub)
RESERVATIONS / BI_CAPACITIESYNN (not applicable)
STREAMING_TIMELINEYNN (not applicable)

User-Defined Functions

FeatureProduction BQGo EmulatorPython Rewrite
SQL scalar UDFsYYY (→ CREATE MACRO)
SQL table-valued functionsYNP (→ CREATE MACRO … AS TABLE)
JavaScript UDFsYP (basic cases; issue #337)N (cannot execute JS)
Python UDFsN (remote functions only)NY (via DuckDB Python API)
User-defined aggregates (UDAFs)YNN
Remote functionsYNN
Persistent UDFs (stored in dataset)YYY (DuckDB catalog)
Temporary UDFsYYY

BigQuery ML

FeatureProduction BQGo EmulatorPython Rewrite
CREATE MODELYNN (SQLGlot drops OPTIONS)
ML.PREDICT / ML.EVALUATEYNN (SQLGlot ParseError)
ML.GENERATE_TEXTYNN
AI.FORECASTYNN (SQLGlot ParseError)

REST API

EndpointProduction BQGo EmulatorPython Rewrite
datasets.* (CRUD)YYY (planned)
tables.* (CRUD)YYY (planned)
tabledata.insertAll (streaming)YYY (planned)
tabledata.listYYY (planned)
jobs.query (synchronous)YYY (planned)
jobs.insert (async)YYY (planned)
jobs.get / list / cancelYYY (planned)
projects.listYYY (planned)
routines.* (CRUD)YYY (planned)
models.* (metadata only)YY (stub)N (stub)
IAM (getIamPolicy, etc.)YN (stub)N (stub)
Transfer APIYNN
Reservation APIYNN
Connection APIYNN

gRPC Storage API

FeatureProduction BQGo EmulatorPython Rewrite
Read API (CreateReadSession)YP (encoding bugs with arrays, nullable fields; issue #409)Y (planned Phase 4)
Avro format readsYP (issue #398, schema bytes in data)Y (planned)
Arrow format readsYPY (DuckDB native Arrow)
Write API (default stream)YN (issue #246)Y (planned Phase 4)
Write API (COMMITTED stream)YN (issue #247)Y (planned)
Write API (PENDING stream)YP (issue #342, stream not found)Y (planned)
BatchCommitWriteStreamsYN (issue #380, panic)Y (planned)
SSL/TLS on gRPCYN (issue #259)Y (planned)

Advanced Features

FeatureProduction BQGo EmulatorPython Rewrite
Materialized ViewsYNN (planned P2)
External TablesYNN (planned P3)
Table Snapshots / ClonesYNN (planned P3)
FOR SYSTEM_TIME AS OF (time travel)YNN (planned P3)
Stored ProceduresYNN (planned P1)
Row-level access policiesYNN (stub)
Column-level security / policy tagsYNN (stub)
Search indexes / SEARCH()YNN (stub)
Differential privacy aggregatesYNN
BI Engine accelerationYN— (DuckDB is in-memory)
GCS integration (load/extract)YP (CSV/JSON only)P (planned; Parquet/CSV/JSON)

Summary Scorecard

CategoryGo EmulatorPython RewriteWinner
ARM64 / Apple SiliconNYPython
DDL (basic)YYTie
DDL (ALTER TABLE)NYPython
DDL (partitioning/clustering)NP (metadata)Python
DML (INSERT/UPDATE/DELETE)P (UPDATE joins broken)YPython
DML (MERGE)P (buggy)YPython
Core query languageYYTie
Correlated subqueriesNYPython
Recursive CTEsNYPython
Window functionsP (NULL crash)YPython
ScriptingN (~0%)P (requires interpreter)Python
INFORMATION_SCHEMAP (minimal)P (synthesized, more planned)Python
Math/String/Date functionsYYTie
JSON functionsP (multiple bugs)YPython
Array/Struct operationsP (encoding bugs)YPython
Geography / ST_*N (3 functions)N (wrong model)Tie (both bad)
NET. functions*YN (planned)Go
HLL_COUNT. sketches*YNGo
APPROX_ aggregates*YPGo
Wildcard tables / _TABLE_SUFFIXYN (planned)Go
JavaScript UDFsPNGo
SAFE. prefix*PN (planned)Go
REST APIY (full)Y (planned)Tie
gRPC Storage Read APIP (encoding bugs)Y (planned, Arrow native)Python
gRPC Storage Write APIN (broken)Y (planned)Python
Type correctness (ARRAY/STRUCT/JSON)P (SQLite blob encoding)Y (DuckDB native columnar)Python
Semantic correctnessP (many edge-case bugs)P (TIMESTAMP tz reversed, IGNORE NULLS)Tie (different bugs)

Score Summary

Go EmulatorPython Rewrite
Categories where it wins5 (NET, HLL, APPROX, wildcards, JS UDFs)16 (ARM64, ALTER, MERGE, CTEs, correlated, etc.)
Categories tied77
Categories where it loses165

Key Takeaway

The Go emulator has a working REST API and covers some niche function categories (NET, HLL sketches, wildcards) that the Python rewrite will need to build. However, it has fundamental structural limitations — x86-64 only, SQLite blob encoding causing type bugs, broken MERGE/UPDATE, no ALTER TABLE, no partitioning, no scripting, broken gRPC Write API.

The Python rewrite gains significant ground on SQL correctness (DuckDB’s native columnar types vs SQLite blob encoding), modern SQL features (recursive CTEs, correlated subqueries), and ARM64 support. Its main gaps are features that need custom emulator-layer implementation: scripting interpreter, INFORMATION_SCHEMA synthesis, wildcard tables, SAFE.* prefix, and NET.* functions.

Last updated: May 2025