Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Fixed
- Fix SQL export generating multiple PRIMARY KEY constraints for composite keys (#1026)
- Preserve parametrized physicalTypes for SQL export (#1086)
- Emit `CREATE VIEW` DDL when model `type` is `view` in SQL export (#1031)
- Fix incorrect SQL type mappings: SQL Server `double`/`jsonb`, MySQL bare `varchar`, missing Trino types (#1110)

## [0.11.7] - 2026-03-24
Expand Down
8 changes: 7 additions & 1 deletion datacontract/export/sql_exporter.py
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,13 @@ def to_sql_ddl(data_contract: OpenDataContractStandard, server_type: str = "snow


def _to_sql_table(model_name: str, model: SchemaObject, server_type: str = "snowflake") -> str:
if server_type == "databricks":
is_view = model.physicalType is not None and model.physicalType.lower() == "view"
if is_view:
if server_type == "databricks":
result = f"CREATE OR REPLACE VIEW {model_name} (\n"
else:
result = f"CREATE VIEW {model_name} (\n"
elif server_type == "databricks":
# Databricks recommends to use the CREATE OR REPLACE statement for unity managed tables
# https://docs.databricks.com/en/sql/language-manual/sql-ref-syntax-ddl-create-table-using.html
result = f"CREATE OR REPLACE TABLE {model_name} (\n"
Expand Down
4 changes: 3 additions & 1 deletion datacontract/output/ci_output.py
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,9 @@ def _write_github_step_summary(results: List[Tuple[str, Run]], summary_path: str

# Per-contract detail sections
for data_contract_file, run in results:
result_display = RESULT_EMOJI.get(run.result, run.result.value if hasattr(run.result, "value") else str(run.result))
result_display = RESULT_EMOJI.get(
run.result, run.result.value if hasattr(run.result, "value") else str(run.result)
)

n_total = len(run.checks) if run.checks else 0
n_passed = sum(1 for c in run.checks if c.result == "passed") if run.checks else 0
Expand Down
25 changes: 25 additions & 0 deletions tests/fixtures/sql-view-export/datacontract.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
dataContractSpecification: 1.2.1
id: sql-view-export
info:
title: SQL View Export
version: 0.0.1
owner: my-domain-team
servers:
production:
type: postgres
host: localhost
port: 4567
database: test
schema: public
models:
my_view:
type: view
description: A sample view definition
fields:
col_a:
type: varchar
description: First column
required: true
col_b:
type: integer
description: Second column
16 changes: 16 additions & 0 deletions tests/test_export_sql.py
Original file line number Diff line number Diff line change
Expand Up @@ -144,3 +144,19 @@ def test_to_sql_ddl_databricks_unity_catalog_staging():
) COMMENT "A single article that is part of an order.";
""".strip()
assert actual == expected


def test_to_sql_ddl_postgres_view():
"""Model with type=view should emit CREATE VIEW, not CREATE TABLE."""
actual = DataContract(data_contract_file="fixtures/sql-view-export/datacontract.yaml").export("sql")
expected = """
-- Data Contract: sql-view-export
-- SQL Dialect: postgres
CREATE VIEW my_view (
col_a text not null,
col_b integer
);
""".strip()
assert actual == expected
assert "CREATE VIEW" in actual
assert "CREATE TABLE" not in actual
Loading