Python — Snowpark & connector
Port your existing Snowflake and Snowpark code to ObeliskDB by changing the import — the APIs are drop-in compatible, and every query flows through the same governed SQL front door (pruning, caching, RBAC, history).
obelisk.snowpark — lazy DataFrames
Snowpark-compatible: a DataFrame is an immutable logical plan, transformations wrap it, and only actions generate SQL and execute. Swap snowflake.snowpark for obelisk.snowpark and your code runs unchanged.
from obelisk.snowpark import Session
from obelisk.snowpark.functions import col, sum, count, upper, when, lit
session = Session.builder.configs({
"database": "DEMO", "schema": "SALES", "warehouse": "COMPUTE_WH",
}).create()
df = (session.table("EVENTS")
.filter(col("DAY_NUM") > 10)
.with_column("K", upper(col("KIND")))
.group_by("K")
.agg(sum(col("EVENT_ID")).alias("TOTAL"), count().alias("N"))
.sort(col("TOTAL").desc()))
df.show() # pretty-print
rows = df.collect() # list[Row] with attribute access
pdf = df.to_pandas() # pandas DataFrame
print(df.queries) # the SQL it compiles to
df.write.mode("overwrite").save_as_table("KIND_TOTALS")
df.create_or_replace_view("KIND_TOTALS_V")
Also: session.sql("..."), session.create_dataframe(data, schema), session.range(n), df.join(other, on="COL"|["A","B"], how="left"), df.union / union_all / distinct / limit / where, when(cond, v).otherwise(v), col(...).isin/.is_null/.cast/.asc/.desc.
obelisk.connector — the DB-API
Shaped like snowflake.connector — port existing code by changing the import:
import obelisk.connector as connector
conn = connector.connect(database="DEMO", schema="SALES",
warehouse="COMPUTE_WH", role="ANALYST")
cur = conn.cursor()
cur.execute("SELECT * FROM EVENTS WHERE DAY_NUM = %(d)s", {"d": 17})
print(cur.sfqid, cur.rowcount, [c.name for c in cur.description])
rows = cur.fetchall()
pdf = cur.fetch_pandas_all()
cur.execute("SELECT * FROM EVENTS WHERE EVENT_ID = ?", (42,)) # qmark too
dcur = conn.cursor(connector.DictCursor) # dict rows
from obelisk.connector import write_pandas
write_pandas(conn, my_dataframe, "MY_TABLE")
Because the connector honors the session role, tools built on it (BI scripts, notebooks, real Streamlit) inherit RBAC and masking automatically.
Snowflake emulator
obelisk emulator serves the real Snowflake REST protocol, so the official snowflake-connector-python (and anything built on it) connects to ObeliskDB with zero code changes — not even the import:
obelisk emulator # port 8084
import snowflake.connector # the real package
conn = snowflake.connector.connect(
user="me", password="anything", account="obelisk",
host="127.0.0.1", port=8084, protocol="http",
warehouse="COMPUTE_WH", database="DEMO", schema="SALES",
)
cur = conn.cursor()
cur.execute("SELECT COUNT(*) FROM EVENTS WHERE DAY_NUM = %s", (17,))
print(cur.fetchone(), cur.sfqid)
Works: login/session context, typed results (NUMBER→Decimal, DATE, TIMESTAMP), pyformat/qmark binds, DML, SHOW, USE, real ProgrammingErrors with error codes. Sessions get their own role — RBAC and masking apply to emulated traffic.
Known gap: fetch_pandas_all() needs the Arrow wire format (not yet served) — use pd.DataFrame(cur.fetchall(), columns=[c.name for c in cur.description]).
Use it to run Snowflake-targeting test suites in CI for free, develop against production-shaped SQL offline, or demo without an account.
Postgres wire protocol
obelisk pgwire (port 5433) speaks pgwire v3, so the entire Postgres ecosystem connects with zero integration:
psql "host=127.0.0.1 port=5433 dbname=DEMO user=me"
import psycopg
conn = psycopg.connect(host="127.0.0.1", port=5433, dbname="DEMO",
user="me", password="x")
cur = conn.cursor()
cur.execute("USE SCHEMA SALES"); cur.execute("USE WAREHOUSE COMPUTE_WH")
cur.execute("SELECT KIND, COUNT(*) FROM EVENTS WHERE DAY_NUM = %s", (17,))
Simple and extended query flows, text + binary parameter formats (int, float, Decimal/numeric, bool, date, timestamp), honest type OIDs, real error responses. SQL is still the Snowflake dialect — time travel, streams, SHOW, branches all work over pgwire. Transaction control (BEGIN/COMMIT/SET) is acknowledged as a no-op: ObeliskDB autocommits with per-statement snapshots.