2929 TTableFormat ,
3030 TTableReferenceParam ,
3131)
32- from dlt .common .destination .dataset import SupportsReadableRelation
3332from dlt .transformations .configuration import TransformationConfiguration
3433from dlt .common .utils import get_callable_name
3534from dlt .extract .exceptions import CurrentSourceNotAvailable
3635
3736
37+ class MaterializableSqlModel (SqlModel ):
38+ # NOTE: we could forward all data access methods to this class
39+ __slots__ = ("relation" ,)
40+
41+ def __init__ (
42+ self ,
43+ relation : Optional [BaseReadableDBAPIRelation ] = None ,
44+ ) -> None :
45+ super ().__init__ (relation .query (), relation .query_dialect ())
46+ self .relation = relation
47+
48+ @classmethod
49+ def from_relation (cls , relation : BaseReadableDBAPIRelation ) -> "MaterializableSqlModel" :
50+ return cls (relation = relation )
51+
52+ def compute_columns (self ) -> TTableSchemaColumns :
53+ computed_columns , _ = self .relation ._compute_columns_schema (
54+ infer_sqlglot_schema = True ,
55+ allow_anonymous_columns = True ,
56+ allow_partial = True ,
57+ )
58+ return computed_columns
59+
60+
3861class DltTransformationResource (DltResource ):
3962 def __init__ (self , * args : Any , ** kwds : Any ) -> None :
4063 super ().__init__ (* args , ** kwds )
@@ -113,11 +136,12 @@ def transformation_function(*args: Any, **kwargs: Any) -> Iterator[TDataItems]:
113136 # Call the transformation function
114137 transformation_result : Any = func (* args , ** kwargs )
115138
116- # If a string is returned, treat it as a SQL query
117- if isinstance (transformation_result , str ):
118- transformation_result = datasets [0 ](transformation_result )
119-
120- if not isinstance (transformation_result , BaseReadableDBAPIRelation ):
139+ # If a string is returned, construct relation from first dataset from it
140+ if isinstance (transformation_result , BaseReadableDBAPIRelation ):
141+ relation = transformation_result
142+ elif isinstance (transformation_result , str ):
143+ relation = datasets [0 ](transformation_result )
144+ else :
121145 raise TransformationInvalidReturnTypeException (
122146 resource_name ,
123147 "Sql Transformation %s returned an invalid type: %s. Please either return a valid"
@@ -126,24 +150,14 @@ def transformation_function(*args: Any, **kwargs: Any) -> Iterator[TDataItems]:
126150 % (name , type (transformation_result )),
127151 )
128152
129- # Compute columns schema
130- computed_columns , _ = transformation_result ._compute_columns_schema (
131- infer_sqlglot_schema = True ,
132- allow_anonymous_columns = True ,
133- allow_partial = True ,
134- )
135- select_dialect = datasets [0 ].sql_client .capabilities .sqlglot_dialect
136- select_query = transformation_result .query ()
137- all_columns = {** computed_columns , ** (columns or {})}
153+ sql_model = MaterializableSqlModel .from_relation (relation )
138154
139155 if not should_materialize :
140- yield dlt .mark .with_hints (
141- SqlModel (select_query , dialect = select_dialect ),
142- hints = make_hints (columns = all_columns ),
143- )
156+ yield sql_model
144157 else :
145- for chunk in datasets [0 ](select_query ).iter_arrow (chunk_size = config .buffer_max_items ):
146- yield dlt .mark .with_hints (chunk , hints = make_hints (columns = all_columns ))
158+ column_hints = make_hints (columns = sql_model .compute_columns ())
159+ for chunk in relation .iter_arrow (chunk_size = config .buffer_max_items ):
160+ yield dlt .mark .with_hints (chunk , hints = column_hints )
147161
148162 return dlt .resource ( # type: ignore[return-value]
149163 name = name ,
0 commit comments