Spark SQL / Databricks

Spark SQL, Databricks SQL, and Hive all run on the same engine 3XCode converts to, so this dialect works fundamentally differently from every other source dialect. Read this before pointing 3XCode at a Databricks SQL or Hive script.

Overview

Spark SQL, Databricks SQL, and Hive/HiveQL are represented internally as a single value, spark_sql, in the conversion pipeline's SQLDialect enum, alongside tsql (SQL Server), plsql (Oracle), plpgsql (PostgreSQL), mysql, and ansi (generic ANSI SQL). During Discovery, a field validator normalizes dozens of spellings a source file or its LLM classification might use: "Hive", "HiveQL", and "Databricks SQL" all collapse to spark_sql, so variation in self-labeling doesn't affect classification.

What makes this page worth reading on its own: every other supported dialect is a genuinely different execution engine from PySpark, with real semantic gaps to close. Spark SQL / Databricks SQL / Hive is not a different engine: it runs on Spark already. That single fact changes what "conversion" means for this dialect end to end.

Why This Dialect Is Different

For T-SQL, PL/SQL, PL/pgSQL, MySQL, and generic ANSI sources, 3XCode's job is to close real semantic gaps between a foreign engine and Spark: things like DATEDIFF argument order reversing, NULL ordering differences, or cursors that have no direct PySpark equivalent. For Spark SQL / Databricks SQL / Hive, none of that applies: the source SQL already executes correctly on Spark today.

Other Source Dialects

  • Cross-engine: T-SQL / PL-SQL / PL-pgSQL / MySQL run on a different engine than Spark, with different execution semantics.
  • Correctness-driven: conversion exists to close real behavioral gaps: date-function argument order, NULL semantics, case-sensitivity, decimal precision.
  • Validation checks for behavioral drift: the whole gotcha checklist (DATEDIFF, cursors, transactions, string comparison) exists because source and target genuinely disagree on semantics.

Spark SQL / Databricks SQL / Hive

  • Same engine: the source SQL already runs on Spark. There is no foreign execution semantics to translate away.
  • Idiom-driven: the job is readability and maintainability (turning a raw SQL script into a well-structured PySpark module), not fixing correctness bugs that don't exist here.
  • A judgment call, not a translation: for each construct, the conversion agent decides between rewriting it as native DataFrame API or keeping it as a spark.sql() passthrough call.

3XCode still runs Spark SQL / Databricks SQL / Hive files through the same pipeline as every other dialect (Discovery, Planning, a deterministic Grouping step, Conversion, Validation, Auto-Fix) and still produces dependency-ordered, structured PySpark modules. What changes is what Validation is actually checking for, and what Conversion is actually deciding.

How Spark SQL / Databricks SQL Is Detected

Detection still runs the same two independent layers as every other dialect during Discovery. You never set a flag telling 3XCode what dialect or platform a file is in.

Dialect Classification (LLM)

Claude classifies the file into the SQLDialect enum during Discovery. A field validator normalizes 'Hive', 'HiveQL', and 'Databricks SQL' (among many other spellings) to the single spark_sql value.

Platform Fingerprinting (deterministic)

A separate, regex-only, zero-LLM-cost pass scores the file against syntax markers for 10 vendor platforms (including Databricks and Hive specifically) and returns the winning platform, a confidence label, and the exact markers that fired.

The platform layer matters more here than for any other dialect: spark_sql is a dialect family, and the specific vendor behind it (genuine Databricks SQL versus plain open-source Hive) determines which syntax extensions are actually valid in the source file. Every generated PySpark file carries a one-line header comment naming the detected platform, confidence, and matched markers, so that distinction is always visible in the output, not just inferred.

Discovery's object inventory can also legitimately come back empty for this dialect. An empty inventory is one of three defined discovery outcomes, and the pipeline explicitly treats "a file already written in Spark SQL" as a valid reason for it: the runner writes a clean no_objects audit rather than a false "success," because there may genuinely be nothing left to convert.

spark.sql() Passthrough vs. DataFrame API

Because there's no correctness gap to close, the Conversion phase's real decision for each construct in a Spark SQL / Databricks SQL / Hive source is an idiom choice: rewrite it as native PySpark DataFrame API, or keep it as a spark.sql("...") passthrough call inside a structured module.

Rewrite as DataFrame API

For ordinary SELECT / JOIN / GROUP BY / window-function logic, a DataFrame rewrite is usually the better outcome: it's more testable, composes with the rest of a Python pipeline, and gets type-checked and linted like the rest of your codebase.

Keep as spark.sql() passthrough

For constructs that are already correct, already idiomatic Spark SQL, and don't have a clean one-to-one DataFrame equivalent, rewriting them just to rewrite them adds risk without adding readability, so 3XCode keeps them as-is inside the generated module.

Same logic, two valid outcomes
# Ordinary aggregation, rewritten to DataFrame API for readability & testability
# Source: SELECT region, SUM(revenue) AS total FROM sales GROUP BY region

def _step_regional_totals(spark, sales_df):
    return (
        sales_df
        .groupBy("region")
        .agg(F.sum("revenue").alias("total"))
    )


# Databricks-specific QUALIFY dedup, kept as a spark.sql() passthrough
# because the source is already correct, already native Spark SQL

def _step_latest_per_customer(spark):
    return spark.sql("""
        SELECT *
        FROM orders
        QUALIFY ROW_NUMBER() OVER (
            PARTITION BY customer_id ORDER BY order_date DESC
        ) = 1
    """)

Both functions above are equally valid PySpark: the difference is a maintainability judgment, not a correctness fix. That judgment call, applied construct by construct, is what "conversion" actually means for this dialect.

Databricks-Only Syntax Is Preserved, Not Genericized

3XCode does not blindly flatten Databricks/Delta-flavored SQL extensions down to open-source Spark grammar, and it does not force them into a DataFrame-only rewrite that could change what the query actually does. Constructs that are already valid, already correct Spark-SQL/Delta syntax (some of which are also standard SQL more broadly, not exclusive to Databricks) are preserved as spark.sql() passthrough rather than being treated as a translation problem to solve.

Spark-SQL / Delta constructs 3XCode preserves

ConstructWhy it is preserved rather than rewritten
QUALIFYA post-aggregation window-function filter shorthand with no single clean DataFrame equivalent: expressing it as chained .withColumn(window) + .filter() calls is more verbose and easier to get subtly wrong than leaving the SQL as-is.
MERGE INTODelta Lake upsert semantics are tied to the underlying table format itself, not something the DataFrame API expresses directly. Rewriting it risks losing the exact upsert behavior the source relies on. Upsert/merge logic is still flagged as a manual-review item in the audit report, since the pipeline treats it as something a developer should double-check rather than a fully closed loop.
Recursive CTEsHierarchical/recursive queries have no DataFrame API equivalent at all in any dialect. For Spark SQL sources specifically, the query is already valid and running, so it is kept intact rather than forced into an unsupported shape.

Platform-aware preservation

Because platform fingerprinting distinguishes genuine Databricks from plain Hive, preserved syntax is checked against what the detected platform actually supports, rather than assuming every spark_sql file can safely use every Databricks extension.

Same audit trail as every dialect

Preserved constructs still flow through Validation and the audit report like any other output: you can see exactly what was kept as passthrough versus rewritten, with the detected platform and confidence noted in the file header.

Converting a Spark SQL / Databricks File

No flag is needed, dialect and platform are both detected automatically during Discovery, exactly as with every other dialect:

Terminal
# Convert a single Databricks SQL / Hive script
3xcode pyspark convert warehouse_jobs.sql

# Dry-run: discovery + planning only, no conversion
3xcode pyspark analyze warehouse_jobs.sql

# Convert every .sql file in a directory (bulk mode)
3xcode pyspark convert --dir ./sql/ --session "databricks-cleanup" -w 4

# Find the exact session ID before resuming
3xcode session list

# Resume an interrupted bulk session using the ID from the list above
3xcode pyspark convert --resume <session-id-from-list>

The --session name you pass in is a label, not the resumable ID. Run 3xcode session list to see the actual session ID before using --resume.

Check <stem>_audit.md after conversion the same way you would for any other dialect: for a spark_sql source, expect fewer behavioral-gotcha items and more readability/structure notes, plus a clear record of which constructs were kept as spark.sql() passthrough.

FAQ

If my SQL already runs on Spark, why convert it at all?

Running correctly on Spark isn't the same as being a well-structured, maintainable PySpark module. A lot of legacy Hive/Databricks SQL is a flat, monolithic script. 3XCode still applies dependency-ordered grouping (utilities, main pipeline, standalone jobs), produces a run_pipeline(spark, params) orchestrator, and chooses DataFrame API where it makes the code more testable and readable: the value is structure and idiom, not correctness.

Will 3XCode strip out QUALIFY, MERGE INTO, or recursive CTEs?

No. These are preserved as spark.sql() passthrough because they're genuine Databricks/Delta extensions that are already correct. 3XCode doesn't force them into a DataFrame rewrite or a generic open-source Spark grammar that might not support them the same way.

How does 3XCode tell genuine Databricks SQL apart from plain Hive?

The deterministic platform-fingerprinting layer scores the file against syntax markers for both platforms independently of dialect classification, and the winning platform, confidence, and matched markers are recorded in the generated file's header comment.

Does the usual gotcha checklist (DATEDIFF, NULL ordering, cursors) still apply?

Largely no: that checklist exists to close semantic gaps between a foreign engine and Spark. Since a spark_sql source already runs on Spark, those specific behavioral gaps don't exist here the way they do for T-SQL, PL/SQL, PL/pgSQL, or MySQL sources.

Is my source SQL sent to the 3XDE backend?

No. Source SQL is never sent to the 3XDE backend for any dialect, including this one. The CLI talks to your configured LLM provider directly for conversion. The backend only handles auth, licensing, and provider-key metadata.