e6data-spark-compatibility 1.0.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- e6_spark_compat/__init__.py +50 -0
- e6_spark_compat/_vendor/__init__.py +35 -0
- e6_spark_compat/_vendor/sqlglot/__init__.py +180 -0
- e6_spark_compat/_vendor/sqlglot/__main__.py +96 -0
- e6_spark_compat/_vendor/sqlglot/_typing.py +17 -0
- e6_spark_compat/_vendor/sqlglot/_version.py +34 -0
- e6_spark_compat/_vendor/sqlglot/dialects/__init__.py +126 -0
- e6_spark_compat/_vendor/sqlglot/dialects/athena.py +288 -0
- e6_spark_compat/_vendor/sqlglot/dialects/bigquery.py +1443 -0
- e6_spark_compat/_vendor/sqlglot/dialects/clickhouse.py +1417 -0
- e6_spark_compat/_vendor/sqlglot/dialects/databricks.py +290 -0
- e6_spark_compat/_vendor/sqlglot/dialects/dialect.py +2004 -0
- e6_spark_compat/_vendor/sqlglot/dialects/doris.py +687 -0
- e6_spark_compat/_vendor/sqlglot/dialects/dremio.py +124 -0
- e6_spark_compat/_vendor/sqlglot/dialects/drill.py +160 -0
- e6_spark_compat/_vendor/sqlglot/dialects/druid.py +20 -0
- e6_spark_compat/_vendor/sqlglot/dialects/duckdb.py +1263 -0
- e6_spark_compat/_vendor/sqlglot/dialects/dune.py +16 -0
- e6_spark_compat/_vendor/sqlglot/dialects/e6.py +3153 -0
- e6_spark_compat/_vendor/sqlglot/dialects/exasol.py +251 -0
- e6_spark_compat/_vendor/sqlglot/dialects/fabric.py +213 -0
- e6_spark_compat/_vendor/sqlglot/dialects/hive.py +813 -0
- e6_spark_compat/_vendor/sqlglot/dialects/materialize.py +96 -0
- e6_spark_compat/_vendor/sqlglot/dialects/mysql.py +1329 -0
- e6_spark_compat/_vendor/sqlglot/dialects/oracle.py +395 -0
- e6_spark_compat/_vendor/sqlglot/dialects/postgres.py +849 -0
- e6_spark_compat/_vendor/sqlglot/dialects/presto.py +857 -0
- e6_spark_compat/_vendor/sqlglot/dialects/prql.py +209 -0
- e6_spark_compat/_vendor/sqlglot/dialects/redshift.py +464 -0
- e6_spark_compat/_vendor/sqlglot/dialects/risingwave.py +85 -0
- e6_spark_compat/_vendor/sqlglot/dialects/singlestore.py +1146 -0
- e6_spark_compat/_vendor/sqlglot/dialects/snowflake.py +1796 -0
- e6_spark_compat/_vendor/sqlglot/dialects/spark.py +261 -0
- e6_spark_compat/_vendor/sqlglot/dialects/spark2.py +456 -0
- e6_spark_compat/_vendor/sqlglot/dialects/sqlite.py +344 -0
- e6_spark_compat/_vendor/sqlglot/dialects/starrocks.py +356 -0
- e6_spark_compat/_vendor/sqlglot/dialects/tableau.py +61 -0
- e6_spark_compat/_vendor/sqlglot/dialects/teradata.py +398 -0
- e6_spark_compat/_vendor/sqlglot/dialects/trino.py +116 -0
- e6_spark_compat/_vendor/sqlglot/dialects/tsql.py +1420 -0
- e6_spark_compat/_vendor/sqlglot/diff.py +468 -0
- e6_spark_compat/_vendor/sqlglot/errors.py +93 -0
- e6_spark_compat/_vendor/sqlglot/executor/__init__.py +95 -0
- e6_spark_compat/_vendor/sqlglot/executor/context.py +101 -0
- e6_spark_compat/_vendor/sqlglot/executor/env.py +246 -0
- e6_spark_compat/_vendor/sqlglot/executor/python.py +460 -0
- e6_spark_compat/_vendor/sqlglot/executor/table.py +155 -0
- e6_spark_compat/_vendor/sqlglot/expressions.py +9186 -0
- e6_spark_compat/_vendor/sqlglot/generator.py +5147 -0
- e6_spark_compat/_vendor/sqlglot/helper.py +582 -0
- e6_spark_compat/_vendor/sqlglot/jsonpath.py +227 -0
- e6_spark_compat/_vendor/sqlglot/lineage.py +433 -0
- e6_spark_compat/_vendor/sqlglot/optimizer/__init__.py +11 -0
- e6_spark_compat/_vendor/sqlglot/optimizer/annotate_types.py +665 -0
- e6_spark_compat/_vendor/sqlglot/optimizer/canonicalize.py +222 -0
- e6_spark_compat/_vendor/sqlglot/optimizer/eliminate_ctes.py +43 -0
- e6_spark_compat/_vendor/sqlglot/optimizer/eliminate_joins.py +181 -0
- e6_spark_compat/_vendor/sqlglot/optimizer/eliminate_subqueries.py +189 -0
- e6_spark_compat/_vendor/sqlglot/optimizer/isolate_table_selects.py +50 -0
- e6_spark_compat/_vendor/sqlglot/optimizer/merge_subqueries.py +418 -0
- e6_spark_compat/_vendor/sqlglot/optimizer/normalize.py +200 -0
- e6_spark_compat/_vendor/sqlglot/optimizer/normalize_identifiers.py +64 -0
- e6_spark_compat/_vendor/sqlglot/optimizer/optimize_joins.py +92 -0
- e6_spark_compat/_vendor/sqlglot/optimizer/optimizer.py +94 -0
- e6_spark_compat/_vendor/sqlglot/optimizer/pushdown_predicates.py +223 -0
- e6_spark_compat/_vendor/sqlglot/optimizer/pushdown_projections.py +176 -0
- e6_spark_compat/_vendor/sqlglot/optimizer/qualify.py +104 -0
- e6_spark_compat/_vendor/sqlglot/optimizer/qualify_columns.py +1111 -0
- e6_spark_compat/_vendor/sqlglot/optimizer/qualify_tables.py +171 -0
- e6_spark_compat/_vendor/sqlglot/optimizer/scope.py +933 -0
- e6_spark_compat/_vendor/sqlglot/optimizer/simplify.py +1603 -0
- e6_spark_compat/_vendor/sqlglot/optimizer/unnest_subqueries.py +302 -0
- e6_spark_compat/_vendor/sqlglot/parser.py +8817 -0
- e6_spark_compat/_vendor/sqlglot/planner.py +463 -0
- e6_spark_compat/_vendor/sqlglot/py.typed +0 -0
- e6_spark_compat/_vendor/sqlglot/schema.py +604 -0
- e6_spark_compat/_vendor/sqlglot/serde.py +68 -0
- e6_spark_compat/_vendor/sqlglot/time.py +687 -0
- e6_spark_compat/_vendor/sqlglot/tokens.py +1559 -0
- e6_spark_compat/_vendor/sqlglot/transforms.py +1044 -0
- e6_spark_compat/_vendor/sqlglot/trie.py +81 -0
- e6_spark_compat/core/__init__.py +1 -0
- e6_spark_compat/core/connection.py +185 -0
- e6_spark_compat/core/e6_executor.py +81 -0
- e6_spark_compat/core/logging_config.py +201 -0
- e6_spark_compat/core/query_plan.py +850 -0
- e6_spark_compat/core/schema_cache.py +73 -0
- e6_spark_compat/core/session.py +303 -0
- e6_spark_compat/core/sql_generator.py +2880 -0
- e6_spark_compat/core/types.py +75 -0
- e6_spark_compat/pyspark_compat.py +14 -0
- e6_spark_compat/sedona/__init__.py +85 -0
- e6_spark_compat/spatial/__init__.py +1 -0
- e6_spark_compat/spatial/functions.py +547 -0
- e6_spark_compat/spatial/sedona_registrator.py +13 -0
- e6_spark_compat/sql/__init__.py +8 -0
- e6_spark_compat/sql/column.py +780 -0
- e6_spark_compat/sql/dataframe.py +1275 -0
- e6_spark_compat/sql/functions.py +1172 -0
- e6_spark_compat/sql/group.py +117 -0
- e6_spark_compat/sql/mock_io.py +441 -0
- e6_spark_compat/sql/reader.py +369 -0
- e6_spark_compat/sql/row.py +65 -0
- e6_spark_compat/sql/types.py +298 -0
- e6_spark_compat/sql/udf_translator.py +1812 -0
- e6_spark_compat/sql/window.py +282 -0
- e6_spark_compat/sql/writer.py +545 -0
- e6data_spark_compatibility-1.0.0.dist-info/METADATA +424 -0
- e6data_spark_compatibility-1.0.0.dist-info/RECORD +112 -0
- e6data_spark_compatibility-1.0.0.dist-info/WHEEL +5 -0
- e6data_spark_compatibility-1.0.0.dist-info/licenses/LICENSE +69 -0
- e6data_spark_compatibility-1.0.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
"""
|
|
2
|
+
e6-spark-compat: PySpark and Sedona compatibility layer for e6data
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
# Fix multiprocessing issue on macOS/Windows before any imports
|
|
6
|
+
import platform
|
|
7
|
+
import multiprocessing
|
|
8
|
+
import os
|
|
9
|
+
|
|
10
|
+
# Only apply fix if:
|
|
11
|
+
# 1. We're on macOS (Darwin) or Windows
|
|
12
|
+
# 2. Not explicitly disabled via environment variable
|
|
13
|
+
# 3. The current method is 'spawn' (problematic)
|
|
14
|
+
if os.environ.get('E6_SPARK_COMPAT_NO_MP_FIX') != '1':
|
|
15
|
+
try:
|
|
16
|
+
current_method = multiprocessing.get_start_method(allow_none=True)
|
|
17
|
+
system = platform.system()
|
|
18
|
+
|
|
19
|
+
# macOS defaults to 'spawn' which causes re-imports
|
|
20
|
+
# current_method can be None initially, which defaults to 'spawn' on macOS
|
|
21
|
+
if system == 'Darwin' and (current_method == 'spawn' or current_method is None):
|
|
22
|
+
multiprocessing.set_start_method('fork', force=True)
|
|
23
|
+
# Windows only supports 'spawn', so we can't fix it this way
|
|
24
|
+
elif system == 'Windows' and current_method == 'spawn':
|
|
25
|
+
# Log a warning but continue
|
|
26
|
+
import warnings
|
|
27
|
+
warnings.warn(
|
|
28
|
+
"e6-spark-compat: Windows detected. Scripts may execute multiple times. "
|
|
29
|
+
"Use 'if __name__ == \"__main__\":' guard to prevent issues.",
|
|
30
|
+
RuntimeWarning
|
|
31
|
+
)
|
|
32
|
+
except (RuntimeError, ValueError):
|
|
33
|
+
# Already set or platform doesn't support the change
|
|
34
|
+
pass
|
|
35
|
+
|
|
36
|
+
# Initialize vendored dependencies first (patches sys.modules for sqlglot)
|
|
37
|
+
from . import _vendor
|
|
38
|
+
|
|
39
|
+
# Initialize logging configuration before importing other modules
|
|
40
|
+
from .core.logging_config import configure_logging
|
|
41
|
+
configure_logging()
|
|
42
|
+
|
|
43
|
+
from .core.session import SparkSession
|
|
44
|
+
from .sql.dataframe import DataFrame
|
|
45
|
+
from .sql.column import Column
|
|
46
|
+
from .sql.row import Row
|
|
47
|
+
from .core.sql_generator import set_use_cte
|
|
48
|
+
|
|
49
|
+
__version__ = "1.0.0"
|
|
50
|
+
__all__ = ["SparkSession", "DataFrame", "Column", "Row", "set_use_cte"]
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Vendored dependencies for e6-spark-compat.
|
|
3
|
+
|
|
4
|
+
This directory contains vendored (bundled) copies of dependencies
|
|
5
|
+
to avoid external package requirements for PyPI publishing.
|
|
6
|
+
"""
|
|
7
|
+
import sys
|
|
8
|
+
import os
|
|
9
|
+
import importlib.util
|
|
10
|
+
|
|
11
|
+
# Pre-register sqlglot in sys.modules BEFORE importing it
|
|
12
|
+
# This is necessary because the vendored sqlglot uses internal imports like 'from sqlglot import ...'
|
|
13
|
+
def _setup_vendored_sqlglot():
|
|
14
|
+
"""Setup vendored sqlglot by pre-registering it in sys.modules."""
|
|
15
|
+
vendor_dir = os.path.dirname(__file__)
|
|
16
|
+
sqlglot_dir = os.path.join(vendor_dir, 'sqlglot')
|
|
17
|
+
|
|
18
|
+
# Create a module spec for the vendored sqlglot
|
|
19
|
+
spec = importlib.util.spec_from_file_location(
|
|
20
|
+
'sqlglot',
|
|
21
|
+
os.path.join(sqlglot_dir, '__init__.py'),
|
|
22
|
+
submodule_search_locations=[sqlglot_dir]
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
# Create the module and add to sys.modules BEFORE executing it
|
|
26
|
+
sqlglot_module = importlib.util.module_from_spec(spec)
|
|
27
|
+
sys.modules['sqlglot'] = sqlglot_module
|
|
28
|
+
|
|
29
|
+
# Now execute the module (this allows internal imports to work)
|
|
30
|
+
spec.loader.exec_module(sqlglot_module)
|
|
31
|
+
|
|
32
|
+
# Also make it available as e6_spark_compat._vendor.sqlglot
|
|
33
|
+
sys.modules['e6_spark_compat._vendor.sqlglot'] = sqlglot_module
|
|
34
|
+
|
|
35
|
+
_setup_vendored_sqlglot()
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
# ruff: noqa: F401
|
|
2
|
+
"""
|
|
3
|
+
.. include:: ../README.md
|
|
4
|
+
|
|
5
|
+
----
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
import logging
|
|
11
|
+
import typing as t
|
|
12
|
+
|
|
13
|
+
from sqlglot import expressions as exp
|
|
14
|
+
from sqlglot.dialects.dialect import Dialect as Dialect, Dialects as Dialects
|
|
15
|
+
from sqlglot.diff import diff as diff
|
|
16
|
+
from sqlglot.errors import (
|
|
17
|
+
ErrorLevel as ErrorLevel,
|
|
18
|
+
ParseError as ParseError,
|
|
19
|
+
TokenError as TokenError,
|
|
20
|
+
UnsupportedError as UnsupportedError,
|
|
21
|
+
)
|
|
22
|
+
from sqlglot.expressions import (
|
|
23
|
+
Expression as Expression,
|
|
24
|
+
alias_ as alias,
|
|
25
|
+
and_ as and_,
|
|
26
|
+
case as case,
|
|
27
|
+
cast as cast,
|
|
28
|
+
column as column,
|
|
29
|
+
condition as condition,
|
|
30
|
+
delete as delete,
|
|
31
|
+
except_ as except_,
|
|
32
|
+
from_ as from_,
|
|
33
|
+
func as func,
|
|
34
|
+
insert as insert,
|
|
35
|
+
intersect as intersect,
|
|
36
|
+
maybe_parse as maybe_parse,
|
|
37
|
+
merge as merge,
|
|
38
|
+
not_ as not_,
|
|
39
|
+
or_ as or_,
|
|
40
|
+
select as select,
|
|
41
|
+
subquery as subquery,
|
|
42
|
+
table_ as table,
|
|
43
|
+
to_column as to_column,
|
|
44
|
+
to_identifier as to_identifier,
|
|
45
|
+
to_table as to_table,
|
|
46
|
+
union as union,
|
|
47
|
+
)
|
|
48
|
+
from sqlglot.generator import Generator as Generator
|
|
49
|
+
from sqlglot.parser import Parser as Parser
|
|
50
|
+
from sqlglot.schema import MappingSchema as MappingSchema, Schema as Schema
|
|
51
|
+
from sqlglot.tokens import (
|
|
52
|
+
Token as Token,
|
|
53
|
+
Tokenizer as Tokenizer,
|
|
54
|
+
TokenType as TokenType,
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
if t.TYPE_CHECKING:
|
|
58
|
+
from sqlglot._typing import E
|
|
59
|
+
from sqlglot.dialects.dialect import DialectType as DialectType
|
|
60
|
+
|
|
61
|
+
logger = logging.getLogger("sqlglot")
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
try:
|
|
65
|
+
from sqlglot._version import __version__, __version_tuple__
|
|
66
|
+
except ImportError:
|
|
67
|
+
logger.error(
|
|
68
|
+
"Unable to set __version__, run `pip install -e .` or `python setup.py develop` first."
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
pretty = False
|
|
73
|
+
"""Whether to format generated SQL by default."""
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def tokenize(sql: str, read: DialectType = None, dialect: DialectType = None) -> t.List[Token]:
|
|
77
|
+
"""
|
|
78
|
+
Tokenizes the given SQL string.
|
|
79
|
+
|
|
80
|
+
Args:
|
|
81
|
+
sql: the SQL code string to tokenize.
|
|
82
|
+
read: the SQL dialect to apply during tokenizing (eg. "spark", "hive", "presto", "mysql").
|
|
83
|
+
dialect: the SQL dialect (alias for read).
|
|
84
|
+
|
|
85
|
+
Returns:
|
|
86
|
+
The resulting list of tokens.
|
|
87
|
+
"""
|
|
88
|
+
return Dialect.get_or_raise(read or dialect).tokenize(sql)
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
def parse(
|
|
92
|
+
sql: str, read: DialectType = None, dialect: DialectType = None, **opts
|
|
93
|
+
) -> t.List[t.Optional[Expression]]:
|
|
94
|
+
"""
|
|
95
|
+
Parses the given SQL string into a collection of syntax trees, one per parsed SQL statement.
|
|
96
|
+
|
|
97
|
+
Args:
|
|
98
|
+
sql: the SQL code string to parse.
|
|
99
|
+
read: the SQL dialect to apply during parsing (eg. "spark", "hive", "presto", "mysql").
|
|
100
|
+
dialect: the SQL dialect (alias for read).
|
|
101
|
+
**opts: other `sqlglot.parser.Parser` options.
|
|
102
|
+
|
|
103
|
+
Returns:
|
|
104
|
+
The resulting syntax tree collection.
|
|
105
|
+
"""
|
|
106
|
+
return Dialect.get_or_raise(read or dialect).parse(sql, **opts)
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
@t.overload
|
|
110
|
+
def parse_one(sql: str, *, into: t.Type[E], **opts) -> E: ...
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
@t.overload
|
|
114
|
+
def parse_one(sql: str, **opts) -> Expression: ...
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
def parse_one(
|
|
118
|
+
sql: str,
|
|
119
|
+
read: DialectType = None,
|
|
120
|
+
dialect: DialectType = None,
|
|
121
|
+
into: t.Optional[exp.IntoType] = None,
|
|
122
|
+
**opts,
|
|
123
|
+
) -> Expression:
|
|
124
|
+
"""
|
|
125
|
+
Parses the given SQL string and returns a syntax tree for the first parsed SQL statement.
|
|
126
|
+
|
|
127
|
+
Args:
|
|
128
|
+
sql: the SQL code string to parse.
|
|
129
|
+
read: the SQL dialect to apply during parsing (eg. "spark", "hive", "presto", "mysql").
|
|
130
|
+
dialect: the SQL dialect (alias for read)
|
|
131
|
+
into: the SQLGlot Expression to parse into.
|
|
132
|
+
**opts: other `sqlglot.parser.Parser` options.
|
|
133
|
+
|
|
134
|
+
Returns:
|
|
135
|
+
The syntax tree for the first parsed statement.
|
|
136
|
+
"""
|
|
137
|
+
|
|
138
|
+
dialect = Dialect.get_or_raise(read or dialect)
|
|
139
|
+
if into:
|
|
140
|
+
result = dialect.parse_into(into, sql, **opts)
|
|
141
|
+
else:
|
|
142
|
+
result = dialect.parse(sql, **opts)
|
|
143
|
+
for expression in result:
|
|
144
|
+
if not expression:
|
|
145
|
+
raise ParseError(f"No expression was parsed from '{sql}'")
|
|
146
|
+
return expression
|
|
147
|
+
else:
|
|
148
|
+
raise ParseError(f"No expression was parsed from '{sql}'")
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
def transpile(
|
|
152
|
+
sql: str,
|
|
153
|
+
read: DialectType = None,
|
|
154
|
+
write: DialectType = None,
|
|
155
|
+
identity: bool = True,
|
|
156
|
+
error_level: t.Optional[ErrorLevel] = None,
|
|
157
|
+
**opts,
|
|
158
|
+
) -> t.List[str]:
|
|
159
|
+
"""
|
|
160
|
+
Parses the given SQL string in accordance with the source dialect and returns a list of SQL strings transformed
|
|
161
|
+
to conform to the target dialect. Each string in the returned list represents a single transformed SQL statement.
|
|
162
|
+
|
|
163
|
+
Args:
|
|
164
|
+
sql: the SQL code string to transpile.
|
|
165
|
+
read: the source dialect used to parse the input string (eg. "spark", "hive", "presto", "mysql").
|
|
166
|
+
write: the target dialect into which the input should be transformed (eg. "spark", "hive", "presto", "mysql").
|
|
167
|
+
identity: if set to `True` and if the target dialect is not specified the source dialect will be used as both:
|
|
168
|
+
the source and the target dialect.
|
|
169
|
+
error_level: the desired error level of the parser.
|
|
170
|
+
**opts: other `sqlglot.generator.Generator` options.
|
|
171
|
+
|
|
172
|
+
Returns:
|
|
173
|
+
The list of transpiled SQL statements.
|
|
174
|
+
"""
|
|
175
|
+
write = (read if write is None else write) if identity else write
|
|
176
|
+
write = Dialect.get_or_raise(write)
|
|
177
|
+
return [
|
|
178
|
+
write.generate(expression, copy=False, **opts) if expression else ""
|
|
179
|
+
for expression in parse(sql, read, error_level=error_level)
|
|
180
|
+
]
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import argparse
|
|
4
|
+
import sys
|
|
5
|
+
import typing as t
|
|
6
|
+
|
|
7
|
+
import sqlglot
|
|
8
|
+
|
|
9
|
+
parser = argparse.ArgumentParser(description="Transpile SQL")
|
|
10
|
+
parser.add_argument(
|
|
11
|
+
"sql",
|
|
12
|
+
metavar="sql",
|
|
13
|
+
type=str,
|
|
14
|
+
help="SQL statement(s) to transpile, or - to parse stdin.",
|
|
15
|
+
)
|
|
16
|
+
parser.add_argument(
|
|
17
|
+
"--read",
|
|
18
|
+
dest="read",
|
|
19
|
+
type=str,
|
|
20
|
+
default=None,
|
|
21
|
+
help="Dialect to read default is generic",
|
|
22
|
+
)
|
|
23
|
+
parser.add_argument(
|
|
24
|
+
"--write",
|
|
25
|
+
dest="write",
|
|
26
|
+
type=str,
|
|
27
|
+
default=None,
|
|
28
|
+
help="Dialect to write default is generic",
|
|
29
|
+
)
|
|
30
|
+
parser.add_argument(
|
|
31
|
+
"--no-identify",
|
|
32
|
+
dest="identify",
|
|
33
|
+
action="store_false",
|
|
34
|
+
help="Don't auto identify fields",
|
|
35
|
+
)
|
|
36
|
+
parser.add_argument(
|
|
37
|
+
"--no-pretty",
|
|
38
|
+
dest="pretty",
|
|
39
|
+
action="store_false",
|
|
40
|
+
help="Compress sql",
|
|
41
|
+
)
|
|
42
|
+
parser.add_argument(
|
|
43
|
+
"--parse",
|
|
44
|
+
dest="parse",
|
|
45
|
+
action="store_true",
|
|
46
|
+
help="Parse and return the expression tree",
|
|
47
|
+
)
|
|
48
|
+
parser.add_argument(
|
|
49
|
+
"--tokenize",
|
|
50
|
+
dest="tokenize",
|
|
51
|
+
action="store_true",
|
|
52
|
+
help="Tokenize and return the tokens list",
|
|
53
|
+
)
|
|
54
|
+
parser.add_argument(
|
|
55
|
+
"--error-level",
|
|
56
|
+
dest="error_level",
|
|
57
|
+
type=str,
|
|
58
|
+
default="IMMEDIATE",
|
|
59
|
+
help="IGNORE, WARN, RAISE, IMMEDIATE (default)",
|
|
60
|
+
)
|
|
61
|
+
parser.add_argument(
|
|
62
|
+
"--version",
|
|
63
|
+
action="version",
|
|
64
|
+
version=sqlglot.__version__,
|
|
65
|
+
help="Display the SQLGlot version",
|
|
66
|
+
)
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
args = parser.parse_args()
|
|
70
|
+
error_level = sqlglot.ErrorLevel[args.error_level.upper()]
|
|
71
|
+
|
|
72
|
+
sql = sys.stdin.read() if args.sql == "-" else args.sql
|
|
73
|
+
|
|
74
|
+
if args.parse:
|
|
75
|
+
objs: t.Union[t.List[str], t.List[sqlglot.tokens.Token]] = [
|
|
76
|
+
repr(expression)
|
|
77
|
+
for expression in sqlglot.parse(
|
|
78
|
+
sql,
|
|
79
|
+
read=args.read,
|
|
80
|
+
error_level=error_level,
|
|
81
|
+
)
|
|
82
|
+
]
|
|
83
|
+
elif args.tokenize:
|
|
84
|
+
objs = sqlglot.Dialect.get_or_raise(args.read).tokenize(sql)
|
|
85
|
+
else:
|
|
86
|
+
objs = sqlglot.transpile(
|
|
87
|
+
sql,
|
|
88
|
+
read=args.read,
|
|
89
|
+
write=args.write,
|
|
90
|
+
identify=args.identify,
|
|
91
|
+
pretty=args.pretty,
|
|
92
|
+
error_level=error_level,
|
|
93
|
+
)
|
|
94
|
+
|
|
95
|
+
for obj in objs:
|
|
96
|
+
print(obj)
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import typing as t
|
|
4
|
+
|
|
5
|
+
import sqlglot
|
|
6
|
+
|
|
7
|
+
if t.TYPE_CHECKING:
|
|
8
|
+
from typing_extensions import Literal as Lit # noqa
|
|
9
|
+
|
|
10
|
+
# A little hack for backwards compatibility with Python 3.7.
|
|
11
|
+
# For example, we might want a TypeVar for objects that support comparison e.g. SupportsRichComparisonT from typeshed.
|
|
12
|
+
# But Python 3.7 doesn't support Protocols, so we'd also need typing_extensions, which we don't want as a dependency.
|
|
13
|
+
A = t.TypeVar("A", bound=t.Any)
|
|
14
|
+
B = t.TypeVar("B", bound="sqlglot.exp.Binary")
|
|
15
|
+
E = t.TypeVar("E", bound="sqlglot.exp.Expression")
|
|
16
|
+
F = t.TypeVar("F", bound="sqlglot.exp.Func")
|
|
17
|
+
T = t.TypeVar("T")
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# file generated by setuptools-scm
|
|
2
|
+
# don't change, don't track in version control
|
|
3
|
+
|
|
4
|
+
__all__ = [
|
|
5
|
+
"__version__",
|
|
6
|
+
"__version_tuple__",
|
|
7
|
+
"version",
|
|
8
|
+
"version_tuple",
|
|
9
|
+
"__commit_id__",
|
|
10
|
+
"commit_id",
|
|
11
|
+
]
|
|
12
|
+
|
|
13
|
+
TYPE_CHECKING = False
|
|
14
|
+
if TYPE_CHECKING:
|
|
15
|
+
from typing import Tuple
|
|
16
|
+
from typing import Union
|
|
17
|
+
|
|
18
|
+
VERSION_TUPLE = Tuple[Union[int, str], ...]
|
|
19
|
+
COMMIT_ID = Union[str, None]
|
|
20
|
+
else:
|
|
21
|
+
VERSION_TUPLE = object
|
|
22
|
+
COMMIT_ID = object
|
|
23
|
+
|
|
24
|
+
version: str
|
|
25
|
+
__version__: str
|
|
26
|
+
__version_tuple__: VERSION_TUPLE
|
|
27
|
+
version_tuple: VERSION_TUPLE
|
|
28
|
+
commit_id: COMMIT_ID
|
|
29
|
+
__commit_id__: COMMIT_ID
|
|
30
|
+
|
|
31
|
+
__version__ = version = '0.0.1.dev6817'
|
|
32
|
+
__version_tuple__ = version_tuple = (0, 0, 1, 'dev6817')
|
|
33
|
+
|
|
34
|
+
__commit_id__ = commit_id = 'g8a0cb2080'
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
# ruff: noqa: F401
|
|
2
|
+
"""
|
|
3
|
+
## Dialects
|
|
4
|
+
|
|
5
|
+
While there is a SQL standard, most SQL engines support a variation of that standard. This makes it difficult
|
|
6
|
+
to write portable SQL code. SQLGlot bridges all the different variations, called "dialects", with an extensible
|
|
7
|
+
SQL transpilation framework.
|
|
8
|
+
|
|
9
|
+
The base `sqlglot.dialects.dialect.Dialect` class implements a generic dialect that aims to be as universal as possible.
|
|
10
|
+
|
|
11
|
+
Each SQL variation has its own `Dialect` subclass, extending the corresponding `Tokenizer`, `Parser` and `Generator`
|
|
12
|
+
classes as needed.
|
|
13
|
+
|
|
14
|
+
### Implementing a custom Dialect
|
|
15
|
+
|
|
16
|
+
Creating a new SQL dialect may seem complicated at first, but it is actually quite simple in SQLGlot:
|
|
17
|
+
|
|
18
|
+
```python
|
|
19
|
+
from sqlglot import exp
|
|
20
|
+
from sqlglot.dialects.dialect import Dialect
|
|
21
|
+
from sqlglot.generator import Generator
|
|
22
|
+
from sqlglot.tokens import Tokenizer, TokenType
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class Custom(Dialect):
|
|
26
|
+
class Tokenizer(Tokenizer):
|
|
27
|
+
QUOTES = ["'", '"'] # Strings can be delimited by either single or double quotes
|
|
28
|
+
IDENTIFIERS = ["`"] # Identifiers can be delimited by backticks
|
|
29
|
+
|
|
30
|
+
# Associates certain meaningful words with tokens that capture their intent
|
|
31
|
+
KEYWORDS = {
|
|
32
|
+
**Tokenizer.KEYWORDS,
|
|
33
|
+
"INT64": TokenType.BIGINT,
|
|
34
|
+
"FLOAT64": TokenType.DOUBLE,
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
class Generator(Generator):
|
|
38
|
+
# Specifies how AST nodes, i.e. subclasses of exp.Expression, should be converted into SQL
|
|
39
|
+
TRANSFORMS = {
|
|
40
|
+
exp.Array: lambda self, e: f"[{self.expressions(e)}]",
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
# Specifies how AST nodes representing data types should be converted into SQL
|
|
44
|
+
TYPE_MAPPING = {
|
|
45
|
+
exp.DataType.Type.TINYINT: "INT64",
|
|
46
|
+
exp.DataType.Type.SMALLINT: "INT64",
|
|
47
|
+
exp.DataType.Type.INT: "INT64",
|
|
48
|
+
exp.DataType.Type.BIGINT: "INT64",
|
|
49
|
+
exp.DataType.Type.DECIMAL: "NUMERIC",
|
|
50
|
+
exp.DataType.Type.FLOAT: "FLOAT64",
|
|
51
|
+
exp.DataType.Type.DOUBLE: "FLOAT64",
|
|
52
|
+
exp.DataType.Type.BOOLEAN: "BOOL",
|
|
53
|
+
exp.DataType.Type.TEXT: "STRING",
|
|
54
|
+
}
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
The above example demonstrates how certain parts of the base `Dialect` class can be overridden to match a different
|
|
58
|
+
specification. Even though it is a fairly realistic starting point, we strongly encourage the reader to study existing
|
|
59
|
+
dialect implementations in order to understand how their various components can be modified, depending on the use-case.
|
|
60
|
+
|
|
61
|
+
----
|
|
62
|
+
"""
|
|
63
|
+
|
|
64
|
+
import importlib
|
|
65
|
+
import threading
|
|
66
|
+
|
|
67
|
+
DIALECTS = [
|
|
68
|
+
"Athena",
|
|
69
|
+
"BigQuery",
|
|
70
|
+
"ClickHouse",
|
|
71
|
+
"Databricks",
|
|
72
|
+
"Doris",
|
|
73
|
+
"Dremio",
|
|
74
|
+
"Drill",
|
|
75
|
+
"Druid",
|
|
76
|
+
"DuckDB",
|
|
77
|
+
"Dune",
|
|
78
|
+
"Fabric",
|
|
79
|
+
"Hive",
|
|
80
|
+
"Materialize",
|
|
81
|
+
"MySQL",
|
|
82
|
+
"Oracle",
|
|
83
|
+
"Postgres",
|
|
84
|
+
"Presto",
|
|
85
|
+
"PRQL",
|
|
86
|
+
"Redshift",
|
|
87
|
+
"RisingWave",
|
|
88
|
+
"SingleStore",
|
|
89
|
+
"Snowflake",
|
|
90
|
+
"Spark",
|
|
91
|
+
"Spark2",
|
|
92
|
+
"SQLite",
|
|
93
|
+
"StarRocks",
|
|
94
|
+
"Tableau",
|
|
95
|
+
"Teradata",
|
|
96
|
+
"Trino",
|
|
97
|
+
"TSQL",
|
|
98
|
+
"Exasol",
|
|
99
|
+
"E6",
|
|
100
|
+
]
|
|
101
|
+
|
|
102
|
+
MODULE_BY_DIALECT = {name: name.lower() for name in DIALECTS}
|
|
103
|
+
DIALECT_MODULE_NAMES = MODULE_BY_DIALECT.values()
|
|
104
|
+
|
|
105
|
+
MODULE_BY_ATTRIBUTE = {
|
|
106
|
+
**MODULE_BY_DIALECT,
|
|
107
|
+
"Dialect": "dialect",
|
|
108
|
+
"Dialects": "dialect",
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
__all__ = list(MODULE_BY_ATTRIBUTE)
|
|
112
|
+
|
|
113
|
+
# We use a reentrant lock because a dialect may depend on (i.e., import) other dialects.
|
|
114
|
+
# Without it, the first dialect import would never be completed, because subsequent
|
|
115
|
+
# imports would be blocked on the lock held by the first import.
|
|
116
|
+
_import_lock = threading.RLock()
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
def __getattr__(name):
|
|
120
|
+
module_name = MODULE_BY_ATTRIBUTE.get(name)
|
|
121
|
+
if module_name:
|
|
122
|
+
with _import_lock:
|
|
123
|
+
module = importlib.import_module(f"sqlglot.dialects.{module_name}")
|
|
124
|
+
return getattr(module, name)
|
|
125
|
+
|
|
126
|
+
raise AttributeError(f"module {__name__} has no attribute {name}")
|