Skip to content
Draft
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
102 changes: 93 additions & 9 deletions python/datafusion/unparser.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,51 +25,135 @@


class Dialect:
"""DataFusion data catalog."""
"""The SQL dialect an :py:class:`Unparser` writes.

The dialect decides how the generated SQL is spelled - most visibly how
identifiers are quoted - so the same logical plan produces different SQL
text for each dialect.
"""

def __init__(self, dialect: unparser_internal.Dialect) -> None:
"""This constructor is not typically called by the end user."""
self.dialect = dialect

@staticmethod
def default() -> "Dialect":
"""Create a new default dialect."""
"""Create a new default dialect.

This dialect leaves identifiers unquoted.

Example usage:

>>> ctx = dfn.SessionContext()
>>> _ = ctx.from_pydict({"a": [1, 2, 3], "b": [10, 20, 30]}, name="t")
>>> plan = ctx.sql("SELECT a FROM t").logical_plan()
>>> Unparser(Dialect.default()).plan_to_sql(plan)
'SELECT t.a FROM t'
"""
return Dialect(unparser_internal.Dialect.default())

@staticmethod
def mysql() -> "Dialect":
"""Create a new MySQL dialect."""
"""Create a new MySQL dialect.

This dialect quotes identifiers with backticks.

Example usage:

>>> ctx = dfn.SessionContext()
>>> _ = ctx.from_pydict({"a": [1, 2, 3], "b": [10, 20, 30]}, name="t")
>>> plan = ctx.sql("SELECT a FROM t").logical_plan()
>>> Unparser(Dialect.mysql()).plan_to_sql(plan)
'SELECT `t`.`a` FROM `t`'
"""
return Dialect(unparser_internal.Dialect.mysql())

@staticmethod
def postgres() -> "Dialect":
"""Create a new PostgreSQL dialect."""
"""Create a new PostgreSQL dialect.

This dialect quotes identifiers with double quotes.

Example usage:

>>> ctx = dfn.SessionContext()
>>> _ = ctx.from_pydict({"a": [1, 2, 3], "b": [10, 20, 30]}, name="t")
>>> plan = ctx.sql("SELECT a FROM t").logical_plan()
>>> Unparser(Dialect.postgres()).plan_to_sql(plan)
'SELECT "t"."a" FROM "t"'
"""
return Dialect(unparser_internal.Dialect.postgres())

@staticmethod
def sqlite() -> "Dialect":
"""Create a new SQLite dialect."""
"""Create a new SQLite dialect.

This dialect quotes identifiers with backticks.

Example usage:

>>> ctx = dfn.SessionContext()
>>> _ = ctx.from_pydict({"a": [1, 2, 3], "b": [10, 20, 30]}, name="t")
>>> plan = ctx.sql("SELECT a FROM t").logical_plan()
>>> Unparser(Dialect.sqlite()).plan_to_sql(plan)
'SELECT `t`.`a` FROM `t`'
"""
return Dialect(unparser_internal.Dialect.sqlite())

@staticmethod
def duckdb() -> "Dialect":
"""Create a new DuckDB dialect."""
"""Create a new DuckDB dialect.

This dialect quotes identifiers with double quotes.

Example usage:

>>> ctx = dfn.SessionContext()
>>> _ = ctx.from_pydict({"a": [1, 2, 3], "b": [10, 20, 30]}, name="t")
>>> plan = ctx.sql("SELECT a FROM t").logical_plan()
>>> Unparser(Dialect.duckdb()).plan_to_sql(plan)
'SELECT "t"."a" FROM "t"'
"""
return Dialect(unparser_internal.Dialect.duckdb())


class Unparser:
"""DataFusion unparser."""
"""Converts a :py:class:`~datafusion.plan.LogicalPlan` back into SQL text."""

def __init__(self, dialect: Dialect) -> None:
"""This constructor is not typically called by the end user."""
self.unparser = unparser_internal.Unparser(dialect.dialect)

def plan_to_sql(self, plan: LogicalPlan) -> str:
"""Convert a logical plan to a SQL string."""
"""Convert a logical plan to a SQL string.

Example usage:

>>> ctx = dfn.SessionContext()
>>> _ = ctx.from_pydict({"a": [1, 2, 3], "b": [10, 20, 30]}, name="t")
>>> plan = ctx.sql("SELECT a, b FROM t WHERE a > 1").logical_plan()
>>> Unparser(Dialect.default()).plan_to_sql(plan)
'SELECT t.a, t.b FROM t WHERE (t.a > 1)'
"""
return self.unparser.plan_to_sql(plan._raw_plan)

def with_pretty(self, pretty: bool) -> "Unparser":
"""Set the pretty flag."""
"""Set the pretty flag.

When set, redundant parentheses are omitted from the generated SQL.
The unparser is modified in place and returned, so the call can be
chained.

Example usage:

>>> ctx = dfn.SessionContext()
>>> _ = ctx.from_pydict({"a": [1, 2, 3], "b": [10, 20, 30]}, name="t")
>>> plan = ctx.sql("SELECT a, b FROM t WHERE a > 1").logical_plan()
>>> Unparser(Dialect.default()).plan_to_sql(plan)
'SELECT t.a, t.b FROM t WHERE (t.a > 1)'
>>> Unparser(Dialect.default()).with_pretty(True).plan_to_sql(plan)
'SELECT t.a, t.b FROM t WHERE t.a > 1'
"""
self.unparser = self.unparser.with_pretty(pretty)
return self

Expand Down