|
32 | 32 | logger = get_logger(__name__) |
33 | 33 |
|
34 | 34 |
|
| 35 | +class SelectLimit: |
| 36 | + """Cross-adapter TOP / LIMIT helper. |
| 37 | +
|
| 38 | + On T-SQL (Fabric / SQL Server) ``SELECT TOP n ...`` is used instead of |
| 39 | + ``... LIMIT n``. Instances expose two string properties so that callers |
| 40 | + can build dialect-agnostic queries:: |
| 41 | +
|
| 42 | + sl = SelectLimit(1, is_tsql=True) |
| 43 | + query = f"SELECT {sl.top}col FROM t ORDER BY x {sl.limit}" |
| 44 | + # → "SELECT TOP 1 col FROM t ORDER BY x " |
| 45 | + """ |
| 46 | + |
| 47 | + def __init__(self, n: int, is_tsql: bool) -> None: |
| 48 | + self.top = f"TOP {n} " if is_tsql else "" |
| 49 | + self.limit = "" if is_tsql else f"LIMIT {n}" |
| 50 | + |
| 51 | + |
35 | 52 | def get_dbt_runner( |
36 | 53 | target: str, project_dir: str, runner_method: Optional[RunnerMethod] = None |
37 | 54 | ) -> BaseDbtRunner: |
@@ -95,22 +112,61 @@ def _run_query_with_run_operation(self, prerendered_query: str): |
95 | 112 | ) |
96 | 113 | return json.loads(run_operation_results[0]) |
97 | 114 |
|
98 | | - @staticmethod |
| 115 | + @property |
| 116 | + def is_tsql(self) -> bool: |
| 117 | + """Return True when the target uses T-SQL dialect (Fabric / SQL Server).""" |
| 118 | + return self.target in ("fabric", "sqlserver") |
| 119 | + |
| 120 | + def select_limit(self, n: int = 1) -> "SelectLimit": |
| 121 | + """Return cross-adapter TOP/LIMIT helpers for use in raw SQL strings. |
| 122 | +
|
| 123 | + Usage:: |
| 124 | +
|
| 125 | + sl = dbt_project.select_limit(1) |
| 126 | + query = f"SELECT {sl.top}col FROM t ORDER BY x {sl.limit}" |
| 127 | + """ |
| 128 | + return SelectLimit(n, self.is_tsql) |
| 129 | + |
| 130 | + def samples_query(self, test_id: str, order_by: str = "created_at desc") -> str: |
| 131 | + """Build a cross-adapter query to fetch test result sample rows. |
| 132 | +
|
| 133 | + This is the shared implementation of the ``SAMPLES_QUERY`` template |
| 134 | + that was previously duplicated across multiple test files. |
| 135 | + """ |
| 136 | + sl = self.select_limit(1) |
| 137 | + return f""" |
| 138 | + with latest_elementary_test_result as ( |
| 139 | + select {sl.top}id |
| 140 | + from {{{{ ref("elementary_test_results") }}}} |
| 141 | + where lower(table_name) = lower('{test_id}') |
| 142 | + order by {order_by} |
| 143 | + {sl.limit} |
| 144 | + ) |
| 145 | +
|
| 146 | + select result_row |
| 147 | + from {{{{ ref("test_result_rows") }}}} |
| 148 | + where elementary_test_results_id in (select * from latest_elementary_test_result) |
| 149 | + """ |
| 150 | + |
99 | 151 | def read_table_query( |
| 152 | + self, |
100 | 153 | table_name: str, |
101 | 154 | where: Optional[str] = None, |
102 | 155 | group_by: Optional[str] = None, |
103 | 156 | order_by: Optional[str] = None, |
104 | 157 | limit: Optional[int] = None, |
105 | 158 | column_names: Optional[List[str]] = None, |
106 | 159 | ): |
| 160 | + columns = ", ".join(column_names) if column_names else "*" |
| 161 | + top_clause = f"TOP {limit} " if limit and self.is_tsql else "" |
| 162 | + limit_clause = f"LIMIT {limit}" if limit and not self.is_tsql else "" |
107 | 163 | return f""" |
108 | | - SELECT {', '.join(column_names) if column_names else '*'} |
| 164 | + SELECT {top_clause}{columns} |
109 | 165 | FROM {{{{ ref('{table_name}') }}}} |
110 | 166 | {f"WHERE {where}" if where else ""} |
111 | 167 | {f"GROUP BY {group_by}" if group_by else ""} |
112 | 168 | {f"ORDER BY {order_by}" if order_by else ""} |
113 | | - {f"LIMIT {limit}" if limit else ""} |
| 169 | + {limit_clause} |
114 | 170 | """ |
115 | 171 |
|
116 | 172 | def read_table( |
|
0 commit comments