data-factory-utils 3.2.2__tar.gz → 3.4.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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: data-factory-utils
3
- Version: 3.2.2
3
+ Version: 3.4.0
4
4
  Summary: Utility functions for interacting with data factories.
5
5
  Requires-Dist: boto3>=1.42.8
6
6
  Requires-Dist: boto3-stubs>=1.42.89
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "data-factory-utils"
3
- version = "3.2.2"
3
+ version = "3.4.0"
4
4
  description = "Utility functions for interacting with data factories."
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.12"
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "data-factory-utils"
3
- version = "3.2.2"
3
+ version = "3.4.0"
4
4
  description = "Utility functions for interacting with data factories."
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.12"
@@ -200,24 +200,23 @@ class AthenaQuery:
200
200
  # Get the raw query results from the API
201
201
  responses = self._get_responses()
202
202
 
203
- # Parse the raw row data from the results
204
- rows = []
203
+ column_names: list[str | None] | None = None
204
+ columns: list[list[str | None]] = []
205
+
205
206
  for response in responses:
206
207
  for row in response["ResultSet"]["Rows"]:
207
208
  row_data = [column.get("VarCharValue") for column in row["Data"]]
208
- rows.append(row_data)
209
-
210
- if len(rows) == 0:
211
- return pl.DataFrame({})
212
209
 
213
- # The first row returned by the response is the names of the columns.
214
- column_names, values_in_rows = rows[0], rows[1:]
210
+ # The first row returned by the response is the names of the columns.
211
+ if column_names is None:
212
+ column_names = row_data
213
+ columns = [[] for _ in column_names]
214
+ continue
215
215
 
216
- if len(values_in_rows) == 0:
217
- return pl.DataFrame(data={col: [] for col in column_names})
216
+ for column, value in zip(columns, row_data, strict=True):
217
+ column.append(value)
218
218
 
219
- # Convert a list of rows into a list of columns
220
- values_in_columns = list(zip(*values_in_rows, strict=True))
219
+ if column_names is None:
220
+ return pl.DataFrame({})
221
221
 
222
- # Package it up into a dictionary of columns
223
- return pl.DataFrame(dict(zip(column_names, values_in_columns, strict=True)))
222
+ return pl.DataFrame(dict(zip(column_names, columns, strict=True)))
@@ -79,6 +79,23 @@ class WhereBasicComparison(NamedTuple):
79
79
  brackets: Literal["open", "close", "wrap"] | None = None
80
80
 
81
81
 
82
+ class WhereExistsComparison(NamedTuple):
83
+ """Config for WHERE EXISTS comparisons."""
84
+
85
+ subquery: Subquery
86
+ negative: bool = False
87
+ prefix: Literal["AND", "OR", "NOT"] | None = "AND"
88
+ brackets: Literal["open", "close", "wrap"] | None = None
89
+
90
+
91
+ class WhereNotExistsComparison(NamedTuple):
92
+ """Config for WHERE NOT EXISTS comparisons."""
93
+
94
+ subquery: Subquery
95
+ prefix: Literal["AND", "OR", "NOT"] | None = "AND"
96
+ brackets: Literal["open", "close", "wrap"] | None = None
97
+
98
+
82
99
  class WhereRangeComparison(NamedTuple):
83
100
  """Config for WHERE BETWEEN comparisons."""
84
101
 
@@ -120,6 +137,17 @@ class WhereLikeRegex(NamedTuple):
120
137
  brackets: Literal["open", "close", "wrap"] | None = None
121
138
 
122
139
 
140
+ WhereCondition = (
141
+ WhereBasicComparison
142
+ | WhereExistsComparison
143
+ | WhereNotExistsComparison
144
+ | WhereRangeComparison
145
+ | WhereSetMembership
146
+ | WherePatternMatch
147
+ | WhereLikeRegex
148
+ )
149
+
150
+
123
151
  class Subquery:
124
152
  """Method to wrap queries as subqueries."""
125
153
 
@@ -537,6 +565,30 @@ def _build_where_between(config: WhereRangeComparison) -> str:
537
565
  return query_str
538
566
 
539
567
 
568
+ def _build_where_exists_comparison(config: WhereExistsComparison | WhereNotExistsComparison) -> str:
569
+ """Build a statement for EXISTS / NOT EXISTS.
570
+
571
+ Accepted styles:
572
+ - WHERE EXISTS (subquery)
573
+ - WHERE NOT EXISTS (subquery)
574
+ - AND EXISTS (subquery)
575
+ - AND NOT EXISTS (subquery)
576
+ - OR EXISTS (subquery)
577
+ - OR NOT EXISTS (subquery)
578
+ """
579
+ if not isinstance(config.subquery, Subquery):
580
+ msg = "EXISTS condition requires a Subquery instance"
581
+ raise TypeError(msg)
582
+
583
+ negative = getattr(config, "negative", False) or isinstance(config, WhereNotExistsComparison)
584
+ op = "NOT EXISTS" if negative else "EXISTS"
585
+ query_str = f"{op} {config.subquery}"
586
+
587
+ if config.brackets:
588
+ query_str = _wrap(loc=config.brackets, text=query_str)
589
+ return query_str
590
+
591
+
540
592
  def _build_where_set_membership(config: WhereSetMembership) -> str:
541
593
  """Build a statement for set membership.
542
594
 
@@ -607,9 +659,7 @@ def _build_where_regex_like(config: WhereLikeRegex) -> str:
607
659
 
608
660
 
609
661
  def _validate_combined_where_conditions(
610
- conditions: list[
611
- WhereBasicComparison | WhereRangeComparison | WhereSetMembership | WherePatternMatch | WhereLikeRegex
612
- ],
662
+ conditions: list[WhereCondition],
613
663
  ) -> bool:
614
664
  """Validate WHERE conditions when used as a list."""
615
665
  open_bracket = []
@@ -803,9 +853,7 @@ class Query:
803
853
 
804
854
  def WHERE( # noqa: N802
805
855
  self,
806
- conditions: list[
807
- WhereBasicComparison | WhereRangeComparison | WhereSetMembership | WherePatternMatch | WhereLikeRegex
808
- ],
856
+ conditions: list[WhereCondition],
809
857
  ) -> Self:
810
858
  """Write WHERE conditions."""
811
859
  query_str = []
@@ -824,6 +872,8 @@ class Query:
824
872
  cond_str = _build_where_pattern_match(condition)
825
873
  case WhereLikeRegex():
826
874
  cond_str = _build_where_regex_like(condition)
875
+ case WhereExistsComparison() | WhereNotExistsComparison():
876
+ cond_str = _build_where_exists_comparison(condition)
827
877
  case _:
828
878
  msg = "WHERE statement has no valid conditions."
829
879
  raise ValueError(msg)