sqlglot 30.0.2__tar.gz → 30.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (349) hide show
  1. {sqlglot-30.0.2 → sqlglot-30.1.0}/.gitignore +4 -1
  2. sqlglot-30.1.0/AGENTS.md +521 -0
  3. {sqlglot-30.0.2 → sqlglot-30.1.0}/CHANGELOG.md +54 -0
  4. {sqlglot-30.0.2 → sqlglot-30.1.0}/CLAUDE.md +4 -4
  5. {sqlglot-30.0.2 → sqlglot-30.1.0}/CONTRIBUTING.md +2 -0
  6. {sqlglot-30.0.2 → sqlglot-30.1.0}/PKG-INFO +19 -19
  7. {sqlglot-30.0.2 → sqlglot-30.1.0}/README.md +16 -16
  8. {sqlglot-30.0.2 → sqlglot-30.1.0}/sqlglot/__init__.py +8 -7
  9. sqlglot-30.1.0/sqlglot/_version.py +24 -0
  10. {sqlglot-30.0.2 → sqlglot-30.1.0}/sqlglot/dialects/athena.py +8 -114
  11. sqlglot-30.1.0/sqlglot/dialects/bigquery.py +203 -0
  12. sqlglot-30.1.0/sqlglot/dialects/clickhouse.py +132 -0
  13. sqlglot-30.1.0/sqlglot/dialects/databricks.py +40 -0
  14. {sqlglot-30.0.2 → sqlglot-30.1.0}/sqlglot/dialects/dialect.py +53 -54
  15. sqlglot-30.1.0/sqlglot/dialects/doris.py +15 -0
  16. sqlglot-30.1.0/sqlglot/dialects/dremio.py +71 -0
  17. sqlglot-30.1.0/sqlglot/dialects/drill.py +62 -0
  18. sqlglot-30.1.0/sqlglot/dialects/druid.py +9 -0
  19. sqlglot-30.1.0/sqlglot/dialects/duckdb.py +124 -0
  20. {sqlglot-30.0.2 → sqlglot-30.1.0}/sqlglot/dialects/dune.py +2 -6
  21. sqlglot-30.1.0/sqlglot/dialects/exasol.py +69 -0
  22. sqlglot-30.1.0/sqlglot/dialects/fabric.py +46 -0
  23. sqlglot-30.1.0/sqlglot/dialects/hive.py +125 -0
  24. sqlglot-30.1.0/sqlglot/dialects/materialize.py +11 -0
  25. sqlglot-30.1.0/sqlglot/dialects/mysql.py +158 -0
  26. sqlglot-30.1.0/sqlglot/dialects/oracle.py +90 -0
  27. sqlglot-30.1.0/sqlglot/dialects/postgres.py +128 -0
  28. sqlglot-30.1.0/sqlglot/dialects/presto.py +71 -0
  29. {sqlglot-30.0.2 → sqlglot-30.1.0}/sqlglot/dialects/prql.py +3 -0
  30. sqlglot-30.1.0/sqlglot/dialects/redshift.py +53 -0
  31. sqlglot-30.1.0/sqlglot/dialects/risingwave.py +22 -0
  32. sqlglot-30.1.0/sqlglot/dialects/singlestore.py +66 -0
  33. sqlglot-30.1.0/sqlglot/dialects/snowflake.py +186 -0
  34. {sqlglot-30.0.2 → sqlglot-30.1.0}/sqlglot/dialects/solr.py +3 -0
  35. sqlglot-30.1.0/sqlglot/dialects/spark.py +34 -0
  36. sqlglot-30.1.0/sqlglot/dialects/spark2.py +30 -0
  37. sqlglot-30.1.0/sqlglot/dialects/sqlite.py +40 -0
  38. sqlglot-30.1.0/sqlglot/dialects/starrocks.py +21 -0
  39. sqlglot-30.1.0/sqlglot/dialects/tableau.py +18 -0
  40. sqlglot-30.1.0/sqlglot/dialects/teradata.py +81 -0
  41. sqlglot-30.1.0/sqlglot/dialects/trino.py +21 -0
  42. sqlglot-30.1.0/sqlglot/dialects/tsql.py +191 -0
  43. {sqlglot-30.0.2 → sqlglot-30.1.0}/sqlglot/diff.py +6 -5
  44. {sqlglot-30.0.2 → sqlglot-30.1.0}/sqlglot/errors.py +3 -3
  45. {sqlglot-30.0.2 → sqlglot-30.1.0}/sqlglot/expressions/builders.py +18 -13
  46. {sqlglot-30.0.2 → sqlglot-30.1.0}/sqlglot/expressions/core.py +71 -68
  47. {sqlglot-30.0.2 → sqlglot-30.1.0}/sqlglot/expressions/ddl.py +4 -0
  48. {sqlglot-30.0.2 → sqlglot-30.1.0}/sqlglot/expressions/dml.py +9 -9
  49. {sqlglot-30.0.2 → sqlglot-30.1.0}/sqlglot/expressions/functions.py +2 -2
  50. {sqlglot-30.0.2 → sqlglot-30.1.0}/sqlglot/expressions/properties.py +4 -0
  51. {sqlglot-30.0.2 → sqlglot-30.1.0}/sqlglot/expressions/query.py +54 -33
  52. {sqlglot-30.0.2 → sqlglot-30.1.0}/sqlglot/expressions/string.py +1 -0
  53. {sqlglot-30.0.2 → sqlglot-30.1.0}/sqlglot/generator.py +120 -79
  54. sqlglot-30.1.0/sqlglot/generators/athena.py +180 -0
  55. sqlglot-30.1.0/sqlglot/generators/bigquery.py +686 -0
  56. sqlglot-30.1.0/sqlglot/generators/clickhouse.py +665 -0
  57. sqlglot-30.1.0/sqlglot/generators/databricks.py +101 -0
  58. sqlglot-30.1.0/sqlglot/generators/doris.py +612 -0
  59. sqlglot-30.1.0/sqlglot/generators/dremio.py +100 -0
  60. sqlglot-30.1.0/sqlglot/generators/drill.py +112 -0
  61. sqlglot-30.1.0/sqlglot/generators/druid.py +31 -0
  62. sqlglot-30.1.0/sqlglot/generators/duckdb.py +4179 -0
  63. sqlglot-30.1.0/sqlglot/generators/dune.py +14 -0
  64. sqlglot-30.1.0/sqlglot/generators/exasol.py +926 -0
  65. sqlglot-30.1.0/sqlglot/generators/fabric.py +149 -0
  66. sqlglot-30.1.0/sqlglot/generators/hive.py +544 -0
  67. sqlglot-30.1.0/sqlglot/generators/materialize.py +56 -0
  68. sqlglot-30.1.0/sqlglot/generators/mysql.py +795 -0
  69. sqlglot-30.1.0/sqlglot/generators/oracle.py +165 -0
  70. sqlglot-30.1.0/sqlglot/generators/postgres.py +567 -0
  71. sqlglot-30.1.0/sqlglot/generators/presto.py +647 -0
  72. sqlglot-30.1.0/sqlglot/generators/prql.py +7 -0
  73. sqlglot-30.1.0/sqlglot/generators/redshift.py +347 -0
  74. sqlglot-30.1.0/sqlglot/generators/risingwave.py +32 -0
  75. sqlglot-30.1.0/sqlglot/generators/singlestore.py +1595 -0
  76. sqlglot-30.1.0/sqlglot/generators/snowflake.py +1138 -0
  77. sqlglot-30.1.0/sqlglot/generators/solr.py +7 -0
  78. sqlglot-30.1.0/sqlglot/generators/spark.py +202 -0
  79. {sqlglot-30.0.2/sqlglot/dialects → sqlglot-30.1.0/sqlglot/generators}/spark2.py +88 -107
  80. sqlglot-30.1.0/sqlglot/generators/sqlite.py +320 -0
  81. sqlglot-30.1.0/sqlglot/generators/starrocks.py +325 -0
  82. sqlglot-30.1.0/sqlglot/generators/tableau.py +51 -0
  83. sqlglot-30.1.0/sqlglot/generators/teradata.py +181 -0
  84. sqlglot-30.1.0/sqlglot/generators/trino.py +68 -0
  85. sqlglot-30.1.0/sqlglot/generators/tsql.py +624 -0
  86. {sqlglot-30.0.2 → sqlglot-30.1.0}/sqlglot/helper.py +21 -18
  87. {sqlglot-30.0.2 → sqlglot-30.1.0}/sqlglot/jsonpath.py +2 -1
  88. {sqlglot-30.0.2 → sqlglot-30.1.0}/sqlglot/lineage.py +21 -13
  89. {sqlglot-30.0.2 → sqlglot-30.1.0}/sqlglot/optimizer/annotate_types.py +40 -2
  90. {sqlglot-30.0.2 → sqlglot-30.1.0}/sqlglot/optimizer/optimizer.py +2 -2
  91. {sqlglot-30.0.2 → sqlglot-30.1.0}/sqlglot/optimizer/qualify_columns.py +7 -6
  92. {sqlglot-30.0.2 → sqlglot-30.1.0}/sqlglot/optimizer/resolver.py +11 -12
  93. {sqlglot-30.0.2 → sqlglot-30.1.0}/sqlglot/optimizer/scope.py +24 -21
  94. {sqlglot-30.0.2 → sqlglot-30.1.0}/sqlglot/optimizer/simplify.py +9 -4
  95. {sqlglot-30.0.2 → sqlglot-30.1.0}/sqlglot/parser.py +97 -98
  96. {sqlglot-30.0.2 → sqlglot-30.1.0}/sqlglot/parsers/clickhouse.py +11 -8
  97. {sqlglot-30.0.2 → sqlglot-30.1.0}/sqlglot/parsers/dremio.py +2 -2
  98. {sqlglot-30.0.2 → sqlglot-30.1.0}/sqlglot/parsers/duckdb.py +5 -3
  99. {sqlglot-30.0.2 → sqlglot-30.1.0}/sqlglot/parsers/hive.py +1 -1
  100. {sqlglot-30.0.2 → sqlglot-30.1.0}/sqlglot/parsers/prql.py +2 -1
  101. {sqlglot-30.0.2 → sqlglot-30.1.0}/sqlglot/parsers/redshift.py +5 -3
  102. {sqlglot-30.0.2 → sqlglot-30.1.0}/sqlglot/parsers/snowflake.py +33 -17
  103. {sqlglot-30.0.2 → sqlglot-30.1.0}/sqlglot/parsers/tsql.py +2 -1
  104. {sqlglot-30.0.2 → sqlglot-30.1.0}/sqlglot/planner.py +9 -8
  105. {sqlglot-30.0.2 → sqlglot-30.1.0}/sqlglot/schema.py +9 -8
  106. {sqlglot-30.0.2 → sqlglot-30.1.0}/sqlglot/tokens.py +1 -1
  107. {sqlglot-30.0.2 → sqlglot-30.1.0}/sqlglot/trie.py +3 -2
  108. {sqlglot-30.0.2 → sqlglot-30.1.0}/sqlglot/typing/__init__.py +2 -1
  109. {sqlglot-30.0.2 → sqlglot-30.1.0}/sqlglot.egg-info/PKG-INFO +19 -19
  110. {sqlglot-30.0.2 → sqlglot-30.1.0}/sqlglot.egg-info/SOURCES.txt +34 -0
  111. {sqlglot-30.0.2 → sqlglot-30.1.0}/sqlglot.egg-info/requires.txt +2 -2
  112. {sqlglot-30.0.2 → sqlglot-30.1.0}/sqlglotc/pyproject.toml +2 -6
  113. sqlglot-30.1.0/sqlglotc/setup.py +132 -0
  114. sqlglot-30.1.0/tests/dialects/__init__.py +0 -0
  115. {sqlglot-30.0.2 → sqlglot-30.1.0}/tests/dialects/test_bigquery.py +27 -1
  116. {sqlglot-30.0.2 → sqlglot-30.1.0}/tests/dialects/test_clickhouse.py +8 -1
  117. {sqlglot-30.0.2 → sqlglot-30.1.0}/tests/dialects/test_dialect.py +7 -7
  118. {sqlglot-30.0.2 → sqlglot-30.1.0}/tests/dialects/test_duckdb.py +24 -5
  119. {sqlglot-30.0.2 → sqlglot-30.1.0}/tests/dialects/test_mysql.py +61 -0
  120. {sqlglot-30.0.2 → sqlglot-30.1.0}/tests/dialects/test_pipe_syntax.py +4 -0
  121. {sqlglot-30.0.2 → sqlglot-30.1.0}/tests/dialects/test_postgres.py +1 -0
  122. {sqlglot-30.0.2 → sqlglot-30.1.0}/tests/dialects/test_redshift.py +2 -0
  123. {sqlglot-30.0.2 → sqlglot-30.1.0}/tests/dialects/test_snowflake.py +134 -2
  124. {sqlglot-30.0.2 → sqlglot-30.1.0}/tests/fixtures/optimizer/simplify.sql +3 -0
  125. {sqlglot-30.0.2 → sqlglot-30.1.0}/tests/test_executor.py +4 -0
  126. {sqlglot-30.0.2 → sqlglot-30.1.0}/tests/test_lineage.py +7 -0
  127. {sqlglot-30.0.2 → sqlglot-30.1.0}/tests/test_optimizer.py +202 -0
  128. sqlglot-30.0.2/sqlglot/_version.py +0 -34
  129. sqlglot-30.0.2/sqlglot/dialects/bigquery.py +0 -883
  130. sqlglot-30.0.2/sqlglot/dialects/clickhouse.py +0 -761
  131. sqlglot-30.0.2/sqlglot/dialects/databricks.py +0 -128
  132. sqlglot-30.0.2/sqlglot/dialects/doris.py +0 -620
  133. sqlglot-30.0.2/sqlglot/dialects/dremio.py +0 -156
  134. sqlglot-30.0.2/sqlglot/dialects/drill.py +0 -150
  135. sqlglot-30.0.2/sqlglot/dialects/druid.py +0 -24
  136. sqlglot-30.0.2/sqlglot/dialects/duckdb.py +0 -4286
  137. sqlglot-30.0.2/sqlglot/dialects/exasol.py +0 -976
  138. sqlglot-30.0.2/sqlglot/dialects/fabric.py +0 -193
  139. sqlglot-30.0.2/sqlglot/dialects/hive.py +0 -651
  140. sqlglot-30.0.2/sqlglot/dialects/materialize.py +0 -61
  141. sqlglot-30.0.2/sqlglot/dialects/mysql.py +0 -919
  142. sqlglot-30.0.2/sqlglot/dialects/oracle.py +0 -239
  143. sqlglot-30.0.2/sqlglot/dialects/postgres.py +0 -683
  144. sqlglot-30.0.2/sqlglot/dialects/presto.py +0 -713
  145. sqlglot-30.0.2/sqlglot/dialects/redshift.py +0 -403
  146. sqlglot-30.0.2/sqlglot/dialects/risingwave.py +0 -47
  147. sqlglot-30.0.2/sqlglot/dialects/singlestore.py +0 -1641
  148. sqlglot-30.0.2/sqlglot/dialects/snowflake.py +0 -1314
  149. sqlglot-30.0.2/sqlglot/dialects/spark.py +0 -224
  150. sqlglot-30.0.2/sqlglot/dialects/sqlite.py +0 -343
  151. sqlglot-30.0.2/sqlglot/dialects/starrocks.py +0 -343
  152. sqlglot-30.0.2/sqlglot/dialects/tableau.py +0 -52
  153. sqlglot-30.0.2/sqlglot/dialects/teradata.py +0 -248
  154. sqlglot-30.0.2/sqlglot/dialects/trino.py +0 -82
  155. sqlglot-30.0.2/sqlglot/dialects/tsql.py +0 -803
  156. sqlglot-30.0.2/sqlglotc/setup.py +0 -107
  157. {sqlglot-30.0.2 → sqlglot-30.1.0}/.gitmodules +0 -0
  158. {sqlglot-30.0.2 → sqlglot-30.1.0}/.gitpod.yml +0 -0
  159. {sqlglot-30.0.2 → sqlglot-30.1.0}/.pre-commit-config.yaml +0 -0
  160. {sqlglot-30.0.2 → sqlglot-30.1.0}/LICENSE +0 -0
  161. {sqlglot-30.0.2 → sqlglot-30.1.0}/MANIFEST.in +0 -0
  162. {sqlglot-30.0.2 → sqlglot-30.1.0}/Makefile +0 -0
  163. {sqlglot-30.0.2 → sqlglot-30.1.0}/pyproject.toml +0 -0
  164. {sqlglot-30.0.2 → sqlglot-30.1.0}/setup.cfg +0 -0
  165. {sqlglot-30.0.2 → sqlglot-30.1.0}/setup.py +0 -0
  166. {sqlglot-30.0.2 → sqlglot-30.1.0}/sqlglot/__main__.py +0 -0
  167. {sqlglot-30.0.2 → sqlglot-30.1.0}/sqlglot/_typing.py +0 -0
  168. {sqlglot-30.0.2 → sqlglot-30.1.0}/sqlglot/dialects/__init__.py +0 -0
  169. {sqlglot-30.0.2 → sqlglot-30.1.0}/sqlglot/executor/__init__.py +0 -0
  170. {sqlglot-30.0.2 → sqlglot-30.1.0}/sqlglot/executor/context.py +0 -0
  171. {sqlglot-30.0.2 → sqlglot-30.1.0}/sqlglot/executor/env.py +0 -0
  172. {sqlglot-30.0.2 → sqlglot-30.1.0}/sqlglot/executor/python.py +0 -0
  173. {sqlglot-30.0.2 → sqlglot-30.1.0}/sqlglot/executor/table.py +0 -0
  174. {sqlglot-30.0.2 → sqlglot-30.1.0}/sqlglot/expressions/__init__.py +0 -0
  175. {sqlglot-30.0.2 → sqlglot-30.1.0}/sqlglot/expressions/aggregate.py +0 -0
  176. {sqlglot-30.0.2 → sqlglot-30.1.0}/sqlglot/expressions/array.py +0 -0
  177. {sqlglot-30.0.2 → sqlglot-30.1.0}/sqlglot/expressions/constraints.py +0 -0
  178. {sqlglot-30.0.2 → sqlglot-30.1.0}/sqlglot/expressions/datatypes.py +0 -0
  179. {sqlglot-30.0.2 → sqlglot-30.1.0}/sqlglot/expressions/json.py +0 -0
  180. {sqlglot-30.0.2 → sqlglot-30.1.0}/sqlglot/expressions/math.py +0 -0
  181. {sqlglot-30.0.2 → sqlglot-30.1.0}/sqlglot/expressions/temporal.py +0 -0
  182. {sqlglot-30.0.2/tests → sqlglot-30.1.0/sqlglot/generators}/__init__.py +0 -0
  183. {sqlglot-30.0.2 → sqlglot-30.1.0}/sqlglot/optimizer/__init__.py +0 -0
  184. {sqlglot-30.0.2 → sqlglot-30.1.0}/sqlglot/optimizer/canonicalize.py +0 -0
  185. {sqlglot-30.0.2 → sqlglot-30.1.0}/sqlglot/optimizer/eliminate_ctes.py +0 -0
  186. {sqlglot-30.0.2 → sqlglot-30.1.0}/sqlglot/optimizer/eliminate_joins.py +0 -0
  187. {sqlglot-30.0.2 → sqlglot-30.1.0}/sqlglot/optimizer/eliminate_subqueries.py +0 -0
  188. {sqlglot-30.0.2 → sqlglot-30.1.0}/sqlglot/optimizer/isolate_table_selects.py +0 -0
  189. {sqlglot-30.0.2 → sqlglot-30.1.0}/sqlglot/optimizer/merge_subqueries.py +0 -0
  190. {sqlglot-30.0.2 → sqlglot-30.1.0}/sqlglot/optimizer/normalize.py +0 -0
  191. {sqlglot-30.0.2 → sqlglot-30.1.0}/sqlglot/optimizer/normalize_identifiers.py +0 -0
  192. {sqlglot-30.0.2 → sqlglot-30.1.0}/sqlglot/optimizer/optimize_joins.py +0 -0
  193. {sqlglot-30.0.2 → sqlglot-30.1.0}/sqlglot/optimizer/pushdown_predicates.py +0 -0
  194. {sqlglot-30.0.2 → sqlglot-30.1.0}/sqlglot/optimizer/pushdown_projections.py +0 -0
  195. {sqlglot-30.0.2 → sqlglot-30.1.0}/sqlglot/optimizer/qualify.py +0 -0
  196. {sqlglot-30.0.2 → sqlglot-30.1.0}/sqlglot/optimizer/qualify_tables.py +0 -0
  197. {sqlglot-30.0.2 → sqlglot-30.1.0}/sqlglot/optimizer/unnest_subqueries.py +0 -0
  198. {sqlglot-30.0.2 → sqlglot-30.1.0}/sqlglot/parsers/__init__.py +0 -0
  199. {sqlglot-30.0.2 → sqlglot-30.1.0}/sqlglot/parsers/athena.py +0 -0
  200. {sqlglot-30.0.2 → sqlglot-30.1.0}/sqlglot/parsers/base.py +0 -0
  201. {sqlglot-30.0.2 → sqlglot-30.1.0}/sqlglot/parsers/bigquery.py +0 -0
  202. {sqlglot-30.0.2 → sqlglot-30.1.0}/sqlglot/parsers/databricks.py +0 -0
  203. {sqlglot-30.0.2 → sqlglot-30.1.0}/sqlglot/parsers/doris.py +0 -0
  204. {sqlglot-30.0.2 → sqlglot-30.1.0}/sqlglot/parsers/drill.py +0 -0
  205. {sqlglot-30.0.2 → sqlglot-30.1.0}/sqlglot/parsers/druid.py +0 -0
  206. {sqlglot-30.0.2 → sqlglot-30.1.0}/sqlglot/parsers/dune.py +0 -0
  207. {sqlglot-30.0.2 → sqlglot-30.1.0}/sqlglot/parsers/exasol.py +0 -0
  208. {sqlglot-30.0.2 → sqlglot-30.1.0}/sqlglot/parsers/fabric.py +0 -0
  209. {sqlglot-30.0.2 → sqlglot-30.1.0}/sqlglot/parsers/materialize.py +0 -0
  210. {sqlglot-30.0.2 → sqlglot-30.1.0}/sqlglot/parsers/mysql.py +0 -0
  211. {sqlglot-30.0.2 → sqlglot-30.1.0}/sqlglot/parsers/oracle.py +0 -0
  212. {sqlglot-30.0.2 → sqlglot-30.1.0}/sqlglot/parsers/postgres.py +0 -0
  213. {sqlglot-30.0.2 → sqlglot-30.1.0}/sqlglot/parsers/presto.py +0 -0
  214. {sqlglot-30.0.2 → sqlglot-30.1.0}/sqlglot/parsers/risingwave.py +0 -0
  215. {sqlglot-30.0.2 → sqlglot-30.1.0}/sqlglot/parsers/singlestore.py +0 -0
  216. {sqlglot-30.0.2 → sqlglot-30.1.0}/sqlglot/parsers/solr.py +0 -0
  217. {sqlglot-30.0.2 → sqlglot-30.1.0}/sqlglot/parsers/spark.py +0 -0
  218. {sqlglot-30.0.2 → sqlglot-30.1.0}/sqlglot/parsers/spark2.py +0 -0
  219. {sqlglot-30.0.2 → sqlglot-30.1.0}/sqlglot/parsers/sqlite.py +0 -0
  220. {sqlglot-30.0.2 → sqlglot-30.1.0}/sqlglot/parsers/starrocks.py +0 -0
  221. {sqlglot-30.0.2 → sqlglot-30.1.0}/sqlglot/parsers/tableau.py +0 -0
  222. {sqlglot-30.0.2 → sqlglot-30.1.0}/sqlglot/parsers/teradata.py +0 -0
  223. {sqlglot-30.0.2 → sqlglot-30.1.0}/sqlglot/parsers/trino.py +0 -0
  224. {sqlglot-30.0.2 → sqlglot-30.1.0}/sqlglot/py.typed +0 -0
  225. {sqlglot-30.0.2 → sqlglot-30.1.0}/sqlglot/serde.py +0 -0
  226. {sqlglot-30.0.2 → sqlglot-30.1.0}/sqlglot/time.py +0 -0
  227. {sqlglot-30.0.2 → sqlglot-30.1.0}/sqlglot/tokenizer_core.py +0 -0
  228. {sqlglot-30.0.2 → sqlglot-30.1.0}/sqlglot/transforms.py +0 -0
  229. {sqlglot-30.0.2 → sqlglot-30.1.0}/sqlglot/typing/bigquery.py +0 -0
  230. {sqlglot-30.0.2 → sqlglot-30.1.0}/sqlglot/typing/clickhouse.py +0 -0
  231. {sqlglot-30.0.2 → sqlglot-30.1.0}/sqlglot/typing/duckdb.py +0 -0
  232. {sqlglot-30.0.2 → sqlglot-30.1.0}/sqlglot/typing/hive.py +0 -0
  233. {sqlglot-30.0.2 → sqlglot-30.1.0}/sqlglot/typing/mysql.py +0 -0
  234. {sqlglot-30.0.2 → sqlglot-30.1.0}/sqlglot/typing/presto.py +0 -0
  235. {sqlglot-30.0.2 → sqlglot-30.1.0}/sqlglot/typing/redshift.py +0 -0
  236. {sqlglot-30.0.2 → sqlglot-30.1.0}/sqlglot/typing/snowflake.py +0 -0
  237. {sqlglot-30.0.2 → sqlglot-30.1.0}/sqlglot/typing/spark.py +0 -0
  238. {sqlglot-30.0.2 → sqlglot-30.1.0}/sqlglot/typing/spark2.py +0 -0
  239. {sqlglot-30.0.2 → sqlglot-30.1.0}/sqlglot/typing/tsql.py +0 -0
  240. {sqlglot-30.0.2 → sqlglot-30.1.0}/sqlglot.egg-info/dependency_links.txt +0 -0
  241. {sqlglot-30.0.2 → sqlglot-30.1.0}/sqlglot.egg-info/top_level.txt +0 -0
  242. {sqlglot-30.0.2 → sqlglot-30.1.0}/sqlglot.png +0 -0
  243. {sqlglot-30.0.2 → sqlglot-30.1.0}/sqlglotc/MANIFEST.in +0 -0
  244. {sqlglot-30.0.2/tests/dialects → sqlglot-30.1.0/tests}/__init__.py +0 -0
  245. {sqlglot-30.0.2 → sqlglot-30.1.0}/tests/dialects/test_athena.py +0 -0
  246. {sqlglot-30.0.2 → sqlglot-30.1.0}/tests/dialects/test_databricks.py +0 -0
  247. {sqlglot-30.0.2 → sqlglot-30.1.0}/tests/dialects/test_doris.py +0 -0
  248. {sqlglot-30.0.2 → sqlglot-30.1.0}/tests/dialects/test_dremio.py +0 -0
  249. {sqlglot-30.0.2 → sqlglot-30.1.0}/tests/dialects/test_drill.py +0 -0
  250. {sqlglot-30.0.2 → sqlglot-30.1.0}/tests/dialects/test_druid.py +0 -0
  251. {sqlglot-30.0.2 → sqlglot-30.1.0}/tests/dialects/test_dune.py +0 -0
  252. {sqlglot-30.0.2 → sqlglot-30.1.0}/tests/dialects/test_exasol.py +0 -0
  253. {sqlglot-30.0.2 → sqlglot-30.1.0}/tests/dialects/test_fabric.py +0 -0
  254. {sqlglot-30.0.2 → sqlglot-30.1.0}/tests/dialects/test_hive.py +0 -0
  255. {sqlglot-30.0.2 → sqlglot-30.1.0}/tests/dialects/test_materialize.py +0 -0
  256. {sqlglot-30.0.2 → sqlglot-30.1.0}/tests/dialects/test_oracle.py +0 -0
  257. {sqlglot-30.0.2 → sqlglot-30.1.0}/tests/dialects/test_presto.py +0 -0
  258. {sqlglot-30.0.2 → sqlglot-30.1.0}/tests/dialects/test_prql.py +0 -0
  259. {sqlglot-30.0.2 → sqlglot-30.1.0}/tests/dialects/test_risingwave.py +0 -0
  260. {sqlglot-30.0.2 → sqlglot-30.1.0}/tests/dialects/test_singlestore.py +0 -0
  261. {sqlglot-30.0.2 → sqlglot-30.1.0}/tests/dialects/test_solr.py +0 -0
  262. {sqlglot-30.0.2 → sqlglot-30.1.0}/tests/dialects/test_spark.py +0 -0
  263. {sqlglot-30.0.2 → sqlglot-30.1.0}/tests/dialects/test_sqlite.py +0 -0
  264. {sqlglot-30.0.2 → sqlglot-30.1.0}/tests/dialects/test_starrocks.py +0 -0
  265. {sqlglot-30.0.2 → sqlglot-30.1.0}/tests/dialects/test_tableau.py +0 -0
  266. {sqlglot-30.0.2 → sqlglot-30.1.0}/tests/dialects/test_teradata.py +0 -0
  267. {sqlglot-30.0.2 → sqlglot-30.1.0}/tests/dialects/test_trino.py +0 -0
  268. {sqlglot-30.0.2 → sqlglot-30.1.0}/tests/dialects/test_tsql.py +0 -0
  269. {sqlglot-30.0.2 → sqlglot-30.1.0}/tests/fixtures/identity.sql +0 -0
  270. {sqlglot-30.0.2 → sqlglot-30.1.0}/tests/fixtures/jsonpath/LICENSE +0 -0
  271. {sqlglot-30.0.2 → sqlglot-30.1.0}/tests/fixtures/jsonpath/cts.json +0 -0
  272. {sqlglot-30.0.2 → sqlglot-30.1.0}/tests/fixtures/optimizer/annotate_functions.sql +0 -0
  273. {sqlglot-30.0.2 → sqlglot-30.1.0}/tests/fixtures/optimizer/annotate_types.sql +0 -0
  274. {sqlglot-30.0.2 → sqlglot-30.1.0}/tests/fixtures/optimizer/canonicalize.sql +0 -0
  275. {sqlglot-30.0.2 → sqlglot-30.1.0}/tests/fixtures/optimizer/eliminate_ctes.sql +0 -0
  276. {sqlglot-30.0.2 → sqlglot-30.1.0}/tests/fixtures/optimizer/eliminate_joins.sql +0 -0
  277. {sqlglot-30.0.2 → sqlglot-30.1.0}/tests/fixtures/optimizer/eliminate_subqueries.sql +0 -0
  278. {sqlglot-30.0.2 → sqlglot-30.1.0}/tests/fixtures/optimizer/isolate_table_selects.sql +0 -0
  279. {sqlglot-30.0.2 → sqlglot-30.1.0}/tests/fixtures/optimizer/merge_subqueries.sql +0 -0
  280. {sqlglot-30.0.2 → sqlglot-30.1.0}/tests/fixtures/optimizer/normalize.sql +0 -0
  281. {sqlglot-30.0.2 → sqlglot-30.1.0}/tests/fixtures/optimizer/normalize_identifiers.sql +0 -0
  282. {sqlglot-30.0.2 → sqlglot-30.1.0}/tests/fixtures/optimizer/optimize_joins.sql +0 -0
  283. {sqlglot-30.0.2 → sqlglot-30.1.0}/tests/fixtures/optimizer/optimizer.sql +0 -0
  284. {sqlglot-30.0.2 → sqlglot-30.1.0}/tests/fixtures/optimizer/pushdown_cte_alias_columns.sql +0 -0
  285. {sqlglot-30.0.2 → sqlglot-30.1.0}/tests/fixtures/optimizer/pushdown_predicates.sql +0 -0
  286. {sqlglot-30.0.2 → sqlglot-30.1.0}/tests/fixtures/optimizer/pushdown_projections.sql +0 -0
  287. {sqlglot-30.0.2 → sqlglot-30.1.0}/tests/fixtures/optimizer/qualify_columns.sql +0 -0
  288. {sqlglot-30.0.2 → sqlglot-30.1.0}/tests/fixtures/optimizer/qualify_columns__invalid.sql +0 -0
  289. {sqlglot-30.0.2 → sqlglot-30.1.0}/tests/fixtures/optimizer/qualify_columns__with_invisible.sql +0 -0
  290. {sqlglot-30.0.2 → sqlglot-30.1.0}/tests/fixtures/optimizer/qualify_columns_ddl.sql +0 -0
  291. {sqlglot-30.0.2 → sqlglot-30.1.0}/tests/fixtures/optimizer/qualify_tables.sql +0 -0
  292. {sqlglot-30.0.2 → sqlglot-30.1.0}/tests/fixtures/optimizer/quote_identifiers.sql +0 -0
  293. {sqlglot-30.0.2 → sqlglot-30.1.0}/tests/fixtures/optimizer/tpc-ds/call_center.csv.gz +0 -0
  294. {sqlglot-30.0.2 → sqlglot-30.1.0}/tests/fixtures/optimizer/tpc-ds/catalog_page.csv.gz +0 -0
  295. {sqlglot-30.0.2 → sqlglot-30.1.0}/tests/fixtures/optimizer/tpc-ds/catalog_returns.csv.gz +0 -0
  296. {sqlglot-30.0.2 → sqlglot-30.1.0}/tests/fixtures/optimizer/tpc-ds/catalog_sales.csv.gz +0 -0
  297. {sqlglot-30.0.2 → sqlglot-30.1.0}/tests/fixtures/optimizer/tpc-ds/customer.csv.gz +0 -0
  298. {sqlglot-30.0.2 → sqlglot-30.1.0}/tests/fixtures/optimizer/tpc-ds/customer_address.csv.gz +0 -0
  299. {sqlglot-30.0.2 → sqlglot-30.1.0}/tests/fixtures/optimizer/tpc-ds/customer_demographics.csv.gz +0 -0
  300. {sqlglot-30.0.2 → sqlglot-30.1.0}/tests/fixtures/optimizer/tpc-ds/date_dim.csv.gz +0 -0
  301. {sqlglot-30.0.2 → sqlglot-30.1.0}/tests/fixtures/optimizer/tpc-ds/household_demographics.csv.gz +0 -0
  302. {sqlglot-30.0.2 → sqlglot-30.1.0}/tests/fixtures/optimizer/tpc-ds/income_band.csv.gz +0 -0
  303. {sqlglot-30.0.2 → sqlglot-30.1.0}/tests/fixtures/optimizer/tpc-ds/inventory.csv.gz +0 -0
  304. {sqlglot-30.0.2 → sqlglot-30.1.0}/tests/fixtures/optimizer/tpc-ds/item.csv.gz +0 -0
  305. {sqlglot-30.0.2 → sqlglot-30.1.0}/tests/fixtures/optimizer/tpc-ds/promotion.csv.gz +0 -0
  306. {sqlglot-30.0.2 → sqlglot-30.1.0}/tests/fixtures/optimizer/tpc-ds/reason.csv.gz +0 -0
  307. {sqlglot-30.0.2 → sqlglot-30.1.0}/tests/fixtures/optimizer/tpc-ds/ship_mode.csv.gz +0 -0
  308. {sqlglot-30.0.2 → sqlglot-30.1.0}/tests/fixtures/optimizer/tpc-ds/store.csv.gz +0 -0
  309. {sqlglot-30.0.2 → sqlglot-30.1.0}/tests/fixtures/optimizer/tpc-ds/store_returns.csv.gz +0 -0
  310. {sqlglot-30.0.2 → sqlglot-30.1.0}/tests/fixtures/optimizer/tpc-ds/store_sales.csv.gz +0 -0
  311. {sqlglot-30.0.2 → sqlglot-30.1.0}/tests/fixtures/optimizer/tpc-ds/time_dim.csv.gz +0 -0
  312. {sqlglot-30.0.2 → sqlglot-30.1.0}/tests/fixtures/optimizer/tpc-ds/tpc-ds.sql +0 -0
  313. {sqlglot-30.0.2 → sqlglot-30.1.0}/tests/fixtures/optimizer/tpc-ds/warehouse.csv.gz +0 -0
  314. {sqlglot-30.0.2 → sqlglot-30.1.0}/tests/fixtures/optimizer/tpc-ds/web_page.csv.gz +0 -0
  315. {sqlglot-30.0.2 → sqlglot-30.1.0}/tests/fixtures/optimizer/tpc-ds/web_returns.csv.gz +0 -0
  316. {sqlglot-30.0.2 → sqlglot-30.1.0}/tests/fixtures/optimizer/tpc-ds/web_sales.csv.gz +0 -0
  317. {sqlglot-30.0.2 → sqlglot-30.1.0}/tests/fixtures/optimizer/tpc-ds/web_site.csv.gz +0 -0
  318. {sqlglot-30.0.2 → sqlglot-30.1.0}/tests/fixtures/optimizer/tpc-h/customer.csv.gz +0 -0
  319. {sqlglot-30.0.2 → sqlglot-30.1.0}/tests/fixtures/optimizer/tpc-h/lineitem.csv.gz +0 -0
  320. {sqlglot-30.0.2 → sqlglot-30.1.0}/tests/fixtures/optimizer/tpc-h/nation.csv.gz +0 -0
  321. {sqlglot-30.0.2 → sqlglot-30.1.0}/tests/fixtures/optimizer/tpc-h/orders.csv.gz +0 -0
  322. {sqlglot-30.0.2 → sqlglot-30.1.0}/tests/fixtures/optimizer/tpc-h/part.csv.gz +0 -0
  323. {sqlglot-30.0.2 → sqlglot-30.1.0}/tests/fixtures/optimizer/tpc-h/partsupp.csv.gz +0 -0
  324. {sqlglot-30.0.2 → sqlglot-30.1.0}/tests/fixtures/optimizer/tpc-h/region.csv.gz +0 -0
  325. {sqlglot-30.0.2 → sqlglot-30.1.0}/tests/fixtures/optimizer/tpc-h/supplier.csv.gz +0 -0
  326. {sqlglot-30.0.2 → sqlglot-30.1.0}/tests/fixtures/optimizer/tpc-h/tpc-h.sql +0 -0
  327. {sqlglot-30.0.2 → sqlglot-30.1.0}/tests/fixtures/optimizer/unnest_subqueries.sql +0 -0
  328. {sqlglot-30.0.2 → sqlglot-30.1.0}/tests/fixtures/partial.sql +0 -0
  329. {sqlglot-30.0.2 → sqlglot-30.1.0}/tests/fixtures/pretty.sql +0 -0
  330. {sqlglot-30.0.2 → sqlglot-30.1.0}/tests/gen_fixtures.py +0 -0
  331. {sqlglot-30.0.2 → sqlglot-30.1.0}/tests/helpers.py +0 -0
  332. {sqlglot-30.0.2 → sqlglot-30.1.0}/tests/test_build.py +0 -0
  333. {sqlglot-30.0.2 → sqlglot-30.1.0}/tests/test_dialect_entry_points.py +0 -0
  334. {sqlglot-30.0.2 → sqlglot-30.1.0}/tests/test_dialect_imports.py +0 -0
  335. {sqlglot-30.0.2 → sqlglot-30.1.0}/tests/test_diff.py +0 -0
  336. {sqlglot-30.0.2 → sqlglot-30.1.0}/tests/test_docs.py +0 -0
  337. {sqlglot-30.0.2 → sqlglot-30.1.0}/tests/test_errors.py +0 -0
  338. {sqlglot-30.0.2 → sqlglot-30.1.0}/tests/test_expressions.py +0 -0
  339. {sqlglot-30.0.2 → sqlglot-30.1.0}/tests/test_generator.py +0 -0
  340. {sqlglot-30.0.2 → sqlglot-30.1.0}/tests/test_helper.py +0 -0
  341. {sqlglot-30.0.2 → sqlglot-30.1.0}/tests/test_integration_loader.py +0 -0
  342. {sqlglot-30.0.2 → sqlglot-30.1.0}/tests/test_jsonpath.py +0 -0
  343. {sqlglot-30.0.2 → sqlglot-30.1.0}/tests/test_parser.py +0 -0
  344. {sqlglot-30.0.2 → sqlglot-30.1.0}/tests/test_schema.py +0 -0
  345. {sqlglot-30.0.2 → sqlglot-30.1.0}/tests/test_serde.py +0 -0
  346. {sqlglot-30.0.2 → sqlglot-30.1.0}/tests/test_time.py +0 -0
  347. {sqlglot-30.0.2 → sqlglot-30.1.0}/tests/test_tokens.py +0 -0
  348. {sqlglot-30.0.2 → sqlglot-30.1.0}/tests/test_transforms.py +0 -0
  349. {sqlglot-30.0.2 → sqlglot-30.1.0}/tests/test_transpile.py +0 -0
@@ -144,4 +144,7 @@ spark_warehouse
144
144
  sqlglot/_version.py
145
145
 
146
146
  # Emacs files
147
- *~
147
+ *~
148
+
149
+ # UV lock file
150
+ uv.lock
@@ -0,0 +1,521 @@
1
+ # Contributing to [SQLGlot](https://github.com/tobymao/sqlglot/blob/main/README.md)
2
+ ## About SQLGlot
3
+
4
+ SQLGlot is a no-dependency SQL parser, transpiler, optimizer, and engine written in pure Python. It supports 31+ SQL dialects and can transpile between them while preserving semantics. The codebase is performance-critical despite being pure Python, with an optional mypyc-compiled C extension for speed improvements (`sqlglotc/`).
5
+
6
+ ## Development Commands
7
+ Before contributing read CONTRIBUTING.md
8
+
9
+ ### Installation
10
+ ```bash
11
+ # Basic installation
12
+ make install
13
+
14
+ # Development installation (Python only, no C extension)
15
+ make install-dev
16
+
17
+ # Development installation with mypyc C extension
18
+ make install-devc
19
+
20
+ # Install pre-commit hooks
21
+ make install-pre-commit
22
+
23
+ # With uv (faster):
24
+ UV=1 make install-dev
25
+ ```
26
+
27
+ ### Testing
28
+ ```bash
29
+ # Run all tests (pure Python, hides .so files during run)
30
+ make test
31
+
32
+ # Run all tests with mypyc C extension (builds extension first)
33
+ make testc
34
+
35
+ # Run only unit tests (skip integration tests, pure Python)
36
+ make unit
37
+
38
+ # Run only unit tests with C extension
39
+ make unitc
40
+
41
+ # Run specific test file
42
+ python -m unittest tests.test_expressions
43
+
44
+ # Run specific test class
45
+ python -m unittest tests.test_expressions.TestExpressions
46
+
47
+ # Run specific test method
48
+ python -m unittest tests.test_expressions.TestExpressions.test_alias
49
+ ```
50
+
51
+ ### Linting & Type Checking
52
+ ```bash
53
+ # Run linter and formatter only
54
+ make style
55
+
56
+ # Run full checks (style + pure Python tests + C extension tests)
57
+ make check
58
+ ```
59
+
60
+ ### Benchmarks
61
+ ```bash
62
+ # Run parsing benchmark
63
+ make bench
64
+
65
+ # Run optimization benchmark
66
+ make bench-optimize
67
+ ```
68
+
69
+ ## Architecture Overview
70
+
71
+ SQLGlot follows a classic compiler architecture with three main phases:
72
+
73
+ ### 1. Tokenizer (`tokens.py`)
74
+ - Converts SQL strings into a sequence of tokens (lexical analysis)
75
+ - Pure Python implementation in `tokens.py`; core logic in `tokenizer_core.py` (mypyc-compiled when using `[c]` extra)
76
+ - Maps lexemes to `TokenType` enum values via `KEYWORDS` and `SINGLE_TOKENS` dictionaries
77
+ - Dialects can override tokenizer behavior by customizing these mappings
78
+
79
+ ### 2. Parser (`parser.py`)
80
+ - Converts tokens into an Abstract Syntax Tree (AST)
81
+ - Uses recursive descent parsing approach
82
+ - Parsing methods follow `_parse_*` naming convention (e.g., `_parse_create()`, `_parse_select()`)
83
+ - Token matching methods: `_match()`, `_match_set()`, `_match_text_seq()`, `_match_texts()`
84
+ - Helper methods for common patterns: `_parse_csv()`, `_parse_wrapped()`, `_parse_wrapped_csv()`
85
+ - Maintains index/cursor with `_advance()` and `_retreat()` methods
86
+ - Falls back to `exp.Command` for unparseable SQL (preserves original text)
87
+
88
+ ### 3. Generator (`generator.py`)
89
+ - Converts AST back to SQL strings
90
+ - Traverses AST recursively, generating SQL for each expression node
91
+ - Two ways to customize generation:
92
+ - `TRANSFORMS` dictionary for single-line generations
93
+ - `<expr_name>_sql()` methods for complex generations
94
+ - Helper methods: `expressions()`, `func()`, `rename_func()`
95
+ - Use `sep()` and `seg()` for proper whitespace/newline handling in pretty-printed output
96
+
97
+ ### 4. Expressions (`expressions.py`)
98
+ - Defines all AST node types as Python classes inheriting from `Expression`
99
+ - Each expression represents a semantic SQL concept (e.g., `Select`, `Join`, `Column`)
100
+ - Expressions can be traversed using `.find()`, `.find_all()`, `.walk()`, `.transform()`
101
+ - Building SQL programmatically: use helper functions like `select()`, `from_()`, `where()`, etc.
102
+
103
+ ### 5. Dialects (`dialects/`)
104
+ - 34 dialect implementations in `dialects/<dialect>.py`
105
+ - Each dialect subclasses base `Dialect` and can override Tokenizer, Parser, and Generator
106
+ - Base "sqlglot" dialect acts as a superset to minimize duplication
107
+ - Dialect customization via:
108
+ - Feature flags (e.g., `SUPPORTS_IMPLICIT_UNNEST`)
109
+ - Token sets (e.g., `RESERVED_TOKENS`)
110
+ - `token -> Callable` mappings (e.g., `FUNCTIONS`, `STATEMENTS`)
111
+ - `Expression -> str` mappings in Generator
112
+
113
+ ### 6. Optimizer (`optimizer/`)
114
+ - Canonicalizes and optimizes queries while preserving semantics
115
+ - Applies sequential optimization rules (order matters!)
116
+ - Key rules:
117
+ - `qualify`: Normalizes identifiers and qualifies all tables/columns (most important rule)
118
+ - `annotate_types`: Infers data types throughout the AST
119
+ - `pushdown_predicates`, `pushdown_projections`: Optimization rewrites
120
+ - `simplify`: Simplifies boolean expressions and arithmetic
121
+ - Rules depend on schema information for best results
122
+ - Optimizer performs logical optimization only (not physical/performance)
123
+
124
+ ### 7. Schema (`schema.py`)
125
+ - Represents database structure (tables, columns, types)
126
+ - Used by optimizer and lineage analysis
127
+ - `MappingSchema` takes nested dict: `{"table": {"col": "type"}}`
128
+
129
+ ### 8. Lineage (`lineage.py`)
130
+ - Traces column-level lineage through queries
131
+ - Requires target query, upstream queries, and root table schemas
132
+ - Builds linked list of `Node` objects representing data flow
133
+ - Can visualize with `node.to_html()`
134
+
135
+ ## Key Concepts
136
+
137
+ ### The "sqlglot" Dialect
138
+ - Base dialect that accommodates common syntax across all dialects
139
+ - All other dialects extend this base
140
+ - When adding multi-dialect features, prefer adding to base dialect to avoid duplication
141
+ - Only add dialect-specific features to individual dialect classes
142
+
143
+ ### AST-First Approach
144
+ - SQLGlot preserves _semantics_ not syntax
145
+ - Parse SQL → AST (semantic representation) → Generate SQL in target dialect
146
+ - This enables accurate cross-dialect transpilation
147
+ - Comments are preserved on best-effort basis
148
+ - See `posts/ast_primer.md` for detailed AST tutorial
149
+
150
+ ### Testing Philosophy
151
+ - Comprehensive test suite in `tests/` directory
152
+ - Dialect-specific tests in `tests/dialects/`
153
+ - Tests are critical - "robust test suite" is a core feature
154
+ - Use `tests/fixtures/` for test data
155
+ - `tests/helpers.py` contains test utilities
156
+
157
+ ### Parser/Generator Symmetry
158
+ - Parser: `token -> Callable` mappings (builds AST from tokens)
159
+ - Generator: `Expression -> str` mappings (builds SQL from AST)
160
+ - Customization follows similar patterns in both
161
+
162
+ ### Type Annotations
163
+ - Type inference is crucial for some transpilations (e.g., `+` can mean addition or concatenation)
164
+ - Optimizer's `annotate_types` rule propagates type information through AST
165
+ - Requires schema information to work effectively
166
+
167
+ ## Common Usage Patterns
168
+
169
+ ### Reading SQL
170
+ ```python
171
+ import sqlglot
172
+ expression = sqlglot.parse_one("SELECT * FROM table", dialect="spark")
173
+ ```
174
+
175
+ ### Validate Function Expression
176
+ ```python
177
+ import sqlglot
178
+ tree = sqlglot.parse_one("SELECT NULLIF(1, 2)", dialect="snowflake")
179
+ if "Anonymous" in repr(tree):
180
+ print("Function expression exists")
181
+ else:
182
+ print("Function expression does not exist")
183
+ ```
184
+
185
+ ### Writing SQL
186
+ ```python
187
+ expression.sql(dialect="duckdb", pretty=True)
188
+ ```
189
+
190
+ ### Building SQL Programmatically
191
+ ```python
192
+ from sqlglot import select, condition
193
+ select("*").from_("y").where(condition("x=1").and_("y=1")).sql()
194
+ ```
195
+
196
+ ### Traversing AST
197
+ ```python
198
+ from sqlglot import parse_one, exp
199
+ tree = parse_one("SELECT a, b + 1 AS c FROM d", dialect="dialect")
200
+ for column in tree.find_all(exp.Column):
201
+ print(column.alias_or_name)
202
+ ```
203
+
204
+ ### Transforming AST
205
+ ```python
206
+ def transformer(node):
207
+ if isinstance(node, exp.Column) and node.name == "a":
208
+ return parse_one("FUN(a)", dialect="dialect")
209
+ return node
210
+
211
+ transformed = tree.transform(transformer)
212
+ ```
213
+
214
+ ## Development Guidelines
215
+
216
+ - Follow [Conventional Commits](https://www.conventionalcommits.org/) for PR titles
217
+ - If you are an agent, you must add your model [CLAUDE, CODEX, etc...] to the end of PR titles and commit messages.
218
+ - Keep PRs minimal in scope - one well-defined change per PR
219
+ - Add tests for non-trivial changes
220
+ - Update docstrings if APIs change
221
+ - Run `make check` before submitting
222
+ - Use comments for complex logic only
223
+ - Don't change license or license files in setup.py, there's nothing wrong with it.
224
+
225
+ ## Important Files
226
+
227
+ - `posts/ast_primer.md`: Detailed AST tutorial
228
+ - `posts/onboarding.md`: Architecture deep-dive (HIGHLY RECOMMENDED)
229
+ - `.pre-commit-config.yaml`: Pre-commit hooks (ruff, ruff-format, mypy)
230
+ - `pyproject.toml`: Project metadata and build config
231
+ - `Makefile`: All development commands
232
+
233
+ ## Performance Considerations
234
+
235
+ - Pure Python implementation with optional mypyc-compiled C extension (`sqlglotc/`)
236
+ - Install with `pip install "sqlglot[c]"` for C extension speed boost
237
+ - The `[c]` extra compiles core modules (`expression_core`, `tokenizer_core`, `parser_core`, etc.) via mypyc
238
+ - Performance is a key feature despite Python implementation
239
+ - Benchmarks compare against other SQL parsers - see `benchmarks/`
240
+ - Avoid use of typing.Protocol, prefer Union Type and Duck Typing
241
+ - Never suggest Pratt parsing as an optimization for the parser. The recursive descent approach is intentional and preferred.
242
+
243
+ ---
244
+
245
+ ## SQLGlot Coding Rules
246
+
247
+ The following patterns are based on PR review feedback. Follow these to minimize review iterations.
248
+
249
+ ### 1. Use Automatic Naming Convention for Generator Methods
250
+
251
+ **Don't do this (module-level function with TRANSFORMS):**
252
+ ```python
253
+ def _my_func_sql(self: MyDialect.Generator, expression: exp.MyFunc) -> str:
254
+ ...
255
+
256
+ class Generator:
257
+ TRANSFORMS = {
258
+ exp.MyFunc: _my_func_sql,
259
+ }
260
+ ```
261
+
262
+ **Don't do this (method with TRANSFORMS):**
263
+ ```python
264
+ class Generator:
265
+ TRANSFORMS = {
266
+ exp.MyFunc: lambda self, e: self._my_func_sql(e),
267
+ }
268
+
269
+ def _my_func_sql(self, expression):
270
+ ...
271
+ ```
272
+
273
+ **Do this (auto-discovered method):**
274
+ ```python
275
+ class Generator:
276
+ # No TRANSFORMS entry needed - automatic discovery by name
277
+
278
+ def myfunc_sql(self, expression: exp.MyFunc) -> str:
279
+ ...
280
+ ```
281
+
282
+ Generator methods named `<lowercase_expr_name>_sql` are automatically discovered.
283
+
284
+ Important: Only use TRANSFORMS for simple one-liners like `rename_func("OTHER_NAME")` or lambdas or functions with multiple entry points. For any single entry point function, always use an auto-discovered method inside the Generator class.
285
+
286
+ SQLGlot automatically applies transformations based on the structure of the name, but when this fails, you must rename the function. This is only when the SQL name is not covered by auto mapping:
287
+
288
+ **Do this:**
289
+ ```python
290
+ class Generator:
291
+ TRANSFORMS = {
292
+ exp.ArrayLength: rename_func("LENGTH"),
293
+ }
294
+ ```
295
+
296
+ **Don't do this:**
297
+ ```python
298
+ exp.ArrayLength: lambda self, e: self.func("LENGTH", e.this),
299
+ ```
300
+
301
+ ### 2. Use Existing Expression Classes, Not Anonymous
302
+
303
+ **Don't do this:**
304
+ ```python
305
+ from_base64 = exp.Anonymous(this="FROM_BASE64", expressions=[input_expr])
306
+ ```
307
+
308
+ **Do this:**
309
+ ```python
310
+ from_base64 = exp.FromBase64(this=input_expr)
311
+ ```
312
+
313
+ Always check if an expression class exists in `expressions.py` before using `exp.Anonymous`. Anonymous should only be used for functions that don't have a dedicated class. Search for the function name in expressions.py first.
314
+
315
+ ### 3. SQL Generation: Choose the Right Approach
316
+
317
+ Use the appropriate method based on complexity. From simplest to most complex:
318
+
319
+ #### Level 1: Generator Helper Methods
320
+ For generating function calls in generator methods, use `self.func()`:
321
+ ```python
322
+ def myfunc_sql(self, expression):
323
+ # Don't: return self.sql(exp.Func(this="MY_FUNC", expressions=[expression.this]))
324
+ # Do:
325
+ return self.func("MY_FUNC", expression.this)
326
+ ```
327
+
328
+ #### Level 2: Expression Builders
329
+ For building expressions, use helper functions instead of direct class construction:
330
+
331
+ | Helper | Instead of | Benefits |
332
+ |--------|-----------|----------|
333
+ | `exp.func("name", *args)` | `exp.Anonymous(...)` | Finds proper Func class |
334
+ | `exp.array(e1, e2, ...)` | `exp.Array(expressions=[...])` | Parses automatically |
335
+ | `exp.and_(e1, e2, ...)` | `exp.And(this=..., expression=...)` | Handles nesting |
336
+ | `exp.or_(e1, e2, ...)` | `exp.Or(this=..., expression=...)` | Handles nesting |
337
+ | `exp.case().when(cond, val).else_(default)` | `exp.Case(ifs=[...])` | Fluent interface |
338
+ | `exp.cast(expr, "TYPE")` | `exp.Cast(this=..., to=...)` | Builds DataType |
339
+ | `exp.column("col", "table")` | `exp.Column(...)` | Handles identifiers |
340
+ | `exp.null()` | `exp.Null()` | Simple factory |
341
+
342
+ Also use expression operators for cleaner code:
343
+ ```python
344
+ # Arithmetic: exp.column("x") + 1 instead of exp.Add(this=..., expression=...)
345
+ # Indexing: arr[index] instead of exp.Bracket(this=arr, expressions=[index])
346
+ # Comparison: arg.is_(exp.Null()) instead of exp.Is(this=arg, expression=exp.Null())
347
+ ```
348
+
349
+ #### Level 3: SQL Templates
350
+ When expressions become complex, use templates with `exp.maybe_parse()` and `exp.replace_placeholders()`:
351
+
352
+ ```python
353
+ # Define template with :placeholder syntax
354
+ MY_TEMPLATE: exp.Expression = exp.maybe_parse(
355
+ "CASE WHEN :arg IS NULL THEN NULL ELSE :result END"
356
+ )
357
+
358
+ # In generator method
359
+ def myfunc_sql(self, expression):
360
+ result = exp.replace_placeholders(
361
+ self.MY_TEMPLATE.copy(),
362
+ arg=expression.this,
363
+ result=some_expression,
364
+ )
365
+ return self.sql(result)
366
+ ```
367
+
368
+ #### Avoid: F-strings with SQL Fragments
369
+ You should rarely, if ever, build SQL with f-strings - it breaks quoting, escaping, and dialect handling:
370
+ ```python
371
+ # NEVER do this:
372
+ def my_func_sql(self, expression):
373
+ return f"CAST({self.sql(expression.this)} AS TIME)"
374
+
375
+ # Do this instead:
376
+ def my_func_sql(self, expression):
377
+ return self.sql(exp.cast(expression.this, "TIME"))
378
+ ```
379
+
380
+ ### 4. Type Checking: `is_string` vs `is_type()`
381
+
382
+ These serve **different purposes**:
383
+
384
+ **`is_type()`** - Semantic type check:
385
+ ```python
386
+ # Returns True if expression's type is text (columns, function results, etc.)
387
+ # Requires annotate_types() to populate type info
388
+ if arg.is_type(*exp.DataType.TEXT_TYPES):
389
+ ...
390
+ ```
391
+
392
+ **`is_string`** - Syntactic check for string literals:
393
+ ```python
394
+ # Returns True only for literal strings like 'hello'
395
+ # Works without type annotation
396
+ if arg.is_string:
397
+ value = arg.name # Extract the string value
398
+ ```
399
+
400
+
401
+ **When to use each:**
402
+
403
+ | Use Case | Method |
404
+ |----------|--------|
405
+ | Check if node is a string literal to extract its value | `is_string` |
406
+ | Check if node is a literal vs column/expression | `is_string` |
407
+ | Check semantic type (works for columns, functions) | `is_type()` |
408
+ | Cover both literals and typed expressions | `is_string or is_type()` |
409
+
410
+ **Combined pattern (from `length_sql`):**
411
+ ```python
412
+ # Fast check for string literals (no annotation needed)
413
+ if arg.is_string:
414
+ return self.func("LENGTH", arg)
415
+
416
+ # For non-literals, get type info if needed
417
+ if not arg.type:
418
+ arg = annotate_types(arg, dialect=self.dialect)
419
+
420
+ # Then check semantic type
421
+ if arg.is_type(*exp.DataType.TEXT_TYPES):
422
+ return self.func("LENGTH", arg)
423
+ ```
424
+
425
+ **Don't do direct type comparisons:**
426
+ ```python
427
+ # Bad
428
+ if input_expr.type and input_expr.type.this in exp.DataType.TEXT_TYPES:
429
+
430
+ # Good
431
+ if input_expr.is_type(*exp.DataType.TEXT_TYPES):
432
+ ```
433
+
434
+ ### 5. Use `to_py()` for Literal Value Extraction
435
+
436
+ **Don't do this:**
437
+ ```python
438
+ if isinstance(arg, exp.Literal):
439
+ value = int(arg.this.strip("'"))
440
+ ```
441
+
442
+ **Do this:**
443
+ ```python
444
+ if isinstance(arg, exp.Literal) and arg.is_number:
445
+ value = int(arg.to_py())
446
+ ```
447
+
448
+ ### 6. Avoid Compile-Time NULL Checks
449
+
450
+ Don't check for `exp.Null()` or literal NULL values in Python during transpilation. NULL handling should happen at query time in the generated SQL using `IS NULL` checks.
451
+
452
+ **Don't do this:**
453
+ ```python
454
+ def myfunc_sql(self, expression):
455
+ # Bad: checking for literal NULL at transpile time
456
+ if any(isinstance(arg, exp.Null) for arg in expression.expressions):
457
+ return self.sql(exp.Null())
458
+ ```
459
+
460
+ **Do this:**
461
+ ```python
462
+ # Good: generate SQL that handles NULL at query time
463
+ TEMPLATE = exp.maybe_parse("CASE WHEN :arg IS NULL THEN NULL ELSE ... END")
464
+ ```
465
+
466
+ Compile-time checks only handle literal `NULL` values in the SQL text, not NULL values that come from columns, parameters, or expressions at runtime. Generate SQL with `IS NULL` checks to handle all cases.
467
+
468
+ ### 7. Type Annotations in Tests
469
+
470
+ When transpilation depends on `is_type()` checks, tests need `annotate_types()`:
471
+
472
+ ```python
473
+ from sqlglot.optimizer import annotate_types
474
+
475
+ # Without annotation - is_type() returns False for literals
476
+ expr = self.validate_identity("SELECT BASE64_ENCODE('Hello World')")
477
+
478
+ # With annotation - types are inferred, is_type() works
479
+ annotated = annotate_types(expr, dialect="snowflake")
480
+ self.assertEqual(annotated.sql("duckdb"), "SELECT TO_BASE64(ENCODE('Hello World'))")
481
+ ```
482
+
483
+ ### 8. Use `find_ancestor` with Scope Boundaries
484
+
485
+ When searching for ancestors, include scope boundaries to avoid crossing into parent queries:
486
+
487
+ ```python
488
+ # Stop at Select to stay within current query scope
489
+ ancestor = expression.find_ancestor(exp.Where, exp.Having, exp.Select)
490
+ if ancestor and not isinstance(ancestor, exp.Select):
491
+ # Found restricted context within current scope
492
+ ...
493
+ ```
494
+
495
+ ### 9. Use @unsupported_args for unsupported arguments
496
+
497
+ When arguments are not supported do this:
498
+
499
+ ```python
500
+ @unsupported_args("ins_cost", "del_cost", "sub_cost")
501
+ def levenshtein_sql(self, expression: exp.Levenshtein) -> str:
502
+ ```
503
+
504
+ ### 10. Keep Code Minimal
505
+
506
+ - Remove unused imports, variables, and dead code
507
+ - Don't add comments for obvious code
508
+ - Don't add docstrings unless the function is complex or public API
509
+ - Prefer inline expressions over intermediate variables when readable
510
+ - Don't add backwards-compatibility shims for removed code
511
+
512
+ ### 11. Test Patterns
513
+
514
+ - Add tests to the appropriate dialect test file (e.g., `tests/dialects/test_snowflake.py`)
515
+ - Use `self.validate_all()` for cross-dialect tests
516
+ - Use `self.validate_identity()` for round-trip tests
517
+ - Don't add tests for functionality that already has coverage
518
+
519
+ ### 12. Ensure Test Validity
520
+
521
+ - Make sure all tests added to tests/dialects/*.py actually run in the relevant databases, such as snowflake or duckdb
@@ -1,6 +1,58 @@
1
1
  Changelog
2
2
  =========
3
3
 
4
+ ## [v30.0.3] - 2026-03-19
5
+ ### :zap: Performance Improvements
6
+ - [`f87ebe0`](https://github.com/tobymao/sqlglot/commit/f87ebe02103b249ec5fa2c93e019e465f77630be) - use mypyc i64 for parser index fields (~1.6% faster) *(commit by [@tobymao](https://github.com/tobymao))*
7
+
8
+ ### :wrench: Chores
9
+ - [`52bca33`](https://github.com/tobymao/sqlglot/commit/52bca33e9395c4f6f621649180f2576eb8591dba) - **lineage**: improve error message when column source index is out of range *(PR [#7336](https://github.com/tobymao/sqlglot/pull/7336) by [@geooo109](https://github.com/geooo109))*
10
+ - :arrow_lower_right: *addresses issue [#7332](https://github.com/tobymao/sqlglot/issues/7332) opened by [@paultiq](https://github.com/paultiq)*
11
+
12
+
13
+ ## [v30.0.2] - 2026-03-19
14
+ ### :boom: BREAKING CHANGES
15
+ - due to [`936617e`](https://github.com/tobymao/sqlglot/commit/936617e749f969b04da318ec02e1086a01212e92) - escape comment markers in sanitize_comment for all dialects *(PR [#7301](https://github.com/tobymao/sqlglot/pull/7301) by [@llimllib](https://github.com/llimllib))*:
16
+
17
+ escape comment markers in sanitize_comment for all dialects (#7301)
18
+
19
+ - due to [`4f6bcd3`](https://github.com/tobymao/sqlglot/commit/4f6bcd3d21cf34346db4c7fc9936302d34a802e2) - Add transpilation support for ARRAY_TO_STRING function *(PR [#7289](https://github.com/tobymao/sqlglot/pull/7289) by [@fivetran-amrutabhimsenayachit](https://github.com/fivetran-amrutabhimsenayachit))*:
20
+
21
+ Add transpilation support for ARRAY_TO_STRING function (#7289)
22
+
23
+
24
+ ### :sparkles: New Features
25
+ - [`4f6bcd3`](https://github.com/tobymao/sqlglot/commit/4f6bcd3d21cf34346db4c7fc9936302d34a802e2) - **duckdb**: Add transpilation support for ARRAY_TO_STRING function *(PR [#7289](https://github.com/tobymao/sqlglot/pull/7289) by [@fivetran-amrutabhimsenayachit](https://github.com/fivetran-amrutabhimsenayachit))*
26
+
27
+ ### :bug: Bug Fixes
28
+ - [`b41a99a`](https://github.com/tobymao/sqlglot/commit/b41a99a1405a5749a5876a64a559bd040ba618a5) - **duckdb**: FROM pipe syntax in subquery *(PR [#7311](https://github.com/tobymao/sqlglot/pull/7311) by [@geooo109](https://github.com/geooo109))*
29
+ - :arrow_lower_right: *fixes issue [#7305](https://github.com/tobymao/sqlglot/issues/7305) opened by [@paultiq](https://github.com/paultiq)*
30
+ - [`936617e`](https://github.com/tobymao/sqlglot/commit/936617e749f969b04da318ec02e1086a01212e92) - escape comment markers in sanitize_comment for all dialects *(PR [#7301](https://github.com/tobymao/sqlglot/pull/7301) by [@llimllib](https://github.com/llimllib))*
31
+ - [`6ddaee3`](https://github.com/tobymao/sqlglot/commit/6ddaee3ccb92f660327501884acf103fad782e07) - **expressions**: restore Expression.alias behaviour for non-Identifier alias nodes *(PR [#7310](https://github.com/tobymao/sqlglot/pull/7310) by [@treff7es](https://github.com/treff7es))*
32
+ - [`ce08047`](https://github.com/tobymao/sqlglot/commit/ce0804717876889c731f2ab64035c70a85f9b294) - **snowflake**: ILIKE/LIKE ANY/ALL with single element transpilation to duckdb *(PR [#7314](https://github.com/tobymao/sqlglot/pull/7314) by [@geooo109](https://github.com/geooo109))*
33
+ - :arrow_lower_right: *fixes issue [#7306](https://github.com/tobymao/sqlglot/issues/7306) opened by [@ultrabear](https://github.com/ultrabear)*
34
+ - [`8a52de6`](https://github.com/tobymao/sqlglot/commit/8a52de63fb908b55df0143e3ec63f3ae37aa4fd8) - **parser**: Add builder for ARRAY_INTERSECT *(PR [#7328](https://github.com/tobymao/sqlglot/pull/7328) by [@VaggelisD](https://github.com/VaggelisD))*
35
+ - :arrow_lower_right: *fixes issue [#7326](https://github.com/tobymao/sqlglot/issues/7326) opened by [@ADBond](https://github.com/ADBond)*
36
+ - [`f8a7ab2`](https://github.com/tobymao/sqlglot/commit/f8a7ab2724cbb75d40355c79c8f68003ee2a5c7e) - **parser**: Do not consume constraints following UNIQUE *(PR [#7330](https://github.com/tobymao/sqlglot/pull/7330) by [@VaggelisD](https://github.com/VaggelisD))*
37
+ - :arrow_lower_right: *fixes issue [#7327](https://github.com/tobymao/sqlglot/issues/7327) opened by [@chunyangfeng](https://github.com/chunyangfeng)*
38
+ - [`64509a2`](https://github.com/tobymao/sqlglot/commit/64509a2a3e7606eab57d714e63424f235c78a6b5) - sqlglotc sdist install fails when ../sqlglot dir doesn't exist *(PR [#7337](https://github.com/tobymao/sqlglot/pull/7337) by [@tobymao](https://github.com/tobymao))*
39
+ - :arrow_lower_right: *fixes issue [#7333](https://github.com/tobymao/sqlglot/issues/7333) opened by [@ZipBrandon](https://github.com/ZipBrandon)*
40
+
41
+ ### :zap: Performance Improvements
42
+ - [`0ac52aa`](https://github.com/tobymao/sqlglot/commit/0ac52aa80241a8ed4049948f2ee0c19c8dc64279) - move instance variables to __init__ for perf *(commit by [@tobymao](https://github.com/tobymao))*
43
+ - [`c95ae50`](https://github.com/tobymao/sqlglot/commit/c95ae50fa122e01c914c92e9d847ff390516e1a4) - optimize parser for nested function calls (-41% on nested_funct… *(PR [#7307](https://github.com/tobymao/sqlglot/pull/7307) by [@tobymao](https://github.com/tobymao))*
44
+ - [`1697bc3`](https://github.com/tobymao/sqlglot/commit/1697bc3a67c10314c5640ed7d52c40063db70f10) - optimize parser fast path for simple table references *(commit by [@tobymao](https://github.com/tobymao))*
45
+ - [`18f15ca`](https://github.com/tobymao/sqlglot/commit/18f15ca96c7bdebe5798e407993495c0a9e9bd43) - inline token parsing for massive gains *(PR [#7335](https://github.com/tobymao/sqlglot/pull/7335) by [@tobymao](https://github.com/tobymao))*
46
+
47
+ ### :wrench: Chores
48
+ - [`fe7874f`](https://github.com/tobymao/sqlglot/commit/fe7874f3cb5dd7899a438249f65b51b684a056f6) - update changelog with v30 release notes *(PR [#7309](https://github.com/tobymao/sqlglot/pull/7309) by [@georgesittas](https://github.com/georgesittas))*
49
+ - [`8a914f0`](https://github.com/tobymao/sqlglot/commit/8a914f09ed3534ecc9998b6ce84f74e2f15909e4) - use ProcessPoolExecutor in test_executor *(PR [#7313](https://github.com/tobymao/sqlglot/pull/7313) by [@georgesittas](https://github.com/georgesittas))*
50
+ - [`604fe3f`](https://github.com/tobymao/sqlglot/commit/604fe3f1770715baf8b461344cffa44eaf017fc2) - improve integration test submodule automations *(PR [#7320](https://github.com/tobymao/sqlglot/pull/7320) by [@georgesittas](https://github.com/georgesittas))*
51
+ - [`c7b55c1`](https://github.com/tobymao/sqlglot/commit/c7b55c1998e7aaea7a4950e8ee347db7b0ec4af3) - Fix uv sync failing for sqlglotc *(PR [#7322](https://github.com/tobymao/sqlglot/pull/7322) by [@VaggelisD](https://github.com/VaggelisD))*
52
+ - :arrow_lower_right: *addresses issue [#7321](https://github.com/tobymao/sqlglot/issues/7321) opened by [@OutSquareCapital](https://github.com/OutSquareCapital)*
53
+ - [`158424c`](https://github.com/tobymao/sqlglot/commit/158424cda0cc908c9f3687cc466194fa31cafb1b) - update readme *(commit by [@tobymao](https://github.com/tobymao))*
54
+
55
+
4
56
  ## [v30.0.1] - 2026-03-16
5
57
  ### :bug: Bug Fixes
6
58
  - [`7bcad5c`](https://github.com/tobymao/sqlglot/commit/7bcad5c61673a8567e9c22d059ed4326e6698457) - auto pin sqlglotc version to sqlglot so there's no version mismatch closes [#7304](https://github.com/tobymao/sqlglot/pull/7304) *(commit by [@tobymao](https://github.com/tobymao))*
@@ -13801,3 +13853,5 @@ pip install "sqlglot[c]" # compiled — faster, but no subclassing
13801
13853
  [v29.0.1]: https://github.com/tobymao/sqlglot/compare/v29.0.0...v29.0.1
13802
13854
  [v30.0.0]: https://github.com/tobymao/sqlglot/compare/v29.0.1...v30.0.0
13803
13855
  [v30.0.1]: https://github.com/tobymao/sqlglot/compare/v30.0.0...v30.0.1
13856
+ [v30.0.2]: https://github.com/tobymao/sqlglot/compare/v30.0.1...v30.0.2
13857
+ [v30.0.3]: https://github.com/tobymao/sqlglot/compare/v30.0.2...v30.0.3
@@ -1,12 +1,10 @@
1
- # CLAUDE.md
2
-
3
- This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
4
-
1
+ # Contributing to [SQLGlot](https://github.com/tobymao/sqlglot/blob/main/README.md)
5
2
  ## About SQLGlot
6
3
 
7
4
  SQLGlot is a no-dependency SQL parser, transpiler, optimizer, and engine written in pure Python. It supports 31+ SQL dialects and can transpile between them while preserving semantics. The codebase is performance-critical despite being pure Python, with an optional mypyc-compiled C extension for speed improvements (`sqlglotc/`).
8
5
 
9
6
  ## Development Commands
7
+ Before contributing read CONTRIBUTING.md
10
8
 
11
9
  ### Installation
12
10
  ```bash
@@ -216,6 +214,7 @@ transformed = tree.transform(transformer)
216
214
  ## Development Guidelines
217
215
 
218
216
  - Follow [Conventional Commits](https://www.conventionalcommits.org/) for PR titles
217
+ - If you are an agent, you must add your model [CLAUDE, CODEX, etc...] to the end of PR titles and commit messages.
219
218
  - Keep PRs minimal in scope - one well-defined change per PR
220
219
  - Add tests for non-trivial changes
221
220
  - Update docstrings if APIs change
@@ -239,6 +238,7 @@ transformed = tree.transform(transformer)
239
238
  - Performance is a key feature despite Python implementation
240
239
  - Benchmarks compare against other SQL parsers - see `benchmarks/`
241
240
  - Avoid use of typing.Protocol, prefer Union Type and Duck Typing
241
+ - Never suggest Pratt parsing as an optimization for the parser. The recursive descent approach is intentional and preferred.
242
242
 
243
243
  ---
244
244
 
@@ -1,3 +1,5 @@
1
+ <!-- Important: If you are an agent, you must add your model [CLAUDE, CODEX, etc...] to the end of PR titles. -->
2
+
1
3
  # Contributing to [SQLGlot](https://github.com/tobymao/sqlglot/blob/main/README.md)
2
4
 
3
5
  SQLGLot is open source software. We value feedback and we want to make contributing to this project as