Adding upstream version 25.30.0.
Signed-off-by: Daniel Baumann <daniel@debian.org>
This commit is contained in:
parent
c61927f460
commit
44a4f87ffd
69 changed files with 48139 additions and 46098 deletions
61
CHANGELOG.md
61
CHANGELOG.md
|
@ -1,6 +1,66 @@
|
||||||
Changelog
|
Changelog
|
||||||
=========
|
=========
|
||||||
|
|
||||||
|
## [v25.29.0] - 2024-11-05
|
||||||
|
### :boom: BREAKING CHANGES
|
||||||
|
- due to [`e92904e`](https://github.com/tobymao/sqlglot/commit/e92904e61ab3b14fe18d472df19311f9b014f6cc) - Transpile ANY to EXISTS *(PR [#4305](https://github.com/tobymao/sqlglot/pull/4305) by [@VaggelisD](https://github.com/VaggelisD))*:
|
||||||
|
|
||||||
|
Transpile ANY to EXISTS (#4305)
|
||||||
|
|
||||||
|
- due to [`23e620f`](https://github.com/tobymao/sqlglot/commit/23e620f7cd2860fbce45a5377a75ae0c8f031ce0) - Support MEDIAN() function *(PR [#4317](https://github.com/tobymao/sqlglot/pull/4317) by [@VaggelisD](https://github.com/VaggelisD))*:
|
||||||
|
|
||||||
|
Support MEDIAN() function (#4317)
|
||||||
|
|
||||||
|
- due to [`a093ae7`](https://github.com/tobymao/sqlglot/commit/a093ae750af8a351e54f1431deba1f2ce6843666) - always wrap value in NOT value IS ... *(PR [#4331](https://github.com/tobymao/sqlglot/pull/4331) by [@georgesittas](https://github.com/georgesittas))*:
|
||||||
|
|
||||||
|
always wrap value in NOT value IS ... (#4331)
|
||||||
|
|
||||||
|
- due to [`84f78aa`](https://github.com/tobymao/sqlglot/commit/84f78aafd5d7e74da407167cd394d2bff0718cfb) - parse information schema views into a single identifier *(PR [#4336](https://github.com/tobymao/sqlglot/pull/4336) by [@georgesittas](https://github.com/georgesittas))*:
|
||||||
|
|
||||||
|
parse information schema views into a single identifier (#4336)
|
||||||
|
|
||||||
|
|
||||||
|
### :sparkles: New Features
|
||||||
|
- [`efd9b4e`](https://github.com/tobymao/sqlglot/commit/efd9b4ed5a761a2ebfc47a1582e9d1b2eb7cb277) - **postgres**: Support JSONB_EXISTS *(PR [#4302](https://github.com/tobymao/sqlglot/pull/4302) by [@VaggelisD](https://github.com/VaggelisD))*
|
||||||
|
- :arrow_lower_right: *addresses issue [#4299](https://github.com/tobymao/sqlglot/issues/4299) opened by [@dor-bernstein](https://github.com/dor-bernstein)*
|
||||||
|
- [`e92904e`](https://github.com/tobymao/sqlglot/commit/e92904e61ab3b14fe18d472df19311f9b014f6cc) - **spark**: Transpile ANY to EXISTS *(PR [#4305](https://github.com/tobymao/sqlglot/pull/4305) by [@VaggelisD](https://github.com/VaggelisD))*
|
||||||
|
- :arrow_lower_right: *addresses issue [#4298](https://github.com/tobymao/sqlglot/issues/4298) opened by [@dor-bernstein](https://github.com/dor-bernstein)*
|
||||||
|
- [`2af4936`](https://github.com/tobymao/sqlglot/commit/2af4936bd9b318c695aae249324ff67bcd1292f6) - **snowflake**: Transpile BQ's TIMESTAMP() function *(PR [#4309](https://github.com/tobymao/sqlglot/pull/4309) by [@VaggelisD](https://github.com/VaggelisD))*
|
||||||
|
- [`50a1c91`](https://github.com/tobymao/sqlglot/commit/50a1c919d0d46384e3bd9ba1d45c24dd07efe6d2) - **snowflake**: Transpile exp.TimestampAdd *(PR [#4320](https://github.com/tobymao/sqlglot/pull/4320) by [@VaggelisD](https://github.com/VaggelisD))*
|
||||||
|
- [`01671ce`](https://github.com/tobymao/sqlglot/commit/01671ce137c9cf8d0f12dadc66e0db141f797d16) - **teradata**: add support for hexadecimal literals *(PR [#4323](https://github.com/tobymao/sqlglot/pull/4323) by [@thomascjohnson](https://github.com/thomascjohnson))*
|
||||||
|
- [`23e620f`](https://github.com/tobymao/sqlglot/commit/23e620f7cd2860fbce45a5377a75ae0c8f031ce0) - Support MEDIAN() function *(PR [#4317](https://github.com/tobymao/sqlglot/pull/4317) by [@VaggelisD](https://github.com/VaggelisD))*
|
||||||
|
- :arrow_lower_right: *addresses issue [#4315](https://github.com/tobymao/sqlglot/issues/4315) opened by [@cpcloud](https://github.com/cpcloud)*
|
||||||
|
- [`9faef8d`](https://github.com/tobymao/sqlglot/commit/9faef8d1ceff91dd88db46b2c187d64f15490bf4) - **snowflake**: Transpile exp.TimestampSub *(PR [#4329](https://github.com/tobymao/sqlglot/pull/4329) by [@VaggelisD](https://github.com/VaggelisD))*
|
||||||
|
- [`2d98cac`](https://github.com/tobymao/sqlglot/commit/2d98cacc723bc0c0df8ce11895983fb7cb9f5237) - **BigQuery**: Support JSON_VALUE() *(PR [#4332](https://github.com/tobymao/sqlglot/pull/4332) by [@VaggelisD](https://github.com/VaggelisD))*
|
||||||
|
- [`f8fec0a`](https://github.com/tobymao/sqlglot/commit/f8fec0ab098df37c3b54d91c24e5d8ec84f7cdbe) - **snowflake**: Transpile exp.DatetimeDiff *(PR [#4334](https://github.com/tobymao/sqlglot/pull/4334) by [@VaggelisD](https://github.com/VaggelisD))*
|
||||||
|
- [`16fd1ea`](https://github.com/tobymao/sqlglot/commit/16fd1ea2653a602bdc0d8b81e971fb1acadee585) - **BigQuery**: Support JSON_QUERY *(PR [#4333](https://github.com/tobymao/sqlglot/pull/4333) by [@VaggelisD](https://github.com/VaggelisD))*
|
||||||
|
- [`c09b6a2`](https://github.com/tobymao/sqlglot/commit/c09b6a2a37807795ead251f4fb81a9ba144cce27) - **duckdb**: support flags for RegexpExtract *(PR [#4326](https://github.com/tobymao/sqlglot/pull/4326) by [@NickCrews](https://github.com/NickCrews))*
|
||||||
|
- [`536973c`](https://github.com/tobymao/sqlglot/commit/536973cfc9d00110e388e8af1ed91d73607e07c2) - **trino**: add support for the ON OVERFLOW clause in LISTAGG *(PR [#4340](https://github.com/tobymao/sqlglot/pull/4340) by [@georgesittas](https://github.com/georgesittas))*
|
||||||
|
- [`4584935`](https://github.com/tobymao/sqlglot/commit/4584935cab328eced61c62a998cc013cab5cc3e3) - **snowflake**: Transpile exp.StrToDate *(PR [#4348](https://github.com/tobymao/sqlglot/pull/4348) by [@VaggelisD](https://github.com/VaggelisD))*
|
||||||
|
- [`71f4a47`](https://github.com/tobymao/sqlglot/commit/71f4a47910d5db97fa1a286891d72b5c4694d294) - **snowflake**: Transpile exp.DatetimeAdd *(PR [#4349](https://github.com/tobymao/sqlglot/pull/4349) by [@VaggelisD](https://github.com/VaggelisD))*
|
||||||
|
|
||||||
|
### :bug: Bug Fixes
|
||||||
|
- [`551afff`](https://github.com/tobymao/sqlglot/commit/551afff58ea7bc1047775bfcd5d80b812fb3f682) - handle a Move edge case in the semantic differ *(PR [#4295](https://github.com/tobymao/sqlglot/pull/4295) by [@georgesittas](https://github.com/georgesittas))*
|
||||||
|
- [`a66e721`](https://github.com/tobymao/sqlglot/commit/a66e721dcd63488f7f3b427569a2115ae044c71b) - **generator**: Add NULL FILTER on ARRAY_AGG only for columns *(PR [#4301](https://github.com/tobymao/sqlglot/pull/4301) by [@VaggelisD](https://github.com/VaggelisD))*
|
||||||
|
- :arrow_lower_right: *fixes issue [#4300](https://github.com/tobymao/sqlglot/issues/4300) opened by [@elad-sachs](https://github.com/elad-sachs)*
|
||||||
|
- [`b4ea602`](https://github.com/tobymao/sqlglot/commit/b4ea602ab17b0e8e85ddb090156c7bd2c6354de4) - **clickhouse**: improve parsing of WITH FILL ... INTERPOLATE *(PR [#4311](https://github.com/tobymao/sqlglot/pull/4311) by [@georgesittas](https://github.com/georgesittas))*
|
||||||
|
- :arrow_lower_right: *fixes issue [#4310](https://github.com/tobymao/sqlglot/issues/4310) opened by [@brunorpinho](https://github.com/brunorpinho)*
|
||||||
|
- [`749886b`](https://github.com/tobymao/sqlglot/commit/749886b574a5dfa03aeb78b76d9cc097aa0f3e65) - **tsql**: Generate LOG(...) for exp.Ln *(PR [#4318](https://github.com/tobymao/sqlglot/pull/4318) by [@VaggelisD](https://github.com/VaggelisD))*
|
||||||
|
- :arrow_lower_right: *fixes issue [#4316](https://github.com/tobymao/sqlglot/issues/4316) opened by [@cpcloud](https://github.com/cpcloud)*
|
||||||
|
- [`5c1b1f4`](https://github.com/tobymao/sqlglot/commit/5c1b1f43014967f6853752ba8d0899757a3efcd5) - **parser**: optionally parse a Stream expression *(PR [#4325](https://github.com/tobymao/sqlglot/pull/4325) by [@georgesittas](https://github.com/georgesittas))*
|
||||||
|
- :arrow_lower_right: *fixes issue [#4324](https://github.com/tobymao/sqlglot/issues/4324) opened by [@lancewl](https://github.com/lancewl)*
|
||||||
|
- [`bb49a00`](https://github.com/tobymao/sqlglot/commit/bb49a00b16487356369bbb77aff9c2ff3f9cda52) - **oracle**: Do not normalize time units for exp.DateTrunc *(PR [#4328](https://github.com/tobymao/sqlglot/pull/4328) by [@VaggelisD](https://github.com/VaggelisD))*
|
||||||
|
- :arrow_lower_right: *fixes issue [#4321](https://github.com/tobymao/sqlglot/issues/4321) opened by [@cpcloud](https://github.com/cpcloud)*
|
||||||
|
- [`a093ae7`](https://github.com/tobymao/sqlglot/commit/a093ae750af8a351e54f1431deba1f2ce6843666) - **clickhouse**: always wrap value in NOT value IS ... *(PR [#4331](https://github.com/tobymao/sqlglot/pull/4331) by [@georgesittas](https://github.com/georgesittas))*
|
||||||
|
- :arrow_lower_right: *fixes issue [#4330](https://github.com/tobymao/sqlglot/issues/4330) opened by [@elchyn-cheliabiyeu](https://github.com/elchyn-cheliabiyeu)*
|
||||||
|
- [`def4f1e`](https://github.com/tobymao/sqlglot/commit/def4f1e3a9eac7545dfad223a5d49cee4fb7eeb8) - Refactor exp.RegexpExtract (follow up 4326) *(PR [#4341](https://github.com/tobymao/sqlglot/pull/4341) by [@VaggelisD](https://github.com/VaggelisD))*
|
||||||
|
- [`c1456d0`](https://github.com/tobymao/sqlglot/commit/c1456d07097c42a2ba2078ad30a8afe4cc89597d) - presto/trino current_time closes [#4344](https://github.com/tobymao/sqlglot/pull/4344) *(commit by [@tobymao](https://github.com/tobymao))*
|
||||||
|
- [`8e16abe`](https://github.com/tobymao/sqlglot/commit/8e16abe2fed324b7ed6c718753cc623a8eb37814) - **duckdb**: we ALWAYS need to render group if params is present for RegexpExtract *(PR [#4343](https://github.com/tobymao/sqlglot/pull/4343) by [@NickCrews](https://github.com/NickCrews))*
|
||||||
|
- [`1689dc7`](https://github.com/tobymao/sqlglot/commit/1689dc7adbb913fe603b5e37eba29cc10d344cd2) - **bigquery**: Parse timezone for DATE_TRUNC *(PR [#4347](https://github.com/tobymao/sqlglot/pull/4347) by [@VaggelisD](https://github.com/VaggelisD))*
|
||||||
|
- :arrow_lower_right: *fixes issue [#4346](https://github.com/tobymao/sqlglot/issues/4346) opened by [@CYFish](https://github.com/CYFish)*
|
||||||
|
- [`84f78aa`](https://github.com/tobymao/sqlglot/commit/84f78aafd5d7e74da407167cd394d2bff0718cfb) - **bigquery**: parse information schema views into a single identifier *(PR [#4336](https://github.com/tobymao/sqlglot/pull/4336) by [@georgesittas](https://github.com/georgesittas))*
|
||||||
|
|
||||||
|
|
||||||
## [v25.28.0] - 2024-10-25
|
## [v25.28.0] - 2024-10-25
|
||||||
### :boom: BREAKING CHANGES
|
### :boom: BREAKING CHANGES
|
||||||
- due to [`1691388`](https://github.com/tobymao/sqlglot/commit/16913887f5573f01eb8cd2b9336d4b37b84a449a) - Fix chained exp.SetOperation type annotation *(PR [#4274](https://github.com/tobymao/sqlglot/pull/4274) by [@VaggelisD](https://github.com/VaggelisD))*:
|
- due to [`1691388`](https://github.com/tobymao/sqlglot/commit/16913887f5573f01eb8cd2b9336d4b37b84a449a) - Fix chained exp.SetOperation type annotation *(PR [#4274](https://github.com/tobymao/sqlglot/pull/4274) by [@VaggelisD](https://github.com/VaggelisD))*:
|
||||||
|
@ -5153,3 +5213,4 @@ Changelog
|
||||||
[v25.26.0]: https://github.com/tobymao/sqlglot/compare/v25.25.1...v25.26.0
|
[v25.26.0]: https://github.com/tobymao/sqlglot/compare/v25.25.1...v25.26.0
|
||||||
[v25.27.0]: https://github.com/tobymao/sqlglot/compare/v25.26.0...v25.27.0
|
[v25.27.0]: https://github.com/tobymao/sqlglot/compare/v25.26.0...v25.27.0
|
||||||
[v25.28.0]: https://github.com/tobymao/sqlglot/compare/v25.27.0...v25.28.0
|
[v25.28.0]: https://github.com/tobymao/sqlglot/compare/v25.27.0...v25.28.0
|
||||||
|
[v25.29.0]: https://github.com/tobymao/sqlglot/compare/v25.28.0...v25.29.0
|
||||||
|
|
File diff suppressed because one or more lines are too long
|
@ -76,8 +76,8 @@
|
||||||
</span><span id="L-12"><a href="#L-12"><span class="linenos">12</span></a><span class="n">__version_tuple__</span><span class="p">:</span> <span class="n">VERSION_TUPLE</span>
|
</span><span id="L-12"><a href="#L-12"><span class="linenos">12</span></a><span class="n">__version_tuple__</span><span class="p">:</span> <span class="n">VERSION_TUPLE</span>
|
||||||
</span><span id="L-13"><a href="#L-13"><span class="linenos">13</span></a><span class="n">version_tuple</span><span class="p">:</span> <span class="n">VERSION_TUPLE</span>
|
</span><span id="L-13"><a href="#L-13"><span class="linenos">13</span></a><span class="n">version_tuple</span><span class="p">:</span> <span class="n">VERSION_TUPLE</span>
|
||||||
</span><span id="L-14"><a href="#L-14"><span class="linenos">14</span></a>
|
</span><span id="L-14"><a href="#L-14"><span class="linenos">14</span></a>
|
||||||
</span><span id="L-15"><a href="#L-15"><span class="linenos">15</span></a><span class="n">__version__</span> <span class="o">=</span> <span class="n">version</span> <span class="o">=</span> <span class="s1">'25.28.0'</span>
|
</span><span id="L-15"><a href="#L-15"><span class="linenos">15</span></a><span class="n">__version__</span> <span class="o">=</span> <span class="n">version</span> <span class="o">=</span> <span class="s1">'25.29.0'</span>
|
||||||
</span><span id="L-16"><a href="#L-16"><span class="linenos">16</span></a><span class="n">__version_tuple__</span> <span class="o">=</span> <span class="n">version_tuple</span> <span class="o">=</span> <span class="p">(</span><span class="mi">25</span><span class="p">,</span> <span class="mi">28</span><span class="p">,</span> <span class="mi">0</span><span class="p">)</span>
|
</span><span id="L-16"><a href="#L-16"><span class="linenos">16</span></a><span class="n">__version_tuple__</span> <span class="o">=</span> <span class="n">version_tuple</span> <span class="o">=</span> <span class="p">(</span><span class="mi">25</span><span class="p">,</span> <span class="mi">29</span><span class="p">,</span> <span class="mi">0</span><span class="p">)</span>
|
||||||
</span></pre></div>
|
</span></pre></div>
|
||||||
|
|
||||||
|
|
||||||
|
@ -97,7 +97,7 @@
|
||||||
<section id="version">
|
<section id="version">
|
||||||
<div class="attr variable">
|
<div class="attr variable">
|
||||||
<span class="name">version</span><span class="annotation">: str</span> =
|
<span class="name">version</span><span class="annotation">: str</span> =
|
||||||
<span class="default_value">'25.28.0'</span>
|
<span class="default_value">'25.29.0'</span>
|
||||||
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
@ -109,7 +109,7 @@
|
||||||
<section id="version_tuple">
|
<section id="version_tuple">
|
||||||
<div class="attr variable">
|
<div class="attr variable">
|
||||||
<span class="name">version_tuple</span><span class="annotation">: object</span> =
|
<span class="name">version_tuple</span><span class="annotation">: object</span> =
|
||||||
<span class="default_value">(25, 28, 0)</span>
|
<span class="default_value">(25, 29, 0)</span>
|
||||||
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -1106,6 +1106,8 @@ Default: True</li>
|
||||||
<dd id="RisingWave.Generator.overlay_sql" class="function"><a href="../generator.html#Generator.overlay_sql">overlay_sql</a></dd>
|
<dd id="RisingWave.Generator.overlay_sql" class="function"><a href="../generator.html#Generator.overlay_sql">overlay_sql</a></dd>
|
||||||
<dd id="RisingWave.Generator.todouble_sql" class="function"><a href="../generator.html#Generator.todouble_sql">todouble_sql</a></dd>
|
<dd id="RisingWave.Generator.todouble_sql" class="function"><a href="../generator.html#Generator.todouble_sql">todouble_sql</a></dd>
|
||||||
<dd id="RisingWave.Generator.string_sql" class="function"><a href="../generator.html#Generator.string_sql">string_sql</a></dd>
|
<dd id="RisingWave.Generator.string_sql" class="function"><a href="../generator.html#Generator.string_sql">string_sql</a></dd>
|
||||||
|
<dd id="RisingWave.Generator.median_sql" class="function"><a href="../generator.html#Generator.median_sql">median_sql</a></dd>
|
||||||
|
<dd id="RisingWave.Generator.overflowtruncatebehavior_sql" class="function"><a href="../generator.html#Generator.overflowtruncatebehavior_sql">overflowtruncatebehavior_sql</a></dd>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
<div><dt><a href="postgres.html#Postgres.Generator">sqlglot.dialects.postgres.Postgres.Generator</a></dt>
|
<div><dt><a href="postgres.html#Postgres.Generator">sqlglot.dialects.postgres.Postgres.Generator</a></dt>
|
||||||
|
@ -1126,6 +1128,7 @@ Default: True</li>
|
||||||
<dd id="RisingWave.Generator.CAN_IMPLEMENT_ARRAY_ANY" class="variable"><a href="postgres.html#Postgres.Generator.CAN_IMPLEMENT_ARRAY_ANY">CAN_IMPLEMENT_ARRAY_ANY</a></dd>
|
<dd id="RisingWave.Generator.CAN_IMPLEMENT_ARRAY_ANY" class="variable"><a href="postgres.html#Postgres.Generator.CAN_IMPLEMENT_ARRAY_ANY">CAN_IMPLEMENT_ARRAY_ANY</a></dd>
|
||||||
<dd id="RisingWave.Generator.COPY_HAS_INTO_KEYWORD" class="variable"><a href="postgres.html#Postgres.Generator.COPY_HAS_INTO_KEYWORD">COPY_HAS_INTO_KEYWORD</a></dd>
|
<dd id="RisingWave.Generator.COPY_HAS_INTO_KEYWORD" class="variable"><a href="postgres.html#Postgres.Generator.COPY_HAS_INTO_KEYWORD">COPY_HAS_INTO_KEYWORD</a></dd>
|
||||||
<dd id="RisingWave.Generator.ARRAY_CONCAT_IS_VAR_LEN" class="variable"><a href="postgres.html#Postgres.Generator.ARRAY_CONCAT_IS_VAR_LEN">ARRAY_CONCAT_IS_VAR_LEN</a></dd>
|
<dd id="RisingWave.Generator.ARRAY_CONCAT_IS_VAR_LEN" class="variable"><a href="postgres.html#Postgres.Generator.ARRAY_CONCAT_IS_VAR_LEN">ARRAY_CONCAT_IS_VAR_LEN</a></dd>
|
||||||
|
<dd id="RisingWave.Generator.SUPPORTS_MEDIAN" class="variable"><a href="postgres.html#Postgres.Generator.SUPPORTS_MEDIAN">SUPPORTS_MEDIAN</a></dd>
|
||||||
<dd id="RisingWave.Generator.SUPPORTED_JSON_PATH_PARTS" class="variable"><a href="postgres.html#Postgres.Generator.SUPPORTED_JSON_PATH_PARTS">SUPPORTED_JSON_PATH_PARTS</a></dd>
|
<dd id="RisingWave.Generator.SUPPORTED_JSON_PATH_PARTS" class="variable"><a href="postgres.html#Postgres.Generator.SUPPORTED_JSON_PATH_PARTS">SUPPORTED_JSON_PATH_PARTS</a></dd>
|
||||||
<dd id="RisingWave.Generator.TYPE_MAPPING" class="variable"><a href="postgres.html#Postgres.Generator.TYPE_MAPPING">TYPE_MAPPING</a></dd>
|
<dd id="RisingWave.Generator.TYPE_MAPPING" class="variable"><a href="postgres.html#Postgres.Generator.TYPE_MAPPING">TYPE_MAPPING</a></dd>
|
||||||
<dd id="RisingWave.Generator.TRANSFORMS" class="variable"><a href="postgres.html#Postgres.Generator.TRANSFORMS">TRANSFORMS</a></dd>
|
<dd id="RisingWave.Generator.TRANSFORMS" class="variable"><a href="postgres.html#Postgres.Generator.TRANSFORMS">TRANSFORMS</a></dd>
|
||||||
|
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load diff
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load diff
|
@ -1874,7 +1874,7 @@ belong to some totally-ordered set.</p>
|
||||||
<section id="DATE_UNITS">
|
<section id="DATE_UNITS">
|
||||||
<div class="attr variable">
|
<div class="attr variable">
|
||||||
<span class="name">DATE_UNITS</span> =
|
<span class="name">DATE_UNITS</span> =
|
||||||
<span class="default_value">{'year_month', 'quarter', 'week', 'day', 'month', 'year'}</span>
|
<span class="default_value">{'year_month', 'week', 'month', 'quarter', 'day', 'year'}</span>
|
||||||
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -586,7 +586,7 @@
|
||||||
<div class="attr variable">
|
<div class="attr variable">
|
||||||
<span class="name">ALL_JSON_PATH_PARTS</span> =
|
<span class="name">ALL_JSON_PATH_PARTS</span> =
|
||||||
<input id="ALL_JSON_PATH_PARTS-view-value" class="view-value-toggle-state" type="checkbox" aria-hidden="true" tabindex="-1">
|
<input id="ALL_JSON_PATH_PARTS-view-value" class="view-value-toggle-state" type="checkbox" aria-hidden="true" tabindex="-1">
|
||||||
<label class="view-value-button pdoc-button" for="ALL_JSON_PATH_PARTS-view-value"></label><span class="default_value">{<class '<a href="expressions.html#JSONPathSubscript">sqlglot.expressions.JSONPathSubscript</a>'>, <class '<a href="expressions.html#JSONPathWildcard">sqlglot.expressions.JSONPathWildcard</a>'>, <class '<a href="expressions.html#JSONPathSelector">sqlglot.expressions.JSONPathSelector</a>'>, <class '<a href="expressions.html#JSONPathSlice">sqlglot.expressions.JSONPathSlice</a>'>, <class '<a href="expressions.html#JSONPathScript">sqlglot.expressions.JSONPathScript</a>'>, <class '<a href="expressions.html#JSONPathRoot">sqlglot.expressions.JSONPathRoot</a>'>, <class '<a href="expressions.html#JSONPathUnion">sqlglot.expressions.JSONPathUnion</a>'>, <class '<a href="expressions.html#JSONPathRecursive">sqlglot.expressions.JSONPathRecursive</a>'>, <class '<a href="expressions.html#JSONPathKey">sqlglot.expressions.JSONPathKey</a>'>, <class '<a href="expressions.html#JSONPathFilter">sqlglot.expressions.JSONPathFilter</a>'>}</span>
|
<label class="view-value-button pdoc-button" for="ALL_JSON_PATH_PARTS-view-value"></label><span class="default_value">{<class '<a href="expressions.html#JSONPathRecursive">sqlglot.expressions.JSONPathRecursive</a>'>, <class '<a href="expressions.html#JSONPathKey">sqlglot.expressions.JSONPathKey</a>'>, <class '<a href="expressions.html#JSONPathWildcard">sqlglot.expressions.JSONPathWildcard</a>'>, <class '<a href="expressions.html#JSONPathFilter">sqlglot.expressions.JSONPathFilter</a>'>, <class '<a href="expressions.html#JSONPathUnion">sqlglot.expressions.JSONPathUnion</a>'>, <class '<a href="expressions.html#JSONPathSubscript">sqlglot.expressions.JSONPathSubscript</a>'>, <class '<a href="expressions.html#JSONPathSelector">sqlglot.expressions.JSONPathSelector</a>'>, <class '<a href="expressions.html#JSONPathSlice">sqlglot.expressions.JSONPathSlice</a>'>, <class '<a href="expressions.html#JSONPathScript">sqlglot.expressions.JSONPathScript</a>'>, <class '<a href="expressions.html#JSONPathRoot">sqlglot.expressions.JSONPathRoot</a>'>}</span>
|
||||||
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
File diff suppressed because one or more lines are too long
|
@ -581,7 +581,7 @@ queries if it would result in multiple table selects in a single query:</p>
|
||||||
<div class="attr variable">
|
<div class="attr variable">
|
||||||
<span class="name">UNMERGABLE_ARGS</span> =
|
<span class="name">UNMERGABLE_ARGS</span> =
|
||||||
<input id="UNMERGABLE_ARGS-view-value" class="view-value-toggle-state" type="checkbox" aria-hidden="true" tabindex="-1">
|
<input id="UNMERGABLE_ARGS-view-value" class="view-value-toggle-state" type="checkbox" aria-hidden="true" tabindex="-1">
|
||||||
<label class="view-value-button pdoc-button" for="UNMERGABLE_ARGS-view-value"></label><span class="default_value">{'pivots', 'cluster', 'distribute', 'windows', 'locks', 'distinct', 'connect', 'operation_modifiers', 'settings', 'with', 'having', 'qualify', 'limit', 'laterals', 'match', 'options', 'format', 'group', 'sort', 'kind', 'sample', 'offset', 'into', 'prewhere'}</span>
|
<label class="view-value-button pdoc-button" for="UNMERGABLE_ARGS-view-value"></label><span class="default_value">{'having', 'into', 'offset', 'operation_modifiers', 'limit', 'distribute', 'kind', 'qualify', 'settings', 'prewhere', 'sample', 'pivots', 'locks', 'options', 'sort', 'laterals', 'connect', 'with', 'format', 'group', 'cluster', 'windows', 'distinct', 'match'}</span>
|
||||||
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -3231,7 +3231,7 @@ prefix are statically known.</p>
|
||||||
<div class="attr variable">
|
<div class="attr variable">
|
||||||
<span class="name">DATETRUNC_COMPARISONS</span> =
|
<span class="name">DATETRUNC_COMPARISONS</span> =
|
||||||
<input id="DATETRUNC_COMPARISONS-view-value" class="view-value-toggle-state" type="checkbox" aria-hidden="true" tabindex="-1">
|
<input id="DATETRUNC_COMPARISONS-view-value" class="view-value-toggle-state" type="checkbox" aria-hidden="true" tabindex="-1">
|
||||||
<label class="view-value-button pdoc-button" for="DATETRUNC_COMPARISONS-view-value"></label><span class="default_value">{<class '<a href="../expressions.html#GTE">sqlglot.expressions.GTE</a>'>, <class '<a href="../expressions.html#LT">sqlglot.expressions.LT</a>'>, <class '<a href="../expressions.html#LTE">sqlglot.expressions.LTE</a>'>, <class '<a href="../expressions.html#EQ">sqlglot.expressions.EQ</a>'>, <class '<a href="../expressions.html#In">sqlglot.expressions.In</a>'>, <class '<a href="../expressions.html#NEQ">sqlglot.expressions.NEQ</a>'>, <class '<a href="../expressions.html#GT">sqlglot.expressions.GT</a>'>}</span>
|
<label class="view-value-button pdoc-button" for="DATETRUNC_COMPARISONS-view-value"></label><span class="default_value">{<class '<a href="../expressions.html#LT">sqlglot.expressions.LT</a>'>, <class '<a href="../expressions.html#In">sqlglot.expressions.In</a>'>, <class '<a href="../expressions.html#NEQ">sqlglot.expressions.NEQ</a>'>, <class '<a href="../expressions.html#EQ">sqlglot.expressions.EQ</a>'>, <class '<a href="../expressions.html#GTE">sqlglot.expressions.GTE</a>'>, <class '<a href="../expressions.html#GT">sqlglot.expressions.GT</a>'>, <class '<a href="../expressions.html#LTE">sqlglot.expressions.LTE</a>'>}</span>
|
||||||
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
@ -3315,7 +3315,7 @@ prefix are statically known.</p>
|
||||||
<section id="JOINS">
|
<section id="JOINS">
|
||||||
<div class="attr variable">
|
<div class="attr variable">
|
||||||
<span class="name">JOINS</span> =
|
<span class="name">JOINS</span> =
|
||||||
<span class="default_value">{('', ''), ('RIGHT', ''), ('RIGHT', 'OUTER'), ('', 'INNER')}</span>
|
<span class="default_value">{('RIGHT', ''), ('RIGHT', 'OUTER'), ('', 'INNER'), ('', '')}</span>
|
||||||
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
17312
docs/sqlglot/parser.html
17312
docs/sqlglot/parser.html
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load diff
File diff suppressed because one or more lines are too long
|
@ -9075,7 +9075,7 @@
|
||||||
<div class="attr variable">
|
<div class="attr variable">
|
||||||
<span class="name">COMMANDS</span> =
|
<span class="name">COMMANDS</span> =
|
||||||
<input id="Tokenizer.COMMANDS-view-value" class="view-value-toggle-state" type="checkbox" aria-hidden="true" tabindex="-1">
|
<input id="Tokenizer.COMMANDS-view-value" class="view-value-toggle-state" type="checkbox" aria-hidden="true" tabindex="-1">
|
||||||
<label class="view-value-button pdoc-button" for="Tokenizer.COMMANDS-view-value"></label><span class="default_value">{<<a href="#TokenType.FETCH">TokenType.FETCH</a>: 'FETCH'>, <<a href="#TokenType.SHOW">TokenType.SHOW</a>: 'SHOW'>, <<a href="#TokenType.RENAME">TokenType.RENAME</a>: 'RENAME'>, <<a href="#TokenType.COMMAND">TokenType.COMMAND</a>: 'COMMAND'>, <<a href="#TokenType.EXECUTE">TokenType.EXECUTE</a>: 'EXECUTE'>}</span>
|
<label class="view-value-button pdoc-button" for="Tokenizer.COMMANDS-view-value"></label><span class="default_value">{<<a href="#TokenType.SHOW">TokenType.SHOW</a>: 'SHOW'>, <<a href="#TokenType.FETCH">TokenType.FETCH</a>: 'FETCH'>, <<a href="#TokenType.EXECUTE">TokenType.EXECUTE</a>: 'EXECUTE'>, <<a href="#TokenType.COMMAND">TokenType.COMMAND</a>: 'COMMAND'>, <<a href="#TokenType.RENAME">TokenType.RENAME</a>: 'RENAME'>}</span>
|
||||||
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -105,6 +105,9 @@
|
||||||
<li>
|
<li>
|
||||||
<a class="function" href="#eliminate_join_marks">eliminate_join_marks</a>
|
<a class="function" href="#eliminate_join_marks">eliminate_join_marks</a>
|
||||||
</li>
|
</li>
|
||||||
|
<li>
|
||||||
|
<a class="function" href="#any_to_exists">any_to_exists</a>
|
||||||
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
|
|
||||||
|
@ -1044,6 +1047,33 @@
|
||||||
</span><span id="L-914"><a href="#L-914"><span class="linenos">914</span></a> <span class="n">where</span><span class="o">.</span><span class="n">pop</span><span class="p">()</span>
|
</span><span id="L-914"><a href="#L-914"><span class="linenos">914</span></a> <span class="n">where</span><span class="o">.</span><span class="n">pop</span><span class="p">()</span>
|
||||||
</span><span id="L-915"><a href="#L-915"><span class="linenos">915</span></a>
|
</span><span id="L-915"><a href="#L-915"><span class="linenos">915</span></a>
|
||||||
</span><span id="L-916"><a href="#L-916"><span class="linenos">916</span></a> <span class="k">return</span> <span class="n">expression</span>
|
</span><span id="L-916"><a href="#L-916"><span class="linenos">916</span></a> <span class="k">return</span> <span class="n">expression</span>
|
||||||
|
</span><span id="L-917"><a href="#L-917"><span class="linenos">917</span></a>
|
||||||
|
</span><span id="L-918"><a href="#L-918"><span class="linenos">918</span></a>
|
||||||
|
</span><span id="L-919"><a href="#L-919"><span class="linenos">919</span></a><span class="k">def</span> <span class="nf">any_to_exists</span><span class="p">(</span><span class="n">expression</span><span class="p">:</span> <span class="n">exp</span><span class="o">.</span><span class="n">Expression</span><span class="p">)</span> <span class="o">-></span> <span class="n">exp</span><span class="o">.</span><span class="n">Expression</span><span class="p">:</span>
|
||||||
|
</span><span id="L-920"><a href="#L-920"><span class="linenos">920</span></a><span class="w"> </span><span class="sd">"""</span>
|
||||||
|
</span><span id="L-921"><a href="#L-921"><span class="linenos">921</span></a><span class="sd"> Transform ANY operator to Spark's EXISTS</span>
|
||||||
|
</span><span id="L-922"><a href="#L-922"><span class="linenos">922</span></a>
|
||||||
|
</span><span id="L-923"><a href="#L-923"><span class="linenos">923</span></a><span class="sd"> For example,</span>
|
||||||
|
</span><span id="L-924"><a href="#L-924"><span class="linenos">924</span></a><span class="sd"> - Postgres: SELECT * FROM tbl WHERE 5 > ANY(tbl.col)</span>
|
||||||
|
</span><span id="L-925"><a href="#L-925"><span class="linenos">925</span></a><span class="sd"> - Spark: SELECT * FROM tbl WHERE EXISTS(tbl.col, x -> x < 5)</span>
|
||||||
|
</span><span id="L-926"><a href="#L-926"><span class="linenos">926</span></a>
|
||||||
|
</span><span id="L-927"><a href="#L-927"><span class="linenos">927</span></a><span class="sd"> Both ANY and EXISTS accept queries but currently only array expressions are supported for this</span>
|
||||||
|
</span><span id="L-928"><a href="#L-928"><span class="linenos">928</span></a><span class="sd"> transformation</span>
|
||||||
|
</span><span id="L-929"><a href="#L-929"><span class="linenos">929</span></a><span class="sd"> """</span>
|
||||||
|
</span><span id="L-930"><a href="#L-930"><span class="linenos">930</span></a> <span class="k">if</span> <span class="nb">isinstance</span><span class="p">(</span><span class="n">expression</span><span class="p">,</span> <span class="n">exp</span><span class="o">.</span><span class="n">Select</span><span class="p">):</span>
|
||||||
|
</span><span id="L-931"><a href="#L-931"><span class="linenos">931</span></a> <span class="k">for</span> <span class="nb">any</span> <span class="ow">in</span> <span class="n">expression</span><span class="o">.</span><span class="n">find_all</span><span class="p">(</span><span class="n">exp</span><span class="o">.</span><span class="n">Any</span><span class="p">):</span>
|
||||||
|
</span><span id="L-932"><a href="#L-932"><span class="linenos">932</span></a> <span class="n">this</span> <span class="o">=</span> <span class="nb">any</span><span class="o">.</span><span class="n">this</span>
|
||||||
|
</span><span id="L-933"><a href="#L-933"><span class="linenos">933</span></a> <span class="k">if</span> <span class="nb">isinstance</span><span class="p">(</span><span class="n">this</span><span class="p">,</span> <span class="n">exp</span><span class="o">.</span><span class="n">Query</span><span class="p">):</span>
|
||||||
|
</span><span id="L-934"><a href="#L-934"><span class="linenos">934</span></a> <span class="k">continue</span>
|
||||||
|
</span><span id="L-935"><a href="#L-935"><span class="linenos">935</span></a>
|
||||||
|
</span><span id="L-936"><a href="#L-936"><span class="linenos">936</span></a> <span class="n">binop</span> <span class="o">=</span> <span class="nb">any</span><span class="o">.</span><span class="n">parent</span>
|
||||||
|
</span><span id="L-937"><a href="#L-937"><span class="linenos">937</span></a> <span class="k">if</span> <span class="nb">isinstance</span><span class="p">(</span><span class="n">binop</span><span class="p">,</span> <span class="n">exp</span><span class="o">.</span><span class="n">Binary</span><span class="p">):</span>
|
||||||
|
</span><span id="L-938"><a href="#L-938"><span class="linenos">938</span></a> <span class="n">lambda_arg</span> <span class="o">=</span> <span class="n">exp</span><span class="o">.</span><span class="n">to_identifier</span><span class="p">(</span><span class="s2">"x"</span><span class="p">)</span>
|
||||||
|
</span><span id="L-939"><a href="#L-939"><span class="linenos">939</span></a> <span class="nb">any</span><span class="o">.</span><span class="n">replace</span><span class="p">(</span><span class="n">lambda_arg</span><span class="p">)</span>
|
||||||
|
</span><span id="L-940"><a href="#L-940"><span class="linenos">940</span></a> <span class="n">lambda_expr</span> <span class="o">=</span> <span class="n">exp</span><span class="o">.</span><span class="n">Lambda</span><span class="p">(</span><span class="n">this</span><span class="o">=</span><span class="n">binop</span><span class="o">.</span><span class="n">copy</span><span class="p">(),</span> <span class="n">expressions</span><span class="o">=</span><span class="p">[</span><span class="n">lambda_arg</span><span class="p">])</span>
|
||||||
|
</span><span id="L-941"><a href="#L-941"><span class="linenos">941</span></a> <span class="n">binop</span><span class="o">.</span><span class="n">replace</span><span class="p">(</span><span class="n">exp</span><span class="o">.</span><span class="n">Exists</span><span class="p">(</span><span class="n">this</span><span class="o">=</span><span class="n">this</span><span class="o">.</span><span class="n">unnest</span><span class="p">(),</span> <span class="n">expression</span><span class="o">=</span><span class="n">lambda_expr</span><span class="p">))</span>
|
||||||
|
</span><span id="L-942"><a href="#L-942"><span class="linenos">942</span></a>
|
||||||
|
</span><span id="L-943"><a href="#L-943"><span class="linenos">943</span></a> <span class="k">return</span> <span class="n">expression</span>
|
||||||
</span></pre></div>
|
</span></pre></div>
|
||||||
|
|
||||||
|
|
||||||
|
@ -2462,6 +2492,57 @@ If this does not hold for a query, consider running <code><a href="optimizer/qua
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
</section>
|
||||||
|
<section id="any_to_exists">
|
||||||
|
<input id="any_to_exists-view-source" class="view-source-toggle-state" type="checkbox" aria-hidden="true" tabindex="-1">
|
||||||
|
<div class="attr function">
|
||||||
|
|
||||||
|
<span class="def">def</span>
|
||||||
|
<span class="name">any_to_exists</span><span class="signature pdoc-code multiline">(<span class="param"> <span class="n">expression</span><span class="p">:</span> <span class="n"><a href="expressions.html#Expression">sqlglot.expressions.Expression</a></span></span><span class="return-annotation">) -> <span class="n"><a href="expressions.html#Expression">sqlglot.expressions.Expression</a></span>:</span></span>
|
||||||
|
|
||||||
|
<label class="view-source-button" for="any_to_exists-view-source"><span>View Source</span></label>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<a class="headerlink" href="#any_to_exists"></a>
|
||||||
|
<div class="pdoc-code codehilite"><pre><span></span><span id="any_to_exists-920"><a href="#any_to_exists-920"><span class="linenos">920</span></a><span class="k">def</span> <span class="nf">any_to_exists</span><span class="p">(</span><span class="n">expression</span><span class="p">:</span> <span class="n">exp</span><span class="o">.</span><span class="n">Expression</span><span class="p">)</span> <span class="o">-></span> <span class="n">exp</span><span class="o">.</span><span class="n">Expression</span><span class="p">:</span>
|
||||||
|
</span><span id="any_to_exists-921"><a href="#any_to_exists-921"><span class="linenos">921</span></a><span class="w"> </span><span class="sd">"""</span>
|
||||||
|
</span><span id="any_to_exists-922"><a href="#any_to_exists-922"><span class="linenos">922</span></a><span class="sd"> Transform ANY operator to Spark's EXISTS</span>
|
||||||
|
</span><span id="any_to_exists-923"><a href="#any_to_exists-923"><span class="linenos">923</span></a>
|
||||||
|
</span><span id="any_to_exists-924"><a href="#any_to_exists-924"><span class="linenos">924</span></a><span class="sd"> For example,</span>
|
||||||
|
</span><span id="any_to_exists-925"><a href="#any_to_exists-925"><span class="linenos">925</span></a><span class="sd"> - Postgres: SELECT * FROM tbl WHERE 5 > ANY(tbl.col)</span>
|
||||||
|
</span><span id="any_to_exists-926"><a href="#any_to_exists-926"><span class="linenos">926</span></a><span class="sd"> - Spark: SELECT * FROM tbl WHERE EXISTS(tbl.col, x -> x < 5)</span>
|
||||||
|
</span><span id="any_to_exists-927"><a href="#any_to_exists-927"><span class="linenos">927</span></a>
|
||||||
|
</span><span id="any_to_exists-928"><a href="#any_to_exists-928"><span class="linenos">928</span></a><span class="sd"> Both ANY and EXISTS accept queries but currently only array expressions are supported for this</span>
|
||||||
|
</span><span id="any_to_exists-929"><a href="#any_to_exists-929"><span class="linenos">929</span></a><span class="sd"> transformation</span>
|
||||||
|
</span><span id="any_to_exists-930"><a href="#any_to_exists-930"><span class="linenos">930</span></a><span class="sd"> """</span>
|
||||||
|
</span><span id="any_to_exists-931"><a href="#any_to_exists-931"><span class="linenos">931</span></a> <span class="k">if</span> <span class="nb">isinstance</span><span class="p">(</span><span class="n">expression</span><span class="p">,</span> <span class="n">exp</span><span class="o">.</span><span class="n">Select</span><span class="p">):</span>
|
||||||
|
</span><span id="any_to_exists-932"><a href="#any_to_exists-932"><span class="linenos">932</span></a> <span class="k">for</span> <span class="nb">any</span> <span class="ow">in</span> <span class="n">expression</span><span class="o">.</span><span class="n">find_all</span><span class="p">(</span><span class="n">exp</span><span class="o">.</span><span class="n">Any</span><span class="p">):</span>
|
||||||
|
</span><span id="any_to_exists-933"><a href="#any_to_exists-933"><span class="linenos">933</span></a> <span class="n">this</span> <span class="o">=</span> <span class="nb">any</span><span class="o">.</span><span class="n">this</span>
|
||||||
|
</span><span id="any_to_exists-934"><a href="#any_to_exists-934"><span class="linenos">934</span></a> <span class="k">if</span> <span class="nb">isinstance</span><span class="p">(</span><span class="n">this</span><span class="p">,</span> <span class="n">exp</span><span class="o">.</span><span class="n">Query</span><span class="p">):</span>
|
||||||
|
</span><span id="any_to_exists-935"><a href="#any_to_exists-935"><span class="linenos">935</span></a> <span class="k">continue</span>
|
||||||
|
</span><span id="any_to_exists-936"><a href="#any_to_exists-936"><span class="linenos">936</span></a>
|
||||||
|
</span><span id="any_to_exists-937"><a href="#any_to_exists-937"><span class="linenos">937</span></a> <span class="n">binop</span> <span class="o">=</span> <span class="nb">any</span><span class="o">.</span><span class="n">parent</span>
|
||||||
|
</span><span id="any_to_exists-938"><a href="#any_to_exists-938"><span class="linenos">938</span></a> <span class="k">if</span> <span class="nb">isinstance</span><span class="p">(</span><span class="n">binop</span><span class="p">,</span> <span class="n">exp</span><span class="o">.</span><span class="n">Binary</span><span class="p">):</span>
|
||||||
|
</span><span id="any_to_exists-939"><a href="#any_to_exists-939"><span class="linenos">939</span></a> <span class="n">lambda_arg</span> <span class="o">=</span> <span class="n">exp</span><span class="o">.</span><span class="n">to_identifier</span><span class="p">(</span><span class="s2">"x"</span><span class="p">)</span>
|
||||||
|
</span><span id="any_to_exists-940"><a href="#any_to_exists-940"><span class="linenos">940</span></a> <span class="nb">any</span><span class="o">.</span><span class="n">replace</span><span class="p">(</span><span class="n">lambda_arg</span><span class="p">)</span>
|
||||||
|
</span><span id="any_to_exists-941"><a href="#any_to_exists-941"><span class="linenos">941</span></a> <span class="n">lambda_expr</span> <span class="o">=</span> <span class="n">exp</span><span class="o">.</span><span class="n">Lambda</span><span class="p">(</span><span class="n">this</span><span class="o">=</span><span class="n">binop</span><span class="o">.</span><span class="n">copy</span><span class="p">(),</span> <span class="n">expressions</span><span class="o">=</span><span class="p">[</span><span class="n">lambda_arg</span><span class="p">])</span>
|
||||||
|
</span><span id="any_to_exists-942"><a href="#any_to_exists-942"><span class="linenos">942</span></a> <span class="n">binop</span><span class="o">.</span><span class="n">replace</span><span class="p">(</span><span class="n">exp</span><span class="o">.</span><span class="n">Exists</span><span class="p">(</span><span class="n">this</span><span class="o">=</span><span class="n">this</span><span class="o">.</span><span class="n">unnest</span><span class="p">(),</span> <span class="n">expression</span><span class="o">=</span><span class="n">lambda_expr</span><span class="p">))</span>
|
||||||
|
</span><span id="any_to_exists-943"><a href="#any_to_exists-943"><span class="linenos">943</span></a>
|
||||||
|
</span><span id="any_to_exists-944"><a href="#any_to_exists-944"><span class="linenos">944</span></a> <span class="k">return</span> <span class="n">expression</span>
|
||||||
|
</span></pre></div>
|
||||||
|
|
||||||
|
|
||||||
|
<div class="docstring"><p>Transform ANY operator to Spark's EXISTS</p>
|
||||||
|
|
||||||
|
<p>For example,
|
||||||
|
- Postgres: SELECT * FROM tbl WHERE 5 > ANY(tbl.col)
|
||||||
|
- Spark: SELECT * FROM tbl WHERE EXISTS(tbl.col, x -> x < 5)</p>
|
||||||
|
|
||||||
|
<p>Both ANY and EXISTS accept queries but currently only array expressions are supported for this
|
||||||
|
transformation</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
</section>
|
</section>
|
||||||
</main>
|
</main>
|
||||||
<script>
|
<script>
|
||||||
|
|
|
@ -29,6 +29,7 @@ from sqlglot.dialects.dialect import (
|
||||||
)
|
)
|
||||||
from sqlglot.helper import seq_get, split_num_words
|
from sqlglot.helper import seq_get, split_num_words
|
||||||
from sqlglot.tokens import TokenType
|
from sqlglot.tokens import TokenType
|
||||||
|
from sqlglot.generator import unsupported_args
|
||||||
|
|
||||||
if t.TYPE_CHECKING:
|
if t.TYPE_CHECKING:
|
||||||
from sqlglot._typing import E, Lit
|
from sqlglot._typing import E, Lit
|
||||||
|
@ -216,26 +217,35 @@ def _build_datetime(args: t.List) -> exp.Func:
|
||||||
return exp.TimestampFromParts.from_arg_list(args)
|
return exp.TimestampFromParts.from_arg_list(args)
|
||||||
|
|
||||||
|
|
||||||
def _build_regexp_extract(args: t.List) -> exp.RegexpExtract:
|
def _build_regexp_extract(
|
||||||
|
expr_type: t.Type[E], default_group: t.Optional[exp.Expression] = None
|
||||||
|
) -> t.Callable[[t.List], E]:
|
||||||
|
def _builder(args: t.List) -> E:
|
||||||
try:
|
try:
|
||||||
group = re.compile(args[1].name).groups == 1
|
group = re.compile(args[1].name).groups == 1
|
||||||
except re.error:
|
except re.error:
|
||||||
group = False
|
group = False
|
||||||
|
|
||||||
return exp.RegexpExtract(
|
# Default group is used for the transpilation of REGEXP_EXTRACT_ALL
|
||||||
|
return expr_type(
|
||||||
this=seq_get(args, 0),
|
this=seq_get(args, 0),
|
||||||
expression=seq_get(args, 1),
|
expression=seq_get(args, 1),
|
||||||
position=seq_get(args, 2),
|
position=seq_get(args, 2),
|
||||||
occurrence=seq_get(args, 3),
|
occurrence=seq_get(args, 3),
|
||||||
group=exp.Literal.number(1) if group else None,
|
group=exp.Literal.number(1) if group else default_group,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
return _builder
|
||||||
|
|
||||||
def _build_json_extract_scalar(args: t.List, dialect: Dialect) -> exp.JSONExtractScalar:
|
|
||||||
|
def _build_extract_json_with_default_path(expr_type: t.Type[E]) -> t.Callable[[t.List, Dialect], E]:
|
||||||
|
def _builder(args: t.List, dialect: Dialect) -> E:
|
||||||
if len(args) == 1:
|
if len(args) == 1:
|
||||||
# The default value for the JSONPath is '$' i.e all of the data
|
# The default value for the JSONPath is '$' i.e all of the data
|
||||||
args.append(exp.Literal.string("$"))
|
args.append(exp.Literal.string("$"))
|
||||||
return parser.build_extract_json_with_path(exp.JSONExtractScalar)(args, dialect)
|
return parser.build_extract_json_with_path(expr_type)(args, dialect)
|
||||||
|
|
||||||
|
return _builder
|
||||||
|
|
||||||
|
|
||||||
def _str_to_datetime_sql(
|
def _str_to_datetime_sql(
|
||||||
|
@ -276,6 +286,24 @@ def _annotate_math_functions(self: TypeAnnotator, expression: E) -> E:
|
||||||
return expression
|
return expression
|
||||||
|
|
||||||
|
|
||||||
|
@unsupported_args("ins_cost", "del_cost", "sub_cost")
|
||||||
|
def _levenshtein_sql(self: BigQuery.Generator, expression: exp.Levenshtein) -> str:
|
||||||
|
max_dist = expression.args.get("max_dist")
|
||||||
|
if max_dist:
|
||||||
|
max_dist = exp.Kwarg(this=exp.var("max_distance"), expression=max_dist)
|
||||||
|
|
||||||
|
return self.func("EDIT_DISTANCE", expression.this, expression.expression, max_dist)
|
||||||
|
|
||||||
|
|
||||||
|
def _build_levenshtein(args: t.List) -> exp.Levenshtein:
|
||||||
|
max_dist = seq_get(args, 2)
|
||||||
|
return exp.Levenshtein(
|
||||||
|
this=seq_get(args, 0),
|
||||||
|
expression=seq_get(args, 1),
|
||||||
|
max_dist=max_dist.expression if max_dist else None,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class BigQuery(Dialect):
|
class BigQuery(Dialect):
|
||||||
WEEK_OFFSET = -1
|
WEEK_OFFSET = -1
|
||||||
UNNEST_COLUMN_ONLY = True
|
UNNEST_COLUMN_ONLY = True
|
||||||
|
@ -433,16 +461,17 @@ class BigQuery(Dialect):
|
||||||
"DATETIME_ADD": build_date_delta_with_interval(exp.DatetimeAdd),
|
"DATETIME_ADD": build_date_delta_with_interval(exp.DatetimeAdd),
|
||||||
"DATETIME_SUB": build_date_delta_with_interval(exp.DatetimeSub),
|
"DATETIME_SUB": build_date_delta_with_interval(exp.DatetimeSub),
|
||||||
"DIV": binary_from_function(exp.IntDiv),
|
"DIV": binary_from_function(exp.IntDiv),
|
||||||
"EDIT_DISTANCE": lambda args: exp.Levenshtein(
|
"EDIT_DISTANCE": _build_levenshtein,
|
||||||
this=seq_get(args, 0), expression=seq_get(args, 1), max_dist=seq_get(args, 2)
|
|
||||||
),
|
|
||||||
"FORMAT_DATE": lambda args: exp.TimeToStr(
|
"FORMAT_DATE": lambda args: exp.TimeToStr(
|
||||||
this=exp.TsOrDsToDate(this=seq_get(args, 1)), format=seq_get(args, 0)
|
this=exp.TsOrDsToDate(this=seq_get(args, 1)), format=seq_get(args, 0)
|
||||||
),
|
),
|
||||||
"GENERATE_ARRAY": exp.GenerateSeries.from_arg_list,
|
"GENERATE_ARRAY": exp.GenerateSeries.from_arg_list,
|
||||||
"JSON_EXTRACT_SCALAR": _build_json_extract_scalar,
|
"JSON_EXTRACT_SCALAR": _build_extract_json_with_default_path(exp.JSONExtractScalar),
|
||||||
|
"JSON_EXTRACT_ARRAY": _build_extract_json_with_default_path(exp.JSONExtractArray),
|
||||||
"JSON_QUERY": parser.build_extract_json_with_path(exp.JSONExtract),
|
"JSON_QUERY": parser.build_extract_json_with_path(exp.JSONExtract),
|
||||||
"JSON_VALUE": _build_json_extract_scalar,
|
"JSON_QUERY_ARRAY": _build_extract_json_with_default_path(exp.JSONExtractArray),
|
||||||
|
"JSON_VALUE": _build_extract_json_with_default_path(exp.JSONExtractScalar),
|
||||||
|
"JSON_VALUE_ARRAY": _build_extract_json_with_default_path(exp.JSONValueArray),
|
||||||
"LENGTH": lambda args: exp.Length(this=seq_get(args, 0), binary=True),
|
"LENGTH": lambda args: exp.Length(this=seq_get(args, 0), binary=True),
|
||||||
"MD5": exp.MD5Digest.from_arg_list,
|
"MD5": exp.MD5Digest.from_arg_list,
|
||||||
"TO_HEX": _build_to_hex,
|
"TO_HEX": _build_to_hex,
|
||||||
|
@ -451,7 +480,11 @@ class BigQuery(Dialect):
|
||||||
),
|
),
|
||||||
"PARSE_TIMESTAMP": _build_parse_timestamp,
|
"PARSE_TIMESTAMP": _build_parse_timestamp,
|
||||||
"REGEXP_CONTAINS": exp.RegexpLike.from_arg_list,
|
"REGEXP_CONTAINS": exp.RegexpLike.from_arg_list,
|
||||||
"REGEXP_EXTRACT": _build_regexp_extract,
|
"REGEXP_EXTRACT": _build_regexp_extract(exp.RegexpExtract),
|
||||||
|
"REGEXP_SUBSTR": _build_regexp_extract(exp.RegexpExtract),
|
||||||
|
"REGEXP_EXTRACT_ALL": _build_regexp_extract(
|
||||||
|
exp.RegexpExtractAll, default_group=exp.Literal.number(0)
|
||||||
|
),
|
||||||
"SHA256": lambda args: exp.SHA2(this=seq_get(args, 0), length=exp.Literal.number(256)),
|
"SHA256": lambda args: exp.SHA2(this=seq_get(args, 0), length=exp.Literal.number(256)),
|
||||||
"SHA512": lambda args: exp.SHA2(this=seq_get(args, 0), length=exp.Literal.number(512)),
|
"SHA512": lambda args: exp.SHA2(this=seq_get(args, 0), length=exp.Literal.number(512)),
|
||||||
"SPLIT": lambda args: exp.Split(
|
"SPLIT": lambda args: exp.Split(
|
||||||
|
@ -735,6 +768,7 @@ class BigQuery(Dialect):
|
||||||
WITH_PROPERTIES_PREFIX = "OPTIONS"
|
WITH_PROPERTIES_PREFIX = "OPTIONS"
|
||||||
SUPPORTS_EXPLODING_PROJECTIONS = False
|
SUPPORTS_EXPLODING_PROJECTIONS = False
|
||||||
EXCEPT_INTERSECT_SUPPORT_ALL_CLAUSE = False
|
EXCEPT_INTERSECT_SUPPORT_ALL_CLAUSE = False
|
||||||
|
SUPPORTS_UNIX_SECONDS = True
|
||||||
|
|
||||||
TRANSFORMS = {
|
TRANSFORMS = {
|
||||||
**generator.Generator.TRANSFORMS,
|
**generator.Generator.TRANSFORMS,
|
||||||
|
@ -744,7 +778,6 @@ class BigQuery(Dialect):
|
||||||
exp.Array: inline_array_unless_query,
|
exp.Array: inline_array_unless_query,
|
||||||
exp.ArrayContains: _array_contains_sql,
|
exp.ArrayContains: _array_contains_sql,
|
||||||
exp.ArrayFilter: filter_array_using_unnest,
|
exp.ArrayFilter: filter_array_using_unnest,
|
||||||
exp.ArraySize: rename_func("ARRAY_LENGTH"),
|
|
||||||
exp.Cast: transforms.preprocess([transforms.remove_precision_parameterized_types]),
|
exp.Cast: transforms.preprocess([transforms.remove_precision_parameterized_types]),
|
||||||
exp.CollateProperty: lambda self, e: (
|
exp.CollateProperty: lambda self, e: (
|
||||||
f"DEFAULT COLLATE {self.sql(e, 'this')}"
|
f"DEFAULT COLLATE {self.sql(e, 'this')}"
|
||||||
|
@ -777,7 +810,7 @@ class BigQuery(Dialect):
|
||||||
exp.ILike: no_ilike_sql,
|
exp.ILike: no_ilike_sql,
|
||||||
exp.IntDiv: rename_func("DIV"),
|
exp.IntDiv: rename_func("DIV"),
|
||||||
exp.JSONFormat: rename_func("TO_JSON_STRING"),
|
exp.JSONFormat: rename_func("TO_JSON_STRING"),
|
||||||
exp.Levenshtein: rename_func("EDIT_DISTANCE"),
|
exp.Levenshtein: _levenshtein_sql,
|
||||||
exp.Max: max_or_greatest,
|
exp.Max: max_or_greatest,
|
||||||
exp.MD5: lambda self, e: self.func("TO_HEX", self.func("MD5", e.this)),
|
exp.MD5: lambda self, e: self.func("TO_HEX", self.func("MD5", e.this)),
|
||||||
exp.MD5Digest: rename_func("MD5"),
|
exp.MD5Digest: rename_func("MD5"),
|
||||||
|
@ -790,6 +823,9 @@ class BigQuery(Dialect):
|
||||||
e.args.get("position"),
|
e.args.get("position"),
|
||||||
e.args.get("occurrence"),
|
e.args.get("occurrence"),
|
||||||
),
|
),
|
||||||
|
exp.RegexpExtractAll: lambda self, e: self.func(
|
||||||
|
"REGEXP_EXTRACT_ALL", e.this, e.expression
|
||||||
|
),
|
||||||
exp.RegexpReplace: regexp_replace_sql,
|
exp.RegexpReplace: regexp_replace_sql,
|
||||||
exp.RegexpLike: rename_func("REGEXP_CONTAINS"),
|
exp.RegexpLike: rename_func("REGEXP_CONTAINS"),
|
||||||
exp.ReturnsProperty: _returnsproperty_sql,
|
exp.ReturnsProperty: _returnsproperty_sql,
|
||||||
|
|
|
@ -23,6 +23,7 @@ from sqlglot.dialects.dialect import (
|
||||||
from sqlglot.generator import Generator
|
from sqlglot.generator import Generator
|
||||||
from sqlglot.helper import is_int, seq_get
|
from sqlglot.helper import is_int, seq_get
|
||||||
from sqlglot.tokens import Token, TokenType
|
from sqlglot.tokens import Token, TokenType
|
||||||
|
from sqlglot.generator import unsupported_args
|
||||||
|
|
||||||
DATEΤΙΜΕ_DELTA = t.Union[exp.DateAdd, exp.DateDiff, exp.DateSub, exp.TimestampSub, exp.TimestampAdd]
|
DATEΤΙΜΕ_DELTA = t.Union[exp.DateAdd, exp.DateDiff, exp.DateSub, exp.TimestampSub, exp.TimestampAdd]
|
||||||
|
|
||||||
|
@ -271,6 +272,8 @@ class ClickHouse(Dialect):
|
||||||
"MD5": exp.MD5Digest.from_arg_list,
|
"MD5": exp.MD5Digest.from_arg_list,
|
||||||
"SHA256": lambda args: exp.SHA2(this=seq_get(args, 0), length=exp.Literal.number(256)),
|
"SHA256": lambda args: exp.SHA2(this=seq_get(args, 0), length=exp.Literal.number(256)),
|
||||||
"SHA512": lambda args: exp.SHA2(this=seq_get(args, 0), length=exp.Literal.number(512)),
|
"SHA512": lambda args: exp.SHA2(this=seq_get(args, 0), length=exp.Literal.number(512)),
|
||||||
|
"EDITDISTANCE": exp.Levenshtein.from_arg_list,
|
||||||
|
"LEVENSHTEINDISTANCE": exp.Levenshtein.from_arg_list,
|
||||||
}
|
}
|
||||||
|
|
||||||
AGG_FUNCTIONS = {
|
AGG_FUNCTIONS = {
|
||||||
|
@ -850,6 +853,7 @@ class ClickHouse(Dialect):
|
||||||
SET_OP_MODIFIERS = False
|
SET_OP_MODIFIERS = False
|
||||||
SUPPORTS_TABLE_ALIAS_COLUMNS = False
|
SUPPORTS_TABLE_ALIAS_COLUMNS = False
|
||||||
VALUES_AS_TABLE = False
|
VALUES_AS_TABLE = False
|
||||||
|
ARRAY_SIZE_NAME = "LENGTH"
|
||||||
|
|
||||||
STRING_TYPE_MAPPING = {
|
STRING_TYPE_MAPPING = {
|
||||||
exp.DataType.Type.CHAR: "String",
|
exp.DataType.Type.CHAR: "String",
|
||||||
|
@ -925,7 +929,6 @@ class ClickHouse(Dialect):
|
||||||
exp.AnyValue: rename_func("any"),
|
exp.AnyValue: rename_func("any"),
|
||||||
exp.ApproxDistinct: rename_func("uniq"),
|
exp.ApproxDistinct: rename_func("uniq"),
|
||||||
exp.ArrayFilter: lambda self, e: self.func("arrayFilter", e.expression, e.this),
|
exp.ArrayFilter: lambda self, e: self.func("arrayFilter", e.expression, e.this),
|
||||||
exp.ArraySize: rename_func("LENGTH"),
|
|
||||||
exp.ArraySum: rename_func("arraySum"),
|
exp.ArraySum: rename_func("arraySum"),
|
||||||
exp.ArgMax: arg_max_or_min_no_count("argMax"),
|
exp.ArgMax: arg_max_or_min_no_count("argMax"),
|
||||||
exp.ArgMin: arg_max_or_min_no_count("argMin"),
|
exp.ArgMin: arg_max_or_min_no_count("argMin"),
|
||||||
|
@ -949,6 +952,7 @@ class ClickHouse(Dialect):
|
||||||
exp.JSONPathKey: json_path_key_only_name,
|
exp.JSONPathKey: json_path_key_only_name,
|
||||||
exp.JSONPathRoot: lambda *_: "",
|
exp.JSONPathRoot: lambda *_: "",
|
||||||
exp.Map: lambda self, e: _lower_func(var_map_sql(self, e)),
|
exp.Map: lambda self, e: _lower_func(var_map_sql(self, e)),
|
||||||
|
exp.Median: rename_func("median"),
|
||||||
exp.Nullif: rename_func("nullIf"),
|
exp.Nullif: rename_func("nullIf"),
|
||||||
exp.PartitionedByProperty: lambda self, e: f"PARTITION BY {self.sql(e, 'this')}",
|
exp.PartitionedByProperty: lambda self, e: f"PARTITION BY {self.sql(e, 'this')}",
|
||||||
exp.Pivot: no_pivot_sql,
|
exp.Pivot: no_pivot_sql,
|
||||||
|
@ -984,6 +988,9 @@ class ClickHouse(Dialect):
|
||||||
exp.Lead: lambda self, e: self.func(
|
exp.Lead: lambda self, e: self.func(
|
||||||
"leadInFrame", e.this, e.args.get("offset"), e.args.get("default")
|
"leadInFrame", e.this, e.args.get("offset"), e.args.get("default")
|
||||||
),
|
),
|
||||||
|
exp.Levenshtein: unsupported_args("ins_cost", "del_cost", "sub_cost", "max_dist")(
|
||||||
|
rename_func("editDistance")
|
||||||
|
),
|
||||||
}
|
}
|
||||||
|
|
||||||
PROPERTIES_LOCATION = {
|
PROPERTIES_LOCATION = {
|
||||||
|
|
|
@ -7,7 +7,6 @@ from sqlglot.dialects.dialect import (
|
||||||
date_delta_sql,
|
date_delta_sql,
|
||||||
build_date_delta,
|
build_date_delta,
|
||||||
timestamptrunc_sql,
|
timestamptrunc_sql,
|
||||||
timestampdiff_sql,
|
|
||||||
)
|
)
|
||||||
from sqlglot.dialects.spark import Spark
|
from sqlglot.dialects.spark import Spark
|
||||||
from sqlglot.tokens import TokenType
|
from sqlglot.tokens import TokenType
|
||||||
|
@ -46,7 +45,6 @@ class Databricks(Spark):
|
||||||
"DATE_ADD": build_date_delta(exp.DateAdd),
|
"DATE_ADD": build_date_delta(exp.DateAdd),
|
||||||
"DATEDIFF": build_date_delta(exp.DateDiff),
|
"DATEDIFF": build_date_delta(exp.DateDiff),
|
||||||
"DATE_DIFF": build_date_delta(exp.DateDiff),
|
"DATE_DIFF": build_date_delta(exp.DateDiff),
|
||||||
"TIMESTAMPDIFF": build_date_delta(exp.TimestampDiff),
|
|
||||||
"GET_JSON_OBJECT": _build_json_extract,
|
"GET_JSON_OBJECT": _build_json_extract,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -75,8 +73,6 @@ class Databricks(Spark):
|
||||||
exp.Mul(this=e.expression, expression=exp.Literal.number(-1)),
|
exp.Mul(this=e.expression, expression=exp.Literal.number(-1)),
|
||||||
e.this,
|
e.this,
|
||||||
),
|
),
|
||||||
exp.DatetimeDiff: timestampdiff_sql,
|
|
||||||
exp.TimestampDiff: timestampdiff_sql,
|
|
||||||
exp.DatetimeTrunc: timestamptrunc_sql(),
|
exp.DatetimeTrunc: timestamptrunc_sql(),
|
||||||
exp.Select: transforms.preprocess(
|
exp.Select: transforms.preprocess(
|
||||||
[
|
[
|
||||||
|
|
|
@ -1364,14 +1364,16 @@ def concat_ws_to_dpipe_sql(self: Generator, expression: exp.ConcatWs) -> str:
|
||||||
|
|
||||||
|
|
||||||
@unsupported_args("position", "occurrence", "parameters")
|
@unsupported_args("position", "occurrence", "parameters")
|
||||||
def regexp_extract_sql(self: Generator, expression: exp.RegexpExtract) -> str:
|
def regexp_extract_sql(
|
||||||
|
self: Generator, expression: exp.RegexpExtract | exp.RegexpExtractAll
|
||||||
|
) -> str:
|
||||||
group = expression.args.get("group")
|
group = expression.args.get("group")
|
||||||
|
|
||||||
# Do not render group if it's the default value for this dialect
|
# Do not render group if it's the default value for this dialect
|
||||||
if group and group.name == str(self.dialect.REGEXP_EXTRACT_DEFAULT_GROUP):
|
if group and group.name == str(self.dialect.REGEXP_EXTRACT_DEFAULT_GROUP):
|
||||||
group = None
|
group = None
|
||||||
|
|
||||||
return self.func("REGEXP_EXTRACT", expression.this, expression.expression, group)
|
return self.func(expression.sql_name(), expression.this, expression.expression, group)
|
||||||
|
|
||||||
|
|
||||||
@unsupported_args("position", "occurrence", "modifiers")
|
@unsupported_args("position", "occurrence", "modifiers")
|
||||||
|
@ -1693,14 +1695,17 @@ def sequence_sql(self: Generator, expression: exp.GenerateSeries | exp.GenerateD
|
||||||
return self.func("SEQUENCE", start, end, step)
|
return self.func("SEQUENCE", start, end, step)
|
||||||
|
|
||||||
|
|
||||||
def build_regexp_extract(args: t.List, dialect: Dialect) -> exp.RegexpExtract:
|
def build_regexp_extract(expr_type: t.Type[E]) -> t.Callable[[t.List, Dialect], E]:
|
||||||
return exp.RegexpExtract(
|
def _builder(args: t.List, dialect: Dialect) -> E:
|
||||||
|
return expr_type(
|
||||||
this=seq_get(args, 0),
|
this=seq_get(args, 0),
|
||||||
expression=seq_get(args, 1),
|
expression=seq_get(args, 1),
|
||||||
group=seq_get(args, 2) or exp.Literal.number(dialect.REGEXP_EXTRACT_DEFAULT_GROUP),
|
group=seq_get(args, 2) or exp.Literal.number(dialect.REGEXP_EXTRACT_DEFAULT_GROUP),
|
||||||
parameters=seq_get(args, 3),
|
parameters=seq_get(args, 3),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
return _builder
|
||||||
|
|
||||||
|
|
||||||
def explode_to_unnest_sql(self: Generator, expression: exp.Lateral) -> str:
|
def explode_to_unnest_sql(self: Generator, expression: exp.Lateral) -> str:
|
||||||
if isinstance(expression.this, exp.Explode):
|
if isinstance(expression.this, exp.Explode):
|
||||||
|
|
|
@ -13,6 +13,7 @@ from sqlglot.dialects.dialect import (
|
||||||
)
|
)
|
||||||
from sqlglot.dialects.mysql import date_add_sql
|
from sqlglot.dialects.mysql import date_add_sql
|
||||||
from sqlglot.transforms import preprocess, move_schema_columns_to_partitioned_by
|
from sqlglot.transforms import preprocess, move_schema_columns_to_partitioned_by
|
||||||
|
from sqlglot.generator import unsupported_args
|
||||||
|
|
||||||
|
|
||||||
def _str_to_date(self: Drill.Generator, expression: exp.StrToDate) -> str:
|
def _str_to_date(self: Drill.Generator, expression: exp.StrToDate) -> str:
|
||||||
|
@ -78,8 +79,10 @@ class Drill(Dialect):
|
||||||
|
|
||||||
FUNCTIONS = {
|
FUNCTIONS = {
|
||||||
**parser.Parser.FUNCTIONS,
|
**parser.Parser.FUNCTIONS,
|
||||||
|
"REPEATED_COUNT": exp.ArraySize.from_arg_list,
|
||||||
"TO_TIMESTAMP": exp.TimeStrToTime.from_arg_list,
|
"TO_TIMESTAMP": exp.TimeStrToTime.from_arg_list,
|
||||||
"TO_CHAR": build_formatted_time(exp.TimeToStr, "drill"),
|
"TO_CHAR": build_formatted_time(exp.TimeToStr, "drill"),
|
||||||
|
"LEVENSHTEIN_DISTANCE": exp.Levenshtein.from_arg_list,
|
||||||
}
|
}
|
||||||
|
|
||||||
LOG_DEFAULTS_TO_LN = True
|
LOG_DEFAULTS_TO_LN = True
|
||||||
|
@ -91,6 +94,7 @@ class Drill(Dialect):
|
||||||
NVL2_SUPPORTED = False
|
NVL2_SUPPORTED = False
|
||||||
LAST_DAY_SUPPORTS_DATE_PART = False
|
LAST_DAY_SUPPORTS_DATE_PART = False
|
||||||
SUPPORTS_CREATE_TABLE_LIKE = False
|
SUPPORTS_CREATE_TABLE_LIKE = False
|
||||||
|
ARRAY_SIZE_NAME = "REPEATED_COUNT"
|
||||||
|
|
||||||
TYPE_MAPPING = {
|
TYPE_MAPPING = {
|
||||||
**generator.Generator.TYPE_MAPPING,
|
**generator.Generator.TYPE_MAPPING,
|
||||||
|
@ -115,7 +119,6 @@ class Drill(Dialect):
|
||||||
**generator.Generator.TRANSFORMS,
|
**generator.Generator.TRANSFORMS,
|
||||||
exp.CurrentTimestamp: lambda *_: "CURRENT_TIMESTAMP",
|
exp.CurrentTimestamp: lambda *_: "CURRENT_TIMESTAMP",
|
||||||
exp.ArrayContains: rename_func("REPEATED_CONTAINS"),
|
exp.ArrayContains: rename_func("REPEATED_CONTAINS"),
|
||||||
exp.ArraySize: rename_func("REPEATED_COUNT"),
|
|
||||||
exp.Create: preprocess([move_schema_columns_to_partitioned_by]),
|
exp.Create: preprocess([move_schema_columns_to_partitioned_by]),
|
||||||
exp.DateAdd: date_add_sql("ADD"),
|
exp.DateAdd: date_add_sql("ADD"),
|
||||||
exp.DateStrToDate: datestrtodate_sql,
|
exp.DateStrToDate: datestrtodate_sql,
|
||||||
|
@ -127,7 +130,9 @@ class Drill(Dialect):
|
||||||
exp.If: lambda self,
|
exp.If: lambda self,
|
||||||
e: f"`IF`({self.format_args(e.this, e.args.get('true'), e.args.get('false'))})",
|
e: f"`IF`({self.format_args(e.this, e.args.get('true'), e.args.get('false'))})",
|
||||||
exp.ILike: lambda self, e: self.binary(e, "`ILIKE`"),
|
exp.ILike: lambda self, e: self.binary(e, "`ILIKE`"),
|
||||||
exp.Levenshtein: rename_func("LEVENSHTEIN_DISTANCE"),
|
exp.Levenshtein: unsupported_args("ins_cost", "del_cost", "sub_cost", "max_dist")(
|
||||||
|
rename_func("LEVENSHTEIN_DISTANCE")
|
||||||
|
),
|
||||||
exp.PartitionedByProperty: lambda self, e: f"PARTITION BY {self.sql(e, 'this')}",
|
exp.PartitionedByProperty: lambda self, e: f"PARTITION BY {self.sql(e, 'this')}",
|
||||||
exp.RegexpLike: rename_func("REGEXP_MATCHES"),
|
exp.RegexpLike: rename_func("REGEXP_MATCHES"),
|
||||||
exp.StrPosition: str_position_sql,
|
exp.StrPosition: str_position_sql,
|
||||||
|
|
|
@ -156,18 +156,24 @@ def _struct_sql(self: DuckDB.Generator, expression: exp.Struct) -> str:
|
||||||
|
|
||||||
# BigQuery allows inline construction such as "STRUCT<a STRING, b INTEGER>('str', 1)" which is
|
# BigQuery allows inline construction such as "STRUCT<a STRING, b INTEGER>('str', 1)" which is
|
||||||
# canonicalized to "ROW('str', 1) AS STRUCT(a TEXT, b INT)" in DuckDB
|
# canonicalized to "ROW('str', 1) AS STRUCT(a TEXT, b INT)" in DuckDB
|
||||||
# The transformation to ROW will take place if a cast to STRUCT / ARRAY of STRUCTs is found
|
# The transformation to ROW will take place if:
|
||||||
|
# 1. The STRUCT itself does not have proper fields (key := value) as a "proper" STRUCT would
|
||||||
|
# 2. A cast to STRUCT / ARRAY of STRUCTs is found
|
||||||
ancestor_cast = expression.find_ancestor(exp.Cast)
|
ancestor_cast = expression.find_ancestor(exp.Cast)
|
||||||
is_struct_cast = ancestor_cast and any(
|
is_bq_inline_struct = (
|
||||||
|
(expression.find(exp.PropertyEQ) is None)
|
||||||
|
and ancestor_cast
|
||||||
|
and any(
|
||||||
casted_type.is_type(exp.DataType.Type.STRUCT)
|
casted_type.is_type(exp.DataType.Type.STRUCT)
|
||||||
for casted_type in ancestor_cast.find_all(exp.DataType)
|
for casted_type in ancestor_cast.find_all(exp.DataType)
|
||||||
)
|
)
|
||||||
|
)
|
||||||
|
|
||||||
for i, expr in enumerate(expression.expressions):
|
for i, expr in enumerate(expression.expressions):
|
||||||
is_property_eq = isinstance(expr, exp.PropertyEQ)
|
is_property_eq = isinstance(expr, exp.PropertyEQ)
|
||||||
value = expr.expression if is_property_eq else expr
|
value = expr.expression if is_property_eq else expr
|
||||||
|
|
||||||
if is_struct_cast:
|
if is_bq_inline_struct:
|
||||||
args.append(self.sql(value))
|
args.append(self.sql(value))
|
||||||
else:
|
else:
|
||||||
key = expr.name if is_property_eq else f"_{i}"
|
key = expr.name if is_property_eq else f"_{i}"
|
||||||
|
@ -175,7 +181,7 @@ def _struct_sql(self: DuckDB.Generator, expression: exp.Struct) -> str:
|
||||||
|
|
||||||
csv_args = ", ".join(args)
|
csv_args = ", ".join(args)
|
||||||
|
|
||||||
return f"ROW({csv_args})" if is_struct_cast else f"{{{csv_args}}}"
|
return f"ROW({csv_args})" if is_bq_inline_struct else f"{{{csv_args}}}"
|
||||||
|
|
||||||
|
|
||||||
def _datatype_sql(self: DuckDB.Generator, expression: exp.DataType) -> str:
|
def _datatype_sql(self: DuckDB.Generator, expression: exp.DataType) -> str:
|
||||||
|
@ -257,6 +263,14 @@ def _generate_datetime_array_sql(
|
||||||
return self.sql(gen_series)
|
return self.sql(gen_series)
|
||||||
|
|
||||||
|
|
||||||
|
def _json_extract_value_array_sql(
|
||||||
|
self: DuckDB.Generator, expression: exp.JSONValueArray | exp.JSONExtractArray
|
||||||
|
) -> str:
|
||||||
|
json_extract = exp.JSONExtract(this=expression.this, expression=expression.expression)
|
||||||
|
data_type = "ARRAY<STRING>" if isinstance(expression, exp.JSONValueArray) else "ARRAY<JSON>"
|
||||||
|
return self.sql(exp.cast(json_extract, to=exp.DataType.build(data_type)))
|
||||||
|
|
||||||
|
|
||||||
class DuckDB(Dialect):
|
class DuckDB(Dialect):
|
||||||
NULL_ORDERING = "nulls_are_last"
|
NULL_ORDERING = "nulls_are_last"
|
||||||
SUPPORTS_USER_DEFINED_TYPES = False
|
SUPPORTS_USER_DEFINED_TYPES = False
|
||||||
|
@ -376,7 +390,8 @@ class DuckDB(Dialect):
|
||||||
"MAKE_TIMESTAMP": _build_make_timestamp,
|
"MAKE_TIMESTAMP": _build_make_timestamp,
|
||||||
"QUANTILE_CONT": exp.PercentileCont.from_arg_list,
|
"QUANTILE_CONT": exp.PercentileCont.from_arg_list,
|
||||||
"QUANTILE_DISC": exp.PercentileDisc.from_arg_list,
|
"QUANTILE_DISC": exp.PercentileDisc.from_arg_list,
|
||||||
"REGEXP_EXTRACT": build_regexp_extract,
|
"REGEXP_EXTRACT": build_regexp_extract(exp.RegexpExtract),
|
||||||
|
"REGEXP_EXTRACT_ALL": build_regexp_extract(exp.RegexpExtractAll),
|
||||||
"REGEXP_MATCHES": exp.RegexpLike.from_arg_list,
|
"REGEXP_MATCHES": exp.RegexpLike.from_arg_list,
|
||||||
"REGEXP_REPLACE": lambda args: exp.RegexpReplace(
|
"REGEXP_REPLACE": lambda args: exp.RegexpReplace(
|
||||||
this=seq_get(args, 0),
|
this=seq_get(args, 0),
|
||||||
|
@ -397,6 +412,7 @@ class DuckDB(Dialect):
|
||||||
"XOR": binary_from_function(exp.BitwiseXor),
|
"XOR": binary_from_function(exp.BitwiseXor),
|
||||||
"GENERATE_SERIES": _build_generate_series(),
|
"GENERATE_SERIES": _build_generate_series(),
|
||||||
"RANGE": _build_generate_series(end_exclusive=True),
|
"RANGE": _build_generate_series(end_exclusive=True),
|
||||||
|
"EDITDIST3": exp.Levenshtein.from_arg_list,
|
||||||
}
|
}
|
||||||
|
|
||||||
FUNCTIONS.pop("DATE_SUB")
|
FUNCTIONS.pop("DATE_SUB")
|
||||||
|
@ -491,13 +507,13 @@ class DuckDB(Dialect):
|
||||||
STAR_EXCEPT = "EXCLUDE"
|
STAR_EXCEPT = "EXCLUDE"
|
||||||
PAD_FILL_PATTERN_IS_REQUIRED = True
|
PAD_FILL_PATTERN_IS_REQUIRED = True
|
||||||
ARRAY_CONCAT_IS_VAR_LEN = False
|
ARRAY_CONCAT_IS_VAR_LEN = False
|
||||||
|
ARRAY_SIZE_DIM_REQUIRED = False
|
||||||
|
|
||||||
TRANSFORMS = {
|
TRANSFORMS = {
|
||||||
**generator.Generator.TRANSFORMS,
|
**generator.Generator.TRANSFORMS,
|
||||||
exp.ApproxDistinct: approx_count_distinct_sql,
|
exp.ApproxDistinct: approx_count_distinct_sql,
|
||||||
exp.Array: inline_array_unless_query,
|
exp.Array: inline_array_unless_query,
|
||||||
exp.ArrayFilter: rename_func("LIST_FILTER"),
|
exp.ArrayFilter: rename_func("LIST_FILTER"),
|
||||||
exp.ArraySize: rename_func("ARRAY_LENGTH"),
|
|
||||||
exp.ArgMax: arg_max_or_min_no_count("ARG_MAX"),
|
exp.ArgMax: arg_max_or_min_no_count("ARG_MAX"),
|
||||||
exp.ArgMin: arg_max_or_min_no_count("ARG_MIN"),
|
exp.ArgMin: arg_max_or_min_no_count("ARG_MIN"),
|
||||||
exp.ArraySort: _array_sort_sql,
|
exp.ArraySort: _array_sort_sql,
|
||||||
|
@ -535,8 +551,10 @@ class DuckDB(Dialect):
|
||||||
exp.IsNan: rename_func("ISNAN"),
|
exp.IsNan: rename_func("ISNAN"),
|
||||||
exp.JSONBExists: rename_func("JSON_EXISTS"),
|
exp.JSONBExists: rename_func("JSON_EXISTS"),
|
||||||
exp.JSONExtract: _arrow_json_extract_sql,
|
exp.JSONExtract: _arrow_json_extract_sql,
|
||||||
|
exp.JSONExtractArray: _json_extract_value_array_sql,
|
||||||
exp.JSONExtractScalar: _arrow_json_extract_sql,
|
exp.JSONExtractScalar: _arrow_json_extract_sql,
|
||||||
exp.JSONFormat: _json_format_sql,
|
exp.JSONFormat: _json_format_sql,
|
||||||
|
exp.JSONValueArray: _json_extract_value_array_sql,
|
||||||
exp.Lateral: explode_to_unnest_sql,
|
exp.Lateral: explode_to_unnest_sql,
|
||||||
exp.LogicalOr: rename_func("BOOL_OR"),
|
exp.LogicalOr: rename_func("BOOL_OR"),
|
||||||
exp.LogicalAnd: rename_func("BOOL_AND"),
|
exp.LogicalAnd: rename_func("BOOL_AND"),
|
||||||
|
@ -610,6 +628,9 @@ class DuckDB(Dialect):
|
||||||
exp.VariancePop: rename_func("VAR_POP"),
|
exp.VariancePop: rename_func("VAR_POP"),
|
||||||
exp.WeekOfYear: rename_func("WEEKOFYEAR"),
|
exp.WeekOfYear: rename_func("WEEKOFYEAR"),
|
||||||
exp.Xor: bool_xor_sql,
|
exp.Xor: bool_xor_sql,
|
||||||
|
exp.Levenshtein: unsupported_args("ins_cost", "del_cost", "sub_cost", "max_dist")(
|
||||||
|
rename_func("LEVENSHTEIN")
|
||||||
|
),
|
||||||
}
|
}
|
||||||
|
|
||||||
SUPPORTED_JSON_PATH_PARTS = {
|
SUPPORTED_JSON_PATH_PARTS = {
|
||||||
|
|
|
@ -44,6 +44,7 @@ from sqlglot.transforms import (
|
||||||
)
|
)
|
||||||
from sqlglot.helper import seq_get
|
from sqlglot.helper import seq_get
|
||||||
from sqlglot.tokens import TokenType
|
from sqlglot.tokens import TokenType
|
||||||
|
from sqlglot.generator import unsupported_args
|
||||||
|
|
||||||
# (FuncType, Multiplier)
|
# (FuncType, Multiplier)
|
||||||
DATE_DELTA_INTERVAL = {
|
DATE_DELTA_INTERVAL = {
|
||||||
|
@ -309,7 +310,8 @@ class Hive(Dialect):
|
||||||
"MONTH": lambda args: exp.Month(this=exp.TsOrDsToDate.from_arg_list(args)),
|
"MONTH": lambda args: exp.Month(this=exp.TsOrDsToDate.from_arg_list(args)),
|
||||||
"PERCENTILE": exp.Quantile.from_arg_list,
|
"PERCENTILE": exp.Quantile.from_arg_list,
|
||||||
"PERCENTILE_APPROX": exp.ApproxQuantile.from_arg_list,
|
"PERCENTILE_APPROX": exp.ApproxQuantile.from_arg_list,
|
||||||
"REGEXP_EXTRACT": build_regexp_extract,
|
"REGEXP_EXTRACT": build_regexp_extract(exp.RegexpExtract),
|
||||||
|
"REGEXP_EXTRACT_ALL": build_regexp_extract(exp.RegexpExtractAll),
|
||||||
"SEQUENCE": exp.GenerateSeries.from_arg_list,
|
"SEQUENCE": exp.GenerateSeries.from_arg_list,
|
||||||
"SIZE": exp.ArraySize.from_arg_list,
|
"SIZE": exp.ArraySize.from_arg_list,
|
||||||
"SPLIT": exp.RegexpSplit.from_arg_list,
|
"SPLIT": exp.RegexpSplit.from_arg_list,
|
||||||
|
@ -461,6 +463,7 @@ class Hive(Dialect):
|
||||||
PARSE_JSON_NAME = None
|
PARSE_JSON_NAME = None
|
||||||
PAD_FILL_PATTERN_IS_REQUIRED = True
|
PAD_FILL_PATTERN_IS_REQUIRED = True
|
||||||
SUPPORTS_MEDIAN = False
|
SUPPORTS_MEDIAN = False
|
||||||
|
ARRAY_SIZE_NAME = "SIZE"
|
||||||
|
|
||||||
EXPRESSIONS_WITHOUT_NESTED_CTES = {
|
EXPRESSIONS_WITHOUT_NESTED_CTES = {
|
||||||
exp.Insert,
|
exp.Insert,
|
||||||
|
@ -498,7 +501,6 @@ class Hive(Dialect):
|
||||||
exp.ArgMin: arg_max_or_min_no_count("MIN_BY"),
|
exp.ArgMin: arg_max_or_min_no_count("MIN_BY"),
|
||||||
exp.ArrayConcat: rename_func("CONCAT"),
|
exp.ArrayConcat: rename_func("CONCAT"),
|
||||||
exp.ArrayToString: lambda self, e: self.func("CONCAT_WS", e.expression, e.this),
|
exp.ArrayToString: lambda self, e: self.func("CONCAT_WS", e.expression, e.this),
|
||||||
exp.ArraySize: rename_func("SIZE"),
|
|
||||||
exp.ArraySort: _array_sort_sql,
|
exp.ArraySort: _array_sort_sql,
|
||||||
exp.With: no_recursive_cte_sql,
|
exp.With: no_recursive_cte_sql,
|
||||||
exp.DateAdd: _add_date_sql,
|
exp.DateAdd: _add_date_sql,
|
||||||
|
@ -542,6 +544,7 @@ class Hive(Dialect):
|
||||||
exp.Quantile: rename_func("PERCENTILE"),
|
exp.Quantile: rename_func("PERCENTILE"),
|
||||||
exp.ApproxQuantile: rename_func("PERCENTILE_APPROX"),
|
exp.ApproxQuantile: rename_func("PERCENTILE_APPROX"),
|
||||||
exp.RegexpExtract: regexp_extract_sql,
|
exp.RegexpExtract: regexp_extract_sql,
|
||||||
|
exp.RegexpExtractAll: regexp_extract_sql,
|
||||||
exp.RegexpReplace: regexp_replace_sql,
|
exp.RegexpReplace: regexp_replace_sql,
|
||||||
exp.RegexpLike: lambda self, e: self.binary(e, "RLIKE"),
|
exp.RegexpLike: lambda self, e: self.binary(e, "RLIKE"),
|
||||||
exp.RegexpSplit: rename_func("SPLIT"),
|
exp.RegexpSplit: rename_func("SPLIT"),
|
||||||
|
@ -598,6 +601,9 @@ class Hive(Dialect):
|
||||||
exp.WeekOfYear: rename_func("WEEKOFYEAR"),
|
exp.WeekOfYear: rename_func("WEEKOFYEAR"),
|
||||||
exp.DayOfMonth: rename_func("DAYOFMONTH"),
|
exp.DayOfMonth: rename_func("DAYOFMONTH"),
|
||||||
exp.DayOfWeek: rename_func("DAYOFWEEK"),
|
exp.DayOfWeek: rename_func("DAYOFWEEK"),
|
||||||
|
exp.Levenshtein: unsupported_args("ins_cost", "del_cost", "sub_cost", "max_dist")(
|
||||||
|
rename_func("LEVENSHTEIN")
|
||||||
|
),
|
||||||
}
|
}
|
||||||
|
|
||||||
PROPERTIES_LOCATION = {
|
PROPERTIES_LOCATION = {
|
||||||
|
|
|
@ -237,6 +237,29 @@ def _unix_to_time_sql(self: Postgres.Generator, expression: exp.UnixToTime) -> s
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def _build_levenshtein_less_equal(args: t.List) -> exp.Levenshtein:
|
||||||
|
# Postgres has two signatures for levenshtein_less_equal function, but in both cases
|
||||||
|
# max_dist is the last argument
|
||||||
|
# levenshtein_less_equal(source, target, ins_cost, del_cost, sub_cost, max_d)
|
||||||
|
# levenshtein_less_equal(source, target, max_d)
|
||||||
|
max_dist = args.pop()
|
||||||
|
|
||||||
|
return exp.Levenshtein(
|
||||||
|
this=seq_get(args, 0),
|
||||||
|
expression=seq_get(args, 1),
|
||||||
|
ins_cost=seq_get(args, 2),
|
||||||
|
del_cost=seq_get(args, 3),
|
||||||
|
sub_cost=seq_get(args, 4),
|
||||||
|
max_dist=max_dist,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def _levenshtein_sql(self: Postgres.Generator, expression: exp.Levenshtein) -> str:
|
||||||
|
name = "LEVENSHTEIN_LESS_EQUAL" if expression.args.get("max_dist") else "LEVENSHTEIN"
|
||||||
|
|
||||||
|
return rename_func(name)(self, expression)
|
||||||
|
|
||||||
|
|
||||||
class Postgres(Dialect):
|
class Postgres(Dialect):
|
||||||
INDEX_OFFSET = 1
|
INDEX_OFFSET = 1
|
||||||
TYPED_DIVISION = True
|
TYPED_DIVISION = True
|
||||||
|
@ -365,6 +388,7 @@ class Postgres(Dialect):
|
||||||
"SHA256": lambda args: exp.SHA2(this=seq_get(args, 0), length=exp.Literal.number(256)),
|
"SHA256": lambda args: exp.SHA2(this=seq_get(args, 0), length=exp.Literal.number(256)),
|
||||||
"SHA384": lambda args: exp.SHA2(this=seq_get(args, 0), length=exp.Literal.number(384)),
|
"SHA384": lambda args: exp.SHA2(this=seq_get(args, 0), length=exp.Literal.number(384)),
|
||||||
"SHA512": lambda args: exp.SHA2(this=seq_get(args, 0), length=exp.Literal.number(512)),
|
"SHA512": lambda args: exp.SHA2(this=seq_get(args, 0), length=exp.Literal.number(512)),
|
||||||
|
"LEVENSHTEIN_LESS_EQUAL": _build_levenshtein_less_equal,
|
||||||
}
|
}
|
||||||
|
|
||||||
FUNCTION_PARSERS = {
|
FUNCTION_PARSERS = {
|
||||||
|
@ -472,6 +496,7 @@ class Postgres(Dialect):
|
||||||
COPY_HAS_INTO_KEYWORD = False
|
COPY_HAS_INTO_KEYWORD = False
|
||||||
ARRAY_CONCAT_IS_VAR_LEN = False
|
ARRAY_CONCAT_IS_VAR_LEN = False
|
||||||
SUPPORTS_MEDIAN = False
|
SUPPORTS_MEDIAN = False
|
||||||
|
ARRAY_SIZE_DIM_REQUIRED = True
|
||||||
|
|
||||||
SUPPORTED_JSON_PATH_PARTS = {
|
SUPPORTED_JSON_PATH_PARTS = {
|
||||||
exp.JSONPathKey,
|
exp.JSONPathKey,
|
||||||
|
@ -495,7 +520,6 @@ class Postgres(Dialect):
|
||||||
exp.AnyValue: any_value_to_max_sql,
|
exp.AnyValue: any_value_to_max_sql,
|
||||||
exp.ArrayConcat: lambda self, e: self.arrayconcat_sql(e, name="ARRAY_CAT"),
|
exp.ArrayConcat: lambda self, e: self.arrayconcat_sql(e, name="ARRAY_CAT"),
|
||||||
exp.ArrayFilter: filter_array_using_unnest,
|
exp.ArrayFilter: filter_array_using_unnest,
|
||||||
exp.ArraySize: lambda self, e: self.func("ARRAY_LENGTH", e.this, e.expression or "1"),
|
|
||||||
exp.BitwiseXor: lambda self, e: self.binary(e, "#"),
|
exp.BitwiseXor: lambda self, e: self.binary(e, "#"),
|
||||||
exp.ColumnDef: transforms.preprocess([_auto_increment_to_serial, _serial_to_generated]),
|
exp.ColumnDef: transforms.preprocess([_auto_increment_to_serial, _serial_to_generated]),
|
||||||
exp.CurrentDate: no_paren_current_date_sql,
|
exp.CurrentDate: no_paren_current_date_sql,
|
||||||
|
@ -568,6 +592,7 @@ class Postgres(Dialect):
|
||||||
exp.Variance: rename_func("VAR_SAMP"),
|
exp.Variance: rename_func("VAR_SAMP"),
|
||||||
exp.Xor: bool_xor_sql,
|
exp.Xor: bool_xor_sql,
|
||||||
exp.UnixToTime: _unix_to_time_sql,
|
exp.UnixToTime: _unix_to_time_sql,
|
||||||
|
exp.Levenshtein: _levenshtein_sql,
|
||||||
}
|
}
|
||||||
TRANSFORMS.pop(exp.CommentColumnConstraint)
|
TRANSFORMS.pop(exp.CommentColumnConstraint)
|
||||||
|
|
||||||
|
|
|
@ -37,6 +37,7 @@ from sqlglot.dialects.mysql import MySQL
|
||||||
from sqlglot.helper import apply_index_offset, seq_get
|
from sqlglot.helper import apply_index_offset, seq_get
|
||||||
from sqlglot.tokens import TokenType
|
from sqlglot.tokens import TokenType
|
||||||
from sqlglot.transforms import unqualify_columns
|
from sqlglot.transforms import unqualify_columns
|
||||||
|
from sqlglot.generator import unsupported_args
|
||||||
|
|
||||||
DATE_ADD_OR_SUB = t.Union[exp.DateAdd, exp.TimestampAdd, exp.DateSub]
|
DATE_ADD_OR_SUB = t.Union[exp.DateAdd, exp.TimestampAdd, exp.DateSub]
|
||||||
|
|
||||||
|
@ -272,8 +273,10 @@ class Presto(Dialect):
|
||||||
"FROM_UTF8": lambda args: exp.Decode(
|
"FROM_UTF8": lambda args: exp.Decode(
|
||||||
this=seq_get(args, 0), replace=seq_get(args, 1), charset=exp.Literal.string("utf-8")
|
this=seq_get(args, 0), replace=seq_get(args, 1), charset=exp.Literal.string("utf-8")
|
||||||
),
|
),
|
||||||
|
"LEVENSHTEIN_DISTANCE": exp.Levenshtein.from_arg_list,
|
||||||
"NOW": exp.CurrentTimestamp.from_arg_list,
|
"NOW": exp.CurrentTimestamp.from_arg_list,
|
||||||
"REGEXP_EXTRACT": build_regexp_extract,
|
"REGEXP_EXTRACT": build_regexp_extract(exp.RegexpExtract),
|
||||||
|
"REGEXP_EXTRACT_ALL": build_regexp_extract(exp.RegexpExtractAll),
|
||||||
"REGEXP_REPLACE": lambda args: exp.RegexpReplace(
|
"REGEXP_REPLACE": lambda args: exp.RegexpReplace(
|
||||||
this=seq_get(args, 0),
|
this=seq_get(args, 0),
|
||||||
expression=seq_get(args, 1),
|
expression=seq_get(args, 1),
|
||||||
|
@ -318,6 +321,7 @@ class Presto(Dialect):
|
||||||
PAD_FILL_PATTERN_IS_REQUIRED = True
|
PAD_FILL_PATTERN_IS_REQUIRED = True
|
||||||
EXCEPT_INTERSECT_SUPPORT_ALL_CLAUSE = False
|
EXCEPT_INTERSECT_SUPPORT_ALL_CLAUSE = False
|
||||||
SUPPORTS_MEDIAN = False
|
SUPPORTS_MEDIAN = False
|
||||||
|
ARRAY_SIZE_NAME = "CARDINALITY"
|
||||||
|
|
||||||
PROPERTIES_LOCATION = {
|
PROPERTIES_LOCATION = {
|
||||||
**generator.Generator.PROPERTIES_LOCATION,
|
**generator.Generator.PROPERTIES_LOCATION,
|
||||||
|
@ -351,7 +355,6 @@ class Presto(Dialect):
|
||||||
exp.ArrayAny: rename_func("ANY_MATCH"),
|
exp.ArrayAny: rename_func("ANY_MATCH"),
|
||||||
exp.ArrayConcat: rename_func("CONCAT"),
|
exp.ArrayConcat: rename_func("CONCAT"),
|
||||||
exp.ArrayContains: rename_func("CONTAINS"),
|
exp.ArrayContains: rename_func("CONTAINS"),
|
||||||
exp.ArraySize: rename_func("CARDINALITY"),
|
|
||||||
exp.ArrayToString: rename_func("ARRAY_JOIN"),
|
exp.ArrayToString: rename_func("ARRAY_JOIN"),
|
||||||
exp.ArrayUniqueAgg: rename_func("SET_AGG"),
|
exp.ArrayUniqueAgg: rename_func("SET_AGG"),
|
||||||
exp.AtTimeZone: rename_func("AT_TIMEZONE"),
|
exp.AtTimeZone: rename_func("AT_TIMEZONE"),
|
||||||
|
@ -399,12 +402,15 @@ class Presto(Dialect):
|
||||||
exp.LastDay: lambda self, e: self.func("LAST_DAY_OF_MONTH", e.this),
|
exp.LastDay: lambda self, e: self.func("LAST_DAY_OF_MONTH", e.this),
|
||||||
exp.Lateral: explode_to_unnest_sql,
|
exp.Lateral: explode_to_unnest_sql,
|
||||||
exp.Left: left_to_substring_sql,
|
exp.Left: left_to_substring_sql,
|
||||||
exp.Levenshtein: rename_func("LEVENSHTEIN_DISTANCE"),
|
exp.Levenshtein: unsupported_args("ins_cost", "del_cost", "sub_cost", "max_dist")(
|
||||||
|
rename_func("LEVENSHTEIN_DISTANCE")
|
||||||
|
),
|
||||||
exp.LogicalAnd: rename_func("BOOL_AND"),
|
exp.LogicalAnd: rename_func("BOOL_AND"),
|
||||||
exp.LogicalOr: rename_func("BOOL_OR"),
|
exp.LogicalOr: rename_func("BOOL_OR"),
|
||||||
exp.Pivot: no_pivot_sql,
|
exp.Pivot: no_pivot_sql,
|
||||||
exp.Quantile: _quantile_sql,
|
exp.Quantile: _quantile_sql,
|
||||||
exp.RegexpExtract: regexp_extract_sql,
|
exp.RegexpExtract: regexp_extract_sql,
|
||||||
|
exp.RegexpExtractAll: regexp_extract_sql,
|
||||||
exp.Right: right_to_substring_sql,
|
exp.Right: right_to_substring_sql,
|
||||||
exp.SafeDivide: no_safe_divide_sql,
|
exp.SafeDivide: no_safe_divide_sql,
|
||||||
exp.Schema: _schema_sql,
|
exp.Schema: _schema_sql,
|
||||||
|
|
|
@ -235,6 +235,61 @@ def _unnest_generate_date_array(expression: exp.Expression) -> exp.Expression:
|
||||||
return expression
|
return expression
|
||||||
|
|
||||||
|
|
||||||
|
def _build_regexp_extract(expr_type: t.Type[E]) -> t.Callable[[t.List], E]:
|
||||||
|
def _builder(args: t.List) -> E:
|
||||||
|
return expr_type(
|
||||||
|
this=seq_get(args, 0),
|
||||||
|
expression=seq_get(args, 1),
|
||||||
|
position=seq_get(args, 2),
|
||||||
|
occurrence=seq_get(args, 3),
|
||||||
|
parameters=seq_get(args, 4),
|
||||||
|
group=seq_get(args, 5) or exp.Literal.number(0),
|
||||||
|
)
|
||||||
|
|
||||||
|
return _builder
|
||||||
|
|
||||||
|
|
||||||
|
def _regexpextract_sql(self, expression: exp.RegexpExtract | exp.RegexpExtractAll) -> str:
|
||||||
|
# Other dialects don't support all of the following parameters, so we need to
|
||||||
|
# generate default values as necessary to ensure the transpilation is correct
|
||||||
|
group = expression.args.get("group")
|
||||||
|
|
||||||
|
# To avoid generating all these default values, we set group to None if
|
||||||
|
# it's 0 (also default value) which doesn't trigger the following chain
|
||||||
|
if group and group.name == "0":
|
||||||
|
group = None
|
||||||
|
|
||||||
|
parameters = expression.args.get("parameters") or (group and exp.Literal.string("c"))
|
||||||
|
occurrence = expression.args.get("occurrence") or (parameters and exp.Literal.number(1))
|
||||||
|
position = expression.args.get("position") or (occurrence and exp.Literal.number(1))
|
||||||
|
|
||||||
|
return self.func(
|
||||||
|
"REGEXP_SUBSTR" if isinstance(expression, exp.RegexpExtract) else "REGEXP_EXTRACT_ALL",
|
||||||
|
expression.this,
|
||||||
|
expression.expression,
|
||||||
|
position,
|
||||||
|
occurrence,
|
||||||
|
parameters,
|
||||||
|
group,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def _json_extract_value_array_sql(
|
||||||
|
self: Snowflake.Generator, expression: exp.JSONValueArray | exp.JSONExtractArray
|
||||||
|
) -> str:
|
||||||
|
json_extract = exp.JSONExtract(this=expression.this, expression=expression.expression)
|
||||||
|
ident = exp.to_identifier("x")
|
||||||
|
|
||||||
|
if isinstance(expression, exp.JSONValueArray):
|
||||||
|
this: exp.Expression = exp.cast(ident, to=exp.DataType.Type.VARCHAR)
|
||||||
|
else:
|
||||||
|
this = exp.ParseJSON(this=f"TO_JSON({ident})")
|
||||||
|
|
||||||
|
transform_lambda = exp.Lambda(expressions=[ident], this=this)
|
||||||
|
|
||||||
|
return self.func("TRANSFORM", json_extract, transform_lambda)
|
||||||
|
|
||||||
|
|
||||||
class Snowflake(Dialect):
|
class Snowflake(Dialect):
|
||||||
# https://docs.snowflake.com/en/sql-reference/identifiers-syntax
|
# https://docs.snowflake.com/en/sql-reference/identifiers-syntax
|
||||||
NORMALIZATION_STRATEGY = NormalizationStrategy.UPPERCASE
|
NORMALIZATION_STRATEGY = NormalizationStrategy.UPPERCASE
|
||||||
|
@ -322,6 +377,9 @@ class Snowflake(Dialect):
|
||||||
"DATEADD": _build_date_time_add(exp.DateAdd),
|
"DATEADD": _build_date_time_add(exp.DateAdd),
|
||||||
"DATEDIFF": _build_datediff,
|
"DATEDIFF": _build_datediff,
|
||||||
"DIV0": _build_if_from_div0,
|
"DIV0": _build_if_from_div0,
|
||||||
|
"EDITDISTANCE": lambda args: exp.Levenshtein(
|
||||||
|
this=seq_get(args, 0), expression=seq_get(args, 1), max_dist=seq_get(args, 2)
|
||||||
|
),
|
||||||
"FLATTEN": exp.Explode.from_arg_list,
|
"FLATTEN": exp.Explode.from_arg_list,
|
||||||
"GET_PATH": lambda args, dialect: exp.JSONExtract(
|
"GET_PATH": lambda args, dialect: exp.JSONExtract(
|
||||||
this=seq_get(args, 0), expression=dialect.to_json_path(seq_get(args, 1))
|
this=seq_get(args, 0), expression=dialect.to_json_path(seq_get(args, 1))
|
||||||
|
@ -335,15 +393,10 @@ class Snowflake(Dialect):
|
||||||
"LISTAGG": exp.GroupConcat.from_arg_list,
|
"LISTAGG": exp.GroupConcat.from_arg_list,
|
||||||
"NULLIFZERO": _build_if_from_nullifzero,
|
"NULLIFZERO": _build_if_from_nullifzero,
|
||||||
"OBJECT_CONSTRUCT": _build_object_construct,
|
"OBJECT_CONSTRUCT": _build_object_construct,
|
||||||
|
"REGEXP_EXTRACT_ALL": _build_regexp_extract(exp.RegexpExtractAll),
|
||||||
"REGEXP_REPLACE": _build_regexp_replace,
|
"REGEXP_REPLACE": _build_regexp_replace,
|
||||||
"REGEXP_SUBSTR": lambda args: exp.RegexpExtract(
|
"REGEXP_SUBSTR": _build_regexp_extract(exp.RegexpExtract),
|
||||||
this=seq_get(args, 0),
|
"REGEXP_SUBSTR_ALL": _build_regexp_extract(exp.RegexpExtractAll),
|
||||||
expression=seq_get(args, 1),
|
|
||||||
position=seq_get(args, 2),
|
|
||||||
occurrence=seq_get(args, 3),
|
|
||||||
parameters=seq_get(args, 4),
|
|
||||||
group=seq_get(args, 5) or exp.Literal.number(0),
|
|
||||||
),
|
|
||||||
"RLIKE": exp.RegexpLike.from_arg_list,
|
"RLIKE": exp.RegexpLike.from_arg_list,
|
||||||
"SQUARE": lambda args: exp.Pow(this=seq_get(args, 0), expression=exp.Literal.number(2)),
|
"SQUARE": lambda args: exp.Pow(this=seq_get(args, 0), expression=exp.Literal.number(2)),
|
||||||
"TIMEADD": _build_date_time_add(exp.TimeAdd),
|
"TIMEADD": _build_date_time_add(exp.TimeAdd),
|
||||||
|
@ -770,6 +823,7 @@ class Snowflake(Dialect):
|
||||||
SUPPORTS_CONVERT_TIMEZONE = True
|
SUPPORTS_CONVERT_TIMEZONE = True
|
||||||
EXCEPT_INTERSECT_SUPPORT_ALL_CLAUSE = False
|
EXCEPT_INTERSECT_SUPPORT_ALL_CLAUSE = False
|
||||||
SUPPORTS_MEDIAN = True
|
SUPPORTS_MEDIAN = True
|
||||||
|
ARRAY_SIZE_NAME = "ARRAY_SIZE"
|
||||||
|
|
||||||
TRANSFORMS = {
|
TRANSFORMS = {
|
||||||
**generator.Generator.TRANSFORMS,
|
**generator.Generator.TRANSFORMS,
|
||||||
|
@ -802,11 +856,13 @@ class Snowflake(Dialect):
|
||||||
),
|
),
|
||||||
exp.GroupConcat: rename_func("LISTAGG"),
|
exp.GroupConcat: rename_func("LISTAGG"),
|
||||||
exp.If: if_sql(name="IFF", false_value="NULL"),
|
exp.If: if_sql(name="IFF", false_value="NULL"),
|
||||||
|
exp.JSONExtractArray: _json_extract_value_array_sql,
|
||||||
exp.JSONExtractScalar: lambda self, e: self.func(
|
exp.JSONExtractScalar: lambda self, e: self.func(
|
||||||
"JSON_EXTRACT_PATH_TEXT", e.this, e.expression
|
"JSON_EXTRACT_PATH_TEXT", e.this, e.expression
|
||||||
),
|
),
|
||||||
exp.JSONObject: lambda self, e: self.func("OBJECT_CONSTRUCT_KEEP_NULL", *e.expressions),
|
exp.JSONObject: lambda self, e: self.func("OBJECT_CONSTRUCT_KEEP_NULL", *e.expressions),
|
||||||
exp.JSONPathRoot: lambda *_: "",
|
exp.JSONPathRoot: lambda *_: "",
|
||||||
|
exp.JSONValueArray: _json_extract_value_array_sql,
|
||||||
exp.LogicalAnd: rename_func("BOOLAND_AGG"),
|
exp.LogicalAnd: rename_func("BOOLAND_AGG"),
|
||||||
exp.LogicalOr: rename_func("BOOLOR_AGG"),
|
exp.LogicalOr: rename_func("BOOLOR_AGG"),
|
||||||
exp.Map: lambda self, e: var_map_sql(self, e, "OBJECT_CONSTRUCT"),
|
exp.Map: lambda self, e: var_map_sql(self, e, "OBJECT_CONSTRUCT"),
|
||||||
|
@ -823,6 +879,8 @@ class Snowflake(Dialect):
|
||||||
[transforms.add_within_group_for_percentiles]
|
[transforms.add_within_group_for_percentiles]
|
||||||
),
|
),
|
||||||
exp.Pivot: transforms.preprocess([_unqualify_unpivot_columns]),
|
exp.Pivot: transforms.preprocess([_unqualify_unpivot_columns]),
|
||||||
|
exp.RegexpExtract: _regexpextract_sql,
|
||||||
|
exp.RegexpExtractAll: _regexpextract_sql,
|
||||||
exp.RegexpILike: _regexpilike_sql,
|
exp.RegexpILike: _regexpilike_sql,
|
||||||
exp.Rand: rename_func("RANDOM"),
|
exp.Rand: rename_func("RANDOM"),
|
||||||
exp.Select: transforms.preprocess(
|
exp.Select: transforms.preprocess(
|
||||||
|
@ -867,6 +925,9 @@ class Snowflake(Dialect):
|
||||||
exp.VarMap: lambda self, e: var_map_sql(self, e, "OBJECT_CONSTRUCT"),
|
exp.VarMap: lambda self, e: var_map_sql(self, e, "OBJECT_CONSTRUCT"),
|
||||||
exp.WeekOfYear: rename_func("WEEKOFYEAR"),
|
exp.WeekOfYear: rename_func("WEEKOFYEAR"),
|
||||||
exp.Xor: rename_func("BOOLXOR"),
|
exp.Xor: rename_func("BOOLXOR"),
|
||||||
|
exp.Levenshtein: unsupported_args("ins_cost", "del_cost", "sub_cost")(
|
||||||
|
rename_func("EDITDISTANCE")
|
||||||
|
),
|
||||||
}
|
}
|
||||||
|
|
||||||
SUPPORTED_JSON_PATH_PARTS = {
|
SUPPORTED_JSON_PATH_PARTS = {
|
||||||
|
@ -1009,30 +1070,6 @@ class Snowflake(Dialect):
|
||||||
|
|
||||||
return f"SHOW {terse}{expression.name}{history}{like}{scope_kind}{scope}{starts_with}{limit}{from_}"
|
return f"SHOW {terse}{expression.name}{history}{like}{scope_kind}{scope}{starts_with}{limit}{from_}"
|
||||||
|
|
||||||
def regexpextract_sql(self, expression: exp.RegexpExtract) -> str:
|
|
||||||
# Other dialects don't support all of the following parameters, so we need to
|
|
||||||
# generate default values as necessary to ensure the transpilation is correct
|
|
||||||
group = expression.args.get("group")
|
|
||||||
|
|
||||||
# To avoid generating all these default values, we set group to None if
|
|
||||||
# it's 0 (also default value) which doesn't trigger the following chain
|
|
||||||
if group and group.name == "0":
|
|
||||||
group = None
|
|
||||||
|
|
||||||
parameters = expression.args.get("parameters") or (group and exp.Literal.string("c"))
|
|
||||||
occurrence = expression.args.get("occurrence") or (parameters and exp.Literal.number(1))
|
|
||||||
position = expression.args.get("position") or (occurrence and exp.Literal.number(1))
|
|
||||||
|
|
||||||
return self.func(
|
|
||||||
"REGEXP_SUBSTR",
|
|
||||||
expression.this,
|
|
||||||
expression.expression,
|
|
||||||
position,
|
|
||||||
occurrence,
|
|
||||||
parameters,
|
|
||||||
group,
|
|
||||||
)
|
|
||||||
|
|
||||||
def describe_sql(self, expression: exp.Describe) -> str:
|
def describe_sql(self, expression: exp.Describe) -> str:
|
||||||
# Default to table if kind is unknown
|
# Default to table if kind is unknown
|
||||||
kind_value = expression.args.get("kind") or "TABLE"
|
kind_value = expression.args.get("kind") or "TABLE"
|
||||||
|
|
|
@ -3,7 +3,7 @@ from __future__ import annotations
|
||||||
import typing as t
|
import typing as t
|
||||||
|
|
||||||
from sqlglot import exp
|
from sqlglot import exp
|
||||||
from sqlglot.dialects.dialect import rename_func, unit_to_var
|
from sqlglot.dialects.dialect import rename_func, unit_to_var, timestampdiff_sql, build_date_delta
|
||||||
from sqlglot.dialects.hive import _build_with_ignore_nulls
|
from sqlglot.dialects.hive import _build_with_ignore_nulls
|
||||||
from sqlglot.dialects.spark2 import Spark2, temporary_storage_provider, _build_as_cast
|
from sqlglot.dialects.spark2 import Spark2, temporary_storage_provider, _build_as_cast
|
||||||
from sqlglot.helper import ensure_list, seq_get
|
from sqlglot.helper import ensure_list, seq_get
|
||||||
|
@ -108,6 +108,7 @@ class Spark(Spark2):
|
||||||
"DATE_ADD": _build_dateadd,
|
"DATE_ADD": _build_dateadd,
|
||||||
"DATEADD": _build_dateadd,
|
"DATEADD": _build_dateadd,
|
||||||
"TIMESTAMPADD": _build_dateadd,
|
"TIMESTAMPADD": _build_dateadd,
|
||||||
|
"TIMESTAMPDIFF": build_date_delta(exp.TimestampDiff),
|
||||||
"DATEDIFF": _build_datediff,
|
"DATEDIFF": _build_datediff,
|
||||||
"DATE_DIFF": _build_datediff,
|
"DATE_DIFF": _build_datediff,
|
||||||
"TIMESTAMP_LTZ": _build_as_cast("TIMESTAMP_LTZ"),
|
"TIMESTAMP_LTZ": _build_as_cast("TIMESTAMP_LTZ"),
|
||||||
|
@ -137,6 +138,7 @@ class Spark(Spark2):
|
||||||
PAD_FILL_PATTERN_IS_REQUIRED = False
|
PAD_FILL_PATTERN_IS_REQUIRED = False
|
||||||
SUPPORTS_CONVERT_TIMEZONE = True
|
SUPPORTS_CONVERT_TIMEZONE = True
|
||||||
SUPPORTS_MEDIAN = True
|
SUPPORTS_MEDIAN = True
|
||||||
|
SUPPORTS_UNIX_SECONDS = True
|
||||||
|
|
||||||
TYPE_MAPPING = {
|
TYPE_MAPPING = {
|
||||||
**Spark2.Generator.TYPE_MAPPING,
|
**Spark2.Generator.TYPE_MAPPING,
|
||||||
|
@ -166,6 +168,8 @@ class Spark(Spark2):
|
||||||
exp.StartsWith: rename_func("STARTSWITH"),
|
exp.StartsWith: rename_func("STARTSWITH"),
|
||||||
exp.TsOrDsAdd: _dateadd_sql,
|
exp.TsOrDsAdd: _dateadd_sql,
|
||||||
exp.TimestampAdd: _dateadd_sql,
|
exp.TimestampAdd: _dateadd_sql,
|
||||||
|
exp.DatetimeDiff: timestampdiff_sql,
|
||||||
|
exp.TimestampDiff: timestampdiff_sql,
|
||||||
exp.TryCast: lambda self, e: (
|
exp.TryCast: lambda self, e: (
|
||||||
self.trycast_sql(e) if e.args.get("safe") else self.cast_sql(e)
|
self.trycast_sql(e) if e.args.get("safe") else self.cast_sql(e)
|
||||||
),
|
),
|
||||||
|
|
|
@ -18,6 +18,7 @@ from sqlglot.dialects.dialect import (
|
||||||
str_position_sql,
|
str_position_sql,
|
||||||
)
|
)
|
||||||
from sqlglot.tokens import TokenType
|
from sqlglot.tokens import TokenType
|
||||||
|
from sqlglot.generator import unsupported_args
|
||||||
|
|
||||||
|
|
||||||
def _date_add_sql(self: SQLite.Generator, expression: exp.DateAdd) -> str:
|
def _date_add_sql(self: SQLite.Generator, expression: exp.DateAdd) -> str:
|
||||||
|
@ -184,7 +185,9 @@ class SQLite(Dialect):
|
||||||
exp.ILike: no_ilike_sql,
|
exp.ILike: no_ilike_sql,
|
||||||
exp.JSONExtract: _json_extract_sql,
|
exp.JSONExtract: _json_extract_sql,
|
||||||
exp.JSONExtractScalar: arrow_json_extract_sql,
|
exp.JSONExtractScalar: arrow_json_extract_sql,
|
||||||
exp.Levenshtein: rename_func("EDITDIST3"),
|
exp.Levenshtein: unsupported_args("ins_cost", "del_cost", "sub_cost", "max_dist")(
|
||||||
|
rename_func("EDITDIST3")
|
||||||
|
),
|
||||||
exp.LogicalOr: rename_func("MAX"),
|
exp.LogicalOr: rename_func("MAX"),
|
||||||
exp.LogicalAnd: rename_func("MIN"),
|
exp.LogicalAnd: rename_func("MIN"),
|
||||||
exp.Pivot: no_pivot_sql,
|
exp.Pivot: no_pivot_sql,
|
||||||
|
|
|
@ -166,6 +166,7 @@ class Teradata(Dialect):
|
||||||
|
|
||||||
FUNCTIONS = {
|
FUNCTIONS = {
|
||||||
**parser.Parser.FUNCTIONS,
|
**parser.Parser.FUNCTIONS,
|
||||||
|
"CARDINALITY": exp.ArraySize.from_arg_list,
|
||||||
"RANDOM": lambda args: exp.Rand(lower=seq_get(args, 0), upper=seq_get(args, 1)),
|
"RANDOM": lambda args: exp.Rand(lower=seq_get(args, 0), upper=seq_get(args, 1)),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -227,6 +228,7 @@ class Teradata(Dialect):
|
||||||
LAST_DAY_SUPPORTS_DATE_PART = False
|
LAST_DAY_SUPPORTS_DATE_PART = False
|
||||||
CAN_IMPLEMENT_ARRAY_ANY = True
|
CAN_IMPLEMENT_ARRAY_ANY = True
|
||||||
TZ_TO_WITH_TIME_ZONE = True
|
TZ_TO_WITH_TIME_ZONE = True
|
||||||
|
ARRAY_SIZE_NAME = "CARDINALITY"
|
||||||
|
|
||||||
TYPE_MAPPING = {
|
TYPE_MAPPING = {
|
||||||
**generator.Generator.TYPE_MAPPING,
|
**generator.Generator.TYPE_MAPPING,
|
||||||
|
@ -246,7 +248,6 @@ class Teradata(Dialect):
|
||||||
**generator.Generator.TRANSFORMS,
|
**generator.Generator.TRANSFORMS,
|
||||||
exp.ArgMax: rename_func("MAX_BY"),
|
exp.ArgMax: rename_func("MAX_BY"),
|
||||||
exp.ArgMin: rename_func("MIN_BY"),
|
exp.ArgMin: rename_func("MIN_BY"),
|
||||||
exp.ArraySize: rename_func("CARDINALITY"),
|
|
||||||
exp.Max: max_or_greatest,
|
exp.Max: max_or_greatest,
|
||||||
exp.Min: min_or_least,
|
exp.Min: min_or_least,
|
||||||
exp.Pow: lambda self, e: self.binary(e, "**"),
|
exp.Pow: lambda self, e: self.binary(e, "**"),
|
||||||
|
|
|
@ -767,6 +767,7 @@ class Expression(metaclass=_Expression):
|
||||||
*expressions: t.Optional[ExpOrStr],
|
*expressions: t.Optional[ExpOrStr],
|
||||||
dialect: DialectType = None,
|
dialect: DialectType = None,
|
||||||
copy: bool = True,
|
copy: bool = True,
|
||||||
|
wrap: bool = True,
|
||||||
**opts,
|
**opts,
|
||||||
) -> Condition:
|
) -> Condition:
|
||||||
"""
|
"""
|
||||||
|
@ -781,18 +782,22 @@ class Expression(metaclass=_Expression):
|
||||||
If an `Expression` instance is passed, it will be used as-is.
|
If an `Expression` instance is passed, it will be used as-is.
|
||||||
dialect: the dialect used to parse the input expression.
|
dialect: the dialect used to parse the input expression.
|
||||||
copy: whether to copy the involved expressions (only applies to Expressions).
|
copy: whether to copy the involved expressions (only applies to Expressions).
|
||||||
|
wrap: whether to wrap the operands in `Paren`s. This is true by default to avoid
|
||||||
|
precedence issues, but can be turned off when the produced AST is too deep and
|
||||||
|
causes recursion-related issues.
|
||||||
opts: other options to use to parse the input expressions.
|
opts: other options to use to parse the input expressions.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
The new And condition.
|
The new And condition.
|
||||||
"""
|
"""
|
||||||
return and_(self, *expressions, dialect=dialect, copy=copy, **opts)
|
return and_(self, *expressions, dialect=dialect, copy=copy, wrap=wrap, **opts)
|
||||||
|
|
||||||
def or_(
|
def or_(
|
||||||
self,
|
self,
|
||||||
*expressions: t.Optional[ExpOrStr],
|
*expressions: t.Optional[ExpOrStr],
|
||||||
dialect: DialectType = None,
|
dialect: DialectType = None,
|
||||||
copy: bool = True,
|
copy: bool = True,
|
||||||
|
wrap: bool = True,
|
||||||
**opts,
|
**opts,
|
||||||
) -> Condition:
|
) -> Condition:
|
||||||
"""
|
"""
|
||||||
|
@ -807,12 +812,15 @@ class Expression(metaclass=_Expression):
|
||||||
If an `Expression` instance is passed, it will be used as-is.
|
If an `Expression` instance is passed, it will be used as-is.
|
||||||
dialect: the dialect used to parse the input expression.
|
dialect: the dialect used to parse the input expression.
|
||||||
copy: whether to copy the involved expressions (only applies to Expressions).
|
copy: whether to copy the involved expressions (only applies to Expressions).
|
||||||
|
wrap: whether to wrap the operands in `Paren`s. This is true by default to avoid
|
||||||
|
precedence issues, but can be turned off when the produced AST is too deep and
|
||||||
|
causes recursion-related issues.
|
||||||
opts: other options to use to parse the input expressions.
|
opts: other options to use to parse the input expressions.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
The new Or condition.
|
The new Or condition.
|
||||||
"""
|
"""
|
||||||
return or_(self, *expressions, dialect=dialect, copy=copy, **opts)
|
return or_(self, *expressions, dialect=dialect, copy=copy, wrap=wrap, **opts)
|
||||||
|
|
||||||
def not_(self, copy: bool = True):
|
def not_(self, copy: bool = True):
|
||||||
"""
|
"""
|
||||||
|
@ -5939,6 +5947,10 @@ class JSONValue(Expression):
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class JSONValueArray(Func):
|
||||||
|
arg_types = {"this": True, "expression": False}
|
||||||
|
|
||||||
|
|
||||||
# # https://docs.oracle.com/en/database/oracle/oracle-database/19/sqlrf/JSON_TABLE.html
|
# # https://docs.oracle.com/en/database/oracle/oracle-database/19/sqlrf/JSON_TABLE.html
|
||||||
class JSONTable(Func):
|
class JSONTable(Func):
|
||||||
arg_types = {
|
arg_types = {
|
||||||
|
@ -5995,6 +6007,10 @@ class JSONExtract(Binary, Func):
|
||||||
return self.expression.output_name if not self.expressions else ""
|
return self.expression.output_name if not self.expressions else ""
|
||||||
|
|
||||||
|
|
||||||
|
class JSONExtractArray(Func):
|
||||||
|
arg_types = {"this": True, "expression": False}
|
||||||
|
|
||||||
|
|
||||||
class JSONExtractScalar(Binary, Func):
|
class JSONExtractScalar(Binary, Func):
|
||||||
arg_types = {"this": True, "expression": True, "only_json_types": False, "expressions": False}
|
arg_types = {"this": True, "expression": True, "only_json_types": False, "expressions": False}
|
||||||
_sql_names = ["JSON_EXTRACT_SCALAR"]
|
_sql_names = ["JSON_EXTRACT_SCALAR"]
|
||||||
|
@ -6233,11 +6249,22 @@ class Reduce(Func):
|
||||||
class RegexpExtract(Func):
|
class RegexpExtract(Func):
|
||||||
arg_types = {
|
arg_types = {
|
||||||
"this": True,
|
"this": True,
|
||||||
"expression": True, # The pattern
|
"expression": True,
|
||||||
"position": False, # Only start searching the string from this index
|
"position": False,
|
||||||
"occurrence": False, # Skip the first `occurence-1` matches
|
"occurrence": False,
|
||||||
"parameters": False, # Flags, eg "i" for case-insensitive
|
"parameters": False,
|
||||||
"group": False, # Which group to return
|
"group": False,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class RegexpExtractAll(Func):
|
||||||
|
arg_types = {
|
||||||
|
"this": True,
|
||||||
|
"expression": True,
|
||||||
|
"position": False,
|
||||||
|
"occurrence": False,
|
||||||
|
"parameters": False,
|
||||||
|
"group": False,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -6516,6 +6543,10 @@ class UnixToTimeStr(Func):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class UnixSeconds(Func):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
class Uuid(Func):
|
class Uuid(Func):
|
||||||
_sql_names = ["UUID", "GEN_RANDOM_UUID", "GENERATE_UUID", "UUID_STRING"]
|
_sql_names = ["UUID", "GEN_RANDOM_UUID", "GENERATE_UUID", "UUID_STRING"]
|
||||||
|
|
||||||
|
@ -6898,6 +6929,7 @@ def _combine(
|
||||||
operator: t.Type[Connector],
|
operator: t.Type[Connector],
|
||||||
dialect: DialectType = None,
|
dialect: DialectType = None,
|
||||||
copy: bool = True,
|
copy: bool = True,
|
||||||
|
wrap: bool = True,
|
||||||
**opts,
|
**opts,
|
||||||
) -> Expression:
|
) -> Expression:
|
||||||
conditions = [
|
conditions = [
|
||||||
|
@ -6907,10 +6939,10 @@ def _combine(
|
||||||
]
|
]
|
||||||
|
|
||||||
this, *rest = conditions
|
this, *rest = conditions
|
||||||
if rest:
|
if rest and wrap:
|
||||||
this = _wrap(this, Connector)
|
this = _wrap(this, Connector)
|
||||||
for expression in rest:
|
for expression in rest:
|
||||||
this = operator(this=this, expression=_wrap(expression, Connector))
|
this = operator(this=this, expression=_wrap(expression, Connector) if wrap else expression)
|
||||||
|
|
||||||
return this
|
return this
|
||||||
|
|
||||||
|
@ -7293,7 +7325,11 @@ def condition(
|
||||||
|
|
||||||
|
|
||||||
def and_(
|
def and_(
|
||||||
*expressions: t.Optional[ExpOrStr], dialect: DialectType = None, copy: bool = True, **opts
|
*expressions: t.Optional[ExpOrStr],
|
||||||
|
dialect: DialectType = None,
|
||||||
|
copy: bool = True,
|
||||||
|
wrap: bool = True,
|
||||||
|
**opts,
|
||||||
) -> Condition:
|
) -> Condition:
|
||||||
"""
|
"""
|
||||||
Combine multiple conditions with an AND logical operator.
|
Combine multiple conditions with an AND logical operator.
|
||||||
|
@ -7307,16 +7343,23 @@ def and_(
|
||||||
If an Expression instance is passed, this is used as-is.
|
If an Expression instance is passed, this is used as-is.
|
||||||
dialect: the dialect used to parse the input expression.
|
dialect: the dialect used to parse the input expression.
|
||||||
copy: whether to copy `expressions` (only applies to Expressions).
|
copy: whether to copy `expressions` (only applies to Expressions).
|
||||||
|
wrap: whether to wrap the operands in `Paren`s. This is true by default to avoid
|
||||||
|
precedence issues, but can be turned off when the produced AST is too deep and
|
||||||
|
causes recursion-related issues.
|
||||||
**opts: other options to use to parse the input expressions.
|
**opts: other options to use to parse the input expressions.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
The new condition
|
The new condition
|
||||||
"""
|
"""
|
||||||
return t.cast(Condition, _combine(expressions, And, dialect, copy=copy, **opts))
|
return t.cast(Condition, _combine(expressions, And, dialect, copy=copy, wrap=wrap, **opts))
|
||||||
|
|
||||||
|
|
||||||
def or_(
|
def or_(
|
||||||
*expressions: t.Optional[ExpOrStr], dialect: DialectType = None, copy: bool = True, **opts
|
*expressions: t.Optional[ExpOrStr],
|
||||||
|
dialect: DialectType = None,
|
||||||
|
copy: bool = True,
|
||||||
|
wrap: bool = True,
|
||||||
|
**opts,
|
||||||
) -> Condition:
|
) -> Condition:
|
||||||
"""
|
"""
|
||||||
Combine multiple conditions with an OR logical operator.
|
Combine multiple conditions with an OR logical operator.
|
||||||
|
@ -7330,16 +7373,23 @@ def or_(
|
||||||
If an Expression instance is passed, this is used as-is.
|
If an Expression instance is passed, this is used as-is.
|
||||||
dialect: the dialect used to parse the input expression.
|
dialect: the dialect used to parse the input expression.
|
||||||
copy: whether to copy `expressions` (only applies to Expressions).
|
copy: whether to copy `expressions` (only applies to Expressions).
|
||||||
|
wrap: whether to wrap the operands in `Paren`s. This is true by default to avoid
|
||||||
|
precedence issues, but can be turned off when the produced AST is too deep and
|
||||||
|
causes recursion-related issues.
|
||||||
**opts: other options to use to parse the input expressions.
|
**opts: other options to use to parse the input expressions.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
The new condition
|
The new condition
|
||||||
"""
|
"""
|
||||||
return t.cast(Condition, _combine(expressions, Or, dialect, copy=copy, **opts))
|
return t.cast(Condition, _combine(expressions, Or, dialect, copy=copy, wrap=wrap, **opts))
|
||||||
|
|
||||||
|
|
||||||
def xor(
|
def xor(
|
||||||
*expressions: t.Optional[ExpOrStr], dialect: DialectType = None, copy: bool = True, **opts
|
*expressions: t.Optional[ExpOrStr],
|
||||||
|
dialect: DialectType = None,
|
||||||
|
copy: bool = True,
|
||||||
|
wrap: bool = True,
|
||||||
|
**opts,
|
||||||
) -> Condition:
|
) -> Condition:
|
||||||
"""
|
"""
|
||||||
Combine multiple conditions with an XOR logical operator.
|
Combine multiple conditions with an XOR logical operator.
|
||||||
|
@ -7353,12 +7403,15 @@ def xor(
|
||||||
If an Expression instance is passed, this is used as-is.
|
If an Expression instance is passed, this is used as-is.
|
||||||
dialect: the dialect used to parse the input expression.
|
dialect: the dialect used to parse the input expression.
|
||||||
copy: whether to copy `expressions` (only applies to Expressions).
|
copy: whether to copy `expressions` (only applies to Expressions).
|
||||||
|
wrap: whether to wrap the operands in `Paren`s. This is true by default to avoid
|
||||||
|
precedence issues, but can be turned off when the produced AST is too deep and
|
||||||
|
causes recursion-related issues.
|
||||||
**opts: other options to use to parse the input expressions.
|
**opts: other options to use to parse the input expressions.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
The new condition
|
The new condition
|
||||||
"""
|
"""
|
||||||
return t.cast(Condition, _combine(expressions, Xor, dialect, copy=copy, **opts))
|
return t.cast(Condition, _combine(expressions, Xor, dialect, copy=copy, wrap=wrap, **opts))
|
||||||
|
|
||||||
|
|
||||||
def not_(expression: ExpOrStr, dialect: DialectType = None, copy: bool = True, **opts) -> Not:
|
def not_(expression: ExpOrStr, dialect: DialectType = None, copy: bool = True, **opts) -> Not:
|
||||||
|
@ -8052,7 +8105,7 @@ def table_name(table: Table | str, dialect: DialectType = None, identify: bool =
|
||||||
|
|
||||||
return ".".join(
|
return ".".join(
|
||||||
(
|
(
|
||||||
part.sql(dialect=dialect, identify=True, copy=False)
|
part.sql(dialect=dialect, identify=True, copy=False, comments=False)
|
||||||
if identify or not SAFE_IDENTIFIER_RE.match(part.name)
|
if identify or not SAFE_IDENTIFIER_RE.match(part.name)
|
||||||
else part.name
|
else part.name
|
||||||
)
|
)
|
||||||
|
|
|
@ -436,9 +436,21 @@ class Generator(metaclass=_Generator):
|
||||||
# Whether MEDIAN(expr) is supported; if not, it will be generated as PERCENTILE_CONT(expr, 0.5)
|
# Whether MEDIAN(expr) is supported; if not, it will be generated as PERCENTILE_CONT(expr, 0.5)
|
||||||
SUPPORTS_MEDIAN = True
|
SUPPORTS_MEDIAN = True
|
||||||
|
|
||||||
|
# Whether UNIX_SECONDS(timestamp) is supported
|
||||||
|
SUPPORTS_UNIX_SECONDS = False
|
||||||
|
|
||||||
# The name to generate for the JSONPath expression. If `None`, only `this` will be generated
|
# The name to generate for the JSONPath expression. If `None`, only `this` will be generated
|
||||||
PARSE_JSON_NAME: t.Optional[str] = "PARSE_JSON"
|
PARSE_JSON_NAME: t.Optional[str] = "PARSE_JSON"
|
||||||
|
|
||||||
|
# The function name of the exp.ArraySize expression
|
||||||
|
ARRAY_SIZE_NAME: str = "ARRAY_LENGTH"
|
||||||
|
|
||||||
|
# Whether exp.ArraySize should generate the dimension arg too (valid for Postgres & DuckDB)
|
||||||
|
# None -> Doesn't support it at all
|
||||||
|
# False (DuckDB) -> Has backwards-compatible support, but preferably generated without
|
||||||
|
# True (Postgres) -> Explicitly requires it
|
||||||
|
ARRAY_SIZE_DIM_REQUIRED: t.Optional[bool] = None
|
||||||
|
|
||||||
TYPE_MAPPING = {
|
TYPE_MAPPING = {
|
||||||
exp.DataType.Type.NCHAR: "CHAR",
|
exp.DataType.Type.NCHAR: "CHAR",
|
||||||
exp.DataType.Type.NVARCHAR: "VARCHAR",
|
exp.DataType.Type.NVARCHAR: "VARCHAR",
|
||||||
|
@ -4472,3 +4484,30 @@ class Generator(metaclass=_Generator):
|
||||||
filler = f" {filler}" if filler else ""
|
filler = f" {filler}" if filler else ""
|
||||||
with_count = "WITH COUNT" if expression.args.get("with_count") else "WITHOUT COUNT"
|
with_count = "WITH COUNT" if expression.args.get("with_count") else "WITHOUT COUNT"
|
||||||
return f"TRUNCATE{filler} {with_count}"
|
return f"TRUNCATE{filler} {with_count}"
|
||||||
|
|
||||||
|
def unixseconds_sql(self, expression: exp.UnixSeconds) -> str:
|
||||||
|
if self.SUPPORTS_UNIX_SECONDS:
|
||||||
|
return self.function_fallback_sql(expression)
|
||||||
|
|
||||||
|
start_ts = exp.cast(
|
||||||
|
exp.Literal.string("1970-01-01 00:00:00+00"), to=exp.DataType.Type.TIMESTAMPTZ
|
||||||
|
)
|
||||||
|
|
||||||
|
return self.sql(
|
||||||
|
exp.TimestampDiff(this=expression.this, expression=start_ts, unit=exp.var("SECONDS"))
|
||||||
|
)
|
||||||
|
|
||||||
|
def arraysize_sql(self, expression: exp.ArraySize) -> str:
|
||||||
|
dim = expression.expression
|
||||||
|
|
||||||
|
# For dialects that don't support the dimension arg, we can safely transpile it's default value (1st dimension)
|
||||||
|
if dim and self.ARRAY_SIZE_DIM_REQUIRED is None:
|
||||||
|
if not (dim.is_int and dim.name == "1"):
|
||||||
|
self.unsupported("Cannot transpile dimension argument for ARRAY_LENGTH")
|
||||||
|
dim = None
|
||||||
|
|
||||||
|
# If dimension is required but not specified, default initialize it
|
||||||
|
if self.ARRAY_SIZE_DIM_REQUIRED and not dim:
|
||||||
|
dim = exp.Literal.number(1)
|
||||||
|
|
||||||
|
return self.func(self.ARRAY_SIZE_NAME, expression.this, dim)
|
||||||
|
|
|
@ -5138,6 +5138,9 @@ class Parser(metaclass=_Parser):
|
||||||
else:
|
else:
|
||||||
this = self.expression(exp.Dot, this=this, expression=field)
|
this = self.expression(exp.Dot, this=this, expression=field)
|
||||||
|
|
||||||
|
if field and field.comments:
|
||||||
|
t.cast(exp.Expression, this).add_comments(field.pop_comments())
|
||||||
|
|
||||||
this = self._parse_bracket(this)
|
this = self._parse_bracket(this)
|
||||||
|
|
||||||
return self._parse_colon_as_variant_extract(this) if self.COLON_IS_VARIANT_EXTRACT else this
|
return self._parse_colon_as_variant_extract(this) if self.COLON_IS_VARIANT_EXTRACT else this
|
||||||
|
|
|
@ -348,7 +348,8 @@ def unnest_to_explode(
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
for join in expression.args.get("joins") or []:
|
joins = expression.args.get("joins") or []
|
||||||
|
for join in list(joins):
|
||||||
join_expr = join.this
|
join_expr = join.this
|
||||||
|
|
||||||
is_lateral = isinstance(join_expr, exp.Lateral)
|
is_lateral = isinstance(join_expr, exp.Lateral)
|
||||||
|
@ -365,9 +366,19 @@ def unnest_to_explode(
|
||||||
has_multi_expr = len(exprs) > 1
|
has_multi_expr = len(exprs) > 1
|
||||||
exprs = _unnest_zip_exprs(unnest, exprs, has_multi_expr)
|
exprs = _unnest_zip_exprs(unnest, exprs, has_multi_expr)
|
||||||
|
|
||||||
expression.args["joins"].remove(join)
|
joins.remove(join)
|
||||||
|
|
||||||
alias_cols = alias.columns if alias else []
|
alias_cols = alias.columns if alias else []
|
||||||
|
|
||||||
|
# # Handle UNNEST to LATERAL VIEW EXPLODE: Exception is raised when there are 0 or > 2 aliases
|
||||||
|
# Spark LATERAL VIEW EXPLODE requires single alias for array/struct and two for Map type column unlike unnest in trino/presto which can take an arbitrary amount.
|
||||||
|
# Refs: https://spark.apache.org/docs/latest/sql-ref-syntax-qry-select-lateral-view.html
|
||||||
|
|
||||||
|
if not has_multi_expr and len(alias_cols) not in (1, 2):
|
||||||
|
raise UnsupportedError(
|
||||||
|
"CROSS JOIN UNNEST to LATERAL VIEW EXPLODE transformation requires explicit column aliases"
|
||||||
|
)
|
||||||
|
|
||||||
for e, column in zip(exprs, alias_cols):
|
for e, column in zip(exprs, alias_cols):
|
||||||
expression.append(
|
expression.append(
|
||||||
"laterals",
|
"laterals",
|
||||||
|
@ -376,7 +387,7 @@ def unnest_to_explode(
|
||||||
view=True,
|
view=True,
|
||||||
alias=exp.TableAlias(
|
alias=exp.TableAlias(
|
||||||
this=alias.this, # type: ignore
|
this=alias.this, # type: ignore
|
||||||
columns=alias_cols if unnest_using_arrays_zip else [column], # type: ignore
|
columns=alias_cols,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
|
@ -10,6 +10,7 @@ from sqlglot import (
|
||||||
exp,
|
exp,
|
||||||
parse,
|
parse,
|
||||||
transpile,
|
transpile,
|
||||||
|
parse_one,
|
||||||
)
|
)
|
||||||
from sqlglot.helper import logger as helper_logger
|
from sqlglot.helper import logger as helper_logger
|
||||||
from sqlglot.parser import logger as parser_logger
|
from sqlglot.parser import logger as parser_logger
|
||||||
|
@ -21,8 +22,6 @@ class TestBigQuery(Validator):
|
||||||
maxDiff = None
|
maxDiff = None
|
||||||
|
|
||||||
def test_bigquery(self):
|
def test_bigquery(self):
|
||||||
self.validate_identity("REGEXP_EXTRACT(x, '(?<)')")
|
|
||||||
|
|
||||||
self.validate_all(
|
self.validate_all(
|
||||||
"EXTRACT(HOUR FROM DATETIME(2008, 12, 25, 15, 30, 00))",
|
"EXTRACT(HOUR FROM DATETIME(2008, 12, 25, 15, 30, 00))",
|
||||||
write={
|
write={
|
||||||
|
@ -182,7 +181,6 @@ LANGUAGE js AS
|
||||||
self.validate_identity("""CREATE TABLE x (a STRUCT<values ARRAY<INT64>>)""")
|
self.validate_identity("""CREATE TABLE x (a STRUCT<values ARRAY<INT64>>)""")
|
||||||
self.validate_identity("""CREATE TABLE x (a STRUCT<b STRING OPTIONS (description='b')>)""")
|
self.validate_identity("""CREATE TABLE x (a STRUCT<b STRING OPTIONS (description='b')>)""")
|
||||||
self.validate_identity("CAST(x AS TIMESTAMP)")
|
self.validate_identity("CAST(x AS TIMESTAMP)")
|
||||||
self.validate_identity("REGEXP_EXTRACT(`foo`, 'bar: (.+?)', 1, 1)")
|
|
||||||
self.validate_identity("BEGIN DECLARE y INT64", check_command_warning=True)
|
self.validate_identity("BEGIN DECLARE y INT64", check_command_warning=True)
|
||||||
self.validate_identity("BEGIN TRANSACTION")
|
self.validate_identity("BEGIN TRANSACTION")
|
||||||
self.validate_identity("COMMIT TRANSACTION")
|
self.validate_identity("COMMIT TRANSACTION")
|
||||||
|
@ -202,9 +200,24 @@ LANGUAGE js AS
|
||||||
self.validate_identity("CAST(x AS NVARCHAR)", "CAST(x AS STRING)")
|
self.validate_identity("CAST(x AS NVARCHAR)", "CAST(x AS STRING)")
|
||||||
self.validate_identity("CAST(x AS TIMESTAMPTZ)", "CAST(x AS TIMESTAMP)")
|
self.validate_identity("CAST(x AS TIMESTAMPTZ)", "CAST(x AS TIMESTAMP)")
|
||||||
self.validate_identity("CAST(x AS RECORD)", "CAST(x AS STRUCT)")
|
self.validate_identity("CAST(x AS RECORD)", "CAST(x AS STRUCT)")
|
||||||
self.validate_identity("EDIT_DISTANCE('a', 'a', max_distance => 2)").assert_is(
|
self.validate_all(
|
||||||
exp.Levenshtein
|
"EDIT_DISTANCE(col1, col2, max_distance => 3)",
|
||||||
|
write={
|
||||||
|
"bigquery": "EDIT_DISTANCE(col1, col2, max_distance => 3)",
|
||||||
|
"clickhouse": UnsupportedError,
|
||||||
|
"databricks": UnsupportedError,
|
||||||
|
"drill": UnsupportedError,
|
||||||
|
"duckdb": UnsupportedError,
|
||||||
|
"hive": UnsupportedError,
|
||||||
|
"postgres": "LEVENSHTEIN_LESS_EQUAL(col1, col2, 3)",
|
||||||
|
"presto": UnsupportedError,
|
||||||
|
"snowflake": "EDITDISTANCE(col1, col2, 3)",
|
||||||
|
"spark": UnsupportedError,
|
||||||
|
"spark2": UnsupportedError,
|
||||||
|
"sqlite": UnsupportedError,
|
||||||
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
self.validate_identity(
|
self.validate_identity(
|
||||||
"MERGE INTO dataset.NewArrivals USING (SELECT * FROM UNNEST([('microwave', 10, 'warehouse #1'), ('dryer', 30, 'warehouse #1'), ('oven', 20, 'warehouse #2')])) ON FALSE WHEN NOT MATCHED THEN INSERT ROW WHEN NOT MATCHED BY SOURCE THEN DELETE"
|
"MERGE INTO dataset.NewArrivals USING (SELECT * FROM UNNEST([('microwave', 10, 'warehouse #1'), ('dryer', 30, 'warehouse #1'), ('oven', 20, 'warehouse #2')])) ON FALSE WHEN NOT MATCHED THEN INSERT ROW WHEN NOT MATCHED BY SOURCE THEN DELETE"
|
||||||
)
|
)
|
||||||
|
@ -314,10 +327,6 @@ LANGUAGE js AS
|
||||||
"SELECT * FROM (SELECT a, b, c FROM test) PIVOT(SUM(b) d, COUNT(*) e FOR c IN ('x', 'y'))",
|
"SELECT * FROM (SELECT a, b, c FROM test) PIVOT(SUM(b) d, COUNT(*) e FOR c IN ('x', 'y'))",
|
||||||
"SELECT * FROM (SELECT a, b, c FROM test) PIVOT(SUM(b) AS d, COUNT(*) AS e FOR c IN ('x', 'y'))",
|
"SELECT * FROM (SELECT a, b, c FROM test) PIVOT(SUM(b) AS d, COUNT(*) AS e FOR c IN ('x', 'y'))",
|
||||||
)
|
)
|
||||||
self.validate_identity(
|
|
||||||
r"REGEXP_EXTRACT(svc_plugin_output, r'\\\((.*)')",
|
|
||||||
r"REGEXP_EXTRACT(svc_plugin_output, '\\\\\\((.*)')",
|
|
||||||
)
|
|
||||||
self.validate_identity(
|
self.validate_identity(
|
||||||
"SELECT CAST(1 AS BYTEINT)",
|
"SELECT CAST(1 AS BYTEINT)",
|
||||||
"SELECT CAST(1 AS INT64)",
|
"SELECT CAST(1 AS INT64)",
|
||||||
|
@ -1378,14 +1387,6 @@ LANGUAGE js AS
|
||||||
"postgres": "SELECT * FROM (VALUES (1)) AS t1(id) CROSS JOIN (VALUES (1)) AS t2(id)",
|
"postgres": "SELECT * FROM (VALUES (1)) AS t1(id) CROSS JOIN (VALUES (1)) AS t2(id)",
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
self.validate_all(
|
|
||||||
"SELECT REGEXP_EXTRACT(abc, 'pattern(group)') FROM table",
|
|
||||||
write={
|
|
||||||
"bigquery": "SELECT REGEXP_EXTRACT(abc, 'pattern(group)') FROM table",
|
|
||||||
"duckdb": '''SELECT REGEXP_EXTRACT(abc, 'pattern(group)', 1) FROM "table"''',
|
|
||||||
},
|
|
||||||
)
|
|
||||||
self.validate_all(
|
self.validate_all(
|
||||||
"SELECT * FROM UNNEST([1]) WITH OFFSET",
|
"SELECT * FROM UNNEST([1]) WITH OFFSET",
|
||||||
write={"bigquery": "SELECT * FROM UNNEST([1]) WITH OFFSET AS offset"},
|
write={"bigquery": "SELECT * FROM UNNEST([1]) WITH OFFSET AS offset"},
|
||||||
|
@ -1602,6 +1603,14 @@ WHERE
|
||||||
"snowflake": """SELECT GET_PATH(PARSE_JSON('{"class": {"students": []}}'), 'class')""",
|
"snowflake": """SELECT GET_PATH(PARSE_JSON('{"class": {"students": []}}'), 'class')""",
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
self.validate_all(
|
||||||
|
"""SELECT JSON_VALUE_ARRAY('{"arr": [1, "a"]}', '$.arr')""",
|
||||||
|
write={
|
||||||
|
"bigquery": """SELECT JSON_VALUE_ARRAY('{"arr": [1, "a"]}', '$.arr')""",
|
||||||
|
"duckdb": """SELECT CAST('{"arr": [1, "a"]}' -> '$.arr' AS TEXT[])""",
|
||||||
|
"snowflake": """SELECT TRANSFORM(GET_PATH(PARSE_JSON('{"arr": [1, "a"]}'), 'arr'), x -> CAST(x AS VARCHAR))""",
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
def test_errors(self):
|
def test_errors(self):
|
||||||
with self.assertRaises(TokenError):
|
with self.assertRaises(TokenError):
|
||||||
|
@ -2116,3 +2125,91 @@ OPTIONS (
|
||||||
"snowflake": """SELECT JSON_EXTRACT_PATH_TEXT('{"name": "Jakob", "age": "6"}', 'age')""",
|
"snowflake": """SELECT JSON_EXTRACT_PATH_TEXT('{"name": "Jakob", "age": "6"}', 'age')""",
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def test_json_extract_array(self):
|
||||||
|
for func in ("JSON_QUERY_ARRAY", "JSON_EXTRACT_ARRAY"):
|
||||||
|
with self.subTest(f"Testing BigQuery's {func}"):
|
||||||
|
self.validate_all(
|
||||||
|
f"""SELECT {func}('{{"fruits": [1, "oranges"]}}', '$.fruits')""",
|
||||||
|
write={
|
||||||
|
"bigquery": f"""SELECT {func}('{{"fruits": [1, "oranges"]}}', '$.fruits')""",
|
||||||
|
"duckdb": """SELECT CAST('{"fruits": [1, "oranges"]}' -> '$.fruits' AS JSON[])""",
|
||||||
|
"snowflake": """SELECT TRANSFORM(GET_PATH(PARSE_JSON('{"fruits": [1, "oranges"]}'), 'fruits'), x -> PARSE_JSON(TO_JSON(x)))""",
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_unix_seconds(self):
|
||||||
|
self.validate_all(
|
||||||
|
"SELECT UNIX_SECONDS('2008-12-25 15:30:00+00')",
|
||||||
|
read={
|
||||||
|
"bigquery": "SELECT UNIX_SECONDS('2008-12-25 15:30:00+00')",
|
||||||
|
"spark": "SELECT UNIX_SECONDS('2008-12-25 15:30:00+00')",
|
||||||
|
"databricks": "SELECT UNIX_SECONDS('2008-12-25 15:30:00+00')",
|
||||||
|
},
|
||||||
|
write={
|
||||||
|
"spark": "SELECT UNIX_SECONDS('2008-12-25 15:30:00+00')",
|
||||||
|
"databricks": "SELECT UNIX_SECONDS('2008-12-25 15:30:00+00')",
|
||||||
|
"duckdb": "SELECT DATE_DIFF('SECONDS', CAST('1970-01-01 00:00:00+00' AS TIMESTAMPTZ), '2008-12-25 15:30:00+00')",
|
||||||
|
"snowflake": "SELECT TIMESTAMPDIFF(SECONDS, CAST('1970-01-01 00:00:00+00' AS TIMESTAMPTZ), '2008-12-25 15:30:00+00')",
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
for dialect in ("bigquery", "spark", "databricks"):
|
||||||
|
parse_one("UNIX_SECONDS(col)", dialect=dialect).assert_is(exp.UnixSeconds)
|
||||||
|
|
||||||
|
def test_regexp_extract(self):
|
||||||
|
self.validate_identity("REGEXP_EXTRACT(x, '(?<)')")
|
||||||
|
self.validate_identity("REGEXP_EXTRACT(`foo`, 'bar: (.+?)', 1, 1)")
|
||||||
|
self.validate_identity(
|
||||||
|
r"REGEXP_EXTRACT(svc_plugin_output, r'\\\((.*)')",
|
||||||
|
r"REGEXP_EXTRACT(svc_plugin_output, '\\\\\\((.*)')",
|
||||||
|
)
|
||||||
|
self.validate_identity(
|
||||||
|
r"REGEXP_SUBSTR(value, pattern, position, occurence)",
|
||||||
|
r"REGEXP_EXTRACT(value, pattern, position, occurence)",
|
||||||
|
)
|
||||||
|
|
||||||
|
self.validate_all(
|
||||||
|
"SELECT REGEXP_EXTRACT(abc, 'pattern(group)') FROM table",
|
||||||
|
write={
|
||||||
|
"bigquery": "SELECT REGEXP_EXTRACT(abc, 'pattern(group)') FROM table",
|
||||||
|
"duckdb": '''SELECT REGEXP_EXTRACT(abc, 'pattern(group)', 1) FROM "table"''',
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
# The pattern does not capture a group (entire regular expression is extracted)
|
||||||
|
self.validate_all(
|
||||||
|
"REGEXP_EXTRACT_ALL('a1_a2a3_a4A5a6', 'a[0-9]')",
|
||||||
|
read={
|
||||||
|
"bigquery": "REGEXP_EXTRACT_ALL('a1_a2a3_a4A5a6', 'a[0-9]')",
|
||||||
|
"trino": "REGEXP_EXTRACT_ALL('a1_a2a3_a4A5a6', 'a[0-9]')",
|
||||||
|
"presto": "REGEXP_EXTRACT_ALL('a1_a2a3_a4A5a6', 'a[0-9]')",
|
||||||
|
"snowflake": "REGEXP_EXTRACT_ALL('a1_a2a3_a4A5a6', 'a[0-9]')",
|
||||||
|
"duckdb": "REGEXP_EXTRACT_ALL('a1_a2a3_a4A5a6', 'a[0-9]', 0)",
|
||||||
|
"spark": "REGEXP_EXTRACT_ALL('a1_a2a3_a4A5a6', 'a[0-9]', 0)",
|
||||||
|
"databricks": "REGEXP_EXTRACT_ALL('a1_a2a3_a4A5a6', 'a[0-9]', 0)",
|
||||||
|
},
|
||||||
|
write={
|
||||||
|
"bigquery": "REGEXP_EXTRACT_ALL('a1_a2a3_a4A5a6', 'a[0-9]')",
|
||||||
|
"trino": "REGEXP_EXTRACT_ALL('a1_a2a3_a4A5a6', 'a[0-9]')",
|
||||||
|
"presto": "REGEXP_EXTRACT_ALL('a1_a2a3_a4A5a6', 'a[0-9]')",
|
||||||
|
"snowflake": "REGEXP_EXTRACT_ALL('a1_a2a3_a4A5a6', 'a[0-9]')",
|
||||||
|
"duckdb": "REGEXP_EXTRACT_ALL('a1_a2a3_a4A5a6', 'a[0-9]', 0)",
|
||||||
|
"spark": "REGEXP_EXTRACT_ALL('a1_a2a3_a4A5a6', 'a[0-9]', 0)",
|
||||||
|
"databricks": "REGEXP_EXTRACT_ALL('a1_a2a3_a4A5a6', 'a[0-9]', 0)",
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
# The pattern does capture >=1 group (the default is to extract the first instance)
|
||||||
|
self.validate_all(
|
||||||
|
"REGEXP_EXTRACT_ALL('a1_a2a3_a4A5a6', '(a)[0-9]')",
|
||||||
|
write={
|
||||||
|
"bigquery": "REGEXP_EXTRACT_ALL('a1_a2a3_a4A5a6', '(a)[0-9]')",
|
||||||
|
"trino": "REGEXP_EXTRACT_ALL('a1_a2a3_a4A5a6', '(a)[0-9]', 1)",
|
||||||
|
"presto": "REGEXP_EXTRACT_ALL('a1_a2a3_a4A5a6', '(a)[0-9]', 1)",
|
||||||
|
"snowflake": "REGEXP_EXTRACT_ALL('a1_a2a3_a4A5a6', '(a)[0-9]', 1, 1, 'c', 1)",
|
||||||
|
"duckdb": "REGEXP_EXTRACT_ALL('a1_a2a3_a4A5a6', '(a)[0-9]', 1)",
|
||||||
|
"spark": "REGEXP_EXTRACT_ALL('a1_a2a3_a4A5a6', '(a)[0-9]')",
|
||||||
|
"databricks": "REGEXP_EXTRACT_ALL('a1_a2a3_a4A5a6', '(a)[0-9]')",
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
|
@ -82,6 +82,7 @@ class TestClickhouse(Validator):
|
||||||
self.validate_identity("SELECT histogram(5)(a)")
|
self.validate_identity("SELECT histogram(5)(a)")
|
||||||
self.validate_identity("SELECT groupUniqArray(2)(a)")
|
self.validate_identity("SELECT groupUniqArray(2)(a)")
|
||||||
self.validate_identity("SELECT exponentialTimeDecayedAvg(60)(a, b)")
|
self.validate_identity("SELECT exponentialTimeDecayedAvg(60)(a, b)")
|
||||||
|
self.validate_identity("levenshteinDistance(col1, col2)", "editDistance(col1, col2)")
|
||||||
self.validate_identity("SELECT * FROM foo WHERE x GLOBAL IN (SELECT * FROM bar)")
|
self.validate_identity("SELECT * FROM foo WHERE x GLOBAL IN (SELECT * FROM bar)")
|
||||||
self.validate_identity("position(haystack, needle)")
|
self.validate_identity("position(haystack, needle)")
|
||||||
self.validate_identity("position(haystack, needle, position)")
|
self.validate_identity("position(haystack, needle, position)")
|
||||||
|
|
|
@ -1444,6 +1444,56 @@ class TestDialect(Validator):
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# UNNEST without column alias
|
||||||
|
self.validate_all(
|
||||||
|
"SELECT * FROM x CROSS JOIN UNNEST(y) AS t",
|
||||||
|
write={
|
||||||
|
"presto": "SELECT * FROM x CROSS JOIN UNNEST(y) AS t",
|
||||||
|
"spark": UnsupportedError,
|
||||||
|
"databricks": UnsupportedError,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
# UNNEST MAP Object into multiple columns, using single alias
|
||||||
|
self.validate_all(
|
||||||
|
"SELECT a, b FROM x CROSS JOIN UNNEST(y) AS t (a, b)",
|
||||||
|
write={
|
||||||
|
"presto": "SELECT a, b FROM x CROSS JOIN UNNEST(y) AS t(a, b)",
|
||||||
|
"spark": "SELECT a, b FROM x LATERAL VIEW EXPLODE(y) t AS a, b",
|
||||||
|
"hive": "SELECT a, b FROM x LATERAL VIEW EXPLODE(y) t AS a, b",
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
# Unnest multiple Expression into respective mapped alias
|
||||||
|
self.validate_all(
|
||||||
|
"SELECT numbers, animals, n, a FROM (SELECT ARRAY(2, 5) AS numbers, ARRAY('dog', 'cat', 'bird') AS animals UNION ALL SELECT ARRAY(7, 8, 9), ARRAY('cow', 'pig')) AS x CROSS JOIN UNNEST(numbers, animals) AS t(n, a)",
|
||||||
|
write={
|
||||||
|
"presto": "SELECT numbers, animals, n, a FROM (SELECT ARRAY[2, 5] AS numbers, ARRAY['dog', 'cat', 'bird'] AS animals UNION ALL SELECT ARRAY[7, 8, 9], ARRAY['cow', 'pig']) AS x CROSS JOIN UNNEST(numbers, animals) AS t(n, a)",
|
||||||
|
"spark": "SELECT numbers, animals, n, a FROM (SELECT ARRAY(2, 5) AS numbers, ARRAY('dog', 'cat', 'bird') AS animals UNION ALL SELECT ARRAY(7, 8, 9), ARRAY('cow', 'pig')) AS x LATERAL VIEW INLINE(ARRAYS_ZIP(numbers, animals)) t AS n, a",
|
||||||
|
"hive": UnsupportedError,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
# Unnest column to more then 2 alias (STRUCT)
|
||||||
|
self.validate_all(
|
||||||
|
"SELECT a, b, c, d, e FROM x CROSS JOIN UNNEST(y) AS t(a, b, c, d)",
|
||||||
|
write={
|
||||||
|
"presto": "SELECT a, b, c, d, e FROM x CROSS JOIN UNNEST(y) AS t(a, b, c, d)",
|
||||||
|
"spark": UnsupportedError,
|
||||||
|
"hive": UnsupportedError,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_multiple_chained_unnest(self):
|
||||||
|
self.validate_all(
|
||||||
|
"SELECT * FROM x CROSS JOIN UNNEST(a) AS j(lista) CROSS JOIN UNNEST(b) AS k(listb) CROSS JOIN UNNEST(c) AS l(listc)",
|
||||||
|
write={
|
||||||
|
"presto": "SELECT * FROM x CROSS JOIN UNNEST(a) AS j(lista) CROSS JOIN UNNEST(b) AS k(listb) CROSS JOIN UNNEST(c) AS l(listc)",
|
||||||
|
"spark": "SELECT * FROM x LATERAL VIEW EXPLODE(a) j AS lista LATERAL VIEW EXPLODE(b) k AS listb LATERAL VIEW EXPLODE(c) l AS listc",
|
||||||
|
"hive": "SELECT * FROM x LATERAL VIEW EXPLODE(a) j AS lista LATERAL VIEW EXPLODE(b) k AS listb LATERAL VIEW EXPLODE(c) l AS listc",
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
def test_lateral_subquery(self):
|
def test_lateral_subquery(self):
|
||||||
self.validate_identity(
|
self.validate_identity(
|
||||||
"SELECT art FROM tbl1 INNER JOIN LATERAL (SELECT art FROM tbl2) AS tbl2 ON tbl1.art = tbl2.art"
|
"SELECT art FROM tbl1 INNER JOIN LATERAL (SELECT art FROM tbl2) AS tbl2 ON tbl1.art = tbl2.art"
|
||||||
|
@ -1761,15 +1811,67 @@ class TestDialect(Validator):
|
||||||
)
|
)
|
||||||
self.validate_all(
|
self.validate_all(
|
||||||
"LEVENSHTEIN(col1, col2)",
|
"LEVENSHTEIN(col1, col2)",
|
||||||
write={
|
read={
|
||||||
"bigquery": "EDIT_DISTANCE(col1, col2)",
|
"bigquery": "EDIT_DISTANCE(col1, col2)",
|
||||||
"duckdb": "LEVENSHTEIN(col1, col2)",
|
"clickhouse": "editDistance(col1, col2)",
|
||||||
"drill": "LEVENSHTEIN_DISTANCE(col1, col2)",
|
"drill": "LEVENSHTEIN_DISTANCE(col1, col2)",
|
||||||
"presto": "LEVENSHTEIN_DISTANCE(col1, col2)",
|
"duckdb": "LEVENSHTEIN(col1, col2)",
|
||||||
"hive": "LEVENSHTEIN(col1, col2)",
|
"hive": "LEVENSHTEIN(col1, col2)",
|
||||||
"spark": "LEVENSHTEIN(col1, col2)",
|
"spark": "LEVENSHTEIN(col1, col2)",
|
||||||
|
"postgres": "LEVENSHTEIN(col1, col2)",
|
||||||
|
"presto": "LEVENSHTEIN_DISTANCE(col1, col2)",
|
||||||
|
"snowflake": "EDITDISTANCE(col1, col2)",
|
||||||
|
"sqlite": "EDITDIST3(col1, col2)",
|
||||||
|
"trino": "LEVENSHTEIN_DISTANCE(col1, col2)",
|
||||||
|
},
|
||||||
|
write={
|
||||||
|
"bigquery": "EDIT_DISTANCE(col1, col2)",
|
||||||
|
"clickhouse": "editDistance(col1, col2)",
|
||||||
|
"drill": "LEVENSHTEIN_DISTANCE(col1, col2)",
|
||||||
|
"duckdb": "LEVENSHTEIN(col1, col2)",
|
||||||
|
"hive": "LEVENSHTEIN(col1, col2)",
|
||||||
|
"spark": "LEVENSHTEIN(col1, col2)",
|
||||||
|
"postgres": "LEVENSHTEIN(col1, col2)",
|
||||||
|
"presto": "LEVENSHTEIN_DISTANCE(col1, col2)",
|
||||||
|
"snowflake": "EDITDISTANCE(col1, col2)",
|
||||||
|
"sqlite": "EDITDIST3(col1, col2)",
|
||||||
|
"trino": "LEVENSHTEIN_DISTANCE(col1, col2)",
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
|
self.validate_all(
|
||||||
|
"LEVENSHTEIN(col1, col2, 1, 2, 3)",
|
||||||
|
write={
|
||||||
|
"bigquery": UnsupportedError,
|
||||||
|
"clickhouse": UnsupportedError,
|
||||||
|
"drill": UnsupportedError,
|
||||||
|
"duckdb": UnsupportedError,
|
||||||
|
"hive": UnsupportedError,
|
||||||
|
"spark": UnsupportedError,
|
||||||
|
"postgres": "LEVENSHTEIN(col1, col2, 1, 2, 3)",
|
||||||
|
"presto": UnsupportedError,
|
||||||
|
"snowflake": UnsupportedError,
|
||||||
|
"sqlite": UnsupportedError,
|
||||||
|
"trino": UnsupportedError,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
self.validate_all(
|
||||||
|
"LEVENSHTEIN(col1, col2, 1, 2, 3, 4)",
|
||||||
|
write={
|
||||||
|
"bigquery": UnsupportedError,
|
||||||
|
"clickhouse": UnsupportedError,
|
||||||
|
"drill": UnsupportedError,
|
||||||
|
"duckdb": UnsupportedError,
|
||||||
|
"hive": UnsupportedError,
|
||||||
|
"spark": UnsupportedError,
|
||||||
|
"postgres": "LEVENSHTEIN_LESS_EQUAL(col1, col2, 1, 2, 3, 4)",
|
||||||
|
"presto": UnsupportedError,
|
||||||
|
"snowflake": UnsupportedError,
|
||||||
|
"sqlite": UnsupportedError,
|
||||||
|
"trino": UnsupportedError,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
self.validate_all(
|
self.validate_all(
|
||||||
"LEVENSHTEIN(coalesce(col1, col2), coalesce(col2, col1))",
|
"LEVENSHTEIN(coalesce(col1, col2), coalesce(col2, col1))",
|
||||||
write={
|
write={
|
||||||
|
@ -3007,7 +3109,7 @@ FROM subquery2""",
|
||||||
"databricks": f"MEDIAN(x){suffix}",
|
"databricks": f"MEDIAN(x){suffix}",
|
||||||
"redshift": f"MEDIAN(x){suffix}",
|
"redshift": f"MEDIAN(x){suffix}",
|
||||||
"oracle": f"MEDIAN(x){suffix}",
|
"oracle": f"MEDIAN(x){suffix}",
|
||||||
"clickhouse": f"MEDIAN(x){suffix}",
|
"clickhouse": f"median(x){suffix}",
|
||||||
"postgres": f"PERCENTILE_CONT(0.5) WITHIN GROUP (ORDER BY x){suffix}",
|
"postgres": f"PERCENTILE_CONT(0.5) WITHIN GROUP (ORDER BY x){suffix}",
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
|
@ -852,6 +852,7 @@ class TestDuckDB(Validator):
|
||||||
"clickhouse": "DATE_TRUNC('DAY', x)",
|
"clickhouse": "DATE_TRUNC('DAY', x)",
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
self.validate_identity("EDITDIST3(col1, col2)", "LEVENSHTEIN(col1, col2)")
|
||||||
|
|
||||||
self.validate_identity("SELECT LENGTH(foo)")
|
self.validate_identity("SELECT LENGTH(foo)")
|
||||||
self.validate_identity("SELECT ARRAY[1, 2, 3]", "SELECT [1, 2, 3]")
|
self.validate_identity("SELECT ARRAY[1, 2, 3]", "SELECT [1, 2, 3]")
|
||||||
|
@ -1153,6 +1154,7 @@ class TestDuckDB(Validator):
|
||||||
self.validate_identity("CAST(x AS BINARY)", "CAST(x AS BLOB)")
|
self.validate_identity("CAST(x AS BINARY)", "CAST(x AS BLOB)")
|
||||||
self.validate_identity("CAST(x AS VARBINARY)", "CAST(x AS BLOB)")
|
self.validate_identity("CAST(x AS VARBINARY)", "CAST(x AS BLOB)")
|
||||||
self.validate_identity("CAST(x AS LOGICAL)", "CAST(x AS BOOLEAN)")
|
self.validate_identity("CAST(x AS LOGICAL)", "CAST(x AS BOOLEAN)")
|
||||||
|
self.validate_identity("""CAST({'i': 1, 's': 'foo'} AS STRUCT("s" TEXT, "i" INT))""")
|
||||||
self.validate_identity(
|
self.validate_identity(
|
||||||
"CAST(ROW(1, ROW(1)) AS STRUCT(number BIGINT, row STRUCT(number BIGINT)))"
|
"CAST(ROW(1, ROW(1)) AS STRUCT(number BIGINT, row STRUCT(number BIGINT)))"
|
||||||
)
|
)
|
||||||
|
@ -1162,11 +1164,11 @@ class TestDuckDB(Validator):
|
||||||
)
|
)
|
||||||
self.validate_identity(
|
self.validate_identity(
|
||||||
"CAST([[STRUCT_PACK(a := 1)]] AS STRUCT(a BIGINT)[][])",
|
"CAST([[STRUCT_PACK(a := 1)]] AS STRUCT(a BIGINT)[][])",
|
||||||
"CAST([[ROW(1)]] AS STRUCT(a BIGINT)[][])",
|
"CAST([[{'a': 1}]] AS STRUCT(a BIGINT)[][])",
|
||||||
)
|
)
|
||||||
self.validate_identity(
|
self.validate_identity(
|
||||||
"CAST([STRUCT_PACK(a := 1)] AS STRUCT(a BIGINT)[])",
|
"CAST([STRUCT_PACK(a := 1)] AS STRUCT(a BIGINT)[])",
|
||||||
"CAST([ROW(1)] AS STRUCT(a BIGINT)[])",
|
"CAST([{'a': 1}] AS STRUCT(a BIGINT)[])",
|
||||||
)
|
)
|
||||||
self.validate_identity(
|
self.validate_identity(
|
||||||
"STRUCT_PACK(a := 'b')::json",
|
"STRUCT_PACK(a := 'b')::json",
|
||||||
|
@ -1174,7 +1176,7 @@ class TestDuckDB(Validator):
|
||||||
)
|
)
|
||||||
self.validate_identity(
|
self.validate_identity(
|
||||||
"STRUCT_PACK(a := 'b')::STRUCT(a TEXT)",
|
"STRUCT_PACK(a := 'b')::STRUCT(a TEXT)",
|
||||||
"CAST(ROW('b') AS STRUCT(a TEXT))",
|
"CAST({'a': 'b'} AS STRUCT(a TEXT))",
|
||||||
)
|
)
|
||||||
|
|
||||||
self.validate_all(
|
self.validate_all(
|
||||||
|
|
|
@ -51,7 +51,6 @@ class TestPostgres(Validator):
|
||||||
self.validate_identity("x$")
|
self.validate_identity("x$")
|
||||||
self.validate_identity("SELECT ARRAY[1, 2, 3]")
|
self.validate_identity("SELECT ARRAY[1, 2, 3]")
|
||||||
self.validate_identity("SELECT ARRAY(SELECT 1)")
|
self.validate_identity("SELECT ARRAY(SELECT 1)")
|
||||||
self.validate_identity("SELECT ARRAY_LENGTH(ARRAY[1, 2, 3], 1)")
|
|
||||||
self.validate_identity("STRING_AGG(x, y)")
|
self.validate_identity("STRING_AGG(x, y)")
|
||||||
self.validate_identity("STRING_AGG(x, ',' ORDER BY y)")
|
self.validate_identity("STRING_AGG(x, ',' ORDER BY y)")
|
||||||
self.validate_identity("STRING_AGG(x, ',' ORDER BY y DESC)")
|
self.validate_identity("STRING_AGG(x, ',' ORDER BY y DESC)")
|
||||||
|
@ -683,6 +682,11 @@ class TestPostgres(Validator):
|
||||||
"""SELECT TRIM(TRAILING ' XXX ' COLLATE "de_DE")""",
|
"""SELECT TRIM(TRAILING ' XXX ' COLLATE "de_DE")""",
|
||||||
"""SELECT RTRIM(' XXX ' COLLATE "de_DE")""",
|
"""SELECT RTRIM(' XXX ' COLLATE "de_DE")""",
|
||||||
)
|
)
|
||||||
|
self.validate_identity("LEVENSHTEIN(col1, col2)")
|
||||||
|
self.validate_identity("LEVENSHTEIN_LESS_EQUAL(col1, col2, 1)")
|
||||||
|
self.validate_identity("LEVENSHTEIN(col1, col2, 1, 2, 3)")
|
||||||
|
self.validate_identity("LEVENSHTEIN_LESS_EQUAL(col1, col2, 1, 2, 3, 4)")
|
||||||
|
|
||||||
self.validate_all(
|
self.validate_all(
|
||||||
"""'{"a":1,"b":2}'::json->'b'""",
|
"""'{"a":1,"b":2}'::json->'b'""",
|
||||||
write={
|
write={
|
||||||
|
@ -1237,3 +1241,49 @@ CROSS JOIN JSON_ARRAY_ELEMENTS(CAST(JSON_EXTRACT_PATH(tbox, 'boxes') AS JSON)) A
|
||||||
self.validate_identity(
|
self.validate_identity(
|
||||||
"""SELECT * FROM table1, ROWS FROM (FUNC1(col1) AS alias1("col1" TEXT)) WITH ORDINALITY AS alias3("col3" INT, "col4" TEXT)"""
|
"""SELECT * FROM table1, ROWS FROM (FUNC1(col1) AS alias1("col1" TEXT)) WITH ORDINALITY AS alias3("col3" INT, "col4" TEXT)"""
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def test_array_length(self):
|
||||||
|
self.validate_identity("SELECT ARRAY_LENGTH(ARRAY[1, 2, 3], 1)")
|
||||||
|
|
||||||
|
self.validate_all(
|
||||||
|
"ARRAY_LENGTH(arr, 1)",
|
||||||
|
read={
|
||||||
|
"bigquery": "ARRAY_LENGTH(arr)",
|
||||||
|
"duckdb": "ARRAY_LENGTH(arr)",
|
||||||
|
"presto": "CARDINALITY(arr)",
|
||||||
|
"drill": "REPEATED_COUNT(arr)",
|
||||||
|
"teradata": "CARDINALITY(arr)",
|
||||||
|
"hive": "SIZE(arr)",
|
||||||
|
"spark2": "SIZE(arr)",
|
||||||
|
"spark": "SIZE(arr)",
|
||||||
|
"databricks": "SIZE(arr)",
|
||||||
|
},
|
||||||
|
write={
|
||||||
|
"duckdb": "ARRAY_LENGTH(arr, 1)",
|
||||||
|
"presto": "CARDINALITY(arr)",
|
||||||
|
"teradata": "CARDINALITY(arr)",
|
||||||
|
"bigquery": "ARRAY_LENGTH(arr)",
|
||||||
|
"drill": "REPEATED_COUNT(arr)",
|
||||||
|
"clickhouse": "LENGTH(arr)",
|
||||||
|
"hive": "SIZE(arr)",
|
||||||
|
"spark2": "SIZE(arr)",
|
||||||
|
"spark": "SIZE(arr)",
|
||||||
|
"databricks": "SIZE(arr)",
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
self.validate_all(
|
||||||
|
"ARRAY_LENGTH(arr, foo)",
|
||||||
|
write={
|
||||||
|
"duckdb": "ARRAY_LENGTH(arr, foo)",
|
||||||
|
"hive": UnsupportedError,
|
||||||
|
"spark2": UnsupportedError,
|
||||||
|
"spark": UnsupportedError,
|
||||||
|
"databricks": UnsupportedError,
|
||||||
|
"presto": UnsupportedError,
|
||||||
|
"teradata": UnsupportedError,
|
||||||
|
"bigquery": UnsupportedError,
|
||||||
|
"drill": UnsupportedError,
|
||||||
|
"clickhouse": UnsupportedError,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
|
@ -946,6 +946,16 @@ WHERE
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
|
self.validate_identity("EDITDISTANCE(col1, col2)")
|
||||||
|
self.validate_all(
|
||||||
|
"EDITDISTANCE(col1, col2, 3)",
|
||||||
|
write={
|
||||||
|
"bigquery": "EDIT_DISTANCE(col1, col2, max_distance => 3)",
|
||||||
|
"postgres": "LEVENSHTEIN_LESS_EQUAL(col1, col2, 3)",
|
||||||
|
"snowflake": "EDITDISTANCE(col1, col2, 3)",
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
def test_null_treatment(self):
|
def test_null_treatment(self):
|
||||||
self.validate_all(
|
self.validate_all(
|
||||||
r"SELECT FIRST_VALUE(TABLE1.COLUMN1) OVER (PARTITION BY RANDOM_COLUMN1, RANDOM_COLUMN2 ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING) AS MY_ALIAS FROM TABLE1",
|
r"SELECT FIRST_VALUE(TABLE1.COLUMN1) OVER (PARTITION BY RANDOM_COLUMN1, RANDOM_COLUMN2 ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING) AS MY_ALIAS FROM TABLE1",
|
||||||
|
@ -1788,7 +1798,6 @@ FROM persons AS p, LATERAL FLATTEN(input => p.c, path => 'contact') AS _flattene
|
||||||
self.validate_all(
|
self.validate_all(
|
||||||
"REGEXP_SUBSTR(subject, pattern, 1, 1, 'c', group)",
|
"REGEXP_SUBSTR(subject, pattern, 1, 1, 'c', group)",
|
||||||
read={
|
read={
|
||||||
"bigquery": "REGEXP_SUBSTR(subject, pattern, 1, 1, 'c', group)",
|
|
||||||
"duckdb": "REGEXP_EXTRACT(subject, pattern, group)",
|
"duckdb": "REGEXP_EXTRACT(subject, pattern, group)",
|
||||||
"hive": "REGEXP_EXTRACT(subject, pattern, group)",
|
"hive": "REGEXP_EXTRACT(subject, pattern, group)",
|
||||||
"presto": "REGEXP_EXTRACT(subject, pattern, group)",
|
"presto": "REGEXP_EXTRACT(subject, pattern, group)",
|
||||||
|
@ -1797,6 +1806,11 @@ FROM persons AS p, LATERAL FLATTEN(input => p.c, path => 'contact') AS _flattene
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
|
self.validate_identity(
|
||||||
|
"REGEXP_SUBSTR_ALL(subject, pattern)",
|
||||||
|
"REGEXP_EXTRACT_ALL(subject, pattern)",
|
||||||
|
)
|
||||||
|
|
||||||
@mock.patch("sqlglot.generator.logger")
|
@mock.patch("sqlglot.generator.logger")
|
||||||
def test_regexp_replace(self, logger):
|
def test_regexp_replace(self, logger):
|
||||||
self.validate_all(
|
self.validate_all(
|
||||||
|
|
|
@ -754,6 +754,17 @@ TBLPROPERTIES (
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
|
self.validate_all(
|
||||||
|
"SELECT TIMESTAMPDIFF(MONTH, foo, bar)",
|
||||||
|
read={
|
||||||
|
"databricks": "SELECT TIMESTAMPDIFF(MONTH, foo, bar)",
|
||||||
|
},
|
||||||
|
write={
|
||||||
|
"spark": "SELECT TIMESTAMPDIFF(MONTH, foo, bar)",
|
||||||
|
"databricks": "SELECT TIMESTAMPDIFF(MONTH, foo, bar)",
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
def test_bool_or(self):
|
def test_bool_or(self):
|
||||||
self.validate_all(
|
self.validate_all(
|
||||||
"SELECT a, LOGICAL_OR(b) FROM table GROUP BY a",
|
"SELECT a, LOGICAL_OR(b) FROM table GROUP BY a",
|
||||||
|
|
|
@ -216,12 +216,14 @@ class TestExpressions(unittest.TestCase):
|
||||||
self.assertEqual(exp.table_name(exp.to_table("a.b.c.d.e", dialect="bigquery")), "a.b.c.d.e")
|
self.assertEqual(exp.table_name(exp.to_table("a.b.c.d.e", dialect="bigquery")), "a.b.c.d.e")
|
||||||
self.assertEqual(exp.table_name(exp.to_table("'@foo'", dialect="snowflake")), "'@foo'")
|
self.assertEqual(exp.table_name(exp.to_table("'@foo'", dialect="snowflake")), "'@foo'")
|
||||||
self.assertEqual(exp.table_name(exp.to_table("@foo", dialect="snowflake")), "@foo")
|
self.assertEqual(exp.table_name(exp.to_table("@foo", dialect="snowflake")), "@foo")
|
||||||
|
self.assertEqual(exp.table_name(bq_dashed_table, identify=True), '"a-1"."b"."c"')
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
exp.table_name(parse_one("foo.`{bar,er}`", read="databricks"), dialect="databricks"),
|
exp.table_name(parse_one("foo.`{bar,er}`", read="databricks"), dialect="databricks"),
|
||||||
"foo.`{bar,er}`",
|
"foo.`{bar,er}`",
|
||||||
)
|
)
|
||||||
|
self.assertEqual(
|
||||||
self.assertEqual(exp.table_name(bq_dashed_table, identify=True), '"a-1"."b"."c"')
|
exp.table_name(parse_one("/*c*/foo.bar", into=exp.Table), identify=True), '"foo"."bar"'
|
||||||
|
)
|
||||||
|
|
||||||
def test_table(self):
|
def test_table(self):
|
||||||
self.assertEqual(exp.table_("a", alias="b"), parse_one("select * from a b").find(exp.Table))
|
self.assertEqual(exp.table_("a", alias="b"), parse_one("select * from a b").find(exp.Table))
|
||||||
|
|
|
@ -340,6 +340,20 @@ class TestParser(unittest.TestCase):
|
||||||
self.assertEqual(expression.expressions[4].comments, [""])
|
self.assertEqual(expression.expressions[4].comments, [""])
|
||||||
self.assertEqual(expression.expressions[5].comments, [" space"])
|
self.assertEqual(expression.expressions[5].comments, [" space"])
|
||||||
|
|
||||||
|
expression = parse_one(
|
||||||
|
"""
|
||||||
|
SELECT a.column_name --# Comment 1
|
||||||
|
,b.column_name2, --# Comment 2
|
||||||
|
b.column_name3 AS NAME3 --# Comment 3
|
||||||
|
FROM table_name a
|
||||||
|
JOIN table_name2 b ON a.column_name = b.column_name
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
|
||||||
|
self.assertEqual(expression.expressions[0].comments, ["# Comment 1"])
|
||||||
|
self.assertEqual(expression.expressions[1].comments, ["# Comment 2"])
|
||||||
|
self.assertEqual(expression.expressions[2].comments, ["# Comment 3"])
|
||||||
|
|
||||||
def test_comments_select_cte(self):
|
def test_comments_select_cte(self):
|
||||||
expression = parse_one(
|
expression = parse_one(
|
||||||
"""
|
"""
|
||||||
|
@ -698,20 +712,20 @@ class TestParser(unittest.TestCase):
|
||||||
self.assertEqual(expected_columns, [col.sql(dialect=dialect) for col in columns])
|
self.assertEqual(expected_columns, [col.sql(dialect=dialect) for col in columns])
|
||||||
|
|
||||||
def test_parse_nested(self):
|
def test_parse_nested(self):
|
||||||
|
def warn_over_threshold(query: str, max_threshold: float = 0.2):
|
||||||
now = time.time()
|
now = time.time()
|
||||||
query = parse_one("SELECT * FROM a " + ("LEFT JOIN b ON a.id = b.id " * 38))
|
ast = parse_one(query)
|
||||||
self.assertIsNotNone(query)
|
end = time.time() - now
|
||||||
self.assertLessEqual(time.time() - now, 0.1)
|
|
||||||
|
|
||||||
now = time.time()
|
self.assertIsNotNone(ast)
|
||||||
query = parse_one("SELECT * FROM a " + ("LEFT JOIN UNNEST(ARRAY[]) " * 15))
|
if end >= max_threshold:
|
||||||
self.assertIsNotNone(query)
|
parser_logger.warning(
|
||||||
self.assertLessEqual(time.time() - now, 0.1)
|
f"Query {query[:100]}... surpassed the time threshold of {max_threshold} seconds"
|
||||||
|
)
|
||||||
|
|
||||||
now = time.time()
|
warn_over_threshold("SELECT * FROM a " + ("LEFT JOIN b ON a.id = b.id " * 38))
|
||||||
query = parse_one("SELECT * FROM a " + ("OUTER APPLY (SELECT * FROM b) " * 30))
|
warn_over_threshold("SELECT * FROM a " + ("LEFT JOIN UNNEST(ARRAY[]) " * 15))
|
||||||
self.assertIsNotNone(query)
|
warn_over_threshold("SELECT * FROM a " + ("OUTER APPLY (SELECT * FROM b) " * 30))
|
||||||
self.assertLessEqual(time.time() - now, 0.1)
|
|
||||||
|
|
||||||
def test_parse_properties(self):
|
def test_parse_properties(self):
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue