semantic-query-compiler 0.1.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.
Files changed (35) hide show
  1. semantic_query_compiler-0.1.0/.github/workflows/ci.yml +26 -0
  2. semantic_query_compiler-0.1.0/.gitignore +8 -0
  3. semantic_query_compiler-0.1.0/CHANGELOG.md +32 -0
  4. semantic_query_compiler-0.1.0/PKG-INFO +565 -0
  5. semantic_query_compiler-0.1.0/README.md +533 -0
  6. semantic_query_compiler-0.1.0/docs/architecture.md +205 -0
  7. semantic_query_compiler-0.1.0/docs/support-matrix.md +37 -0
  8. semantic_query_compiler-0.1.0/docs/validation.md +142 -0
  9. semantic_query_compiler-0.1.0/examples/quickstart/README.md +37 -0
  10. semantic_query_compiler-0.1.0/examples/quickstart/request.json +6 -0
  11. semantic_query_compiler-0.1.0/examples/quickstart/semantic.yml +63 -0
  12. semantic_query_compiler-0.1.0/examples/quickstart/setup.sql +28 -0
  13. semantic_query_compiler-0.1.0/pyproject.toml +57 -0
  14. semantic_query_compiler-0.1.0/src/semantic_query_compiler/__init__.py +11 -0
  15. semantic_query_compiler-0.1.0/src/semantic_query_compiler/cli.py +757 -0
  16. semantic_query_compiler-0.1.0/src/semantic_query_compiler/column_registry.py +216 -0
  17. semantic_query_compiler-0.1.0/src/semantic_query_compiler/compiler.py +1632 -0
  18. semantic_query_compiler-0.1.0/src/semantic_query_compiler/dbt_manifest.py +70 -0
  19. semantic_query_compiler-0.1.0/src/semantic_query_compiler/dialects.py +93 -0
  20. semantic_query_compiler-0.1.0/src/semantic_query_compiler/loader.py +68 -0
  21. semantic_query_compiler-0.1.0/src/semantic_query_compiler/models.py +231 -0
  22. semantic_query_compiler-0.1.0/src/semantic_query_compiler/plan.py +42 -0
  23. semantic_query_compiler-0.1.0/src/semantic_query_compiler/result_compare.py +128 -0
  24. semantic_query_compiler-0.1.0/src/semantic_query_compiler/validator.py +539 -0
  25. semantic_query_compiler-0.1.0/tests/fixtures/column_registry.json +31 -0
  26. semantic_query_compiler-0.1.0/tests/fixtures/semantic_layer.yml +116 -0
  27. semantic_query_compiler-0.1.0/tests/test_column_registry.py +104 -0
  28. semantic_query_compiler-0.1.0/tests/test_compare_metric.py +125 -0
  29. semantic_query_compiler-0.1.0/tests/test_compile.py +1008 -0
  30. semantic_query_compiler-0.1.0/tests/test_dbt_manifest.py +97 -0
  31. semantic_query_compiler-0.1.0/tests/test_dialect_matrix.py +50 -0
  32. semantic_query_compiler-0.1.0/tests/test_duckdb_execution.py +428 -0
  33. semantic_query_compiler-0.1.0/tests/test_result_compare.py +67 -0
  34. semantic_query_compiler-0.1.0/tests/test_validate.py +298 -0
  35. semantic_query_compiler-0.1.0/uv.lock +859 -0
@@ -0,0 +1,26 @@
1
+ name: ci
2
+
3
+ on:
4
+ pull_request:
5
+ push:
6
+ branches: [master, main]
7
+
8
+ concurrency:
9
+ group: ${{ github.workflow }}-${{ github.ref }}
10
+ cancel-in-progress: true
11
+
12
+ jobs:
13
+ test:
14
+ runs-on: ubuntu-latest
15
+ steps:
16
+ - uses: actions/checkout@v5
17
+ - name: Install uv
18
+ uses: astral-sh/setup-uv@v7
19
+ - name: Set up Python
20
+ run: uv python install 3.12
21
+ - name: Install dependencies
22
+ run: uv sync --extra dev
23
+ - name: Lint
24
+ run: uv run ruff check .
25
+ - name: Test
26
+ run: uv run pytest
@@ -0,0 +1,8 @@
1
+ .venv/
2
+ __pycache__/
3
+ *.py[cod]
4
+ .pytest_cache/
5
+ .ruff_cache/
6
+ build/
7
+ dist/
8
+ *.egg-info/
@@ -0,0 +1,32 @@
1
+ # Changelog
2
+
3
+ ## 0.1.0 - beta
4
+
5
+ Initial beta release of `semantic-query-compiler`.
6
+
7
+ ### Included
8
+
9
+ - YAML semantic model loader with `semantic`, `model`, `semantic_layer`, `modules`, and single `module` roots.
10
+ - CLI commands for model inspection, validation, SQL compilation, result comparison, and column-registry generation.
11
+ - BigQuery, DuckDB, Postgres, and Snowflake SQL rendering.
12
+ - Aggregate, count, count-distinct, ratio, custom-value, custom-ratio, and cross-module derived-ratio metrics.
13
+ - Metric filters, query filters, named slices, breakdowns, joined breakdowns, and safe multi-hop joins over `many-to-one` / `one-to-one` relationships.
14
+ - Period shortcuts including closed trailing periods such as `last 24 complete months`.
15
+ - Time comparison, cumulative windows, targets, target comparisons, and ratio target handling.
16
+ - Custom SQL parsing/transpilation with optional function allowlists.
17
+ - Column registry generation from `information_schema` JSON/JSONL and dbt `manifest.json`.
18
+ - Result comparison harness for DuckDB and BigQuery.
19
+ - Fresh-install DuckDB quickstart example.
20
+
21
+ ### Support level
22
+
23
+ - BigQuery: execution-tested and used as the primary warehouse validation target.
24
+ - DuckDB: execution-tested, including copied real-model data checks across all metrics and key request shapes.
25
+ - Postgres and Snowflake: compile/parse-tested; execution validation is not part of this beta.
26
+
27
+ ### Known limits
28
+
29
+ - This is a beta, not a production-trust/v1 release.
30
+ - Dashboard/export parity against trusted business reports is still required before relying on a migrated model for production decisions.
31
+ - Postgres and Snowflake support should be treated as SQL rendering support until execution tests are added.
32
+ - The compiler owns SQL generation and comparison helpers; warehouse execution is intentionally left to caller tooling.
@@ -0,0 +1,565 @@
1
+ Metadata-Version: 2.4
2
+ Name: semantic-query-compiler
3
+ Version: 0.1.0
4
+ Summary: Agent-friendly semantic metric compiler with dialect-aware SQL rendering
5
+ Project-URL: Homepage, https://github.com/rasmusengelbrecht/semantic-query-compiler
6
+ Project-URL: Repository, https://github.com/rasmusengelbrecht/semantic-query-compiler
7
+ Project-URL: Issues, https://github.com/rasmusengelbrecht/semantic-query-compiler/issues
8
+ Author: Rasmus Engelbrecht
9
+ License-Expression: MIT
10
+ Keywords: analytics,bigquery,duckdb,metrics,semantic-layer,sql
11
+ Classifier: Development Status :: 3 - Alpha
12
+ Classifier: Environment :: Console
13
+ Classifier: Intended Audience :: Developers
14
+ Classifier: Intended Audience :: Information Technology
15
+ Classifier: License :: OSI Approved :: MIT License
16
+ Classifier: Programming Language :: Python :: 3
17
+ Classifier: Programming Language :: Python :: 3.11
18
+ Classifier: Programming Language :: Python :: 3.12
19
+ Classifier: Topic :: Database
20
+ Classifier: Topic :: Software Development :: Code Generators
21
+ Requires-Python: >=3.11
22
+ Requires-Dist: duckdb>=1.1
23
+ Requires-Dist: pydantic<2.12,>=2.7
24
+ Requires-Dist: pyyaml>=6.0
25
+ Requires-Dist: sqlglot>=30.0
26
+ Provides-Extra: bigquery
27
+ Requires-Dist: google-cloud-bigquery>=3.0; extra == 'bigquery'
28
+ Provides-Extra: dev
29
+ Requires-Dist: pytest>=8.0; extra == 'dev'
30
+ Requires-Dist: ruff>=0.8; extra == 'dev'
31
+ Description-Content-Type: text/markdown
32
+
33
+ # semantic-query-compiler
34
+
35
+ Agent-friendly semantic metric compiler with dialect-aware SQL rendering.
36
+
37
+ `semantic-query-compiler` turns governed YAML metric definitions into inspectable SQL for warehouses such as BigQuery, DuckDB, Postgres, and Snowflake. It is designed for CLI and agent workflows: discover metrics, validate a model, compile SQL, explain the query plan, and compare results against trusted SQL when you need confidence.
38
+
39
+ The compiler is intentionally separate from warehouse execution. It owns semantic definitions, validation, planning, SQL rendering, and comparison helpers. Run compiled SQL with your warehouse client, BI tool, notebook, dbt workflow, or agent runtime.
40
+
41
+ Current status: `0.1.0` beta. BigQuery and DuckDB have execution coverage; Postgres and Snowflake are compile/parse-tested. See `docs/support-matrix.md` for support levels and v1 release gates.
42
+
43
+ ## Install
44
+
45
+ From a checkout:
46
+
47
+ ```bash
48
+ uv venv
49
+ uv pip install -e '.[dev]'
50
+ semantic --help
51
+ ```
52
+
53
+ From GitHub:
54
+
55
+ ```bash
56
+ uv tool install 'semantic-query-compiler @ git+https://github.com/rasmusengelbrecht/semantic-query-compiler.git'
57
+ ```
58
+
59
+ Optional BigQuery comparison support needs the BigQuery extra or dependency:
60
+
61
+ ```bash
62
+ uv pip install -e '.[bigquery]'
63
+ ```
64
+
65
+ ## Quickstart
66
+
67
+ Create starter files:
68
+
69
+ ```bash
70
+ semantic init --dir demo-semantic --with-duckdb-example
71
+ cd demo-semantic
72
+ semantic validate --model semantic.yml --check-compilable
73
+ semantic compile revenue --model semantic.yml --period "current month" --request request.json --format sql
74
+ semantic compare-metric revenue \
75
+ --model semantic.yml \
76
+ --period "current month" \
77
+ --request request.json \
78
+ --reference reference_revenue.sql \
79
+ --dialect duckdb \
80
+ --engine duckdb \
81
+ --setup setup.sql
82
+ ```
83
+
84
+ A runnable DuckDB example lives in `examples/quickstart/`.
85
+
86
+ ## Define a semantic model
87
+
88
+ Create a YAML file, for example `semantic.yml`. The recommended root key is `semantic`; `model` and `semantic_layer` are also accepted root keys.
89
+
90
+ ```yaml
91
+ semantic:
92
+ version: 1
93
+ modules:
94
+ - identifier: bookings
95
+ project: demo-project
96
+ schema: analytics
97
+ table: bookings
98
+ dimensions:
99
+ - column: country
100
+ type: categorical
101
+ - column: channel
102
+ type: categorical
103
+ metrics:
104
+ - identifier: booking_count
105
+ name: Booking Count
106
+ calculation: count
107
+ time: bookings.created_at
108
+ filters:
109
+ - column: state
110
+ operator: in
111
+ expression: confirmed,completed,cancelled
112
+ slices:
113
+ - name: confirmed_only
114
+ filter:
115
+ column: state
116
+ operator: equals
117
+ expression: confirmed
118
+ dimensions:
119
+ - country
120
+ - channel
121
+
122
+ - identifier: revenue
123
+ name: Revenue
124
+ calculation: sum
125
+ time: bookings.created_at
126
+ value: bookings.revenue
127
+ filters:
128
+ - column: state
129
+ operator: in
130
+ expression: confirmed,completed,cancelled
131
+ dimensions:
132
+ - country
133
+ - channel
134
+
135
+ - identifier: average_daily_rate
136
+ name: Average Daily Rate
137
+ calculation: ratio
138
+ time: bookings.created_at
139
+ numerator: bookings.gross_booking_value
140
+ denominator: bookings.nights
141
+ dimensions:
142
+ - country
143
+
144
+ - identifier: request_success_rate
145
+ name: Request Success Rate
146
+ calculation: custom-ratio
147
+ time: bookings.created_at
148
+ numerator_sql: COUNT(CASE WHEN requested_at IS NOT NULL AND state IN ('confirmed', 'completed') THEN id END)
149
+ denominator_sql: COUNT(CASE WHEN requested_at IS NOT NULL THEN id END)
150
+ allowed_functions: [COUNT, IF]
151
+ dimensions:
152
+ - country
153
+
154
+ - identifier: ad_spend
155
+ project: demo-project
156
+ schema: analytics
157
+ table: marketing_costs
158
+ dimensions:
159
+ - column: country
160
+ type: categorical
161
+ metrics:
162
+ - identifier: marketing_spend
163
+ name: Marketing Spend
164
+ calculation: sum
165
+ time: marketing_costs.date
166
+ value: marketing_costs.cost
167
+ dimensions:
168
+ - country
169
+
170
+ cross_module_metrics:
171
+ - identifier: roas
172
+ name: ROAS
173
+ calculation: derived-ratio
174
+ numerator_metric: revenue
175
+ denominator_metric: marketing_spend
176
+ join_on: time
177
+ join_type: inner
178
+ dimensions:
179
+ - country
180
+ ```
181
+
182
+ Supported metric calculations:
183
+
184
+ - `sum`
185
+ - `count`
186
+ - `count-distinct`
187
+ - `ratio` as safe `SUM(numerator) / SUM(denominator)`
188
+ - `custom-value`
189
+ - `custom-ratio`
190
+ - cross-module `derived-ratio`
191
+
192
+ Supported metric and slice filter operators:
193
+
194
+ - `equals`, `not-equals`
195
+ - `is`, `is-not`
196
+ - `in`, `not-in`
197
+ - `like`, `not-like`
198
+ - `greater-than`, `greater-than-or-equal`
199
+ - `less-than`, `less-than-or-equal`
200
+ - `null`, `not-null`, `is-null`, `is-not-null`
201
+
202
+ ## Create a request shape
203
+
204
+ For CLI use, prefer `--period` for the date window and keep the request file focused on query shape:
205
+
206
+ ```json
207
+ {
208
+ "timeGrain": "monthly",
209
+ "breakdownDimensionIds": ["country"],
210
+ "filters": [
211
+ { "dimensionId": "country", "filterValues": ["DK"] }
212
+ ]
213
+ }
214
+ ```
215
+
216
+ Then compile with a named period:
217
+
218
+ ```bash
219
+ semantic compile revenue \
220
+ --model semantic.yml \
221
+ --period "last 24 complete months" \
222
+ --request request.json \
223
+ --dialect bigquery
224
+ ```
225
+
226
+ Supported request features:
227
+
228
+ - `timeGrain`: `none`, `daily`, `weekly`, `monthly`, `quarterly`, `yearly`
229
+ - `breakdownDimensionIds`
230
+ - `sliceName` for named metric slice filters
231
+ - query filters
232
+ - `timeComparison`: `YoY`, `MoM`, `Last Year`, `Last Month`, `YoY Match Weekday`
233
+ - `cumulativeMode`: `ytd`, `mtd`, `rolling_days`
234
+ - `targetSeries` with explicit target table mapping
235
+ - `excludeOpenPeriod` and `excludeToday` period clipping
236
+
237
+ For API use, or when you need exact boundaries, `fromDate` and `toDate` can still be provided directly in the request JSON.
238
+
239
+ Supported period shortcuts:
240
+
241
+ - `current year` (year-to-date)
242
+ - `current month` (month-to-date)
243
+ - `last N days|weeks|months|quarters|years`
244
+ - `last N complete days|weeks|months|quarters|years`
245
+ - `Q1 2026`
246
+ - `2026`
247
+ - `01-2026`
248
+
249
+ For trailing closed-period reporting, prefer `complete` periods. Example: `--period "last 24 complete months"` returns 24 closed monthly buckets. For current-period-to-date reporting, combine a relative period with `excludeOpenPeriod` in the request. Example: current-year YTD through the last complete month:
250
+
251
+ ```json
252
+ {
253
+ "timeGrain": "monthly",
254
+ "cumulativeMode": "ytd",
255
+ "timeComparison": "YoY",
256
+ "targetSeries": "budget_current",
257
+ "excludeOpenPeriod": true
258
+ }
259
+ ```
260
+
261
+ ## Compile SQL
262
+
263
+ ```bash
264
+ semantic compile revenue \
265
+ --model semantic.yml \
266
+ --period "last 24 complete months" \
267
+ --request request.json \
268
+ --dialect bigquery \
269
+ --format sql
270
+ ```
271
+
272
+ Explain the generated plan:
273
+
274
+ ```bash
275
+ semantic compile revenue \
276
+ --model semantic.yml \
277
+ --period "current year" \
278
+ --request request.json \
279
+ --dialect bigquery \
280
+ --format explain
281
+ ```
282
+
283
+ Write SQL to a file:
284
+
285
+ ```bash
286
+ semantic compile revenue \
287
+ --model semantic.yml \
288
+ --period "current year" \
289
+ --request request.json \
290
+ --dialect bigquery \
291
+ --output query.sql
292
+ ```
293
+
294
+ ## Inspect and validate a model
295
+
296
+ ```bash
297
+ semantic init --dir demo-semantic --with-duckdb-example
298
+
299
+ semantic metrics --model semantic.yml --format json
300
+ semantic search-metrics "gross booking value" --model semantic.yml --format json
301
+ semantic describe revenue --model semantic.yml --format json
302
+ semantic joins --model semantic.yml --format json
303
+ ```
304
+
305
+ `metrics`, `search-metrics`, and `describe` include discovery metadata such as descriptions, `ai_context` synonyms, teams, generic metadata, and relationship/formula notes when present in the model.
306
+
307
+ Run validation:
308
+
309
+ ```bash
310
+ semantic validate \
311
+ --model semantic.yml \
312
+ --check-compilable \
313
+ --format json
314
+ ```
315
+
316
+ Optional: for stronger governance, validate against a column registry. This catches model drift such as missing tables/columns, stale warehouse metadata, and unsafe joins when uniqueness metadata is available:
317
+
318
+ ```bash
319
+ semantic validate \
320
+ --model semantic.yml \
321
+ --column-registry column_registry.json \
322
+ --max-registry-age-hours 24 \
323
+ --require-join-uniqueness \
324
+ --check-compilable \
325
+ --format json
326
+ ```
327
+
328
+ A registry can be generated from warehouse `information_schema` exports or a dbt `manifest.json` when you have those available:
329
+
330
+ ```bash
331
+ semantic registry-from-information-schema \
332
+ --input information_schema_columns.jsonl \
333
+ --warehouse warehouse-prod \
334
+ --output column_registry.json
335
+
336
+ semantic registry-from-dbt-manifest \
337
+ --input target/manifest.json \
338
+ --warehouse warehouse-prod \
339
+ --output column_registry.json
340
+ ```
341
+
342
+ ## Targets
343
+
344
+ Targets are optional benchmark/budget/goal values that can be joined onto metric results. They live outside the semantic model in a normal warehouse table. The compiler only needs to know which table to read and how its columns map to the generic target contract.
345
+
346
+ A simple unsegmented target table has one row per metric, target series, and time period:
347
+
348
+ ```sql
349
+ CREATE TABLE analytics.metric_targets (
350
+ metric_id TEXT,
351
+ target_series TEXT,
352
+ metric_time DATE,
353
+ target_value DOUBLE
354
+ );
355
+ ```
356
+
357
+ Example rows:
358
+
359
+ | metric_id | target_series | metric_time | target_value |
360
+ | --- | --- | --- | ---: |
361
+ | revenue | budget_current | 2026-01-01 | 125000 |
362
+ | revenue | budget_current | 2026-02-01 | 130000 |
363
+ | booking_count | budget_current | 2026-01-01 | 420 |
364
+
365
+ Then request a target series:
366
+
367
+ ```json
368
+ {
369
+ "timeGrain": "monthly",
370
+ "targetSeries": "budget_current"
371
+ }
372
+ ```
373
+
374
+ Compile with the target table:
375
+
376
+ ```bash
377
+ semantic compile revenue \
378
+ --model semantic.yml \
379
+ --period "current year" \
380
+ --request target-request.json \
381
+ --target-table warehouse.analytics.metric_targets \
382
+ --format sql
383
+ ```
384
+
385
+ The default target column contract is:
386
+
387
+ | Purpose | Default column |
388
+ | --- | --- |
389
+ | Metric identifier | `metric_id` |
390
+ | Target series / version | `target_series` |
391
+ | Target period start | `metric_time` |
392
+ | Target value | `target_value` |
393
+ | Breakdown dimensions | one column per grouped breakdown alias |
394
+
395
+ If your table uses different names, map them at compile time:
396
+
397
+ ```bash
398
+ semantic compile revenue \
399
+ --model semantic.yml \
400
+ --period "current year" \
401
+ --request target-request.json \
402
+ --target-table warehouse.analytics.metric_targets \
403
+ --target-metric-column metric \
404
+ --target-series-column series \
405
+ --target-time-column time \
406
+ --target-value-column value \
407
+ --format sql
408
+ ```
409
+
410
+ For breakdown targets, add one target column per breakdown dimension. The grain becomes one row per metric, target series, time period, and target dimension combination. For example, a monthly `revenue` target broken down by `country` and `channel` should have one row for each `(metric_id, target_series, metric_time, country, channel)` combination.
411
+
412
+ The target column should use the same name as the breakdown alias, or the dimension can set `target_column` in the semantic model:
413
+
414
+ ```yaml
415
+ - column: country
416
+ type: categorical
417
+ target_column: market
418
+ ```
419
+
420
+ Example segmented target table:
421
+
422
+ ```sql
423
+ CREATE TABLE analytics.metric_targets (
424
+ metric_id TEXT,
425
+ target_series TEXT,
426
+ metric_time DATE,
427
+ market TEXT,
428
+ channel TEXT,
429
+ target_value DOUBLE
430
+ );
431
+ ```
432
+
433
+ Example segmented rows:
434
+
435
+ | metric_id | target_series | metric_time | market | channel | target_value |
436
+ | --- | --- | --- | --- | --- | ---: |
437
+ | revenue | budget_current | 2026-01-01 | DK | paid_search | 45000 |
438
+ | revenue | budget_current | 2026-01-01 | DK | organic | 30000 |
439
+ | revenue | budget_current | 2026-01-01 | SE | paid_search | 25000 |
440
+ | revenue | budget_current | 2026-01-01 | SE | organic | 25000 |
441
+
442
+ When compiling `breakdownDimensionIds: ["country", "channel"]`, the compiler joins target rows on `metric_id`, `target_series`, `metric_time`, `market`, and `channel`, then returns `target_value` and `target_comparison_percentage`. If the request does not include a breakdown, matching target rows are summed to the requested time grain.
443
+
444
+ If one physical target table stores multiple overlapping grains, pass each possible target dimension column with `--target-dimension-column`:
445
+
446
+ ```bash
447
+ semantic compile revenue \
448
+ --model semantic.yml \
449
+ --period "current year" \
450
+ --request country-request.json \
451
+ --target-table warehouse.analytics.metric_targets \
452
+ --target-dimension-column market \
453
+ --target-dimension-column channel \
454
+ --format sql
455
+ ```
456
+
457
+ The compiler then picks the lowest matching target grain for the requested breakdown before summing targets. This prevents duplicate totals when a table contains both total rows and segmented rows. Use `--target-null-column` only when you want to force a fixed-grain target by requiring a target dimension column to be `NULL`.
458
+
459
+ Targets can be combined with time comparison and cumulative modes. Target columns describe current-period target comparison; historical comparison columns describe historical metric comparison.
460
+
461
+ Ratio target semantics:
462
+
463
+ - additive metrics sum matching target rows
464
+ - same-module `ratio` metrics try to find matching numerator and denominator `sum` metrics with the same time/filter shape, then compute `SUM(numerator target) / SUM(denominator target)`
465
+ - `custom-ratio` metrics use the direct target metric aggregate (`SUM(value)`) because their numerator/denominator are arbitrary SQL snippets, not reusable metric IDs
466
+ - cross-module `derived-ratio` metrics compute `SUM(numerator metric target) / SUM(denominator metric target)`
467
+ - these component target semantics also apply when targets are combined with `timeComparison` or `cumulativeMode`
468
+
469
+ That means a direct target row for the ratio metric itself is not used when component target metrics are available. If a component target is missing, the ratio target is `NULL`.
470
+
471
+ Request filters are applied to target rows too. The compiler maps filter dimensions through `target_column` metadata before filtering the target table.
472
+
473
+ ## Custom SQL governance
474
+
475
+ Custom metrics can use SQL snippets:
476
+
477
+ ```yaml
478
+ - identifier: postgres_custom
479
+ name: Postgres Custom
480
+ calculation: custom-value
481
+ time: bookings.created_at
482
+ custom_sql_dialect: postgres
483
+ allowed_functions: [SUM, CAST]
484
+ sql_expression: "SUM(gross_booking_value::INT)"
485
+ ```
486
+
487
+ Behavior:
488
+
489
+ - snippets are parsed with SQLGlot
490
+ - `custom_sql_dialect` parses from a source dialect and renders into the requested target dialect
491
+ - bare columns are qualified with the base table alias
492
+ - `allowed_functions` is optional; when present, functions outside the allowlist fail compilation
493
+
494
+ SQLGlot models `CASE WHEN ... THEN ... END` branches as `IF`, so include `IF` in `allowed_functions` for governed CASE expressions.
495
+
496
+ ## Compare results
497
+
498
+ Compare compiled SQL for one metric against reference SQL:
499
+
500
+ ```bash
501
+ semantic compare-metric revenue \
502
+ --model semantic.yml \
503
+ --request request.json \
504
+ --reference reference.sql \
505
+ --engine bigquery \
506
+ --project my-gcp-project \
507
+ --location EU \
508
+ --format json
509
+ ```
510
+
511
+ Compare two arbitrary SQL statements:
512
+
513
+ ```bash
514
+ semantic compare-results \
515
+ --left current.sql \
516
+ --right reference.sql \
517
+ --engine duckdb \
518
+ --setup setup.sql \
519
+ --format json
520
+ ```
521
+
522
+ Use this for adoption checks: compare compiler output to your own trusted SQL, dashboards, or finance-approved reports before relying on a metric in production.
523
+
524
+ See `docs/support-matrix.md` for current beta support levels and v1 release gates.
525
+
526
+ ## Execute compiled SQL
527
+
528
+ The compiler does not own warehouse execution. Write SQL to a file and run it with your warehouse client, BI tool, notebook, dbt operation, or agent runtime:
529
+
530
+ ```bash
531
+ semantic compile revenue \
532
+ --model semantic.yml \
533
+ --period "current year" \
534
+ --request request.json \
535
+ --dialect bigquery \
536
+ --output query.sql
537
+ ```
538
+
539
+ ## What is implemented
540
+
541
+ - YAML semantic model loader
542
+ - metric listing, metadata-aware search, description, validation, and join graph CLI
543
+ - SQL / JSON / explain output
544
+ - BigQuery, DuckDB, Postgres, and Snowflake dialect rendering
545
+ - aggregate, ratio, custom, and cross-module metrics
546
+ - safe multi-hop joins over `many-to-one` / `one-to-one` relationships
547
+ - metric filters, query filters, breakdowns, and time grains
548
+ - time comparisons
549
+ - cumulative windows
550
+ - target joins and target/comparison/cumulative combinations
551
+ - custom SQL source-dialect transpilation and function allowlists
552
+ - result comparison harness for DuckDB and BigQuery
553
+ - column registry generation from `information_schema` and dbt `manifest.json`
554
+ - dialect matrix tests that compile and parse representative BigQuery, DuckDB, Postgres, and Snowflake SQL
555
+ - CI lint/test gate
556
+
557
+ ## Development
558
+
559
+ ```bash
560
+ uv venv
561
+ uv pip install -e '.[dev]'
562
+ uv run ruff check .
563
+ uv run pytest
564
+ uv build
565
+ ```