diff --git a/core-spec/expression_language.md b/core-spec/expression_language.md index f635dc4d..6fdeb735 100644 --- a/core-spec/expression_language.md +++ b/core-spec/expression_language.md @@ -643,6 +643,8 @@ a IS DISTINCT FROM b -- TRUE if one is NULL and other isn't Ossie implementations MAY support additional functions through dialect-specific extensions. When using dialect extensions, the expression must specify the dialect. +Standardized dialect identifiers in the core spec include `ANSI_SQL`, `SNOWFLAKE`, `MDX`, `DAX`, `TABLEAU`, `DATABRICKS`, `MAQL`, and `BIGQUERY`. + The Ossie dialect should always be supported. Other dialects MAY be ignored. There is no guarantee that all different dialects for an expression will act the same, so implementations should be consistent with their dialect handling. This means that if an Ossie model has an expression written in two dialects, the implementation should deterministically choose which dialect to use. ### Declaring Dialect-Specific Expressions @@ -656,18 +658,20 @@ expression: expression: DATE_TRUNC('month', order_date) - dialect: BIGQUERY expression: DATE_TRUNC(order_date, MONTH) + - dialect: DAX + expression: DATE(YEAR(order_date), MONTH(order_date), 1) ``` ### Common Dialect Variations -| Function | ANSI\_SQL | Snowflake | BigQuery | Databricks | PostgreSQL | -| :---- | :---- | :---- | :---- | :---- | :---- | -| Date truncation | `DATE_TRUNC('month', d)` | `DATE_TRUNC('month', d)` | `DATE_TRUNC(d, MONTH)` | `DATE_TRUNC('month', d)` | `DATE_TRUNC('month', d)` | -| Date add | `DATEADD(day, 7, d)` | `DATEADD(day, 7, d)` | `DATE_ADD(d, INTERVAL 7 DAY)` | `DATE_ADD(d, 7)` | `d + INTERVAL '7 days'` | -| String concat | `CONCAT(a, b)` | `CONCAT(a, b)` | `CONCAT(a, b)` | `CONCAT(a, b)` | `a || b` | -| Null coalesce | `COALESCE(a, b)` | `COALESCE(a, b)` or `NVL(a, b)` | `COALESCE(a, b)` or `IFNULL(a, b)` | `COALESCE(a, b)` | `COALESCE(a, b)` | -| Current timestamp | `CURRENT_TIMESTAMP` | `CURRENT_TIMESTAMP()` | `CURRENT_TIMESTAMP()` | `CURRENT_TIMESTAMP()` | `CURRENT_TIMESTAMP` | -| Substring | `SUBSTRING(s, start, len)` | `SUBSTR(s, start, len)` | `SUBSTR(s, start, len)` | `SUBSTRING(s, start, len)` | `SUBSTRING(s, start, len)` | +| Function | ANSI\_SQL | Snowflake | BigQuery | Databricks | PostgreSQL | DAX | +| :---- | :---- | :---- | :---- | :---- | :---- | :---- | +| Date truncation | `DATE_TRUNC('month', d)` | `DATE_TRUNC('month', d)` | `DATE_TRUNC(d, MONTH)` | `DATE_TRUNC('month', d)` | `DATE_TRUNC('month', d)` | `DATE(YEAR(d), MONTH(d), 1)` | +| Date add | `DATEADD(day, 7, d)` | `DATEADD(day, 7, d)` | `DATE_ADD(d, INTERVAL 7 DAY)` | `DATE_ADD(d, 7)` | `d + INTERVAL '7 days'` | `DATEADD(d, 7, DAY)` | +| String concat | `CONCAT(a, b)` | `CONCAT(a, b)` | `CONCAT(a, b)` | `CONCAT(a, b)` | `CONCAT_WS('', a, b)` | `CONCATENATE(a, b)` or `a & b` | +| Null coalesce | `COALESCE(a, b)` | `COALESCE(a, b)` or `NVL(a, b)` | `COALESCE(a, b)` or `IFNULL(a, b)` | `COALESCE(a, b)` | `COALESCE(a, b)` | `COALESCE(a, b)` | +| Current timestamp | `CURRENT_TIMESTAMP` | `CURRENT_TIMESTAMP()` | `CURRENT_TIMESTAMP()` | `CURRENT_TIMESTAMP()` | `CURRENT_TIMESTAMP` | `NOW()` or `UTCNOW()` | +| Substring | `SUBSTRING(s, start, len)` | `SUBSTR(s, start, len)` | `SUBSTR(s, start, len)` | `SUBSTRING(s, start, len)` | `SUBSTRING(s, start, len)` | `MID(s, start, len)` | ### diff --git a/core-spec/osi-schema.json b/core-spec/osi-schema.json index 163db20a..93a35f93 100644 --- a/core-spec/osi-schema.json +++ b/core-spec/osi-schema.json @@ -23,12 +23,12 @@ "$defs": { "Dialect": { "type": "string", - "enum": ["ANSI_SQL", "SNOWFLAKE", "MDX", "TABLEAU", "DATABRICKS", "MAQL", "BIGQUERY"], + "enum": ["ANSI_SQL", "SNOWFLAKE", "MDX", "DAX", "TABLEAU", "DATABRICKS", "MAQL", "BIGQUERY"], "description": "Supported SQL and expression language dialects" }, "Vendor": { "type": "string", - "examples": ["COMMON", "SNOWFLAKE", "SALESFORCE", "DBT", "DATABRICKS", "GOODDATA", "WISDOM"], + "examples": ["COMMON", "SNOWFLAKE", "SALESFORCE", "DBT", "DATABRICKS", "GOODDATA", "WISDOM", "MICROSOFT"], "description": "Vendor name for custom extensions. Any string value is accepted." }, "AIContext": { diff --git a/core-spec/spec.md b/core-spec/spec.md index b7c285ed..dfdd5548 100644 --- a/core-spec/spec.md +++ b/core-spec/spec.md @@ -54,6 +54,7 @@ Supported SQL and expression language dialects for metrics and field definitions | `ANSI_SQL` | Standard SQL dialect | | `SNOWFLAKE` | Snowflake SQL | | `MDX` | Multi-Dimensional Expressions | +| `DAX` | Data Analysis Expressions | | `TABLEAU` | Tableau calculations | | `DATABRICKS` | Databricks SQL | | `MAQL` | GoodData MAQL (Metric Analysis and Query Language) | diff --git a/core-spec/spec.yaml b/core-spec/spec.yaml index 2c9c73e5..31271054 100644 --- a/core-spec/spec.yaml +++ b/core-spec/spec.yaml @@ -33,6 +33,7 @@ dialects: - "ANSI_SQL" # Standard SQL dialect - "SNOWFLAKE" # Snowflake - "MDX" # Multi-Dimensional Expressions + - "DAX" # Data Analysis Expressions - "TABLEAU" # Tableau - "DATABRICKS" # Databricks SQL - "MAQL" # GoodData MAQL (Multi-Dimensional Analytical Query Language) diff --git a/examples/tpcds_semantic_model.yaml b/examples/tpcds_semantic_model.yaml index c20f288e..f605d2a2 100644 --- a/examples/tpcds_semantic_model.yaml +++ b/examples/tpcds_semantic_model.yaml @@ -517,6 +517,8 @@ semantic_model: dialects: - dialect: ANSI_SQL expression: SUM(store_sales.ss_ext_sales_price) + - dialect: DAX + expression: SUM(store_sales.ss_ext_sales_price) description: Total sales revenue across all transactions ai_context: synonyms: diff --git a/python/src/ossie/models.py b/python/src/ossie/models.py index 9be74a6c..223333c4 100644 --- a/python/src/ossie/models.py +++ b/python/src/ossie/models.py @@ -28,6 +28,7 @@ class OSIDialect(str, Enum): ANSI_SQL = "ANSI_SQL" SNOWFLAKE = "SNOWFLAKE" MDX = "MDX" + DAX = "DAX" MAQL = "MAQL" TABLEAU = "TABLEAU" DATABRICKS = "DATABRICKS" diff --git a/validation/validate.py b/validation/validate.py index 258d34f1..e20c867b 100644 --- a/validation/validate.py +++ b/validation/validate.py @@ -67,12 +67,13 @@ "DATABRICKS": "databricks", "BIGQUERY": "bigquery", "MDX": None, # Not supported by sqlglot, skip validation + "DAX": None, # Not supported by sqlglot, skip validation "TABLEAU": None, # Not supported by sqlglot, skip validation "MAQL": None, # Not supported by sqlglot, skip validation } # Dialects that sqlglot cannot parse -SKIP_SQL_VALIDATION = {"MDX", "TABLEAU", "MAQL"} +SKIP_SQL_VALIDATION = {"MDX", "DAX", "TABLEAU", "MAQL"} def validate_schema(data: dict, schema: dict) -> list[str]: