sqlmesh 0.183.1.dev10__py3-none-any.whl → 0.183.1.dev12__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.
sqlmesh/_version.py CHANGED
@@ -17,5 +17,5 @@ __version__: str
17
17
  __version_tuple__: VERSION_TUPLE
18
18
  version_tuple: VERSION_TUPLE
19
19
 
20
- __version__ = version = '0.183.1.dev10'
21
- __version_tuple__ = version_tuple = (0, 183, 1, 'dev10')
20
+ __version__ = version = '0.183.1.dev12'
21
+ __version_tuple__ = version_tuple = (0, 183, 1, 'dev12')
@@ -1008,6 +1008,8 @@ class EngineAdapter:
1008
1008
  if materialized_properties and not materialized:
1009
1009
  raise SQLMeshError("Materialized properties are only supported for materialized views")
1010
1010
 
1011
+ query_or_df = self._native_df_to_pandas_df(query_or_df)
1012
+
1011
1013
  if isinstance(query_or_df, pd.DataFrame):
1012
1014
  values: t.List[t.Tuple[t.Any, ...]] = list(
1013
1015
  query_or_df.itertuples(index=False, name=None)
@@ -2002,6 +2004,19 @@ class EngineAdapter:
2002
2004
  self.execute(query, quote_identifiers=quote_identifiers)
2003
2005
  return self.cursor.fetchdf()
2004
2006
 
2007
+ def _native_df_to_pandas_df(
2008
+ self,
2009
+ query_or_df: QueryOrDF,
2010
+ ) -> t.Union[Query, pd.DataFrame]:
2011
+ """
2012
+ Take a "native" DataFrame (eg Pyspark, Bigframe, Snowpark etc) and convert it to Pandas
2013
+ """
2014
+ if isinstance(query_or_df, (exp.Query, exp.DerivedTable, pd.DataFrame)):
2015
+ return query_or_df
2016
+
2017
+ # EngineAdapter subclasses that have native DataFrame types should override this
2018
+ raise NotImplementedError(f"Unable to convert {type(query_or_df)} to Pandas")
2019
+
2005
2020
  def fetchdf(
2006
2021
  self, query: t.Union[exp.Expression, str], quote_identifiers: bool = False
2007
2022
  ) -> pd.DataFrame:
@@ -26,6 +26,7 @@ from sqlmesh.core.schema_diff import SchemaDiffer
26
26
  from sqlmesh.utils import optional_import
27
27
  from sqlmesh.utils.date import to_datetime
28
28
  from sqlmesh.utils.errors import SQLMeshError
29
+ from sqlmesh.utils.pandas import columns_to_types_from_dtypes
29
30
 
30
31
  if t.TYPE_CHECKING:
31
32
  from google.api_core.retry import Retry
@@ -1107,6 +1108,39 @@ class BigQueryEngineAdapter(InsertOverwriteWithMergeMixin, ClusteredByMixin, Row
1107
1108
  def _normalize_nested_value(self, col: exp.Expression) -> exp.Expression:
1108
1109
  return exp.func("TO_JSON_STRING", col, dialect=self.dialect)
1109
1110
 
1111
+ @t.overload
1112
+ def _columns_to_types(
1113
+ self, query_or_df: DF, columns_to_types: t.Optional[t.Dict[str, exp.DataType]] = None
1114
+ ) -> t.Dict[str, exp.DataType]: ...
1115
+
1116
+ @t.overload
1117
+ def _columns_to_types(
1118
+ self, query_or_df: Query, columns_to_types: t.Optional[t.Dict[str, exp.DataType]] = None
1119
+ ) -> t.Optional[t.Dict[str, exp.DataType]]: ...
1120
+
1121
+ def _columns_to_types(
1122
+ self, query_or_df: QueryOrDF, columns_to_types: t.Optional[t.Dict[str, exp.DataType]] = None
1123
+ ) -> t.Optional[t.Dict[str, exp.DataType]]:
1124
+ if (
1125
+ not columns_to_types
1126
+ and bigframes
1127
+ and isinstance(query_or_df, bigframes.dataframe.DataFrame)
1128
+ ):
1129
+ # using dry_run=True attempts to prevent the DataFrame from being materialized just to read the column types from it
1130
+ dtypes = query_or_df.to_pandas(dry_run=True).columnDtypes
1131
+ return columns_to_types_from_dtypes(dtypes.items())
1132
+
1133
+ return super()._columns_to_types(query_or_df, columns_to_types)
1134
+
1135
+ def _native_df_to_pandas_df(
1136
+ self,
1137
+ query_or_df: QueryOrDF,
1138
+ ) -> t.Union[Query, pd.DataFrame]:
1139
+ if bigframes and isinstance(query_or_df, bigframes.dataframe.DataFrame):
1140
+ return query_or_df.to_pandas()
1141
+
1142
+ return super()._native_df_to_pandas_df(query_or_df)
1143
+
1110
1144
  @property
1111
1145
  def _query_data(self) -> t.Any:
1112
1146
  return self._connection_pool.get_attribute("query_data")
@@ -27,6 +27,7 @@ from sqlmesh.core.engine_adapter.shared import (
27
27
  from sqlmesh.core.schema_diff import SchemaDiffer
28
28
  from sqlmesh.utils import optional_import
29
29
  from sqlmesh.utils.errors import SQLMeshError
30
+ from sqlmesh.utils.pandas import columns_to_types_from_dtypes
30
31
 
31
32
  logger = logging.getLogger(__name__)
32
33
  snowpark = optional_import("snowflake.snowpark")
@@ -298,13 +299,23 @@ class SnowflakeEngineAdapter(GetCurrentCatalogFromFunctionMixin, ClusteredByMixi
298
299
 
299
300
  if is_snowpark_dataframe:
300
301
  temp_table.set("catalog", database)
301
- df_renamed = df.rename(
302
- {
303
- col: exp.to_identifier(col).sql(dialect=self.dialect, identify=True)
304
- for col in columns_to_types
305
- }
306
- ) # type: ignore
307
- df_renamed.createOrReplaceTempView(
302
+
303
+ # only quote columns if they arent already quoted
304
+ # if the Snowpark dataframe was created from a Pandas dataframe via snowpark.create_dataframe(pandas_df),
305
+ # then they will be quoted already. But if the Snowpark dataframe was created manually by the user, then the
306
+ # columns may not be quoted
307
+ columns_already_quoted = all(
308
+ col.startswith('"') and col.endswith('"') for col in df.columns
309
+ )
310
+ local_df = df
311
+ if not columns_already_quoted:
312
+ local_df = df.rename(
313
+ {
314
+ col: exp.to_identifier(col).sql(dialect=self.dialect, identify=True)
315
+ for col in columns_to_types
316
+ }
317
+ ) # type: ignore
318
+ local_df.createOrReplaceTempView(
308
319
  temp_table.sql(dialect=self.dialect, identify=True)
309
320
  ) # type: ignore
310
321
  elif isinstance(df, pd.DataFrame):
@@ -356,6 +367,11 @@ class SnowflakeEngineAdapter(GetCurrentCatalogFromFunctionMixin, ClusteredByMixi
356
367
 
357
368
  def cleanup() -> None:
358
369
  if is_snowpark_dataframe:
370
+ if hasattr(df, "table_name"):
371
+ if isinstance(df.table_name, str):
372
+ # created by the Snowpark library if the Snowpark DataFrame was created from a Pandas DataFrame
373
+ # (if the Snowpark DataFrame was created via native means then there is no 'table_name' property and no temp table)
374
+ self.drop_table(df.table_name)
359
375
  self.drop_view(temp_table)
360
376
  else:
361
377
  self.drop_table(temp_table)
@@ -381,6 +397,15 @@ class SnowflakeEngineAdapter(GetCurrentCatalogFromFunctionMixin, ClusteredByMixi
381
397
  columns = self.cursor._result_set.batches[0].column_names
382
398
  return pd.DataFrame([dict(zip(columns, row)) for row in rows])
383
399
 
400
+ def _native_df_to_pandas_df(
401
+ self,
402
+ query_or_df: QueryOrDF,
403
+ ) -> t.Union[Query, pd.DataFrame]:
404
+ if snowpark and isinstance(query_or_df, snowpark.DataFrame):
405
+ return query_or_df.to_pandas()
406
+
407
+ return super()._native_df_to_pandas_df(query_or_df)
408
+
384
409
  def _get_data_objects(
385
410
  self, schema_name: SchemaName, object_names: t.Optional[t.Set[str]] = None
386
411
  ) -> t.List[DataObject]:
@@ -426,6 +451,10 @@ class SnowflakeEngineAdapter(GetCurrentCatalogFromFunctionMixin, ClusteredByMixi
426
451
  if object_names:
427
452
  query = query.where(exp.column("TABLE_NAME").isin(*object_names))
428
453
 
454
+ # exclude SNOWPARK_TEMP_TABLE tables that are managed by the Snowpark library and are an implementation
455
+ # detail of dealing with DataFrame's
456
+ query = query.where(exp.column("TABLE_NAME").like("SNOWPARK_TEMP_TABLE%").not_())
457
+
429
458
  df = self.fetchdf(query, quote_identifiers=True)
430
459
  if df.empty:
431
460
  return []
@@ -537,3 +566,21 @@ class SnowflakeEngineAdapter(GetCurrentCatalogFromFunctionMixin, ClusteredByMixi
537
566
  clone_kwargs=clone_kwargs,
538
567
  **kwargs,
539
568
  )
569
+
570
+ @t.overload
571
+ def _columns_to_types(
572
+ self, query_or_df: DF, columns_to_types: t.Optional[t.Dict[str, exp.DataType]] = None
573
+ ) -> t.Dict[str, exp.DataType]: ...
574
+
575
+ @t.overload
576
+ def _columns_to_types(
577
+ self, query_or_df: Query, columns_to_types: t.Optional[t.Dict[str, exp.DataType]] = None
578
+ ) -> t.Optional[t.Dict[str, exp.DataType]]: ...
579
+
580
+ def _columns_to_types(
581
+ self, query_or_df: QueryOrDF, columns_to_types: t.Optional[t.Dict[str, exp.DataType]] = None
582
+ ) -> t.Optional[t.Dict[str, exp.DataType]]:
583
+ if not columns_to_types and snowpark and isinstance(query_or_df, snowpark.DataFrame):
584
+ return columns_to_types_from_dtypes(query_or_df.sample(n=1).to_pandas().dtypes.items())
585
+
586
+ return super()._columns_to_types(query_or_df, columns_to_types)
@@ -382,51 +382,14 @@ class SparkEngineAdapter(
382
382
  partitioned_by=[exp.column(x) for x in primary_key] if primary_key else None,
383
383
  )
384
384
 
385
- def create_view(
385
+ def _native_df_to_pandas_df(
386
386
  self,
387
- view_name: TableName,
388
387
  query_or_df: QueryOrDF,
389
- columns_to_types: t.Optional[t.Dict[str, exp.DataType]] = None,
390
- replace: bool = True,
391
- materialized: bool = False,
392
- materialized_properties: t.Optional[t.Dict[str, t.Any]] = None,
393
- table_description: t.Optional[str] = None,
394
- column_descriptions: t.Optional[t.Dict[str, str]] = None,
395
- view_properties: t.Optional[t.Dict[str, exp.Expression]] = None,
396
- **create_kwargs: t.Any,
397
- ) -> None:
398
- """Create a view with a query or dataframe.
399
-
400
- If a dataframe is passed in, it will be converted into a literal values statement.
401
- This should only be done if the dataframe is very small!
402
-
403
- Args:
404
- view_name: The view name.
405
- query_or_df: A query or dataframe.
406
- columns_to_types: Columns to use in the view statement.
407
- replace: Whether or not to replace an existing view - defaults to True.
408
- materialized: Whether or not the view should be materialized - defaults to False.
409
- materialized_properties: Optional materialized view properties to add to the view.
410
- table_description: Optional table description from MODEL DDL.
411
- column_descriptions: Optional column descriptions from model query.
412
- create_kwargs: Additional kwargs to pass into the Create expression
413
- view_properties: Optional view properties to add to the view.
414
- """
415
- pyspark_df = self.try_get_pyspark_df(query_or_df)
416
- if pyspark_df:
417
- query_or_df = pyspark_df.toPandas()
418
- super().create_view(
419
- view_name,
420
- query_or_df,
421
- columns_to_types,
422
- replace,
423
- materialized,
424
- materialized_properties,
425
- table_description,
426
- column_descriptions,
427
- view_properties=view_properties,
428
- **create_kwargs,
429
- )
388
+ ) -> t.Union[Query, pd.DataFrame]:
389
+ if pyspark_df := self.try_get_pyspark_df(query_or_df):
390
+ return pyspark_df.toPandas()
391
+
392
+ return super()._native_df_to_pandas_df(query_or_df)
430
393
 
431
394
  def _create_table(
432
395
  self,
sqlmesh/utils/pandas.py CHANGED
@@ -27,13 +27,20 @@ PANDAS_TYPE_MAPPINGS = {
27
27
  pd.Float32Dtype(): exp.DataType.build("float"),
28
28
  pd.Float64Dtype(): exp.DataType.build("double"),
29
29
  pd.StringDtype(): exp.DataType.build("text"), # type: ignore
30
+ pd.StringDtype("pyarrow"): exp.DataType.build("text"),
30
31
  pd.BooleanDtype(): exp.DataType.build("boolean"),
31
32
  }
32
33
 
33
34
 
34
35
  def columns_to_types_from_df(df: pd.DataFrame) -> t.Dict[str, exp.DataType]:
36
+ return columns_to_types_from_dtypes(df.dtypes.items())
37
+
38
+
39
+ def columns_to_types_from_dtypes(
40
+ dtypes: t.Iterable[t.Tuple[t.Hashable, t.Any]],
41
+ ) -> t.Dict[str, exp.DataType]:
35
42
  result = {}
36
- for column_name, column_type in df.dtypes.items():
43
+ for column_name, column_type in dtypes:
37
44
  exp_type: t.Optional[exp.DataType] = None
38
45
  if hasattr(pd, "DatetimeTZDtype") and isinstance(column_type, pd.DatetimeTZDtype):
39
46
  exp_type = exp.DataType.build("timestamptz")
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: sqlmesh
3
- Version: 0.183.1.dev10
3
+ Version: 0.183.1.dev12
4
4
  Summary: Next-generation data transformation framework
5
5
  Author-email: "TobikoData Inc." <engineering@tobikodata.com>
6
6
  License: Apache License
@@ -1,5 +1,5 @@
1
1
  sqlmesh/__init__.py,sha256=DRovDXI-9D9xeYDP7t8Fq7mI31S2btlN-LHPOK3y7PA,7240
2
- sqlmesh/_version.py,sha256=FNHekwCAh7xnfXMedM3cpDDrnCXslHnzGiv5Rog-Xho,530
2
+ sqlmesh/_version.py,sha256=PNcoRr1Tz4tr1llNJk86ORpVx483m6Xj_6wKHJhtX0E,530
3
3
  sqlmesh/magics.py,sha256=9oY1Uz8uwsuTtPRg9HTYDxnGiJ7-OVRCpmCgAhbKHUU,39431
4
4
  sqlmesh/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
5
5
  sqlmesh/cicd/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -59,9 +59,9 @@ sqlmesh/core/config/ui.py,sha256=jsO-S6_d9NkLZGG5pT4mgKgxMF34KzkDociZAMvCX3U,278
59
59
  sqlmesh/core/engine_adapter/__init__.py,sha256=two_WTs4UnSNp-fscJh8vOunJq0Xi37EuDuMjKsKuNE,2281
60
60
  sqlmesh/core/engine_adapter/_typing.py,sha256=BAM_U7Vsi0k37XHu_nNnu3whXxuTVDey_uZe3aelKYY,1033
61
61
  sqlmesh/core/engine_adapter/athena.py,sha256=kVkGhDhjVd_8zsvq7B94kr_CHD0bct6XXFkhzrOjQOY,26158
62
- sqlmesh/core/engine_adapter/base.py,sha256=Ni9kF_ms6s5xKGPgroMAsxjqTvJmX1relpGaDAxYsuI,99947
62
+ sqlmesh/core/engine_adapter/base.py,sha256=XQeJi4Rr5VllnQKFZBYXNs1G142T16ZOVU_l9UYvzoE,100539
63
63
  sqlmesh/core/engine_adapter/base_postgres.py,sha256=DOsOdCJyQAVfA9S0GznuH0J1bonMqYVP_yJLDYcxFYM,6772
64
- sqlmesh/core/engine_adapter/bigquery.py,sha256=mdgXlFOdyz6Qvno_io_9hm5QLEOQncLnT5itnOU7Ock,47845
64
+ sqlmesh/core/engine_adapter/bigquery.py,sha256=gnd2cFc7v95tT_E9XMHHgccS-QwSF2sJHsL8xfcSguk,49249
65
65
  sqlmesh/core/engine_adapter/clickhouse.py,sha256=4ACsYsZ8-gvyCozwoEMEayTTUdMjmNea4xUkxtXw6R4,34907
66
66
  sqlmesh/core/engine_adapter/databricks.py,sha256=AbNbnt9nQdOd7lTov2zsKROZlzgV2fLvOYXam3cowvk,14507
67
67
  sqlmesh/core/engine_adapter/duckdb.py,sha256=FfNmBzLySDDi--nTlsSavTwdperCzhpYzDoroCilPcA,4900
@@ -72,8 +72,8 @@ sqlmesh/core/engine_adapter/postgres.py,sha256=XWnV7MDndkCRokb7ih2yxXodw4W8E61yH
72
72
  sqlmesh/core/engine_adapter/redshift.py,sha256=_rZWv4rchDAzB6m6idoV1NLHFBKK_gM4nlfeErqXEcE,16691
73
73
  sqlmesh/core/engine_adapter/risingwave.py,sha256=NXXuiPfbB5ZSp7igMqZpaX2c-5TYreOlIouPU7iCVJ8,936
74
74
  sqlmesh/core/engine_adapter/shared.py,sha256=NyF2osSJDWPp7KlJUBR4GZcS_cJFr4daUPvhGyp4XcQ,12909
75
- sqlmesh/core/engine_adapter/snowflake.py,sha256=M0--yVpl8S9x1kjz7t-lcIORewtnA7dEPEd4oBehb7Q,23967
76
- sqlmesh/core/engine_adapter/spark.py,sha256=NwtVkdvy39bD6gnrIvIzITa0g510KZOrP3jz0aMKjwI,22711
75
+ sqlmesh/core/engine_adapter/snowflake.py,sha256=UrfoKY6Z4S9rOgtcJqzYW84Z3XN4h-HGrJnftZCxPHo,26403
76
+ sqlmesh/core/engine_adapter/spark.py,sha256=nDJXH1su08wv3m9X0Mz-x98xhZblq1BvM3EtRYjZhrI,21012
77
77
  sqlmesh/core/engine_adapter/trino.py,sha256=pZ2E1owyC6DPmkU3RkibR3CIE1gE-8fNd016YHh52II,14915
78
78
  sqlmesh/core/linter/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
79
79
  sqlmesh/core/linter/definition.py,sha256=UL05OjFBfoLSYGWXNf4zjZYzIKr-OyFIMIcIh7RhR8s,5237
@@ -262,11 +262,11 @@ sqlmesh/utils/hashing.py,sha256=4Bi0NvRctBHzg58btNxXwSNgcvj8doIVVvOjJ40mHBg,512
262
262
  sqlmesh/utils/jinja.py,sha256=6Iob2Pv88L2rCaHyl_V90b5GKsG9hI10Ua_FRWhQIl0,23628
263
263
  sqlmesh/utils/metaprogramming.py,sha256=i1rxWi-oxdh1VwKoBh5Qvy0GgHm9GaDZMvIrv3QXGt4,23083
264
264
  sqlmesh/utils/migration.py,sha256=R7Ck6FX-LfcujGy7sgS0kDWlK-xAEBODr3gqIQ8gC08,835
265
- sqlmesh/utils/pandas.py,sha256=j_tYhuPcWpzuPceZZ6IhdTVbmxYyGJW7l4FHr4bQqjM,1824
265
+ sqlmesh/utils/pandas.py,sha256=WmNlTLuv_qLelrcTnwb05QMaW53AYM6dpOW0o3dz6qM,2051
266
266
  sqlmesh/utils/pydantic.py,sha256=ioaAzYF42PutEJRyAS11-AHguiCG3yWI6VRcTVa2G-g,10470
267
267
  sqlmesh/utils/rich.py,sha256=xNZyPCqEc4TAlJlsEthg-5eYUWpaizz0QjBzO7a1Yv4,1509
268
268
  sqlmesh/utils/yaml.py,sha256=np-WVcwmwsKghCgAj1OBBMsK1SXSRn4wR-H37cGiFr8,2282
269
- sqlmesh-0.183.1.dev10.dist-info/licenses/LICENSE,sha256=OlMefUjgWJdULtf84BLW0AZZcY8DwdgQqb_1j2862j8,11346
269
+ sqlmesh-0.183.1.dev12.dist-info/licenses/LICENSE,sha256=OlMefUjgWJdULtf84BLW0AZZcY8DwdgQqb_1j2862j8,11346
270
270
  web/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
271
271
  web/client/dist/index.html,sha256=nPqIDCJmonOaEAp4hhwrwgPdwt_F-5zzw-WW1kJqq3I,5472
272
272
  web/client/dist/assets/Audits-DiRd7NXk.js,sha256=Q5dIFqsHps-vVYD8037NpTPW_1juQgn_Ov45smEm7Y8,1041
@@ -384,8 +384,8 @@ web/server/api/endpoints/models.py,sha256=SzS5bshzdXyM6hb948sZ_3k-xhpGCHkxkb7pHz
384
384
  web/server/api/endpoints/modules.py,sha256=8hqqgonGay_mJmpCw0IdbjsPhWlQH2VLdKAqha-myac,468
385
385
  web/server/api/endpoints/plan.py,sha256=OYuQ_4dgbhG9ibTcX1a8mvYgH3ANxw9Fhbb4wg6QWqc,9222
386
386
  web/server/api/endpoints/table_diff.py,sha256=Wd5YxeKbZB7AkrDvqFgvV9ZQlnKYOF1n5W_w2EHE_Zw,2085
387
- sqlmesh-0.183.1.dev10.dist-info/METADATA,sha256=LkrG5OZf9FaOfDjDYLCRygRhTc2x3RK1OuNqzzG_iYc,25253
388
- sqlmesh-0.183.1.dev10.dist-info/WHEEL,sha256=Nw36Djuh_5VDukK0H78QzOX-_FQEo6V37m3nkm96gtU,91
389
- sqlmesh-0.183.1.dev10.dist-info/entry_points.txt,sha256=pIYKtg-u-uO7BK_Y0qrdmfyj0bYrcMc3Xiqr5WE3VFk,121
390
- sqlmesh-0.183.1.dev10.dist-info/top_level.txt,sha256=bspjo5vjELQkuN0qKuTFIbsrUt3Zeo8D2mlKeHG1LdE,12
391
- sqlmesh-0.183.1.dev10.dist-info/RECORD,,
387
+ sqlmesh-0.183.1.dev12.dist-info/METADATA,sha256=3GDwXQ8VnIg--Q9fGYfL2wKdBNSBPGtIbSFzUrYINLI,25253
388
+ sqlmesh-0.183.1.dev12.dist-info/WHEEL,sha256=Nw36Djuh_5VDukK0H78QzOX-_FQEo6V37m3nkm96gtU,91
389
+ sqlmesh-0.183.1.dev12.dist-info/entry_points.txt,sha256=pIYKtg-u-uO7BK_Y0qrdmfyj0bYrcMc3Xiqr5WE3VFk,121
390
+ sqlmesh-0.183.1.dev12.dist-info/top_level.txt,sha256=bspjo5vjELQkuN0qKuTFIbsrUt3Zeo8D2mlKeHG1LdE,12
391
+ sqlmesh-0.183.1.dev12.dist-info/RECORD,,