Spark compatibility
What Databasin Sail does exactly like Apache Spark 4.0, from partition overwrite and ANSI arithmetic to CSV readers and views, plus the differences and what isn't supported yet.
Spark SQL and PySpark run on Sail. This page lists where Sail matches Apache Spark, where it differs, and what isn't supported yet.
We check Sail against Apache Spark 4.0 (with Delta Lake and Apache Iceberg) by running thousands of the same statements and DataFrame calls on both and comparing the results. Newer functions are checked against Spark 4.2. Many of the differences those checks found are now fixed. The ones you're most likely to meet are listed below, along with the features Sail doesn't have yet.
Run your existing queries and notebooks first. When something differs, check the lists at the bottom of this page.
What behaves like Spark
Writing tables
- Dynamic partition overwrite. With
spark.sql.sources.partitionOverwriteMode=dynamic,INSERT OVERWRITEreplaces only the partitions you write, on Iceberg and Delta tables. INSERTcolumn lists and static partition values (PARTITION (region = 'west')).- Row counts for
INSERT,MERGE,UPDATEandDELETEcome back in Spark's shape. - Default table format.
CREATE TABLEwithoutUSINGtakes the catalog's format: Iceberg in an Iceberg catalog, Delta in a Delta one. A different explicit format is refused. CREATE OR REPLACE TABLEwith or withoutAS SELECT,CREATE TABLE LIKE,COMMENT ON TABLE.DROP TABLE … PURGEandDROP SCHEMA … CASCADE.- Iceberg
MERGE WITH SCHEMA EVOLUTION.
Numbers, dates and time zones
- ANSI arithmetic. Integer overflow raises an error as Spark's ANSI mode does, instead of wrapping silently.
- Rounding and decimals.
roundrounds half up; decimal precision,avgrounding, type widening and double-to-string conversion match Spark. - Dates. Day-of-week functions,
to_timestampon a number, andRANGEwindow frames over dates. - Time zones.
SET TIME ZONEandspark.sql.session.timeZonework as in Spark on Iceberg and Delta tables.
Strings, regex and functions
- Java regular expressions, with Spark's semantics for
rlike,regexp_extract,regexp_replaceand the rest. split,substring,initcapand the binary string functions.- Collations
UTF8_BINARYandUTF8_LCASEin comparisons,IN,LIKE, joins,ORDER BYand windows (not yet inGROUP BYor set operations onUTF8_LCASE). approx_count_distinctgives the same estimates as Spark, andpercentile_approxaccepts arrays.session_window,input_file_name()and the_metadatacolumn, and some functions added in Spark 4.1 and 4.2 (see the Sail SQL reference).
Reading files
- CSV reads as Spark's reader does, including
PERMISSIVE,DROPMALFORMEDandFAILFASTmodes,_corrupt_record, andencoding/charset. - JSON parse modes,
multiLine,inferTimestampandprefersDecimal. - Reader options Sail doesn't support fail with a clear error instead of being ignored.
- Folder listing follows Spark's rules:
basePath, globs,recursiveFileLookup, and__HIVE_DEFAULT_PARTITION__read asNULL. Folders with conflicting layouts fail instead of dropping rows. - Parquet
INT96timestamps.
SQL statements
PIVOT,GROUP BY ALL,ORDER BY ALLand lateral column aliases.DISTRIBUTE BY,CLUSTER BY,SORT BY,TABLESAMPLEwithROWS,LIKE ANY/LIKE ALL.SET,RESETand${var}substitution; session variables andEXECUTE IMMEDIATE; temporary SQL functions.DESCRIBEin its forms, includingDESCRIBE QUERYand single columns.- Persistent views in lake catalogs:
CREATE VIEW,CREATE OR REPLACE VIEW,ALTER VIEW … ASandDROP VIEW. Iceberg views written in Trino SQL are translated when you read them, if they mean the same thing in Spark SQL; otherwise Sail tells you why it can't run them. CACHE TABLE,UNCACHE TABLEandREFRESHanswer as Spark does.
DataFrames
describe(),summary(),transpose(),groupingSets(),approxQuantile(),crosstab().cache()andpersist()report storage levels as Spark does.- Result order. Results come back in partition order, as Spark returns them:
UNION ALLbranch by branch, andshow(),take(),first()andLIMITfrom the first partition on.
Differences from Spark
These are deliberate, or known and not yet changed:
| Area | On Sail |
|---|---|
cache(), persist() |
Keep no copy of the data. Results are the same; repeat reads use the node's local file cache instead. |
| Global temporary views | Visible only to the user who created them, not shared across users. |
CHAR(n) |
Values aren't padded with spaces when read or compared. |
DATE - DATE |
Returns a day-to-second interval type. |
monotonically_increasing_id |
Values can differ from Spark's, because range() splits into a different number of partitions. |
EXPLAIN |
Shows Sail's own plan, not Spark's. Tools that parse Spark plans won't read it. |
information_schema |
Appears as a schema in every catalog. |
| Partition columns from paths | Stay strings; Spark infers numbers and dates. |
| Empty or missing folder | Reads as an empty result; Spark raises an error. |
| Result order | Reads over many files, and repartition(n), can return rows in a different order. |
| Regex | Forward back references aren't supported, nesting is limited to 64 levels, and very long backtracking stops. |
multiLine JSON |
Reads each file whole; a string column over 2 GB fails. |
| Substitution | ${env:…} and ${system:…} are refused. |
| Functions | Persistent CREATE FUNCTION and SQL SECURITY DEFINER are refused; temporary SQL functions work. |
| Delta time travel | Refused on a table with hidden columns. |
TIME type |
Supported and on by default. Spark 4.0 refuses it; Spark 4.2 has it behind a setting. |
Some Spark features are turned off on purpose, for security on a shared lakehouse: reading or writing storage by path (except your project's /Files), LOCATION on tables, engine-managed table properties and settings, Python UDFs on shared clusters, writes to tables with hidden columns, and the socket and console sources. How Sail works explains each one.
Not supported yet
- Readers and formats: ORC and XML files, the Avro functions (
from_avro,to_avro). - SQL: pipe syntax,
ALTER DATABASEandCOMMENT ON DATABASE, ICU collations,st_srid. - Tables: Delta
MERGEinto identity columns, IcebergREPLACE WHEREbeyond whole partitions. - Access control: row filters and column masks. A table protected by one is refused.
- DataFrame API:
_joinAsOfandobserve(). - Outside Spark SQL: Spark ML (MLlib), Structured Streaming, the RDD API, Scala,
addArtifacts. See Python and PySpark on Sail.
We haven't yet compared these with Spark: Delta deletion vectors, change data feed, CHECK constraints and generated columns. If you rely on them, test on Sail first.
Each cluster keeps the Sail build it was created with, so a cluster created a while ago may not have every fix on this page yet. Before you rely on one, such as dynamic partition overwrite, try it on a small table first. If a result on your cluster doesn't match Spark and isn't listed here, tell us through support.