sqlmesh 0.213.1.dev1__py3-none-any.whl → 0.227.2.dev4__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (252) hide show
  1. sqlmesh/__init__.py +12 -2
  2. sqlmesh/_version.py +2 -2
  3. sqlmesh/cli/main.py +0 -44
  4. sqlmesh/cli/project_init.py +11 -2
  5. sqlmesh/core/_typing.py +1 -0
  6. sqlmesh/core/audit/definition.py +8 -2
  7. sqlmesh/core/config/__init__.py +1 -1
  8. sqlmesh/core/config/connection.py +17 -5
  9. sqlmesh/core/config/dbt.py +13 -0
  10. sqlmesh/core/config/janitor.py +12 -0
  11. sqlmesh/core/config/loader.py +7 -0
  12. sqlmesh/core/config/model.py +2 -0
  13. sqlmesh/core/config/root.py +3 -0
  14. sqlmesh/core/console.py +81 -3
  15. sqlmesh/core/constants.py +1 -1
  16. sqlmesh/core/context.py +69 -26
  17. sqlmesh/core/dialect.py +3 -0
  18. sqlmesh/core/engine_adapter/_typing.py +2 -0
  19. sqlmesh/core/engine_adapter/base.py +322 -22
  20. sqlmesh/core/engine_adapter/base_postgres.py +17 -1
  21. sqlmesh/core/engine_adapter/bigquery.py +146 -7
  22. sqlmesh/core/engine_adapter/clickhouse.py +17 -13
  23. sqlmesh/core/engine_adapter/databricks.py +33 -2
  24. sqlmesh/core/engine_adapter/fabric.py +10 -29
  25. sqlmesh/core/engine_adapter/mixins.py +142 -48
  26. sqlmesh/core/engine_adapter/mssql.py +15 -4
  27. sqlmesh/core/engine_adapter/mysql.py +2 -2
  28. sqlmesh/core/engine_adapter/postgres.py +9 -3
  29. sqlmesh/core/engine_adapter/redshift.py +4 -0
  30. sqlmesh/core/engine_adapter/risingwave.py +1 -0
  31. sqlmesh/core/engine_adapter/shared.py +6 -0
  32. sqlmesh/core/engine_adapter/snowflake.py +82 -11
  33. sqlmesh/core/engine_adapter/spark.py +14 -10
  34. sqlmesh/core/engine_adapter/trino.py +4 -2
  35. sqlmesh/core/environment.py +2 -0
  36. sqlmesh/core/janitor.py +181 -0
  37. sqlmesh/core/lineage.py +1 -0
  38. sqlmesh/core/linter/definition.py +13 -13
  39. sqlmesh/core/linter/rules/builtin.py +29 -0
  40. sqlmesh/core/macros.py +35 -13
  41. sqlmesh/core/model/common.py +2 -0
  42. sqlmesh/core/model/definition.py +82 -28
  43. sqlmesh/core/model/kind.py +66 -2
  44. sqlmesh/core/model/meta.py +108 -4
  45. sqlmesh/core/node.py +101 -1
  46. sqlmesh/core/plan/builder.py +18 -10
  47. sqlmesh/core/plan/common.py +199 -2
  48. sqlmesh/core/plan/definition.py +25 -6
  49. sqlmesh/core/plan/evaluator.py +75 -113
  50. sqlmesh/core/plan/explainer.py +90 -8
  51. sqlmesh/core/plan/stages.py +42 -21
  52. sqlmesh/core/renderer.py +78 -32
  53. sqlmesh/core/scheduler.py +102 -22
  54. sqlmesh/core/selector.py +137 -9
  55. sqlmesh/core/signal.py +64 -1
  56. sqlmesh/core/snapshot/__init__.py +2 -0
  57. sqlmesh/core/snapshot/definition.py +146 -34
  58. sqlmesh/core/snapshot/evaluator.py +689 -124
  59. sqlmesh/core/state_sync/__init__.py +0 -1
  60. sqlmesh/core/state_sync/base.py +55 -33
  61. sqlmesh/core/state_sync/cache.py +12 -7
  62. sqlmesh/core/state_sync/common.py +216 -111
  63. sqlmesh/core/state_sync/db/environment.py +6 -4
  64. sqlmesh/core/state_sync/db/facade.py +42 -24
  65. sqlmesh/core/state_sync/db/interval.py +27 -7
  66. sqlmesh/core/state_sync/db/migrator.py +34 -16
  67. sqlmesh/core/state_sync/db/snapshot.py +177 -169
  68. sqlmesh/core/table_diff.py +2 -2
  69. sqlmesh/core/test/context.py +2 -0
  70. sqlmesh/core/test/definition.py +14 -9
  71. sqlmesh/dbt/adapter.py +22 -16
  72. sqlmesh/dbt/basemodel.py +75 -56
  73. sqlmesh/dbt/builtin.py +116 -12
  74. sqlmesh/dbt/column.py +17 -5
  75. sqlmesh/dbt/common.py +19 -5
  76. sqlmesh/dbt/context.py +14 -1
  77. sqlmesh/dbt/loader.py +61 -9
  78. sqlmesh/dbt/manifest.py +174 -16
  79. sqlmesh/dbt/model.py +183 -85
  80. sqlmesh/dbt/package.py +16 -1
  81. sqlmesh/dbt/profile.py +3 -3
  82. sqlmesh/dbt/project.py +12 -7
  83. sqlmesh/dbt/seed.py +6 -1
  84. sqlmesh/dbt/source.py +13 -1
  85. sqlmesh/dbt/target.py +25 -6
  86. sqlmesh/dbt/test.py +36 -5
  87. sqlmesh/migrations/v0000_baseline.py +95 -0
  88. sqlmesh/migrations/v0061_mysql_fix_blob_text_type.py +5 -7
  89. sqlmesh/migrations/v0062_add_model_gateway.py +5 -1
  90. sqlmesh/migrations/v0063_change_signals.py +5 -3
  91. sqlmesh/migrations/v0064_join_when_matched_strings.py +5 -3
  92. sqlmesh/migrations/v0065_add_model_optimize.py +5 -1
  93. sqlmesh/migrations/v0066_add_auto_restatements.py +8 -3
  94. sqlmesh/migrations/v0067_add_tsql_date_full_precision.py +5 -1
  95. sqlmesh/migrations/v0068_include_unrendered_query_in_metadata_hash.py +5 -1
  96. sqlmesh/migrations/v0069_update_dev_table_suffix.py +5 -3
  97. sqlmesh/migrations/v0070_include_grains_in_metadata_hash.py +5 -1
  98. sqlmesh/migrations/v0071_add_dev_version_to_intervals.py +9 -5
  99. sqlmesh/migrations/v0072_add_environment_statements.py +5 -3
  100. sqlmesh/migrations/v0073_remove_symbolic_disable_restatement.py +5 -3
  101. sqlmesh/migrations/v0074_add_partition_by_time_column_property.py +5 -1
  102. sqlmesh/migrations/v0075_remove_validate_query.py +5 -3
  103. sqlmesh/migrations/v0076_add_cron_tz.py +5 -1
  104. sqlmesh/migrations/v0077_fix_column_type_hash_calculation.py +5 -1
  105. sqlmesh/migrations/v0078_warn_if_non_migratable_python_env.py +5 -3
  106. sqlmesh/migrations/v0079_add_gateway_managed_property.py +10 -5
  107. sqlmesh/migrations/v0080_add_batch_size_to_scd_type_2_models.py +5 -1
  108. sqlmesh/migrations/v0081_update_partitioned_by.py +5 -3
  109. sqlmesh/migrations/v0082_warn_if_incorrectly_duplicated_statements.py +5 -3
  110. sqlmesh/migrations/v0083_use_sql_for_scd_time_data_type_data_hash.py +5 -1
  111. sqlmesh/migrations/v0084_normalize_quote_when_matched_and_merge_filter.py +5 -1
  112. sqlmesh/migrations/v0085_deterministic_repr.py +5 -3
  113. sqlmesh/migrations/v0086_check_deterministic_bug.py +5 -3
  114. sqlmesh/migrations/v0087_normalize_blueprint_variables.py +5 -3
  115. sqlmesh/migrations/v0088_warn_about_variable_python_env_diffs.py +5 -3
  116. sqlmesh/migrations/v0089_add_virtual_environment_mode.py +5 -1
  117. sqlmesh/migrations/v0090_add_forward_only_column.py +9 -5
  118. sqlmesh/migrations/v0091_on_additive_change.py +5 -1
  119. sqlmesh/migrations/v0092_warn_about_dbt_data_type_diff.py +5 -3
  120. sqlmesh/migrations/v0093_use_raw_sql_in_fingerprint.py +5 -1
  121. sqlmesh/migrations/v0094_add_dev_version_and_fingerprint_columns.py +123 -0
  122. sqlmesh/migrations/v0095_warn_about_dbt_raw_sql_diff.py +49 -0
  123. sqlmesh/migrations/v0096_remove_plan_dags_table.py +13 -0
  124. sqlmesh/migrations/v0097_add_dbt_name_in_node.py +9 -0
  125. sqlmesh/migrations/{v0060_move_audits_to_model.py → v0098_add_dbt_node_info_in_node.py} +33 -16
  126. sqlmesh/migrations/v0099_add_last_altered_to_intervals.py +25 -0
  127. sqlmesh/migrations/v0100_add_grants_and_grants_target_layer.py +9 -0
  128. sqlmesh/utils/__init__.py +8 -1
  129. sqlmesh/utils/cache.py +5 -1
  130. sqlmesh/utils/connection_pool.py +2 -1
  131. sqlmesh/utils/dag.py +65 -10
  132. sqlmesh/utils/date.py +8 -1
  133. sqlmesh/utils/errors.py +8 -0
  134. sqlmesh/utils/jinja.py +54 -4
  135. sqlmesh/utils/pydantic.py +6 -6
  136. sqlmesh/utils/windows.py +13 -3
  137. {sqlmesh-0.213.1.dev1.dist-info → sqlmesh-0.227.2.dev4.dist-info}/METADATA +7 -10
  138. sqlmesh-0.227.2.dev4.dist-info/RECORD +370 -0
  139. sqlmesh_dbt/cli.py +70 -7
  140. sqlmesh_dbt/console.py +14 -6
  141. sqlmesh_dbt/operations.py +103 -24
  142. sqlmesh_dbt/selectors.py +39 -1
  143. web/client/dist/assets/{Audits-Ucsx1GzF.js → Audits-CBiYyyx-.js} +1 -1
  144. web/client/dist/assets/{Banner-BWDzvavM.js → Banner-DSRbUlO5.js} +1 -1
  145. web/client/dist/assets/{ChevronDownIcon-D2VL13Ah.js → ChevronDownIcon-MK_nrjD_.js} +1 -1
  146. web/client/dist/assets/{ChevronRightIcon-DWGYbf1l.js → ChevronRightIcon-CLWtT22Q.js} +1 -1
  147. web/client/dist/assets/{Content-DdHDZM3I.js → Content-BNuGZN5l.js} +1 -1
  148. web/client/dist/assets/{Content-Bikfy8fh.js → Content-CSHJyW0n.js} +1 -1
  149. web/client/dist/assets/{Data-CzAJH7rW.js → Data-C1oRDbLx.js} +1 -1
  150. web/client/dist/assets/{DataCatalog-BJF11g8f.js → DataCatalog-HXyX2-_j.js} +1 -1
  151. web/client/dist/assets/{Editor-s0SBpV2y.js → Editor-BDyfpUuw.js} +1 -1
  152. web/client/dist/assets/{Editor-DgLhgKnm.js → Editor-D0jNItwC.js} +1 -1
  153. web/client/dist/assets/{Errors-D0m0O1d3.js → Errors-BfuFLcPi.js} +1 -1
  154. web/client/dist/assets/{FileExplorer-CEv0vXkt.js → FileExplorer-BR9IE3he.js} +1 -1
  155. web/client/dist/assets/{Footer-BwzXn8Ew.js → Footer-CgBEtiAh.js} +1 -1
  156. web/client/dist/assets/{Header-6heDkEqG.js → Header-DSqR6nSO.js} +1 -1
  157. web/client/dist/assets/{Input-obuJsD6k.js → Input-B-oZ6fGO.js} +1 -1
  158. web/client/dist/assets/Lineage-DYQVwDbD.js +1 -0
  159. web/client/dist/assets/{ListboxShow-HM9_qyrt.js → ListboxShow-BE5-xevs.js} +1 -1
  160. web/client/dist/assets/{ModelLineage-zWdKo0U2.js → ModelLineage-DkIFAYo4.js} +1 -1
  161. web/client/dist/assets/{Models-Bcu66SRz.js → Models-D5dWr8RB.js} +1 -1
  162. web/client/dist/assets/{Page-BWEEQfIt.js → Page-C-XfU5BR.js} +1 -1
  163. web/client/dist/assets/{Plan-C4gXCqlf.js → Plan-ZEuTINBq.js} +1 -1
  164. web/client/dist/assets/{PlusCircleIcon-CVDO651q.js → PlusCircleIcon-DVXAHG8_.js} +1 -1
  165. web/client/dist/assets/{ReportErrors-BT6xFwAr.js → ReportErrors-B7FEPzMB.js} +1 -1
  166. web/client/dist/assets/{Root-ryJoBK4h.js → Root-8aZyhPxF.js} +1 -1
  167. web/client/dist/assets/{SearchList-DB04sPb9.js → SearchList-W_iT2G82.js} +1 -1
  168. web/client/dist/assets/{SelectEnvironment-CUYcXUu6.js → SelectEnvironment-C65jALmO.js} +1 -1
  169. web/client/dist/assets/{SourceList-Doo_9ZGp.js → SourceList-DSLO6nVJ.js} +1 -1
  170. web/client/dist/assets/{SourceListItem-D5Mj7Dly.js → SourceListItem-BHt8d9-I.js} +1 -1
  171. web/client/dist/assets/{SplitPane-qHmkD1qy.js → SplitPane-CViaZmw6.js} +1 -1
  172. web/client/dist/assets/{Tests-DH1Z74ML.js → Tests-DhaVt5t1.js} +1 -1
  173. web/client/dist/assets/{Welcome-DqUJUNMF.js → Welcome-DvpjH-_4.js} +1 -1
  174. web/client/dist/assets/context-BctCsyGb.js +71 -0
  175. web/client/dist/assets/{context-Dr54UHLi.js → context-DFNeGsFF.js} +1 -1
  176. web/client/dist/assets/{editor-DYIP1yQ4.js → editor-CcO28cqd.js} +1 -1
  177. web/client/dist/assets/{file-DarlIDVi.js → file-CvJN3aZO.js} +1 -1
  178. web/client/dist/assets/{floating-ui.react-dom-BH3TFvkM.js → floating-ui.react-dom-CjE-JNW1.js} +1 -1
  179. web/client/dist/assets/{help-Bl8wqaQc.js → help-DuPhjipa.js} +1 -1
  180. web/client/dist/assets/{index-D1sR7wpN.js → index-C-dJH7yZ.js} +1 -1
  181. web/client/dist/assets/{index-O3mjYpnE.js → index-Dj0i1-CA.js} +2 -2
  182. web/client/dist/assets/{plan-CehRrJUG.js → plan-BTRSbjKn.js} +1 -1
  183. web/client/dist/assets/{popover-CqgMRE0G.js → popover-_Sf0yvOI.js} +1 -1
  184. web/client/dist/assets/{project-6gxepOhm.js → project-BvSOI8MY.js} +1 -1
  185. web/client/dist/index.html +1 -1
  186. sqlmesh/integrations/llm.py +0 -56
  187. sqlmesh/migrations/v0001_init.py +0 -60
  188. sqlmesh/migrations/v0002_remove_identify.py +0 -5
  189. sqlmesh/migrations/v0003_move_batch_size.py +0 -34
  190. sqlmesh/migrations/v0004_environmnent_add_finalized_at.py +0 -23
  191. sqlmesh/migrations/v0005_create_seed_table.py +0 -24
  192. sqlmesh/migrations/v0006_change_seed_hash.py +0 -5
  193. sqlmesh/migrations/v0007_env_table_info_to_kind.py +0 -99
  194. sqlmesh/migrations/v0008_create_intervals_table.py +0 -38
  195. sqlmesh/migrations/v0009_remove_pre_post_hooks.py +0 -62
  196. sqlmesh/migrations/v0010_seed_hash_batch_size.py +0 -5
  197. sqlmesh/migrations/v0011_add_model_kind_name.py +0 -63
  198. sqlmesh/migrations/v0012_update_jinja_expressions.py +0 -86
  199. sqlmesh/migrations/v0013_serde_using_model_dialects.py +0 -87
  200. sqlmesh/migrations/v0014_fix_dev_intervals.py +0 -14
  201. sqlmesh/migrations/v0015_environment_add_promoted_snapshot_ids.py +0 -26
  202. sqlmesh/migrations/v0016_fix_windows_path.py +0 -59
  203. sqlmesh/migrations/v0017_fix_windows_seed_path.py +0 -55
  204. sqlmesh/migrations/v0018_rename_snapshot_model_to_node.py +0 -53
  205. sqlmesh/migrations/v0019_add_env_suffix_target.py +0 -28
  206. sqlmesh/migrations/v0020_remove_redundant_attributes_from_dbt_models.py +0 -80
  207. sqlmesh/migrations/v0021_fix_table_properties.py +0 -62
  208. sqlmesh/migrations/v0022_move_project_to_model.py +0 -54
  209. sqlmesh/migrations/v0023_fix_added_models_with_forward_only_parents.py +0 -65
  210. sqlmesh/migrations/v0024_replace_model_kind_name_enum_with_value.py +0 -55
  211. sqlmesh/migrations/v0025_fix_intervals_and_missing_change_category.py +0 -117
  212. sqlmesh/migrations/v0026_remove_dialect_from_seed.py +0 -55
  213. sqlmesh/migrations/v0027_minute_interval_to_five.py +0 -57
  214. sqlmesh/migrations/v0028_add_plan_dags_table.py +0 -29
  215. sqlmesh/migrations/v0029_generate_schema_types_using_dialect.py +0 -69
  216. sqlmesh/migrations/v0030_update_unrestorable_snapshots.py +0 -65
  217. sqlmesh/migrations/v0031_remove_dbt_target_fields.py +0 -65
  218. sqlmesh/migrations/v0032_add_sqlmesh_version.py +0 -25
  219. sqlmesh/migrations/v0033_mysql_fix_blob_text_type.py +0 -45
  220. sqlmesh/migrations/v0034_add_default_catalog.py +0 -367
  221. sqlmesh/migrations/v0035_add_catalog_name_override.py +0 -22
  222. sqlmesh/migrations/v0036_delete_plan_dags_bug_fix.py +0 -14
  223. sqlmesh/migrations/v0037_remove_dbt_is_incremental_macro.py +0 -61
  224. sqlmesh/migrations/v0038_add_expiration_ts_to_snapshot.py +0 -73
  225. sqlmesh/migrations/v0039_include_environment_in_plan_dag_spec.py +0 -68
  226. sqlmesh/migrations/v0040_add_previous_finalized_snapshots.py +0 -26
  227. sqlmesh/migrations/v0041_remove_hash_raw_query_attribute.py +0 -59
  228. sqlmesh/migrations/v0042_trim_indirect_versions.py +0 -66
  229. sqlmesh/migrations/v0043_fix_remove_obsolete_attributes_in_plan_dags.py +0 -61
  230. sqlmesh/migrations/v0044_quote_identifiers_in_model_attributes.py +0 -5
  231. sqlmesh/migrations/v0045_move_gateway_variable.py +0 -70
  232. sqlmesh/migrations/v0046_add_batch_concurrency.py +0 -8
  233. sqlmesh/migrations/v0047_change_scd_string_to_column.py +0 -5
  234. sqlmesh/migrations/v0048_drop_indirect_versions.py +0 -59
  235. sqlmesh/migrations/v0049_replace_identifier_with_version_in_seeds_table.py +0 -57
  236. sqlmesh/migrations/v0050_drop_seeds_table.py +0 -11
  237. sqlmesh/migrations/v0051_rename_column_descriptions.py +0 -65
  238. sqlmesh/migrations/v0052_add_normalize_name_in_environment_naming_info.py +0 -28
  239. sqlmesh/migrations/v0053_custom_model_kind_extra_attributes.py +0 -5
  240. sqlmesh/migrations/v0054_fix_trailing_comments.py +0 -5
  241. sqlmesh/migrations/v0055_add_updated_ts_unpaused_ts_ttl_ms_unrestorable_to_snapshot.py +0 -132
  242. sqlmesh/migrations/v0056_restore_table_indexes.py +0 -118
  243. sqlmesh/migrations/v0057_add_table_format.py +0 -5
  244. sqlmesh/migrations/v0058_add_requirements.py +0 -26
  245. sqlmesh/migrations/v0059_add_physical_version.py +0 -5
  246. sqlmesh-0.213.1.dev1.dist-info/RECORD +0 -421
  247. web/client/dist/assets/Lineage-D0Hgdz2v.js +0 -1
  248. web/client/dist/assets/context-DgX0fp2E.js +0 -68
  249. {sqlmesh-0.213.1.dev1.dist-info → sqlmesh-0.227.2.dev4.dist-info}/WHEEL +0 -0
  250. {sqlmesh-0.213.1.dev1.dist-info → sqlmesh-0.227.2.dev4.dist-info}/entry_points.txt +0 -0
  251. {sqlmesh-0.213.1.dev1.dist-info → sqlmesh-0.227.2.dev4.dist-info}/licenses/LICENSE +0 -0
  252. {sqlmesh-0.213.1.dev1.dist-info → sqlmesh-0.227.2.dev4.dist-info}/top_level.txt +0 -0
sqlmesh/__init__.py CHANGED
@@ -126,6 +126,8 @@ def is_cicd_environment() -> bool:
126
126
 
127
127
 
128
128
  def is_interactive_environment() -> bool:
129
+ if sys.stdin is None or sys.stdout is None:
130
+ return False
129
131
  return sys.stdin.isatty() and sys.stdout.isatty()
130
132
 
131
133
 
@@ -186,6 +188,7 @@ def configure_logging(
186
188
  write_to_file: bool = True,
187
189
  log_file_dir: t.Optional[t.Union[str, Path]] = None,
188
190
  ignore_warnings: bool = False,
191
+ log_level: t.Optional[t.Union[str, int]] = None,
189
192
  ) -> None:
190
193
  # Remove noisy grpc logs that are not useful for users
191
194
  os.environ["GRPC_VERBOSITY"] = os.environ.get("GRPC_VERBOSITY", "NONE")
@@ -193,8 +196,15 @@ def configure_logging(
193
196
  logger = logging.getLogger()
194
197
  debug = force_debug or debug_mode_enabled()
195
198
 
196
- # base logger needs to be the lowest level that we plan to log
197
- level = logging.DEBUG if debug else logging.INFO
199
+ if log_level is not None:
200
+ if isinstance(log_level, str):
201
+ level = logging._nameToLevel.get(log_level.upper()) or logging.INFO
202
+ else:
203
+ level = log_level
204
+ else:
205
+ # base logger needs to be the lowest level that we plan to log
206
+ level = logging.DEBUG if debug else logging.INFO
207
+
198
208
  logger.setLevel(level)
199
209
 
200
210
  if debug:
sqlmesh/_version.py CHANGED
@@ -28,7 +28,7 @@ version_tuple: VERSION_TUPLE
28
28
  commit_id: COMMIT_ID
29
29
  __commit_id__: COMMIT_ID
30
30
 
31
- __version__ = version = '0.213.1.dev1'
32
- __version_tuple__ = version_tuple = (0, 213, 1, 'dev1')
31
+ __version__ = version = '0.227.2.dev4'
32
+ __version_tuple__ = version_tuple = (0, 227, 2, 'dev4')
33
33
 
34
34
  __commit_id__ = commit_id = None
sqlmesh/cli/main.py CHANGED
@@ -1079,50 +1079,6 @@ def rewrite(obj: Context, sql: str, read: str = "", write: str = "") -> None:
1079
1079
  )
1080
1080
 
1081
1081
 
1082
- @cli.command("prompt")
1083
- @click.argument("prompt")
1084
- @click.option(
1085
- "-e",
1086
- "--evaluate",
1087
- is_flag=True,
1088
- help="Evaluate the generated SQL query and display the results.",
1089
- )
1090
- @click.option(
1091
- "-t",
1092
- "--temperature",
1093
- type=float,
1094
- help="Sampling temperature. 0.0 - precise and predictable, 0.5 - balanced, 1.0 - creative. Default: 0.7",
1095
- default=0.7,
1096
- )
1097
- @opt.verbose
1098
- @click.pass_context
1099
- @error_handler
1100
- @cli_analytics
1101
- def prompt(
1102
- ctx: click.Context,
1103
- prompt: str,
1104
- evaluate: bool,
1105
- temperature: float,
1106
- verbose: int,
1107
- ) -> None:
1108
- """Uses LLM to generate a SQL query from a prompt."""
1109
- from sqlmesh.integrations.llm import LLMIntegration
1110
-
1111
- context = ctx.obj
1112
-
1113
- llm_integration = LLMIntegration(
1114
- context.models.values(),
1115
- context.engine_adapter.dialect,
1116
- temperature=temperature,
1117
- verbosity=Verbosity(verbose),
1118
- )
1119
- query = llm_integration.query(prompt)
1120
-
1121
- context.console.log_status_update(query)
1122
- if evaluate:
1123
- context.console.log_success(context.fetchdf(query))
1124
-
1125
-
1126
1082
  @cli.command("clean")
1127
1083
  @click.pass_obj
1128
1084
  @error_handler
@@ -114,8 +114,17 @@ linter:
114
114
  rules:
115
115
  - ambiguousorinvalidcolumn
116
116
  - invalidselectstarexpansion
117
+ - noambiguousprojections
117
118
  """,
118
- ProjectTemplate.DBT: f"""# --- Virtual Data Environment Mode ---
119
+ ProjectTemplate.DBT: f"""# --- DBT-specific options ---
120
+ dbt:
121
+ # This configuration ensures that each dbt target gets its own isolated state.
122
+ # The inferred state schemas are named "sqlmesh_state_<profile name>_<target schema>", eg "sqlmesh_state_jaffle_shop_dev"
123
+ # If this is undesirable, you may manually configure the gateway to use a specific state schema name
124
+ # https://sqlmesh.readthedocs.io/en/stable/integrations/dbt/#selecting-a-different-state-connection
125
+ infer_state_schema_name: True
126
+
127
+ # --- Virtual Data Environment Mode ---
119
128
  # Enable Virtual Data Environments (VDE) for *development* environments.
120
129
  # Note that the production environment in dbt projects is not virtual by default to maintain compatibility with existing tooling.
121
130
  # https://sqlmesh.readthedocs.io/en/stable/guides/configuration/#virtual-data-environment-modes
@@ -297,6 +306,7 @@ def init_example_project(
297
306
  dlt_path: t.Optional[str] = None,
298
307
  schema_name: str = "sqlmesh_example",
299
308
  cli_mode: InitCliMode = InitCliMode.DEFAULT,
309
+ start: t.Optional[str] = None,
300
310
  ) -> Path:
301
311
  root_path = Path(path)
302
312
 
@@ -335,7 +345,6 @@ def init_example_project(
335
345
 
336
346
  models: t.Set[t.Tuple[str, str]] = set()
337
347
  settings = None
338
- start = None
339
348
  if engine_type and template == ProjectTemplate.DLT:
340
349
  project_dialect = dialect or DIALECT_TO_TYPE.get(engine_type)
341
350
  if pipeline and project_dialect:
sqlmesh/core/_typing.py CHANGED
@@ -11,6 +11,7 @@ if t.TYPE_CHECKING:
11
11
  SessionProperties = t.Dict[str, t.Union[exp.Expression, str, int, float, bool]]
12
12
  CustomMaterializationProperties = t.Dict[str, t.Union[exp.Expression, str, int, float, bool]]
13
13
 
14
+
14
15
  if sys.version_info >= (3, 11):
15
16
  from typing import Self as Self
16
17
  else:
@@ -19,7 +19,7 @@ from sqlmesh.core.model.common import (
19
19
  sorted_python_env_payloads,
20
20
  )
21
21
  from sqlmesh.core.model.common import make_python_env, single_value_or_tuple, ParsableSql
22
- from sqlmesh.core.node import _Node
22
+ from sqlmesh.core.node import _Node, DbtInfoMixin, DbtNodeInfo
23
23
  from sqlmesh.core.renderer import QueryRenderer
24
24
  from sqlmesh.utils.date import TimeLike
25
25
  from sqlmesh.utils.errors import AuditConfigError, SQLMeshError, raise_config_error
@@ -120,7 +120,7 @@ def audit_map_validator(cls: t.Type, v: t.Any, values: t.Any) -> t.Dict[str, t.A
120
120
  return {}
121
121
 
122
122
 
123
- class ModelAudit(PydanticModel, AuditMixin, frozen=True):
123
+ class ModelAudit(PydanticModel, AuditMixin, DbtInfoMixin, frozen=True):
124
124
  """
125
125
  Audit is an assertion made about your tables.
126
126
 
@@ -137,6 +137,7 @@ class ModelAudit(PydanticModel, AuditMixin, frozen=True):
137
137
  expressions_: t.Optional[t.List[ParsableSql]] = Field(default=None, alias="expressions")
138
138
  jinja_macros: JinjaMacroRegistry = JinjaMacroRegistry()
139
139
  formatting: t.Optional[bool] = Field(default=None, exclude=True)
140
+ dbt_node_info_: t.Optional[DbtNodeInfo] = Field(alias="dbt_node_info", default=None)
140
141
 
141
142
  _path: t.Optional[Path] = None
142
143
 
@@ -150,6 +151,10 @@ class ModelAudit(PydanticModel, AuditMixin, frozen=True):
150
151
  path = f": {self._path.name}" if self._path else ""
151
152
  return f"{self.__class__.__name__}<{self.name}{path}>"
152
153
 
154
+ @property
155
+ def dbt_node_info(self) -> t.Optional[DbtNodeInfo]:
156
+ return self.dbt_node_info_
157
+
153
158
 
154
159
  class StandaloneAudit(_Node, AuditMixin):
155
160
  """
@@ -552,4 +557,5 @@ META_FIELD_CONVERTER: t.Dict[str, t.Callable] = {
552
557
  "depends_on_": lambda value: exp.Tuple(expressions=sorted(value)),
553
558
  "tags": single_value_or_tuple,
554
559
  "default_catalog": exp.to_identifier,
560
+ "dbt_node_info_": lambda value: value.to_expression(),
555
561
  }
@@ -36,6 +36,6 @@ from sqlmesh.core.config.model import ModelDefaultsConfig as ModelDefaultsConfig
36
36
  from sqlmesh.core.config.naming import NameInferenceConfig as NameInferenceConfig
37
37
  from sqlmesh.core.config.linter import LinterConfig as LinterConfig
38
38
  from sqlmesh.core.config.plan import PlanConfig as PlanConfig
39
- from sqlmesh.core.config.root import Config as Config
39
+ from sqlmesh.core.config.root import Config as Config, DbtConfig as DbtConfig
40
40
  from sqlmesh.core.config.run import RunConfig as RunConfig
41
41
  from sqlmesh.core.config.scheduler import BuiltInSchedulerConfig as BuiltInSchedulerConfig
@@ -58,6 +58,7 @@ FORBIDDEN_STATE_SYNC_ENGINES = {
58
58
  "clickhouse",
59
59
  }
60
60
  MOTHERDUCK_TOKEN_REGEX = re.compile(r"(\?|\&)(motherduck_token=)(\S*)")
61
+ PASSWORD_REGEX = re.compile(r"(password=)(\S+)")
61
62
 
62
63
 
63
64
  def _get_engine_import_validator(
@@ -101,6 +102,7 @@ class ConnectionConfig(abc.ABC, BaseConfig):
101
102
  pre_ping: bool
102
103
  pretty_sql: bool = False
103
104
  schema_differ_overrides: t.Optional[t.Dict[str, t.Any]] = None
105
+ catalog_type_overrides: t.Optional[t.Dict[str, str]] = None
104
106
 
105
107
  # Whether to share a single connection across threads or create a new connection per thread.
106
108
  shared_connection: t.ClassVar[bool] = False
@@ -176,6 +178,7 @@ class ConnectionConfig(abc.ABC, BaseConfig):
176
178
  pretty_sql=self.pretty_sql,
177
179
  shared_connection=self.shared_connection,
178
180
  schema_differ_overrides=self.schema_differ_overrides,
181
+ catalog_type_overrides=self.catalog_type_overrides,
179
182
  **self._extra_engine_config,
180
183
  )
181
184
 
@@ -477,13 +480,13 @@ class BaseDuckDBConnectionConfig(ConnectionConfig):
477
480
  adapter = BaseDuckDBConnectionConfig._data_file_to_adapter.get(key)
478
481
  if adapter is not None:
479
482
  logger.info(
480
- f"Using existing DuckDB adapter due to overlapping data file: {self._mask_motherduck_token(key)}"
483
+ f"Using existing DuckDB adapter due to overlapping data file: {self._mask_sensitive_data(key)}"
481
484
  )
482
485
  return adapter
483
486
 
484
487
  if data_files:
485
488
  masked_files = {
486
- self._mask_motherduck_token(file if isinstance(file, str) else file.path)
489
+ self._mask_sensitive_data(file if isinstance(file, str) else file.path)
487
490
  for file in data_files
488
491
  }
489
492
  logger.info(f"Creating new DuckDB adapter for data files: {masked_files}")
@@ -505,10 +508,14 @@ class BaseDuckDBConnectionConfig(ConnectionConfig):
505
508
  return list(self.catalogs)[0]
506
509
  return None
507
510
 
508
- def _mask_motherduck_token(self, string: str) -> str:
509
- return MOTHERDUCK_TOKEN_REGEX.sub(
510
- lambda m: f"{m.group(1)}{m.group(2)}{'*' * len(m.group(3))}", string
511
+ def _mask_sensitive_data(self, string: str) -> str:
512
+ # Mask MotherDuck tokens with fixed number of asterisks
513
+ result = MOTHERDUCK_TOKEN_REGEX.sub(
514
+ lambda m: f"{m.group(1)}{m.group(2)}{'*' * 8 if m.group(3) else ''}", string
511
515
  )
516
+ # Mask PostgreSQL/MySQL passwords with fixed number of asterisks
517
+ result = PASSWORD_REGEX.sub(lambda m: f"{m.group(1)}{'*' * 8}", result)
518
+ return result
512
519
 
513
520
 
514
521
  class MotherDuckConnectionConfig(BaseDuckDBConnectionConfig):
@@ -1753,6 +1760,7 @@ class SparkConnectionConfig(ConnectionConfig):
1753
1760
  config_dir: t.Optional[str] = None
1754
1761
  catalog: t.Optional[str] = None
1755
1762
  config: t.Dict[str, t.Any] = {}
1763
+ wap_enabled: bool = False
1756
1764
 
1757
1765
  concurrent_tasks: int = 4
1758
1766
  register_comments: bool = True
@@ -1799,6 +1807,10 @@ class SparkConnectionConfig(ConnectionConfig):
1799
1807
  .getOrCreate(),
1800
1808
  }
1801
1809
 
1810
+ @property
1811
+ def _extra_engine_config(self) -> t.Dict[str, t.Any]:
1812
+ return {"wap_enabled": self.wap_enabled}
1813
+
1802
1814
 
1803
1815
  class TrinoAuthenticationMethod(str, Enum):
1804
1816
  NO_AUTH = "no-auth"
@@ -0,0 +1,13 @@
1
+ from sqlmesh.core.config.base import BaseConfig
2
+
3
+
4
+ class DbtConfig(BaseConfig):
5
+ """
6
+ Represents dbt-specific options on the SQLMesh root config.
7
+
8
+ These options are only taken into account for dbt projects and are ignored on native projects
9
+ """
10
+
11
+ infer_state_schema_name: bool = False
12
+ """If set, indicates to the dbt loader that the state schema should be inferred based on the profile/target
13
+ so that each target gets its own isolated state"""
@@ -1,7 +1,9 @@
1
1
  from __future__ import annotations
2
2
 
3
+ import typing as t
3
4
 
4
5
  from sqlmesh.core.config.base import BaseConfig
6
+ from sqlmesh.utils.pydantic import field_validator
5
7
 
6
8
 
7
9
  class JanitorConfig(BaseConfig):
@@ -9,6 +11,16 @@ class JanitorConfig(BaseConfig):
9
11
 
10
12
  Args:
11
13
  warn_on_delete_failure: Whether to warn instead of erroring if the janitor fails to delete the expired environment schema / views.
14
+ expired_snapshots_batch_size: Maximum number of expired snapshots to clean in a single batch.
12
15
  """
13
16
 
14
17
  warn_on_delete_failure: bool = False
18
+ expired_snapshots_batch_size: t.Optional[int] = None
19
+
20
+ @field_validator("expired_snapshots_batch_size", mode="before")
21
+ @classmethod
22
+ def _validate_batch_size(cls, value: int) -> int:
23
+ batch_size = int(value)
24
+ if batch_size <= 0:
25
+ raise ValueError("expired_snapshots_batch_size must be greater than 0")
26
+ return batch_size
@@ -172,11 +172,18 @@ def load_config_from_paths(
172
172
  if dbt_project_file:
173
173
  from sqlmesh.dbt.loader import sqlmesh_config
174
174
 
175
+ infer_state_schema_name = False
176
+ if dbt := non_python_config.dbt:
177
+ infer_state_schema_name = dbt.infer_state_schema_name
178
+
175
179
  dbt_python_config = sqlmesh_config(
176
180
  project_root=dbt_project_file.parent,
181
+ profiles_dir=kwargs.pop("profiles_dir", None),
177
182
  dbt_profile_name=kwargs.pop("profile", None),
178
183
  dbt_target_name=kwargs.pop("target", None),
179
184
  variables=variables,
185
+ threads=kwargs.pop("threads", None),
186
+ infer_state_schema_name=infer_state_schema_name,
180
187
  )
181
188
  if type(dbt_python_config) != config_type:
182
189
  dbt_python_config = convert_config_type(dbt_python_config, config_type)
@@ -45,6 +45,7 @@ class ModelDefaultsConfig(BaseConfig):
45
45
  allow_partials: Whether the models can process partial (incomplete) data intervals.
46
46
  enabled: Whether the models are enabled.
47
47
  interval_unit: The temporal granularity of the models data intervals. By default computed from cron.
48
+ batch_concurrency: The maximum number of batches that can run concurrently for an incremental model.
48
49
  pre_statements: The list of SQL statements that get executed before a model runs.
49
50
  post_statements: The list of SQL statements that get executed before a model runs.
50
51
  on_virtual_update: The list of SQL statements to be executed after the virtual update.
@@ -69,6 +70,7 @@ class ModelDefaultsConfig(BaseConfig):
69
70
  interval_unit: t.Optional[t.Union[str, IntervalUnit]] = None
70
71
  enabled: t.Optional[t.Union[str, bool]] = None
71
72
  formatting: t.Optional[t.Union[str, bool]] = None
73
+ batch_concurrency: t.Optional[int] = None
72
74
  pre_statements: t.Optional[t.List[t.Union[str, exp.Expression]]] = None
73
75
  post_statements: t.Optional[t.List[t.Union[str, exp.Expression]]] = None
74
76
  on_virtual_update: t.Optional[t.List[t.Union[str, exp.Expression]]] = None
@@ -36,6 +36,7 @@ from sqlmesh.core.config.naming import NameInferenceConfig as NameInferenceConfi
36
36
  from sqlmesh.core.config.linter import LinterConfig as LinterConfig
37
37
  from sqlmesh.core.config.plan import PlanConfig
38
38
  from sqlmesh.core.config.run import RunConfig
39
+ from sqlmesh.core.config.dbt import DbtConfig
39
40
  from sqlmesh.core.config.scheduler import (
40
41
  BuiltInSchedulerConfig,
41
42
  SchedulerConfig,
@@ -173,6 +174,7 @@ class Config(BaseConfig):
173
174
  linter: LinterConfig = LinterConfig()
174
175
  janitor: JanitorConfig = JanitorConfig()
175
176
  cache_dir: t.Optional[str] = None
177
+ dbt: t.Optional[DbtConfig] = None
176
178
 
177
179
  _FIELD_UPDATE_STRATEGY: t.ClassVar[t.Dict[str, UpdateStrategy]] = {
178
180
  "gateways": UpdateStrategy.NESTED_UPDATE,
@@ -191,6 +193,7 @@ class Config(BaseConfig):
191
193
  "before_all": UpdateStrategy.EXTEND,
192
194
  "after_all": UpdateStrategy.EXTEND,
193
195
  "linter": UpdateStrategy.NESTED_UPDATE,
196
+ "dbt": UpdateStrategy.NESTED_UPDATE,
194
197
  }
195
198
 
196
199
  _connection_config_validator = connection_config_validator
sqlmesh/core/console.py CHANGED
@@ -551,6 +551,22 @@ class Console(
551
551
  def log_failed_models(self, errors: t.List[NodeExecutionFailedError]) -> None:
552
552
  """Display list of models that failed during evaluation to the user."""
553
553
 
554
+ @abc.abstractmethod
555
+ def log_models_updated_during_restatement(
556
+ self,
557
+ snapshots: t.List[t.Tuple[SnapshotTableInfo, SnapshotTableInfo]],
558
+ environment_naming_info: EnvironmentNamingInfo,
559
+ default_catalog: t.Optional[str],
560
+ ) -> None:
561
+ """Display a list of models where new versions got deployed to the specified :environment while we were restating data the old versions
562
+
563
+ Args:
564
+ snapshots: a list of (snapshot_we_restated, snapshot_it_got_replaced_with_during_restatement) tuples
565
+ environment: which environment got updated while we were restating models
566
+ environment_naming_info: how snapshots are named in that :environment (for display name purposes)
567
+ default_catalog: the configured default catalog (for display name purposes)
568
+ """
569
+
554
570
  @abc.abstractmethod
555
571
  def loading_start(self, message: t.Optional[str] = None) -> uuid.UUID:
556
572
  """Starts loading and returns a unique ID that can be used to stop the loading. Optionally can display a message."""
@@ -771,6 +787,14 @@ class NoopConsole(Console):
771
787
  def log_failed_models(self, errors: t.List[NodeExecutionFailedError]) -> None:
772
788
  pass
773
789
 
790
+ def log_models_updated_during_restatement(
791
+ self,
792
+ snapshots: t.List[t.Tuple[SnapshotTableInfo, SnapshotTableInfo]],
793
+ environment_naming_info: EnvironmentNamingInfo,
794
+ default_catalog: t.Optional[str],
795
+ ) -> None:
796
+ pass
797
+
774
798
  def log_destructive_change(
775
799
  self,
776
800
  snapshot_name: str,
@@ -1998,7 +2022,34 @@ class TerminalConsole(Console):
1998
2022
  plan = plan_builder.build()
1999
2023
 
2000
2024
  if plan.restatements:
2001
- self._print("\n[bold]Restating models\n")
2025
+ # A plan can have restatements for the following reasons:
2026
+ # - The user specifically called `sqlmesh plan` with --restate-model.
2027
+ # This creates a "restatement plan" which disallows all other changes and simply force-backfills
2028
+ # the selected models and their downstream dependencies using the versions of the models stored in state.
2029
+ # - There are no specific restatements (so changes are allowed) AND dev previews need to be computed.
2030
+ # The "restatements" feature is currently reused for dev previews.
2031
+ if plan.selected_models_to_restate:
2032
+ # There were legitimate restatements, no dev previews
2033
+ tree = Tree(
2034
+ "[bold]Models selected for restatement:[/bold]\n"
2035
+ "This causes backfill of the model itself as well as affected downstream models"
2036
+ )
2037
+ model_fqn_to_snapshot = {s.name: s for s in plan.snapshots.values()}
2038
+ for model_fqn in plan.selected_models_to_restate:
2039
+ snapshot = model_fqn_to_snapshot[model_fqn]
2040
+ display_name = snapshot.display_name(
2041
+ plan.environment_naming_info,
2042
+ default_catalog if self.verbosity < Verbosity.VERY_VERBOSE else None,
2043
+ dialect=self.dialect,
2044
+ )
2045
+ tree.add(
2046
+ display_name
2047
+ ) # note: we deliberately dont show any intervals here; they get shown in the backfill section
2048
+ self._print(tree)
2049
+ else:
2050
+ # We are computing dev previews, do not confuse the user by printing out something to do
2051
+ # with restatements. Dev previews are already highlighted in the backfill step
2052
+ pass
2002
2053
  else:
2003
2054
  self.show_environment_difference_summary(
2004
2055
  plan.context_diff,
@@ -2076,7 +2127,7 @@ class TerminalConsole(Console):
2076
2127
  if text_diff:
2077
2128
  self._print("")
2078
2129
  self._print(Syntax(text_diff, "sql", word_wrap=True))
2079
- self._print(tree)
2130
+ self._print(tree)
2080
2131
 
2081
2132
  def _show_missing_dates(self, plan: Plan, default_catalog: t.Optional[str]) -> None:
2082
2133
  """Displays the models with missing dates."""
@@ -2225,6 +2276,30 @@ class TerminalConsole(Console):
2225
2276
  for node_name, msg in error_messages.items():
2226
2277
  self._print(f" [red]{node_name}[/red]\n\n{msg}")
2227
2278
 
2279
+ def log_models_updated_during_restatement(
2280
+ self,
2281
+ snapshots: t.List[t.Tuple[SnapshotTableInfo, SnapshotTableInfo]],
2282
+ environment_naming_info: EnvironmentNamingInfo,
2283
+ default_catalog: t.Optional[str] = None,
2284
+ ) -> None:
2285
+ if snapshots:
2286
+ tree = Tree(
2287
+ f"[yellow]The following models had new versions deployed while data was being restated:[/yellow]"
2288
+ )
2289
+
2290
+ for restated_snapshot, updated_snapshot in snapshots:
2291
+ display_name = restated_snapshot.display_name(
2292
+ environment_naming_info,
2293
+ default_catalog if self.verbosity < Verbosity.VERY_VERBOSE else None,
2294
+ dialect=self.dialect,
2295
+ )
2296
+ current_branch = tree.add(display_name)
2297
+ current_branch.add(f"restated version: '{restated_snapshot.version}'")
2298
+ current_branch.add(f"currently active version: '{updated_snapshot.version}'")
2299
+
2300
+ self._print(tree)
2301
+ self._print("") # newline spacer
2302
+
2228
2303
  def log_destructive_change(
2229
2304
  self,
2230
2305
  snapshot_name: str,
@@ -3566,7 +3641,10 @@ class MarkdownConsole(CaptureTerminalConsole):
3566
3641
  msg = f"\nLinter {severity} for `{model._path}`:\n{violations_msg}\n"
3567
3642
 
3568
3643
  self._print(msg)
3569
- self._errors.append(msg)
3644
+ if is_error:
3645
+ self._errors.append(msg)
3646
+ else:
3647
+ self._warnings.append(msg)
3570
3648
 
3571
3649
  @property
3572
3650
  def captured_warnings(self) -> str:
sqlmesh/core/constants.py CHANGED
@@ -8,7 +8,7 @@ from pathlib import Path
8
8
 
9
9
  SQLMESH = "sqlmesh"
10
10
  SQLMESH_MANAGED = "sqlmesh_managed"
11
- SQLMESH_PATH = Path.home() / ".sqlmesh"
11
+ SQLMESH_PATH = Path(os.getenv("SQLMESH_HOME") or Path.home() / ".sqlmesh")
12
12
 
13
13
  PROD = "prod"
14
14
  """Prod"""