From a9e63bba2cc0bd7bca7517bb174430345eec5219 Mon Sep 17 00:00:00 2001 From: Kevin Longe <273747162+kvlonge@users.noreply.github.com> Date: Fri, 9 Oct 2026 06:58:51 +0100 Subject: [PATCH 01/31] feat(native): recover CTE facts natively behind the preview engine --- .../analysis_session/_helpers/cte_facts.rs | 1297 +++++++++++++++++ .../analysis_session/_helpers/dict_walk.rs | 247 +++- .../_helpers/dynamic_pivot.rs | 29 +- .../analysis_session/_helpers/enrichment.rs | 58 +- .../assembly/analysis_session/_helpers/mod.rs | 1 + .../analysis_session/_helpers/session.rs | 1 + .../assembly/analysis_session/constants.rs | 74 + .../src/assembly/analysis_session/models.rs | 2 + .../analysis_session/tests/helpers.rs | 55 +- .../tests/test_analysis_session.rs | 70 +- .../analysis_session/tests/test_types.rs | 15 + .../_helpers/analysis_session/session.rs | 3 + .../analysis_corpus/analysis_session.py | 30 + .../classes/analysis_features.py | 22 + .../analysis_session/_helpers/session_rows.py | 27 + .../compiler/analysis_session/constants.py | 2 + .../compiler/analysis_session/_test_types.py | 1 + .../compiler/analysis_session/helpers.py | 48 +- .../test_native_model_analysis.py | 10 +- 19 files changed, 1947 insertions(+), 45 deletions(-) create mode 100644 crates/sqlbuild-analysis/src/assembly/analysis_session/_helpers/cte_facts.rs diff --git a/crates/sqlbuild-analysis/src/assembly/analysis_session/_helpers/cte_facts.rs b/crates/sqlbuild-analysis/src/assembly/analysis_session/_helpers/cte_facts.rs new file mode 100644 index 0000000000..dbb9d925c9 --- /dev/null +++ b/crates/sqlbuild-analysis/src/assembly/analysis_session/_helpers/cte_facts.rs @@ -0,0 +1,1297 @@ +//! Python's CTE fact recovery and filtered non-null outputs, over the wheel's expression view. + +use std::cell::Cell; +use std::collections::HashSet; +use std::rc::Rc; + +use polyglot_sql::Expression; +use polyglot_sql::traversal::ExpressionWalk; +use serde_json::{Map, Value}; + +use crate::assembly::analysis_session::_helpers::dict_walk::{ + PyValue, alias_or_name, arg, children, column_table_name, direct_tables, expression_args, + find_all, is_star, kind, name, name_payload, output_name, parse_one, projected_expression, + py_truthy, python_int, raw_name_payload, set_operation, to_dict, top_level_ctes, + unwrap_annotations, where_clause, +}; +use crate::assembly::analysis_session::constants::{ + BINARY_OPERAND_COUNT, BOOLEAN_RESULT_AST_KINDS, BOOLEAN_TYPE, CASE_AST_KIND, CAST_AST_KIND, + CAST_AST_KINDS, COALESCE_AST_KIND, COLUMN_AST_KIND, CONCAT_AST_KIND, CONDITIONAL_RESULT_RULE, + COUNT_AST_KIND, CUSTOM_TYPE_NAME, DECIMAL_TYPE, FIRST_ARG_RULE, FUNCTION_AST_KIND, + IF_FUNC_AST_KIND, IS_NULL_AST_KIND, JOIN_FULL, JOIN_LEFT, JOIN_RIGHT, LITERAL_AST_KIND, + NON_NULL_NULLABILITY, NULL_AST_KIND, NULL_SET_OPERATION_TYPE, NULLABLE_NULLABILITY, + NULLIF_FUNCTION_NAME, POLYGLOT_TYPE_NAMES, SELECT_AST_KIND, SET_OPERATION_AST_KINDS, + STRING_LITERAL_TYPE, SUBSTRING_AST_KIND, TABLE_AST_KIND, TEXT_TYPE, TIMESTAMP_TYPE_NAME, + TIMESTAMP_TZ_TYPE, TRY_CAST_AST_KIND, TYPE_PASSTHROUGH_AST_KINDS, UNKNOWN_NULLABILITY, + VARCHAR_DATA_TYPES, WILDCARD, +}; +use crate::assembly::analysis_session::types::{Pairs, Shapes}; +use crate::type_system::main::normalize_type::normalize_type; +use crate::type_system::models::{NormalizedType, TypeFamily}; + +/// A relation's column types; the `Rc` is the identity Python's `id()` dedup compares. +type Shape = Rc; +/// Relation facts in Python dict order. +type Relations = Vec<(String, Shape)>; +/// A relation's column nullabilities. +type NullShape = Rc>; +type NullRelations = Vec<(String, NullShape)>; +/// Python's `NonNullFilterContext`: the selected relations and the proven non-null columns. +type FilterContext = (Vec<(String, NullShape)>, HashSet<(String, String)>); +/// The result of one step, or the reason Python's result is not reproduced. +type Fact = Result; + +/// The inference profile facts the recovery reads. +pub(crate) struct RecoveryProfile<'a> { + pub(crate) dialect: &'a str, + pub(crate) function_return_types: &'a Pairs, + /// Adapter nullability rules by function name; None where a rule is not one Python ships. + pub(crate) rules: Option<&'a Pairs>, +} + +/// One enrichment's recovery request. +pub(crate) struct RecoveryInput<'a> { + pub(crate) cleaned_sql: &'a str, + pub(crate) input_schemas: &'a Shapes, + /// Whether Python runs `_polyglot_cte_passthrough_facts` past its early return. + pub(crate) recover: bool, + /// Whether a filter reads NULL, so Python looks for filtered non-null outputs. + pub(crate) null_filter: bool, + pub(crate) profile: RecoveryProfile<'a>, +} + +/// What Python's compact re-analysis takes from the parsed query. +#[derive(Debug, Default)] +pub(crate) struct Recovery { + pub(crate) types: Vec<(String, String)>, + pub(crate) nullability: Vec<(String, &'static str)>, + pub(crate) direct_outputs: HashSet, + pub(crate) non_null_outputs: HashSet, +} + +/// Python's CTE pass-through facts and filtered non-null outputs, or why they defer. +pub(crate) fn recovery(input: &RecoveryInput<'_>) -> Fact { + NON_ASCII_FOLDED.set(false); + let recovered: Fact = recovered(input); + if NON_ASCII_FOLDED.replace(false) { + return Err("non-ASCII text Python casefolds or upper-cases differently".to_owned()); + } + recovered +} + +thread_local! { + /// Whether this recovery folded non-ASCII text, where Python's `casefold`/`upper` may differ. + static NON_ASCII_FOLDED: Cell = const { Cell::new(false) }; +} + +fn recovered(input: &RecoveryInput<'_>) -> Fact { + let mut recovery: Recovery = Recovery::default(); + let mut parsed: Option> = None; + if input.recover { + let root: Option = parse_one(input.cleaned_sql, input.profile.dialect)?; + if let Some(root) = &root { + recovery = passthrough_facts(root, input, recovery)?; + } + parsed = Some(root); + } + if input.null_filter { + let root: Option = match parsed { + Some(root) => root, + None => parse_one(input.cleaned_sql, input.profile.dialect)?, + }; + if let Some(root) = &root { + recovery.non_null_outputs = filtered_non_null(root, &input_nullability(input))?; + } + } + Ok(recovery) +} + +/// Python's enrichment types: the input shapes. +fn input_types(input: &RecoveryInput<'_>) -> Relations { + input + .input_schemas + .iter() + .map(|(table, shape)| (table.clone(), Rc::new(shape.clone()))) + .collect() +} + +/// Python's enrichment nullability: every input column unknown. +fn input_nullability(input: &RecoveryInput<'_>) -> NullRelations { + input + .input_schemas + .iter() + .map(|(table, shape)| (table.clone(), Rc::new(unknown_columns(shape)))) + .collect() +} + +fn unknown_columns(shape: &Pairs) -> Vec<(String, &'static str)> { + shape + .iter() + .map(|(column, _)| (column.clone(), UNKNOWN_NULLABILITY)) + .collect() +} + +/// Python's `_polyglot_cte_passthrough_facts` after a successful parse. +fn passthrough_facts( + root: &Expression, + input: &RecoveryInput<'_>, + mut recovery: Recovery, +) -> Fact { + let ctes: Vec<(&str, bool, &Expression)> = top_level_ctes(root); + if ctes.is_empty() { + return Ok(recovery); + } + if input.profile.rules.is_none() { + return Err("an adapter nullability rule Python does not ship".to_owned()); + } + let context = Context { + profile: &input.profile, + }; + recovery.types = context.passthrough_types(root, &input_types(input))?; + recovery.nullability = context.passthrough_nullability(root, &input_nullability(input))?; + let names: HashSet = ctes.iter().map(|(name, _, _)| casefold(name)).collect(); + recovery.direct_outputs = direct_cte_outputs(root, &names); + Ok(recovery) +} + +/// Python's `_polyglot_direct_cte_output_names`. +fn direct_cte_outputs(root: &Expression, cte_names: &HashSet) -> HashSet { + let reads_cte: bool = direct_tables(root) + .iter() + .any(|table| cte_names.contains(&casefold(table.get_name()))); + if !reads_cte { + return HashSet::new(); + } + let mut outputs: HashSet = HashSet::new(); + for projection in root.get_expressions() { + let projection: Option<&Expression> = unwrap_annotations(Some(projection)); + if kind(projected_expression(projection)) != COLUMN_AST_KIND { + continue; + } + let output: &str = output_name(projection); + if !output.is_empty() && output != WILDCARD { + outputs.insert(output.to_owned()); + } + } + outputs +} + +/// The profile every resolver reads. +struct Context<'a, 'b> { + profile: &'a RecoveryProfile<'b>, +} + +impl Context<'_, '_> { + /// Python's `_polyglot_cte_passthrough_types_from_parsed`. + fn passthrough_types( + &self, + root: &Expression, + facts: &Relations, + ) -> Fact> { + let ctes: Vec<(&str, bool, &Expression)> = top_level_ctes(root); + if ctes.is_empty() { + return Ok(Vec::new()); + } + let mut relations: Relations = referenced_facts(root, facts); + for (cte_name, has_column_aliases, body) in ctes { + if has_column_aliases { + continue; + } + let inferred: Vec<(String, String)> = + self.select_output_types(Some(body), &relations)?; + if !inferred.is_empty() { + relations = dict_set(relations, cte_name, Rc::new(inferred)); + } + } + if SET_OPERATION_AST_KINDS.contains(&kind(Some(root))) { + return self.set_operation_types(root, &relations); + } + self.direct_select_types(Some(root), &relations) + } + + /// Python's `_polyglot_select_output_types`. + fn select_output_types( + &self, + select: Option<&Expression>, + relations: &Relations, + ) -> Fact> { + let Some(select) = unwrap_annotations(select) else { + return Err("Python reads CTEs of a missing expression".to_owned()); + }; + if !top_level_ctes(select).is_empty() { + return self.passthrough_types(select, relations); + } + if SET_OPERATION_AST_KINDS.contains(&kind(Some(select))) { + return self.set_operation_types(select, relations); + } + self.direct_select_types(Some(select), relations) + } + + /// Python's `_polyglot_set_operation_output_types`. + fn set_operation_types( + &self, + operation: &Expression, + relations: &Relations, + ) -> Fact> { + let Some((left, right, by_name)) = set_operation(operation) else { + return Ok(Vec::new()); + }; + let mut output: Vec<(String, String)> = Vec::new(); + if by_name { + let left_types: Vec<(String, String)> = + self.select_output_types(Some(left), relations)?; + let right_types: Vec<(String, String)> = + self.select_output_types(Some(right), relations)?; + for (name, left_type) in &left_types { + let right_type: Option<&str> = ci_get(&right_types, name).map(String::as_str); + if let Some(common) = self.common_type(Some(left_type), right_type)? { + output = dict_set(output, name, common); + } + } + return Ok(output); + } + let left_slots: Vec = self.type_slots(Some(left), relations)?; + if left_slots.is_empty() { + return Ok(Vec::new()); + } + let right_slots: Vec = self.type_slots(Some(right), relations)?; + if right_slots.is_empty() || left_slots.len() != right_slots.len() { + return Ok(Vec::new()); + } + for ((name, left_type), (_, right_type)) in left_slots.iter().zip(&right_slots) { + if let Some(common) = self.common_type(left_type.as_deref(), right_type.as_deref())? { + output = dict_set(output, name, common); + } + } + Ok(output) + } + + /// Python's `_polyglot_select_output_type_slots`. + fn type_slots(&self, select: Option<&Expression>, relations: &Relations) -> Fact> { + let select: Option<&Expression> = unwrap_annotations(select); + if let Some((left, right, by_name)) = select.and_then(set_operation) { + let left_slots: Vec = self.type_slots(Some(left), relations)?; + if left_slots.is_empty() { + return Ok(Vec::new()); + } + let right_slots: Vec = self.type_slots(Some(right), relations)?; + if right_slots.is_empty() { + return Ok(Vec::new()); + } + return self.combined_slots(&left_slots, &right_slots, by_name); + } + let Some(select) = select.filter(|select| kind(Some(select)) == SELECT_AST_KIND) else { + return Ok(Vec::new()); + }; + let inferred: Vec<(String, String)> = self.direct_select_types(Some(select), relations)?; + let mut slots: Vec = Vec::new(); + for projection in select.get_expressions() { + let projection: Option<&Expression> = unwrap_annotations(Some(projection)); + let output: &str = output_name(projection); + if is_star(projection) || output.is_empty() || output == WILDCARD { + return Ok(Vec::new()); + } + let mut inferred_type: Option = ci_get(&inferred, output).cloned(); + if inferred_type.is_none() && kind(projected_expression(projection)) == NULL_AST_KIND { + inferred_type = Some(NULL_SET_OPERATION_TYPE.to_owned()); + } + slots.push((output.to_owned(), inferred_type)); + } + Ok(slots) + } + + fn combined_slots(&self, left: &[Slot], right: &[Slot], by_name: bool) -> Fact> { + if by_name { + let mut right_types: Vec = Vec::new(); + for (name, slot_type) in right { + right_types = dict_set(right_types, name, slot_type.clone()); + } + let mut slots: Vec = Vec::with_capacity(left.len()); + for (name, left_type) in left { + let right_type: Option<&str> = ci_get_optional(&right_types, name); + slots.push(( + name.clone(), + self.common_type(left_type.as_deref(), right_type)?, + )); + } + return Ok(slots); + } + if left.len() != right.len() { + return Ok(Vec::new()); + } + let mut slots: Vec = Vec::with_capacity(left.len()); + for ((name, left_type), (_, right_type)) in left.iter().zip(right) { + slots.push(( + name.clone(), + self.common_type(left_type.as_deref(), right_type.as_deref())?, + )); + } + Ok(slots) + } + + /// Python's `_polyglot_common_set_operation_type`. + fn common_type(&self, left: Option<&str>, right: Option<&str>) -> Fact> { + let null: Option<&str> = Some(NULL_SET_OPERATION_TYPE); + if left == null && right == null { + return Ok(None); + } + if left == null { + return Ok(right.map(str::to_owned)); + } + if right == null { + return Ok(left.map(str::to_owned)); + } + let (Some(left), Some(right)) = (left, right) else { + return Ok(None); + }; + Ok(self.types_equal(left, right)?.then(|| left.to_owned())) + } + + /// Python's `_polyglot_types_equal`. + fn types_equal(&self, left: &str, right: &str) -> Fact { + Ok(left == right || self.normalized(left)? == self.normalized(right)?) + } + + /// Python's `normalize_type`, or a deferral where the native normalization defers. + fn normalized(&self, type_sql: &str) -> Fact { + normalize_type(type_sql, self.profile.dialect) + .map(|normalization| normalization.normalized) + .ok_or_else(|| format!("type normalization of {type_sql} defers")) + } + + /// Python's `_polyglot_direct_select_output_types`. + fn direct_select_types( + &self, + select: Option<&Expression>, + relations: &Relations, + ) -> Fact> { + let Some(select) = select.filter(|select| kind(Some(select)) == SELECT_AST_KIND) else { + return Ok(Vec::new()); + }; + let mut by_alias: Relations = Vec::new(); + for table in direct_tables(select) { + let table_name: &str = table.get_name(); + let Some(column_types) = ci_get(relations, table_name) else { + continue; + }; + let column_types: Shape = Rc::clone(column_types); + by_alias = dict_set(by_alias, table_name, Rc::clone(&column_types)); + let alias: &str = alias_or_name(table); + if !alias.is_empty() { + by_alias = dict_set(by_alias, alias, column_types); + } + } + let mut inferred: Vec<(String, String)> = Vec::new(); + for projection in select.get_expressions() { + let projection: Option<&Expression> = unwrap_annotations(Some(projection)); + if let Some(star) = projection.filter(|projection| is_star(Some(projection))) { + for (column, column_type) in star_types(star, &by_alias)? { + inferred = dict_set(inferred, &column, column_type); + } + continue; + } + let output: &str = output_name(projection); + if output.is_empty() || output == WILDCARD { + continue; + } + let expression: Option<&Expression> = projected_expression(projection); + if let Some(inferred_type) = self.expression_type_in(expression, &by_alias)? { + inferred = dict_set(inferred, output, inferred_type); + } + } + Ok(inferred) + } + + /// Python's `_polyglot_direct_expression_type`. + fn expression_type_in( + &self, + expression: Option<&Expression>, + by_alias: &Relations, + ) -> Fact> { + let expression: Option<&Expression> = unwrap_annotations(expression); + let node_kind: &str = kind(expression); + if let Some(inferred) = self.expression_type(expression)? { + return Ok(Some(inferred)); + } + let Some(node) = expression else { + return Ok(None); + }; + if node_kind == COLUMN_AST_KIND { + return direct_column_type(node, by_alias); + } + if BOOLEAN_RESULT_AST_KINDS.contains(&node_kind) { + return Ok(Some(BOOLEAN_TYPE.to_owned())); + } + if node_kind == CONCAT_AST_KIND { + return self.concat_type(node, by_alias); + } + if node_kind == SUBSTRING_AST_KIND { + return self.value_type(arg(Some(node), "this")?, by_alias); + } + if TYPE_PASSTHROUGH_AST_KINDS.contains(&node_kind) { + if let Some(inner) = node.get_this() { + return self.expression_type_in(Some(inner), by_alias); + } + return self.value_type(arg(Some(node), "this")?, by_alias); + } + let results: Vec> = result_expressions(node)?; + if results.is_empty() { + return Ok(None); + } + self.common_result_type(&results, by_alias) + } + + /// The type of a converted payload value; a non-expression has none. + fn value_type(&self, value: Option, by_alias: &Relations) -> Fact> { + match value { + Some(PyValue::Expr(inner)) => self.expression_type_in(Some(&inner), by_alias), + _ => Ok(None), + } + } + + fn concat_type(&self, node: &Expression, by_alias: &Relations) -> Fact> { + let operands: Vec = [arg(Some(node), "left")?, arg(Some(node), "right")?] + .into_iter() + .flatten() + .collect(); + if operands.len() != BINARY_OPERAND_COUNT { + return Ok(None); + } + let mut operand_types: Vec = Vec::with_capacity(BINARY_OPERAND_COUNT); + for operand in operands { + let PyValue::Expr(operand) = operand else { + return Ok(None); + }; + if string_literal(&operand)? { + operand_types.push(TEXT_TYPE.to_owned()); + continue; + } + let Some(operand_type) = self.expression_type_in(Some(&operand), by_alias)? else { + return Ok(None); + }; + operand_types.push(operand_type); + } + for operand_type in &operand_types { + if self.normalized(operand_type)?.family != TypeFamily::String { + return Ok(None); + } + } + Ok(Some(TEXT_TYPE.to_owned())) + } + + /// Python's `_polyglot_common_result_type`. + fn common_result_type( + &self, + expressions: &[Option], + by_alias: &Relations, + ) -> Fact> { + let mut resolved: Vec = Vec::new(); + for expression in expressions { + let Some(expression) = expression else { + return Ok(None); + }; + let expression: Option<&Expression> = unwrap_annotations(Some(expression)); + if kind(expression) == NULL_AST_KIND { + continue; + } + let Some(resolved_type) = self.expression_type_in(expression, by_alias)? else { + return Ok(None); + }; + resolved.push(resolved_type); + } + let Some((first, others)) = resolved.split_first() else { + return Ok(None); + }; + for other in others { + if !self.types_equal(first, other)? { + return Ok(None); + } + } + Ok(Some(first.clone())) + } + + /// Python's `_polyglot_expression_type`. + fn expression_type(&self, expression: Option<&Expression>) -> Fact> { + let node_kind: &str = kind(expression); + let node_name: &str = name(expression); + let function_name: &str = if node_name.is_empty() { + node_kind + } else { + node_name + }; + if node_kind != COLUMN_AST_KIND + && let Some(declared) = self.function_return_type(function_name)? + { + return Ok(Some(declared)); + } + if BOOLEAN_RESULT_AST_KINDS.contains(&node_kind) { + return Ok(Some(BOOLEAN_TYPE.to_owned())); + } + let Some(node) = expression.filter(|_| CAST_AST_KINDS.contains(&node_kind)) else { + return Ok(None); + }; + let value: Value = to_dict(node)?; + let target: Option<&Map> = value + .get(node_kind) + .and_then(Value::as_object) + .and_then(|payload| payload.get("to")) + .and_then(Value::as_object); + Ok(target.and_then(cast_target_type)) + } + + /// Python's `ExpressionInferenceProfile.function_return_type`. + fn function_return_type(&self, function_name: &str) -> Fact> { + let upper: String = upper(function_name); + Ok(self + .profile + .function_return_types + .iter() + .find(|(declared, _)| *declared == upper) + .map(|(_, return_type)| return_type.clone())) + } + + /// Python's `_polyglot_cte_passthrough_nullability_from_parsed`. + fn passthrough_nullability( + &self, + root: &Expression, + facts: &NullRelations, + ) -> Fact> { + let ctes: Vec<(&str, bool, &Expression)> = top_level_ctes(root); + if ctes.is_empty() { + return Ok(Vec::new()); + } + let mut relations: NullRelations = referenced_facts(root, facts); + for (cte_name, has_column_aliases, body) in ctes { + if has_column_aliases { + continue; + } + let inferred: Vec<(String, &'static str)> = + self.direct_select_nullability(body, &relations)?; + if !inferred.is_empty() { + relations = dict_set(relations, cte_name, Rc::new(inferred)); + } + } + self.direct_select_nullability(root, &relations) + } + + /// Python's `_polyglot_direct_select_output_nullability`. + fn direct_select_nullability( + &self, + select: &Expression, + relations: &NullRelations, + ) -> Fact> { + if kind(Some(select)) != SELECT_AST_KIND { + return Ok(Vec::new()); + } + let mut scoped: NullRelations = Vec::new(); + for table in direct_tables(select) { + let table_name: &str = table.get_name(); + if let Some(facts) = ci_get(relations, table_name) { + scoped = dict_set(scoped, table_name, Rc::new(facts.as_ref().clone())); + } + } + let known: bool = scoped.iter().any(|(_, facts)| has_known_nullability(facts)); + let (aliases, scoped): (Vec<(String, &'static str)>, NullRelations) = if known { + alias_nullability(select, scoped)? + } else { + (Vec::new(), scoped) + }; + let filter: Option = filter_context(select, &scoped)?; + let mut inferred: Vec<(String, &'static str)> = Vec::new(); + for projection in select.get_expressions() { + let projection: Option<&Expression> = unwrap_annotations(Some(projection)); + if is_star(projection) { + continue; + } + let output: &str = output_name(projection); + if output.is_empty() || output == WILDCARD { + continue; + } + let expression: Option<&Expression> = projected_expression(projection); + let nullability: &'static str = if non_null_after_filter(expression, filter.as_ref())? { + NON_NULL_NULLABILITY + } else if known { + self.nullability(expression, &aliases, &scoped)? + } else { + self.shallow_nullability(expression)? + }; + if nullability != UNKNOWN_NULLABILITY { + inferred = dict_set(inferred, output, nullability); + } + } + Ok(inferred) + } + + /// Python's `_infer_polyglot_nullability`. + fn nullability( + &self, + expression: Option<&Expression>, + aliases: &[(String, &'static str)], + scoped: &NullRelations, + ) -> Fact<&'static str> { + let node_kind: &str = kind(expression); + match node_kind { + NULL_AST_KIND => return Ok(NULLABLE_NULLABILITY), + LITERAL_AST_KIND | COUNT_AST_KIND | IS_NULL_AST_KIND => { + return Ok(NON_NULL_NULLABILITY); + } + TRY_CAST_AST_KIND => return Ok(UNKNOWN_NULLABILITY), + _ => {} + } + if let Some(column) = expression.filter(|_| node_kind == COLUMN_AST_KIND) { + return column_nullability(column, aliases, scoped); + } + if node_kind == CAST_AST_KIND { + return match expression.and_then(Expression::get_this) { + Some(inner) => self.nullability(Some(inner), aliases, scoped), + None => Ok(UNKNOWN_NULLABILITY), + }; + } + let rule: Option<&str> = self.rule(node_kind)?; + if node_kind != COALESCE_AST_KIND && rule.is_none() { + return Ok(UNKNOWN_NULLABILITY); + } + let mut arguments: Vec<&'static str> = Vec::new(); + for child in expression_args(expression) { + arguments.push(self.nullability(Some(child), aliases, scoped)?); + } + Ok(combined(node_kind, rule, &arguments)) + } + + /// Python's `_infer_polyglot_shallow_nullability`. + fn shallow_nullability(&self, expression: Option<&Expression>) -> Fact<&'static str> { + let node_kind: &str = kind(expression); + match node_kind { + NULL_AST_KIND => return Ok(NULLABLE_NULLABILITY), + LITERAL_AST_KIND | COUNT_AST_KIND | IS_NULL_AST_KIND => { + return Ok(NON_NULL_NULLABILITY); + } + COLUMN_AST_KIND | TRY_CAST_AST_KIND => return Ok(UNKNOWN_NULLABILITY), + _ => {} + } + if node_kind == CAST_AST_KIND { + return match expression.and_then(Expression::get_this) { + Some(inner) => self.shallow_nullability(Some(inner)), + None => Ok(UNKNOWN_NULLABILITY), + }; + } + let rule: Option<&str> = self.rule(node_kind)?; + if node_kind != COALESCE_AST_KIND && rule.is_none() { + return Ok(UNKNOWN_NULLABILITY); + } + let mut arguments: Vec<&'static str> = Vec::new(); + for child in expression_args(expression) { + arguments.push(self.shallow_nullability(Some(child))?); + } + Ok(combined(node_kind, rule, &arguments)) + } + + /// `inference_profile.function_nullability_rule(kind)`. + fn rule(&self, node_kind: &str) -> Fact> { + let rules: &Pairs = self + .profile + .rules + .ok_or("an adapter nullability rule Python does not ship")?; + let upper: String = upper(node_kind); + Ok(rules + .iter() + .find(|(declared, _)| *declared == upper) + .map(|(_, rule)| rule.as_str())) + } +} + +/// One set operation output slot: its name and type, None where unproven. +type Slot = (String, Option); + +/// COALESCE's or an adapter rule's nullability over the argument nullabilities. +fn combined(node_kind: &str, rule: Option<&str>, arguments: &[&'static str]) -> &'static str { + if node_kind == COALESCE_AST_KIND { + if arguments.contains(&NON_NULL_NULLABILITY) { + return NON_NULL_NULLABILITY; + } + if !arguments.is_empty() && arguments.iter().all(|value| *value == NULLABLE_NULLABILITY) { + return NULLABLE_NULLABILITY; + } + return UNKNOWN_NULLABILITY; + } + match rule { + Some(FIRST_ARG_RULE) => arguments.first().copied().unwrap_or(UNKNOWN_NULLABILITY), + Some(CONDITIONAL_RESULT_RULE) => conditional_result(arguments), + _ => UNKNOWN_NULLABILITY, + } +} + +/// Python's `conditional_result_nullability`. +fn conditional_result(arguments: &[&'static str]) -> &'static str { + let [_, when_true, when_false, ..] = arguments else { + return UNKNOWN_NULLABILITY; + }; + if *when_true == NON_NULL_NULLABILITY && *when_false == NON_NULL_NULLABILITY { + return NON_NULL_NULLABILITY; + } + if *when_true == NULLABLE_NULLABILITY || *when_false == NULLABLE_NULLABILITY { + return NULLABLE_NULLABILITY; + } + UNKNOWN_NULLABILITY +} + +/// Python's `_infer_polyglot_column_nullability`. +fn column_nullability( + column: &Expression, + aliases: &[(String, &'static str)], + scoped: &NullRelations, +) -> Fact<&'static str> { + let column_name: &str = column.get_name(); + if column_name.is_empty() { + return Ok(UNKNOWN_NULLABILITY); + } + let table_name: String = column_table_name(column)?; + if !table_name.is_empty() { + if dict_get(aliases, &table_name) == Some(&NULLABLE_NULLABILITY) { + return Ok(NULLABLE_NULLABILITY); + } + return Ok(dict_get(scoped, &table_name) + .and_then(|facts| dict_get(facts, column_name)) + .copied() + .unwrap_or(UNKNOWN_NULLABILITY)); + } + let matches: Vec<&'static str> = scoped + .iter() + .filter_map(|(_, facts)| dict_get(facts, column_name).copied()) + .collect(); + Ok(match matches.as_slice() { + [only] => only, + _ => UNKNOWN_NULLABILITY, + }) +} + +/// Python's `_polyglot_alias_nullability_from_select`, copying facts to aliases in `scoped`. +fn alias_nullability( + select: &Expression, + mut scoped: NullRelations, +) -> Fact<(Vec<(String, &'static str)>, NullRelations)> { + let mut aliases: Vec<(String, &'static str)> = Vec::new(); + let mut current: Vec = Vec::new(); + let value: Value = to_dict(select)?; + let Some(payload) = value.get("select").and_then(Value::as_object) else { + return Ok((aliases, scoped)); + }; + let from: Option<&Vec> = payload + .get("from") + .and_then(Value::as_object) + .and_then(|from| from.get("expressions")) + .and_then(Value::as_array); + if let Some([relation]) = from.map(Vec::as_slice) + && let Some(table) = relation + .get(TABLE_AST_KIND) + .filter(|table| table.is_object()) + { + let (alias, table_name) = table_alias_and_name(table); + current.push(alias.clone()); + aliases = dict_set(aliases, &alias, UNKNOWN_NULLABILITY); + scoped = copy_facts_to_alias(&alias, &table_name, scoped); + } + let Some(joins) = payload.get("joins").and_then(Value::as_array) else { + return Ok((aliases, scoped)); + }; + for join in joins.iter().filter_map(Value::as_object) { + let Some(table) = join + .get("this") + .filter(|this| this.is_object()) + .and_then(|this| this.get(TABLE_AST_KIND)) + .filter(|table| table.is_object()) + else { + continue; + }; + let (joined, joined_name) = table_alias_and_name(table); + let side: String = match join.get("kind") { + None | Some(Value::Null) => String::new(), + Some(Value::String(side)) => upper(side), + Some(_) => return Err("a join side Python stringifies".to_owned()), + }; + let current_side: Option<&'static str> = match side.as_str() { + JOIN_RIGHT | JOIN_FULL => Some(NULLABLE_NULLABILITY), + _ => None, + }; + if let Some(nullability) = current_side { + for alias in ¤t { + aliases = dict_set(aliases, alias, nullability); + } + } + let joined_nullability: &'static str = match side.as_str() { + JOIN_LEFT | JOIN_FULL => NULLABLE_NULLABILITY, + _ => UNKNOWN_NULLABILITY, + }; + aliases = dict_set(aliases, &joined, joined_nullability); + current.push(joined.clone()); + scoped = copy_facts_to_alias(&joined, &joined_name, scoped); + } + Ok((aliases, scoped)) +} + +/// Python's `_polyglot_table_payload_alias_and_name`. +fn table_alias_and_name(table: &Value) -> (String, String) { + let table_name: String = raw_name_payload(table.get("name")); + let alias: String = raw_name_payload(table.get("alias")); + ( + if alias.is_empty() { + table_name.clone() + } else { + alias + }, + table_name, + ) +} + +/// Python's `_copy_table_facts_to_alias`. +fn copy_facts_to_alias(alias: &str, table_name: &str, mut scoped: NullRelations) -> NullRelations { + if alias == table_name || dict_get(&scoped, alias).is_some() { + return scoped; + } + if let Some(facts) = dict_get(&scoped, table_name).cloned() { + scoped.push((alias.to_owned(), facts)); + } + scoped +} + +fn has_known_nullability(facts: &[(String, &'static str)]) -> bool { + facts.iter().any(|(_, value)| *value != UNKNOWN_NULLABILITY) +} + +/// Python's `_polyglot_non_null_filter_context`. +fn filter_context(select: &Expression, nullability: &NullRelations) -> Fact> { + let Some(clause) = where_clause(select)? else { + return Ok(None); + }; + let Some(clause) = clause.as_object() else { + return Ok(None); + }; + let references: Vec<(String, String)> = non_null_conjuncts(clause.get("this")); + if references.is_empty() { + return Ok(None); + } + let mut relations: Vec<(String, NullShape)> = Vec::new(); + for table in direct_tables(select) { + let table_name: &str = table.get_name(); + let alias: &str = alias_or_name(table); + if let Some(columns) = ci_get(nullability, table_name) { + let key: &str = if alias.is_empty() { table_name } else { alias }; + relations.push((key.to_owned(), Rc::clone(columns))); + } + } + let columns: HashSet<(String, String)> = references + .iter() + .filter_map(|reference| resolved_column(reference, &relations)) + .collect(); + Ok((!columns.is_empty()).then_some((relations, columns))) +} + +/// Python's `_polyglot_non_null_conjuncts`. +fn non_null_conjuncts(payload: Option<&Value>) -> Vec<(String, String)> { + let Some(payload) = payload.and_then(Value::as_object) else { + return Vec::new(); + }; + if let Some(paren) = payload.get("paren").and_then(Value::as_object) { + return non_null_conjuncts(paren.get("this")); + } + if let Some(and) = payload.get("and").and_then(Value::as_object) { + let mut references: Vec<(String, String)> = non_null_conjuncts(and.get("left")); + references.extend(non_null_conjuncts(and.get("right"))); + return references; + } + let Some(is_null) = payload.get(IS_NULL_AST_KIND).and_then(Value::as_object) else { + return Vec::new(); + }; + if is_null.is_empty() || is_null.get("not") != Some(&Value::Bool(true)) { + return Vec::new(); + } + let Some(column) = is_null + .get("this") + .and_then(Value::as_object) + .and_then(|this| this.get(COLUMN_AST_KIND)) + .and_then(Value::as_object) + else { + return Vec::new(); + }; + let column_name: String = raw_name_payload(column.get("name")); + let table_name: String = raw_name_payload(column.get("table")); + if column_name.is_empty() { + return Vec::new(); + } + vec![(table_name, column_name)] +} + +/// Python's `_polyglot_resolved_relation_column`. +fn resolved_column( + reference: &(String, String), + relations: &[(String, NullShape)], +) -> Option<(String, String)> { + let (table_name, column_name) = reference; + let matching: Vec<(String, String)> = relations + .iter() + .filter(|(alias, _)| table_name.is_empty() || casefold(alias) == casefold(table_name)) + .filter_map(|(alias, columns)| folded_column(alias, columns, column_name)) + .collect(); + match <[(String, String); 1]>::try_from(matching) { + Ok([only]) => Some(only), + Err(_) => None, + } +} + +fn folded_column(alias: &str, columns: &NullShape, column_name: &str) -> Option<(String, String)> { + let key: &str = ci_key(columns, column_name)?; + Some((casefold(alias), casefold(key))) +} + +/// Python's `_polyglot_expression_is_non_null_after_filter`. +fn non_null_after_filter( + expression: Option<&Expression>, + context: Option<&FilterContext>, +) -> Fact { + let Some((relations, columns)) = context else { + return Ok(false); + }; + let Some(reference) = direct_column_reference(expression)? else { + return Ok(false); + }; + Ok(resolved_column(&reference, relations).is_some_and(|resolved| columns.contains(&resolved))) +} + +/// Python's `_polyglot_direct_column_reference`. +fn direct_column_reference(expression: Option<&Expression>) -> Fact> { + let mut expression: Option<&Expression> = unwrap_annotations(expression); + if kind(expression) == CAST_AST_KIND { + expression = unwrap_annotations(expression.and_then(Expression::get_this)); + } + if kind(expression) != COLUMN_AST_KIND { + return Ok(None); + } + let column_name: &str = name(expression); + if column_name.is_empty() { + return Ok(None); + } + let table_name: String = name_payload(arg(expression, "table")?.as_ref()); + Ok(Some((table_name, column_name.to_owned()))) +} + +/// Python's `_polyglot_filtered_non_null_outputs` once its filter test passed. +fn filtered_non_null(root: &Expression, nullability: &NullRelations) -> Fact> { + let select: Option<&Expression> = if kind(Some(root)) == SELECT_AST_KIND { + Some(root) + } else { + root.dfs() + .skip(1) + .find(|node| node.variant_name() == SELECT_AST_KIND) + }; + let Some(select) = select else { + return Ok(HashSet::new()); + }; + let context: Option = filter_context(select, nullability)?; + let mut outputs: HashSet = HashSet::new(); + for projection in select.get_expressions() { + let projection: Option<&Expression> = unwrap_annotations(Some(projection)); + let output: &str = output_name(projection); + if output.is_empty() || output == WILDCARD { + continue; + } + if non_null_after_filter(projected_expression(projection), context.as_ref())? { + outputs.insert(output.to_owned()); + } + } + Ok(outputs) +} + +/// Python's `_polyglot_referenced_relation_facts`. +fn referenced_facts(root: &Expression, facts: &[(String, V)]) -> Vec<(String, V)> { + let mut referenced: Vec<(String, V)> = Vec::new(); + for table in find_all(root, TABLE_AST_KIND) { + let table_name: &str = table.get_name(); + if let Some(column_facts) = dict_get(facts, table_name) { + referenced = dict_set(referenced, table_name, column_facts.clone()); + } + } + referenced +} + +/// Python's `_polyglot_direct_column_type`. +fn direct_column_type(column: &Expression, by_alias: &Relations) -> Fact> { + let column_name: &str = column.get_name(); + let table_name: String = column_table_name(column)?; + if !table_name.is_empty() { + return Ok(ci_get(by_alias, &table_name) + .and_then(|column_types| ci_get(column_types, column_name)) + .cloned()); + } + let matching: Vec<&String> = distinct_shapes(by_alias) + .into_iter() + .filter_map(|column_types| ci_get(column_types, column_name)) + .collect(); + Ok(match matching.as_slice() { + [only] => Some((*only).clone()), + _ => None, + }) +} + +/// The relations of `by_alias` in order, each `Rc` once, as Python's `id()` dedup keeps them. +fn distinct_shapes(by_alias: &Relations) -> Vec<&Shape> { + let mut distinct: Vec<&Shape> = Vec::new(); + for (_, shape) in by_alias { + if !distinct.iter().any(|seen| Rc::ptr_eq(seen, shape)) { + distinct.push(shape); + } + } + distinct +} + +/// Python's `_polyglot_star_output_types`. +fn star_types(star: &Expression, by_alias: &Relations) -> Fact> { + if py_truthy(arg(Some(star), "rename")?.as_ref()) { + return Ok(Vec::new()); + } + let table_name: String = name_payload(arg(Some(star), "table")?.as_ref()); + let sources: Vec<&Shape> = if table_name.is_empty() { + distinct_shapes(by_alias) + } else { + ci_get(by_alias, &table_name).into_iter().collect() + }; + let excluded: HashSet = listed_names(arg(Some(star), "except")?, false)?; + let replaced: HashSet = listed_names(arg(Some(star), "replace")?, true)?; + let mut inferred: Vec<(String, String)> = Vec::new(); + let mut ambiguous: Vec = Vec::new(); + for column_types in sources { + for (column_name, column_type) in column_types.iter() { + let folded: String = casefold(column_name); + if excluded.contains(&folded) || replaced.contains(&folded) { + continue; + } + if let Some((existing, _)) = inferred.iter().find(|(name, _)| casefold(name) == folded) + { + ambiguous.push(existing.clone()); + continue; + } + inferred.push((column_name.clone(), column_type.clone())); + } + } + inferred.retain(|(name, _)| !ambiguous.contains(name)); + Ok(inferred) +} + +/// The folded names of a star's `except` list, or of its `replace` aliases. +fn listed_names(value: Option, aliases: bool) -> Fact> { + let items: Vec = match value { + None => Vec::new(), + Some(PyValue::Exprs(_)) => return Ok(HashSet::new()), + Some(PyValue::Raw(Value::Array(items))) => items, + Some(PyValue::Raw(raw)) if !py_truthy(Some(&PyValue::Raw(raw.clone()))) => Vec::new(), + Some(_) => return Err("a star modifier Python iterates unusually".to_owned()), + }; + Ok(items + .iter() + .filter(|item| !aliases || item.is_object()) + .map(|item| match aliases { + true => raw_name_payload(item.get("alias")), + false => raw_name_payload(Some(item)), + }) + .filter(|name| !name.is_empty()) + .map(|name| casefold(&name)) + .collect()) +} + +/// Whether `node` is a literal whose `literal_type` arg is Python's `"string"`. +fn string_literal(node: &Expression) -> Fact { + if node.variant_name() != LITERAL_AST_KIND { + return Ok(false); + } + Ok(matches!( + arg(Some(node), "literal_type")?, + Some(PyValue::Raw(Value::String(literal_type))) if literal_type == STRING_LITERAL_TYPE + )) +} + +/// Python's `_polyglot_result_expressions`; None stands for a value that is not an expression. +fn result_expressions(node: &Expression) -> Fact>> { + let node_kind: &str = node.variant_name(); + if node_kind == COALESCE_AST_KIND { + return Ok(match arg(Some(node), "expressions")? { + Some(PyValue::Exprs(values)) => values.into_iter().map(Some).collect(), + Some(PyValue::Raw(Value::Array(values))) => values.iter().map(|_| None).collect(), + _ => Vec::new(), + }); + } + if node_kind == IF_FUNC_AST_KIND { + let mut values: Vec> = Vec::new(); + for key in ["true_value", "false_value"] { + if let Some(value) = arg(Some(node), key)? { + values.push(expression_value(value)); + } + } + return Ok(values); + } + if node_kind == CASE_AST_KIND { + return case_results(node); + } + if node_kind == FUNCTION_AST_KIND && upper(node.get_name()) == NULLIF_FUNCTION_NAME { + return Ok(match arg(Some(node), "args")? { + Some(PyValue::Exprs(mut values)) if !values.is_empty() => { + vec![Some(values.swap_remove(0))] + } + Some(PyValue::Raw(Value::Array(values))) if !values.is_empty() => vec![None], + _ => Vec::new(), + }); + } + + Ok(Vec::new()) +} + +fn expression_value(value: PyValue) -> Option { + match value { + PyValue::Expr(expression) => Some(expression), + _ => None, + } +} + +/// CASE's result branches by child position, as Python indexes `children()`. +fn case_results(node: &Expression) -> Fact>> { + let node_children: Vec<&Expression> = children(node); + let mut values: Vec> = Vec::new(); + let when_count: Option = match arg(Some(node), "whens")? { + Some(PyValue::Exprs(whens)) => Some(whens.len()), + Some(PyValue::Raw(Value::Array(whens))) => Some(whens.len()), + _ => None, + }; + if let Some(when_count) = when_count { + let first: usize = usize::from(arg(Some(node), "operand")?.is_some()); + for index in 0..when_count { + let child: &Expression = node_children + .get(first + index * 2 + 1) + .ok_or("Python indexes past CASE's children")?; + values.push(Some(child.clone())); + } + } + if arg(Some(node), "else_")?.is_some() + && let Some(last) = node_children.last() + { + values.push(Some((*last).clone())); + } + Ok(values) +} + +/// Python's cast-target rendering in `_polyglot_expression_type`. +fn cast_target_type(target: &Map) -> Option { + let raw_type: &str = target + .get("data_type") + .and_then(Value::as_str) + .filter(|raw| !raw.is_empty())?; + if upper(raw_type) == CUSTOM_TYPE_NAME + && let Some(custom) = target + .get("name") + .and_then(Value::as_str) + .filter(|custom| !custom.is_empty()) + { + return Some(upper(custom)); + } + let lower: String = casefold(raw_type); + if lower == TIMESTAMP_TYPE_NAME && target.get("timezone") == Some(&Value::Bool(true)) { + return Some(TIMESTAMP_TZ_TYPE.to_owned()); + } + let type_name: String = POLYGLOT_TYPE_NAMES + .iter() + .find(|(raw, _)| *raw == raw_type) + .map_or_else( + || upper(&raw_type.replace('_', " ")), + |(_, rendered)| (*rendered).to_owned(), + ); + if VARCHAR_DATA_TYPES.contains(&lower.as_str()) + && let Some(length) = python_int(target.get("length")) + { + return Some(format!("VARCHAR({length})")); + } + if type_name == DECIMAL_TYPE + && let Some(precision) = python_int(target.get("precision")) + { + return Some(match python_int(target.get("scale")) { + Some(scale) => format!("DECIMAL({precision}, {scale})"), + None => format!("DECIMAL({precision})"), + }); + } + Some(type_name) +} + +/// Python's `str.casefold`, exact for ASCII; non-ASCII text marks the recovery deferred. +fn casefold(text: &str) -> String { + flag_non_ascii(text); + text.to_ascii_lowercase() +} + +/// Python's `str.upper`, exact for ASCII; non-ASCII text marks the recovery deferred. +fn upper(text: &str) -> String { + flag_non_ascii(text); + text.to_ascii_uppercase() +} + +fn flag_non_ascii(text: &str) { + if !text.is_ascii() { + NON_ASCII_FOLDED.set(true); + } +} + +fn dict_get<'a, V>(dict: &'a [(String, V)], key: &str) -> Option<&'a V> { + dict.iter() + .find(|(candidate, _)| candidate == key) + .map(|(_, value)| value) +} + +/// Python's `dict[key] = value`: replace in place, otherwise append. +fn dict_set(mut dict: Vec<(String, V)>, key: &str, value: V) -> Vec<(String, V)> { + match dict.iter_mut().find(|(candidate, _)| candidate == key) { + Some((_, existing)) => *existing = value, + None => dict.push((key.to_owned(), value)), + } + dict +} + +/// Python's `_case_insensitive_mapping_get` over values that are never None. +fn ci_get<'a, V>(dict: &'a [(String, V)], key: &str) -> Option<&'a V> { + if let Some(direct) = dict_get(dict, key) { + return Some(direct); + } + unique_folded(dict, key) +} + +/// Python's `_case_insensitive_mapping_get` over values that may be None. +fn ci_get_optional<'a>(dict: &'a [(String, Option)], key: &str) -> Option<&'a str> { + if let Some(Some(direct)) = dict_get(dict, key) { + return Some(direct); + } + unique_folded(dict, key).and_then(Option::as_deref) +} + +fn unique_folded<'a, V>(dict: &'a [(String, V)], key: &str) -> Option<&'a V> { + let folded: String = casefold(key); + let matches: Vec<&V> = dict + .iter() + .filter(|(candidate, _)| casefold(candidate) == folded) + .map(|(_, value)| value) + .collect(); + match matches.as_slice() { + [only] => Some(only), + _ => None, + } +} + +/// Python's `_case_insensitive_mapping_key`. +fn ci_key<'a, V>(dict: &'a [(String, V)], key: &str) -> Option<&'a str> { + if let Some((candidate, _)) = dict.iter().find(|(candidate, _)| candidate == key) { + return Some(candidate); + } + let folded: String = casefold(key); + let matches: Vec<&str> = dict + .iter() + .filter(|(candidate, _)| casefold(candidate) == folded) + .map(|(candidate, _)| candidate.as_str()) + .collect(); + match matches.as_slice() { + [only] => Some(only), + _ => None, + } +} diff --git a/crates/sqlbuild-analysis/src/assembly/analysis_session/_helpers/dict_walk.rs b/crates/sqlbuild-analysis/src/assembly/analysis_session/_helpers/dict_walk.rs index d11f30c0ae..96b1a60d02 100644 --- a/crates/sqlbuild-analysis/src/assembly/analysis_session/_helpers/dict_walk.rs +++ b/crates/sqlbuild-analysis/src/assembly/analysis_session/_helpers/dict_walk.rs @@ -1,10 +1,14 @@ -//! Python's dict walk over a parsed query's `to_dict()` payload, on the same serde values. +//! Python's view of a parsed query: the wheel's expression accessors and its dict walk. -use serde_json::{Map, Value}; +use polyglot_sql::expressions::Cte; +use polyglot_sql::traversal::ExpressionWalk; +use polyglot_sql::{ComplexityGuardOptions, Dialect, Expression, ParseOptions, ast_json}; +use serde_json::{Map, Value, json}; use crate::assembly::analysis_session::constants::{ - COLUMN_AST_KIND, DEPENDENCY_FUNCTION_NAMES, FUNCTION_AST_KIND, RENDERED_TYPE_NAMES, - STAR_AST_KIND, TABLE_AST_KIND, + ALIAS_AST_KIND, ANNOTATED_AST_KIND, COLUMN_AST_KIND, DEPENDENCY_FUNCTION_NAMES, + FUNCTION_AST_KIND, MAX_FUNCTION_CALL_DEPTH, RENDERED_TYPE_NAMES, STAR_AST_KIND, TABLE_AST_KIND, + WILDCARD, }; /// Python's `bool(value)`. @@ -142,7 +146,7 @@ pub(crate) fn render_type(node: Option<&Value>) -> Option { } /// Python's `str(value)` for a value `isinstance(value, int)` accepts. -fn python_int(value: Option<&Value>) -> Option { +pub(crate) fn python_int(value: Option<&Value>) -> Option { match value? { Value::Bool(flag) => Some(if *flag { "True" } else { "False" }.to_owned()), Value::Number(number) if number.is_i64() || number.is_u64() => Some(number.to_string()), @@ -154,3 +158,236 @@ fn python_int(value: Option<&Value>) -> Option { pub(crate) fn casefold(text: &str) -> String { text.to_ascii_lowercase() } + +/// A value the wheel hands Python for one payload key: an expression, a list of them, or data. +#[derive(Debug, Clone)] +pub(crate) enum PyValue { + Expr(Expression), + Exprs(Vec), + Raw(Value), +} + +/// The wheel's guarded `parse_one`; Ok(None) is the `PolyglotError` Python catches. +pub(crate) fn parse_one(sql: &str, dialect: &str) -> Result, String> { + let guard: ComplexityGuardOptions = + serde_json::from_value(json!({"maxFunctionCallDepth": MAX_FUNCTION_CALL_DEPTH})) + .map_err(|error| error.to_string())?; + let options: ParseOptions = ParseOptions { + complexity_guard: Some(guard), + }; + let parsed: Result, _> = Dialect::get_by_name(dialect) + .ok_or_else(|| format!("unknown dialect {dialect}"))? + .parse_with_options(sql, &options); + let mut expressions: Vec = match parsed { + Ok(expressions) => expressions, + Err(_) => return Ok(None), + }; + if expressions.len() != 1 { + return Ok(None); + } + Ok(expressions.pop()) +} + +/// `str(getattr(node, "kind", ""))`. +pub(crate) fn kind(node: Option<&Expression>) -> &'static str { + node.map_or("", Expression::variant_name) +} + +/// `node.name`, "" for None. +pub(crate) fn name(node: Option<&Expression>) -> &str { + node.map_or("", Expression::get_name) +} + +/// `node.output_name`, "" for None. +pub(crate) fn output_name(node: Option<&Expression>) -> &str { + node.map_or("", Expression::get_output_name) +} + +/// `node.alias_or_name`. +pub(crate) fn alias_or_name(node: &Expression) -> &str { + let alias: &str = node.get_alias(); + if alias.is_empty() { + node.get_name() + } else { + alias + } +} + +/// `node.is_star`. +pub(crate) fn is_star(node: Option<&Expression>) -> bool { + match node { + Some(Expression::Star(_)) => true, + Some(Expression::Column(column)) => column.name.name == WILDCARD, + _ => false, + } +} + +/// Python's `_unwrap_polyglot_annotations`. +pub(crate) fn unwrap_annotations(mut node: Option<&Expression>) -> Option<&Expression> { + while kind(node) == ANNOTATED_AST_KIND { + node = node.and_then(Expression::get_this); + } + node +} + +/// A projection's expression: an alias's `this`, otherwise the projection. +pub(crate) fn projected_expression(projection: Option<&Expression>) -> Option<&Expression> { + if kind(projection) == ALIAS_AST_KIND { + return projection.and_then(Expression::get_this); + } + projection +} + +/// `node.this` then `node.expressions`: Python's `_polyglot_expression_args`. +pub(crate) fn expression_args(node: Option<&Expression>) -> Vec<&Expression> { + let Some(node) = node else { + return Vec::new(); + }; + node.get_this() + .into_iter() + .chain(node.get_expressions()) + .collect() +} + +/// `node.children()`. +pub(crate) fn children(node: &Expression) -> Vec<&Expression> { + ExpressionWalk::children(node) +} + +/// Python's `_polyglot_direct_select_tables`: the table children. +pub(crate) fn direct_tables(node: &Expression) -> Vec<&Expression> { + children(node) + .into_iter() + .filter(|child| child.variant_name() == TABLE_AST_KIND) + .collect() +} + +/// `node.find_all(kind)`: descendants in depth-first order, the node itself excluded. +pub(crate) fn find_all<'a>(node: &'a Expression, wanted: &str) -> Vec<&'a Expression> { + node.dfs() + .skip(1) + .filter(|descendant| descendant.variant_name() == wanted) + .collect() +} + +/// `node.to_dict()`. +pub(crate) fn to_dict(node: &Expression) -> Result { + serde_json::to_value(node).map_err(|error| error.to_string()) +} + +/// The wheel's `expression_payload`: the tagged value's fields. +pub(crate) fn expression_payload(node: &Expression) -> Result, String> { + Ok(match to_dict(node)? { + Value::Object(map) => match map.into_iter().next() { + Some((_, Value::Object(payload))) => payload, + _ => Map::new(), + }, + _ => Map::new(), + }) +} + +/// `node.args.get(key)` (and `node.arg(key)` outside its special keys); None for Python's None. +pub(crate) fn arg(node: Option<&Expression>, key: &str) -> Result, String> { + let Some(node) = node else { + return Ok(None); + }; + Ok(expression_payload(node)?.remove(key).and_then(py_value)) +} + +/// The wheel's `value_to_python_object`; None where Python sees None. +fn py_value(value: Value) -> Option { + if let Ok(expression) = ast_json::expression_from_value(value.clone()) { + return Some(PyValue::Expr(expression)); + } + if let Ok(expressions) = ast_json::expressions_from_value(value.clone()) { + return Some(PyValue::Exprs(expressions)); + } + (!value.is_null()).then_some(PyValue::Raw(value)) +} + +/// Python's `bool()` of a converted value: expressions are always truthy. +pub(crate) fn py_truthy(value: Option<&PyValue>) -> bool { + match value { + None => false, + Some(PyValue::Expr(_)) => true, + Some(PyValue::Exprs(items)) => !items.is_empty(), + Some(PyValue::Raw(raw)) => truthy(Some(raw)), + } +} + +/// Python's `_polyglot_name_payload_value` of a converted value. +pub(crate) fn name_payload(value: Option<&PyValue>) -> String { + match value { + Some(PyValue::Raw(raw)) => raw_name_payload(Some(raw)), + _ => String::new(), + } +} + +/// Python's `_polyglot_name_payload_value` of plain data. +pub(crate) fn raw_name_payload(value: Option<&Value>) -> String { + match value { + Some(Value::String(text)) => text.clone(), + Some(Value::Object(object)) => object + .get("name") + .and_then(Value::as_str) + .unwrap_or_default() + .to_owned(), + _ => String::new(), + } +} + +/// Python's `_polyglot_column_table_name`: `to_dict()["column"]["table"]["name"]`. +pub(crate) fn column_table_name(node: &Expression) -> Result { + let value: Value = to_dict(node)?; + let table: Option<&Value> = value + .get("column") + .filter(|column| column.is_object()) + .and_then(|column| column.get("table")) + .filter(|table| table.is_object()); + Ok(raw_name_payload(table)) +} + +/// The top-level CTEs `with_ctes()` returns: name, whether it has column aliases, and its body. +pub(crate) fn top_level_ctes(node: &Expression) -> Vec<(&str, bool, &Expression)> { + let with = match node { + Expression::Select(select) => select.with.as_ref(), + Expression::Union(union) => union.with.as_ref(), + Expression::Intersect(intersect) => intersect.with.as_ref(), + Expression::Except(except) => except.with.as_ref(), + _ => None, + }; + with.map(|with| with.ctes.iter().map(cte_entry).collect()) + .unwrap_or_default() +} + +fn cte_entry(cte: &Cte) -> (&str, bool, &Expression) { + (cte.alias.name.as_str(), !cte.columns.is_empty(), &cte.this) +} + +/// A set operation's `arg("left")`, `arg("right")` and `arg("by_name")`. +pub(crate) fn set_operation(node: &Expression) -> Option<(&Expression, &Expression, bool)> { + match node { + Expression::Union(operation) => { + Some((&operation.left, &operation.right, operation.by_name)) + } + Expression::Intersect(operation) => { + Some((&operation.left, &operation.right, operation.by_name)) + } + Expression::Except(operation) => { + Some((&operation.left, &operation.right, operation.by_name)) + } + _ => None, + } +} + +/// A select's `arg("where_clause")` as plain data. +pub(crate) fn where_clause(node: &Expression) -> Result, String> { + let Expression::Select(select) = node else { + return Ok(None); + }; + select + .where_clause + .as_ref() + .map(|clause| serde_json::to_value(clause).map_err(|error| error.to_string())) + .transpose() +} diff --git a/crates/sqlbuild-analysis/src/assembly/analysis_session/_helpers/dynamic_pivot.rs b/crates/sqlbuild-analysis/src/assembly/analysis_session/_helpers/dynamic_pivot.rs index 78f0b3bdba..40386cc58b 100644 --- a/crates/sqlbuild-analysis/src/assembly/analysis_session/_helpers/dynamic_pivot.rs +++ b/crates/sqlbuild-analysis/src/assembly/analysis_session/_helpers/dynamic_pivot.rs @@ -2,17 +2,16 @@ use std::collections::{HashMap, HashSet}; -use polyglot_sql::{ComplexityGuardOptions, Dialect, Expression, ParseOptions}; -use serde_json::{Map, Value, json}; +use serde_json::{Map, Value}; use sqlbuild_core::panics::main::catch_compiler_panic::catch_compiler_panic; use crate::assembly::analysis_session::_helpers::dict_walk::{ casefold, column_name, dict_list, identifier_name, is_single_wildcard, nested, node_key, - payload, relation_name, render_type, truthy, + parse_one, payload, relation_name, render_type, to_dict, truthy, }; use crate::assembly::analysis_session::constants::{ ALIAS_AST_KIND, CAST_AST_KIND, CTE_AST_KIND, DUCKDB_DIALECT, DYNAMIC_VALUE_SOURCE_KINDS, - MAX_FUNCTION_CALL_DEPTH, MOTHERDUCK_DIALECT, PIVOT_AST_KIND, QUERY_AST_KINDS, SELECT_AST_KIND, + MOTHERDUCK_DIALECT, PIVOT_AST_KIND, QUERY_AST_KINDS, SELECT_AST_KIND, SIMPLIFIED_PIVOT_DIALECTS, SUPPORTED_PIVOT_DIALECTS, TYPE_PRESERVING_AGGREGATES, UNKNOWN_NULLABILITY, UNKNOWN_TYPE, }; @@ -80,25 +79,9 @@ pub(crate) fn pivot_outcome( /// Python's `parse_one(...).to_dict()`, or None where Python reports a parse error. fn parsed(sql: &str, dialect: &str) -> Result, String> { - let guard: ComplexityGuardOptions = - serde_json::from_value(json!({"maxFunctionCallDepth": MAX_FUNCTION_CALL_DEPTH})) - .map_err(|error| error.to_string())?; - let options: ParseOptions = ParseOptions { - complexity_guard: Some(guard), - }; - let parsed: Result, _> = Dialect::get_by_name(dialect) - .ok_or_else(|| format!("unknown dialect {dialect}"))? - .parse_with_options(sql, &options); - let expressions: Vec = match parsed { - Ok(expressions) => expressions, - Err(_) => return Ok(None), - }; - let [expression] = expressions.as_slice() else { - return Ok(None); - }; - serde_json::to_value(expression) - .map(Some) - .map_err(|error| error.to_string()) + parse_one(sql, dialect)? + .map(|expression| to_dict(&expression)) + .transpose() } /// Casefolding equals Python's only for ASCII text. diff --git a/crates/sqlbuild-analysis/src/assembly/analysis_session/_helpers/enrichment.rs b/crates/sqlbuild-analysis/src/assembly/analysis_session/_helpers/enrichment.rs index 2e176ff81e..4a2da05dde 100644 --- a/crates/sqlbuild-analysis/src/assembly/analysis_session/_helpers/enrichment.rs +++ b/crates/sqlbuild-analysis/src/assembly/analysis_session/_helpers/enrichment.rs @@ -6,11 +6,14 @@ use polyglot_sql::{AnalyzeQueryOptions, analyze_query}; use serde_json::{Map, Value, json}; use sqlbuild_core::panics::main::catch_compiler_panic::catch_compiler_panic; +use crate::assembly::analysis_session::_helpers::cte_facts::{ + Recovery, RecoveryInput, RecoveryProfile, recovery, +}; use crate::assembly::analysis_session::_helpers::dict_walk::truthy; use crate::assembly::analysis_session::constants::{ CAST_TRANSFORM, CONFIDENCE_HIGH, CONFIDENCE_MEDIUM, CONFIDENCE_UNKNOWN, FILTER_CONTEXT, - MAX_FUNCTION_CALL_DEPTH, NULL_KEYWORD, RESOLVED_SOURCE_CONFIDENCE, SELECT_SHAPE, - SET_OPERATION_SHAPE, TRANSFORM_AGGREGATION, TRANSFORM_CAST, TRANSFORM_CONSTANT, + MAX_FUNCTION_CALL_DEPTH, NON_NULL_NULLABILITY, NULL_KEYWORD, RESOLVED_SOURCE_CONFIDENCE, + SELECT_SHAPE, SET_OPERATION_SHAPE, TRANSFORM_AGGREGATION, TRANSFORM_CAST, TRANSFORM_CONSTANT, TRANSFORM_DIRECT, TRANSFORM_EXPRESSION, TRANSFORM_STAR, UNKNOWN_NULLABILITY, UNKNOWN_TYPE, WILDCARD, }; @@ -25,6 +28,8 @@ pub(crate) struct EnrichmentInput<'a> { pub(crate) input_schemas: &'a Shapes, pub(crate) dialect: &'a str, pub(crate) function_return_types: &'a Pairs, + /// Adapter nullability rules by function name; None where one is not a rule Python ships. + pub(crate) nullability_rules: Option<&'a Pairs>, } /// Python's successful re-analysis: columns, plain lineage facts and the star flag. @@ -75,7 +80,7 @@ fn analysed(input: &EnrichmentInput<'_>) -> Result, String> { Err(_) => return Ok(None), }; let analysis: Value = serde_json::to_value(analysis).map_err(|error| error.to_string())?; - Ok(projected(input, &analysis)) + Ok(projected(input, &analysis, &cleaned)) } /// Python's `_compact_analysis_schema` over the input shapes, nullability unknown. @@ -109,15 +114,26 @@ fn analysis_schema(input: &EnrichmentInput<'_>) -> Option { } /// Python's projection of `analyze_query` facts into columns and lineage. -fn projected(input: &EnrichmentInput<'_>, analysis: &Value) -> Option { +fn projected(input: &EnrichmentInput<'_>, analysis: &Value, cleaned: &str) -> Option { let projections: &Vec = analysis.get("projections")?.as_array()?; let shape: Option<&str> = analysis.get("shape").and_then(Value::as_str); if !matches!(shape, Some(SELECT_SHAPE | SET_OPERATION_SHAPE)) || !eligible(projections) { return None; } - if has_null_filter(analysis) || recovers_cte_facts(input, analysis) { - return None; - } + let recovery: Recovery = match recovery(&RecoveryInput { + cleaned_sql: cleaned, + input_schemas: input.input_schemas, + recover: recovers_cte_facts(input, analysis), + null_filter: has_null_filter(analysis), + profile: RecoveryProfile { + dialect: input.dialect, + function_return_types: input.function_return_types, + rules: input.nullability_rules, + }, + }) { + Ok(recovery) => recovery, + Err(_deferred) => return None, + }; let resources: HashMap<&str, (&str, &str)> = input .model .lineage_references @@ -136,10 +152,24 @@ fn projected(input: &EnrichmentInput<'_>, analysis: &Value) -> Option = if direct { + recovered(&recovery.types, name).cloned() + } else { + projection_type(projection, input.function_return_types)? + }; + let nullability: &str = if recovery.non_null_outputs.contains(name) { + NON_NULL_NULLABILITY + } else { + recovered(&recovery.nullability, name) + .filter(|_| direct) + .copied() + .unwrap_or_else(|| projection_nullability(projection, infer_nullability)) + }; columns.push(ColumnFact { name: name.to_owned(), - data_type: projection_type(projection, input.function_return_types)?, - nullability: projection_nullability(projection, infer_nullability).to_owned(), + data_type, + nullability: nullability.to_owned(), }); let (sources, confidence) = upstream(projection, &resources); let transform: u8 = transform_code(projection, !sources.is_empty()); @@ -176,7 +206,15 @@ fn projected(input: &EnrichmentInput<'_>, analysis: &Value) -> Option(facts: &'a [(String, V)], name: &str) -> Option<&'a V> { + facts + .iter() + .find(|(output, _)| output == name) + .map(|(_, value)| value) +} + +/// Whether Python's CTE pass-through recovery runs past its early return. fn recovers_cte_facts(input: &EnrichmentInput<'_>, analysis: &Value) -> bool { input.model.recover_cte_facts && truthy(analysis.get("cteFacts")) diff --git a/crates/sqlbuild-analysis/src/assembly/analysis_session/_helpers/mod.rs b/crates/sqlbuild-analysis/src/assembly/analysis_session/_helpers/mod.rs index 1ff9c1dda6..3534a64196 100644 --- a/crates/sqlbuild-analysis/src/assembly/analysis_session/_helpers/mod.rs +++ b/crates/sqlbuild-analysis/src/assembly/analysis_session/_helpers/mod.rs @@ -1,5 +1,6 @@ pub(crate) mod catalog_state; pub(crate) mod compact_batch; +pub(crate) mod cte_facts; pub(crate) mod dict_walk; pub(crate) mod dynamic_pivot; pub(crate) mod enrichment; diff --git a/crates/sqlbuild-analysis/src/assembly/analysis_session/_helpers/session.rs b/crates/sqlbuild-analysis/src/assembly/analysis_session/_helpers/session.rs index cf945f7c71..43418c7881 100644 --- a/crates/sqlbuild-analysis/src/assembly/analysis_session/_helpers/session.rs +++ b/crates/sqlbuild-analysis/src/assembly/analysis_session/_helpers/session.rs @@ -352,6 +352,7 @@ impl AnalysisSession { input_schemas, dialect: &self.request.dialect, function_return_types: &self.request.function_return_types, + nullability_rules: self.request.nullability_rules.as_ref(), }) .collect(); let pool = self.catalog.native.analysis_pool()?; diff --git a/crates/sqlbuild-analysis/src/assembly/analysis_session/constants.rs b/crates/sqlbuild-analysis/src/assembly/analysis_session/constants.rs index d0d6fa5520..f7ddb49793 100644 --- a/crates/sqlbuild-analysis/src/assembly/analysis_session/constants.rs +++ b/crates/sqlbuild-analysis/src/assembly/analysis_session/constants.rs @@ -95,3 +95,77 @@ pub(crate) const RENDERED_TYPE_NAMES: [(&str, &str); 12] = [ ]; /// The stack a standalone dynamic pivot proof parses on, as the analysis workers have. pub(crate) const PIVOT_WORKER_STACK_BYTES: usize = 16 * 1024 * 1024; +/// The wheel's node kinds Python's CTE fact recovery reads. +pub(crate) const ANNOTATED_AST_KIND: &str = "annotated"; +pub(crate) const LITERAL_AST_KIND: &str = "literal"; +pub(crate) const NULL_AST_KIND: &str = "null"; +pub(crate) const CONCAT_AST_KIND: &str = "concat"; +pub(crate) const SUBSTRING_AST_KIND: &str = "substring"; +pub(crate) const COALESCE_AST_KIND: &str = "coalesce"; +pub(crate) const IF_FUNC_AST_KIND: &str = "if_func"; +pub(crate) const CASE_AST_KIND: &str = "case"; +pub(crate) const TRY_CAST_AST_KIND: &str = "try_cast"; +pub(crate) const COUNT_AST_KIND: &str = "count"; +pub(crate) const IS_NULL_AST_KIND: &str = "is_null"; +pub(crate) const SET_OPERATION_AST_KINDS: [&str; 3] = ["union", "intersect", "except"]; +pub(crate) const CAST_AST_KINDS: [&str; 2] = ["cast", "try_cast"]; +pub(crate) const TYPE_PASSTHROUGH_AST_KINDS: [&str; 4] = + ["max", "min", "window_function", "within_group"]; +/// Python's `POLYGLOT_BOOLEAN_RESULT_KINDS`. +pub(crate) const BOOLEAN_RESULT_AST_KINDS: [&str; 18] = [ + "boolean", + "and", + "between", + "eq", + "exists", + "gt", + "gte", + "ilike", + "in", + "is", + "is_null", + "like", + "lt", + "lte", + "neq", + "not", + "or", + "regexp_like", +]; +pub(crate) const NULLIF_FUNCTION_NAME: &str = "NULLIF"; +pub(crate) const STRING_LITERAL_TYPE: &str = "string"; +pub(crate) const BINARY_OPERAND_COUNT: usize = 2; +/// The type Python's set operation slots give a bare NULL. +pub(crate) const NULL_SET_OPERATION_TYPE: &str = "__SQLBUILD_NULL_SET_OPERATION_TYPE__"; +pub(crate) const BOOLEAN_TYPE: &str = "BOOLEAN"; +pub(crate) const TEXT_TYPE: &str = "TEXT"; +pub(crate) const CUSTOM_TYPE_NAME: &str = "CUSTOM"; +pub(crate) const TIMESTAMP_TYPE_NAME: &str = "timestamp"; +pub(crate) const TIMESTAMP_TZ_TYPE: &str = "TIMESTAMPTZ"; +pub(crate) const DECIMAL_TYPE: &str = "DECIMAL"; +pub(crate) const VARCHAR_DATA_TYPES: [&str; 2] = ["var_char", "varchar"]; +/// Python's `_polyglot_type_name` names, matched case-sensitively as Python's dict is. +pub(crate) const POLYGLOT_TYPE_NAMES: [(&str, &str); 13] = [ + ("big_int", "BIGINT"), + ("bool", "BOOLEAN"), + ("boolean", "BOOLEAN"), + ("date", "DATE"), + ("decimal", "DECIMAL"), + ("double", "DOUBLE"), + ("float", "FLOAT"), + ("int", "INT"), + ("integer", "INT"), + ("text", "TEXT"), + ("timestamp", "TIMESTAMP"), + ("var_char", "TEXT"), + ("varchar", "TEXT"), +]; +/// Python's join sides that make relations nullable. +pub(crate) const JOIN_LEFT: &str = "LEFT"; +pub(crate) const JOIN_RIGHT: &str = "RIGHT"; +pub(crate) const JOIN_FULL: &str = "FULL"; +/// Ids of the adapter nullability rules Python registers, by the function it calls. +pub(crate) const FIRST_ARG_RULE: &str = "first_arg"; +pub(crate) const CONDITIONAL_RESULT_RULE: &str = "conditional_result"; +pub(crate) const NON_NULL_NULLABILITY: &str = "non_null"; +pub(crate) const NULLABLE_NULLABILITY: &str = "nullable"; diff --git a/crates/sqlbuild-analysis/src/assembly/analysis_session/models.rs b/crates/sqlbuild-analysis/src/assembly/analysis_session/models.rs index d10d56c0ad..7b9b41c64c 100644 --- a/crates/sqlbuild-analysis/src/assembly/analysis_session/models.rs +++ b/crates/sqlbuild-analysis/src/assembly/analysis_session/models.rs @@ -89,6 +89,8 @@ pub struct SessionRequest { /// Whether published shapes keep authored quoting, Python's `inferred_binding_shape` test. pub case_sensitive_shapes: bool, pub function_return_types: Pairs, + /// Adapter nullability rules as `(function name, rule id)`; None where one is not Python's. + pub nullability_rules: Option, pub rich_type_inference: bool, pub column_types: Shapes, pub column_nullability: Shapes, diff --git a/crates/sqlbuild-analysis/src/assembly/analysis_session/tests/helpers.rs b/crates/sqlbuild-analysis/src/assembly/analysis_session/tests/helpers.rs index 1f274fdfc1..33eabbad61 100644 --- a/crates/sqlbuild-analysis/src/assembly/analysis_session/tests/helpers.rs +++ b/crates/sqlbuild-analysis/src/assembly/analysis_session/tests/helpers.rs @@ -1,3 +1,6 @@ +use crate::assembly::analysis_session::_helpers::cte_facts::{ + Recovery, RecoveryInput, RecoveryProfile, recovery, +}; use crate::assembly::analysis_session::_helpers::mappings::catalog_relations; use crate::assembly::analysis_session::main::finish_analysis_session::finish_analysis_session; use crate::assembly::analysis_session::main::provide_deferred_analyses::provide_deferred_analyses; @@ -8,7 +11,7 @@ use crate::assembly::analysis_session::models::{ ModelOutcome, ModelReference, ModelRequest, PivotOutcome, PivotRequest, SessionRequest, SessionStep, }; -use crate::assembly::analysis_session::tests::test_types::ModelSpec; +use crate::assembly::analysis_session::tests::test_types::{ModelSpec, RecoveredFacts}; use crate::assembly::analysis_session::types::{Pairs, Shapes}; use crate::semantic_validation::models::{CatalogInput, ProjectCatalog}; @@ -95,6 +98,7 @@ pub(crate) fn orders_request(models: Vec) -> SessionRequest { dialect: "duckdb".to_owned(), case_sensitive_shapes: false, function_return_types: Vec::new(), + nullability_rules: Some(Vec::new()), rich_type_inference: true, column_types: raw_orders.clone(), column_nullability: shapes(&[( @@ -297,3 +301,52 @@ pub(crate) fn proven( bare_dynamic_pivot: bare, }) } + +/// Python's recovery over `orders` for a contract-enforced model whose filter reads NULL. +pub(crate) fn recovered_facts(sql: &str) -> Option<(Pairs, Pairs, Vec, Vec)> { + let input_schemas: Shapes = shapes(&[( + "orders", + &[ + ("order_id", "INTEGER"), + ("amount", "DOUBLE"), + ("status", "VARCHAR"), + ], + )]); + let rules: Pairs = pairs(&[("UPPER", "first_arg")]); + let recovered: Recovery = recovery(&RecoveryInput { + cleaned_sql: sql, + input_schemas: &input_schemas, + recover: true, + null_filter: true, + profile: RecoveryProfile { + dialect: "duckdb", + function_return_types: &Vec::new(), + rules: Some(&rules), + }, + }) + .ok()?; + let mut direct: Vec = recovered.direct_outputs.into_iter().collect(); + direct.sort(); + let mut non_null: Vec = recovered.non_null_outputs.into_iter().collect(); + non_null.sort(); + let nullability: Pairs = recovered + .nullability + .into_iter() + .map(|(name, value)| (name, value.to_owned())) + .collect(); + Some((recovered.types, nullability, direct, non_null)) +} + +/// The expected recovery as owned values. +pub(crate) fn expected_facts( + facts: Option, +) -> Option<(Pairs, Pairs, Vec, Vec)> { + facts.map(|(types, nullability, direct, non_null)| { + ( + pairs(types), + pairs(nullability), + direct.iter().map(|name| (*name).to_owned()).collect(), + non_null.iter().map(|name| (*name).to_owned()).collect(), + ) + }) +} diff --git a/crates/sqlbuild-analysis/src/assembly/analysis_session/tests/test_analysis_session.rs b/crates/sqlbuild-analysis/src/assembly/analysis_session/tests/test_analysis_session.rs index c56625f012..ff31a21f99 100644 --- a/crates/sqlbuild-analysis/src/assembly/analysis_session/tests/test_analysis_session.rs +++ b/crates/sqlbuild-analysis/src/assembly/analysis_session/tests/test_analysis_session.rs @@ -2,10 +2,11 @@ use crate::assembly::analysis_session::main::prove_dynamic_contract::prove_dynam use crate::assembly::analysis_session::main::start_analysis_session::start_analysis_session; use crate::assembly::analysis_session::models::PivotOutcome; use crate::assembly::analysis_session::tests::helpers::{ - catalog, failed, model_requests, orders_request, pivot_request, proven, session_lines, + catalog, expected_facts, failed, model_requests, orders_request, pivot_request, proven, + recovered_facts, session_lines, }; use crate::assembly::analysis_session::tests::test_types::{ - PivotTestCase, SessionTestCase, UnscheduledTestCase, + CteRecoveryTestCase, PivotTestCase, SessionTestCase, UnscheduledTestCase, }; #[test] @@ -291,3 +292,68 @@ fn given_dynamic_pivots_when_proving_then_matches_python_or_defers() { ); } } + +#[test] +fn given_cte_reads_when_recovering_facts_then_matches_python_or_defers() { + let test_cases = [ + CteRecoveryTestCase { + description: "direct CTE reads keep input and cast types", + sql: "WITH base AS (SELECT order_id, CAST(amount AS DECIMAL(10, 2)) AS amount \ + FROM orders) SELECT order_id, amount FROM base", + expected_facts: Some(( + &[("order_id", "INTEGER"), ("amount", "DECIMAL(10, 2)")], + &[], + &["amount", "order_id"], + &[], + )), + }, + CteRecoveryTestCase { + description: "literals and filtered columns are non-null through an aliased CTE", + sql: "WITH base AS (SELECT 'x' AS tag, UPPER(status) AS up, order_id FROM orders \ + WHERE order_id IS NOT NULL) SELECT b.tag, b.up, b.order_id FROM base AS b", + expected_facts: Some(( + &[("order_id", "INTEGER")], + &[("tag", "non_null"), ("order_id", "non_null")], + &["order_id", "tag", "up"], + &[], + )), + }, + CteRecoveryTestCase { + description: "a set operation branch with an unnamed NULL proves nothing", + sql: "WITH u AS (SELECT order_id AS k, status AS s FROM orders UNION ALL \ + SELECT NULL, 'y') SELECT k, s FROM u", + expected_facts: Some((&[], &[], &["k", "s"], &[])), + }, + CteRecoveryTestCase { + description: "a star with EXCLUDE and a joined relation", + sql: "WITH a AS (SELECT * EXCLUDE (amount) FROM orders) SELECT o.status, a.order_id \ + FROM a LEFT JOIN orders AS o ON a.order_id = o.order_id", + expected_facts: Some(( + &[("status", "VARCHAR"), ("order_id", "INTEGER")], + &[], + &["order_id", "status"], + &[], + )), + }, + CteRecoveryTestCase { + description: "no CTE leaves only the filtered non-null outputs", + sql: "SELECT order_id, status FROM orders WHERE order_id IS NOT NULL", + expected_facts: Some((&[], &[], &[], &["order_id"])), + }, + CteRecoveryTestCase { + description: "non-ASCII names Python casefolds differently", + sql: "WITH b\u{e4}se AS (SELECT order_id FROM orders) SELECT order_id FROM b\u{e4}se", + expected_facts: None, + }, + ]; + for test_case in test_cases { + let facts = recovered_facts(test_case.sql); + + assert_eq!( + facts, + expected_facts(test_case.expected_facts), + "{}", + test_case.description + ); + } +} diff --git a/crates/sqlbuild-analysis/src/assembly/analysis_session/tests/test_types.rs b/crates/sqlbuild-analysis/src/assembly/analysis_session/tests/test_types.rs index c09b783048..8e4d0b84e1 100644 --- a/crates/sqlbuild-analysis/src/assembly/analysis_session/tests/test_types.rs +++ b/crates/sqlbuild-analysis/src/assembly/analysis_session/tests/test_types.rs @@ -51,3 +51,18 @@ pub(crate) struct PivotTestCase { pub(crate) family: Option<(&'static str, &'static str, &'static str)>, pub(crate) expected_outcome: PivotOutcome, } + +/// Types, nullabilities, direct CTE outputs and filtered non-null outputs, in Python's order. +pub(crate) type RecoveredFacts = ( + &'static [(&'static str, &'static str)], + &'static [(&'static str, &'static str)], + &'static [&'static str], + &'static [&'static str], +); + +pub(crate) struct CteRecoveryTestCase { + pub(crate) description: &'static str, + pub(crate) sql: &'static str, + /// None where the recovery defers to Python. + pub(crate) expected_facts: Option, +} diff --git a/crates/sqlbuild-python/src/bindings/_helpers/analysis_session/session.rs b/crates/sqlbuild-python/src/bindings/_helpers/analysis_session/session.rs index 87af8f9b33..7d04738e92 100644 --- a/crates/sqlbuild-python/src/bindings/_helpers/analysis_session/session.rs +++ b/crates/sqlbuild-python/src/bindings/_helpers/analysis_session/session.rs @@ -47,6 +47,7 @@ type RequestRow = ( String, bool, Pairs, + Option, bool, Shapes, Shapes, @@ -226,6 +227,7 @@ fn session_request(request: RequestRow) -> SessionRequest { dialect, case_sensitive_shapes, function_return_types, + nullability_rules, rich_type_inference, column_types, column_nullability, @@ -238,6 +240,7 @@ fn session_request(request: RequestRow) -> SessionRequest { dialect, case_sensitive_shapes, function_return_types, + nullability_rules, rich_type_inference, column_types, column_nullability, diff --git a/scripts/compiler_differential/_helpers/analysis_corpus/analysis_session.py b/scripts/compiler_differential/_helpers/analysis_corpus/analysis_session.py index 4a7c34a23e..c769c944bd 100644 --- a/scripts/compiler_differential/_helpers/analysis_corpus/analysis_session.py +++ b/scripts/compiler_differential/_helpers/analysis_corpus/analysis_session.py @@ -26,6 +26,13 @@ def _pivot_model(*, pivot_column: str, body: str) -> str: ) +def _contract_header(columns: str) -> str: + return ( + 'MODEL (\n description "Order totals per customer",\n contract enforced,\n' + f" columns (\n{columns} ),\n);\n\n" + ) + + def analysis_session_failure_cases() -> tuple[FailureCase, ...]: """Return this lane's failure cases; the lane appends here without editing shared lists.""" @@ -70,6 +77,29 @@ def analysis_session_failure_cases() -> tuple[FailureCase, ...]: f"SELECT customer_id, amount\nFROM (SELECT * FROM {_ORDERS}) staged\n", }, ), + failure_case( + name="analysis-session-cte-fact-type-mismatch", + expected_code="K002", + files={ + FAILURE_MART_PATH: _contract_header( + " customer_id (type INTEGER),\n total_amount (type VARCHAR),\n" + ) + + "WITH totals AS (\n SELECT customer_id, CAST(amount AS DOUBLE) AS total_amount\n" + f" FROM {_ORDERS}\n)\n" + "SELECT customer_id + 0 AS customer_id, total_amount FROM totals\n", + }, + ), + failure_case( + name="analysis-session-cte-fact-nullability", + expected_code="K004", + files={ + FAILURE_MART_PATH: _contract_header( + " customer_id (type INTEGER),\n note (type VARCHAR, nullable false),\n" + ) + + "WITH base AS (\n SELECT customer_id, CAST(NULL AS VARCHAR) AS note\n" + f" FROM {_ORDERS}\n)\nSELECT customer_id + 0 AS customer_id, note FROM base\n", + }, + ), failure_case( name="analysis-session-pivot-values-on-clause", expected_code="K011", diff --git a/scripts/compiler_differential/classes/analysis_features.py b/scripts/compiler_differential/classes/analysis_features.py index e45a65edaa..9240dc67dc 100644 --- a/scripts/compiler_differential/classes/analysis_features.py +++ b/scripts/compiler_differential/classes/analysis_features.py @@ -331,6 +331,28 @@ def _python_sql(self) -> None: def _analysis_modes(self) -> None: self._features.add("analysis_modes") base: ModelPlan = self._base() + self._model( + name=f"cte_facts_{base.name}", + description=f"Contracted CTE reads of {base.name}", + header=( + " contract enforced,\n columns (\n" + + "".join( + f' {column} (description "Recovered {column}"),\n' + for column in ("id", "amount", "kind", "status") + ) + + " ),\n" + ), + body=( + "WITH base AS (\n" + " SELECT id, CAST(amount AS DECIMAL(12, 2)) AS amount, 'event' AS kind,\n" + " COALESCE(status, 'unknown') AS status\n" + f' FROM __ref("{base.name}")\n' + " WHERE id IS NOT NULL\n" + ")\n" + "SELECT id + 0 AS id, amount, UPPER(kind) AS kind, UPPER(status) AS status\n" + "FROM base\n" + ), + ) self.commands = tuple( _selected_command(label=label, arguments=arguments, model=base.name) for label, arguments in GENERATOR_ANALYSIS_MODE_COMMANDS diff --git a/src/sqlbuild/compiler/analysis_session/_helpers/session_rows.py b/src/sqlbuild/compiler/analysis_session/_helpers/session_rows.py index 093c361e6e..99cd11b33f 100644 --- a/src/sqlbuild/compiler/analysis_session/_helpers/session_rows.py +++ b/src/sqlbuild/compiler/analysis_session/_helpers/session_rows.py @@ -5,6 +5,15 @@ from collections.abc import Mapping from sqlbuild.adapter.contract.models import ExpressionInferenceProfile +from sqlbuild.adapter.contract.types import FunctionNullabilityRule +from sqlbuild.adapter.type_system.main.conditional_result_nullability import ( + conditional_result_nullability, +) +from sqlbuild.adapter.type_system.main.first_arg_nullability import first_arg_nullability +from sqlbuild.compiler.analysis_session.constants import ( + NULLABILITY_RULE_CONDITIONAL_RESULT, + NULLABILITY_RULE_FIRST_ARG, +) from sqlbuild.compiler.analysis_session.models import NativeModelAnalysisRequest from sqlbuild.compiler.analysis_session.types import ( ColumnRow, @@ -30,6 +39,11 @@ from sqlbuild.compiler.sql_analysis.models import SqlBindingDiagnostic from sqlbuild.spec.contracts.models import SchemaDynamicColumnFamily +_NULLABILITY_RULE_IDS: dict[FunctionNullabilityRule, str] = { + first_arg_nullability: NULLABILITY_RULE_FIRST_ARG, + conditional_result_nullability: NULLABILITY_RULE_CONDITIONAL_RESULT, +} + def shape_rows(shapes: Mapping[str, Mapping[str, object]]) -> ShapeRows | None: """`{relation: {column: value}}` as ordered rows, or None when a value is not text.""" @@ -66,6 +80,7 @@ def session_request( profile.sql_analysis_dialect or "generic", case_sensitive_shapes(profile=profile, dialect=profile.sql_analysis_dialect), list(profile.function_return_types.items()), + nullability_rule_rows(profile), request.rich_type_inference, *shapes, family_rows(request.dynamic_families_by_table), @@ -73,6 +88,18 @@ def session_request( ) +def nullability_rule_rows(profile: ExpressionInferenceProfile) -> list[tuple[str, str]] | None: + """Adapter nullability rules as `(name, rule id)`, or None for a rule SQLBuild lacks.""" + + rows: list[tuple[str, str]] = [] + for name, rule in profile.function_nullability_rules.items(): + rule_id: str | None = _NULLABILITY_RULE_IDS.get(rule) + if rule_id is None: + return None + rows.append((name, rule_id)) + return rows + + def contract_proof(row: ProofRow | None) -> DynamicColumnContractProof | None: """A native dynamic pivot proof as Python's, None when absent or left to Python.""" diff --git a/src/sqlbuild/compiler/analysis_session/constants.py b/src/sqlbuild/compiler/analysis_session/constants.py index 9d819d795d..4a4e269c40 100644 --- a/src/sqlbuild/compiler/analysis_session/constants.py +++ b/src/sqlbuild/compiler/analysis_session/constants.py @@ -16,3 +16,5 @@ NATIVE_ANALYSIS_FAILURE_MESSAGE: str = "native compact query analysis failed" NATIVE_SESSION_FAILURE_MESSAGE: str = "native model analysis failed; falling back" NATIVE_SHAPES_FAILURE_MESSAGE: str = "native expression-source shape inference failed; falling back" +NULLABILITY_RULE_FIRST_ARG: str = "first_arg" +NULLABILITY_RULE_CONDITIONAL_RESULT: str = "conditional_result" diff --git a/tests/integration/src/sqlbuild/compiler/analysis_session/_test_types.py b/tests/integration/src/sqlbuild/compiler/analysis_session/_test_types.py index f3ea3a0aac..e725c50b3f 100644 --- a/tests/integration/src/sqlbuild/compiler/analysis_session/_test_types.py +++ b/tests/integration/src/sqlbuild/compiler/analysis_session/_test_types.py @@ -15,6 +15,7 @@ class GeneratedAnalysisParityTestCase: expected_minimum_native: int expected_minimum_expression_shapes: int expected_minimum_pivot_proofs: int + expected_minimum_cte_recoveries: int expected_minimum_proven_pivots: int expected_maximum_enrichment_deferrals: int expected_minimum_native_enrichments: int diff --git a/tests/integration/src/sqlbuild/compiler/analysis_session/helpers.py b/tests/integration/src/sqlbuild/compiler/analysis_session/helpers.py index 47095774c0..e7b002e85d 100644 --- a/tests/integration/src/sqlbuild/compiler/analysis_session/helpers.py +++ b/tests/integration/src/sqlbuild/compiler/analysis_session/helpers.py @@ -15,6 +15,7 @@ import sqlbuild._native as native_module import sqlbuild.compiler.analysis_session.classes.native_model_analysis as native_model_analysis +import sqlbuild.compiler.compile._helpers.analysis.compact as compact_analysis import sqlbuild.compiler.compile._helpers.assembly.project as project_assembly import sqlbuild.compiler.compile._helpers.native_stages.assembly as native_stage_assembly from sqlbuild.adapter.contract.models import ExpressionInferenceProfile @@ -171,6 +172,8 @@ def _pivot_models(rng: random.Random) -> dict[str, str]: return files +_PYTHON_CTE_RECOVERY: Callable[..., Any] = compact_analysis._polyglot_cte_passthrough_facts + type _Relation = tuple[str, tuple[str, ...]] type _Template = Callable[[random.Random, list[_Relation]], tuple[str, str, tuple[str, ...]]] @@ -258,6 +261,35 @@ def _contract(rng: random.Random, inputs: list[_Relation]) -> tuple[str, str, tu return header, sql, ("contract_key", "contract_label") +_CTE_CONTRACT_BODIES: tuple[str, ...] = ( + "WITH base AS (\n SELECT {a}, {b} FROM {left}\n)\nSELECT {a}, {b} FROM base", + "WITH base AS (\n SELECT CAST({a} AS VARCHAR(12)) AS label, COALESCE({b}, {a}) AS filled,\n" + " 'fixed' AS tag, CAST(1.5 AS DECIMAL(10, 2)) AS ratio, {a} IS NULL AS missing\n" + " FROM {left}\n WHERE {a} IS NOT NULL\n)\n" + "SELECT label, filled, tag, ratio, missing FROM base", + "WITH base AS (\n SELECT CASE WHEN {a} IS NULL THEN 'none' ELSE 'some' END AS state,\n" + " UPPER(CAST({b} AS VARCHAR)) AS upper_b, CAST({a} AS TEXT) || '-x' AS joined\n" + " FROM {left}\n)\nSELECT state, upper_b, joined FROM base", + "WITH unioned AS (\n SELECT {a} AS shared FROM {left}\n UNION ALL\n" + " SELECT {c} FROM {right}\n)\nSELECT shared FROM unioned", + "WITH base AS (\n SELECT * EXCLUDE ({a}) FROM {left}\n)\nSELECT {b} FROM base", + "WITH lhs AS (\n SELECT {a}, {b} FROM {left}\n),\nrhs AS (\n SELECT {c} FROM {right}\n)\n" + "SELECT l.{a}, r.{c}\nFROM lhs l\nLEFT JOIN rhs r ON l.{a} = r.{c}", + "WITH outer_cte AS (\n WITH inner_cte AS (SELECT {a}, {b} FROM {left})\n" + " SELECT {a}, {b} FROM inner_cte\n)\nSELECT {a}, {b} FROM outer_cte", + "WITH base AS (\n SELECT {a}, NULLIF({b}, {b}) AS cleared, MAX({b}) AS latest FROM {left}\n" + " GROUP BY {a}\n)\nSELECT {a}, cleared, latest FROM base WHERE {a} IS NOT NULL", +) + + +def _cte_contract(rng: random.Random, inputs: list[_Relation]) -> tuple[str, str, tuple[str, ...]]: + (left, left_columns), (right, right_columns) = inputs[0], inputs[-1] + first, second = rng.sample(list(left_columns), k=2) + body: str = rng.choice(_CTE_CONTRACT_BODIES) + sql: str = body.format(a=first, b=second, c=right_columns[0], left=left, right=right) + return " contract enforced,\n", sql, (first, second) + + def _unknown_column( rng: random.Random, inputs: list[_Relation] ) -> tuple[str, str, tuple[str, ...]]: @@ -282,6 +314,7 @@ def _subquery(rng: random.Random, inputs: list[_Relation]) -> tuple[str, str, tu _aggregate, _quoted, _contract, + _cte_contract, _unknown_column, _subquery, ) @@ -373,6 +406,7 @@ class AnalysisParity: analysed_models: int = 0 expression_shapes: int = 0 pivot_proofs: int = 0 + cte_recoveries: int = 0 standalone_proofs: int = 0 proven_pivots: int = 0 native_enrichments: int = 0 @@ -404,7 +438,13 @@ def models_by_both( dynamic_families_by_table=dynamic_families_by_table, ) ) - python: dict[str, ModelSqlAnalysis] = python_analysis.func(**keywords) + with monkeypatch.context() as patch: + patch.setattr( + compact_analysis, + "_polyglot_cte_passthrough_facts", + partial(_counted_cte_recovery, parity=parity), + ) + python: dict[str, ModelSqlAnalysis] = python_analysis.func(**keywords) parity.analysed_models += len(python) _append(parity, "model analyses", _analysis_views(python), _analysis_views(native)) _append( @@ -515,6 +555,12 @@ def deferral_kinds(record_dir: Path) -> Counter[str]: return Counter(f"{record['site']}:{record['kind']}" for record in records) +def _counted_cte_recovery(*, parity: AnalysisParity, **arguments: Any) -> Any: + recovered: Any = _PYTHON_CTE_RECOVERY(**arguments) + parity.cte_recoveries += bool(recovered[2]) + return recovered + + def _append(parity: AnalysisParity, name: str, python: object, native: object) -> None: parity.names.append(name) parity.python.append(python) diff --git a/tests/integration/src/sqlbuild/compiler/analysis_session/test_native_model_analysis.py b/tests/integration/src/sqlbuild/compiler/analysis_session/test_native_model_analysis.py index 4e9527ea64..8ba99d7ca8 100644 --- a/tests/integration/src/sqlbuild/compiler/analysis_session/test_native_model_analysis.py +++ b/tests/integration/src/sqlbuild/compiler/analysis_session/test_native_model_analysis.py @@ -54,7 +54,7 @@ "test_case", [ GeneratedAnalysisParityTestCase( - description="stars, CTEs, set operations, untyped inputs, contracts, contract CTEs, pivots", + description="stars, CTEs, CTE facts, set operations, untyped inputs, contracts, pivots", seed=20261008, count=6, model_count=24, @@ -62,8 +62,9 @@ expected_minimum_native=500, expected_minimum_expression_shapes=40, expected_minimum_pivot_proofs=80, + expected_minimum_cte_recoveries=20, expected_minimum_proven_pivots=12, - expected_maximum_enrichment_deferrals=120, + expected_maximum_enrichment_deferrals=0, expected_minimum_native_enrichments=90, ) ], @@ -90,8 +91,11 @@ def test_given_generated_projects_when_analysing_natively_then_matches_python( assert mismatches(inputs=parity.names, expected=parity.python, actual=parity.native) == [] assert kinds["analysis_session:session"] == kinds["analysis_session:expression_shapes"] == 0 assert kinds["analysis_session:dynamic_pivot"] == 0 + assert parity.cte_recoveries >= test_case.expected_minimum_cte_recoveries assert ( - parity.analysed_models - kinds["analysis_session:legacy_analysis"] + parity.analysed_models + - kinds["analysis_session:legacy_analysis"] + - kinds["analysis_session:input_enrichment"] >= test_case.expected_minimum_native ) assert parity.expression_shapes >= test_case.expected_minimum_expression_shapes From 3c903e79f194f72b3925825b163c627d00759208 Mon Sep 17 00:00:00 2001 From: Kevin Longe <273747162+kvlonge@users.noreply.github.com> Date: Fri, 9 Oct 2026 10:14:37 +0100 Subject: [PATCH 02/31] test(compile): allow a 1.2x wall-time cache-write overhead in the fresh-process guard Shared CI runners swing wall time while CPU stays flat; the CPU overhead limits are unchanged and still catch real cache-write cost. --- .../compile/test_fresh_process_compile_cache_performance.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/e2e/src/sqlbuild/cli/commands/main/compile/test_fresh_process_compile_cache_performance.py b/tests/e2e/src/sqlbuild/cli/commands/main/compile/test_fresh_process_compile_cache_performance.py index 34566e6567..2b3a388cf0 100644 --- a/tests/e2e/src/sqlbuild/cli/commands/main/compile/test_fresh_process_compile_cache_performance.py +++ b/tests/e2e/src/sqlbuild/cli/commands/main/compile/test_fresh_process_compile_cache_performance.py @@ -29,7 +29,7 @@ _MAX_EDIT_TO_COLD_RATIO: float = 0.70 # Writing the cache during a cold compile must stay a small same-runner cost over --no-cache. _MAX_CACHE_WRITE_CPU_OVERHEAD_RATIO: float = 1.10 -_MAX_CACHE_WRITE_WALL_OVERHEAD_RATIO: float = 1.15 +_MAX_CACHE_WRITE_WALL_OVERHEAD_RATIO: float = 1.20 _MAX_LARGE_CACHE_WRITE_CPU_OVERHEAD_RATIO: float = 1.15 From 1efb420bbe8a11d93ba3fb3803ee6aad3ef4b328 Mon Sep 17 00:00:00 2001 From: Kevin Longe <273747162+kvlonge@users.noreply.github.com> Date: Fri, 9 Oct 2026 12:01:35 +0100 Subject: [PATCH 03/31] test(native): compare CTE fact recovery with Python function for function A seeded differential compares native CTE fact recovery with Python's over 5,000 queries and adapter profiles through a test-only oracle binding. The model-analysis CTE recovery count is relabelled as a corpus floor over Python's reference run. --- .../assembly/analysis_session/constants.rs | 2 + .../src/assembly/analysis_session/main/mod.rs | 1 + .../main/recover_cte_facts.rs | 47 ++ .../src/assembly/analysis_session/models.rs | 27 ++ .../bindings/_helpers/analysis_session/mod.rs | 1 + .../_helpers/analysis_session/oracle.rs | 62 +++ .../_helpers/analysis_session/registration.rs | 5 +- src/sqlbuild/_native.pyi | 12 + .../analysis_session/_test_helpers.py | 440 ++++++++++++++++++ .../compiler/analysis_session/_test_types.py | 13 +- .../compiler/analysis_session/helpers.py | 4 +- .../test_native_cte_fact_recovery.py | 56 +++ .../test_native_model_analysis.py | 4 +- 13 files changed, 667 insertions(+), 7 deletions(-) create mode 100644 crates/sqlbuild-analysis/src/assembly/analysis_session/main/recover_cte_facts.rs create mode 100644 crates/sqlbuild-python/src/bindings/_helpers/analysis_session/oracle.rs create mode 100644 tests/integration/src/sqlbuild/compiler/analysis_session/_test_helpers.py create mode 100644 tests/integration/src/sqlbuild/compiler/analysis_session/test_native_cte_fact_recovery.py diff --git a/crates/sqlbuild-analysis/src/assembly/analysis_session/constants.rs b/crates/sqlbuild-analysis/src/assembly/analysis_session/constants.rs index f7ddb49793..ccdf67f459 100644 --- a/crates/sqlbuild-analysis/src/assembly/analysis_session/constants.rs +++ b/crates/sqlbuild-analysis/src/assembly/analysis_session/constants.rs @@ -95,6 +95,8 @@ pub(crate) const RENDERED_TYPE_NAMES: [(&str, &str); 12] = [ ]; /// The stack a standalone dynamic pivot proof parses on, as the analysis workers have. pub(crate) const PIVOT_WORKER_STACK_BYTES: usize = 16 * 1024 * 1024; +/// Stack for one standalone CTE fact recovery, whose AST walks recurse with query depth. +pub(crate) const CTE_FACT_WORKER_STACK_BYTES: usize = 16 * 1024 * 1024; /// The wheel's node kinds Python's CTE fact recovery reads. pub(crate) const ANNOTATED_AST_KIND: &str = "annotated"; pub(crate) const LITERAL_AST_KIND: &str = "literal"; diff --git a/crates/sqlbuild-analysis/src/assembly/analysis_session/main/mod.rs b/crates/sqlbuild-analysis/src/assembly/analysis_session/main/mod.rs index 6c5394355c..2e74f7a45c 100644 --- a/crates/sqlbuild-analysis/src/assembly/analysis_session/main/mod.rs +++ b/crates/sqlbuild-analysis/src/assembly/analysis_session/main/mod.rs @@ -2,5 +2,6 @@ pub mod expression_shapes; pub mod finish_analysis_session; pub mod prove_dynamic_contract; pub mod provide_deferred_analyses; +pub mod recover_cte_facts; pub mod run_analysis_session; pub mod start_analysis_session; diff --git a/crates/sqlbuild-analysis/src/assembly/analysis_session/main/recover_cte_facts.rs b/crates/sqlbuild-analysis/src/assembly/analysis_session/main/recover_cte_facts.rs new file mode 100644 index 0000000000..84aace7ca4 --- /dev/null +++ b/crates/sqlbuild-analysis/src/assembly/analysis_session/main/recover_cte_facts.rs @@ -0,0 +1,47 @@ +//! Python's CTE pass-through facts and filtered non-null outputs for one query. + +use crate::assembly::analysis_session::_helpers::cte_facts::{ + Recovery, RecoveryInput, RecoveryProfile, recovery, +}; +use crate::assembly::analysis_session::constants::CTE_FACT_WORKER_STACK_BYTES; +use crate::assembly::analysis_session::models::{CteFactRequest, CteFacts}; + +/// The facts enrichment recovers for the query, or why they are left to Python. +pub fn recover_cte_facts(request: &CteFactRequest) -> Result { + std::thread::scope(|scope| { + std::thread::Builder::new() + .stack_size(CTE_FACT_WORKER_STACK_BYTES) + .spawn_scoped(scope, || recovered_facts(request)) + .map_err(|error| error.to_string())? + .join() + .map_err(|_| "native CTE fact recovery panicked".to_owned())? + }) +} + +fn recovered_facts(request: &CteFactRequest) -> Result { + let recovered: Recovery = recovery(&RecoveryInput { + cleaned_sql: &request.cleaned_sql, + input_schemas: &request.input_schemas, + recover: request.recover, + null_filter: request.null_filter, + profile: RecoveryProfile { + dialect: &request.dialect, + function_return_types: &request.function_return_types, + rules: request.nullability_rules.as_ref(), + }, + })?; + let mut direct_outputs: Vec = recovered.direct_outputs.into_iter().collect(); + direct_outputs.sort_unstable(); + let mut non_null_outputs: Vec = recovered.non_null_outputs.into_iter().collect(); + non_null_outputs.sort_unstable(); + Ok(CteFacts { + types: recovered.types, + nullability: recovered + .nullability + .into_iter() + .map(|(name, nullability)| (name, nullability.to_owned())) + .collect(), + direct_outputs, + non_null_outputs, + }) +} diff --git a/crates/sqlbuild-analysis/src/assembly/analysis_session/models.rs b/crates/sqlbuild-analysis/src/assembly/analysis_session/models.rs index 7b9b41c64c..30657d426e 100644 --- a/crates/sqlbuild-analysis/src/assembly/analysis_session/models.rs +++ b/crates/sqlbuild-analysis/src/assembly/analysis_session/models.rs @@ -259,3 +259,30 @@ pub struct AnalysisSession { pub(crate) publications: Shapes, pub(crate) failures: Vec, } + +/// One query's CTE fact recovery, as Python's compact enrichment runs it. +#[derive(Debug, Clone)] +pub struct CteFactRequest { + pub cleaned_sql: String, + pub dialect: String, + /// Input relation types; every input column's nullability is unknown, as in enrichment. + pub input_schemas: Shapes, + pub function_return_types: Pairs, + /// Adapter nullability rules as `(function name, rule id)`; None where one is not Python's. + pub nullability_rules: Option, + /// Whether Python runs `_polyglot_cte_passthrough_facts` past its early return. + pub recover: bool, + /// Whether a filter reads NULL, so Python looks for filtered non-null outputs. + pub null_filter: bool, +} + +/// Python's recovered CTE pass-through facts and filtered non-null outputs for one query. +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct CteFacts { + pub types: Pairs, + pub nullability: Pairs, + /// Sorted direct CTE output names. + pub direct_outputs: Vec, + /// Sorted outputs a filter proves non-null. + pub non_null_outputs: Vec, +} diff --git a/crates/sqlbuild-python/src/bindings/_helpers/analysis_session/mod.rs b/crates/sqlbuild-python/src/bindings/_helpers/analysis_session/mod.rs index 54e3fdf710..feba290be6 100644 --- a/crates/sqlbuild-python/src/bindings/_helpers/analysis_session/mod.rs +++ b/crates/sqlbuild-python/src/bindings/_helpers/analysis_session/mod.rs @@ -1,2 +1,3 @@ +pub(crate) mod oracle; pub(crate) mod registration; pub(crate) mod session; diff --git a/crates/sqlbuild-python/src/bindings/_helpers/analysis_session/oracle.rs b/crates/sqlbuild-python/src/bindings/_helpers/analysis_session/oracle.rs new file mode 100644 index 0000000000..e631bc54ea --- /dev/null +++ b/crates/sqlbuild-python/src/bindings/_helpers/analysis_session/oracle.rs @@ -0,0 +1,62 @@ +//! Test-only hook comparing native CTE fact recovery with Python's, function for function. + +use pyo3::prelude::{Bound, PyModule, PyModuleMethods, PyResult, Python}; +use pyo3::{pyfunction, wrap_pyfunction}; +use sqlbuild_analysis::assembly::analysis_session::main::recover_cte_facts::recover_cte_facts; +use sqlbuild_analysis::assembly::analysis_session::models::{CteFactRequest, CteFacts}; +use sqlbuild_analysis::assembly::analysis_session::types::{Pairs, Shapes}; + +use crate::bindings::_helpers::boundary::panics::value_error; +use crate::bindings::types::CompilerDetach; + +/// `(cleaned SQL, dialect, input schemas, return types, nullability rules, recover, null filter)`. +type RecoveryRow = (String, String, Shapes, Pairs, Option, bool, bool); +/// `(deferral, types, nullability, sorted direct outputs, sorted non-null outputs)`. +type FactsRow = (Option, Pairs, Pairs, Vec, Vec); + +/// The facts native enrichment recovers for one query, or the reason it defers to Python. +#[pyfunction] +fn _oracle_cte_fact_recovery(py: Python<'_>, request: RecoveryRow) -> PyResult { + let ( + cleaned_sql, + dialect, + input_schemas, + function_return_types, + nullability_rules, + recover, + null_filter, + ) = request; + let request = CteFactRequest { + cleaned_sql, + dialect, + input_schemas, + function_return_types, + nullability_rules, + recover, + null_filter, + }; + let facts: Result = py + .compiler_detach(|| Ok(recover_cte_facts(&request))) + .map_err(value_error)?; + Ok(match facts { + Ok(facts) => ( + None, + facts.types, + facts.nullability, + facts.direct_outputs, + facts.non_null_outputs, + ), + Err(deferral) => ( + Some(deferral), + Vec::new(), + Vec::new(), + Vec::new(), + Vec::new(), + ), + }) +} + +pub(crate) fn register(module: &Bound<'_, PyModule>) -> PyResult<()> { + module.add_function(wrap_pyfunction!(_oracle_cte_fact_recovery, module)?)?; + Ok(()) +} diff --git a/crates/sqlbuild-python/src/bindings/_helpers/analysis_session/registration.rs b/crates/sqlbuild-python/src/bindings/_helpers/analysis_session/registration.rs index bfe82cb5d1..426d0f43eb 100644 --- a/crates/sqlbuild-python/src/bindings/_helpers/analysis_session/registration.rs +++ b/crates/sqlbuild-python/src/bindings/_helpers/analysis_session/registration.rs @@ -2,8 +2,9 @@ use pyo3::prelude::{Bound, PyModule, PyResult}; -use crate::bindings::_helpers::analysis_session::session; +use crate::bindings::_helpers::analysis_session::{oracle, session}; pub(crate) fn register(module: &Bound<'_, PyModule>) -> PyResult<()> { - session::register(module) + session::register(module)?; + oracle::register(module) } diff --git a/src/sqlbuild/_native.pyi b/src/sqlbuild/_native.pyi index b98b504587..6042e56cb7 100644 --- a/src/sqlbuild/_native.pyi +++ b/src/sqlbuild/_native.pyi @@ -452,6 +452,18 @@ def prove_dynamic_column_contract( def infer_expression_source_shapes( catalog: object, request: tuple[str, bool, list[tuple[str, str]], list[str]], / ) -> tuple[list[tuple[bool, list[tuple[str, str]] | None]], str | None]: ... +def _oracle_cte_fact_recovery( + request: tuple[ + str, + str, + list[tuple[str, list[tuple[str, str]]]], + list[tuple[str, str]], + list[tuple[str, str]] | None, + bool, + bool, + ], + /, +) -> tuple[str | None, list[tuple[str, str]], list[tuple[str, str]], list[str], list[str]]: ... # Native analysis: semantic completion. class SemanticTypeRecovery: diff --git a/tests/integration/src/sqlbuild/compiler/analysis_session/_test_helpers.py b/tests/integration/src/sqlbuild/compiler/analysis_session/_test_helpers.py new file mode 100644 index 0000000000..7622a8d2bf --- /dev/null +++ b/tests/integration/src/sqlbuild/compiler/analysis_session/_test_helpers.py @@ -0,0 +1,440 @@ +"""Seeded CTE queries and both CTE fact recoveries, compared function for function.""" + +from __future__ import annotations + +import random +from collections import Counter +from collections.abc import Callable +from dataclasses import dataclass +from typing import Any + +import sqlbuild._native as native_module +import sqlbuild.compiler.compile._helpers.analysis.compact as compact_analysis +from sqlbuild.adapter.contract.classes.base_adapter import BaseAdapter +from sqlbuild.adapter.contract.models import ExpressionInferenceProfile +from sqlbuild.adapter.type_system.main.conditional_result_nullability import ( + conditional_result_nullability, +) +from sqlbuild.adapter.type_system.main.first_arg_nullability import first_arg_nullability +from sqlbuild.adapters.bigquery.classes.bigquery_adapter import BigQueryAdapter +from sqlbuild.adapters.databricks.classes.databricks_adapter import DatabricksAdapter +from sqlbuild.adapters.duckdb.classes.duckdb_adapter import DuckDbAdapter +from sqlbuild.adapters.postgres.classes.postgres_adapter import PostgresAdapter +from sqlbuild.adapters.snowflake.classes.snowflake_adapter import SnowflakeAdapter +from sqlbuild.adapters.sqlserver.classes.sqlserver_adapter import SqlServerAdapter +from sqlbuild.compiler.analysis_session._helpers.session_rows import nullability_rule_rows +from sqlbuild.compiler.compile._helpers.analysis.cte_facts import ( + _polyglot_cte_passthrough_facts, + _polyglot_filtered_non_null_outputs, +) +from sqlbuild.compiler.compile.models import CteFactResolvers +from sqlbuild.compiler.lineage.types import InferredNullability +from sqlbuild.compiler.sql_analysis.main.import_polyglot import import_polyglot + +type Generate = Callable[[random.Random, list[str], int], str] +type CteFactView = ( + tuple[list[tuple[str, str]], list[tuple[str, str]], list[str], list[str]] | tuple[str, str] +) + + +@dataclass(frozen=True) +class CteFactQuery: + """One generated query and the enrichment facts both recoveries read.""" + + sql: str + profile: ExpressionInferenceProfile + recover: bool + null_filter: bool + + +@dataclass +class CteFactParity: + """Python's and native's recovered facts for every query native answered.""" + + queries: list[object] + python: list[object] + native: list[object] + counts: Counter[str] + + +_SCHEMAS: dict[str, dict[str, str]] = { + "orders": { + "order_id": "INTEGER", + "customer_id": "BIGINT", + "qty": "INT", + "price": "DECIMAL(10, 2)", + "big_price": "NUMERIC(12,2)", + "ratio": "DOUBLE", + "status": "VARCHAR", + "code": "VARCHAR(20)", + "note": "TEXT", + "flag": "BOOLEAN", + "placed_on": "DATE", + "placed_at": "TIMESTAMP", + "MixedCase": "INTEGER", + "mixedcase": "BIGINT", + "Price": "FLOAT", + }, + "customers": { + "customer_id": "BIGINT", + "order_id": "VARCHAR", + "region": "STRING", + "price": "DOUBLE", + "Region": "CHAR(2)", + "empty_type": "", + }, + "Events": {"event_id": "INT", "kind": "VARCHAR(10)"}, + "events": {"event_id": "BIGINT", "payload": "JSON"}, +} +_INPUT_NULLABILITY: dict[str, dict[str, InferredNullability]] = { + table: dict.fromkeys(columns, InferredNullability.UNKNOWN) + for table, columns in _SCHEMAS.items() +} +_INPUT_SHAPES: list[tuple[str, list[tuple[str, str]]]] = [ + (table, list(columns.items())) for table, columns in _SCHEMAS.items() +] +_NULL_FILTER_USE: dict[str, str] = {"context": "filter", "expressionSql": "x IS NOT NULL"} +_RESOLVERS: CteFactResolvers = CteFactResolvers( + expression_type=compact_analysis._polyglot_expression_type, + nullability=compact_analysis._infer_polyglot_nullability, + shallow_nullability=compact_analysis._infer_polyglot_shallow_nullability, + alias_nullability=compact_analysis._polyglot_alias_nullability_from_select, +) +_ADAPTERS: tuple[BaseAdapter, ...] = ( + DuckDbAdapter(), + SnowflakeAdapter(), + BigQueryAdapter(), + DatabricksAdapter(), + PostgresAdapter(), + SqlServerAdapter(), +) +# Rules apply by node kind. The wheel parses IF, IFF and IIF as `if_func`, whose arguments +# Python never collects, so adapters' IF/IFF rules stay unknown here; the profile keyed by +# node kinds with collected arguments reaches the conditional_result and first_arg rules. +PROFILES: tuple[ExpressionInferenceProfile, ...] = ( + *(adapter.expression_inference_profile() for adapter in _ADAPTERS), + ExpressionInferenceProfile( + sql_analysis_dialect="duckdb", + function_nullability_rules=DuckDbAdapter() + .expression_inference_profile() + .function_nullability_rules, + function_return_types={"LENGTH": "BIGINT", "UPPER": "VARCHAR(5)", "ABS": "DECIMAL(10,2)"}, + ), + ExpressionInferenceProfile( + sql_analysis_dialect="snowflake", + function_nullability_rules={ + "FUNCTION": conditional_result_nullability, + "IN": conditional_result_nullability, + "ARRAY_FUNC": first_arg_nullability, + "CONCAT": first_arg_nullability, + "UPPER": first_arg_nullability, + "LOWER": first_arg_nullability, + }, + function_return_types=SnowflakeAdapter() + .expression_inference_profile() + .function_return_types, + ), + ExpressionInferenceProfile(sql_analysis_dialect="generic"), + ExpressionInferenceProfile(sql_analysis_dialect=None), +) +_TYPES: tuple[str, ...] = ( + "INT", "INTEGER", "BIGINT", "SMALLINT", "TINYINT", "DECIMAL(10, 2)", "DECIMAL(12,2)", + "DECIMAL", "DECIMAL(9)", "NUMERIC(10, 2)", "NUMERIC", "NUMBER", "NUMBER(10, 2)", "VARCHAR", + "VARCHAR(10)", "TEXT", "STRING", "CHAR(3)", "DOUBLE", "FLOAT", "REAL", "FLOAT64", "INT64", + "BOOLEAN", "BOOL", "DATE", "TIMESTAMP", "TIMESTAMPTZ", "TIMESTAMP WITH TIME ZONE", + "TIMESTAMP_NTZ", "DATETIME", "JSON", "VARIANT", "BINARY", "HUGEINT", "BIGNUMERIC", + "NVARCHAR(5)", "DOUBLE PRECISION", "INTERVAL", "UUID", "ARRAY", "INT[]", + "STRUCT", "MAP(VARCHAR, INT)", +) # fmt: skip +_LITERALS: tuple[str, ...] = ("1", "'x'", "NULL", "1.5", "TRUE", "DATE '2020-01-01'") +_MAX_DEPTH: int = 3 +_ALIASES: tuple[str, ...] = ("b", "c", "o2", "X") +_JOINS: tuple[tuple[str, str], ...] = ( + ("JOIN", " ON TRUE"), + ("LEFT JOIN", " ON TRUE"), + ("RIGHT JOIN", " ON TRUE"), + ("FULL OUTER JOIN", " ON TRUE"), + ("LEFT OUTER JOIN", " ON TRUE"), + ("INNER JOIN", " ON TRUE"), + ("CROSS JOIN", ""), +) +_OUTPUT_NAMES: tuple[str, ...] = ( + "c{index}", "C{index}", '"Mx{index}"', "status", "qty", "dup", "Dup", '"Größe"', + '"STRASSE"', '"straße"', "ıd", +) # fmt: skip +_STARS: tuple[str, ...] = ( + "*", + "{alias}.*", + "* EXCLUDE (status)", + "* EXCLUDE (order_id, qty)", + "* REPLACE (1 AS qty)", +) +_SET_OPERATIONS: tuple[str, ...] = ( + "UNION ALL", + "UNION", + "UNION ALL BY NAME", + "EXCEPT", + "INTERSECT", +) +_FACT_KINDS: tuple[str, ...] = ("types", "nullability", "direct", "non_null") +_CTE_NAMES: tuple[str, ...] = ("cte{index}", "Cte{index}", '"Cte{index}"', "base", "orders") + + +def _quoted(rng: random.Random, name: str) -> str: + return rng.choices((f'"{name}"', name.upper(), name.lower(), name), weights=(10, 10, 5, 75))[0] + + +def _column(rng: random.Random, columns: list[str]) -> str: + return _quoted(rng, rng.choice(columns)) + + +def _nested(rng: random.Random, columns: list[str], depth: int) -> str: + return _expression(rng=rng, columns=columns, depth=depth + 1) + + +_LEAVES: tuple[Generate, ...] = ( + lambda rng, columns, depth: _column(rng, columns), + lambda rng, columns, depth: rng.choice(_LITERALS), +) +_COMPOUNDS: tuple[Generate, ...] = ( + lambda rng, columns, depth: _column(rng, columns), + lambda rng, columns, depth: _column(rng, columns), + lambda r, c, d: f"CAST({_nested(r, c, d)} AS {r.choice(_TYPES)})", + lambda r, c, d: f"TRY_CAST({_nested(r, c, d)} AS {r.choice(_TYPES)})", + lambda r, c, d: f"{_nested(r, c, d)}::{r.choice(('INT', 'DECIMAL(5, 1)', 'VARCHAR', 'TEXT'))}", + lambda rng, columns, depth: rng.choice(_LITERALS), + lambda r, c, d: ( + f"CASE WHEN {_nested(r, c, d)} THEN {_nested(r, c, d)} ELSE {_nested(r, c, d)} END" + ), + lambda r, c, d: ( + f"CASE {_nested(r, c, d)} WHEN 1 THEN {_nested(r, c, d)} WHEN 2 THEN {_nested(r, c, d)} END" + ), + lambda r, c, d: f"CASE WHEN {_nested(r, c, d)} THEN {_nested(r, c, d)} END", + lambda r, c, d: f"COALESCE({_nested(r, c, d)}, {_nested(r, c, d)})", + lambda r, c, d: f"COALESCE({_nested(r, c, d)}, {_nested(r, c, d)}, {_nested(r, c, d)})", + lambda r, c, d: f"NULLIF({_nested(r, c, d)}, {_nested(r, c, d)})", + lambda r, c, d: f"IF({_nested(r, c, d)}, {_nested(r, c, d)}, {_nested(r, c, d)})", + lambda r, c, d: f"IFF({_nested(r, c, d)}, {_nested(r, c, d)}, {_nested(r, c, d)})", + lambda r, c, d: f"UPPER({_nested(r, c, d)})", + lambda r, c, d: f"LOWER({_nested(r, c, d)})", + lambda r, c, d: f"{_nested(r, c, d)} || {_nested(r, c, d)}", + lambda r, c, d: f"SUBSTRING({_nested(r, c, d)}, 1, 2)", + lambda r, c, d: f"{r.choice(('MAX', 'MIN', 'SUM', 'AVG', 'COUNT'))}({_nested(r, c, d)})", + lambda r, c, d: f"MAX({_nested(r, c, d)}) OVER (PARTITION BY {_column(r, c)})", + lambda r, c, d: f"ROW_NUMBER() OVER (ORDER BY {_column(r, c)})", + lambda r, c, d: f"{_nested(r, c, d)} {r.choice(('>', '=', '<>', 'LIKE'))} {_nested(r, c, d)}", + lambda r, c, d: f"{_nested(r, c, d)} IS NULL", + lambda r, c, d: f"{_nested(r, c, d)} IS NOT NULL", + lambda r, c, d: f"{_nested(r, c, d)} {r.choice(('+', '*', '/'))} {_nested(r, c, d)}", + lambda r, c, d: ( + f"{r.choice(('TO_VARCHAR', 'ABS', 'LENGTH', 'REPLACE', 'CONCAT', 'TO_DATE'))}" + f"({_nested(r, c, d)}, {_nested(r, c, d)})" + ), + lambda r, c, d: f"LENGTH({_nested(r, c, d)})", + lambda r, c, d: ( + f"{r.choice(('GREATEST', 'CONCAT_WS', 'DECODE'))}({_nested(r, c, d)}, " + f"{_nested(r, c, d)}, {_nested(r, c, d)})" + ), + lambda r, c, d: f"{_nested(r, c, d)} IN ({_nested(r, c, d)}, {_nested(r, c, d)})", + lambda r, c, d: f"[{_nested(r, c, d)}, {_nested(r, c, d)}]", +) + + +def _expression(*, rng: random.Random, columns: list[str], depth: int) -> str: + builders: tuple[Generate, ...] = (_LEAVES, _COMPOUNDS)[depth < _MAX_DEPTH] + return rng.choice(builders)(rng, columns, depth) + + +def _star_projection( + rng: random.Random, aliases: list[str], columns: list[str], index: int +) -> tuple[str, list[str]]: + return rng.choice(_STARS).format(alias=aliases[0]), list(columns) + + +def _column_projection( + rng: random.Random, aliases: list[str], columns: list[str], index: int +) -> tuple[str, list[str]]: + column: str = rng.choice(columns) + qualifier: str = rng.choices(("", f"{rng.choice(aliases)}."), weights=(6, 4))[0] + return f"{qualifier}{_quoted(rng, column)}", [column] + + +def _expression_projection( + rng: random.Random, aliases: list[str], columns: list[str], index: int +) -> tuple[str, list[str]]: + name: str = rng.choice(_OUTPUT_NAMES).format(index=index) + return f"{_expression(rng=rng, columns=columns, depth=0)} AS {name}", [name.strip('"')] + + +_PROJECTIONS: tuple[Callable[..., tuple[str, list[str]]], ...] = ( + _star_projection, + _column_projection, + _expression_projection, +) + + +def _predicate(rng: random.Random, aliases: list[str], columns: list[str]) -> str: + qualifier: str = rng.choice(("", f"{rng.choice(aliases)}.")) + column: str = f"{qualifier}{_column(rng, columns)}" + return rng.choice( + ( + f"{column} IS NOT NULL", + f"({column} IS NOT NULL)", + f"{column} > 1", + f"NOT {column} IS NULL", + ) + ) + + +def _select( + *, rng: random.Random, relations: dict[str, list[str]], nests: bool +) -> tuple[str, list[str]]: + first: str = rng.choice(list(relations)) + alias_sql, alias = rng.choice(((("", first)), (" a", "a"), (" AS A", "A"), (" t1", "t1"))) + columns: list[str] = list(relations[first]) + aliases: list[str] = [alias] + joins: list[str] = [] + for _ in range(rng.choice((0, 0, 1, 1, 2))): + other: str = rng.choice(list(relations)) + join_alias: str = rng.choice(_ALIASES) + join, condition = rng.choice(_JOINS) + joins.append(f"\n{join} {_quoted(rng, other)} AS {join_alias}{condition}") + columns.extend(relations[other]) + aliases.append(join_alias) + projections: list[str] = [] + outputs: list[str] = [] + for index in range(rng.randint(1, 5)): + projection, names = rng.choices(_PROJECTIONS, weights=(12, 33, 55))[0]( + rng, aliases, columns, index + ) + projections.append(projection) + outputs.extend(names) + predicates: list[str] = [_predicate(rng, aliases, columns) for _ in range(rng.randint(1, 3))] + where: str = rng.choices( + ("", "\nWHERE " + rng.choice((" AND ", " OR ", " AND ")).join(predicates)), + weights=(3, 7), + )[0] + group: str = rng.choices(("", "\nGROUP BY 1"), weights=(9, 1))[0] + body: str = ( + f"SELECT {', '.join(projections)}\nFROM {_quoted(rng, first)}{alias_sql}" + f"{''.join(joins)}{where}{group}" + ) + operation: Callable[[], str] = rng.choices( + (lambda: "", lambda: _set_operation(rng=rng, relations=relations)), weights=(85, 15) + )[0] + selected: str = f"{body}{operation()}" + nested: bool = nests and rng.random() < 0.15 + return ( + lambda: (selected, outputs), + lambda: _nested_select(rng=rng, relations=relations), + )[nested]() + + +def _set_operation(*, rng: random.Random, relations: dict[str, list[str]]) -> str: + other, _ = _select(rng=rng, relations=relations, nests=False) + return f"\n{rng.choice(_SET_OPERATIONS)}\n{other}" + + +def _nested_select(*, rng: random.Random, relations: dict[str, list[str]]) -> tuple[str, list[str]]: + inner, outputs = _select(rng=rng, relations=relations, nests=False) + return f"WITH inner_cte AS ({inner})\nSELECT * FROM inner_cte", outputs + + +def generated_cte_query(*, rng: random.Random) -> str: + """A query of one to three CTEs over typed inputs and an outer select reading them.""" + + relations: dict[str, list[str]] = {table: list(columns) for table, columns in _SCHEMAS.items()} + ctes: list[str] = [] + for index in range(rng.randint(1, 3)): + body, outputs = _select(rng=rng, relations=relations, nests=True) + name: str = rng.choice(_CTE_NAMES).format(index=index) + column_aliases: str = rng.choices(("", " (x, y)"), weights=(93, 7))[0] + ctes.append(f"{name}{column_aliases} AS (\n{body}\n)") + relations[name.strip('"')] = outputs or ["order_id"] + cte_relations: dict[str, list[str]] = dict( + filter(lambda item: item[0] not in _SCHEMAS or rng.random() < 0.2, relations.items()) + ) + outer, _ = _select(rng=rng, relations=cte_relations or relations, nests=False) + return "WITH " + ",\n".join(ctes) + "\n" + outer + + +def generated_cte_fact_query(*, rng: random.Random) -> CteFactQuery: + """A generated query under one adapter's profile, with enrichment's recovery gates.""" + + return CteFactQuery( + sql=generated_cte_query(rng=rng), + profile=rng.choice(PROFILES), + recover=rng.random() < 0.9, + null_filter=rng.random() < 0.8, + ) + + +def python_cte_facts(*, query: CteFactQuery) -> CteFactView: + """Python's recovered facts, as enrichment computes them, or what it raises.""" + + analysis: dict[str, Any] = { + "cteFacts": [1][: int(query.recover)], + "columnUses": [_NULL_FILTER_USE][: int(query.null_filter)], + } + dialect: str | None = query.profile.sql_analysis_dialect + try: + types, nullability, direct, parsed = _polyglot_cte_passthrough_facts( + polyglot_module=import_polyglot(), + cleaned_sql=query.sql, + dialect=dialect, + column_types_by_table=_SCHEMAS, + column_nullability_by_table=_INPUT_NULLABILITY, + inference_profile=query.profile, + analysis=analysis, + resolvers=_RESOLVERS, + ) + non_null: frozenset[str] = _polyglot_filtered_non_null_outputs( + polyglot_module=import_polyglot(), + cleaned_sql=query.sql, + dialect=dialect, + analysis=analysis, + column_nullability_by_table=_INPUT_NULLABILITY, + parsed=parsed, + ) + except Exception as error: # noqa: BLE001 - native must not answer where Python raises + return (type(error).__name__, str(error)) + return ( + list(types.items()), + [(name, value.value) for name, value in nullability.items()], + sorted(direct), + sorted(non_null), + ) + + +def native_cte_facts(*, query: CteFactQuery) -> tuple[str | None, CteFactView]: + """Native recovery's deferral reason, or None and its facts.""" + + deferral, types, nullability, direct, non_null = native_module._oracle_cte_fact_recovery( + ( + query.sql, + query.profile.sql_analysis_dialect or "generic", + _INPUT_SHAPES, + list(query.profile.function_return_types.items()), + nullability_rule_rows(query.profile), + query.recover, + query.null_filter, + ) + ) + return deferral, (types, nullability, direct, non_null) + + +def compare_cte_facts(*, query: CteFactQuery, parity: CteFactParity) -> None: + """Record both recoveries of `query` where native answers, and what each recovered.""" + + deferral, native = native_cte_facts(query=query) + answered: list[CteFactView] = [native][: int(deferral is None)] + python: list[CteFactView] = [python_cte_facts(query=query) for _ in answered] + parity.counts.update( + [f"deferred:{str(deferral).partition(' ')[0]}"][: int(deferral is not None)] + ) + parity.counts["compared"] += len(answered) + for index, kind in enumerate(_FACT_KINDS): + parity.counts[f"recovered_{kind}"] += sum(bool(view[index]) for view in answered) + parity.queries.extend((query.sql, query.profile.sql_analysis_dialect) for _ in answered) + parity.python.extend(python) + parity.native.extend(answered) diff --git a/tests/integration/src/sqlbuild/compiler/analysis_session/_test_types.py b/tests/integration/src/sqlbuild/compiler/analysis_session/_test_types.py index e725c50b3f..e9958740b8 100644 --- a/tests/integration/src/sqlbuild/compiler/analysis_session/_test_types.py +++ b/tests/integration/src/sqlbuild/compiler/analysis_session/_test_types.py @@ -15,7 +15,7 @@ class GeneratedAnalysisParityTestCase: expected_minimum_native: int expected_minimum_expression_shapes: int expected_minimum_pivot_proofs: int - expected_minimum_cte_recoveries: int + expected_minimum_python_cte_recoveries: int expected_minimum_proven_pivots: int expected_maximum_enrichment_deferrals: int expected_minimum_native_enrichments: int @@ -50,3 +50,14 @@ class StandalonePivotProofTestCase: analysed_models: frozenset[str] expected_native_proofs: int expected_proven_by_model: dict[str, bool | None] + + +@dataclass(frozen=True) +class CteFactRecoveryParityTestCase: + """Seeded CTE queries whose native and Python CTE fact recoveries must agree.""" + + description: str + seed: int + count: int + expected_minimum_compared: int + expected_minimum_recovered: dict[str, int] diff --git a/tests/integration/src/sqlbuild/compiler/analysis_session/helpers.py b/tests/integration/src/sqlbuild/compiler/analysis_session/helpers.py index e7b002e85d..6c6a9d89be 100644 --- a/tests/integration/src/sqlbuild/compiler/analysis_session/helpers.py +++ b/tests/integration/src/sqlbuild/compiler/analysis_session/helpers.py @@ -406,7 +406,7 @@ class AnalysisParity: analysed_models: int = 0 expression_shapes: int = 0 pivot_proofs: int = 0 - cte_recoveries: int = 0 + python_cte_recoveries: int = 0 standalone_proofs: int = 0 proven_pivots: int = 0 native_enrichments: int = 0 @@ -557,7 +557,7 @@ def deferral_kinds(record_dir: Path) -> Counter[str]: def _counted_cte_recovery(*, parity: AnalysisParity, **arguments: Any) -> Any: recovered: Any = _PYTHON_CTE_RECOVERY(**arguments) - parity.cte_recoveries += bool(recovered[2]) + parity.python_cte_recoveries += bool(recovered[2]) return recovered diff --git a/tests/integration/src/sqlbuild/compiler/analysis_session/test_native_cte_fact_recovery.py b/tests/integration/src/sqlbuild/compiler/analysis_session/test_native_cte_fact_recovery.py new file mode 100644 index 0000000000..119e8e0c71 --- /dev/null +++ b/tests/integration/src/sqlbuild/compiler/analysis_session/test_native_cte_fact_recovery.py @@ -0,0 +1,56 @@ +"""Native CTE fact recovery equals Python's recovery on seeded queries, query by query.""" + +from __future__ import annotations + +import random +from collections import Counter + +import pytest + +from tests.integration.src.sqlbuild.compiler.analysis_session._test_helpers import ( + CteFactParity, + compare_cte_facts, + generated_cte_fact_query, +) +from tests.integration.src.sqlbuild.compiler.analysis_session._test_types import ( + CteFactRecoveryParityTestCase, +) +from tests.integration.src.sqlbuild.compiler.helpers import mismatches + + +@pytest.mark.parametrize( + "test_case", + [ + CteFactRecoveryParityTestCase( + description="casts, CASE, COALESCE, IF/IFF, adapter rules, set operations, stars, joins, filters", + seed=20261009, + count=5000, + expected_minimum_compared=4319, + expected_minimum_recovered={ + "types": 1243, + "nullability": 417, + "direct": 859, + "non_null": 38, + }, + ) + ], + ids=lambda case: case.description, +) +def test_given_seeded_cte_queries_when_recovering_facts_natively_then_matches_python( + test_case: CteFactRecoveryParityTestCase, +) -> None: + rng: random.Random = random.Random(test_case.seed) + parity: CteFactParity = CteFactParity(queries=[], python=[], native=[], counts=Counter()) + for _ in range(test_case.count): + compare_cte_facts(query=generated_cte_fact_query(rng=rng), parity=parity) + + assert mismatches(inputs=parity.queries, expected=parity.python, actual=parity.native) == [] + assert parity.counts["compared"] >= test_case.expected_minimum_compared + assert { + kind: parity.counts[f"recovered_{kind}"] >= minimum + for kind, minimum in test_case.expected_minimum_recovered.items() + } == dict.fromkeys(test_case.expected_minimum_recovered, True) + + +if __name__ == "__main__": + pytest.main([__file__, "-vv"]) diff --git a/tests/integration/src/sqlbuild/compiler/analysis_session/test_native_model_analysis.py b/tests/integration/src/sqlbuild/compiler/analysis_session/test_native_model_analysis.py index 8ba99d7ca8..f0d116fe9d 100644 --- a/tests/integration/src/sqlbuild/compiler/analysis_session/test_native_model_analysis.py +++ b/tests/integration/src/sqlbuild/compiler/analysis_session/test_native_model_analysis.py @@ -62,7 +62,7 @@ expected_minimum_native=500, expected_minimum_expression_shapes=40, expected_minimum_pivot_proofs=80, - expected_minimum_cte_recoveries=20, + expected_minimum_python_cte_recoveries=40, expected_minimum_proven_pivots=12, expected_maximum_enrichment_deferrals=0, expected_minimum_native_enrichments=90, @@ -91,7 +91,7 @@ def test_given_generated_projects_when_analysing_natively_then_matches_python( assert mismatches(inputs=parity.names, expected=parity.python, actual=parity.native) == [] assert kinds["analysis_session:session"] == kinds["analysis_session:expression_shapes"] == 0 assert kinds["analysis_session:dynamic_pivot"] == 0 - assert parity.cte_recoveries >= test_case.expected_minimum_cte_recoveries + assert parity.python_cte_recoveries >= test_case.expected_minimum_python_cte_recoveries assert ( parity.analysed_models - kinds["analysis_session:legacy_analysis"] From 046309c52739766db4b5e33c89160924a8291182 Mon Sep 17 00:00:00 2001 From: Kevin Longe <273747162+kvlonge@users.noreply.github.com> Date: Fri, 9 Oct 2026 15:27:55 +0100 Subject: [PATCH 04/31] fix(compile): reuse SQL test artifacts whose bytes are unchanged when only the mtime moved --- .../compile/sql_test_artifact_cache.py | 31 +++++++++----- .../_helpers/compile/target_writer.py | 11 ++--- src/sqlbuild/cli/compile/models.py | 1 + .../compile/test_compile_target_writer.py | 42 +++++++++++++++++++ 4 files changed, 70 insertions(+), 15 deletions(-) diff --git a/src/sqlbuild/cli/commands/_helpers/compile/sql_test_artifact_cache.py b/src/sqlbuild/cli/commands/_helpers/compile/sql_test_artifact_cache.py index c34c588311..64c5ccd69f 100644 --- a/src/sqlbuild/cli/commands/_helpers/compile/sql_test_artifact_cache.py +++ b/src/sqlbuild/cli/commands/_helpers/compile/sql_test_artifact_cache.py @@ -6,6 +6,7 @@ import json import os import platform +from dataclasses import replace from importlib.metadata import PackageNotFoundError, version from pathlib import Path from typing import cast @@ -26,7 +27,7 @@ CompileSqlTestCte, ) -_CACHE_VERSION: int = 3 +_CACHE_VERSION: int = 4 _ALGORITHM_FINGERPRINT: str = "sql-test-artifact-v4-orjson-identity" _CACHE_FILE_NAME: str = "sql-test-artifacts.json" _MAX_CACHE_BYTES: int = 10_000_000 @@ -142,6 +143,7 @@ def write_sql_test_artifact_cache( "relative_path": record.relative_path.as_posix(), "size": record.size, "mtime_ns": record.mtime_ns, + "content_sha256": record.content_sha256, } for key, record in records.items() }, @@ -161,23 +163,23 @@ def write_sql_test_artifact_cache( def artifact_matches_cache_record( *, tests_root: Path, record: SqlTestArtifactCacheRecord, identity: str -) -> Path | None: - """Return the safe existing artifact path when identity and stat metadata match.""" +) -> SqlTestArtifactCacheRecord | None: + """Return the reusable artifact's record, verifying content when only its mtime moved.""" if record.identity != identity or not _is_safe_relative_sql_path(record.relative_path): return None artifact_path: Path = tests_root / record.relative_path try: stat: os.stat_result = artifact_path.stat() + if not artifact_path.is_file() or stat.st_size != record.size: + return None + if stat.st_mtime_ns == record.mtime_ns: + return record + if _file_sha256(artifact_path) != record.content_sha256: + return None except OSError: return None - if ( - not artifact_path.is_file() - or stat.st_size != record.size - or stat.st_mtime_ns != record.mtime_ns - ): - return None - return artifact_path + return replace(record, mtime_ns=stat.st_mtime_ns) def build_sql_test_artifact_cache_record( @@ -188,6 +190,7 @@ def build_sql_test_artifact_cache_record( try: relative_path: Path = artifact_path.relative_to(tests_root) stat: os.stat_result = artifact_path.stat() + content_sha256: str = _file_sha256(artifact_path) except (OSError, ValueError): return None if not _is_safe_relative_sql_path(relative_path): @@ -197,6 +200,7 @@ def build_sql_test_artifact_cache_record( relative_path=relative_path, size=stat.st_size, mtime_ns=stat.st_mtime_ns, + content_sha256=content_sha256, ) @@ -252,6 +256,7 @@ def _record_from_payload(payload: object) -> SqlTestArtifactCacheRecord | None: relative_path: object = values.get("relative_path") size: object = values.get("size") mtime_ns: object = values.get("mtime_ns") + content_sha256: object = values.get("content_sha256") if ( not isinstance(identity, str) or not isinstance(relative_path, str) @@ -261,6 +266,7 @@ def _record_from_payload(payload: object) -> SqlTestArtifactCacheRecord | None: or not isinstance(mtime_ns, int) or isinstance(mtime_ns, bool) or mtime_ns < 0 + or not isinstance(content_sha256, str) ): return None path: Path = Path(relative_path) @@ -271,6 +277,7 @@ def _record_from_payload(payload: object) -> SqlTestArtifactCacheRecord | None: relative_path=path, size=size, mtime_ns=mtime_ns, + content_sha256=content_sha256, ) @@ -282,6 +289,10 @@ def _is_safe_relative_sql_path(path: Path) -> bool: ) +def _file_sha256(path: Path) -> str: + return hashlib.sha256(path.read_bytes()).hexdigest() + + def _payload_digest(payload: object) -> str: return hashlib.sha256(orjson.dumps(payload, option=orjson.OPT_SORT_KEYS)).hexdigest() diff --git a/src/sqlbuild/cli/commands/_helpers/compile/target_writer.py b/src/sqlbuild/cli/commands/_helpers/compile/target_writer.py index 6f04c17d01..b58ec2ecce 100644 --- a/src/sqlbuild/cli/commands/_helpers/compile/target_writer.py +++ b/src/sqlbuild/cli/commands/_helpers/compile/target_writer.py @@ -544,19 +544,20 @@ def _partition_cached_static_tests( ) cached_record: SqlTestArtifactCacheRecord | None = cached_records.get(record_key) if cached_record is not None: - cached_path: Path | None = artifact_matches_cache_record( + reusable_record: SqlTestArtifactCacheRecord | None = artifact_matches_cache_record( tests_root=tests_root, record=cached_record, identity=artifact_identity, ) - if cached_path is not None: + if reusable_record is not None: + cached_path: Path = tests_root / reusable_record.relative_path COMPILE_ARTIFACT_WRITES.kept( path=cached_path, - size=cached_record.size, - mtime_ns=cached_record.mtime_ns, + size=reusable_record.size, + mtime_ns=reusable_record.mtime_ns, ) managed_paths.add(cached_path) - current_records[record_key] = cached_record + current_records[record_key] = reusable_record continue pending.append( PendingStaticSqlTest( diff --git a/src/sqlbuild/cli/compile/models.py b/src/sqlbuild/cli/compile/models.py index a590ab705e..2ef89e4674 100644 --- a/src/sqlbuild/cli/compile/models.py +++ b/src/sqlbuild/cli/compile/models.py @@ -66,6 +66,7 @@ class SqlTestArtifactCacheRecord: relative_path: Path size: int mtime_ns: int + content_sha256: str @dataclass(frozen=True) diff --git a/tests/unit/src/sqlbuild/cli/commands/main/compile/test_compile_target_writer.py b/tests/unit/src/sqlbuild/cli/commands/main/compile/test_compile_target_writer.py index 5ce8ab56c7..c851c21ef7 100644 --- a/tests/unit/src/sqlbuild/cli/commands/main/compile/test_compile_target_writer.py +++ b/tests/unit/src/sqlbuild/cli/commands/main/compile/test_compile_target_writer.py @@ -341,6 +341,48 @@ def test_given_unchanged_test_artifact_when_writing_again_then_skips_test_plan_r assert planner_spy.call_count == test_case.expected_builder_calls +@pytest.mark.parametrize( + "test_case", + ( + TargetWriterCacheTestCase( + description="identical bytes with a moved mtime", expected_builder_calls=0 + ), + ), + ids=lambda case: case.description, +) +def test_given_artifact_with_moved_mtime_and_same_bytes_when_writing_then_reuses_it( + tmp_path: Path, + monkeypatch: pytest.MonkeyPatch, + test_case: TargetWriterCacheTestCase, +) -> None: + target_dir: Path = tmp_path / "target" + project: CompiledProject = build_cached_target_writer_project(target_dir=target_dir) + _ = write_static_compile_target( + target_dir=target_dir, + adapter=DuckDbAdapter(), + project=project, + ) + artifact_path: Path = next((target_dir / "compiled" / "tests").rglob("*.sql")) + moved_mtime_ns: int = artifact_path.stat().st_mtime_ns + 1_000_000_000 + os.utime(artifact_path, ns=(moved_mtime_ns, moved_mtime_ns)) + planner_spy: Mock = Mock(wraps=target_writer_module.plan_and_render_sql_test_artifacts) + monkeypatch.setattr(target_writer_module, "plan_and_render_sql_test_artifacts", planner_spy) + + _ = write_static_compile_target( + target_dir=target_dir, + adapter=DuckDbAdapter(), + project=project, + ) + _ = write_static_compile_target( + target_dir=target_dir, + adapter=DuckDbAdapter(), + project=project, + ) + + assert planner_spy.call_count == test_case.expected_builder_calls + assert artifact_path.stat().st_mtime_ns == moved_mtime_ns + + @pytest.mark.parametrize( "test_case", ( From 702b6be6aabfccfefec450ebb7c9447469f0bcf3 Mon Sep 17 00:00:00 2001 From: Kevin Longe <273747162+kvlonge@users.noreply.github.com> Date: Fri, 9 Oct 2026 14:25:59 +0100 Subject: [PATCH 05/31] feat(native): batch dynamic pivot proofs and opt-out syntax checks through the native session The finished analysis session is kept on CompiledProject.native_session and proves pivot models outside it from its own tables; without a session one batched standalone call sends the tables once. The per-model native proof path is deleted. SQL-analysis opt-out syntax checks use the native checker under the preview engine. Rule hosts drop the session before pickling. --- .../_helpers/dynamic_pivot.rs | 46 ++++++++- .../analysis_session/_helpers/session.rs | 26 ++++- .../main/finish_analysis_session.rs | 8 +- .../src/assembly/analysis_session/main/mod.rs | 3 +- .../main/prove_dynamic_contract.rs | 26 ----- .../main/prove_dynamic_contracts.rs | 17 ++++ .../main/prove_finished_dynamic_contracts.rs | 13 +++ .../src/assembly/analysis_session/models.rs | 26 ++++- .../analysis_session/tests/helpers.rs | 50 +++++++--- .../tests/test_analysis_session.rs | 20 +++- .../assembly/project/main/check_sql_syntax.rs | 28 ++++++ .../src/assembly/project/main/mod.rs | 1 + .../src/assembly/project/tests/helpers.rs | 12 +++ .../project/tests/test_project_assembly.rs | 38 ++++++- .../src/assembly/project/tests/test_types.rs | 7 ++ .../_helpers/analysis_session/session.rs | 78 ++++++++++----- .../_helpers/project_assembly/resources.rs | 19 ++++ src/sqlbuild/_native.pyi | 37 ++++--- .../_helpers/dynamic_contract.py | 52 ---------- .../_helpers/dynamic_contracts.py | 73 ++++++++++++++ .../_helpers/model_analysis.py | 18 ++-- .../main/_analyze_native_model_sql.py | 12 +-- .../main/_prove_native_dynamic_contract.py | 31 ------ .../main/_prove_native_dynamic_contracts.py | 21 ++++ .../compiler/analysis_session/models.py | 26 ++++- .../_helpers/analysis/pivot_requests.py | 50 ++++++++++ .../compile/_helpers/assembly/project.py | 60 +++++------ .../diagnostics/sql_analysis_opt_outs.py | 28 +++++- .../_helpers/native_stages/assembly.py | 99 ++++++++++++++----- .../main/_check_native_sql_syntax.py | 24 +++++ .../compiler/project_assembly/models.py | 22 ++++- .../_helpers/engine/custom_rules.py | 1 + .../compile/test_native_analysis_seams.py | 5 +- .../compiler/analysis_session/_test_types.py | 9 ++ .../compiler/analysis_session/helpers.py | 85 +++++++++++----- .../test_native_model_analysis.py | 67 +++++++++++-- .../compiler/project_assembly/_test_types.py | 10 ++ .../compiler/project_assembly/helpers.py | 34 +++++++ .../test_native_project_assembly.py | 51 ++++++++++ .../host_payload/test_host_payload_facts.py | 2 +- .../_helpers/native_stages/_test_types.py | 1 + .../test_dynamic_column_contract_by_engine.py | 13 +++ 42 files changed, 966 insertions(+), 283 deletions(-) delete mode 100644 crates/sqlbuild-analysis/src/assembly/analysis_session/main/prove_dynamic_contract.rs create mode 100644 crates/sqlbuild-analysis/src/assembly/analysis_session/main/prove_dynamic_contracts.rs create mode 100644 crates/sqlbuild-analysis/src/assembly/analysis_session/main/prove_finished_dynamic_contracts.rs create mode 100644 crates/sqlbuild-analysis/src/assembly/project/main/check_sql_syntax.rs delete mode 100644 src/sqlbuild/compiler/analysis_session/_helpers/dynamic_contract.py create mode 100644 src/sqlbuild/compiler/analysis_session/_helpers/dynamic_contracts.py delete mode 100644 src/sqlbuild/compiler/analysis_session/main/_prove_native_dynamic_contract.py create mode 100644 src/sqlbuild/compiler/analysis_session/main/_prove_native_dynamic_contracts.py create mode 100644 src/sqlbuild/compiler/compile/_helpers/analysis/pivot_requests.py create mode 100644 src/sqlbuild/compiler/project_assembly/main/_check_native_sql_syntax.py diff --git a/crates/sqlbuild-analysis/src/assembly/analysis_session/_helpers/dynamic_pivot.rs b/crates/sqlbuild-analysis/src/assembly/analysis_session/_helpers/dynamic_pivot.rs index 78f0b3bdba..88a2523cb7 100644 --- a/crates/sqlbuild-analysis/src/assembly/analysis_session/_helpers/dynamic_pivot.rs +++ b/crates/sqlbuild-analysis/src/assembly/analysis_session/_helpers/dynamic_pivot.rs @@ -1,8 +1,11 @@ //! Python's `analyze_dynamic_column_contract` over the crate's parse of the model SQL. use std::collections::{HashMap, HashSet}; +use std::panic::{AssertUnwindSafe, catch_unwind}; use polyglot_sql::{ComplexityGuardOptions, Dialect, Expression, ParseOptions}; +use rayon::ThreadPool; +use rayon::iter::{IntoParallelRefIterator, ParallelIterator}; use serde_json::{Map, Value, json}; use sqlbuild_core::panics::main::catch_compiler_panic::catch_compiler_panic; @@ -17,7 +20,7 @@ use crate::assembly::analysis_session::constants::{ UNKNOWN_NULLABILITY, UNKNOWN_TYPE, }; use crate::assembly::analysis_session::models::{ - ColumnFact, ContractProof, DynamicFamily, PivotOutcome, + ColumnFact, ContractProof, DynamicFamily, PivotModel, PivotOutcome, PivotTables, }; use crate::assembly::analysis_session::types::{Pairs, Shapes}; @@ -33,6 +36,47 @@ pub(crate) struct PivotFacts<'a> { pub(crate) families_by_table: &'a [(String, Vec)], } +/// Each model's proof in order; a model whose proof panics is deferred to Python. +pub(crate) fn pivot_outcomes(models: &[PivotModel], tables: &PivotTables) -> Vec { + let facts = pivot_facts(tables); + models + .iter() + .map(|model| guarded_outcome(model, &facts)) + .collect() +} + +/// Each model's proof in order on `pool`; a model whose proof panics is deferred to Python. +pub(crate) fn pooled_pivot_outcomes( + pool: &ThreadPool, + models: &[PivotModel], + tables: &PivotTables, +) -> Vec { + let facts = pivot_facts(tables); + pool.install(|| { + models + .par_iter() + .map(|model| guarded_outcome(model, &facts)) + .collect() + }) +} + +fn pivot_facts(tables: &PivotTables) -> PivotFacts<'_> { + PivotFacts { + dialect: &tables.dialect, + column_types: &tables.column_types, + authoritative_types: &tables.authoritative_types, + column_nullability: &tables.column_nullability, + families_by_table: &tables.families_by_table, + } +} + +fn guarded_outcome(model: &PivotModel, facts: &PivotFacts<'_>) -> PivotOutcome { + catch_unwind(AssertUnwindSafe(|| { + pivot_outcome(&model.sql, &model.families, facts) + })) + .unwrap_or(PivotOutcome::Deferred) +} + /// One CTE body by folded name; None for a CTE with column aliases. type CteMap<'a> = HashMap>; diff --git a/crates/sqlbuild-analysis/src/assembly/analysis_session/_helpers/session.rs b/crates/sqlbuild-analysis/src/assembly/analysis_session/_helpers/session.rs index cf945f7c71..fa9ebdc21f 100644 --- a/crates/sqlbuild-analysis/src/assembly/analysis_session/_helpers/session.rs +++ b/crates/sqlbuild-analysis/src/assembly/analysis_session/_helpers/session.rs @@ -1,7 +1,9 @@ //! The session's phases: Python's uncached model analysis, wave by wave. use std::collections::HashMap; +use std::sync::Arc; +use rayon::ThreadPool; use rayon::iter::{IntoParallelRefIterator, ParallelIterator}; use crate::assembly::analysis_session::_helpers::catalog_state::SessionCatalog; @@ -21,8 +23,9 @@ use crate::assembly::analysis_session::constants::{ DEFERRAL_ANALYSIS, DEFERRAL_ENRICHMENT, UNKNOWN_NULLABILITY, UNKNOWN_TYPE, }; use crate::assembly::analysis_session::models::{ - AnalysisSession, Awaiting, ColumnFact, Deferral, DeferredAnalysis, LineageFacts, ModelAnalysis, - ModelOutcome, ModelRequest, Phase, PivotOutcome, SessionOutcome, SessionRequest, SessionStep, + AnalysisSession, Awaiting, ColumnFact, Deferral, DeferredAnalysis, FinishedSession, + LineageFacts, ModelAnalysis, ModelOutcome, ModelRequest, Phase, PivotOutcome, PivotTables, + SessionOutcome, SessionRequest, SessionStep, }; use crate::assembly::analysis_session::types::{Pairs, Shapes}; use crate::semantic_validation::models::ProjectCatalog; @@ -148,23 +151,36 @@ impl AnalysisSession { } /// Every model's outcome and the binding catalog changes Python records. - pub(crate) fn finish(self) -> Result { + pub(crate) fn finish(self) -> Result<(SessionOutcome, FinishedSession), String> { if !matches!(self.phase, Phase::Done) { return Err("the session has not finished".to_owned()); } let dynamic_contracts: Vec = self.dynamic_contracts()?; + let pool: Result, String> = self.catalog.native.analysis_pool(); let models: Vec = self .outcomes .into_iter() .collect::>() .ok_or("the session left a model unanalysed")?; let (schema_additions, analysis_names) = self.catalog.into_changes(); - Ok(SessionOutcome { + let request: SessionRequest = self.request; + let finished = FinishedSession { + tables: PivotTables { + dialect: request.dialect, + column_types: request.column_types, + authoritative_types: request.complete_schemas, + column_nullability: request.column_nullability, + families_by_table: request.dynamic_families_by_table, + }, + pool, + }; + let outcome = SessionOutcome { models, schema_additions, analysis_names, dynamic_contracts, - }) + }; + Ok((outcome, finished)) } /// Python's dynamic pivot proof of each model over the relation facts before analysis. diff --git a/crates/sqlbuild-analysis/src/assembly/analysis_session/main/finish_analysis_session.rs b/crates/sqlbuild-analysis/src/assembly/analysis_session/main/finish_analysis_session.rs index f7a4fec8b3..11185a1ad6 100644 --- a/crates/sqlbuild-analysis/src/assembly/analysis_session/main/finish_analysis_session.rs +++ b/crates/sqlbuild-analysis/src/assembly/analysis_session/main/finish_analysis_session.rs @@ -1,8 +1,10 @@ //! Collect a finished native model analysis session's outcomes. -use crate::assembly::analysis_session::models::{AnalysisSession, SessionOutcome}; +use crate::assembly::analysis_session::models::{AnalysisSession, FinishedSession, SessionOutcome}; -/// Every model's outcome and the binding catalog changes Python records. -pub fn finish_analysis_session(session: AnalysisSession) -> Result { +/// Every model's outcome and catalog changes, and the session kept for later pivot proofs. +pub fn finish_analysis_session( + session: AnalysisSession, +) -> Result<(SessionOutcome, FinishedSession), String> { session.finish() } diff --git a/crates/sqlbuild-analysis/src/assembly/analysis_session/main/mod.rs b/crates/sqlbuild-analysis/src/assembly/analysis_session/main/mod.rs index 6c5394355c..f50a22f2ca 100644 --- a/crates/sqlbuild-analysis/src/assembly/analysis_session/main/mod.rs +++ b/crates/sqlbuild-analysis/src/assembly/analysis_session/main/mod.rs @@ -1,6 +1,7 @@ pub mod expression_shapes; pub mod finish_analysis_session; -pub mod prove_dynamic_contract; +pub mod prove_dynamic_contracts; +pub mod prove_finished_dynamic_contracts; pub mod provide_deferred_analyses; pub mod run_analysis_session; pub mod start_analysis_session; diff --git a/crates/sqlbuild-analysis/src/assembly/analysis_session/main/prove_dynamic_contract.rs b/crates/sqlbuild-analysis/src/assembly/analysis_session/main/prove_dynamic_contract.rs deleted file mode 100644 index 96759642d4..0000000000 --- a/crates/sqlbuild-analysis/src/assembly/analysis_session/main/prove_dynamic_contract.rs +++ /dev/null @@ -1,26 +0,0 @@ -//! Python's `analyze_dynamic_column_contract` for one model outside the analysis session. - -use crate::assembly::analysis_session::_helpers::dynamic_pivot::{PivotFacts, pivot_outcome}; -use crate::assembly::analysis_session::constants::PIVOT_WORKER_STACK_BYTES; -use crate::assembly::analysis_session::models::{PivotOutcome, PivotRequest}; - -/// The model's proof, or a deferral where Python's result is not reproduced exactly. -pub fn prove_dynamic_contract(request: &PivotRequest) -> Result { - let facts = PivotFacts { - dialect: &request.dialect, - column_types: &request.column_types, - authoritative_types: &request.authoritative_types, - column_nullability: &request.column_nullability, - families_by_table: &request.families_by_table, - }; - std::thread::scope(|scope| { - std::thread::Builder::new() - .stack_size(PIVOT_WORKER_STACK_BYTES) - .spawn_scoped(scope, || { - pivot_outcome(&request.sql, &request.families, &facts) - }) - .map_err(|error| error.to_string())? - .join() - .map_err(|_| "the native dynamic pivot proof panicked".to_owned()) - }) -} diff --git a/crates/sqlbuild-analysis/src/assembly/analysis_session/main/prove_dynamic_contracts.rs b/crates/sqlbuild-analysis/src/assembly/analysis_session/main/prove_dynamic_contracts.rs new file mode 100644 index 0000000000..bbbb199898 --- /dev/null +++ b/crates/sqlbuild-analysis/src/assembly/analysis_session/main/prove_dynamic_contracts.rs @@ -0,0 +1,17 @@ +//! Python's `analyze_dynamic_column_contract` for models no analysis session analysed. + +use crate::assembly::analysis_session::_helpers::dynamic_pivot::pivot_outcomes; +use crate::assembly::analysis_session::constants::PIVOT_WORKER_STACK_BYTES; +use crate::assembly::analysis_session::models::{PivotBatchRequest, PivotOutcome}; + +/// Each model's proof in order; a model whose proof panics is deferred to Python. +pub fn prove_dynamic_contracts(request: &PivotBatchRequest) -> Result, String> { + std::thread::scope(|scope| { + std::thread::Builder::new() + .stack_size(PIVOT_WORKER_STACK_BYTES) + .spawn_scoped(scope, || pivot_outcomes(&request.models, &request.tables)) + .map_err(|error| error.to_string())? + .join() + .map_err(|_| "the native dynamic pivot proofs panicked".to_owned()) + }) +} diff --git a/crates/sqlbuild-analysis/src/assembly/analysis_session/main/prove_finished_dynamic_contracts.rs b/crates/sqlbuild-analysis/src/assembly/analysis_session/main/prove_finished_dynamic_contracts.rs new file mode 100644 index 0000000000..28a9f22db8 --- /dev/null +++ b/crates/sqlbuild-analysis/src/assembly/analysis_session/main/prove_finished_dynamic_contracts.rs @@ -0,0 +1,13 @@ +//! Python's `analyze_dynamic_column_contract` for models outside a finished analysis session. + +use crate::assembly::analysis_session::_helpers::dynamic_pivot::pooled_pivot_outcomes; +use crate::assembly::analysis_session::models::{FinishedSession, PivotModel, PivotOutcome}; + +/// Each model's proof in order from the session's own tables; a panicking proof is deferred. +pub fn prove_finished_dynamic_contracts( + session: &FinishedSession, + models: &[PivotModel], +) -> Result, String> { + let pool = session.pool.as_ref().map_err(Clone::clone)?; + Ok(pooled_pivot_outcomes(pool, models, &session.tables)) +} diff --git a/crates/sqlbuild-analysis/src/assembly/analysis_session/models.rs b/crates/sqlbuild-analysis/src/assembly/analysis_session/models.rs index d10d56c0ad..565ccf3544 100644 --- a/crates/sqlbuild-analysis/src/assembly/analysis_session/models.rs +++ b/crates/sqlbuild-analysis/src/assembly/analysis_session/models.rs @@ -1,6 +1,9 @@ //! Plain-data request, deferrals and outcomes of the native model analysis session. use std::collections::HashMap; +use std::sync::Arc; + +use rayon::ThreadPool; use crate::assembly::analysis_session::_helpers::catalog_state::SessionCatalog; use crate::assembly::analysis_session::_helpers::mappings::ShapeTable; @@ -61,18 +64,37 @@ pub struct ContractProof { pub bare_dynamic_pivot: bool, } -/// One model's dynamic pivot proof inputs, for a model the session does not analyse. +/// The relation facts every dynamic pivot proof reads: Python's tables before analysis. #[derive(Debug, Clone, Default)] -pub struct PivotRequest { +pub struct PivotTables { pub dialect: String, pub column_types: Shapes, pub authoritative_types: Shapes, pub column_nullability: Shapes, pub families_by_table: Vec<(String, Vec)>, +} + +/// One model's pivot SQL and declared families. +#[derive(Debug, Clone, Default)] +pub struct PivotModel { pub sql: String, pub families: Vec, } +/// Dynamic pivot proofs for models no session analysed. +#[derive(Debug, Clone, Default)] +pub struct PivotBatchRequest { + pub tables: PivotTables, + pub models: Vec, +} + +/// A finished session's pivot tables and analysis pool, kept to prove models outside it. +#[derive(Debug)] +pub struct FinishedSession { + pub(crate) tables: PivotTables, + pub(crate) pool: Result, String>, +} + /// A model's dynamic pivot proof: none declared, left to Python, or proven natively. #[derive(Debug, Clone, PartialEq, Eq)] pub enum PivotOutcome { diff --git a/crates/sqlbuild-analysis/src/assembly/analysis_session/tests/helpers.rs b/crates/sqlbuild-analysis/src/assembly/analysis_session/tests/helpers.rs index 1f274fdfc1..0970aafe27 100644 --- a/crates/sqlbuild-analysis/src/assembly/analysis_session/tests/helpers.rs +++ b/crates/sqlbuild-analysis/src/assembly/analysis_session/tests/helpers.rs @@ -5,8 +5,8 @@ use crate::assembly::analysis_session::main::run_analysis_session::run_analysis_ use crate::assembly::analysis_session::main::start_analysis_session::start_analysis_session; use crate::assembly::analysis_session::models::{ AnalysisSession, ColumnFact, ContractProof, Deferral, DeferredAnalysis, DynamicFamily, - ModelOutcome, ModelReference, ModelRequest, PivotOutcome, PivotRequest, SessionRequest, - SessionStep, + FinishedSession, ModelOutcome, ModelReference, ModelRequest, PivotBatchRequest, PivotModel, + PivotOutcome, PivotTables, SessionRequest, SessionStep, }; use crate::assembly::analysis_session::tests::test_types::ModelSpec; use crate::assembly::analysis_session::types::{Pairs, Shapes}; @@ -131,7 +131,7 @@ pub(crate) fn completed( }) }) .collect(); - let outcome = finish_analysis_session(session).expect("the session finished"); + let (outcome, _) = finish_analysis_session(session).expect("the session finished"); (steps, outcome.models) } @@ -215,12 +215,12 @@ pub(crate) fn session_lines(models: &[ModelSpec]) -> (Vec>, Vec, -) -> PivotRequest { +) -> PivotBatchRequest { let raw_orders: Shapes = shapes(&[( "raw_orders", &[ @@ -237,17 +237,35 @@ pub(crate) fn pivot_request( }) .into_iter() .collect(); - PivotRequest { - dialect: dialect.to_owned(), - column_types: [raw_orders.clone(), upstream].concat(), - authoritative_types: raw_orders, - column_nullability: shapes(&[("raw_orders", &[("customer_id", "non_null")])]), - families_by_table: vec![( - "status_amounts".to_owned(), - vec![amounts("status", "amount", "MAX")], - )], - sql: sql.to_owned(), - families, + PivotBatchRequest { + tables: PivotTables { + dialect: dialect.to_owned(), + column_types: [raw_orders.clone(), upstream].concat(), + authoritative_types: raw_orders, + column_nullability: shapes(&[("raw_orders", &[("customer_id", "non_null")])]), + families_by_table: vec![( + "status_amounts".to_owned(), + vec![amounts("status", "amount", "MAX")], + )], + }, + models: vec![ + PivotModel { + sql: sql.to_owned(), + families, + }, + PivotModel { + sql: "SELECT 1".to_owned(), + families: Vec::new(), + }, + ], + } +} + +/// A finished session holding `tables` and an analysis pool. +pub(crate) fn finished_session(tables: PivotTables) -> FinishedSession { + FinishedSession { + tables, + pool: catalog("duckdb", &Vec::new()).analysis_pool(), } } diff --git a/crates/sqlbuild-analysis/src/assembly/analysis_session/tests/test_analysis_session.rs b/crates/sqlbuild-analysis/src/assembly/analysis_session/tests/test_analysis_session.rs index c56625f012..ef2f2315bf 100644 --- a/crates/sqlbuild-analysis/src/assembly/analysis_session/tests/test_analysis_session.rs +++ b/crates/sqlbuild-analysis/src/assembly/analysis_session/tests/test_analysis_session.rs @@ -1,8 +1,10 @@ -use crate::assembly::analysis_session::main::prove_dynamic_contract::prove_dynamic_contract; +use crate::assembly::analysis_session::main::prove_dynamic_contracts::prove_dynamic_contracts; +use crate::assembly::analysis_session::main::prove_finished_dynamic_contracts::prove_finished_dynamic_contracts; use crate::assembly::analysis_session::main::start_analysis_session::start_analysis_session; use crate::assembly::analysis_session::models::PivotOutcome; use crate::assembly::analysis_session::tests::helpers::{ - catalog, failed, model_requests, orders_request, pivot_request, proven, session_lines, + catalog, failed, finished_session, model_requests, orders_request, pivot_request, proven, + session_lines, }; use crate::assembly::analysis_session::tests::test_types::{ PivotTestCase, SessionTestCase, UnscheduledTestCase, @@ -281,11 +283,21 @@ fn given_dynamic_pivots_when_proving_then_matches_python_or_defers() { ]; for test_case in test_cases { let request = pivot_request(test_case.dialect, test_case.sql, test_case.family); + let session = finished_session(request.tables.clone()); - let outcome = prove_dynamic_contract(&request).expect("the proof runs"); + let standalone = prove_dynamic_contracts(&request).expect("the proofs run"); + let in_session = + prove_finished_dynamic_contracts(&session, &request.models).expect("the proofs run"); assert_eq!( - outcome, test_case.expected_outcome, + standalone, + vec![test_case.expected_outcome.clone(), PivotOutcome::Absent], + "{}", + test_case.description + ); + assert_eq!( + in_session, + vec![test_case.expected_outcome, PivotOutcome::Absent], "{}", test_case.description ); diff --git a/crates/sqlbuild-analysis/src/assembly/project/main/check_sql_syntax.rs b/crates/sqlbuild-analysis/src/assembly/project/main/check_sql_syntax.rs new file mode 100644 index 0000000000..a2604be080 --- /dev/null +++ b/crates/sqlbuild-analysis/src/assembly/project/main/check_sql_syntax.rs @@ -0,0 +1,28 @@ +//! Whether Python's syntax validation accepts every SQL string, stopping at the first it rejects. + +use sqlbuild_core::constants::PANIC_MESSAGE; +use sqlbuild_core::panics::main::catch_compiler_panic::catch_compiler_panic; + +use crate::assembly::project::_helpers::syntax::syntax_valid; +use crate::assembly::project::constants::PANIC_DEFERRAL; +use crate::assembly::project::models::SyntaxCheck; + +/// True when every check parses, false at the first Python rejects, Err to let Python decide. +pub fn check_sql_syntax(dialect: &str, checks: &[SyntaxCheck]) -> Result { + catch_compiler_panic(|| all_valid(dialect, checks)).map_err(|kind| { + if kind == PANIC_MESSAGE { + PANIC_DEFERRAL.to_owned() + } else { + kind + } + }) +} + +fn all_valid(dialect: &str, checks: &[SyntaxCheck]) -> Result { + for check in checks { + if !syntax_valid(check, dialect)? { + return Ok(false); + } + } + Ok(true) +} diff --git a/crates/sqlbuild-analysis/src/assembly/project/main/mod.rs b/crates/sqlbuild-analysis/src/assembly/project/main/mod.rs index d53093ec5a..d35ca0b269 100644 --- a/crates/sqlbuild-analysis/src/assembly/project/main/mod.rs +++ b/crates/sqlbuild-analysis/src/assembly/project/main/mod.rs @@ -1 +1,2 @@ pub mod assemble_project_resources; +pub mod check_sql_syntax; diff --git a/crates/sqlbuild-analysis/src/assembly/project/tests/helpers.rs b/crates/sqlbuild-analysis/src/assembly/project/tests/helpers.rs index 5f245315d3..e983316a09 100644 --- a/crates/sqlbuild-analysis/src/assembly/project/tests/helpers.rs +++ b/crates/sqlbuild-analysis/src/assembly/project/tests/helpers.rs @@ -6,6 +6,7 @@ use crate::assembly::project::_helpers::deps::audit_deps; use crate::assembly::project::_helpers::syntax::syntax_valid; use crate::assembly::project::_helpers::targets::{managed_source, seed_target}; use crate::assembly::project::_helpers::templates::TemplateInputs; +use crate::assembly::project::main::check_sql_syntax::check_sql_syntax; use crate::assembly::project::models::{ AuditFacts, InputRead, Namespace, Reference, SeedDefaults, SeedFacts, SourceFacts, SyntaxCheck, TargetNamespace, Variable, @@ -114,6 +115,17 @@ pub(crate) fn valid(sql: &str, placeholders: &[(&str, &str)], dialect: &str) -> .ok() } +pub(crate) fn all_valid(sqls: &[&str]) -> Option { + let checks: Vec = sqls + .iter() + .map(|sql| SyntaxCheck { + sql: (*sql).to_owned(), + placeholders: vec![("x".to_owned(), "1".to_owned())], + }) + .collect(); + check_sql_syntax("generic", &checks).ok() +} + pub(crate) fn namespace(expected: ExpectedNamespace) -> Namespace { let (database, schema, logical_database, logical_schema) = expected; Namespace { diff --git a/crates/sqlbuild-analysis/src/assembly/project/tests/test_project_assembly.rs b/crates/sqlbuild-analysis/src/assembly/project/tests/test_project_assembly.rs index e7a7b02ccc..184a8b3ff3 100644 --- a/crates/sqlbuild-analysis/src/assembly/project/tests/test_project_assembly.rs +++ b/crates/sqlbuild-analysis/src/assembly/project/tests/test_project_assembly.rs @@ -1,7 +1,10 @@ use crate::assembly::project::models::InputRead; -use crate::assembly::project::tests::helpers::{deps, namespace, seed, seed_reads, source, valid}; +use crate::assembly::project::tests::helpers::{ + all_valid, deps, namespace, seed, seed_reads, source, valid, +}; use crate::assembly::project::tests::test_types::{ - DepsTestCase, EnvironmentTestCase, SeedTestCase, SourceTestCase, SyntaxTestCase, keys, + DepsTestCase, EnvironmentTestCase, SeedTestCase, SourceTestCase, SyntaxBatchTestCase, + SyntaxTestCase, keys, }; #[test] @@ -287,3 +290,34 @@ fn given_environment_templates_when_resolving_seeds_then_matches_python_values_a assert_eq!(reads, test_case.expected_reads, "{}", test_case.description); } } + +#[test] +fn given_sql_batches_when_checking_syntax_then_stops_at_python_first_rejection_or_defers() { + let test_cases = [ + SyntaxBatchTestCase { + description: "every statement parses", + sqls: &["SELECT @@@x AS a", "SELECT 2"], + expected_valid: Some(true), + }, + SyntaxBatchTestCase { + description: "a later statement Python rejects", + sqls: &["SELECT 1", "SELECT FROM WHERE ("], + expected_valid: Some(false), + }, + SyntaxBatchTestCase { + description: "Python stops at the rejection before a statement native defers", + sqls: &["SELECT FROM WHERE (", "SELECT @@@x\u{e9}"], + expected_valid: Some(false), + }, + SyntaxBatchTestCase { + description: "a statement native defers before any rejection", + sqls: &["SELECT @@@x\u{e9}", "SELECT FROM WHERE ("], + expected_valid: None, + }, + ]; + for test_case in test_cases { + let valid = all_valid(test_case.sqls); + + assert_eq!(valid, test_case.expected_valid, "{}", test_case.description); + } +} diff --git a/crates/sqlbuild-analysis/src/assembly/project/tests/test_types.rs b/crates/sqlbuild-analysis/src/assembly/project/tests/test_types.rs index f39fb71bfd..ca67d431c2 100644 --- a/crates/sqlbuild-analysis/src/assembly/project/tests/test_types.rs +++ b/crates/sqlbuild-analysis/src/assembly/project/tests/test_types.rs @@ -73,3 +73,10 @@ pub(crate) struct EnvironmentTestCase { pub(crate) expected_schema: Option<&'static str>, pub(crate) expected_reads: Vec, } + +pub(crate) struct SyntaxBatchTestCase { + pub(crate) description: &'static str, + pub(crate) sqls: &'static [&'static str], + /// None where native defers to Python. + pub(crate) expected_valid: Option, +} diff --git a/crates/sqlbuild-python/src/bindings/_helpers/analysis_session/session.rs b/crates/sqlbuild-python/src/bindings/_helpers/analysis_session/session.rs index 87af8f9b33..284a8545d3 100644 --- a/crates/sqlbuild-python/src/bindings/_helpers/analysis_session/session.rs +++ b/crates/sqlbuild-python/src/bindings/_helpers/analysis_session/session.rs @@ -4,14 +4,16 @@ use pyo3::prelude::{Bound, PyModule, PyModuleMethods, PyRef, PyResult, Python}; use pyo3::{pyclass, pyfunction, pymethods, wrap_pyfunction}; use sqlbuild_analysis::assembly::analysis_session::main::expression_shapes::expression_shapes; use sqlbuild_analysis::assembly::analysis_session::main::finish_analysis_session::finish_analysis_session; -use sqlbuild_analysis::assembly::analysis_session::main::prove_dynamic_contract::prove_dynamic_contract; +use sqlbuild_analysis::assembly::analysis_session::main::prove_dynamic_contracts::prove_dynamic_contracts; +use sqlbuild_analysis::assembly::analysis_session::main::prove_finished_dynamic_contracts::prove_finished_dynamic_contracts; use sqlbuild_analysis::assembly::analysis_session::main::provide_deferred_analyses::provide_deferred_analyses; use sqlbuild_analysis::assembly::analysis_session::main::run_analysis_session::run_analysis_session; use sqlbuild_analysis::assembly::analysis_session::main::start_analysis_session::start_analysis_session; use sqlbuild_analysis::assembly::analysis_session::models::{ AnalysisSession, ColumnFact, Deferral, DeferredAnalysis, DynamicFamily, ExpressionShape, - ExpressionShapeRequest, LineageFacts, LineageRow, ModelOutcome, ModelReference, ModelRequest, - PivotOutcome, PivotRequest, SessionOutcome, SessionRequest, SessionStep, + ExpressionShapeRequest, FinishedSession, LineageFacts, LineageRow, ModelOutcome, + ModelReference, ModelRequest, PivotBatchRequest, PivotModel, PivotOutcome, PivotTables, + SessionOutcome, SessionRequest, SessionStep, }; use sqlbuild_analysis::assembly::analysis_session::types::{Pairs, Shapes}; use sqlbuild_analysis::semantic_validation::types::DiagnosticRow; @@ -97,15 +99,16 @@ type ProofRow = ( Option, bool, ); -/// The dialect, relation facts, families by table, SQL and families of one pivot proof. +/// One model's pivot SQL and declared families. +type PivotModelRow = (String, Vec); +/// The dialect, relation facts and families by table every proof reads, and the models. type PivotRequestRow = ( String, Shapes, Shapes, Shapes, Vec<(String, Vec)>, - String, - Vec, + Vec, ); /// `("absent" | "deferred" | "proof", proof)`. type ContractRow = (&'static str, Option); @@ -116,6 +119,7 @@ type FinishRow = (Vec, Shapes, Vec, Vec); #[pyclass(module = "sqlbuild._native")] pub(crate) struct NativeModelAnalysisSession { inner: Option, + finished: Option, failure: Option, } @@ -162,9 +166,24 @@ impl NativeModelAnalysisSession { /// Every model's outcome once the session is done, or None to analyse in Python. fn finish(&mut self) -> Option { let session: AnalysisSession = self.inner.take()?; - let outcome: Result = - catch_compiler_panic(|| finish_analysis_session(session).map(finish_row)); - self.kept(outcome) + let outcome = catch_compiler_panic(|| finish_analysis_session(session)); + let (outcome, finished) = self.kept(outcome)?; + self.finished = Some(finished); + Some(finish_row(outcome)) + } + + /// Each model's dynamic pivot proof from the finished session's tables, or None for Python. + fn prove_dynamic_contracts( + &self, + py: Python<'_>, + models: Vec, + ) -> Option> { + let session: &FinishedSession = self.finished.as_ref()?; + let models: Vec = models.into_iter().map(pivot_model).collect(); + match py.compiler_detach(|| prove_finished_dynamic_contracts(session, &models)) { + Ok(outcomes) => Some(outcomes.into_iter().map(contract_row).collect()), + Err(_) => None, + } } /// Why the session handed the analysis back to Python, when it did. @@ -188,6 +207,7 @@ fn start_model_analysis_session( .map_err(compiler_error)?; Ok(session.map(|session| NativeModelAnalysisSession { inner: Some(session), + finished: None, failure: None, })) } @@ -425,28 +445,42 @@ fn outcome_row(outcome: ModelOutcome) -> OutcomeRow { ) } -/// One model's dynamic pivot proof, or `("deferred", None)` for Python's proof. +/// Each model's dynamic pivot proof outside any session, or None for Python's proofs. #[pyfunction] -fn prove_dynamic_column_contract(py: Python<'_>, request: PivotRequestRow) -> ContractRow { - let (dialect, column_types, authoritative_types, column_nullability, by_table, sql, families) = +fn prove_dynamic_column_contracts( + py: Python<'_>, + request: PivotRequestRow, +) -> Option> { + let (dialect, column_types, authoritative_types, column_nullability, by_table, models) = request; - let request = PivotRequest { - dialect, - column_types, - authoritative_types, - column_nullability, - families_by_table: families_by_table(by_table), + let request = PivotBatchRequest { + tables: PivotTables { + dialect, + column_types, + authoritative_types, + column_nullability, + families_by_table: families_by_table(by_table), + }, + models: models.into_iter().map(pivot_model).collect(), + }; + match py.compiler_detach(|| prove_dynamic_contracts(&request)) { + Ok(outcomes) => Some(outcomes.into_iter().map(contract_row).collect()), + Err(_) => None, + } +} + +fn pivot_model(row: PivotModelRow) -> PivotModel { + let (sql, families) = row; + PivotModel { sql, families: families.into_iter().map(dynamic_family).collect(), - }; - py.compiler_detach(|| prove_dynamic_contract(&request)) - .map_or(("deferred", None), contract_row) + } } pub(crate) fn register(module: &Bound<'_, PyModule>) -> PyResult<()> { module.add_class::()?; module.add_function(wrap_pyfunction!(start_model_analysis_session, module)?)?; module.add_function(wrap_pyfunction!(infer_expression_source_shapes, module)?)?; - module.add_function(wrap_pyfunction!(prove_dynamic_column_contract, module)?)?; + module.add_function(wrap_pyfunction!(prove_dynamic_column_contracts, module)?)?; Ok(()) } diff --git a/crates/sqlbuild-python/src/bindings/_helpers/project_assembly/resources.rs b/crates/sqlbuild-python/src/bindings/_helpers/project_assembly/resources.rs index d2fbcc1350..48d24695f1 100644 --- a/crates/sqlbuild-python/src/bindings/_helpers/project_assembly/resources.rs +++ b/crates/sqlbuild-python/src/bindings/_helpers/project_assembly/resources.rs @@ -4,6 +4,7 @@ use pyo3::prelude::{Bound, PyModule, PyModuleMethods, PyResult, Python}; use pyo3::{pyfunction, wrap_pyfunction}; use sqlbuild_analysis::assembly::analysis_session::types::Pairs; use sqlbuild_analysis::assembly::project::main::assemble_project_resources::assemble_project_resources; +use sqlbuild_analysis::assembly::project::main::check_sql_syntax::check_sql_syntax; use sqlbuild_analysis::assembly::project::models::{ AuditFacts, InputRead, ModelFacts, Namespace, ProjectRequest, ProjectResources, Reference, SeedDefaults, SeedFacts, SourceFacts, SyntaxCheck, TargetNamespace, Variable, @@ -84,6 +85,23 @@ fn assemble_project_resource_facts( } } +/// Whether every `(sql, placeholders)` parses, or None with the reason Python must check it. +#[pyfunction] +fn check_native_sql_syntax( + py: Python<'_>, + request: (String, Vec<(String, Pairs)>), +) -> (Option, Option) { + let (dialect, rows) = request; + let checks: Vec = rows + .into_iter() + .map(|(sql, placeholders)| SyntaxCheck { sql, placeholders }) + .collect(); + match py.compiler_detach(|| check_sql_syntax(&dialect, &checks)) { + Ok(valid) => (Some(valid), None), + Err(reason) => (None, Some(reason)), + } +} + fn project_request(request: RequestRow) -> ProjectRequest { let ( dialect, @@ -203,5 +221,6 @@ fn namespace_row(namespace: Namespace) -> NamespaceRow { pub(crate) fn register(module: &Bound<'_, PyModule>) -> PyResult<()> { module.add_function(wrap_pyfunction!(assemble_project_resource_facts, module)?)?; + module.add_function(wrap_pyfunction!(check_native_sql_syntax, module)?)?; Ok(()) } diff --git a/src/sqlbuild/_native.pyi b/src/sqlbuild/_native.pyi index 5d194a0bf1..8bfeb38c3b 100644 --- a/src/sqlbuild/_native.pyi +++ b/src/sqlbuild/_native.pyi @@ -349,6 +349,19 @@ def normalize_type( # Native analysis: model analysis session. type _AnalysisDiagnosticRow = tuple[str, str, int | None, int | None, int | None, int | None, str] +type _PivotContractRow = tuple[ + str, + tuple[ + bool, + list[tuple[str, str | None, str]], + list[tuple[str, str | None]], + list[str], + str | None, + bool, + ] + | None, +] + class NativeModelAnalysisSession: def run( self, @@ -420,35 +433,26 @@ class NativeModelAnalysisSession: ] | None ): ... + def prove_dynamic_contracts( + self, models: list[tuple[str, list[tuple[str, str, str, str, str, str | None]]]], / + ) -> list[_PivotContractRow] | None: ... @property def failure(self) -> str | None: ... def start_model_analysis_session( catalog: object, request: tuple[object, ...], / ) -> NativeModelAnalysisSession | None: ... -def prove_dynamic_column_contract( +def prove_dynamic_column_contracts( request: tuple[ str, list[tuple[str, list[tuple[str, str]]]], list[tuple[str, list[tuple[str, str]]]], list[tuple[str, list[tuple[str, str]]]], list[tuple[str, list[tuple[str, str, str, str, str, str | None]]]], - str, - list[tuple[str, str, str, str, str, str | None]], + list[tuple[str, list[tuple[str, str, str, str, str, str | None]]]], ], /, -) -> tuple[ - str, - tuple[ - bool, - list[tuple[str, str | None, str]], - list[tuple[str, str | None]], - list[str], - str | None, - bool, - ] - | None, -]: ... +) -> list[_PivotContractRow] | None: ... def infer_expression_source_shapes( catalog: object, request: tuple[str, bool, list[tuple[str, str]], list[str]], / ) -> tuple[list[tuple[bool, list[tuple[str, str]] | None]], str | None]: ... @@ -622,6 +626,9 @@ def plan_compiled_sql_tests( def resolve_compiled_sql_test_chains(request: NativeSqlTestChainRequest, /) -> list[list[str]]: ... # Native analysis: compiled project assembly. +def check_native_sql_syntax( + request: tuple[str, list[tuple[str, list[tuple[str, str]]]]], / +) -> tuple[bool | None, str | None]: ... def assemble_project_resource_facts( request: tuple[ str, diff --git a/src/sqlbuild/compiler/analysis_session/_helpers/dynamic_contract.py b/src/sqlbuild/compiler/analysis_session/_helpers/dynamic_contract.py deleted file mode 100644 index 803935326f..0000000000 --- a/src/sqlbuild/compiler/analysis_session/_helpers/dynamic_contract.py +++ /dev/null @@ -1,52 +0,0 @@ -"""Prove one model's dynamic column contract natively, outside the analysis session.""" - -from __future__ import annotations - -import sqlbuild._native as _native -from sqlbuild.compiler.analysis_session._helpers.deferral_records import record_analysis_deferral -from sqlbuild.compiler.analysis_session._helpers.session_rows import ( - contract_proof, - family_rows, - shape_rows, -) -from sqlbuild.compiler.analysis_session.constants import CONTRACT_DEFERRED, DEFERRAL_DYNAMIC_PIVOT -from sqlbuild.compiler.analysis_session.types import ContractRow, ShapeRows -from sqlbuild.compiler.compile.classes.python_model_analysis import PythonModelAnalysis -from sqlbuild.compiler.compile.models import DynamicColumnContractProof -from sqlbuild.compiler.lineage.types import InferredNullability -from sqlbuild.spec.contracts.models import SchemaDynamicColumnFamily - - -def native_dynamic_contract( - *, - query_sql: str, - dialect: str | None, - families: tuple[SchemaDynamicColumnFamily, ...], - column_types_by_table: dict[str, dict[str, str]], - authoritative_column_types_by_table: dict[str, dict[str, str]], - column_nullability_by_table: dict[str, dict[str, InferredNullability]], - dynamic_families_by_table: dict[str, tuple[SchemaDynamicColumnFamily, ...]], -) -> DynamicColumnContractProof | None: - """Python's proof for declared families, or None (recorded) where Python must prove it.""" - - shapes: tuple[ShapeRows | None, ...] = ( - shape_rows(column_types_by_table), - shape_rows(authoritative_column_types_by_table), - shape_rows(column_nullability_by_table), - ) - row: ContractRow = (CONTRACT_DEFERRED, None) - if all(rows is not None for rows in shapes): - row = _native.prove_dynamic_column_contract( - ( - dialect or "generic", - shapes[0] or [], - shapes[1] or [], - shapes[2] or [], - family_rows(dynamic_families_by_table), - query_sql, - [PythonModelAnalysis.family_row(family) for family in families], - ) - ) - if row[0] == CONTRACT_DEFERRED: - record_analysis_deferral(kind=DEFERRAL_DYNAMIC_PIVOT) - return contract_proof(row[1]) diff --git a/src/sqlbuild/compiler/analysis_session/_helpers/dynamic_contracts.py b/src/sqlbuild/compiler/analysis_session/_helpers/dynamic_contracts.py new file mode 100644 index 0000000000..560870c25c --- /dev/null +++ b/src/sqlbuild/compiler/analysis_session/_helpers/dynamic_contracts.py @@ -0,0 +1,73 @@ +"""Prove dynamic column contracts natively for models outside the analysis session.""" + +from __future__ import annotations + +from typing import Any + +import sqlbuild._native as _native +from sqlbuild.compiler.analysis_session._helpers.deferral_records import record_analysis_deferral +from sqlbuild.compiler.analysis_session._helpers.session_rows import ( + contract_proof, + family_rows, + shape_rows, +) +from sqlbuild.compiler.analysis_session.constants import CONTRACT_DEFERRED, DEFERRAL_DYNAMIC_PIVOT +from sqlbuild.compiler.analysis_session.models import NativePivotTables +from sqlbuild.compiler.analysis_session.types import ContractRow, ShapeRows +from sqlbuild.compiler.compile.classes.python_model_analysis import PythonModelAnalysis +from sqlbuild.compiler.compile.models import DynamicColumnContractProof +from sqlbuild.spec.contracts.models import SchemaDynamicColumnFamily + +type _ModelRow = tuple[str, list[tuple[str, str, str, str, str, str | None]]] + + +def native_dynamic_contracts( + *, + session: Any | None, + tables: NativePivotTables, + models: tuple[tuple[str, tuple[SchemaDynamicColumnFamily, ...]], ...], +) -> tuple[DynamicColumnContractProof | None, ...]: + """Each model's proof in order, None (recorded) where Python must prove it.""" + + if not models: + return () + rows: list[_ModelRow] = [_model_row(sql=sql, families=families) for sql, families in models] + contracts: list[ContractRow] | None = ( + session.prove_dynamic_contracts(rows) + if session is not None + else _standalone_contracts(tables=tables, rows=rows) + ) + if contracts is None: + record_analysis_deferral(kind=DEFERRAL_DYNAMIC_PIVOT, count=len(models)) + return (None,) * len(models) + record_analysis_deferral( + kind=DEFERRAL_DYNAMIC_PIVOT, + count=sum(kind == CONTRACT_DEFERRED for kind, _ in contracts), + ) + return tuple(contract_proof(proof) for _, proof in contracts) + + +def _model_row(*, sql: str, families: tuple[SchemaDynamicColumnFamily, ...]) -> _ModelRow: + return (sql, list(map(PythonModelAnalysis.family_row, families))) + + +def _standalone_contracts( + *, tables: NativePivotTables, rows: list[_ModelRow] +) -> list[ContractRow] | None: + shapes: tuple[ShapeRows | None, ...] = ( + shape_rows(tables.column_types_by_table), + shape_rows(tables.authoritative_column_types_by_table), + shape_rows(tables.column_nullability_by_table), + ) + if any(shape is None for shape in shapes): + return None + return _native.prove_dynamic_column_contracts( + ( + tables.dialect or "generic", + shapes[0] or [], + shapes[1] or [], + shapes[2] or [], + family_rows(tables.dynamic_families_by_table), + rows, + ) + ) diff --git a/src/sqlbuild/compiler/analysis_session/_helpers/model_analysis.py b/src/sqlbuild/compiler/analysis_session/_helpers/model_analysis.py index c0ec38e947..a03302a8a2 100644 --- a/src/sqlbuild/compiler/analysis_session/_helpers/model_analysis.py +++ b/src/sqlbuild/compiler/analysis_session/_helpers/model_analysis.py @@ -13,15 +13,16 @@ DEFERRAL_NO_COMPACT_ANALYSIS, DEFERRAL_SESSION, ) -from sqlbuild.compiler.analysis_session.models import NativeModelAnalysisRequest +from sqlbuild.compiler.analysis_session.models import ( + NativeModelAnalyses, + NativeModelAnalysisRequest, +) from sqlbuild.compiler.compile.classes.python_model_analysis import PythonModelAnalysis from sqlbuild.compiler.compile.models import ModelSqlAnalysis -def native_model_analyses( - *, request: NativeModelAnalysisRequest -) -> dict[str, ModelSqlAnalysis] | None: - """Each model's analysis by name, or None (recorded) where Python must analyse.""" +def native_model_analyses(*, request: NativeModelAnalysisRequest) -> NativeModelAnalyses | None: + """Each model's analysis by name and the finished session, or None (recorded) for Python.""" catalog: Any = request.inference_profile.binding_catalog if not request.allow_compact_analysis: @@ -31,7 +32,7 @@ def native_model_analyses( record_analysis_deferral(kind=DEFERRAL_NO_CATALOG) return None if not request.model_inputs: - return {} + return NativeModelAnalyses(analyses={}, session=None) python: PythonModelAnalysis = PythonModelAnalysis( model_inputs=request.model_inputs, inference_profile=request.inference_profile, @@ -46,4 +47,7 @@ def native_model_analyses( if session is None: record_analysis_deferral(kind=DEFERRAL_SESSION) return None - return NativeModelAnalysis(request=request, python=python, catalog=catalog).analyses(session) + analyses: dict[str, ModelSqlAnalysis] | None = NativeModelAnalysis( + request=request, python=python, catalog=catalog + ).analyses(session) + return None if analyses is None else NativeModelAnalyses(analyses=analyses, session=session) diff --git a/src/sqlbuild/compiler/analysis_session/main/_analyze_native_model_sql.py b/src/sqlbuild/compiler/analysis_session/main/_analyze_native_model_sql.py index f7807824f2..8f72279583 100644 --- a/src/sqlbuild/compiler/analysis_session/main/_analyze_native_model_sql.py +++ b/src/sqlbuild/compiler/analysis_session/main/_analyze_native_model_sql.py @@ -3,13 +3,13 @@ from __future__ import annotations from sqlbuild.compiler.analysis_session._helpers.model_analysis import native_model_analyses -from sqlbuild.compiler.analysis_session.models import NativeModelAnalysisRequest -from sqlbuild.compiler.compile.models import ModelSqlAnalysis +from sqlbuild.compiler.analysis_session.models import ( + NativeModelAnalyses, + NativeModelAnalysisRequest, +) -def analyze_native_model_sql( - *, request: NativeModelAnalysisRequest -) -> dict[str, ModelSqlAnalysis] | None: - """Return each model's analysis (and any pivot proof) by name, or None to defer to Python.""" +def analyze_native_model_sql(*, request: NativeModelAnalysisRequest) -> NativeModelAnalyses | None: + """Return each model's analysis (and pivot proof) and the session, or None to defer.""" return native_model_analyses(request=request) diff --git a/src/sqlbuild/compiler/analysis_session/main/_prove_native_dynamic_contract.py b/src/sqlbuild/compiler/analysis_session/main/_prove_native_dynamic_contract.py deleted file mode 100644 index 9d47358f42..0000000000 --- a/src/sqlbuild/compiler/analysis_session/main/_prove_native_dynamic_contract.py +++ /dev/null @@ -1,31 +0,0 @@ -"""Prove a dynamic column contract natively for the preview compiler engine.""" - -from __future__ import annotations - -from sqlbuild.compiler.analysis_session._helpers.dynamic_contract import native_dynamic_contract -from sqlbuild.compiler.compile.models import DynamicColumnContractProof -from sqlbuild.compiler.lineage.types import InferredNullability -from sqlbuild.spec.contracts.models import SchemaDynamicColumnFamily - - -def prove_native_dynamic_contract( - *, - query_sql: str, - dialect: str | None, - families: tuple[SchemaDynamicColumnFamily, ...], - column_types_by_table: dict[str, dict[str, str]], - authoritative_column_types_by_table: dict[str, dict[str, str]], - column_nullability_by_table: dict[str, dict[str, InferredNullability]], - dynamic_families_by_table: dict[str, tuple[SchemaDynamicColumnFamily, ...]], -) -> DynamicColumnContractProof | None: - """Return Python's proof for declared families, or None to prove it in Python.""" - - return native_dynamic_contract( - query_sql=query_sql, - dialect=dialect, - families=families, - column_types_by_table=column_types_by_table, - authoritative_column_types_by_table=authoritative_column_types_by_table, - column_nullability_by_table=column_nullability_by_table, - dynamic_families_by_table=dynamic_families_by_table, - ) diff --git a/src/sqlbuild/compiler/analysis_session/main/_prove_native_dynamic_contracts.py b/src/sqlbuild/compiler/analysis_session/main/_prove_native_dynamic_contracts.py new file mode 100644 index 0000000000..4f01fb1e9f --- /dev/null +++ b/src/sqlbuild/compiler/analysis_session/main/_prove_native_dynamic_contracts.py @@ -0,0 +1,21 @@ +"""Prove dynamic column contracts natively for the preview compiler engine.""" + +from __future__ import annotations + +from typing import Any + +from sqlbuild.compiler.analysis_session._helpers.dynamic_contracts import native_dynamic_contracts +from sqlbuild.compiler.analysis_session.models import NativePivotTables +from sqlbuild.compiler.compile.models import DynamicColumnContractProof +from sqlbuild.spec.contracts.models import SchemaDynamicColumnFamily + + +def prove_native_dynamic_contracts( + *, + session: Any | None, + tables: NativePivotTables, + models: tuple[tuple[str, tuple[SchemaDynamicColumnFamily, ...]], ...], +) -> tuple[DynamicColumnContractProof | None, ...]: + """Python's proof per `(sql, families)` model from the session or `tables`; None for Python.""" + + return native_dynamic_contracts(session=session, tables=tables, models=models) diff --git a/src/sqlbuild/compiler/analysis_session/models.py b/src/sqlbuild/compiler/analysis_session/models.py index 5323f6d344..2db27e023e 100644 --- a/src/sqlbuild/compiler/analysis_session/models.py +++ b/src/sqlbuild/compiler/analysis_session/models.py @@ -3,9 +3,14 @@ from __future__ import annotations from dataclasses import dataclass +from typing import Any from sqlbuild.adapter.contract.models import ExpressionInferenceProfile -from sqlbuild.compiler.compile.models import AnalysisCacheContext, CompileModelInput +from sqlbuild.compiler.compile.models import ( + AnalysisCacheContext, + CompileModelInput, + ModelSqlAnalysis, +) from sqlbuild.compiler.lineage.types import InferredNullability from sqlbuild.spec.contracts.models import SchemaDynamicColumnFamily @@ -25,3 +30,22 @@ class NativeModelAnalysisRequest: analysis_cache: AnalysisCacheContext | None complete_binding_schemas: dict[str, dict[str, str]] dynamic_families_by_table: dict[str, tuple[SchemaDynamicColumnFamily, ...]] + + +@dataclass(frozen=True, slots=True) +class NativeModelAnalyses: + """Each model's native analysis by name, and the finished session that produced them.""" + + analyses: dict[str, ModelSqlAnalysis] + session: Any | None + + +@dataclass(frozen=True, slots=True) +class NativePivotTables: + """The relation facts Python's dynamic pivot proofs read, for proofs outside a session.""" + + dialect: str | None + column_types_by_table: dict[str, dict[str, str]] + authoritative_column_types_by_table: dict[str, dict[str, str]] + column_nullability_by_table: dict[str, dict[str, InferredNullability]] + dynamic_families_by_table: dict[str, tuple[SchemaDynamicColumnFamily, ...]] diff --git a/src/sqlbuild/compiler/compile/_helpers/analysis/pivot_requests.py b/src/sqlbuild/compiler/compile/_helpers/analysis/pivot_requests.py new file mode 100644 index 0000000000..9ed2ea2cad --- /dev/null +++ b/src/sqlbuild/compiler/compile/_helpers/analysis/pivot_requests.py @@ -0,0 +1,50 @@ +"""The SQL and declared families Python's dynamic pivot proof reads for each model.""" + +from __future__ import annotations + +from collections.abc import Mapping + +from sqlbuild.compiler.compile._helpers.analysis.columns import substitute_placeholder_defaults +from sqlbuild.compiler.compile._helpers.analysis.syntax_checks import model_placeholders +from sqlbuild.compiler.compile._helpers.render.cursor_intrinsics import ( + cursor_intrinsics_analysis_sql, +) +from sqlbuild.compiler.compile.models import CompileModelInput, ModelSqlAnalysis +from sqlbuild.spec.contracts.models import SchemaDynamicColumnFamily + + +def model_pivot_sql(*, query_sql: str, placeholders: dict[str, str] | None) -> str: + """Return the analysis SQL with placeholder defaults, as the pivot proof parses it.""" + + if not placeholders: + return query_sql + return substitute_placeholder_defaults(query_sql=query_sql, placeholders=placeholders) + + +def model_dynamic_families(model_input: CompileModelInput) -> tuple[SchemaDynamicColumnFamily, ...]: + """Return the dynamic column families the model's schema declares.""" + + if model_input.schema_entry is None: + return () + return model_input.schema_entry.dynamic_columns + + +def standalone_pivot_models( + *, model_inputs: tuple[CompileModelInput, ...], analyses: Mapping[str, ModelSqlAnalysis] +) -> dict[int, tuple[str, tuple[SchemaDynamicColumnFamily, ...]]]: + """Return `(sql, families)` by model index for each pivot model no analysis covered.""" + + models: dict[int, tuple[str, tuple[SchemaDynamicColumnFamily, ...]]] = {} + for index, model_input in enumerate(model_inputs): + families: tuple[SchemaDynamicColumnFamily, ...] = model_dynamic_families(model_input) + if not families or model_input.model_file.file_path.stem in analyses: + continue + query_sql: str = cursor_intrinsics_analysis_sql( + sql=model_input.query_sql, + cursor_type=model_input.config.values.get("cursor_type"), + ) + models[index] = ( + model_pivot_sql(query_sql=query_sql, placeholders=model_placeholders(model_input)), + families, + ) + return models diff --git a/src/sqlbuild/compiler/compile/_helpers/assembly/project.py b/src/sqlbuild/compiler/compile/_helpers/assembly/project.py index 87bf84e5ab..b05203f0f0 100644 --- a/src/sqlbuild/compiler/compile/_helpers/assembly/project.py +++ b/src/sqlbuild/compiler/compile/_helpers/assembly/project.py @@ -23,7 +23,6 @@ record_analysis_cache_metrics, write_model_analyses, ) -from sqlbuild.compiler.compile._helpers.analysis.columns import substitute_placeholder_defaults from sqlbuild.compiler.compile._helpers.analysis.compact import ( NativeCompactAnalysis, analyze_columns_and_lineage_with_polyglot, @@ -34,6 +33,10 @@ from sqlbuild.compiler.compile._helpers.analysis.dynamic_pivot import ( analyze_dynamic_column_contract, ) +from sqlbuild.compiler.compile._helpers.analysis.pivot_requests import ( + model_dynamic_families, + model_pivot_sql, +) from sqlbuild.compiler.compile._helpers.analysis.syntax_checks import ( model_placeholders as _model_placeholders, ) @@ -86,7 +89,7 @@ analyze_model_sql_by_engine, dynamic_column_contract_by_engine, expression_source_shapes_by_engine, - project_resources_by_engine, + project_facts_by_engine, ) from sqlbuild.compiler.compile._helpers.native_stages.sql_tests import ( assemble_sql_tests_by_engine, @@ -166,7 +169,11 @@ from sqlbuild.compiler.lineage.types import ColumnLineageMode, InferredNullability from sqlbuild.compiler.planner.types import ContractPolicy from sqlbuild.compiler.profiling.main.record import record_compile_timing -from sqlbuild.compiler.project_assembly.models import NativeProjectResources +from sqlbuild.compiler.project_assembly.models import ( + NativeModelFacts, + NativeProjectFacts, + NativeProjectResources, +) from sqlbuild.compiler.references.types import SqlReferenceKind from sqlbuild.compiler.resource_names.main.function_node_type import function_node_type from sqlbuild.compiler.scopes.models import ScopeIndex @@ -325,6 +332,7 @@ def assemble_compiled_project( else None ) model_sql_analysis_by_name: dict[str, _ModelSqlAnalysis] = {} + native_session: Any | None = None if sql_analysis_enabled: with record_compile_timing("model_analysis_ms"): python_analysis: partial[dict[str, _ModelSqlAnalysis]] = partial( @@ -350,20 +358,22 @@ def assemble_compiled_project( analysis_cache=analysis_cache, complete_binding_schemas=complete_binding_schemas, ) - model_sql_analysis_by_name = analyze_model_sql_by_engine( + model_sql_analysis_by_name, native_session = analyze_model_sql_by_engine( python_analysis=python_analysis, dynamic_families_by_table=dynamic_families_by_table, ) - native: NativeProjectResources | None = project_resources_by_engine( + native: NativeProjectFacts | None = project_facts_by_engine( inputs=inputs, dialect=profile.sql_analysis_dialect, analysis_model_names=analysis_model_names, - analysis_succeeded=frozenset( - name - for name, analysis in model_sql_analysis_by_name.items() - if analysis.polyglot_analysis.analysis_succeeded - ), + analyses=model_sql_analysis_by_name, + session=native_session, + column_types_by_table=column_types_by_table, + authoritative_column_types_by_table=complete_binding_schemas, + column_nullability_by_table=column_nullability_by_table, + dynamic_families_by_table=dynamic_families_by_table, ) + resources: NativeProjectResources | None = native.resources if native else None scope_index: ScopeIndex = scope_index_with_compile_usages(inputs=inputs) report_scope_index_errors(index=scope_index) effective_target_values: dict[str, object] = resolve_early_model_templates( @@ -416,7 +426,7 @@ def assemble_compiled_project( sql_validation_enabled=( analysis_model_names is None or _model_name(model_input) in analysis_model_names ), - native_deps=native.model_deps[index] if native else None, + native_model=native.models[index] if native else None, column_nullability_by_table=column_nullability_by_table, column_types_by_table=column_types_by_table, dynamic_contract_analysis_inputs=dynamic_contract_analysis_inputs, @@ -431,7 +441,7 @@ def assemble_compiled_project( source_input=source_input, target_config=inputs.effective_target, effective_vars=inputs.effective_vars, - native_entry=native.source_entries[index] if native else None, + native_entry=resources.source_entries[index] if resources else None, ) for index, source_input in enumerate(inputs.source_inputs) ), @@ -441,7 +451,7 @@ def assemble_compiled_project( defaults=inputs.project_config.defaults, target_config=inputs.effective_target, effective_vars=inputs.effective_vars, - native_destination=native.seed_destinations[index] if native else None, + native_destination=resources.seed_destinations[index] if resources else None, ) for index, seed_input in enumerate(inputs.seed_inputs) ), @@ -449,14 +459,14 @@ def assemble_compiled_project( _assemble_compiled_function( function_input=function_input, seed_names=seed_names, - native_deps=native.function_deps[index] if native else None, + native_deps=resources.function_deps[index] if resources else None, ) for index, function_input in enumerate(inputs.sql_function_inputs) ), audits=tuple( _assemble_compiled_audit( audit_input=audit_input, - native_scope_deps=native.audit_scope_deps[index] if native else None, + native_scope_deps=resources.audit_scope_deps[index] if resources else None, ) for index, audit_input in enumerate(inputs.audit_inputs) ), @@ -488,6 +498,7 @@ def assemble_compiled_project( ), external_sql_reference_resolver=inputs.external_sql_reference_resolver, scope_index=scope_index, + native_session=native_session, ) return complete_semantic_diagnostics( project=project, @@ -519,7 +530,7 @@ def _assemble_compiled_model( model_input: CompileModelInput, sql_analysis_enabled: bool, sql_validation_enabled: bool = True, - native_deps: tuple[CompiledObjectKey, ...] | None = None, + native_model: NativeModelFacts | None = None, column_nullability_by_table: dict[str, dict[str, InferredNullability]] | None = None, column_types_by_table: dict[str, dict[str, str]] | None = None, dynamic_contract_analysis_inputs: _DynamicContractAnalysisInputs | None = None, @@ -528,6 +539,7 @@ def _assemble_compiled_model( allow_compact_analysis: bool = False, ) -> CompiledModel: model_name: str = model_input.model_file.file_path.stem + native_deps: tuple[CompiledObjectKey, ...] | None = native_model.deps if native_model else None syntax_validated: bool = native_deps is not None profile: ExpressionInferenceProfile = inference_profile or ExpressionInferenceProfile() analysis_query_sql: str = cursor_intrinsics_analysis_sql( @@ -595,22 +607,12 @@ def _assemble_compiled_model( ) dynamic_column_contract: DynamicColumnContractProof | None = dynamic_column_contract_by_engine( sql_analysis=sql_analysis, + native_proof=native_model.dynamic_contract if native_model else None, python_proof=partial( analyze_dynamic_column_contract, - query_sql=( - substitute_placeholder_defaults( - query_sql=analysis_query_sql, - placeholders=placeholders, - ) - if placeholders - else analysis_query_sql - ), + query_sql=model_pivot_sql(query_sql=analysis_query_sql, placeholders=placeholders), dialect=profile.sql_analysis_dialect, - families=( - model_input.schema_entry.dynamic_columns - if model_input.schema_entry is not None - else () - ), + families=model_dynamic_families(model_input), column_types_by_table=column_types_by_table or {}, authoritative_column_types_by_table=( dynamic_contract_analysis_inputs.authoritative_column_types_by_table diff --git a/src/sqlbuild/compiler/compile/_helpers/diagnostics/sql_analysis_opt_outs.py b/src/sqlbuild/compiler/compile/_helpers/diagnostics/sql_analysis_opt_outs.py index 020aa3568e..6db1cd78b5 100644 --- a/src/sqlbuild/compiler/compile/_helpers/diagnostics/sql_analysis_opt_outs.py +++ b/src/sqlbuild/compiler/compile/_helpers/diagnostics/sql_analysis_opt_outs.py @@ -8,6 +8,7 @@ from pathlib import Path from sqlbuild.compiler.compile._helpers.analysis.validation import ( + hook_sql_statements, validate_hook_sql_syntax, validate_sql_syntax, ) @@ -31,6 +32,11 @@ SQL_ANALYSIS_CONFIG_KEY, ) from sqlbuild.compiler.discovery.models import DiscoveredSqlModelFile +from sqlbuild.compiler.frontier.main.native_stage_enabled import native_stage_enabled +from sqlbuild.compiler.frontier.types import NativeStage +from sqlbuild.compiler.project_assembly.main._check_native_sql_syntax import ( + check_native_sql_syntax, +) from sqlbuild.errors.setting_help.main.join_helps import join_helps from sqlbuild.errors.setting_help.main.setting_help import setting_help from sqlbuild.errors.setting_help.main.setting_note import setting_note @@ -66,6 +72,24 @@ def rejected_sql_analysis_opt_out(request: SqlAnalysisOptOutRequest) -> SourceLo or not any(request.config.values.get(key) is False for key in _OPT_OUT_KEYS) ): return None + if not _parses(request): + return None + return _opt_out_location(request) + + +def _parses(request: SqlAnalysisOptOutRequest) -> bool: + """Whether the model query and every hook pass Python's syntax validation.""" + + if native_stage_enabled(NativeStage.PROJECT_ASSEMBLY): + statements: list[str] = [request.query_sql] + for hook_name in _HOOK_KEYS: + statements.extend(hook_sql_statements(request.config.values.get(hook_name))) + native: bool | None = check_native_sql_syntax( + checks=tuple((statement, request.placeholders) for statement in statements), + dialect=None, + ) + if native is not None: + return native model_name: str = request.model_file.file_path.stem try: validate_sql_syntax( @@ -83,8 +107,8 @@ def rejected_sql_analysis_opt_out(request: SqlAnalysisOptOutRequest) -> SourceLo placeholders=request.placeholders, ) except CompileInputError: - return None - return _opt_out_location(request) + return False + return True def reject_unneeded_sql_analysis_opt_outs(project: CompiledProject) -> CompiledProject: diff --git a/src/sqlbuild/compiler/compile/_helpers/native_stages/assembly.py b/src/sqlbuild/compiler/compile/_helpers/native_stages/assembly.py index 2c1f1bc21d..9130989292 100644 --- a/src/sqlbuild/compiler/compile/_helpers/native_stages/assembly.py +++ b/src/sqlbuild/compiler/compile/_helpers/native_stages/assembly.py @@ -3,6 +3,7 @@ from __future__ import annotations from functools import partial +from typing import Any from sqlbuild.adapter.contract.models import ExpressionInferenceProfile from sqlbuild.compiler.analysis_session.main._analyze_native_model_sql import ( @@ -11,10 +12,15 @@ from sqlbuild.compiler.analysis_session.main._infer_native_expression_source_shapes import ( infer_native_expression_source_shapes, ) -from sqlbuild.compiler.analysis_session.main._prove_native_dynamic_contract import ( - prove_native_dynamic_contract, +from sqlbuild.compiler.analysis_session.main._prove_native_dynamic_contracts import ( + prove_native_dynamic_contracts, ) -from sqlbuild.compiler.analysis_session.models import NativeModelAnalysisRequest +from sqlbuild.compiler.analysis_session.models import ( + NativeModelAnalyses, + NativeModelAnalysisRequest, + NativePivotTables, +) +from sqlbuild.compiler.compile._helpers.analysis.pivot_requests import standalone_pivot_models from sqlbuild.compiler.compile._helpers.analysis.syntax_checks import model_syntax_checks from sqlbuild.compiler.compile._helpers.assembly.semantic_shapes import ( get_expression_source_shapes, @@ -26,32 +32,78 @@ ) from sqlbuild.compiler.frontier.main.native_stage_enabled import native_stage_enabled from sqlbuild.compiler.frontier.types import NativeStage +from sqlbuild.compiler.lineage.types import InferredNullability from sqlbuild.compiler.project_assembly.main._assemble_native_project_resources import ( assemble_native_project_resources, ) -from sqlbuild.compiler.project_assembly.models import NativeProjectResources +from sqlbuild.compiler.project_assembly.models import ( + NativeModelFacts, + NativeProjectFacts, + NativeProjectResources, +) from sqlbuild.spec.contracts.models import SchemaDynamicColumnFamily -def project_resources_by_engine( +def project_facts_by_engine( *, inputs: CompileProjectInputs, dialect: str | None, analysis_model_names: frozenset[str] | None, - analysis_succeeded: frozenset[str], -) -> NativeProjectResources | None: - """Return the natively assembled resource facts, or None where Python must assemble them.""" + analyses: dict[str, ModelSqlAnalysis], + session: Any | None, + column_types_by_table: dict[str, dict[str, str]], + authoritative_column_types_by_table: dict[str, dict[str, str]], + column_nullability_by_table: dict[str, dict[str, InferredNullability]], + dynamic_families_by_table: dict[str, tuple[SchemaDynamicColumnFamily, ...]], +) -> NativeProjectFacts | None: + """Return the native project facts, or None where Python must derive all of them.""" if not native_stage_enabled(NativeStage.PROJECT_ASSEMBLY): return None - return assemble_native_project_resources( + resources: NativeProjectResources | None = assemble_native_project_resources( inputs=inputs, dialect=dialect, syntax_checks=model_syntax_checks( model_inputs=inputs.model_inputs, analysis_model_names=analysis_model_names, - analysis_succeeded=analysis_succeeded, + analysis_succeeded=frozenset( + name + for name, analysis in analyses.items() + if analysis.polyglot_analysis.analysis_succeeded + ), + ), + ) + pivots: dict[int, tuple[str, tuple[SchemaDynamicColumnFamily, ...]]] = ( + standalone_pivot_models(model_inputs=inputs.model_inputs, analyses=analyses) + if native_stage_enabled(NativeStage.MODEL_ANALYSIS) + else {} + ) + proofs: dict[int, DynamicColumnContractProof | None] = dict( + zip( + pivots, + prove_native_dynamic_contracts( + session=session, + tables=NativePivotTables( + dialect=dialect, + column_types_by_table=column_types_by_table, + authoritative_column_types_by_table=authoritative_column_types_by_table, + column_nullability_by_table=column_nullability_by_table, + dynamic_families_by_table=dynamic_families_by_table, + ), + models=tuple(pivots.values()), + ), + strict=True, + ) + ) + return NativeProjectFacts( + models=tuple( + NativeModelFacts( + deps=resources.model_deps[index] if resources is not None else None, + dynamic_contract=proofs.get(index), + ) + for index in range(len(inputs.model_inputs)) ), + resources=resources, ) @@ -73,37 +125,30 @@ def analyze_model_sql_by_engine( *, python_analysis: partial[dict[str, ModelSqlAnalysis]], dynamic_families_by_table: dict[str, tuple[SchemaDynamicColumnFamily, ...]], -) -> dict[str, ModelSqlAnalysis]: - """Analyze models natively with the Python analysis's arguments, or run the Python analysis.""" +) -> tuple[dict[str, ModelSqlAnalysis], Any | None]: + """Analyze models natively, or with Python; also return the finished native session.""" if native_stage_enabled(NativeStage.MODEL_ANALYSIS): - native_analyses: dict[str, ModelSqlAnalysis] | None = analyze_native_model_sql( + native: NativeModelAnalyses | None = analyze_native_model_sql( request=NativeModelAnalysisRequest( **python_analysis.keywords, dynamic_families_by_table=dynamic_families_by_table ) ) - if native_analyses is not None: - return native_analyses - return python_analysis() + if native is not None: + return native.analyses, native.session + return python_analysis(), None def dynamic_column_contract_by_engine( *, sql_analysis: ModelSqlAnalysis | None, + native_proof: DynamicColumnContractProof | None, python_proof: partial[DynamicColumnContractProof | None], ) -> DynamicColumnContractProof | None: - """Return the proof native analysis carries or proves alone, or prove it in Python.""" + """Return the proof native analysis carries or proved alone, or prove it in Python.""" if sql_analysis is not None and sql_analysis.dynamic_column_contract is not None: return sql_analysis.dynamic_column_contract - if ( - sql_analysis is None - and python_proof.keywords["families"] - and native_stage_enabled(NativeStage.MODEL_ANALYSIS) - ): - native: DynamicColumnContractProof | None = prove_native_dynamic_contract( - **python_proof.keywords - ) - if native is not None: - return native + if native_proof is not None: + return native_proof return python_proof() diff --git a/src/sqlbuild/compiler/project_assembly/main/_check_native_sql_syntax.py b/src/sqlbuild/compiler/project_assembly/main/_check_native_sql_syntax.py new file mode 100644 index 0000000000..a929ed239f --- /dev/null +++ b/src/sqlbuild/compiler/project_assembly/main/_check_native_sql_syntax.py @@ -0,0 +1,24 @@ +"""Check SQL syntax natively for the preview compiler engine.""" + +from __future__ import annotations + +import sqlbuild._native as _native +from sqlbuild.compiler.project_assembly._helpers.deferrals import record_project_assembly_deferral + + +def check_native_sql_syntax( + *, checks: tuple[tuple[str, dict[str, str] | None], ...], dialect: str | None +) -> bool | None: + """Whether Python's syntax validation accepts every `(sql, placeholders)`; None to check it.""" + + valid: bool | None + deferral: str | None + valid, deferral = _native.check_native_sql_syntax( + ( + dialect or "generic", + [(sql, list((placeholders or {}).items())) for sql, placeholders in checks], + ) + ) + if valid is None: + _ = record_project_assembly_deferral(kind=deferral or "unknown") + return valid diff --git a/src/sqlbuild/compiler/project_assembly/models.py b/src/sqlbuild/compiler/project_assembly/models.py index 8c6631c2cf..7905511275 100644 --- a/src/sqlbuild/compiler/project_assembly/models.py +++ b/src/sqlbuild/compiler/project_assembly/models.py @@ -4,7 +4,11 @@ from dataclasses import dataclass -from sqlbuild.compiler.compile.models import CompiledObjectKey, CompiledRelationLocation +from sqlbuild.compiler.compile.models import ( + CompiledObjectKey, + CompiledRelationLocation, + DynamicColumnContractProof, +) from sqlbuild.spec.contracts.models import SourceEntry @@ -17,3 +21,19 @@ class NativeProjectResources: seed_destinations: tuple[CompiledRelationLocation, ...] function_deps: tuple[tuple[CompiledObjectKey, ...], ...] audit_scope_deps: tuple[tuple[CompiledObjectKey, ...], ...] + + +@dataclass(frozen=True, slots=True) +class NativeModelFacts: + """One model's native facts; each None where Python derives it.""" + + deps: tuple[CompiledObjectKey, ...] | None + dynamic_contract: DynamicColumnContractProof | None + + +@dataclass(frozen=True, slots=True) +class NativeProjectFacts: + """Every model's native facts, and the resource facts unless Python must assemble them.""" + + models: tuple[NativeModelFacts, ...] + resources: NativeProjectResources | None diff --git a/src/sqlbuild/rule_engine/_helpers/engine/custom_rules.py b/src/sqlbuild/rule_engine/_helpers/engine/custom_rules.py index b806fb398c..3ce8285d28 100644 --- a/src/sqlbuild/rule_engine/_helpers/engine/custom_rules.py +++ b/src/sqlbuild/rule_engine/_helpers/engine/custom_rules.py @@ -277,6 +277,7 @@ def host_project(project: CompiledProject) -> CompiledProject: return replace( project, binding_catalog=None, + native_session=None, sql_expansions={}, loaded_macros={}, loader_functions=(), diff --git a/tests/e2e/src/sqlbuild/cli/commands/main/compile/test_native_analysis_seams.py b/tests/e2e/src/sqlbuild/cli/commands/main/compile/test_native_analysis_seams.py index bd27e5c2d5..1f9ef3f3c5 100644 --- a/tests/e2e/src/sqlbuild/cli/commands/main/compile/test_native_analysis_seams.py +++ b/tests/e2e/src/sqlbuild/cli/commands/main/compile/test_native_analysis_seams.py @@ -8,6 +8,7 @@ import pytest import sqlbuild._native as native_module +from sqlbuild.compiler.analysis_session.models import NativeModelAnalyses from sqlbuild.compiler.compile.models import CompiledProject from sqlbuild.compiler.contracts.models import ContractValidationResult from sqlbuild.compiler.lineage.models import ProjectColumnLineage @@ -81,7 +82,8 @@ def test_given_project_when_compiling_with_each_engine_then_native_seams_answer_ ) assert isinstance(preview_seams["build_native_column_lineage"][0], ProjectColumnLineage) assert isinstance(preview_seams["assemble_native_project_resources"][0], NativeProjectResources) - assert isinstance(preview_seams["analyze_native_model_sql"][0], dict) + assert isinstance(preview_seams["analyze_native_model_sql"][0], NativeModelAnalyses) + assert preview_seams["analyze_native_model_sql"][0].session is not None assert isinstance(preview_seams["infer_native_expression_source_shapes"][0], tuple) planned_artifacts: object = preview_seams["plan_native_sql_test_artifacts"][0] assert isinstance(planned_artifacts, tuple) @@ -91,6 +93,7 @@ def test_given_project_when_compiling_with_each_engine_then_native_seams_answer_ len(planned_artifacts) ] assert isinstance(preview_seams["complete_native_semantic_diagnostics"][0], CompiledProject) + assert preview_seams["complete_native_semantic_diagnostics"][0].native_session is not None assert isinstance(preview_seams["evaluate_native_model_contracts"][0], ContractValidationResult) assert (native_run.returncode, preview_run.returncode) == (0, 0) assert report_without_engine(native_run) == report_without_engine(python_run) diff --git a/tests/integration/src/sqlbuild/compiler/analysis_session/_test_types.py b/tests/integration/src/sqlbuild/compiler/analysis_session/_test_types.py index f3ea3a0aac..4aca1c7f8d 100644 --- a/tests/integration/src/sqlbuild/compiler/analysis_session/_test_types.py +++ b/tests/integration/src/sqlbuild/compiler/analysis_session/_test_types.py @@ -48,4 +48,13 @@ class StandalonePivotProofTestCase: description: str analysed_models: frozenset[str] expected_native_proofs: int + expected_session_answers: list[bool] expected_proven_by_model: dict[str, bool | None] + + +@dataclass(frozen=True) +class HostPayloadTestCase: + """A natively analysed project that custom-rule hosts receive without its native session.""" + + description: str + expected_models: tuple[str, ...] diff --git a/tests/integration/src/sqlbuild/compiler/analysis_session/helpers.py b/tests/integration/src/sqlbuild/compiler/analysis_session/helpers.py index 47095774c0..669514450a 100644 --- a/tests/integration/src/sqlbuild/compiler/analysis_session/helpers.py +++ b/tests/integration/src/sqlbuild/compiler/analysis_session/helpers.py @@ -25,10 +25,14 @@ from sqlbuild.compiler.analysis_session.main._infer_native_expression_source_shapes import ( infer_native_expression_source_shapes, ) -from sqlbuild.compiler.analysis_session.main._prove_native_dynamic_contract import ( - prove_native_dynamic_contract, +from sqlbuild.compiler.analysis_session.main._prove_native_dynamic_contracts import ( + prove_native_dynamic_contracts, +) +from sqlbuild.compiler.analysis_session.models import ( + NativeModelAnalyses, + NativeModelAnalysisRequest, + NativePivotTables, ) -from sqlbuild.compiler.analysis_session.models import NativeModelAnalysisRequest from sqlbuild.compiler.compile._helpers.assembly.semantic_shapes import ( get_expression_source_shapes, ) @@ -301,19 +305,27 @@ def pivot_project_files() -> dict[str, str]: } -def native_pivot_proofs( - *, monkeypatch: pytest.MonkeyPatch -) -> list[DynamicColumnContractProof | None]: - """Record every standalone native pivot proof assembly takes, failing any wheel pivot proof.""" +@dataclass +class NativePivotProofs: + """Every standalone native pivot proof assembly took, and whether a session answered it.""" + + proofs: list[DynamicColumnContractProof | None] = field(default_factory=list) + session_answers: list[bool] = field(default_factory=list) + + +def native_pivot_proofs(*, monkeypatch: pytest.MonkeyPatch) -> NativePivotProofs: + """Record every batch of native pivot proofs assembly takes, failing any wheel pivot proof.""" - proofs: list[DynamicColumnContractProof | None] = [] - native_proof: Callable[..., DynamicColumnContractProof | None] = ( - native_stage_assembly.prove_native_dynamic_contract + recorded_proofs: NativePivotProofs = NativePivotProofs() + native_proofs: Callable[..., tuple[DynamicColumnContractProof | None, ...]] = ( + native_stage_assembly.prove_native_dynamic_contracts ) - def recorded(**keywords: Any) -> DynamicColumnContractProof | None: - proofs.append(native_proof(**keywords)) - return proofs[-1] + def recorded(**keywords: Any) -> tuple[DynamicColumnContractProof | None, ...]: + proofs: tuple[DynamicColumnContractProof | None, ...] = native_proofs(**keywords) + recorded_proofs.proofs.extend(proofs) + recorded_proofs.session_answers.extend([keywords["session"] is not None] * len(proofs)) + return proofs python_proof: Callable[..., DynamicColumnContractProof | None] = ( project_assembly.analyze_dynamic_column_contract @@ -323,9 +335,9 @@ def wheel_not_expected(**keywords: Any) -> DynamicColumnContractProof | None: assert not keywords["families"], "the Python wheel proved a pivot native should prove" return python_proof(**keywords) - monkeypatch.setattr(native_stage_assembly, "prove_native_dynamic_contract", recorded) + monkeypatch.setattr(native_stage_assembly, "prove_native_dynamic_contracts", recorded) monkeypatch.setattr(project_assembly, "analyze_dynamic_column_contract", wheel_not_expected) - return proofs + return recorded_proofs def generated_analysis_files(*, rng: random.Random, model_count: int) -> dict[str, str]: @@ -374,6 +386,7 @@ class AnalysisParity: expression_shapes: int = 0 pivot_proofs: int = 0 standalone_proofs: int = 0 + session_proofs: int = 0 proven_pivots: int = 0 native_enrichments: int = 0 @@ -387,15 +400,17 @@ def compare_analyses( ) -> None: """Assemble `inputs`, analysing every seam call with both engines into `parity`.""" + sessions: list[Any] = [] + def models_by_both( *, python_analysis: partial[dict[str, ModelSqlAnalysis]], dynamic_families_by_table: dict[str, tuple[SchemaDynamicColumnFamily, ...]], - ) -> dict[str, ModelSqlAnalysis]: + ) -> tuple[dict[str, ModelSqlAnalysis], Any]: keywords: dict[str, Any] = {**python_analysis.keywords, "analysis_cache": None} profile: ExpressionInferenceProfile = keywords["inference_profile"] native_catalog: Any = profile.binding_catalog.with_relations({}) - native: dict[str, ModelSqlAnalysis] | None = analyze_native_model_sql( + native_result: NativeModelAnalyses | None = analyze_native_model_sql( request=NativeModelAnalysisRequest( **{ **keywords, @@ -404,6 +419,8 @@ def models_by_both( dynamic_families_by_table=dynamic_families_by_table, ) ) + native: dict[str, ModelSqlAnalysis] | None = getattr(native_result, "analyses", None) + sessions.append(getattr(native_result, "session", None)) python: dict[str, ModelSqlAnalysis] = python_analysis.func(**keywords) parity.analysed_models += len(python) _append(parity, "model analyses", _analysis_views(python), _analysis_views(native)) @@ -413,25 +430,32 @@ def models_by_both( _catalog_view(profile.binding_catalog), _catalog_view(native_catalog), ) - return native or python + return native or python, sessions[-1] def proofs_by_both( *, sql_analysis: ModelSqlAnalysis | None, + native_proof: DynamicColumnContractProof | None, python_proof: partial[DynamicColumnContractProof | None], ) -> DynamicColumnContractProof | None: native: DynamicColumnContractProof | None = getattr( sql_analysis, "dynamic_column_contract", None ) python: DynamicColumnContractProof | None = python_proof() - standalone: DynamicColumnContractProof | None = prove_native_dynamic_contract( - **python_proof.keywords + session: Any = (sessions or [None])[-1] + standalone: DynamicColumnContractProof | None = _batched_proof( + session=None, python_proof=python_proof + ) + in_session: DynamicColumnContractProof | None = _batched_proof( + session=session, python_proof=python_proof ) + parity.session_proofs += in_session is not None and session is not None parity.pivot_proofs += native is not None parity.proven_pivots += native is not None and native.output_proven parity.standalone_proofs += standalone is not None _append(parity, "dynamic pivot proof", python, native or python) _append(parity, "standalone dynamic pivot proof", python, standalone or python) + _append(parity, "session dynamic pivot proof", python, in_session or python) return python def shapes_by_both( @@ -485,13 +509,13 @@ def captured( *, python_analysis: partial[dict[str, ModelSqlAnalysis]], dynamic_families_by_table: dict[str, tuple[SchemaDynamicColumnFamily, ...]], - ) -> dict[str, ModelSqlAnalysis]: + ) -> tuple[dict[str, ModelSqlAnalysis], None]: requests.append( NativeModelAnalysisRequest( **python_analysis.keywords, dynamic_families_by_table=dynamic_families_by_table ) ) - return python_analysis() + return python_analysis(), None with monkeypatch.context() as patch, suppress(CompileInputError): patch.setattr(project_assembly, "analyze_model_sql_by_engine", captured) @@ -515,6 +539,23 @@ def deferral_kinds(record_dir: Path) -> Counter[str]: return Counter(f"{record['site']}:{record['kind']}" for record in records) +def _batched_proof( + *, session: Any, python_proof: partial[DynamicColumnContractProof | None] +) -> DynamicColumnContractProof | None: + keywords: dict[str, Any] = python_proof.keywords + return prove_native_dynamic_contracts( + session=session, + tables=NativePivotTables( + dialect=keywords["dialect"], + column_types_by_table=keywords["column_types_by_table"], + authoritative_column_types_by_table=keywords["authoritative_column_types_by_table"], + column_nullability_by_table=keywords["column_nullability_by_table"], + dynamic_families_by_table=keywords["dynamic_families_by_table"], + ), + models=((keywords["query_sql"], keywords["families"]),), + )[0] + + def _append(parity: AnalysisParity, name: str, python: object, native: object) -> None: parity.names.append(name) parity.python.append(python) diff --git a/tests/integration/src/sqlbuild/compiler/analysis_session/test_native_model_analysis.py b/tests/integration/src/sqlbuild/compiler/analysis_session/test_native_model_analysis.py index 4e9527ea64..1b996f612a 100644 --- a/tests/integration/src/sqlbuild/compiler/analysis_session/test_native_model_analysis.py +++ b/tests/integration/src/sqlbuild/compiler/analysis_session/test_native_model_analysis.py @@ -2,6 +2,7 @@ from __future__ import annotations +import pickle import random from collections import Counter from dataclasses import replace @@ -18,20 +19,23 @@ from sqlbuild.compiler.compile.models import ( CompiledProject, CompileProjectInputs, - DynamicColumnContractProof, ) from sqlbuild.compiler.frontier.constants import COMPILER_ENGINE_ENV_VAR from sqlbuild.compiler.frontier.types import CompilerEngine from sqlbuild.compiler.sql_analysis.constants import ANALYSIS_RECORD_DIR_ENV_VAR +from sqlbuild.rule_engine._helpers.engine.custom_rules import host_project +from sqlbuild.rule_engine._helpers.host.custom_host_pool import host_payload_project from tests.integration.src.sqlbuild.compiler.analysis_session._test_types import ( AnalysisFallbackTestCase, GeneratedAnalysisParityTestCase, + HostPayloadTestCase, SessionFailureTestCase, StandalonePivotProofTestCase, ) from tests.integration.src.sqlbuild.compiler.analysis_session.helpers import ( AnalysisParity, FailingProvideSession, + NativePivotProofs, analysis_request, compare_analyses, compile_inputs, @@ -97,6 +101,7 @@ def test_given_generated_projects_when_analysing_natively_then_matches_python( assert parity.expression_shapes >= test_case.expected_minimum_expression_shapes assert parity.pivot_proofs >= test_case.expected_minimum_pivot_proofs assert parity.standalone_proofs >= test_case.expected_minimum_pivot_proofs + assert parity.session_proofs >= test_case.expected_minimum_pivot_proofs assert parity.proven_pivots >= test_case.expected_minimum_proven_pivots assert ( kinds["analysis_session:input_enrichment"] @@ -199,12 +204,24 @@ def test_given_session_failure_after_deferrals_when_analysing_then_records_only_ description="a selection that leaves the pivots out of model analysis", analysed_models=frozenset({"orders_list"}), expected_native_proofs=2, + expected_session_answers=[True, True], expected_proven_by_model={ "orders_list": None, "status_amounts": True, "status_passthrough": True, }, - ) + ), + StandalonePivotProofTestCase( + description="no model analysis, so the proofs run without a session", + analysed_models=frozenset(), + expected_native_proofs=2, + expected_session_answers=[False, False], + expected_proven_by_model={ + "orders_list": None, + "status_amounts": True, + "status_passthrough": True, + }, + ), ], ids=lambda case: case.description, ) @@ -215,7 +232,7 @@ def test_given_unanalysed_pivot_model_when_assembling_then_native_proves_it_with project_dir=tmp_path / "project", files=pivot_project_files() ) monkeypatch.setenv(COMPILER_ENGINE_ENV_VAR, CompilerEngine.NATIVE_PREVIEW.value) - proofs: list[DynamicColumnContractProof | None] = native_pivot_proofs(monkeypatch=monkeypatch) + recorded: NativePivotProofs = native_pivot_proofs(monkeypatch=monkeypatch) project: CompiledProject = assemble_compiled_project( inputs=inputs, @@ -224,13 +241,51 @@ def test_given_unanalysed_pivot_model_when_assembling_then_native_proves_it_with ) assert ( - all(proof is not None for proof in proofs), - len(proofs), + all(proof is not None for proof in recorded.proofs), + len(recorded.proofs), + recorded.session_answers, { model.name: getattr(model.dynamic_column_contract, "output_proven", None) for model in project.models }, - ) == (True, test_case.expected_native_proofs, test_case.expected_proven_by_model) + ) == ( + True, + test_case.expected_native_proofs, + test_case.expected_session_answers, + test_case.expected_proven_by_model, + ) + + +@pytest.mark.parametrize( + "test_case", + [ + HostPayloadTestCase( + description="a pivot project analysed in one native session", + expected_models=("orders_list", "status_amounts", "status_passthrough"), + ) + ], + ids=lambda case: case.description, +) +def test_given_native_session_when_publishing_rule_host_payload_then_session_is_dropped( + test_case: HostPayloadTestCase, tmp_path: Path, monkeypatch: pytest.MonkeyPatch +) -> None: + inputs: CompileProjectInputs = compile_inputs( + project_dir=tmp_path / "project", files=pivot_project_files() + ) + monkeypatch.setenv(COMPILER_ENGINE_ENV_VAR, CompilerEngine.NATIVE_PREVIEW.value) + project: CompiledProject = assemble_compiled_project( + inputs=inputs, inference_profile=ExpressionInferenceProfile(sql_analysis_dialect="duckdb") + ) + + payload: CompiledProject = pickle.loads( + pickle.dumps(host_payload_project(host_project(project))) + ) + + assert project.native_session is not None + assert (payload.native_session, tuple(sorted(model.name for model in payload.models))) == ( + None, + test_case.expected_models, + ) if __name__ == "__main__": diff --git a/tests/integration/src/sqlbuild/compiler/project_assembly/_test_types.py b/tests/integration/src/sqlbuild/compiler/project_assembly/_test_types.py index 680d93254c..1e010ed519 100644 --- a/tests/integration/src/sqlbuild/compiler/project_assembly/_test_types.py +++ b/tests/integration/src/sqlbuild/compiler/project_assembly/_test_types.py @@ -45,3 +45,13 @@ class WindowsEnvironmentTestCase: environment: dict[str, str] seed_schema: str expected_schema: str + + +@dataclass(frozen=True) +class OptOutTestCase: + """Models opting out of required SQL analysis, and which ones parse so Python rejects them.""" + + description: str + models: dict[str, str] + expected_rejected: dict[str, bool] + expected_python_validations: int diff --git a/tests/integration/src/sqlbuild/compiler/project_assembly/helpers.py b/tests/integration/src/sqlbuild/compiler/project_assembly/helpers.py index 05d2a1188c..c8b4ded2df 100644 --- a/tests/integration/src/sqlbuild/compiler/project_assembly/helpers.py +++ b/tests/integration/src/sqlbuild/compiler/project_assembly/helpers.py @@ -15,6 +15,7 @@ from sqlbuild.adapters.duckdb.classes.duckdb_adapter import DuckDbAdapter from sqlbuild.compiler.compile._helpers.assembly import project as project_module from sqlbuild.compiler.compile._helpers.assembly.project import assemble_compiled_project +from sqlbuild.compiler.compile._helpers.diagnostics import sql_analysis_opt_outs from sqlbuild.compiler.compile.constants import COMPILE_INPUT_READS from sqlbuild.compiler.compile.main._build_compile_inputs import build_compile_inputs from sqlbuild.compiler.compile.models import ( @@ -38,6 +39,7 @@ "audit_scope_deps", ) _ASSEMBLY_RECORD_PREFIX: str = "project_assembly:" +_OPT_OUT_VALIDATORS: tuple[str, ...] = ("validate_sql_syntax", "validate_hook_sql_syntax") _ADAPTER_CONTEXT: CompileAdapterContext = CompileAdapterContext( value_renderer=DuckDbAdapter(), collection_rendering=CollectionRendering.VALUE_LIST, @@ -199,6 +201,38 @@ def assembly_view(project: CompiledProject) -> object: ) +def rejected_opt_outs( + *, + project_dir: Path, + files: dict[str, str], + engine: CompilerEngine, + monkeypatch: pytest.MonkeyPatch, +) -> tuple[dict[str, bool], int]: + """Attach `files` with `engine`; return each opt-out's rejection and Python validations.""" + + calls: Counter[str] = Counter() + monkeypatch.setenv(COMPILER_ENGINE_ENV_VAR, engine.value) + with monkeypatch.context() as patch: + _ = [ + patch.setattr( + sql_analysis_opt_outs, + name, + _counted(name=name, function=getattr(sql_analysis_opt_outs, name), calls=calls), + ) + for name in _OPT_OUT_VALIDATORS + ] + inputs: CompileProjectInputs = project_inputs(project_dir=project_dir, files=files) + return ( + { + model_input.model_file.file_path.stem: ( + model_input.rejected_sql_analysis_opt_out is not None + ) + for model_input in inputs.model_inputs + }, + calls.total(), + ) + + def assembly_deferrals(record_dir: Path) -> Counter[str]: """Count the project assembly `site:kind` deferral records below `record_dir`.""" diff --git a/tests/integration/src/sqlbuild/compiler/project_assembly/test_native_project_assembly.py b/tests/integration/src/sqlbuild/compiler/project_assembly/test_native_project_assembly.py index 5b9ef5783e..e88e498591 100644 --- a/tests/integration/src/sqlbuild/compiler/project_assembly/test_native_project_assembly.py +++ b/tests/integration/src/sqlbuild/compiler/project_assembly/test_native_project_assembly.py @@ -19,6 +19,7 @@ AssemblyDeferralTestCase, DeferredAssemblyTestCase, GeneratedAssemblyParityTestCase, + OptOutTestCase, WindowsEnvironmentTestCase, ) from tests.integration.src.sqlbuild.compiler.project_assembly.helpers import ( @@ -29,6 +30,7 @@ project_inputs, python_resource_calls, recorded_assembly, + rejected_opt_outs, seed_yml, windows_environ, ) @@ -37,6 +39,11 @@ 'name = "assembly"\nadapter = "duckdb"\n\n[connection]\ndatabase = ":memory:"\n\n' ) _ORDERS_MODEL: str = 'MODEL (description "Orders");\n\nSELECT 1 AS n\n' +_REQUIRED_ANALYSIS_TOML: str = ( + 'name = "assembly"\nadapter = "duckdb"\n\n' + '[connection]\ndatabase = ":memory:"\n\n[settings]\nrequire_sql_analysis = true\n' +) +_OPT_OUT_HEADER: str = 'MODEL (\n description "Orders",\n sql_analysis false,\n' _SEED_FILES: dict[str, str] = { "seeds/countries.csv": "n\n1\n", "models/orders.sql": _ORDERS_MODEL, @@ -261,3 +268,47 @@ def test_given_windows_environment_when_assembling_natively_then_env_lookups_mat assert python.seeds[0].destination.schema == test_case.expected_schema assert assembly_view(native) == assembly_view(python) assert assembly_deferrals(record_dir) == Counter() + + +@pytest.mark.parametrize( + "test_case", + [ + OptOutTestCase( + description="parseable, unparseable and unparseable-hook opt-outs", + models={ + "models/parses.sql": f"{_OPT_OUT_HEADER});\n\nSELECT 1 AS n\n", + "models/broken_query.sql": f"{_OPT_OUT_HEADER});\n\nSELECT FROM WHERE (\n", + "models/broken_hook.sql": ( + f'{_OPT_OUT_HEADER} post_hooks [inline_sql("SELECT FROM (")],\n);\n\n' + "SELECT 1 AS n\n" + ), + }, + expected_rejected={"broken_hook": False, "broken_query": False, "parses": True}, + expected_python_validations=7, + ) + ], + ids=lambda case: case.description, +) +def test_given_sql_analysis_opt_outs_when_attaching_natively_then_python_rejections_match( + test_case: OptOutTestCase, tmp_path: Path, monkeypatch: pytest.MonkeyPatch +) -> None: + record_dir: Path = tmp_path / "records" + monkeypatch.setenv(ANALYSIS_RECORD_DIR_ENV_VAR, str(record_dir)) + files: dict[str, str] = {"sqlbuild_project.toml": _REQUIRED_ANALYSIS_TOML, **test_case.models} + + python: tuple[dict[str, bool], int] = rejected_opt_outs( + project_dir=tmp_path / "python", + files=files, + engine=CompilerEngine.PYTHON, + monkeypatch=monkeypatch, + ) + native: tuple[dict[str, bool], int] = rejected_opt_outs( + project_dir=tmp_path / "native", + files=files, + engine=CompilerEngine.NATIVE_PREVIEW, + monkeypatch=monkeypatch, + ) + + assert python == (test_case.expected_rejected, test_case.expected_python_validations) + assert native == (test_case.expected_rejected, 0) + assert assembly_deferrals(record_dir) == Counter() diff --git a/tests/integration/src/sqlbuild/rule_engine/host_payload/test_host_payload_facts.py b/tests/integration/src/sqlbuild/rule_engine/host_payload/test_host_payload_facts.py index 8b65e73896..33c8c913f4 100644 --- a/tests/integration/src/sqlbuild/rule_engine/host_payload/test_host_payload_facts.py +++ b/tests/integration/src/sqlbuild/rule_engine/host_payload/test_host_payload_facts.py @@ -100,7 +100,7 @@ "SELECT 1\n" ), }, - expected_unpopulated_trimmed_fields=frozenset(), + expected_unpopulated_trimmed_fields=frozenset({"native_session"}), ), ), ids=lambda case: case.description, diff --git a/tests/unit/src/sqlbuild/compiler/compile/_helpers/native_stages/_test_types.py b/tests/unit/src/sqlbuild/compiler/compile/_helpers/native_stages/_test_types.py index a49bf87317..6fd230b52e 100644 --- a/tests/unit/src/sqlbuild/compiler/compile/_helpers/native_stages/_test_types.py +++ b/tests/unit/src/sqlbuild/compiler/compile/_helpers/native_stages/_test_types.py @@ -10,6 +10,7 @@ class DynamicColumnContractDispatchTestCase: description: str sql_analysis: ModelSqlAnalysis | None + native_proof: DynamicColumnContractProof | None dialect: str families: tuple[SchemaDynamicColumnFamily, ...] expected_proof: DynamicColumnContractProof | None diff --git a/tests/unit/src/sqlbuild/compiler/compile/_helpers/native_stages/test_dynamic_column_contract_by_engine.py b/tests/unit/src/sqlbuild/compiler/compile/_helpers/native_stages/test_dynamic_column_contract_by_engine.py index 92ff874493..8addc9e694 100644 --- a/tests/unit/src/sqlbuild/compiler/compile/_helpers/native_stages/test_dynamic_column_contract_by_engine.py +++ b/tests/unit/src/sqlbuild/compiler/compile/_helpers/native_stages/test_dynamic_column_contract_by_engine.py @@ -44,6 +44,7 @@ ( DynamicColumnContractDispatchTestCase( description="native analysis proof replaces the python proof", + native_proof=None, sql_analysis=ModelSqlAnalysis( polyglot_analysis=_ANALYSIS, placeholders=None, @@ -55,6 +56,7 @@ ), DynamicColumnContractDispatchTestCase( description="analysis without a proof falls back to the python proof", + native_proof=None, sql_analysis=ModelSqlAnalysis(polyglot_analysis=_ANALYSIS, placeholders=None), dialect="bigquery", families=(_FAMILY,), @@ -62,13 +64,23 @@ ), DynamicColumnContractDispatchTestCase( description="unanalyzed model falls back to the python proof", + native_proof=None, sql_analysis=None, dialect="bigquery", families=(_FAMILY,), expected_proof=_PYTHON_UNSUPPORTED_PROOF, ), + DynamicColumnContractDispatchTestCase( + description="a batched native proof answers an unanalyzed model", + native_proof=_NATIVE_PROOF, + sql_analysis=None, + dialect="bigquery", + families=(_FAMILY,), + expected_proof=_NATIVE_PROOF, + ), DynamicColumnContractDispatchTestCase( description="model without dynamic families has no proof", + native_proof=None, sql_analysis=ModelSqlAnalysis(polyglot_analysis=_ANALYSIS, placeholders=None), dialect="snowflake", families=(), @@ -82,6 +94,7 @@ def test_given_model_analysis_when_proving_dynamic_contract_then_native_proof_ta ) -> None: proof: DynamicColumnContractProof | None = dynamic_column_contract_by_engine( sql_analysis=test_case.sql_analysis, + native_proof=test_case.native_proof, python_proof=partial( analyze_dynamic_column_contract, query_sql="SELECT * FROM order_amounts", From c51b7c43d00478373c89b2efc39dc8932046e3de Mon Sep 17 00:00:00 2001 From: Kevin Longe <273747162+kvlonge@users.noreply.github.com> Date: Fri, 9 Oct 2026 14:38:30 +0100 Subject: [PATCH 06/31] fix(native): keep the finished analysis session out of CompiledProject and gate on session proofs --- .../compile/_helpers/assembly/project.py | 1 - .../_helpers/engine/custom_rules.py | 1 - .../compile/test_native_analysis_seams.py | 1 - .../compiler/analysis_session/_test_types.py | 10 +---- .../compiler/analysis_session/helpers.py | 7 +-- .../test_native_model_analysis.py | 44 ++----------------- .../host_payload/test_host_payload_facts.py | 2 +- 7 files changed, 10 insertions(+), 56 deletions(-) diff --git a/src/sqlbuild/compiler/compile/_helpers/assembly/project.py b/src/sqlbuild/compiler/compile/_helpers/assembly/project.py index b05203f0f0..2e925251bf 100644 --- a/src/sqlbuild/compiler/compile/_helpers/assembly/project.py +++ b/src/sqlbuild/compiler/compile/_helpers/assembly/project.py @@ -498,7 +498,6 @@ def assemble_compiled_project( ), external_sql_reference_resolver=inputs.external_sql_reference_resolver, scope_index=scope_index, - native_session=native_session, ) return complete_semantic_diagnostics( project=project, diff --git a/src/sqlbuild/rule_engine/_helpers/engine/custom_rules.py b/src/sqlbuild/rule_engine/_helpers/engine/custom_rules.py index 3ce8285d28..b806fb398c 100644 --- a/src/sqlbuild/rule_engine/_helpers/engine/custom_rules.py +++ b/src/sqlbuild/rule_engine/_helpers/engine/custom_rules.py @@ -277,7 +277,6 @@ def host_project(project: CompiledProject) -> CompiledProject: return replace( project, binding_catalog=None, - native_session=None, sql_expansions={}, loaded_macros={}, loader_functions=(), diff --git a/tests/e2e/src/sqlbuild/cli/commands/main/compile/test_native_analysis_seams.py b/tests/e2e/src/sqlbuild/cli/commands/main/compile/test_native_analysis_seams.py index 1f9ef3f3c5..5df91de88a 100644 --- a/tests/e2e/src/sqlbuild/cli/commands/main/compile/test_native_analysis_seams.py +++ b/tests/e2e/src/sqlbuild/cli/commands/main/compile/test_native_analysis_seams.py @@ -93,7 +93,6 @@ def test_given_project_when_compiling_with_each_engine_then_native_seams_answer_ len(planned_artifacts) ] assert isinstance(preview_seams["complete_native_semantic_diagnostics"][0], CompiledProject) - assert preview_seams["complete_native_semantic_diagnostics"][0].native_session is not None assert isinstance(preview_seams["evaluate_native_model_contracts"][0], ContractValidationResult) assert (native_run.returncode, preview_run.returncode) == (0, 0) assert report_without_engine(native_run) == report_without_engine(python_run) diff --git a/tests/integration/src/sqlbuild/compiler/analysis_session/_test_types.py b/tests/integration/src/sqlbuild/compiler/analysis_session/_test_types.py index 4aca1c7f8d..25a92f2c22 100644 --- a/tests/integration/src/sqlbuild/compiler/analysis_session/_test_types.py +++ b/tests/integration/src/sqlbuild/compiler/analysis_session/_test_types.py @@ -48,13 +48,5 @@ class StandalonePivotProofTestCase: description: str analysed_models: frozenset[str] expected_native_proofs: int - expected_session_answers: list[bool] + expected_session_proofs: int expected_proven_by_model: dict[str, bool | None] - - -@dataclass(frozen=True) -class HostPayloadTestCase: - """A natively analysed project that custom-rule hosts receive without its native session.""" - - description: str - expected_models: tuple[str, ...] diff --git a/tests/integration/src/sqlbuild/compiler/analysis_session/helpers.py b/tests/integration/src/sqlbuild/compiler/analysis_session/helpers.py index 669514450a..9da32670a2 100644 --- a/tests/integration/src/sqlbuild/compiler/analysis_session/helpers.py +++ b/tests/integration/src/sqlbuild/compiler/analysis_session/helpers.py @@ -307,10 +307,10 @@ def pivot_project_files() -> dict[str, str]: @dataclass class NativePivotProofs: - """Every standalone native pivot proof assembly took, and whether a session answered it.""" + """Every native pivot proof assembly took, and how many a finished session proved.""" proofs: list[DynamicColumnContractProof | None] = field(default_factory=list) - session_answers: list[bool] = field(default_factory=list) + session_proofs: int = 0 def native_pivot_proofs(*, monkeypatch: pytest.MonkeyPatch) -> NativePivotProofs: @@ -324,7 +324,8 @@ def native_pivot_proofs(*, monkeypatch: pytest.MonkeyPatch) -> NativePivotProofs def recorded(**keywords: Any) -> tuple[DynamicColumnContractProof | None, ...]: proofs: tuple[DynamicColumnContractProof | None, ...] = native_proofs(**keywords) recorded_proofs.proofs.extend(proofs) - recorded_proofs.session_answers.extend([keywords["session"] is not None] * len(proofs)) + proven: int = sum(proof is not None for proof in proofs) + recorded_proofs.session_proofs += proven * (keywords["session"] is not None) return proofs python_proof: Callable[..., DynamicColumnContractProof | None] = ( diff --git a/tests/integration/src/sqlbuild/compiler/analysis_session/test_native_model_analysis.py b/tests/integration/src/sqlbuild/compiler/analysis_session/test_native_model_analysis.py index 1b996f612a..d7aebb0d9b 100644 --- a/tests/integration/src/sqlbuild/compiler/analysis_session/test_native_model_analysis.py +++ b/tests/integration/src/sqlbuild/compiler/analysis_session/test_native_model_analysis.py @@ -2,7 +2,6 @@ from __future__ import annotations -import pickle import random from collections import Counter from dataclasses import replace @@ -23,12 +22,9 @@ from sqlbuild.compiler.frontier.constants import COMPILER_ENGINE_ENV_VAR from sqlbuild.compiler.frontier.types import CompilerEngine from sqlbuild.compiler.sql_analysis.constants import ANALYSIS_RECORD_DIR_ENV_VAR -from sqlbuild.rule_engine._helpers.engine.custom_rules import host_project -from sqlbuild.rule_engine._helpers.host.custom_host_pool import host_payload_project from tests.integration.src.sqlbuild.compiler.analysis_session._test_types import ( AnalysisFallbackTestCase, GeneratedAnalysisParityTestCase, - HostPayloadTestCase, SessionFailureTestCase, StandalonePivotProofTestCase, ) @@ -204,7 +200,7 @@ def test_given_session_failure_after_deferrals_when_analysing_then_records_only_ description="a selection that leaves the pivots out of model analysis", analysed_models=frozenset({"orders_list"}), expected_native_proofs=2, - expected_session_answers=[True, True], + expected_session_proofs=2, expected_proven_by_model={ "orders_list": None, "status_amounts": True, @@ -215,7 +211,7 @@ def test_given_session_failure_after_deferrals_when_analysing_then_records_only_ description="no model analysis, so the proofs run without a session", analysed_models=frozenset(), expected_native_proofs=2, - expected_session_answers=[False, False], + expected_session_proofs=0, expected_proven_by_model={ "orders_list": None, "status_amounts": True, @@ -243,7 +239,7 @@ def test_given_unanalysed_pivot_model_when_assembling_then_native_proves_it_with assert ( all(proof is not None for proof in recorded.proofs), len(recorded.proofs), - recorded.session_answers, + recorded.session_proofs, { model.name: getattr(model.dynamic_column_contract, "output_proven", None) for model in project.models @@ -251,42 +247,10 @@ def test_given_unanalysed_pivot_model_when_assembling_then_native_proves_it_with ) == ( True, test_case.expected_native_proofs, - test_case.expected_session_answers, + test_case.expected_session_proofs, test_case.expected_proven_by_model, ) -@pytest.mark.parametrize( - "test_case", - [ - HostPayloadTestCase( - description="a pivot project analysed in one native session", - expected_models=("orders_list", "status_amounts", "status_passthrough"), - ) - ], - ids=lambda case: case.description, -) -def test_given_native_session_when_publishing_rule_host_payload_then_session_is_dropped( - test_case: HostPayloadTestCase, tmp_path: Path, monkeypatch: pytest.MonkeyPatch -) -> None: - inputs: CompileProjectInputs = compile_inputs( - project_dir=tmp_path / "project", files=pivot_project_files() - ) - monkeypatch.setenv(COMPILER_ENGINE_ENV_VAR, CompilerEngine.NATIVE_PREVIEW.value) - project: CompiledProject = assemble_compiled_project( - inputs=inputs, inference_profile=ExpressionInferenceProfile(sql_analysis_dialect="duckdb") - ) - - payload: CompiledProject = pickle.loads( - pickle.dumps(host_payload_project(host_project(project))) - ) - - assert project.native_session is not None - assert (payload.native_session, tuple(sorted(model.name for model in payload.models))) == ( - None, - test_case.expected_models, - ) - - if __name__ == "__main__": pytest.main([__file__, "-vv"]) diff --git a/tests/integration/src/sqlbuild/rule_engine/host_payload/test_host_payload_facts.py b/tests/integration/src/sqlbuild/rule_engine/host_payload/test_host_payload_facts.py index 33c8c913f4..8b65e73896 100644 --- a/tests/integration/src/sqlbuild/rule_engine/host_payload/test_host_payload_facts.py +++ b/tests/integration/src/sqlbuild/rule_engine/host_payload/test_host_payload_facts.py @@ -100,7 +100,7 @@ "SELECT 1\n" ), }, - expected_unpopulated_trimmed_fields=frozenset({"native_session"}), + expected_unpopulated_trimmed_fields=frozenset(), ), ), ids=lambda case: case.description, From 8cbd849fc5331b3bde047a8c52b7265f7382b18d Mon Sep 17 00:00:00 2001 From: Kevin Longe <273747162+kvlonge@users.noreply.github.com> Date: Fri, 9 Oct 2026 16:24:21 +0100 Subject: [PATCH 07/31] perf(native): evaluate model contracts without repeated failing type deserialization Scalars skip the Expression deserialization that always failed and built a large error per call, and contract evaluation normalizes each type string once per request. Adds contracts_cpu_ms to compile timings and a same-runner python vs native-preview gate on it. --- .github/workflows/verify.yml | 28 ++++++- .../src/contracts/_helpers/columns.rs | 12 +-- .../src/contracts/_helpers/dynamic.rs | 6 +- .../src/contracts/_helpers/type_comparison.rs | 58 ++++++++++----- .../main/evaluate_model_contracts.rs | 4 +- .../tests/test_evaluate_model_contracts.rs | 61 ++++++++++++++- .../src/contracts/tests/test_types.rs | 7 ++ .../src/type_system/_helpers/parsed.rs | 5 +- .../_helpers/measure.py | 33 ++++++--- .../_helpers/report.py | 12 +++ .../_helpers/verdict.py | 33 +++++++++ .../compile_performance_ratio/constants.py | 4 + .../compile_performance_ratio/main/compare.py | 54 ++++++++++++-- .../cli/commands/_helpers/compile/pipeline.py | 3 + .../cli/commands/main/project/_compile.py | 1 + src/sqlbuild/cli/compile/models.py | 1 + .../compile_performance_ratio/_test_types.py | 9 +++ .../test_compare_compile_performance.py | 74 +++++++++++++++++++ .../main/build/test_compile_json_behavior.py | 1 + 19 files changed, 358 insertions(+), 48 deletions(-) diff --git a/.github/workflows/verify.yml b/.github/workflows/verify.yml index ccc2a8763d..28478a2601 100644 --- a/.github/workflows/verify.yml +++ b/.github/workflows/verify.yml @@ -585,7 +585,7 @@ jobs: run: make test-e2e-diagnostic-compile-performance compile_performance_ratio: - name: Same-runner compile ratio (${{ matrix.kind }} 3000 models) + name: Same-runner compile ratio (${{ matrix.label || matrix.kind }} 3000 models) needs: [changes, lockfile] if: needs.changes.outputs.performance == 'true' runs-on: ubuntu-24.04 @@ -598,6 +598,12 @@ jobs: max_ratio: "1.10" - kind: fresh max_ratio: "1.10" + - kind: dense + label: native-preview contracts vs python + base_engine: python + head_engine: native-preview + gate_phase: contracts_cpu_ms + max_ratio: "1.00" steps: - uses: actions/checkout@v4 - name: Check out the comparison base @@ -624,7 +630,27 @@ jobs: - name: Install base dependencies working-directory: base run: uv sync --locked --all-groups --all-extras + - name: Compare python and native-preview contract evaluation CPU on head + if: matrix.gate_phase + env: + KIND: ${{ matrix.kind }} + MAX_RATIO: ${{ matrix.max_ratio }} + BASE_ENGINE: ${{ matrix.base_engine }} + HEAD_ENGINE: ${{ matrix.head_engine }} + GATE_PHASE: ${{ matrix.gate_phase }} + run: | + uv run python -m scripts.compare_compile_performance \ + --kind "${KIND}" \ + --modes cold \ + --max-ratio "${MAX_RATIO}" \ + --models 3000 \ + --base-python .venv/bin/python \ + --head-python .venv/bin/python \ + --base-engine "${BASE_ENGINE}" \ + --head-engine "${HEAD_ENGINE}" \ + --gate-phase "${GATE_PHASE}" - name: Compare base and head cold, warm and one-model edit compiles + if: ${{ !matrix.gate_phase }} env: KIND: ${{ matrix.kind }} MAX_RATIO: ${{ matrix.max_ratio }} diff --git a/crates/sqlbuild-analysis/src/contracts/_helpers/columns.rs b/crates/sqlbuild-analysis/src/contracts/_helpers/columns.rs index 7e7fda68b8..20922fe52b 100644 --- a/crates/sqlbuild-analysis/src/contracts/_helpers/columns.rs +++ b/crates/sqlbuild-analysis/src/contracts/_helpers/columns.rs @@ -3,7 +3,7 @@ use std::collections::{HashMap, HashSet}; use crate::contracts::_helpers::dynamic::dynamic_column_diagnostics; -use crate::contracts::_helpers::type_comparison::{TypeComparison, types_equal}; +use crate::contracts::_helpers::type_comparison::{TypeComparer, TypeComparison}; use crate::contracts::constants::{ CONTRACT_ENFORCED, CONTRACT_NONE, DYNAMIC_OUTPUT_NOT_PROVEN_CODE, EXTRA_COLUMN_CODE, EXTRA_COLUMN_HELP, EXTRA_COLUMN_NAMED_SCHEMA_HELP, MISSING_COLUMN_CODE, @@ -45,7 +45,7 @@ pub(crate) fn declared_shape_validation_is_active(model: &ContractModel, implici pub(crate) fn model_contract_diagnostics( model: &ContractModel, validate_declared_shape: bool, - dialect: &str, + types: &TypeComparer<'_>, ) -> Result, ContractDeferral> { let contract_enforced: bool = model.contract.as_deref() == Some(CONTRACT_ENFORCED); let Some(schema) = model @@ -59,7 +59,7 @@ pub(crate) fn model_contract_diagnostics( Vec::new() }); }; - let mut diagnostics: Vec = dynamic_column_diagnostics(model, dialect)?; + let mut diagnostics: Vec = dynamic_column_diagnostics(model, types)?; let dynamic_output_unproven: bool = diagnostics .iter() .any(|diagnostic| diagnostic.code == DYNAMIC_OUTPUT_NOT_PROVEN_CODE); @@ -107,7 +107,7 @@ pub(crate) fn model_contract_diagnostics( type_enforcement: schema.type_enforcement, contract_enforced, unchecked: &unchecked, - dialect, + types, }; diagnostics.extend(column_type(&checked, &check)?); } @@ -118,7 +118,7 @@ struct TypeCheck<'a> { type_enforcement: bool, contract_enforced: bool, unchecked: &'a HashSet<&'a str>, - dialect: &'a str, + types: &'a TypeComparer<'a>, } struct TypedColumn<'a> { @@ -271,7 +271,7 @@ fn column_type( help: UNKNOWN_TYPE_HELP.to_owned(), })); }; - if types_equal(declared_type, inferred_type, check.dialect)? == TypeComparison::Equal { + if check.types.types_equal(declared_type, inferred_type)? == TypeComparison::Equal { return Ok(None); } Ok(Some(ContractDiagnostic { diff --git a/crates/sqlbuild-analysis/src/contracts/_helpers/dynamic.rs b/crates/sqlbuild-analysis/src/contracts/_helpers/dynamic.rs index b42f5df666..058b485381 100644 --- a/crates/sqlbuild-analysis/src/contracts/_helpers/dynamic.rs +++ b/crates/sqlbuild-analysis/src/contracts/_helpers/dynamic.rs @@ -2,7 +2,7 @@ use std::collections::HashMap; -use crate::contracts::_helpers::type_comparison::{TypeComparison, types_equal}; +use crate::contracts::_helpers::type_comparison::{TypeComparer, TypeComparison}; use crate::contracts::constants::{ DYNAMIC_FAMILY_TYPE_MISMATCH_HELP, DYNAMIC_FAMILY_UNKNOWN_TYPE_HELP, DYNAMIC_OUTPUT_NOT_PROVEN_CODE, DYNAMIC_OUTPUT_NOT_PROVEN_HELP, NO_COMPILER_EVIDENCE, @@ -16,7 +16,7 @@ use crate::contracts::models::{ /// The dynamic column contract diagnostics of one model, before its fixed columns. pub(crate) fn dynamic_column_diagnostics( model: &ContractModel, - dialect: &str, + types: &TypeComparer<'_>, ) -> Result, ContractDeferral> { let Some(schema) = model .schema @@ -49,7 +49,7 @@ pub(crate) fn dynamic_column_diagnostics( )); continue; }; - if types_equal(declared, inferred, dialect)? == TypeComparison::Different { + if types.types_equal(declared, inferred)? == TypeComparison::Different { diagnostics.push(family_diagnostic( TYPE_MISMATCH_CODE, ContractSeverity::Error, diff --git a/crates/sqlbuild-analysis/src/contracts/_helpers/type_comparison.rs b/crates/sqlbuild-analysis/src/contracts/_helpers/type_comparison.rs index 9089685e7e..13bb94ae69 100644 --- a/crates/sqlbuild-analysis/src/contracts/_helpers/type_comparison.rs +++ b/crates/sqlbuild-analysis/src/contracts/_helpers/type_comparison.rs @@ -1,5 +1,8 @@ //! Python's `types_equal` over the native type system, deferring where it cannot answer. +use std::cell::RefCell; +use std::collections::HashMap; + use crate::contracts::models::ContractDeferral; use crate::type_system::main::normalize_type::normalize_type; use crate::type_system::models::{NormalizedType, TypeNormalization}; @@ -11,23 +14,44 @@ pub(crate) enum TypeComparison { Different, } -/// Compare two types as Python's `types_equal`, or defer when either type has no native answer. -pub(crate) fn types_equal( - left: &str, - right: &str, - dialect: &str, -) -> Result { - let left: NormalizedType = normalized(left, dialect)?; - let right: NormalizedType = normalized(right, dialect)?; - Ok(if left == right { - TypeComparison::Equal - } else { - TypeComparison::Different - }) +/// One request's type comparisons, normalizing each type string once as Python's cache does. +pub(crate) struct TypeComparer<'a> { + dialect: &'a str, + normalized: RefCell>>, } -fn normalized(type_sql: &str, dialect: &str) -> Result { - normalize_type(type_sql, dialect) - .map(|TypeNormalization { normalized, .. }| normalized) - .ok_or(ContractDeferral::TypeNormalization) +impl<'a> TypeComparer<'a> { + pub(crate) fn new(dialect: &'a str) -> Self { + Self { + dialect, + normalized: RefCell::new(HashMap::new()), + } + } + + /// Compare two types as Python's `types_equal`, or defer when either has no native answer. + pub(crate) fn types_equal( + &self, + left: &str, + right: &str, + ) -> Result { + let left: NormalizedType = self.normalized(left)?; + let right: NormalizedType = self.normalized(right)?; + Ok(if left == right { + TypeComparison::Equal + } else { + TypeComparison::Different + }) + } + + fn normalized(&self, type_sql: &str) -> Result { + if let Some(known) = self.normalized.borrow().get(type_sql) { + return known.clone().ok_or(ContractDeferral::TypeNormalization); + } + let normalized: Option = normalize_type(type_sql, self.dialect) + .map(|TypeNormalization { normalized, .. }| normalized); + self.normalized + .borrow_mut() + .insert(type_sql.to_owned(), normalized.clone()); + normalized.ok_or(ContractDeferral::TypeNormalization) + } } diff --git a/crates/sqlbuild-analysis/src/contracts/main/evaluate_model_contracts.rs b/crates/sqlbuild-analysis/src/contracts/main/evaluate_model_contracts.rs index b70090de85..a1fa939fbf 100644 --- a/crates/sqlbuild-analysis/src/contracts/main/evaluate_model_contracts.rs +++ b/crates/sqlbuild-analysis/src/contracts/main/evaluate_model_contracts.rs @@ -3,12 +3,14 @@ use crate::contracts::_helpers::columns::{ declared_shape_validation_is_active, model_contract_diagnostics, requires_contract_evaluation, }; +use crate::contracts::_helpers::type_comparison::TypeComparer; use crate::contracts::models::{ContractOutcome, ContractRequest}; /// One outcome per requested model; models that need no evaluation have no diagnostics. #[must_use] pub fn evaluate_model_contracts(request: &ContractRequest) -> Vec { let implicit: bool = request.implicit_column_contracts; + let types = TypeComparer::new(&request.dialect); request .models .iter() @@ -19,7 +21,7 @@ pub fn evaluate_model_contracts(request: &ContractRequest) -> Vec ContractOutcome::Diagnostics(diagnostics), Err(deferral) => ContractOutcome::Deferred(deferral), diff --git a/crates/sqlbuild-analysis/src/contracts/tests/test_evaluate_model_contracts.rs b/crates/sqlbuild-analysis/src/contracts/tests/test_evaluate_model_contracts.rs index fa83f9ded2..2b3883d3e1 100644 --- a/crates/sqlbuild-analysis/src/contracts/tests/test_evaluate_model_contracts.rs +++ b/crates/sqlbuild-analysis/src/contracts/tests/test_evaluate_model_contracts.rs @@ -1,9 +1,9 @@ use crate::contracts::main::evaluate_model_contracts::evaluate_model_contracts; -use crate::contracts::models::{ContractOutcome, ContractRequest}; +use crate::contracts::models::{ContractModel, ContractOutcome, ContractRequest}; use crate::contracts::tests::helpers::{ declared, dynamic_model, inferred, model, outcome_lines, proof, schema, with_inferred, }; -use crate::contracts::tests::test_types::ContractTestCase; +use crate::contracts::tests::test_types::{ContractTestCase, SharedTypesTestCase}; #[test] fn given_model_contracts_when_evaluating_then_diagnostics_follow_python_order() { @@ -210,3 +210,60 @@ fn given_model_contracts_when_evaluating_then_diagnostics_follow_python_order() ); } } + +#[test] +fn given_models_sharing_types_when_evaluating_together_then_each_matches_its_own_request() { + let enforced = |declared_type: &str, inferred_type: &str| { + with_inferred( + model( + Some("enforced"), + Some(schema( + vec![declared("amount", Some(declared_type), false)], + false, + )), + ), + vec![inferred("amount", Some(inferred_type), false)], + ) + }; + let test_cases = [SharedTypesTestCase { + description: "repeated equal, mismatched and unanswerable types", + models: vec![ + enforced("INT", "INTEGER"), + enforced("INT", "VARCHAR"), + enforced("TÉXT", "VARCHAR"), + enforced("INT", "INTEGER"), + enforced("INT", "VARCHAR"), + enforced("TÉXT", "VARCHAR"), + ], + expected_lines: &[ + "K002 error declared:0 column 'amount' inferred as VARCHAR but declared type is INT [amount: inferred VARCHAR]", + "deferred:type_normalization", + "K002 error declared:0 column 'amount' inferred as VARCHAR but declared type is INT [amount: inferred VARCHAR]", + "deferred:type_normalization", + ], + }]; + for test_case in test_cases { + let request = |models: Vec| ContractRequest { + dialect: "duckdb".to_owned(), + implicit_column_contracts: true, + models, + }; + let together: Vec = evaluate_model_contracts(&request(test_case.models.clone())) + .iter() + .flat_map(outcome_lines) + .collect(); + let alone: Vec = test_case + .models + .iter() + .flat_map(|model| evaluate_model_contracts(&request(vec![model.clone()]))) + .flat_map(|outcome| outcome_lines(&outcome)) + .collect(); + + assert_eq!(together, alone, "{}", test_case.description); + assert_eq!( + together, test_case.expected_lines, + "{}", + test_case.description + ); + } +} diff --git a/crates/sqlbuild-analysis/src/contracts/tests/test_types.rs b/crates/sqlbuild-analysis/src/contracts/tests/test_types.rs index bfbdb73893..43a0260eb6 100644 --- a/crates/sqlbuild-analysis/src/contracts/tests/test_types.rs +++ b/crates/sqlbuild-analysis/src/contracts/tests/test_types.rs @@ -8,6 +8,13 @@ pub(crate) struct ContractTestCase { pub(crate) expected_lines: &'static [&'static str], } +pub(crate) struct SharedTypesTestCase { + pub(crate) description: &'static str, + pub(crate) models: Vec, + /// Every model's lines in order, as each model's own single-model request yields them. + pub(crate) expected_lines: &'static [&'static str], +} + pub(crate) struct PromotionTestCase { pub(crate) description: &'static str, pub(crate) request: PromotionRequest, diff --git a/crates/sqlbuild-analysis/src/type_system/_helpers/parsed.rs b/crates/sqlbuild-analysis/src/type_system/_helpers/parsed.rs index dfc944b99f..dc08a3b5d3 100644 --- a/crates/sqlbuild-analysis/src/type_system/_helpers/parsed.rs +++ b/crates/sqlbuild-analysis/src/type_system/_helpers/parsed.rs @@ -227,8 +227,11 @@ fn python_str(value: &Value) -> Option { } } -/// Whether the wheel hands Python this value as an expression object rather than data. +/// Whether the wheel hands Python an expression object; every variant holds data, so not a scalar. fn is_python_expression(value: &Value) -> bool { + if !matches!(value, Value::Object(_) | Value::Array(_)) { + return false; + } if let Ok(_expression) = expression_from_value(value.clone()) { return true; } diff --git a/scripts/compile_performance_ratio/_helpers/measure.py b/scripts/compile_performance_ratio/_helpers/measure.py index 9931aefcc4..d17e8204a8 100644 --- a/scripts/compile_performance_ratio/_helpers/measure.py +++ b/scripts/compile_performance_ratio/_helpers/measure.py @@ -20,6 +20,7 @@ BASE_LABEL, COLD_MODE, COMPILE_ENTRY, + COMPILER_ENGINE_KEY, DENSE_KIND, EDIT_MODE, ERROR_TAIL_CHARACTERS, @@ -116,34 +117,42 @@ def compare_builds( head_python: Path, runs: int, modes: tuple[str, ...], + engines: tuple[str | None, str | None] = (None, None), ) -> tuple[CompileComparison, ...]: """Alternate base and head compiles per mode; warm and edit reuse the cold project.""" - builds: tuple[tuple[str, Path, Path], ...] = ( - (BASE_LABEL, base_python, base_project_dir), - (HEAD_LABEL, head_python, head_project_dir), + base_engine, head_engine = engines + builds: tuple[tuple[str, Path, Path, str | None], ...] = ( + (BASE_LABEL, base_python, base_project_dir, base_engine), + (HEAD_LABEL, head_python, head_project_dir, head_engine), ) comparisons: list[CompileComparison] = [] cache_primed: bool = False for mode in modes: print(f"Comparing {MODE_TITLES[mode]} over {runs} alternating runs", file=sys.stderr) if mode == COLD_MODE: - for label, python, project_dir in builds: - _ = _compile_once(label=label, python=python, project_dir=project_dir, mode=mode) + for label, python, project_dir, engine in builds: + _ = _compile_once( + label=label, python=python, project_dir=project_dir, mode=mode, engine=engine + ) cache_primed = False elif not cache_primed: - for label, python, project_dir in builds: + for label, python, project_dir, engine in builds: _ = _compile_once( - label=label, python=python, project_dir=project_dir, mode=WARM_MODE + label=label, + python=python, + project_dir=project_dir, + mode=WARM_MODE, + engine=engine, ) cache_primed = True results: dict[str, list[CompileRun]] = {BASE_LABEL: [], HEAD_LABEL: []} for revision in range(runs): - for label, python, project_dir in builds: + for label, python, project_dir, engine in builds: if mode == EDIT_MODE: _ = apply_one_model_edit(project_dir=project_dir, revision=revision) run: CompileRun = _compile_once( - label=label, python=python, project_dir=project_dir, mode=mode + label=label, python=python, project_dir=project_dir, mode=mode, engine=engine ) _check_cache_use(run=run, mode=mode) results[label].append(run) @@ -191,12 +200,16 @@ def _check_cache_use(*, run: CompileRun, mode: str) -> None: ) -def _compile_once(*, label: str, python: Path, project_dir: Path, mode: str) -> CompileRun: +def _compile_once( + *, label: str, python: Path, project_dir: Path, mode: str, engine: str | None +) -> CompileRun: cache_args: tuple[str, ...] = () if mode == COLD_MODE: shutil.rmtree(project_dir / "target", ignore_errors=True) cache_args = ("--no-cache",) environment: dict[str, str] = _compile_environment() + if engine is not None: + environment[COMPILER_ENGINE_KEY] = engine before: resource.struct_rusage = resource.getrusage(resource.RUSAGE_CHILDREN) started: float = time.perf_counter() completed: subprocess.CompletedProcess[str] = subprocess.run( diff --git a/scripts/compile_performance_ratio/_helpers/report.py b/scripts/compile_performance_ratio/_helpers/report.py index 84cdd7589e..2cc764acf3 100644 --- a/scripts/compile_performance_ratio/_helpers/report.py +++ b/scripts/compile_performance_ratio/_helpers/report.py @@ -18,6 +18,8 @@ def comparison_markdown( per_side_projects: bool, failures: tuple[str, ...], mode_max_ratios: Mapping[str, float] | None = None, + engines: tuple[str | None, str | None] = (None, None), + gate_phases: tuple[str, ...] = (), ) -> str: """Summarize medians, ratios and per-phase timings of every mode as Markdown tables.""" @@ -27,6 +29,16 @@ def comparison_markdown( else "One project generated with head's generator and compiled by both builds." ) lines: list[str] = [] + base_engine, head_engine = engines + if base_engine is not None or head_engine is not None: + lines.extend( + ( + f"Engines: base `{base_engine or 'default'}`, head `{head_engine or 'default'}`.", + "", + ) + ) + if gate_phases: + lines.extend((f"Gated phases: {', '.join(gate_phases)}.", "")) for comparison in comparisons: lines.extend( ( diff --git a/scripts/compile_performance_ratio/_helpers/verdict.py b/scripts/compile_performance_ratio/_helpers/verdict.py index cca469c30b..4267778723 100644 --- a/scripts/compile_performance_ratio/_helpers/verdict.py +++ b/scripts/compile_performance_ratio/_helpers/verdict.py @@ -47,5 +47,38 @@ def ratio_failures( return tuple(failures) +def phase_failures( + *, + comparisons: tuple[CompileComparison, ...], + modes: tuple[str, ...], + phases: tuple[str, ...], + max_ratio: float, + noise_floor_ms: float, +) -> tuple[str, ...]: + """Describe every gated phase that is unmeasured or slower than ratio plus floor allow.""" + + measured: dict[str, CompileComparison] = { + comparison.mode: comparison for comparison in comparisons + } + failures: list[str] = [] + for mode in modes: + comparison: CompileComparison | None = measured.get(mode) + if comparison is None: + failures.append(f"{mode}: no measurement") + continue + for phase in phases: + prefix: str = f"{comparison.kind} {comparison.models} {mode} {phase}" + base: float | None = comparison.base_timings_ms.get(phase) + head: float | None = comparison.head_timings_ms.get(phase) + if base is None or head is None: + failures.append(f"{prefix}: not reported") + elif head > base * max_ratio + noise_floor_ms: + failures.append( + f"{prefix}: {head:.0f} ms exceeds {max_ratio:.2f}x base {base:.0f} ms " + f"plus {noise_floor_ms:.0f} ms" + ) + return tuple(failures) + + def _measured(seconds: float) -> bool: return math.isfinite(seconds) and seconds > 0 diff --git a/scripts/compile_performance_ratio/constants.py b/scripts/compile_performance_ratio/constants.py index ca1301ec6a..d51fa2cd83 100644 --- a/scripts/compile_performance_ratio/constants.py +++ b/scripts/compile_performance_ratio/constants.py @@ -34,6 +34,8 @@ "kind=sys.argv[1], project_dir=Path(sys.argv[2]), models=int(sys.argv[3]))" ) PYTHONPATH_KEY: str = "PYTHONPATH" +COMPILER_ENGINE_KEY: str = "SQLBUILD_COMPILER_ENGINE" +PHASE_NOISE_FLOOR_MS: float = 50.0 ERROR_TAIL_CHARACTERS: int = 2000 FRESH_SOURCE_SHARE: float = 713 / 3000 FRESH_SEED_SHARE: float = 141 / 3000 @@ -46,6 +48,8 @@ "graph_ms", "model_analysis_ms", "analysis_native_ms", + "contracts_ms", + "contracts_cpu_ms", "built_in_rules_ms", "custom_rules_ms", "test_planning_ms", diff --git a/scripts/compile_performance_ratio/main/compare.py b/scripts/compile_performance_ratio/main/compare.py index 330bc2704d..322780b793 100644 --- a/scripts/compile_performance_ratio/main/compare.py +++ b/scripts/compile_performance_ratio/main/compare.py @@ -14,14 +14,16 @@ write_benchmark_project, ) from scripts.compile_performance_ratio._helpers.report import append_summary, comparison_markdown -from scripts.compile_performance_ratio._helpers.verdict import ratio_failures +from scripts.compile_performance_ratio._helpers.verdict import phase_failures, ratio_failures from scripts.compile_performance_ratio.constants import ( COMPILE_MODES, DEFAULT_MAX_RATIO, DEFAULT_RUNS, EDIT_MODE, NOISE_FLOOR_SECONDS, + PHASE_NOISE_FLOOR_MS, PROJECT_KINDS, + REPORTED_PHASES, ) from scripts.compile_performance_ratio.models import CompileComparison @@ -59,16 +61,28 @@ def compare_compile_performance(argv: list[str] | None = None) -> int: head_python=args.head_python, runs=args.runs, modes=modes, + engines=(args.base_engine, args.head_engine), ) mode_max_ratios: dict[str, float] = ( {} if args.edit_max_ratio is None else {EDIT_MODE: args.edit_max_ratio} ) - failures: tuple[str, ...] = ratio_failures( - comparisons=comparisons, - modes=modes, - max_ratio=args.max_ratio, - noise_floor_seconds=args.noise_floor_seconds, - mode_max_ratios=mode_max_ratios, + gate_phases: tuple[str, ...] = tuple(dict.fromkeys(args.gate_phases)) + failures: tuple[str, ...] = ( + phase_failures( + comparisons=comparisons, + modes=modes, + phases=gate_phases, + max_ratio=args.max_ratio, + noise_floor_ms=args.phase_noise_floor_ms, + ) + if gate_phases + else ratio_failures( + comparisons=comparisons, + modes=modes, + max_ratio=args.max_ratio, + noise_floor_seconds=args.noise_floor_seconds, + mode_max_ratios=mode_max_ratios, + ) ) markdown: str = comparison_markdown( comparisons=comparisons, @@ -77,6 +91,8 @@ def compare_compile_performance(argv: list[str] | None = None) -> int: per_side_projects=per_side_projects, failures=failures, mode_max_ratios=mode_max_ratios, + engines=(args.base_engine, args.head_engine), + gate_phases=gate_phases, ) print(markdown) _ = append_summary(path=Path(summary_value) if summary_value else None, markdown=markdown) @@ -122,4 +138,28 @@ def _parse_args(argv: list[str] | None) -> argparse.Namespace: default=NOISE_FLOOR_SECONDS, help="Seconds of slack added to the ratio allowance before a slowdown fails.", ) + parser.add_argument( + "--base-engine", + default=None, + help="Compiler engine the base build runs; defaults to the build's own default.", + ) + parser.add_argument( + "--head-engine", + default=None, + help="Compiler engine the head build runs; defaults to the build's own default.", + ) + parser.add_argument( + "--gate-phase", + dest="gate_phases", + action="append", + choices=REPORTED_PHASES, + default=[], + help="Gate only this reported phase's median instead of whole-compile wall and CPU.", + ) + parser.add_argument( + "--phase-noise-floor-ms", + type=float, + default=PHASE_NOISE_FLOOR_MS, + help="Milliseconds of slack added to a gated phase's ratio allowance.", + ) return parser.parse_args(argv) diff --git a/src/sqlbuild/cli/commands/_helpers/compile/pipeline.py b/src/sqlbuild/cli/commands/_helpers/compile/pipeline.py index 1f21ac3d72..5ecad25968 100644 --- a/src/sqlbuild/cli/commands/_helpers/compile/pipeline.py +++ b/src/sqlbuild/cli/commands/_helpers/compile/pipeline.py @@ -170,6 +170,7 @@ def _analyze_compile_project( status=status, message=f"Analyzed column lineage. ({lineage_ms / 1000:.2f}s)" ) contracts_start: float = time.monotonic() + contracts_cpu_start: float = time.thread_time() contract_result: ContractValidationResult if profile_flags.skip_contracts: contract_result = ContractValidationResult(diagnostics=()) @@ -180,6 +181,7 @@ def _analyze_compile_project( dialect=adapter.sql_analysis_dialect(), ) contract_ms: int = elapsed_ms(contracts_start) + contract_cpu_ms: int = int((time.thread_time() - contracts_cpu_start) * 1000) if not profile_flags.skip_contracts: _ = complete_compile_phase( status=status, message=f"Validated model contracts. ({contract_ms / 1000:.2f}s)" @@ -255,6 +257,7 @@ def _analyze_compile_project( graph_ms=graph_ms, lineage_ms=lineage_ms, contract_ms=contract_ms, + contract_cpu_ms=contract_cpu_ms, built_in_rules_ms=rules_result.built_in_ms, custom_rules_ms=rules_result.custom_ms, early_lint_wait_ms=early_lint_wait_ms, diff --git a/src/sqlbuild/cli/commands/main/project/_compile.py b/src/sqlbuild/cli/commands/main/project/_compile.py index 7edbb6d3e1..6ea2d58d06 100644 --- a/src/sqlbuild/cli/commands/main/project/_compile.py +++ b/src/sqlbuild/cli/commands/main/project/_compile.py @@ -166,6 +166,7 @@ def _run_compile_with_status( "graph_ms": analysis.graph_ms, "lineage_ms": analysis.lineage_ms, "contracts_ms": analysis.contract_ms, + "contracts_cpu_ms": analysis.contract_cpu_ms, "built_in_rules_ms": analysis.built_in_rules_ms, "custom_rules_ms": analysis.custom_rules_ms, "early_lint_wait_ms": analysis.early_lint_wait_ms, diff --git a/src/sqlbuild/cli/compile/models.py b/src/sqlbuild/cli/compile/models.py index a590ab705e..1a166ca13a 100644 --- a/src/sqlbuild/cli/compile/models.py +++ b/src/sqlbuild/cli/compile/models.py @@ -42,6 +42,7 @@ class CompileAnalysis: graph_ms: int lineage_ms: int contract_ms: int + contract_cpu_ms: int built_in_rules_ms: int = 0 custom_rules_ms: int = 0 early_lint_wait_ms: int = 0 diff --git a/tests/e2e/scripts/compile_performance_ratio/_test_types.py b/tests/e2e/scripts/compile_performance_ratio/_test_types.py index 8f517caae3..4df5ed04c2 100644 --- a/tests/e2e/scripts/compile_performance_ratio/_test_types.py +++ b/tests/e2e/scripts/compile_performance_ratio/_test_types.py @@ -23,3 +23,12 @@ class PerSideCompilePerformanceRatioTestCase: expected_fragments: tuple[str, ...] expected_base_projects: tuple[str, ...] expected_head_projects: tuple[str, ...] + + +@dataclass(frozen=True) +class EnginePhaseRatioTestCase: + description: str + max_ratio: str + phase_noise_floor_ms: str + expected_return_code: int + expected_fragments: tuple[str, ...] diff --git a/tests/e2e/scripts/compile_performance_ratio/test_compare_compile_performance.py b/tests/e2e/scripts/compile_performance_ratio/test_compare_compile_performance.py index fda715ddc8..cfda3aaa37 100644 --- a/tests/e2e/scripts/compile_performance_ratio/test_compare_compile_performance.py +++ b/tests/e2e/scripts/compile_performance_ratio/test_compare_compile_performance.py @@ -11,6 +11,7 @@ from tests.e2e.scripts.compile_performance_ratio._test_types import ( CompilePerformanceRatioTestCase, + EnginePhaseRatioTestCase, PerSideCompilePerformanceRatioTestCase, ) from tests.e2e.scripts.compile_performance_ratio.helpers import ( @@ -167,5 +168,78 @@ def test_given_base_root_when_comparing_compile_performance_then_each_side_uses_ assert logged_projects(head_log) == test_case.expected_head_projects +@pytest.mark.parametrize( + "test_case", + ( + EnginePhaseRatioTestCase( + description="native-preview contracts within a generous limit of python's", + max_ratio="1000", + phase_noise_floor_ms="1000", + expected_return_code=0, + expected_fragments=( + "Engines: base `python`, head `native-preview`.", + "Gated phases: contracts_cpu_ms.", + "| contracts_cpu_ms |", + "**Result: passed.**", + ), + ), + EnginePhaseRatioTestCase( + description="a gated phase over its allowance fails on that phase alone", + max_ratio="0", + phase_noise_floor_ms="-1", + expected_return_code=1, + expected_fragments=( + "Compile performance regression: dense 20 cold contracts_cpu_ms:", + "**Result: failed.**", + ), + ), + ), + ids=lambda case: case.description, +) +def test_given_two_engines_when_gating_a_phase_then_enforces_only_that_phase( + test_case: EnginePhaseRatioTestCase, +) -> None: + environment: dict[str, str] = dict(os.environ) + _ = environment.pop("GITHUB_STEP_SUMMARY", None) + result: subprocess.CompletedProcess[str] = subprocess.run( + [ + sys.executable, + "-m", + "scripts.compare_compile_performance", + "--kind", + "dense", + "--models", + "20", + "--modes", + "cold", + "--runs", + "1", + "--base-python", + sys.executable, + "--head-python", + sys.executable, + "--base-engine", + "python", + "--head-engine", + "native-preview", + "--gate-phase", + "contracts_cpu_ms", + "--max-ratio", + test_case.max_ratio, + "--phase-noise-floor-ms", + test_case.phase_noise_floor_ms, + ], + cwd=_REPO_ROOT, + capture_output=True, + text=True, + env=environment, + check=False, + ) + + output: str = result.stdout + result.stderr + assert result.returncode == test_case.expected_return_code, output + assert all(fragment in output for fragment in test_case.expected_fragments), output + + if __name__ == "__main__": pytest.main([__file__, "-vv"]) diff --git a/tests/e2e/src/sqlbuild/cli/commands/main/build/test_compile_json_behavior.py b/tests/e2e/src/sqlbuild/cli/commands/main/build/test_compile_json_behavior.py index 1bf00f83ce..dff9027895 100644 --- a/tests/e2e/src/sqlbuild/cli/commands/main/build/test_compile_json_behavior.py +++ b/tests/e2e/src/sqlbuild/cli/commands/main/build/test_compile_json_behavior.py @@ -66,6 +66,7 @@ def test_given_waffle_shop_when_running_compile_json_then_it_reports_offline_que "graph_ms", "lineage_ms", "contracts_ms", + "contracts_cpu_ms", "write_ms", "attachment_ms", "built_in_rules_ms", From 6c83a034a5bdcd71975aee537a494d9143742f3d Mon Sep 17 00:00:00 2001 From: Kevin Longe <273747162+kvlonge@users.noreply.github.com> Date: Fri, 9 Oct 2026 16:42:21 +0100 Subject: [PATCH 08/31] test(perf): report a phase missing from any run instead of counting it as zero --- .../_helpers/measure.py | 13 +++-- .../_helpers/_test_types.py | 9 ++- .../_helpers/test_measure.py | 58 +++++++++++++++++++ 3 files changed, 74 insertions(+), 6 deletions(-) create mode 100644 tests/unit/scripts/compile_performance_ratio/_helpers/test_measure.py diff --git a/scripts/compile_performance_ratio/_helpers/measure.py b/scripts/compile_performance_ratio/_helpers/measure.py index d17e8204a8..0f3882c47a 100644 --- a/scripts/compile_performance_ratio/_helpers/measure.py +++ b/scripts/compile_performance_ratio/_helpers/measure.py @@ -179,8 +179,8 @@ def _comparison( head_wall_seconds=statistics.median(run.wall_seconds for run in head), base_cpu_seconds=statistics.median(run.cpu_seconds for run in base), head_cpu_seconds=statistics.median(run.cpu_seconds for run in head), - base_timings_ms=_median_phases(runs=base), - head_timings_ms=_median_phases(runs=head), + base_timings_ms=median_phases(runs=base), + head_timings_ms=median_phases(runs=head), ) @@ -260,9 +260,12 @@ def _compile_environment() -> dict[str, str]: } -def _median_phases(*, runs: list[CompileRun]) -> dict[str, float]: +def median_phases(*, runs: list[CompileRun]) -> dict[str, float]: + """Return each phase's median over runs, omitting phases any run did not report.""" + medians: dict[str, float] = {} for phase in REPORTED_PHASES: - values: list[int] = [run.timings_ms.get(phase, 0) for run in runs] - medians[phase] = statistics.median(values) + values: list[int] = [run.timings_ms[phase] for run in runs if phase in run.timings_ms] + if values and len(values) == len(runs): + medians[phase] = statistics.median(values) return medians diff --git a/tests/unit/scripts/compile_performance_ratio/_helpers/_test_types.py b/tests/unit/scripts/compile_performance_ratio/_helpers/_test_types.py index 459d0a388b..5f2afdd6ce 100644 --- a/tests/unit/scripts/compile_performance_ratio/_helpers/_test_types.py +++ b/tests/unit/scripts/compile_performance_ratio/_helpers/_test_types.py @@ -2,7 +2,7 @@ from dataclasses import dataclass, field -from scripts.compile_performance_ratio.models import CompileComparison +from scripts.compile_performance_ratio.models import CompileComparison, CompileRun @dataclass(frozen=True) @@ -37,3 +37,10 @@ class OneModelEditErrorTestCase: description: str model_files: dict[str, str] expected_message: str + + +@dataclass(frozen=True) +class MedianPhasesTestCase: + description: str + runs: tuple[CompileRun, ...] + expected_phases: dict[str, float] diff --git a/tests/unit/scripts/compile_performance_ratio/_helpers/test_measure.py b/tests/unit/scripts/compile_performance_ratio/_helpers/test_measure.py new file mode 100644 index 0000000000..cc7cff4c35 --- /dev/null +++ b/tests/unit/scripts/compile_performance_ratio/_helpers/test_measure.py @@ -0,0 +1,58 @@ +"""Tests for per-phase medians in the same-runner compile performance ratio guard.""" + +import pytest + +from scripts.compile_performance_ratio._helpers.measure import median_phases +from scripts.compile_performance_ratio.models import CompileRun +from tests.unit.scripts.compile_performance_ratio._helpers._test_types import ( + MedianPhasesTestCase, +) + + +@pytest.mark.parametrize( + "test_case", + ( + MedianPhasesTestCase( + description="a phase every run reports gets its median", + runs=( + CompileRun( + label="head", + wall_seconds=1.0, + cpu_seconds=1.0, + timings_ms={"contracts_cpu_ms": 90}, + ), + CompileRun( + label="head", + wall_seconds=1.0, + cpu_seconds=1.0, + timings_ms={"contracts_cpu_ms": 120}, + ), + CompileRun( + label="head", + wall_seconds=1.0, + cpu_seconds=1.0, + timings_ms={"contracts_cpu_ms": 100}, + ), + ), + expected_phases={"contracts_cpu_ms": 100}, + ), + MedianPhasesTestCase( + description="a phase one run omits is left unreported instead of counted as zero", + runs=( + CompileRun( + label="head", + wall_seconds=1.0, + cpu_seconds=1.0, + timings_ms={"contracts_cpu_ms": 90}, + ), + CompileRun(label="head", wall_seconds=1.0, cpu_seconds=1.0, timings_ms={}), + ), + expected_phases={}, + ), + ), + ids=lambda case: case.description, +) +def test_given_compile_runs_when_taking_phase_medians_then_reports_only_complete_phases( + test_case: MedianPhasesTestCase, +) -> None: + assert median_phases(runs=list(test_case.runs)) == test_case.expected_phases From 6838836b1470638d0f6c5f64ba9d0f22bb6263dc Mon Sep 17 00:00:00 2001 From: Kevin Longe <273747162+kvlonge@users.noreply.github.com> Date: Fri, 9 Oct 2026 08:54:50 +0100 Subject: [PATCH 09/31] feat(native): assemble SQL test facts natively behind the preview engine --- .../compiler/_helpers/sql_tests/assembly.rs | 171 +++++ .../_helpers/sql_tests/case_identity.rs | 168 +++++ .../_helpers/sql_tests/macro_call_names.rs | 105 ++++ .../compiler/_helpers/sql_tests/mock_reads.rs | 588 ++++++++++++++++++ .../src/compiler/_helpers/sql_tests/mod.rs | 4 + .../src/compiler/main/mod.rs | 1 + .../src/compiler/main/sql_test_assembly.rs | 9 + .../sqlbuild-analysis/src/compiler/models.rs | 151 +++++ .../_helpers/sql_test_glue/assembly.rs | 253 ++++++++ .../bindings/_helpers/sql_test_glue/mod.rs | 1 + .../_helpers/sql_test_glue/registration.rs | 7 +- src/sqlbuild/_native.pyi | 6 +- .../_helpers/native_stages/sql_tests.py | 19 +- .../sql_test_glue/_helpers/__init__.py | 0 .../sql_test_glue/_helpers/assembly.py | 133 ++++ .../sql_test_glue/_helpers/deferrals.py | 24 + .../compiler/sql_test_glue/constants.py | 1 + .../main/_assemble_native_sql_tests.py | 30 +- src/sqlbuild/compiler/sql_test_glue/models.py | 27 +- src/sqlbuild/compiler/sql_test_glue/types.py | 12 +- .../compile/test_native_analysis_seams.py | 7 +- .../compiler/sql_test_glue/_test_types.py | 15 + .../compiler/sql_test_glue/helpers.py | 166 ++++- .../test_native_sql_test_planning_glue.py | 68 +- 24 files changed, 1945 insertions(+), 21 deletions(-) create mode 100644 crates/sqlbuild-analysis/src/compiler/_helpers/sql_tests/assembly.rs create mode 100644 crates/sqlbuild-analysis/src/compiler/_helpers/sql_tests/case_identity.rs create mode 100644 crates/sqlbuild-analysis/src/compiler/_helpers/sql_tests/macro_call_names.rs create mode 100644 crates/sqlbuild-analysis/src/compiler/_helpers/sql_tests/mock_reads.rs create mode 100644 crates/sqlbuild-analysis/src/compiler/main/sql_test_assembly.rs create mode 100644 crates/sqlbuild-python/src/bindings/_helpers/sql_test_glue/assembly.rs create mode 100644 src/sqlbuild/compiler/sql_test_glue/_helpers/__init__.py create mode 100644 src/sqlbuild/compiler/sql_test_glue/_helpers/assembly.py create mode 100644 src/sqlbuild/compiler/sql_test_glue/_helpers/deferrals.py diff --git a/crates/sqlbuild-analysis/src/compiler/_helpers/sql_tests/assembly.rs b/crates/sqlbuild-analysis/src/compiler/_helpers/sql_tests/assembly.rs new file mode 100644 index 0000000000..0e8866fc2e --- /dev/null +++ b/crates/sqlbuild-analysis/src/compiler/_helpers/sql_tests/assembly.rs @@ -0,0 +1,171 @@ +//! Python's `_assemble_compiled_sql_test` facts: name, targets, scope, case identity and P013s. + +use rayon::prelude::{IntoParallelRefIterator, ParallelIterator}; +use sqlbuild_sqltext::sql_scan::models::LexicalSyntax; + +use crate::compiler::_helpers::sql_tests::case_identity::case_fingerprint; +use crate::compiler::_helpers::sql_tests::macro_call_names::macro_call_names; +use crate::compiler::_helpers::sql_tests::mock_reads::mock_reading_helper_diagnostics; +use crate::compiler::models::{ + AssembledSqlTestFacts, SqlTestAssemblyBatch, SqlTestAssemblyDeferral, SqlTestAssemblyModel, + SqlTestAssemblyOutcome, SqlTestAssemblyPayload, SqlTestAssemblyTest, +}; + +const MODEL_RESOURCE: &str = "model"; +const TABLE_FUNCTION_RESOURCE: &str = "table_fn"; +const MACRO_MODE: &str = "macro"; +const UDF_MODE: &str = "udf"; +const UDF_REFERENCE_KIND: &str = "udf"; + +/// Each test's facts in request order, or the reason Python assembles it. +pub(crate) fn assemble_batch(batch: &SqlTestAssemblyBatch) -> Vec { + let tests_macros = batch.tests.iter().any(|test| { + matches!(&test.payload, SqlTestAssemblyPayload::Direct { mode, .. } if mode == MACRO_MODE) + }); + let macro_names: Vec = batch + .models + .iter() + .map(|model| match &model.unscanned_macro_source { + Some(sql) if tests_macros => macro_call_names(sql), + Some(_) => Ok(Vec::new()), + None => Ok(model.macro_deps.clone()), + }) + .collect(); + let project = Project { + models: &batch.models, + macro_names: ¯o_names, + syntax: &batch.lexical_syntax, + }; + batch + .tests + .par_iter() + .map(|test| match assemble(test, &project) { + Ok(facts) => SqlTestAssemblyOutcome::Assembled(facts), + Err(deferral) => SqlTestAssemblyOutcome::Deferred(deferral), + }) + .collect() +} + +/// A model's macro dependencies, or Python's scan of its pre-macro SQL when none were recorded. +type MacroNames = Result, SqlTestAssemblyDeferral>; + +struct Project<'a> { + models: &'a [SqlTestAssemblyModel], + macro_names: &'a [MacroNames], + syntax: &'a LexicalSyntax, +} + +fn assemble( + test: &SqlTestAssemblyTest, + project: &Project<'_>, +) -> Result { + let models = project.models; + let syntax = project.syntax; + let mut target_model_names: Vec = Vec::new(); + let mut tested_resources: Vec<(String, String)> = Vec::new(); + let mut diagnostics = Vec::new(); + let scope_deps: Vec<(&'static str, String)> = match &test.payload { + SqlTestAssemblyPayload::Direct { + mode, + tested_resource_names, + } => { + tested_resources = tested_resource_names + .iter() + .map(|name| (mode.clone(), name.clone())) + .collect(); + direct_scope_deps(mode, tested_resource_names, project)? + } + SqlTestAssemblyPayload::Model(payload) => { + if payload.has_macro_mocks { + return Err(SqlTestAssemblyDeferral::MacroMocks); + } + for name in payload + .expected_model_names + .iter() + .chain(&payload.assertion_target_model_names) + .chain(&payload.reference_target_model_names) + { + if !target_model_names.contains(name) { + target_model_names.push(name.clone()); + } + } + diagnostics = mock_reading_helper_diagnostics( + test, + payload, + models, + &target_model_names, + syntax, + )?; + target_model_names + .iter() + .map(|name| (MODEL_RESOURCE, name.clone())) + .collect() + } + }; + let case_fingerprint = match &test.case_name { + Some(case_name) => Some(case_fingerprint( + test, + case_name, + &scope_deps, + &tested_resources, + )?), + None => None, + }; + Ok(AssembledSqlTestFacts { + name: test_name(test), + scope_deps, + target_model_names, + case_fingerprint, + diagnostic_resource_name: non_empty(&test.block_name) + .unwrap_or(&test.relative_stem) + .to_owned(), + diagnostics, + }) +} + +fn non_empty(name: &Option) -> Option<&str> { + name.as_deref().filter(|name| !name.is_empty()) +} + +/// Python's `_resolve_test_name`. +fn test_name(test: &SqlTestAssemblyTest) -> String { + let parent = non_empty(&test.block_name).unwrap_or(&test.file_stem); + match &test.case_name { + Some(case_name) => format!("{parent} [{case_name}]"), + None => parent.to_owned(), + } +} + +/// Python's macro, UDF and table-function scope dependencies of a direct-logic test. +fn direct_scope_deps( + mode: &str, + tested_names: &[String], + project: &Project<'_>, +) -> Result, SqlTestAssemblyDeferral> { + let mut scope_deps: Vec<(&'static str, String)> = Vec::new(); + match mode { + MACRO_MODE => { + for (model, names) in project.models.iter().zip(project.macro_names) { + let names = names.as_ref().map_err(|deferral| *deferral)?; + if names.iter().any(|name| tested_names.contains(name)) { + scope_deps.push((MODEL_RESOURCE, model.name.clone())); + } + } + } + UDF_MODE => { + for model in project.models { + if model.references.iter().any(|reference| { + reference.kind == UDF_REFERENCE_KIND && tested_names.contains(&reference.name) + }) { + scope_deps.push((MODEL_RESOURCE, model.name.clone())); + } + } + } + _ => scope_deps.extend( + tested_names + .iter() + .map(|name| (TABLE_FUNCTION_RESOURCE, name.clone())), + ), + } + Ok(scope_deps) +} diff --git a/crates/sqlbuild-analysis/src/compiler/_helpers/sql_tests/case_identity.rs b/crates/sqlbuild-analysis/src/compiler/_helpers/sql_tests/case_identity.rs new file mode 100644 index 0000000000..4bbc98f63f --- /dev/null +++ b/crates/sqlbuild-analysis/src/compiler/_helpers/sql_tests/case_identity.rs @@ -0,0 +1,168 @@ +//! Python's `build_sql_test_case_fingerprint`: the version identity of one expanded test case. + +use sha2::{Digest, Sha256}; +use sqlbuild_core::json::main::dumps::dumps; +use sqlbuild_core::json::models::{JsonDialect, JsonInteger, JsonValue, StdlibJsonOptions}; + +use crate::compiler::models::{ + SqlTestAssemblyDeferral, SqlTestAssemblyTest, SqlTestParameterValue, +}; + +/// Python's default decimal context: precision and exponent limits `normalize()` applies. +const DECIMAL_PRECISION: usize = 28; +const DECIMAL_EMAX: i64 = 999_999; +const DECIMAL_EMIN: i64 = -999_999; + +/// The SHA-256 of Python's compact, ASCII, key-sorted JSON of the case's compile inputs. +pub(crate) fn case_fingerprint( + test: &SqlTestAssemblyTest, + case_name: &str, + scope_deps: &[(&'static str, String)], + tested_resources: &[(String, String)], +) -> Result { + let mut sorted_deps: Vec<(&str, &str)> = scope_deps + .iter() + .map(|(kind, name)| (*kind, name.as_str())) + .collect(); + sorted_deps.sort_unstable(); + let mut sorted_resources: Vec<(&str, &str)> = tested_resources + .iter() + .map(|(kind, name)| (kind.as_str(), name.as_str())) + .collect(); + sorted_resources.sort_unstable(); + let parameter_values: Vec = test + .parameter_values + .iter() + .map(|(name, value)| Ok(JsonValue::Array(vec![string(name), identity(value)?]))) + .collect::>()?; + let payload = JsonValue::Object(vec![ + ("source_path".to_owned(), string(&test.relative_path)), + ("block_index".to_owned(), integer(test.block_index)), + ("case_name".to_owned(), string(case_name)), + ( + "parameter_schema".to_owned(), + JsonValue::Array( + test.parameter_schema + .iter() + .map(|(name, kind, nullable)| { + JsonValue::Array(vec![ + string(name), + string(kind), + JsonValue::Bool(*nullable), + ]) + }) + .collect(), + ), + ), + ( + "parameter_values".to_owned(), + JsonValue::Array(parameter_values), + ), + ("expanded_sql".to_owned(), string(&test.sql_body)), + ("scope_deps".to_owned(), pairs(&sorted_deps)), + ("tested_resources".to_owned(), pairs(&sorted_resources)), + ]); + let dialect = JsonDialect::Stdlib(StdlibJsonOptions { + indent: None, + item_separator: ",".to_owned(), + key_separator: ":".to_owned(), + ensure_ascii: true, + sort_keys: true, + allow_nan: true, + }); + let encoded = + dumps(&payload, &dialect).map_err(|_| SqlTestAssemblyDeferral::UnencodableValue)?; + Ok(Sha256::digest(encoded.as_bytes()) + .iter() + .map(|byte| format!("{byte:02x}")) + .collect()) +} + +fn string(text: &str) -> JsonValue { + JsonValue::String(text.to_owned()) +} + +fn integer(value: i64) -> JsonValue { + JsonInteger::parse(&value.to_string()).map_or(JsonValue::Null, JsonValue::Integer) +} + +fn pairs(entries: &[(&str, &str)]) -> JsonValue { + JsonValue::Array( + entries + .iter() + .map(|(first, second)| JsonValue::Array(vec![string(first), string(second)])) + .collect(), + ) +} + +/// Python's `sql_value_identity`: `(kind, payload)` with decimals as normalized tuples. +fn identity(value: &SqlTestParameterValue) -> Result { + let (kind, payload) = match value { + SqlTestParameterValue::String(text) => ("string", string(text)), + SqlTestParameterValue::Integer(number) => ("integer", integer(*number)), + SqlTestParameterValue::Boolean(flag) => ("boolean", JsonValue::Bool(*flag)), + SqlTestParameterValue::Float(number) => ("float", JsonValue::Float(*number)), + SqlTestParameterValue::Decimal { + negative, + digits, + exponent, + } => ("decimal", normalized_decimal(*negative, digits, *exponent)?), + SqlTestParameterValue::Null => ("null", JsonValue::Null), + SqlTestParameterValue::List(items) => ("list", identities(items)?), + SqlTestParameterValue::Set(items) => ("set", identities(items)?), + SqlTestParameterValue::Object(entries) => ( + "object", + JsonValue::Array( + entries + .iter() + .map(|(key, item)| Ok(JsonValue::Array(vec![string(key), identity(item)?]))) + .collect::>()?, + ), + ), + }; + Ok(JsonValue::Array(vec![string(kind), payload])) +} + +fn identities(items: &[SqlTestParameterValue]) -> Result { + Ok(JsonValue::Array( + items.iter().map(identity).collect::>()?, + )) +} + +/// `Decimal.normalize().as_tuple()` where the default context neither rounds nor clamps. +fn normalized_decimal( + negative: bool, + digits: &[u8], + exponent: i64, +) -> Result { + let sign = integer(i64::from(negative)); + let first = digits.iter().position(|digit| *digit != 0); + let Some(first) = first else { + return Ok(JsonValue::Array(vec![ + sign, + JsonValue::Array(vec![integer(0)]), + integer(0), + ])); + }; + let significant = &digits[first..]; + let adjusted = exponent + i64::try_from(significant.len()).unwrap_or(i64::MAX) - 1; + if significant.len() > DECIMAL_PRECISION || !(DECIMAL_EMIN..=DECIMAL_EMAX).contains(&adjusted) { + return Err(SqlTestAssemblyDeferral::DecimalContext); + } + let mut end = significant.len(); + let mut normalized_exponent = exponent; + while significant[end - 1] == 0 && normalized_exponent < DECIMAL_EMAX { + end -= 1; + normalized_exponent += 1; + } + Ok(JsonValue::Array(vec![ + sign, + JsonValue::Array( + significant[..end] + .iter() + .map(|digit| integer(i64::from(*digit))) + .collect(), + ), + integer(normalized_exponent), + ])) +} diff --git a/crates/sqlbuild-analysis/src/compiler/_helpers/sql_tests/macro_call_names.rs b/crates/sqlbuild-analysis/src/compiler/_helpers/sql_tests/macro_call_names.rs new file mode 100644 index 0000000000..bb695db494 --- /dev/null +++ b/crates/sqlbuild-analysis/src/compiler/_helpers/sql_tests/macro_call_names.rs @@ -0,0 +1,105 @@ +//! Python's `find_macro_call_names`: unique top-level macro call names in encounter order. + +use sqlbuild_sqltext::sql_scan::main::matching_paren::matching_paren; +use sqlbuild_sqltext::sql_scan::main::non_code_end::non_code_end; +use sqlbuild_sqltext::sql_scan::models::QuotePolicy; + +use crate::compiler::models::SqlTestAssemblyDeferral; + +/// Declaration calls that expansion substitutes rather than running as macros. +const DECLARATION_CALLS: [&str; 2] = ["enum", "const"]; + +type Scan = Result; + +/// The names Python's scan returns; text it raises on, or classifies by Unicode, is Python's. +pub(crate) fn macro_call_names(sql: &str) -> Scan> { + let bytes = sql.as_bytes(); + let mut names: Vec = Vec::new(); + let mut index = 0; + while index < bytes.len() { + match non_code_end(bytes, index, QuotePolicy::COMPILER) { + Ok(Some(end)) => { + index = end; + continue; + } + Ok(None) => {} + Err(_) => return Err(SqlTestAssemblyDeferral::MacroCallScan), + } + if bytes[index] != b'@' || !is_call_start(bytes, index)? { + index += 1; + continue; + } + let name_end = identifier_end(bytes, index + 1)?; + let name = &sql[index + 1..name_end]; + if !names.iter().any(|known| known == name) { + names.push(name.to_owned()); + } + let open = space_end(bytes, name_end)?; + if bytes.get(open) != Some(&b'(') { + return Err(SqlTestAssemblyDeferral::MacroCallScan); + } + index = matching_paren(bytes, open, QuotePolicy::COMPILER) + .map_err(|_| SqlTestAssemblyDeferral::MacroCallScan)? + + 1; + } + names.retain(|name| !DECLARATION_CALLS.contains(&name.as_str())); + Ok(names) +} + +/// Python's `_is_macro_call_start`, which skips whitespace after each name character. +fn is_call_start(bytes: &[u8], at: usize) -> Scan { + let Some(&first) = bytes.get(at + 1) else { + return Ok(false); + }; + if !ascii(first)? || !(first == b'_' || first.is_ascii_alphabetic()) { + return Ok(false); + } + let mut cursor = at + 2; + while let Some(&byte) = bytes.get(cursor) { + if !ascii(byte)? || !is_identifier_continue(byte) { + break; + } + cursor = space_end(bytes, cursor + 1)?; + } + Ok(bytes.get(cursor) == Some(&b'(')) +} + +fn identifier_end(bytes: &[u8], start: usize) -> Scan { + let mut cursor = start; + while let Some(&byte) = bytes.get(cursor) { + if !ascii(byte)? || !is_identifier_continue(byte) { + break; + } + cursor += 1; + } + Ok(cursor) +} + +/// Python's `str.isspace` skip; a non-ASCII character there is classified by Python. +fn space_end(bytes: &[u8], start: usize) -> Scan { + let mut cursor = start; + while let Some(&byte) = bytes.get(cursor) { + if !ascii(byte)? + || !matches!( + byte, + b' ' | b'\t' | b'\n' | b'\r' | 0x0b | 0x0c | 0x1c..=0x1f + ) + { + break; + } + cursor += 1; + } + Ok(cursor) +} + +fn is_identifier_continue(byte: u8) -> bool { + byte == b'_' || byte.is_ascii_alphanumeric() +} + +fn ascii(byte: u8) -> Scan { + if byte.is_ascii() { + Ok(true) + } else { + Err(SqlTestAssemblyDeferral::MacroCallScan) + } +} diff --git a/crates/sqlbuild-analysis/src/compiler/_helpers/sql_tests/mock_reads.rs b/crates/sqlbuild-analysis/src/compiler/_helpers/sql_tests/mock_reads.rs new file mode 100644 index 0000000000..f2a2807103 --- /dev/null +++ b/crates/sqlbuild-analysis/src/compiler/_helpers/sql_tests/mock_reads.rs @@ -0,0 +1,588 @@ +//! Python's `report_mocks_reading_referencing_helpers`: mocks a test reads that read a helper +//! calling a reference, which cannot resolve because mocks are defined before the models run. + +use std::collections::HashSet; + +use polyglot_sql::{Dialect, DialectType, Expression, ExpressionWalk}; +use serde_json::Value; +use sqlbuild_sqltext::sql_references::main::extract_sql_references::extract_sql_references; +use sqlbuild_sqltext::sql_references::models::{ReferenceExtraction, SqlReference}; +use sqlbuild_sqltext::sql_scan::models::LexicalSyntax; + +use crate::compiler::_helpers::sql_tests::planning::MOCK_CTE_PREFIXES; +use crate::compiler::models::{ + SqlTestAssemblyDeferral, SqlTestAssemblyModel, SqlTestAssemblyModelPayload, + SqlTestAssemblyReference, SqlTestAssemblyTest, SqlTestCte, SqlTestHelperDiagnostic, +}; +use crate::lineage::_helpers::parsed_lineage::proxy_parse_options; + +const REF_KIND: &str = "ref"; +const TABLE_FUNCTION_KIND: &str = "table_fn"; +const DBT_REF_KIND: &str = "dbt_ref"; +const RELATION_KINDS: [&str; 5] = [ + REF_KIND, + "source", + "seed", + DBT_REF_KIND, + TABLE_FUNCTION_KIND, +]; +/// Payload nesting past which the parsed tree is left to Python's `to_dict` walk. +const MAX_TREE_DEPTH: usize = 400; + +type Deferrable = Result; + +/// A reference as compile carries it: the scanner's or a model input's. +#[derive(Clone)] +struct Reference { + kind: String, + name: String, + package: Option, +} + +/// Python's `SqlTestCteGraph`, keyed by case-folded CTE name in first-definition order. +struct CteGraph<'a> { + ctes: Vec<(String, &'a SqlTestCte)>, + reads: Vec<(String, Vec)>, + reader_reads: Vec, +} + +impl CteGraph<'_> { + fn cte(&self, key: &str) -> Option<&SqlTestCte> { + self.ctes + .iter() + .find(|(candidate, _)| candidate == key) + .map(|(_, cte)| *cte) + } + + fn reads(&self, key: &str) -> &[String] { + self.reads + .iter() + .find(|(candidate, _)| candidate == key) + .map_or(&[], |(_, reads)| reads.as_slice()) + } + + /// Python's `reachable_cte_keys`: breadth first, roots included, in visit order. + fn reachable(&self, roots: &[String]) -> Vec { + let mut pending: Vec = Vec::new(); + for root in roots { + if self.cte(root).is_some() { + pending.push(root.clone()); + } + } + let mut visited: Vec = Vec::new(); + for key in &pending { + if !visited.contains(key) { + visited.push(key.clone()); + } + } + let mut cursor = 0; + while cursor < pending.len() { + let key = pending[cursor].clone(); + cursor += 1; + for dependency in self.reads(&key) { + if !visited.contains(dependency) { + visited.push(dependency.clone()); + pending.push(dependency.clone()); + } + } + } + visited + } +} + +/// The P013 diagnostics Python reports for one model test, in report order. +pub(crate) fn mock_reading_helper_diagnostics( + test: &SqlTestAssemblyTest, + payload: &SqlTestAssemblyModelPayload, + models: &[SqlTestAssemblyModel], + target_model_names: &[String], + syntax: &LexicalSyntax, +) -> Deferrable> { + let mut referencing: Vec<(String, Reference)> = Vec::new(); + for cte in &payload.authored_ctes { + if is_mock_name(&cte.name) { + continue; + } + let first = references(&cte.sql_body, syntax)? + .into_iter() + .find(|reference| RELATION_KINDS.contains(&reference.kind.as_str())); + if let Some(reference) = first { + insert(&mut referencing, ascii_fold(&cte.name)?, reference); + } + } + if referencing.is_empty() { + return Ok(Vec::new()); + } + require_ascii(test, payload)?; + let readers: Vec<&SqlTestCte> = payload + .expected_ctes + .iter() + .chain(&payload.assertion_ctes) + .collect(); + let graph = cte_graph(&payload.authored_ctes, &readers)?; + let mut called_mocks: Vec = Vec::new(); + let read_helpers = read_helper_keys(&graph); + let helper_sqls = read_helpers + .iter() + .filter_map(|key| graph.cte(key).map(|cte| cte.sql_body.as_str())); + for sql in readers + .iter() + .map(|cte| cte.sql_body.as_str()) + .chain(helper_sqls) + { + for reference in references(sql, syntax)? { + called_mocks.extend(mock_keys(&reference)?); + } + } + let used = used_mock_keys( + &graph, + &called_mocks, + models, + target_model_names, + &payload.mock_model_names, + )?; + let mut diagnostics: Vec = Vec::new(); + for mock_key in used { + let reachable = graph.reachable(graph.reads(&mock_key)); + let Some((helper_key, reference)) = reachable.iter().find_map(|key| { + referencing + .iter() + .find(|(candidate, _)| candidate == key) + .map(|(_, reference)| (key, reference)) + }) else { + continue; + }; + let (Some(mock), Some(helper)) = (graph.cte(&mock_key), graph.cte(helper_key)) else { + continue; + }; + let call = reference_call(reference); + let message = format!( + "SQL test mock '{}' reads helper CTE '{}', which calls {call}; mocks and fixtures are \ + defined before the models the test runs, so the helper cannot be resolved for them", + mock.name, helper.name + ); + let help = mock_reference_help(&mock.name, &call, reference); + diagnostics.push(located(test, &helper.name, &call, message, help)); + } + Ok(diagnostics) +} + +fn is_mock_name(name: &str) -> bool { + MOCK_CTE_PREFIXES + .iter() + .any(|prefix| name.starts_with(prefix)) +} + +/// Python dict assignment: a repeated key keeps its first position and takes the new value. +fn insert(entries: &mut Vec<(String, T)>, key: String, value: T) { + match entries.iter_mut().find(|(candidate, _)| *candidate == key) { + Some(entry) => entry.1 = value, + None => entries.push((key, value)), + } +} + +/// Python's `str.casefold`, exact only on ASCII text. +fn ascii_fold(text: &str) -> Deferrable { + if text.is_ascii() { + Ok(text.to_ascii_lowercase()) + } else { + Err(SqlTestAssemblyDeferral::NonAsciiText) + } +} + +/// Python's folding, token and header patterns read only ASCII text exactly. +fn require_ascii( + test: &SqlTestAssemblyTest, + payload: &SqlTestAssemblyModelPayload, +) -> Deferrable<()> { + let ascii = test.contents.is_ascii() + && test.block_sql.is_ascii() + && payload + .authored_ctes + .iter() + .chain(&payload.expected_ctes) + .chain(&payload.assertion_ctes) + .all(|cte| cte.name.is_ascii() && cte.sql_body.is_ascii()); + if ascii { + Ok(()) + } else { + Err(SqlTestAssemblyDeferral::NonAsciiText) + } +} + +/// Python's `scan_sql_reference_calls(...).references`; a failing scan is Python's to raise. +fn references(sql: &str, syntax: &LexicalSyntax) -> Deferrable> { + match extract_sql_references(sql, syntax) { + ReferenceExtraction::Extracted(scan) => { + Ok(scan.references.into_iter().map(scanned).collect()) + } + ReferenceExtraction::Failed(_) | ReferenceExtraction::Deferred => { + Err(SqlTestAssemblyDeferral::ReferenceScan) + } + } +} + +fn scanned(reference: SqlReference) -> Reference { + Reference { + kind: reference.kind.to_owned(), + name: reference.name, + package: reference.package, + } +} + +fn model_reference(reference: &SqlTestAssemblyReference) -> Reference { + Reference { + kind: reference.kind.clone(), + name: reference.name.clone(), + package: reference.package.clone(), + } +} + +/// Python's `sql_test_cte_graph`. +fn cte_graph<'a>(authored: &'a [SqlTestCte], readers: &[&SqlTestCte]) -> Deferrable> { + let mut ctes: Vec<(String, &'a SqlTestCte)> = Vec::new(); + for cte in authored { + insert(&mut ctes, ascii_fold(&cte.name)?, cte); + } + let keys: Vec = ctes.iter().map(|(key, _)| key.clone()).collect(); + let mut reads: Vec<(String, Vec)> = Vec::new(); + for (key, cte) in &ctes { + let others: Vec = keys.iter().filter(|other| *other != key).cloned().collect(); + let cte_reads = match parsed_reads(&cte.sql_body, &others)? { + Some(parsed) => parsed, + None => token_reads(&cte.sql_body, &others), + }; + reads.push((key.clone(), cte_reads)); + } + let mut reader_reads: Vec = Vec::new(); + for reader in readers { + let parsed = match parsed_reads(&reader.sql_body, &keys)? { + Some(parsed) => parsed, + None => token_reads(&reader.sql_body, &keys), + }; + for key in parsed { + if !reader_reads.contains(&key) { + reader_reads.push(key); + } + } + } + Ok(CteGraph { + ctes, + reads, + reader_reads, + }) +} + +/// Python's `_parsed_reads`: unqualified reads of `keys` no nested CTE shadows, or None unparsed. +fn parsed_reads(sql: &str, keys: &[String]) -> Deferrable>> { + let folded = ascii_fold(sql)?; + if !keys.iter().any(|key| folded.contains(key.as_str())) { + return Ok(Some(Vec::new())); + } + let Ok(options) = proxy_parse_options() else { + return Err(SqlTestAssemblyDeferral::UnreadableTree); + }; + let Ok(mut statements) = Dialect::get(DialectType::Generic).parse_with_options(sql, &options) + else { + return Ok(None); + }; + if statements.len() != 1 { + return Ok(None); + } + let parsed: Expression = statements.remove(0); + let nested = defined_cte_keys(&parsed)?; + let mut reads: Vec = Vec::new(); + for node in parsed.dfs() { + let Expression::Table(table) = node else { + continue; + }; + if table.schema.is_some() || table.catalog.is_some() { + continue; + } + let key = ascii_fold(&table.name.name)?; + if keys.contains(&key) && !nested.contains(&key) && !reads.contains(&key) { + reads.push(key); + } + } + Ok(Some(reads)) +} + +/// Python's `_defined_cte_keys` over the serialized tree Python's `to_dict` returns. +fn defined_cte_keys(parsed: &Expression) -> Deferrable> { + let tree = serde_json::to_value(parsed).map_err(|_| SqlTestAssemblyDeferral::UnreadableTree)?; + let mut keys: HashSet = HashSet::new(); + let mut pending: Vec<(&Value, usize)> = vec![(&tree, 0)]; + while let Some((value, depth)) = pending.pop() { + if depth > MAX_TREE_DEPTH { + return Err(SqlTestAssemblyDeferral::UnreadableTree); + } + match value { + Value::Array(items) => pending.extend(items.iter().map(|item| (item, depth + 1))), + Value::Object(entries) => { + if let Some(Value::Array(ctes)) = entries.get("ctes") { + for cte in ctes { + if let Some(Value::String(name)) = cte + .get("alias") + .filter(|alias| alias.is_object()) + .and_then(|alias| alias.get("name")) + { + let _ = keys.insert(ascii_fold(name)?); + } + } + } + pending.extend(entries.values().map(|item| (item, depth + 1))); + } + _ => {} + } + } + Ok(keys) +} + +/// Python's `_token_reads` over `[A-Za-z_][\w$]*` tokens of ASCII text. +fn token_reads(sql: &str, keys: &[String]) -> Vec { + let bytes = sql.as_bytes(); + let mut tokens: Vec = Vec::new(); + let mut index = 0; + while index < bytes.len() { + if !(bytes[index].is_ascii_alphabetic() || bytes[index] == b'_') { + index += 1; + continue; + } + let start = index; + index += 1; + while index < bytes.len() + && (bytes[index].is_ascii_alphanumeric() || matches!(bytes[index], b'_' | b'$')) + { + index += 1; + } + let token = sql[start..index].to_ascii_lowercase(); + if !tokens.contains(&token) { + tokens.push(token); + } + } + tokens.retain(|token| keys.contains(token)); + tokens +} + +/// Python's `_read_helper_keys`: non-mock CTEs the test's readers reach. +fn read_helper_keys(graph: &CteGraph<'_>) -> Vec { + graph + .reachable(&graph.reader_reads) + .into_iter() + .filter(|key| graph.cte(key).is_some_and(|cte| !is_mock_name(&cte.name))) + .collect() +} + +/// Python's `_mock_keys`: the case-folded mock CTE names that stand in for `reference`. +fn mock_keys(reference: &Reference) -> Deferrable> { + let prefix = match reference.kind.as_str() { + "ref" => "__ref__", + "source" => "__source__", + "seed" => "__seed__", + "dbt_ref" => "__dbt_ref__", + "table_fn" => "__table_fn__", + _ => return Ok(Vec::new()), + }; + let mut names: Vec = vec![reference.name.clone()]; + if let Some(package) = &reference.package { + names.push(format!("{package}__{}", reference.name)); + } + names + .iter() + .map(|name| ascii_fold(&format!("{prefix}{name}"))) + .collect() +} + +/// Python's `_used_mock_keys`. +fn used_mock_keys( + graph: &CteGraph<'_>, + called_mocks: &[String], + models: &[SqlTestAssemblyModel], + target_model_names: &[String], + mock_model_names: &[String], +) -> Deferrable> { + let mut used: Vec = graph + .reachable(&graph.reader_reads) + .into_iter() + .filter(|key| graph.cte(key).is_some_and(|cte| is_mock_name(&cte.name))) + .collect(); + let mut seen_called: Vec<&String> = Vec::new(); + for key in called_mocks { + if seen_called.contains(&key) { + continue; + } + seen_called.push(key); + if graph.cte(key).is_some() && !used.contains(key) { + used.push(key.clone()); + } + } + let mut model_references: Vec<(String, &[SqlTestAssemblyReference])> = Vec::new(); + for model in models { + insert( + &mut model_references, + model.name.clone(), + model.references.as_slice(), + ); + } + let mut pending: Vec = target_model_names + .iter() + .filter(|name| !mock_model_names.contains(name)) + .cloned() + .collect(); + let mut visited: HashSet = pending.iter().cloned().collect(); + let mut cursor = 0; + while cursor < pending.len() { + let model_name = pending[cursor].clone(); + cursor += 1; + let references = model_references + .iter() + .find(|(name, _)| *name == model_name) + .map_or(&[][..], |(_, references)| *references); + for reference in references { + let reference = model_reference(reference); + for mock_key in mock_keys(&reference)? { + if graph.cte(&mock_key).is_some() && !used.contains(&mock_key) { + used.push(mock_key); + } + } + if reference.kind == REF_KIND + && !mock_model_names.contains(&reference.name) + && visited.insert(reference.name.clone()) + { + pending.push(reference.name); + } + } + } + Ok(used) +} + +/// Python's `SqlReferenceKind.example_call(*args, quote='"')`. +fn example_call(kind: &str, arguments: &[&str]) -> String { + let quoted: Vec = arguments + .iter() + .map(|argument| format!("\"{}\"", argument.replace('"', "\"\""))) + .collect(); + format!("__{kind}({})", quoted.join(", ")) +} + +/// Python's `_reference_call`. +fn reference_call(reference: &Reference) -> String { + match (&reference.package, reference.kind.as_str()) { + (Some(package), DBT_REF_KIND) => example_call(&reference.kind, &[package, &reference.name]), + _ => example_call(&reference.kind, &[&reference.name]), + } +} + +/// Python's `_mock_reference_help`. +fn mock_reference_help(mock_name: &str, call: &str, reference: &Reference) -> String { + if reference.kind != TABLE_FUNCTION_KIND && reference.package.is_none() { + let mock_cte = format!("__{}__{}", reference.kind, reference.name); + return format!( + "Read a mock by its CTE name instead, for example FROM {mock_cte} rather than \ + FROM {call}, defining {mock_cte} AS (SELECT ...) if the test does not mock it, \ + or write the rows of '{mock_name}' directly." + ); + } + format!("Write the rows of '{mock_name}' directly, for example {mock_name} AS (SELECT ...).") +} + +/// Python's `sql_test_cte_location` and `reference_call_location` on ASCII contents. +fn located( + test: &SqlTestAssemblyTest, + cte_name: &str, + call: &str, + message: String, + help: String, +) -> SqlTestHelperDiagnostic { + let contents = test.contents.as_str(); + let block_offset = contents.find(test.block_sql.as_str()).unwrap_or(0); + let (start, length) = match cte_header(contents, cte_name, block_offset) { + Some((header_start, header_end)) => find_ignoring_case(contents, call, header_end) + .map_or((header_start, cte_name.len()), |found| (found, call.len())), + None => (block_offset, 0), + }; + let end = start + length; + let (line, column) = line_and_column(contents, start); + let (end_line, end_column) = line_and_column(contents, end); + SqlTestHelperDiagnostic { + line, + column, + end_line, + end_column, + message, + help, + } +} + +/// Python's `(? Option<(usize, usize)> { + let bytes = contents.as_bytes(); + let mut start = from; + while let Some(found) = find_ignoring_case(contents, name, start) { + start = found + 1; + if found > 0 && is_word_or_dollar(bytes[found - 1]) { + continue; + } + let mut cursor = found + name.len(); + if matches!(bytes.get(cursor), Some(b'"' | b'`')) { + cursor += 1; + } + let spaces = cursor; + while bytes + .get(cursor) + .is_some_and(|byte| is_python_ascii_space(*byte)) + { + cursor += 1; + } + if cursor == spaces + || !bytes + .get(cursor..cursor + 2) + .is_some_and(|keyword| keyword.eq_ignore_ascii_case(b"AS")) + { + continue; + } + cursor += 2; + while bytes + .get(cursor) + .is_some_and(|byte| is_python_ascii_space(*byte)) + { + cursor += 1; + } + if bytes.get(cursor) == Some(&b'(') { + return Some((found, cursor + 1)); + } + } + None +} + +fn find_ignoring_case(contents: &str, needle: &str, from: usize) -> Option { + let haystack = contents.as_bytes(); + let needle = needle.as_bytes(); + if needle.is_empty() { + return (from <= haystack.len()).then_some(from); + } + (from..=haystack.len().checked_sub(needle.len())?) + .find(|&index| haystack[index..index + needle.len()].eq_ignore_ascii_case(needle)) +} + +fn is_word_or_dollar(byte: u8) -> bool { + byte.is_ascii_alphanumeric() || matches!(byte, b'_' | b'$') +} + +/// Python's `\s` on ASCII text, which includes the information separators. +fn is_python_ascii_space(byte: u8) -> bool { + matches!( + byte, + b' ' | b'\t' | b'\n' | b'\r' | 0x0b | 0x0c | 0x1c..=0x1f + ) +} + +fn line_and_column(contents: &str, offset: usize) -> (usize, usize) { + let before = &contents.as_bytes()[..offset]; + let line = before.iter().filter(|byte| **byte == b'\n').count() + 1; + let line_start = before + .iter() + .rposition(|byte| *byte == b'\n') + .map_or(0, |index| index + 1); + (line, offset - line_start + 1) +} diff --git a/crates/sqlbuild-analysis/src/compiler/_helpers/sql_tests/mod.rs b/crates/sqlbuild-analysis/src/compiler/_helpers/sql_tests/mod.rs index 364bfe13c4..2661b0ec0a 100644 --- a/crates/sqlbuild-analysis/src/compiler/_helpers/sql_tests/mod.rs +++ b/crates/sqlbuild-analysis/src/compiler/_helpers/sql_tests/mod.rs @@ -1,4 +1,6 @@ +pub mod assembly; pub mod authored_macro_calls; +pub mod case_identity; pub mod cte_rename; pub mod cte_slices; pub mod cte_sql; @@ -7,7 +9,9 @@ pub mod extraction; pub mod glue; pub mod helper_names; pub mod helper_scope; +pub mod macro_call_names; pub mod markers; +pub mod mock_reads; pub mod planning; pub mod reader_references; pub mod reference_calls; diff --git a/crates/sqlbuild-analysis/src/compiler/main/mod.rs b/crates/sqlbuild-analysis/src/compiler/main/mod.rs index 357fadc9d0..94b6925b7e 100644 --- a/crates/sqlbuild-analysis/src/compiler/main/mod.rs +++ b/crates/sqlbuild-analysis/src/compiler/main/mod.rs @@ -1,4 +1,5 @@ pub mod sql_scenario_extraction; +pub mod sql_test_assembly; pub mod sql_test_chain_resolution; pub mod sql_test_chains; pub mod sql_test_difference_sampling; diff --git a/crates/sqlbuild-analysis/src/compiler/main/sql_test_assembly.rs b/crates/sqlbuild-analysis/src/compiler/main/sql_test_assembly.rs new file mode 100644 index 0000000000..f3af180469 --- /dev/null +++ b/crates/sqlbuild-analysis/src/compiler/main/sql_test_assembly.rs @@ -0,0 +1,9 @@ +//! Compiled SQL test facts from a typed request, test by test. + +use crate::compiler::_helpers::sql_tests::assembly; +use crate::compiler::models::{SqlTestAssemblyBatch, SqlTestAssemblyOutcome}; + +/// Assemble every test of `batch` in request order; a deferred test is Python's to assemble. +pub fn assemble_sql_test_batch(batch: &SqlTestAssemblyBatch) -> Vec { + assembly::assemble_batch(batch) +} diff --git a/crates/sqlbuild-analysis/src/compiler/models.rs b/crates/sqlbuild-analysis/src/compiler/models.rs index 03e40d082d..7ef1d147f3 100644 --- a/crates/sqlbuild-analysis/src/compiler/models.rs +++ b/crates/sqlbuild-analysis/src/compiler/models.rs @@ -182,3 +182,154 @@ pub struct SqlTestAssertionStep { #[serde(default)] pub comparison_body_sql: Option, } + +/// The compiled SQL tests to assemble, with the facts of every model the project compiles. +#[derive(Debug)] +pub struct SqlTestAssemblyBatch { + pub models: Vec, + pub tests: Vec, + pub lexical_syntax: LexicalSyntax, +} + +/// What assembly reads of one model: its name, macro dependencies and references. +#[derive(Debug)] +pub struct SqlTestAssemblyModel { + pub name: String, + pub macro_deps: Vec, + /// The pre-macro SQL to scan for calls when no macro dependencies were recorded and it + /// contains a macro token; Python scans it only then. + pub unscanned_macro_source: Option, + pub references: Vec, +} + +/// One logical reference: Python `SqlReferenceKind` value, name and optional package. +#[derive(Clone, Debug)] +pub struct SqlTestAssemblyReference { + pub kind: String, + pub name: String, + pub package: Option, +} + +/// One expanded SQL test case as compile inputs carry it. +#[derive(Debug)] +pub struct SqlTestAssemblyTest { + pub block_name: Option, + pub file_stem: String, + pub relative_path: String, + pub relative_stem: String, + pub contents: String, + pub block_sql: String, + pub block_index: i64, + pub sql_body: String, + pub case_name: Option, + /// `(name, value kind, nullable)` per declared parameter. + pub parameter_schema: Vec<(String, String, bool)>, + pub parameter_values: Vec<(String, SqlTestParameterValue)>, + pub payload: SqlTestAssemblyPayload, +} + +/// A typed SQL value as its kind and payload; decimals keep their unnormalized digits. +#[derive(Clone, Debug)] +pub enum SqlTestParameterValue { + String(String), + Integer(i64), + Boolean(bool), + Float(f64), + Decimal { + negative: bool, + digits: Vec, + exponent: i64, + }, + Null, + List(Vec), + Set(Vec), + Object(Vec<(String, SqlTestParameterValue)>), +} + +/// The part of a test's payload assembly reads. +#[derive(Debug)] +pub enum SqlTestAssemblyPayload { + /// A macro, UDF or table-function test of the named resources. + Direct { + mode: String, + tested_resource_names: Vec, + }, + Model(SqlTestAssemblyModelPayload), +} + +#[derive(Debug)] +pub struct SqlTestAssemblyModelPayload { + pub authored_ctes: Vec, + pub expected_ctes: Vec, + pub assertion_ctes: Vec, + pub expected_model_names: Vec, + pub assertion_target_model_names: Vec, + pub reference_target_model_names: Vec, + pub mock_model_names: Vec, + pub has_macro_mocks: bool, +} + +/// One test's assembled facts, or why Python assembles it. +#[derive(Debug, PartialEq)] +pub enum SqlTestAssemblyOutcome { + Assembled(AssembledSqlTestFacts), + Deferred(SqlTestAssemblyDeferral), +} + +/// The facts Python's `_assemble_compiled_sql_test` computes for one test. +#[derive(Debug, PartialEq)] +pub struct AssembledSqlTestFacts { + pub name: String, + /// `(resource type, name)` per scope dependency, in Python's order. + pub scope_deps: Vec<(&'static str, String)>, + pub target_model_names: Vec, + pub case_fingerprint: Option, + /// Resource name of the test's diagnostics: the block name, else the relative path's stem. + pub diagnostic_resource_name: String, + pub diagnostics: Vec, +} + +/// One P013 diagnostic for a mock that reads a helper calling a reference. +#[derive(Debug, PartialEq, Eq)] +pub struct SqlTestHelperDiagnostic { + pub line: usize, + pub column: usize, + pub end_line: usize, + pub end_column: usize, + pub message: String, + pub help: String, +} + +/// Why one test is assembled by Python instead. +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum SqlTestAssemblyDeferral { + /// Macro mocks re-expand every model, which only Python's macro expansion does. + MacroMocks, + /// A model's macro call scan raises in Python or meets text Python classifies by Unicode. + MacroCallScan, + /// The reference scan fails or cannot classify the text as Python does. + ReferenceScan, + /// Non-ASCII text where Python's case folding or Unicode classes would apply. + NonAsciiText, + /// A decimal parameter Python's decimal context would round or clamp. + DecimalContext, + /// A parameter value the JSON encoder cannot encode as Python's does. + UnencodableValue, + /// A parsed CTE tree too deep, or otherwise unfit, to read as Python's `to_dict` walk does. + UnreadableTree, +} + +impl SqlTestAssemblyDeferral { + /// The kind recorded for the deferral. + pub fn as_str(self) -> &'static str { + match self { + Self::MacroMocks => "macro_mocks", + Self::MacroCallScan => "macro_call_scan", + Self::ReferenceScan => "reference_scan", + Self::NonAsciiText => "non_ascii_text", + Self::DecimalContext => "decimal_context", + Self::UnencodableValue => "unencodable_value", + Self::UnreadableTree => "unreadable_tree", + } + } +} diff --git a/crates/sqlbuild-python/src/bindings/_helpers/sql_test_glue/assembly.rs b/crates/sqlbuild-python/src/bindings/_helpers/sql_test_glue/assembly.rs new file mode 100644 index 0000000000..8769cb3b70 --- /dev/null +++ b/crates/sqlbuild-python/src/bindings/_helpers/sql_test_glue/assembly.rs @@ -0,0 +1,253 @@ +//! Assemble compiled SQL test facts from compile inputs' own objects, without a JSON request. + +use pyo3::exceptions::PyValueError; +use pyo3::prelude::{Bound, PyAny, PyAnyMethods, PyModule, PyModuleMethods, PyResult, Python}; +use pyo3::{FromPyObject, PyErr, pyfunction, wrap_pyfunction}; +use sqlbuild_analysis::compiler::main::sql_test_assembly::assemble_sql_test_batch; +use sqlbuild_analysis::compiler::models::{ + AssembledSqlTestFacts, SqlTestAssemblyBatch, SqlTestAssemblyModel, SqlTestAssemblyModelPayload, + SqlTestAssemblyOutcome, SqlTestAssemblyPayload, SqlTestAssemblyReference, SqlTestAssemblyTest, + SqlTestCte, SqlTestParameterValue, +}; + +use crate::bindings::_helpers::boundary::panics::value_error; +use crate::bindings::_helpers::sqltext::lexical_syntax::LexicalSyntaxInput; +use crate::bindings::types::CompilerDetach; + +const MACRO_TOKEN: char = '@'; + +/// `(line, column, end line, end column, message, help)` of one P013 diagnostic. +type DiagnosticRow = (usize, usize, usize, usize, String, String); +/// `(name, scope deps, target models, case fingerprint, diagnostic resource, diagnostics)`. +type FactsRow = ( + String, + Vec<(&'static str, String)>, + Vec, + Option, + String, + Vec, +); +/// The test's facts, or the deferral kind of a test Python assembles. +type AssemblyRow = (Option, Option<&'static str>); + +/// The Python assembly request: the project's model inputs and the SQL test inputs. +#[derive(FromPyObject)] +struct AssemblyRequest<'py> { + #[pyo3(attribute)] + model_inputs: Vec>, + #[pyo3(attribute)] + test_inputs: Vec>, + #[pyo3(attribute)] + lexical_syntax: LexicalSyntaxInput, +} + +/// Each test input's assembled facts, or the deferral of a test Python must assemble. +#[pyfunction] +fn assemble_compiled_sql_tests( + py: Python<'_>, + request: AssemblyRequest<'_>, +) -> PyResult> { + let batch = SqlTestAssemblyBatch { + models: request + .model_inputs + .iter() + .map(assembly_model) + .collect::>()?, + tests: request + .test_inputs + .iter() + .map(assembly_test) + .collect::>()?, + lexical_syntax: request.lexical_syntax.into(), + }; + let outcomes = py + .compiler_detach(|| Ok(assemble_sql_test_batch(&batch))) + .map_err(value_error)?; + Ok(outcomes.into_iter().map(assembly_row).collect()) +} + +fn assembly_model(model: &Bound<'_, PyAny>) -> PyResult { + let macro_deps: Vec = model.getattr("macro_deps")?.extract()?; + let unscanned_macro_source = if macro_deps.is_empty() { + Some(model.getattr("macro_source_sql")?.extract::()?) + .filter(|sql| sql.contains(MACRO_TOKEN)) + } else { + None + }; + Ok(SqlTestAssemblyModel { + name: model + .getattr("model_file")? + .getattr("file_path")? + .getattr("stem")? + .extract()?, + macro_deps, + unscanned_macro_source, + references: model + .getattr("references")? + .try_iter()? + .map(|reference| { + let reference = reference?; + Ok(SqlTestAssemblyReference { + kind: reference.getattr("ref_kind")?.extract()?, + name: reference.getattr("ref_name")?.extract()?, + package: reference.getattr("ref_package")?.extract()?, + }) + }) + .collect::>()?, + }) +} + +fn assembly_test(test: &Bound<'_, PyAny>) -> PyResult { + let test_file = test.getattr("test_file")?; + let test_block = test.getattr("test_block")?; + let relative_path = test_file.getattr("relative_path")?; + let payload = test.getattr("payload")?; + let payload = if payload.hasattr("actual_cte")? { + SqlTestAssemblyPayload::Direct { + mode: payload.getattr("mode")?.extract()?, + tested_resource_names: payload.getattr("tested_resource_names")?.extract()?, + } + } else { + SqlTestAssemblyPayload::Model(SqlTestAssemblyModelPayload { + authored_ctes: ctes(&payload, "authored_ctes")?, + expected_ctes: ctes(&payload, "expected_ctes")?, + assertion_ctes: ctes(&payload, "assertion_ctes")?, + expected_model_names: payload.getattr("expected_model_names")?.extract()?, + assertion_target_model_names: payload + .getattr("assertion_target_model_names")? + .extract()?, + reference_target_model_names: payload + .getattr("reference_target_model_names")? + .extract()?, + mock_model_names: payload.getattr("mock_model_names")?.extract()?, + has_macro_mocks: payload.getattr("macro_mocks")?.is_truthy()?, + }) + }; + Ok(SqlTestAssemblyTest { + block_name: test_block.getattr("name")?.extract()?, + file_stem: test_file.getattr("file_path")?.getattr("stem")?.extract()?, + relative_path: relative_path.call_method0("as_posix")?.extract()?, + relative_stem: relative_path.getattr("stem")?.extract()?, + contents: test_file.getattr("contents")?.extract()?, + block_sql: test_block.getattr("sql_body")?.extract()?, + block_index: test_block.getattr("test_index")?.extract()?, + sql_body: test.getattr("sql_body")?.extract()?, + case_name: test.getattr("case_name")?.extract()?, + parameter_schema: test + .getattr("parameter_schema")? + .try_iter()? + .map(|parameter| { + let parameter = parameter?; + Ok(( + parameter.getattr("name")?.extract()?, + parameter.getattr("value_type")?.extract()?, + parameter.getattr("nullable")?.extract()?, + )) + }) + .collect::>()?, + parameter_values: test + .getattr("parameter_values")? + .try_iter()? + .map(|entry| { + let (name, value): (String, Bound<'_, PyAny>) = entry?.extract()?; + Ok((name, parameter_value(&value)?)) + }) + .collect::>()?, + payload, + }) +} + +fn ctes(owner: &Bound<'_, PyAny>, attribute: &str) -> PyResult> { + owner + .getattr(attribute)? + .try_iter()? + .map(|value| { + let value = value?; + Ok(SqlTestCte { + name: value.getattr("name")?.extract()?, + sql_body: value.getattr("sql_body")?.extract()?, + }) + }) + .collect() +} + +/// One `SqlValue`, read by its logical kind; decimals keep `as_tuple()`'s digits. +fn parameter_value(value: &Bound<'_, PyAny>) -> PyResult { + let kind: String = value.getattr("logical_type")?.getattr("kind")?.extract()?; + let payload = value.getattr("value")?; + Ok(match kind.as_str() { + "string" => SqlTestParameterValue::String(payload.extract()?), + "integer" => SqlTestParameterValue::Integer(payload.extract()?), + "boolean" => SqlTestParameterValue::Boolean(payload.extract()?), + "float" => SqlTestParameterValue::Float(payload.extract()?), + "decimal" => { + let (sign, digits, exponent): (u8, Vec, i64) = + payload.call_method0("as_tuple")?.extract()?; + SqlTestParameterValue::Decimal { + negative: sign == 1, + digits, + exponent, + } + } + "null" => SqlTestParameterValue::Null, + "list" => SqlTestParameterValue::List(parameter_values(&payload)?), + "set" => SqlTestParameterValue::Set(parameter_values(&payload)?), + "object" => SqlTestParameterValue::Object( + payload + .try_iter()? + .map(|entry| { + let (key, item): (String, Bound<'_, PyAny>) = entry?.extract()?; + Ok((key, parameter_value(&item)?)) + }) + .collect::>()?, + ), + other => { + return Err(PyErr::new::(format!( + "unknown SQL value kind {other:?}" + ))); + } + }) +} + +fn parameter_values(items: &Bound<'_, PyAny>) -> PyResult> { + items + .try_iter()? + .map(|item| parameter_value(&item?)) + .collect() +} + +fn assembly_row(outcome: SqlTestAssemblyOutcome) -> AssemblyRow { + match outcome { + SqlTestAssemblyOutcome::Assembled(facts) => (Some(facts_row(facts)), None), + SqlTestAssemblyOutcome::Deferred(deferral) => (None, Some(deferral.as_str())), + } +} + +fn facts_row(facts: AssembledSqlTestFacts) -> FactsRow { + ( + facts.name, + facts.scope_deps, + facts.target_model_names, + facts.case_fingerprint, + facts.diagnostic_resource_name, + facts + .diagnostics + .into_iter() + .map(|diagnostic| { + ( + diagnostic.line, + diagnostic.column, + diagnostic.end_line, + diagnostic.end_column, + diagnostic.message, + diagnostic.help, + ) + }) + .collect(), + ) +} + +pub(crate) fn register(module: &Bound<'_, PyModule>) -> PyResult<()> { + module.add_function(wrap_pyfunction!(assemble_compiled_sql_tests, module)?)?; + Ok(()) +} diff --git a/crates/sqlbuild-python/src/bindings/_helpers/sql_test_glue/mod.rs b/crates/sqlbuild-python/src/bindings/_helpers/sql_test_glue/mod.rs index 8be67c2871..75d847c142 100644 --- a/crates/sqlbuild-python/src/bindings/_helpers/sql_test_glue/mod.rs +++ b/crates/sqlbuild-python/src/bindings/_helpers/sql_test_glue/mod.rs @@ -1,2 +1,3 @@ +pub(crate) mod assembly; pub(crate) mod planning; pub(crate) mod registration; diff --git a/crates/sqlbuild-python/src/bindings/_helpers/sql_test_glue/registration.rs b/crates/sqlbuild-python/src/bindings/_helpers/sql_test_glue/registration.rs index d7cf7c6c23..ac702ccbfd 100644 --- a/crates/sqlbuild-python/src/bindings/_helpers/sql_test_glue/registration.rs +++ b/crates/sqlbuild-python/src/bindings/_helpers/sql_test_glue/registration.rs @@ -1,9 +1,10 @@ -//! Register the native SQL test planning functions. +//! Register the native SQL test planning and assembly functions. use pyo3::prelude::{Bound, PyModule, PyResult}; -use crate::bindings::_helpers::sql_test_glue::planning; +use crate::bindings::_helpers::sql_test_glue::{assembly, planning}; pub(crate) fn register(module: &Bound<'_, PyModule>) -> PyResult<()> { - planning::register(module) + planning::register(module)?; + assembly::register(module) } diff --git a/src/sqlbuild/_native.pyi b/src/sqlbuild/_native.pyi index 5d194a0bf1..7f9dcc47de 100644 --- a/src/sqlbuild/_native.pyi +++ b/src/sqlbuild/_native.pyi @@ -4,10 +4,11 @@ from collections.abc import Sequence from typing import Any, TypedDict from sqlbuild.compiler.sql_test_glue.models import ( + NativeSqlTestAssemblyRequest, NativeSqlTestChainRequest, NativeSqlTestPlanningRequest, ) -from sqlbuild.compiler.sql_test_glue.types import NativeSqlTestPlanRow +from sqlbuild.compiler.sql_test_glue.types import NativeSqlTestAssemblyRow, NativeSqlTestPlanRow BUILD_IDENTITY: str @@ -620,6 +621,9 @@ def plan_compiled_sql_tests( request: NativeSqlTestPlanningRequest, / ) -> tuple[list[NativeSqlTestPlanRow], int, int]: ... def resolve_compiled_sql_test_chains(request: NativeSqlTestChainRequest, /) -> list[list[str]]: ... +def assemble_compiled_sql_tests( + request: NativeSqlTestAssemblyRequest, / +) -> list[NativeSqlTestAssemblyRow]: ... # Native analysis: compiled project assembly. def assemble_project_resource_facts( diff --git a/src/sqlbuild/compiler/compile/_helpers/native_stages/sql_tests.py b/src/sqlbuild/compiler/compile/_helpers/native_stages/sql_tests.py index fde4b1cf87..6704d1b09f 100644 --- a/src/sqlbuild/compiler/compile/_helpers/native_stages/sql_tests.py +++ b/src/sqlbuild/compiler/compile/_helpers/native_stages/sql_tests.py @@ -4,6 +4,7 @@ from collections.abc import Callable +from sqlbuild.compiler.compile._helpers.diagnostics.collector import report_compile_diagnostic from sqlbuild.compiler.compile.models import ( CompiledSqlTest, CompileProjectInputs, @@ -14,6 +15,7 @@ from sqlbuild.compiler.sql_test_glue.main._assemble_native_sql_tests import ( assemble_native_sql_tests, ) +from sqlbuild.compiler.sql_test_glue.models import NativeSqlTestAssembly def assemble_sql_tests_by_engine( @@ -23,8 +25,15 @@ def assemble_sql_tests_by_engine( ) -> tuple[CompiledSqlTest, ...]: """Assemble every SQL test natively, or each test in Python where native defers.""" - if native_stage_enabled(NativeStage.SQL_TEST_GLUE): - native_tests: tuple[CompiledSqlTest, ...] | None = assemble_native_sql_tests(inputs=inputs) - if native_tests is not None: - return native_tests - return tuple(assemble_python_test(test_input) for test_input in inputs.test_inputs) + if not native_stage_enabled(NativeStage.SQL_TEST_GLUE): + return tuple(assemble_python_test(test_input) for test_input in inputs.test_inputs) + native: tuple[NativeSqlTestAssembly | None, ...] = assemble_native_sql_tests(inputs=inputs) + tests: list[CompiledSqlTest] = [] + for test_input, assembly in zip(inputs.test_inputs, native, strict=True): + if assembly is None: + tests.append(assemble_python_test(test_input)) + continue + for key, diagnostic in assembly.diagnostics: + report_compile_diagnostic(key=key, diagnostic=diagnostic) + tests.append(assembly.test) + return tuple(tests) diff --git a/src/sqlbuild/compiler/sql_test_glue/_helpers/__init__.py b/src/sqlbuild/compiler/sql_test_glue/_helpers/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/src/sqlbuild/compiler/sql_test_glue/_helpers/assembly.py b/src/sqlbuild/compiler/sql_test_glue/_helpers/assembly.py new file mode 100644 index 0000000000..0ed42636e2 --- /dev/null +++ b/src/sqlbuild/compiler/sql_test_glue/_helpers/assembly.py @@ -0,0 +1,133 @@ +"""Compiled SQL tests built from the facts the native assembly returns.""" + +from __future__ import annotations + +from sqlbuild.compiler.compile.constants import SQL_TEST_HELPER_REFERENCE_CODE +from sqlbuild.compiler.compile.models import ( + CompiledDirectLogicSqlTestPayload, + CompileDirectLogicSqlTestInputPayload, + CompiledModelSqlTestPayload, + CompiledObjectKey, + CompiledSqlTest, + CompiledSqlTestResource, + CompilerDiagnostic, + CompileSqlTestInput, +) +from sqlbuild.compiler.compile.types import ( + CompiledResourceType, + DiagnosticPhase, + DiagnosticSeverity, +) +from sqlbuild.compiler.sql_test_glue.models import NativeSqlTestAssembly +from sqlbuild.compiler.sql_test_glue.types import ( + NativeSqlTestDiagnosticRow, + NativeSqlTestFactsRow, +) +from sqlbuild.spec.contracts.models import SourceLocation + + +def native_sql_test_assembly( + *, test_input: CompileSqlTestInput, facts: NativeSqlTestFactsRow +) -> NativeSqlTestAssembly: + """The compiled test `_assemble_compiled_sql_test` builds, from natively assembled facts.""" + + name, scope_dep_rows, target_model_names, case_fingerprint, resource_name, diagnostics = facts + scope_deps: tuple[CompiledObjectKey, ...] = tuple( + CompiledObjectKey(resource_type=resource_type, name=dep_name) + for resource_type, dep_name in scope_dep_rows + ) + payload = test_input.payload + compiled_payload: CompiledModelSqlTestPayload | CompiledDirectLogicSqlTestPayload + tested_resources: tuple[CompiledSqlTestResource, ...] = () + expected_model_names: tuple[str, ...] = () + assertion_names: tuple[str, ...] = () + assertion_target_model_names: tuple[str, ...] = () + read_helper_names: tuple[str, ...] = () + reference_target_model_names: tuple[str, ...] = () + if isinstance(payload, CompileDirectLogicSqlTestInputPayload): + compiled_payload = CompiledDirectLogicSqlTestPayload( + mode=payload.mode, + helper_ctes=payload.helper_ctes, + actual_cte=payload.actual_cte, + expected_cte=payload.expected_cte, + tested_resource_names=payload.tested_resource_names, + ) + tested_resources = tuple( + CompiledSqlTestResource(kind=payload.mode, name=resource_name) + for resource_name in payload.tested_resource_names + ) + else: + compiled_payload = CompiledModelSqlTestPayload( + authored_ctes=payload.authored_ctes, + macro_mocks=payload.macro_mocks, + model_query_overrides={}, + mock_model_names=payload.mock_model_names, + mock_source_names=payload.mock_source_names, + mock_seed_names=payload.mock_seed_names, + mock_dbt_ref_names=payload.mock_dbt_ref_names, + mock_table_function_names=payload.mock_table_function_names, + expected_ctes=payload.expected_ctes, + expected_model_names=payload.expected_model_names, + assertion_ctes=payload.assertion_ctes, + assertion_names=payload.assertion_names, + ) + expected_model_names = payload.expected_model_names + assertion_names = payload.assertion_names + assertion_target_model_names = payload.assertion_target_model_names + read_helper_names = payload.read_helper_names + reference_target_model_names = payload.reference_target_model_names + return NativeSqlTestAssembly( + test=CompiledSqlTest( + key=CompiledObjectKey(resource_type=CompiledResourceType.SQL_TEST, name=name), + scope_deps=scope_deps, + name=name, + test_file=test_input.test_file, + test_block=test_input.test_block, + sql_body=test_input.sql_body, + mode=test_input.mode, + payload=compiled_payload, + source_path=test_input.test_file.relative_path, + ownership_root=test_input.test_file.ownership_root, + block_index=test_input.test_block.test_index, + explicit_name=test_input.test_block.name, + parent_name=test_input.parent_name, + case_name=test_input.case_name, + case_index=test_input.case_index, + case_fingerprint=case_fingerprint, + parameter_schema=test_input.parameter_schema, + parameter_values=test_input.parameter_values, + expected_model_names=expected_model_names, + assertion_names=assertion_names, + assertion_target_model_names=assertion_target_model_names, + read_helper_names=read_helper_names, + reference_target_model_names=reference_target_model_names, + target_model_names=tuple(target_model_names), + tested_resources=tested_resources, + ), + diagnostics=tuple( + _keyed_diagnostic(test_input=test_input, resource_name=resource_name, row=row) + for row in diagnostics + ), + ) + + +def _keyed_diagnostic( + *, test_input: CompileSqlTestInput, resource_name: str, row: NativeSqlTestDiagnosticRow +) -> tuple[tuple[str, ...], CompilerDiagnostic]: + line, column, end_line, end_column, message, help_text = row + path = test_input.test_file.relative_path + return ( + (SQL_TEST_HELPER_REFERENCE_CODE, path.as_posix(), f"{line:09d}:{column:09d}", message), + CompilerDiagnostic( + phase=DiagnosticPhase.COMPILE, + severity=DiagnosticSeverity.ERROR, + code=SQL_TEST_HELPER_REFERENCE_CODE, + message=message, + resource_type=CompiledResourceType.SQL_TEST, + resource_name=resource_name, + location=SourceLocation( + path=path, line=line, column=column, end_line=end_line, end_column=end_column + ), + help=help_text, + ), + ) diff --git a/src/sqlbuild/compiler/sql_test_glue/_helpers/deferrals.py b/src/sqlbuild/compiler/sql_test_glue/_helpers/deferrals.py new file mode 100644 index 0000000000..bf25fb8907 --- /dev/null +++ b/src/sqlbuild/compiler/sql_test_glue/_helpers/deferrals.py @@ -0,0 +1,24 @@ +"""Debug-only record of SQL tests the native assembly hands back to Python.""" + +from __future__ import annotations + +import json +import os +from pathlib import Path + +from sqlbuild.compiler.lineage.constants import ANALYSIS_DEFERRAL_RECORD_PREFIX +from sqlbuild.compiler.sql_analysis.constants import ANALYSIS_RECORD_DIR_ENV_VAR +from sqlbuild.compiler.sql_test_glue.constants import SQL_TEST_ASSEMBLY_DEFERRAL_SITE + + +def record_sql_test_assembly_deferral(*, kind: str) -> None: + """Append one `{"kind", "site"}` line when the analysis record directory is set.""" + + directory: str | None = os.environ.get(ANALYSIS_RECORD_DIR_ENV_VAR) + if not directory: + return + target: Path = Path(directory) + target.mkdir(parents=True, exist_ok=True) + record: str = json.dumps({"kind": kind, "site": SQL_TEST_ASSEMBLY_DEFERRAL_SITE}) + with open(target / f"{ANALYSIS_DEFERRAL_RECORD_PREFIX}{os.getpid()}.jsonl", "a") as handle: + _ = handle.write(record + "\n") diff --git a/src/sqlbuild/compiler/sql_test_glue/constants.py b/src/sqlbuild/compiler/sql_test_glue/constants.py index 727ece2d99..8c14439660 100644 --- a/src/sqlbuild/compiler/sql_test_glue/constants.py +++ b/src/sqlbuild/compiler/sql_test_glue/constants.py @@ -3,3 +3,4 @@ from __future__ import annotations CURSOR_INTRINSIC_NAMES: tuple[str, ...] = ("__cursor_start", "__cursor_end") +SQL_TEST_ASSEMBLY_DEFERRAL_SITE: str = "sql_test_assembly" diff --git a/src/sqlbuild/compiler/sql_test_glue/main/_assemble_native_sql_tests.py b/src/sqlbuild/compiler/sql_test_glue/main/_assemble_native_sql_tests.py index b732f71039..c4f6a144d2 100644 --- a/src/sqlbuild/compiler/sql_test_glue/main/_assemble_native_sql_tests.py +++ b/src/sqlbuild/compiler/sql_test_glue/main/_assemble_native_sql_tests.py @@ -2,12 +2,34 @@ from __future__ import annotations -from sqlbuild.compiler.compile.models import CompiledSqlTest, CompileProjectInputs +import sqlbuild._native as native_module +from sqlbuild.compiler.compile.models import CompileProjectInputs +from sqlbuild.compiler.sql_test_glue._helpers.assembly import native_sql_test_assembly +from sqlbuild.compiler.sql_test_glue._helpers.deferrals import record_sql_test_assembly_deferral +from sqlbuild.compiler.sql_test_glue.models import ( + NativeSqlTestAssembly, + NativeSqlTestAssemblyRequest, +) +from sqlbuild.compiler.sql_test_glue.types import NativeSqlTestAssemblyRow def assemble_native_sql_tests( *, inputs: CompileProjectInputs -) -> tuple[CompiledSqlTest, ...] | None: - """Return one compiled test per test input, or None where Python must assemble the tests.""" +) -> tuple[NativeSqlTestAssembly | None, ...]: + """Return one assembly per test input, or None for each test Python must assemble.""" - return None + rows: list[NativeSqlTestAssemblyRow] = native_module.assemble_compiled_sql_tests( + NativeSqlTestAssemblyRequest( + model_inputs=inputs.model_inputs, + test_inputs=inputs.test_inputs, + lexical_syntax=inputs.sql_lexical_syntax.native_mapping, + ) + ) + assemblies: list[NativeSqlTestAssembly | None] = [] + for test_input, (facts, deferral) in zip(inputs.test_inputs, rows, strict=True): + if facts is None: + record_sql_test_assembly_deferral(kind=str(deferral)) + assemblies.append(None) + continue + assemblies.append(native_sql_test_assembly(test_input=test_input, facts=facts)) + return tuple(assemblies) diff --git a/src/sqlbuild/compiler/sql_test_glue/models.py b/src/sqlbuild/compiler/sql_test_glue/models.py index 0883787198..69403cb491 100644 --- a/src/sqlbuild/compiler/sql_test_glue/models.py +++ b/src/sqlbuild/compiler/sql_test_glue/models.py @@ -1,10 +1,16 @@ -"""Requests the native SQL-test planning glue reads from compiled objects.""" +"""Requests and results of the native SQL-test glue, read from compiled objects.""" from __future__ import annotations from dataclasses import dataclass -from sqlbuild.compiler.compile.models import CompiledModel, CompiledSqlTest +from sqlbuild.compiler.compile.models import ( + CompiledModel, + CompiledSqlTest, + CompileModelInput, + CompilerDiagnostic, + CompileSqlTestInput, +) @dataclass(frozen=True) @@ -32,3 +38,20 @@ class NativeSqlTestChainRequest: models: tuple[CompiledModel, ...] tests: tuple[CompiledSqlTest, ...] lexical_syntax: dict[str, object] + + +@dataclass(frozen=True) +class NativeSqlTestAssemblyRequest: + """The project's model inputs and the SQL test inputs whose compiled facts to assemble.""" + + model_inputs: tuple[CompileModelInput, ...] + test_inputs: tuple[CompileSqlTestInput, ...] + lexical_syntax: dict[str, object] + + +@dataclass(frozen=True) +class NativeSqlTestAssembly: + """One natively assembled test and the keyed diagnostics to report before it.""" + + test: CompiledSqlTest + diagnostics: tuple[tuple[tuple[str, ...], CompilerDiagnostic], ...] diff --git a/src/sqlbuild/compiler/sql_test_glue/types.py b/src/sqlbuild/compiler/sql_test_glue/types.py index 9a3fdf65c4..3e995450c1 100644 --- a/src/sqlbuild/compiler/sql_test_glue/types.py +++ b/src/sqlbuild/compiler/sql_test_glue/types.py @@ -1,4 +1,4 @@ -"""Row shapes the native SQL-test planning glue returns.""" +"""Row shapes the native SQL-test glue returns.""" from __future__ import annotations @@ -22,3 +22,13 @@ list[NativePlanWarningRow], list[str], ] +type NativeSqlTestDiagnosticRow = tuple[int, int, int, int, str, str] +type NativeSqlTestFactsRow = tuple[ + str, + list[tuple[str, str]], + list[str], + str | None, + str, + list[NativeSqlTestDiagnosticRow], +] +type NativeSqlTestAssemblyRow = tuple[NativeSqlTestFactsRow | None, str | None] diff --git a/tests/e2e/src/sqlbuild/cli/commands/main/compile/test_native_analysis_seams.py b/tests/e2e/src/sqlbuild/cli/commands/main/compile/test_native_analysis_seams.py index bd27e5c2d5..b2d96bc1b3 100644 --- a/tests/e2e/src/sqlbuild/cli/commands/main/compile/test_native_analysis_seams.py +++ b/tests/e2e/src/sqlbuild/cli/commands/main/compile/test_native_analysis_seams.py @@ -13,6 +13,7 @@ from sqlbuild.compiler.lineage.models import ProjectColumnLineage from sqlbuild.compiler.planner.models import NativeSqlTestArtifact from sqlbuild.compiler.project_assembly.models import NativeProjectResources +from sqlbuild.compiler.sql_test_glue.models import NativeSqlTestAssembly from tests.e2e.src.sqlbuild.cli.commands.main.compile._test_types import ( NativeAnalysisSeamTestCase, ) @@ -36,7 +37,7 @@ "assemble_native_project_resources": [ANY], "infer_native_expression_source_shapes": [ANY], "analyze_native_model_sql": [ANY], - "assemble_native_sql_tests": [None], + "assemble_native_sql_tests": [ANY], "complete_native_semantic_diagnostics": [ANY], "evaluate_native_model_contracts": [ANY], "native_promotion_conflict_diagnostics": [()], @@ -83,6 +84,10 @@ def test_given_project_when_compiling_with_each_engine_then_native_seams_answer_ assert isinstance(preview_seams["assemble_native_project_resources"][0], NativeProjectResources) assert isinstance(preview_seams["analyze_native_model_sql"][0], dict) assert isinstance(preview_seams["infer_native_expression_source_shapes"][0], tuple) + assembled_tests: object = preview_seams["assemble_native_sql_tests"][0] + assert isinstance(assembled_tests, tuple) + assert assembled_tests + assert all(isinstance(assembled, NativeSqlTestAssembly) for assembled in assembled_tests) planned_artifacts: object = preview_seams["plan_native_sql_test_artifacts"][0] assert isinstance(planned_artifacts, tuple) assert planned_artifacts diff --git a/tests/integration/src/sqlbuild/compiler/sql_test_glue/_test_types.py b/tests/integration/src/sqlbuild/compiler/sql_test_glue/_test_types.py index 1ead44a814..4916b98965 100644 --- a/tests/integration/src/sqlbuild/compiler/sql_test_glue/_test_types.py +++ b/tests/integration/src/sqlbuild/compiler/sql_test_glue/_test_types.py @@ -22,3 +22,18 @@ class GeneratedSqlTestPlanningParityTestCase: expected_native_raised_kinds: frozenset[str] expected_minimum_answered_batches: int expected_minimum_raised_outcomes: int + + +@dataclass(frozen=True) +class GeneratedSqlTestAssemblyParityTestCase: + """Seeded SQL-test projects native and Python assembly must compile identically.""" + + description: str + seed: int + count: int + test_count: int + shape: SqlTestCorpusShape + expected_minimum_native_assembled: int + expected_minimum_native_with_diagnostics: int + expected_minimum_native_case_fingerprints: int + expected_minimum_deferred_macro_mocks: int diff --git a/tests/integration/src/sqlbuild/compiler/sql_test_glue/helpers.py b/tests/integration/src/sqlbuild/compiler/sql_test_glue/helpers.py index cfbadc548b..610bfc9731 100644 --- a/tests/integration/src/sqlbuild/compiler/sql_test_glue/helpers.py +++ b/tests/integration/src/sqlbuild/compiler/sql_test_glue/helpers.py @@ -15,6 +15,7 @@ import sqlbuild._native as native_module from sqlbuild.adapter.contract.classes.base_adapter import BaseAdapter from sqlbuild.adapters.duckdb.classes.duckdb_adapter import DuckDbAdapter +from sqlbuild.compiler.compile._helpers.native_stages import sql_tests as sql_test_stage from sqlbuild.compiler.compile.models import ( CompiledModelSqlTestPayload, CompiledProject, @@ -27,10 +28,15 @@ from sqlbuild.compiler.planner._helpers.sql_tests import native_planning from sqlbuild.compiler.planner.models import NativeSqlTestArtifact from sqlbuild.compiler.sql_test_glue.models import ( + NativeSqlTestAssemblyRequest, NativeSqlTestChainRequest, NativeSqlTestPlanningRequest, ) -from sqlbuild.compiler.sql_test_glue.types import NativeSqlTestPlanRow +from sqlbuild.compiler.sql_test_glue.types import ( + NativeSqlTestAssemblyRow, + NativeSqlTestFactsRow, + NativeSqlTestPlanRow, +) @dataclass(frozen=True) @@ -284,13 +290,121 @@ def _model_test(*, rng: random.Random, index: int, shape: SqlTestCorpusShape) -> return f'TEST (name "generated_{index}"{window});\n\nWITH\n{body}\nSELECT 1\n' -def compiled_project(*, project_dir: Path, files: dict[str, str]) -> CompiledProject: - """Write and compile a project, keeping any compile diagnostics on it.""" +_CASE_VALUES: dict[str, tuple[str, ...]] = { + "string": ('"open"', '"O\\\'Brien"', '"caf\u00e9 \u2603"', '""', '"a\\\\b"', "null"), + "integer": ("0", "-7", "8", "9223372036854775807", "null"), + "boolean": ("true", "false", "null"), + "float": ("1.25", "-0.5", "0.1", "100.0", "0.0", "null"), + "decimal": ( + '"2.4700"', + '"-0.000"', + '"3.00"', + '"1E+5"', + '"0.0001230"', + '"12345678901234567890123456789.5"', + "null", + ), +} +_REFERENCING_HELPERS: tuple[str, ...] = ( + 'SELECT order_id, amount FROM __source("raw_orders")', + 'SELECT order_id, amount FROM __ref("stg_orders")', + 'SELECT region, label FROM __seed("regions")', + 'SELECT order_id, amount FROM __table_fn("table_fn__customer_orders")(1)', + "SELECT 1 AS order_id, 2 AS amount", +) +_MOCK_READS: tuple[str, ...] = ( + "SELECT * FROM {helper}", + "SELECT * FROM {upper_helper}", + "SELECT * FROM {via}", + "WITH {helper} AS (SELECT 1 AS order_id) SELECT * FROM {helper}", + "SELECT * FROM main.{helper}", + "SELECT * FROM {helper} WHERE order_id ==> 1", + "SELECT 1 AS order_id", +) +_ASSEMBLY_TARGETS: tuple[str, ...] = ("stg_orders", "int_orders", "fct_regions") + + +def generated_sql_test_assembly_files( + *, rng: random.Random, test_count: int, shape: SqlTestCorpusShape +) -> dict[str, str]: + """The planning projects plus typed parameter cases and mocks reading referencing helpers.""" + + files: dict[str, str] = generated_sql_test_files(rng=rng, test_count=test_count, shape=shape) + files.update( + (f"tests/unit/test_cases_{index}.sql", _case_test(rng=rng, index=index)) + for index in range(test_count) + ) + files.update( + (f"tests/unit/test_helper_mock_{index}.sql", _helper_mock_test(rng=rng, index=index)) + for index in range(test_count) + ) + return files + + +def _case_test(*, rng: random.Random, index: int) -> str: + """A parameterized test over random typed parameters and up to three random cases.""" + + kinds: list[str] = rng.sample(sorted(_CASE_VALUES), k=rng.randint(1, 3)) + parameters: str = ", ".join(f"p_{kind} (type {kind}, nullable true)" for kind in kinds) + cases: str = ", ".join( + f"case_{case} ({_case_values(rng=rng, kinds=kinds)})" for case in range(rng.randint(1, 3)) + ) + target: str = rng.choice(_ASSEMBLY_TARGETS) + used: str = ", ".join(f'@param("p_{kind}") AS p_{kind}' for kind in kinds) + return ( + f'TEST (name "cases_{index}", parameters ({parameters}), cases ({cases}));\n\n' + f"WITH\n{_UPSTREAM[target][0]} AS (SELECT 1 AS order_id, {used}),\n" + f'__assert__{target}_rows AS (SELECT * FROM __ref("{target}") WHERE 1 = 0)\nSELECT 1\n' + ) + + +def _case_values(*, rng: random.Random, kinds: list[str]) -> str: + return ", ".join(f"p_{kind} {rng.choice(_CASE_VALUES[kind])}" for kind in kinds) + + +def _helper_mock_test(*, rng: random.Random, index: int) -> str: + """A model test whose mocks read helpers, some of which call a reference.""" + + helper: str = f"source_rows_{index}" + via: str = f"via_{index}" + target: str = rng.choice(_ASSEMBLY_TARGETS) + mocks: list[str] = rng.sample(list(_UPSTREAM[target]), k=rng.randint(1, len(_UPSTREAM[target]))) + reads: dict[str, str] = {"helper": helper, "upper_helper": helper.upper(), "via": via} + mock_ctes: list[str] = [ + f"{mock} AS (\n {rng.choice(_MOCK_READS).format(**reads)}\n)" for mock in mocks + ] + check: str = rng.choice( + ( + f"__expected__{target} AS (SELECT order_id FROM {helper})", + f'__assert__{target}_rows AS (SELECT * FROM __ref("{target}") WHERE 1 = 0)', + f"__expected__{target} AS (SELECT 1 AS order_id)", + ) + ) + comment: str = rng.choice(("", "", "-- r\u00e9sum\u00e9\n")) + ctes: str = ",\n".join( + ( + f"{helper} AS (\n {rng.choice(_REFERENCING_HELPERS)}\n)", + f"{via} AS (SELECT * FROM {helper})", + *mock_ctes, + check, + ) + ) + return f'TEST (name "helper_mock_{index}");\n\nWITH\n{comment}{ctes}\nSELECT 1\n' + + +def write_project(*, project_dir: Path, files: dict[str, str]) -> None: + """Write a project's files below `project_dir`.""" for relative_path, contents in files.items(): path: Path = project_dir / relative_path path.parent.mkdir(parents=True, exist_ok=True) _ = path.write_text(contents, encoding="utf-8") + + +def compiled_project(*, project_dir: Path, files: dict[str, str]) -> CompiledProject: + """Write and compile a project, keeping any compile diagnostics on it.""" + + write_project(project_dir=project_dir, files=files) return build_project_graph( discovered_inputs=discover_project_inputs(project_dir=project_dir), adapter=DuckDbAdapter(), @@ -330,6 +444,52 @@ def use_sql_test_glue(*, monkeypatch: pytest.MonkeyPatch, enabled: bool) -> None ) +def assembly_outcome( + *, project_dir: Path, monkeypatch: pytest.MonkeyPatch, native: bool +) -> PlanningCallOutcome: + """The compiled SQL tests and diagnostics with native or Python assembly, or what it raises.""" + + monkeypatch.setattr( + sql_test_stage, + "native_stage_enabled", + lambda stage: native and stage is NativeStage.SQL_TEST_GLUE, + ) + return _outcome(lambda: _compiled_tests_and_diagnostics(project_dir=project_dir)) + + +def _compiled_tests_and_diagnostics(*, project_dir: Path) -> tuple[object, object]: + project: CompiledProject = build_project_graph( + discovered_inputs=discover_project_inputs(project_dir=project_dir), + adapter=DuckDbAdapter(), + ).project + return project.sql_tests, project.diagnostics + + +def record_native_assemblies(*, monkeypatch: pytest.MonkeyPatch) -> Counter[str]: + """Count tests the native assembly itself assembles, with diagnostics, cases, or defers. + + `native_assembled` counts only rows carrying native facts; a deferred test counts under + `deferred_` and is assembled by Python, so it never counts as native work. + """ + + answers: Counter[str] = Counter() + assemble: Callable[[NativeSqlTestAssemblyRequest], list[NativeSqlTestAssemblyRow]] = ( + native_module.assemble_compiled_sql_tests + ) + + def counted(request: NativeSqlTestAssemblyRequest) -> list[NativeSqlTestAssemblyRow]: + rows: list[NativeSqlTestAssemblyRow] = assemble(request) + facts: list[NativeSqlTestFactsRow] = [row[0] for row in rows if row[0] is not None] + answers["native_assembled"] += len(facts) + answers["native_with_diagnostics"] += sum(bool(row[5]) for row in facts) + answers["native_case_fingerprints"] += sum(row[3] is not None for row in facts) + answers.update(f"deferred_{row[1]}" for row in rows if row[1] is not None) + return rows + + monkeypatch.setattr(native_module, "assemble_compiled_sql_tests", counted) + return answers + + def planning_outcome( *, project: CompiledProject, diff --git a/tests/integration/src/sqlbuild/compiler/sql_test_glue/test_native_sql_test_planning_glue.py b/tests/integration/src/sqlbuild/compiler/sql_test_glue/test_native_sql_test_planning_glue.py index c24cf34efd..48ac5b75ac 100644 --- a/tests/integration/src/sqlbuild/compiler/sql_test_glue/test_native_sql_test_planning_glue.py +++ b/tests/integration/src/sqlbuild/compiler/sql_test_glue/test_native_sql_test_planning_glue.py @@ -1,4 +1,4 @@ -"""SQL tests planned natively from compiled objects equal the JSON-request plans, test by test.""" +"""SQL tests assembled and planned natively equal Python's assembly and the JSON-request plans.""" from __future__ import annotations @@ -17,6 +17,7 @@ from sqlbuild.compiler.compile.models import CompiledProject, CompiledSqlTest from tests.integration.src.sqlbuild.compiler.helpers import mismatches from tests.integration.src.sqlbuild.compiler.sql_test_glue._test_types import ( + GeneratedSqlTestAssemblyParityTestCase, GeneratedSqlTestPlanningParityTestCase, ) from tests.integration.src.sqlbuild.compiler.sql_test_glue.helpers import ( @@ -26,15 +27,19 @@ PlanningCallOutcome, SqlTestCorpusShape, artifact_outcome, + assembly_outcome, chain_outcome, compiled_project, + generated_sql_test_assembly_files, generated_sql_test_files, native_raised_kinds, outcome_kind, planning_outcome, record_native_answers, + record_native_assemblies, use_sql_test_glue, with_unmocked_assertion, + write_project, ) _ADAPTERS: dict[str, BaseAdapter] = { @@ -171,3 +176,64 @@ def test_given_generated_sql_tests_when_planning_natively_then_plans_match_the_j outcomes["batch_answered"] >= test_case.expected_minimum_answered_batches, outcomes["raised"] >= test_case.expected_minimum_raised_outcomes, ) == ([], True, True, True, True, True, True, True), (native_answers, outcomes) + + +@pytest.mark.parametrize( + "test_case", + [ + GeneratedSqlTestAssemblyParityTestCase( + description="model, direct, parameterized and mock-reads-helper tests", + seed=20261010, + count=4, + test_count=12, + shape=SqlTestCorpusShape( + windows=(NO_WINDOW, VALID_WINDOW, INVERTED_WINDOW), + stray_window_share=0.1, + helper_redefinition_share=0.15, + assertion_share=0.2, + unflattenable_assertion_share=0.1, + ), + expected_minimum_native_assembled=140, + expected_minimum_native_with_diagnostics=18, + expected_minimum_native_case_fingerprints=65, + expected_minimum_deferred_macro_mocks=4, + ) + ], + ids=lambda case: case.description, +) +def test_given_generated_sql_tests_when_assembling_natively_then_compiled_tests_match_python( + test_case: GeneratedSqlTestAssemblyParityTestCase, + tmp_path: Path, + monkeypatch: pytest.MonkeyPatch, +) -> None: + rng: random.Random = random.Random(test_case.seed) + native_assemblies: Counter[str] = record_native_assemblies(monkeypatch=monkeypatch) + differences: list[tuple[object, object, object]] = [] + outcomes: Counter[str] = Counter() + for index in range(test_case.count): + project_dir: Path = tmp_path / f"project_{index}" + write_project( + project_dir=project_dir, + files=generated_sql_test_assembly_files( + rng=rng, test_count=test_case.test_count, shape=test_case.shape + ), + ) + expected: PlanningCallOutcome = assembly_outcome( + project_dir=project_dir, monkeypatch=monkeypatch, native=False + ) + actual: PlanningCallOutcome = assembly_outcome( + project_dir=project_dir, monkeypatch=monkeypatch, native=True + ) + differences.extend(mismatches(inputs=[index], expected=[expected], actual=[actual])) + outcomes[outcome_kind(actual)] += 1 + + assert ( + differences, + native_assemblies["native_assembled"] >= test_case.expected_minimum_native_assembled, + native_assemblies["native_with_diagnostics"] + >= test_case.expected_minimum_native_with_diagnostics, + native_assemblies["native_case_fingerprints"] + >= test_case.expected_minimum_native_case_fingerprints, + native_assemblies["deferred_macro_mocks"] + >= test_case.expected_minimum_deferred_macro_mocks, + ) == ([], True, True, True, True), (native_assemblies, outcomes) From 4182487e7439bace687c1d8aa9ae48122df67e11 Mon Sep 17 00:00:00 2001 From: Kevin Longe <273747162+kvlonge@users.noreply.github.com> Date: Fri, 9 Oct 2026 09:50:16 +0100 Subject: [PATCH 10/31] refactor(native): bundle SQL test assembly inputs and count only consumed native assemblies --- .../compiler/_helpers/sql_tests/assembly.rs | 14 +- .../_helpers/sql_tests/macro_call_names.rs | 4 +- .../compiler/_helpers/sql_tests/mock_reads.rs | 233 ++++++++++-------- .../sqlbuild-analysis/src/compiler/models.rs | 3 +- .../tests/test_sql_test_helper_references.rs | 216 +++++++++++++++- .../src/compiler/tests/test_types.rs | 6 + .../_helpers/analysis_corpus/sql_test_glue.py | 21 +- .../sql_test_glue/_helpers/assembly.py | 26 +- .../main/_assemble_native_sql_tests.py | 12 +- .../compiler/sql_test_glue/helpers.py | 36 ++- .../test_native_sql_test_planning_glue.py | 8 +- 11 files changed, 440 insertions(+), 139 deletions(-) diff --git a/crates/sqlbuild-analysis/src/compiler/_helpers/sql_tests/assembly.rs b/crates/sqlbuild-analysis/src/compiler/_helpers/sql_tests/assembly.rs index 0e8866fc2e..6836d3cf0f 100644 --- a/crates/sqlbuild-analysis/src/compiler/_helpers/sql_tests/assembly.rs +++ b/crates/sqlbuild-analysis/src/compiler/_helpers/sql_tests/assembly.rs @@ -5,10 +5,12 @@ use sqlbuild_sqltext::sql_scan::models::LexicalSyntax; use crate::compiler::_helpers::sql_tests::case_identity::case_fingerprint; use crate::compiler::_helpers::sql_tests::macro_call_names::macro_call_names; -use crate::compiler::_helpers::sql_tests::mock_reads::mock_reading_helper_diagnostics; +use crate::compiler::_helpers::sql_tests::mock_reads::{ + MockReadsRequest, mock_reading_helper_diagnostics, +}; use crate::compiler::models::{ AssembledSqlTestFacts, SqlTestAssemblyBatch, SqlTestAssemblyDeferral, SqlTestAssemblyModel, - SqlTestAssemblyOutcome, SqlTestAssemblyPayload, SqlTestAssemblyTest, + SqlTestAssemblyOutcome, SqlTestAssemblyPayload, SqlTestAssemblyTest, SqlTestHelperDiagnostic, }; const MODEL_RESOURCE: &str = "model"; @@ -63,7 +65,7 @@ fn assemble( let syntax = project.syntax; let mut target_model_names: Vec = Vec::new(); let mut tested_resources: Vec<(String, String)> = Vec::new(); - let mut diagnostics = Vec::new(); + let mut diagnostics: Vec = Vec::new(); let scope_deps: Vec<(&'static str, String)> = match &test.payload { SqlTestAssemblyPayload::Direct { mode, @@ -89,13 +91,13 @@ fn assemble( target_model_names.push(name.clone()); } } - diagnostics = mock_reading_helper_diagnostics( + diagnostics = mock_reading_helper_diagnostics(&MockReadsRequest { test, payload, models, - &target_model_names, + target_model_names: &target_model_names, syntax, - )?; + })?; target_model_names .iter() .map(|name| (MODEL_RESOURCE, name.clone())) diff --git a/crates/sqlbuild-analysis/src/compiler/_helpers/sql_tests/macro_call_names.rs b/crates/sqlbuild-analysis/src/compiler/_helpers/sql_tests/macro_call_names.rs index bb695db494..38c0421c61 100644 --- a/crates/sqlbuild-analysis/src/compiler/_helpers/sql_tests/macro_call_names.rs +++ b/crates/sqlbuild-analysis/src/compiler/_helpers/sql_tests/macro_call_names.rs @@ -25,7 +25,7 @@ pub(crate) fn macro_call_names(sql: &str) -> Scan> { Ok(None) => {} Err(_) => return Err(SqlTestAssemblyDeferral::MacroCallScan), } - if bytes[index] != b'@' || !is_call_start(bytes, index)? { + if bytes[index] != b'@' || !macro_call_at(bytes, index)? { index += 1; continue; } @@ -47,7 +47,7 @@ pub(crate) fn macro_call_names(sql: &str) -> Scan> { } /// Python's `_is_macro_call_start`, which skips whitespace after each name character. -fn is_call_start(bytes: &[u8], at: usize) -> Scan { +fn macro_call_at(bytes: &[u8], at: usize) -> Scan { let Some(&first) = bytes.get(at + 1) else { return Ok(false); }; diff --git a/crates/sqlbuild-analysis/src/compiler/_helpers/sql_tests/mock_reads.rs b/crates/sqlbuild-analysis/src/compiler/_helpers/sql_tests/mock_reads.rs index f2a2807103..f381916286 100644 --- a/crates/sqlbuild-analysis/src/compiler/_helpers/sql_tests/mock_reads.rs +++ b/crates/sqlbuild-analysis/src/compiler/_helpers/sql_tests/mock_reads.rs @@ -1,9 +1,10 @@ -//! Python's `report_mocks_reading_referencing_helpers`: mocks a test reads that read a helper -//! calling a reference, which cannot resolve because mocks are defined before the models run. +//! Python's `report_mocks_reading_referencing_helpers`: P013s for mocks reading referencing helpers. use std::collections::HashSet; -use polyglot_sql::{Dialect, DialectType, Expression, ExpressionWalk}; +use polyglot_sql::{ + ComplexityGuardOptions, Dialect, DialectType, Expression, ExpressionWalk, ParseOptions, +}; use serde_json::Value; use sqlbuild_sqltext::sql_references::main::extract_sql_references::extract_sql_references; use sqlbuild_sqltext::sql_references::models::{ReferenceExtraction, SqlReference}; @@ -14,7 +15,6 @@ use crate::compiler::models::{ SqlTestAssemblyDeferral, SqlTestAssemblyModel, SqlTestAssemblyModelPayload, SqlTestAssemblyReference, SqlTestAssemblyTest, SqlTestCte, SqlTestHelperDiagnostic, }; -use crate::lineage::_helpers::parsed_lineage::proxy_parse_options; const REF_KIND: &str = "ref"; const TABLE_FUNCTION_KIND: &str = "table_fn"; @@ -28,6 +28,8 @@ const RELATION_KINDS: [&str; 5] = [ ]; /// Payload nesting past which the parsed tree is left to Python's `to_dict` walk. const MAX_TREE_DEPTH: usize = 400; +/// The function call depth SQLBuild's Polyglot proxy allows `parse_one`. +const MAX_FUNCTION_CALL_DEPTH: usize = 512; type Deferrable = Result; @@ -39,26 +41,72 @@ struct Reference { package: Option, } +/// One model test whose mocks may read referencing helpers, with the project's models. +pub(crate) struct MockReadsRequest<'a> { + pub(crate) test: &'a SqlTestAssemblyTest, + pub(crate) payload: &'a SqlTestAssemblyModelPayload, + pub(crate) models: &'a [SqlTestAssemblyModel], + pub(crate) target_model_names: &'a [String], + pub(crate) syntax: &'a LexicalSyntax, +} + +/// A Python dict by string key: a repeated key keeps its first position and takes the new value. +struct OrderedEntries { + entries: Vec<(String, T)>, +} + +impl OrderedEntries { + fn new() -> Self { + Self { + entries: Vec::new(), + } + } + + fn assign(&mut self, key: String, value: T) { + match self + .entries + .iter_mut() + .find(|(candidate, _)| *candidate == key) + { + Some(entry) => entry.1 = value, + None => self.entries.push((key, value)), + } + } + + fn get(&self, key: &str) -> Option<&T> { + self.entries + .iter() + .find(|(candidate, _)| candidate == key) + .map(|(_, value)| value) + } + + fn keys(&self) -> Vec { + self.entries.iter().map(|(key, _)| key.clone()).collect() + } +} + /// Python's `SqlTestCteGraph`, keyed by case-folded CTE name in first-definition order. struct CteGraph<'a> { - ctes: Vec<(String, &'a SqlTestCte)>, - reads: Vec<(String, Vec)>, + ctes: OrderedEntries<&'a SqlTestCte>, + reads: OrderedEntries>, reader_reads: Vec, } +/// A located span as Python's `reference_call_location` reports it. +struct Span { + line: usize, + column: usize, + end_line: usize, + end_column: usize, +} + impl CteGraph<'_> { fn cte(&self, key: &str) -> Option<&SqlTestCte> { - self.ctes - .iter() - .find(|(candidate, _)| candidate == key) - .map(|(_, cte)| *cte) + self.ctes.get(key).copied() } fn reads(&self, key: &str) -> &[String] { - self.reads - .iter() - .find(|(candidate, _)| candidate == key) - .map_or(&[], |(_, reads)| reads.as_slice()) + self.reads.get(key).map_or(&[], Vec::as_slice) } /// Python's `reachable_cte_keys`: breadth first, roots included, in visit order. @@ -92,77 +140,74 @@ impl CteGraph<'_> { /// The P013 diagnostics Python reports for one model test, in report order. pub(crate) fn mock_reading_helper_diagnostics( - test: &SqlTestAssemblyTest, - payload: &SqlTestAssemblyModelPayload, - models: &[SqlTestAssemblyModel], - target_model_names: &[String], - syntax: &LexicalSyntax, + request: &MockReadsRequest<'_>, ) -> Deferrable> { - let mut referencing: Vec<(String, Reference)> = Vec::new(); + let payload = request.payload; + let mut referencing: OrderedEntries = OrderedEntries::new(); for cte in &payload.authored_ctes { if is_mock_name(&cte.name) { continue; } - let first = references(&cte.sql_body, syntax)? + let first = references(&cte.sql_body, request.syntax)? .into_iter() .find(|reference| RELATION_KINDS.contains(&reference.kind.as_str())); if let Some(reference) = first { - insert(&mut referencing, ascii_fold(&cte.name)?, reference); + referencing.assign(ascii_fold(&cte.name)?, reference); } } - if referencing.is_empty() { + if referencing.entries.is_empty() { return Ok(Vec::new()); } - require_ascii(test, payload)?; + require_ascii(request.test, payload)?; let readers: Vec<&SqlTestCte> = payload .expected_ctes .iter() .chain(&payload.assertion_ctes) .collect(); let graph = cte_graph(&payload.authored_ctes, &readers)?; + let mut scanned: Vec<&str> = readers.iter().map(|cte| cte.sql_body.as_str()).collect(); + for key in read_helper_keys(&graph) { + if let Some(cte) = graph.cte(&key) { + scanned.push(cte.sql_body.as_str()); + } + } let mut called_mocks: Vec = Vec::new(); - let read_helpers = read_helper_keys(&graph); - let helper_sqls = read_helpers - .iter() - .filter_map(|key| graph.cte(key).map(|cte| cte.sql_body.as_str())); - for sql in readers - .iter() - .map(|cte| cte.sql_body.as_str()) - .chain(helper_sqls) - { - for reference in references(sql, syntax)? { + for sql in scanned { + for reference in references(sql, request.syntax)? { called_mocks.extend(mock_keys(&reference)?); } } - let used = used_mock_keys( - &graph, - &called_mocks, - models, - target_model_names, - &payload.mock_model_names, - )?; + let used = used_mock_keys(request, &graph, &called_mocks)?; let mut diagnostics: Vec = Vec::new(); for mock_key in used { - let reachable = graph.reachable(graph.reads(&mock_key)); - let Some((helper_key, reference)) = reachable.iter().find_map(|key| { - referencing - .iter() - .find(|(candidate, _)| candidate == key) - .map(|(_, reference)| (key, reference)) - }) else { + let mut helper: Option<(String, &Reference)> = None; + for key in graph.reachable(graph.reads(&mock_key)) { + if let Some(reference) = referencing.get(&key) { + helper = Some((key, reference)); + break; + } + } + let Some((helper_key, reference)) = helper else { continue; }; - let (Some(mock), Some(helper)) = (graph.cte(&mock_key), graph.cte(helper_key)) else { + let (Some(mock), Some(helper)) = (graph.cte(&mock_key), graph.cte(&helper_key)) else { continue; }; let call = reference_call(reference); - let message = format!( - "SQL test mock '{}' reads helper CTE '{}', which calls {call}; mocks and fixtures are \ - defined before the models the test runs, so the helper cannot be resolved for them", - mock.name, helper.name - ); - let help = mock_reference_help(&mock.name, &call, reference); - diagnostics.push(located(test, &helper.name, &call, message, help)); + let span = located(request.test, &helper.name, &call); + diagnostics.push(SqlTestHelperDiagnostic { + line: span.line, + column: span.column, + end_line: span.end_line, + end_column: span.end_column, + message: format!( + "SQL test mock '{}' reads helper CTE '{}', which calls {call}; mocks and \ + fixtures are defined before the models the test runs, so the helper cannot be \ + resolved for them", + mock.name, helper.name + ), + help: mock_reference_help(&mock.name, &call, reference), + }); } Ok(diagnostics) } @@ -173,14 +218,6 @@ fn is_mock_name(name: &str) -> bool { .any(|prefix| name.starts_with(prefix)) } -/// Python dict assignment: a repeated key keeps its first position and takes the new value. -fn insert(entries: &mut Vec<(String, T)>, key: String, value: T) { - match entries.iter_mut().find(|(candidate, _)| *candidate == key) { - Some(entry) => entry.1 = value, - None => entries.push((key, value)), - } -} - /// Python's `str.casefold`, exact only on ASCII text. fn ascii_fold(text: &str) -> Deferrable { if text.is_ascii() { @@ -240,19 +277,19 @@ fn model_reference(reference: &SqlTestAssemblyReference) -> Reference { /// Python's `sql_test_cte_graph`. fn cte_graph<'a>(authored: &'a [SqlTestCte], readers: &[&SqlTestCte]) -> Deferrable> { - let mut ctes: Vec<(String, &'a SqlTestCte)> = Vec::new(); + let mut ctes: OrderedEntries<&'a SqlTestCte> = OrderedEntries::new(); for cte in authored { - insert(&mut ctes, ascii_fold(&cte.name)?, cte); + ctes.assign(ascii_fold(&cte.name)?, cte); } - let keys: Vec = ctes.iter().map(|(key, _)| key.clone()).collect(); - let mut reads: Vec<(String, Vec)> = Vec::new(); - for (key, cte) in &ctes { + let keys: Vec = ctes.keys(); + let mut reads: OrderedEntries> = OrderedEntries::new(); + for (key, cte) in &ctes.entries { let others: Vec = keys.iter().filter(|other| *other != key).cloned().collect(); let cte_reads = match parsed_reads(&cte.sql_body, &others)? { Some(parsed) => parsed, None => token_reads(&cte.sql_body, &others), }; - reads.push((key.clone(), cte_reads)); + reads.assign(key.clone(), cte_reads); } let mut reader_reads: Vec = Vec::new(); for reader in readers { @@ -279,9 +316,7 @@ fn parsed_reads(sql: &str, keys: &[String]) -> Deferrable>> { if !keys.iter().any(|key| folded.contains(key.as_str())) { return Ok(Some(Vec::new())); } - let Ok(options) = proxy_parse_options() else { - return Err(SqlTestAssemblyDeferral::UnreadableTree); - }; + let options = proxy_parse_options()?; let Ok(mut statements) = Dialect::get(DialectType::Generic).parse_with_options(sql, &options) else { return Ok(None); @@ -307,6 +342,17 @@ fn parsed_reads(sql: &str, keys: &[String]) -> Deferrable>> { Ok(Some(reads)) } +/// The parse options SQLBuild's Polyglot proxy gives `parse_one`. +fn proxy_parse_options() -> Deferrable { + let guard: ComplexityGuardOptions = serde_json::from_value(serde_json::json!({ + "maxFunctionCallDepth": MAX_FUNCTION_CALL_DEPTH, + })) + .map_err(|_| SqlTestAssemblyDeferral::UnreadableTree)?; + Ok(ParseOptions { + complexity_guard: Some(guard), + }) +} + /// Python's `_defined_cte_keys` over the serialized tree Python's `to_dict` returns. fn defined_cte_keys(parsed: &Expression) -> Deferrable> { let tree = serde_json::to_value(parsed).map_err(|_| SqlTestAssemblyDeferral::UnreadableTree)?; @@ -395,12 +441,11 @@ fn mock_keys(reference: &Reference) -> Deferrable> { /// Python's `_used_mock_keys`. fn used_mock_keys( + request: &MockReadsRequest<'_>, graph: &CteGraph<'_>, called_mocks: &[String], - models: &[SqlTestAssemblyModel], - target_model_names: &[String], - mock_model_names: &[String], ) -> Deferrable> { + let mock_model_names = &request.payload.mock_model_names; let mut used: Vec = graph .reachable(&graph.reader_reads) .into_iter() @@ -416,15 +461,12 @@ fn used_mock_keys( used.push(key.clone()); } } - let mut model_references: Vec<(String, &[SqlTestAssemblyReference])> = Vec::new(); - for model in models { - insert( - &mut model_references, - model.name.clone(), - model.references.as_slice(), - ); + let mut model_references: OrderedEntries<&[SqlTestAssemblyReference]> = OrderedEntries::new(); + for model in request.models { + model_references.assign(model.name.clone(), model.references.as_slice()); } - let mut pending: Vec = target_model_names + let mut pending: Vec = request + .target_model_names .iter() .filter(|name| !mock_model_names.contains(name)) .cloned() @@ -434,10 +476,10 @@ fn used_mock_keys( while cursor < pending.len() { let model_name = pending[cursor].clone(); cursor += 1; - let references = model_references - .iter() - .find(|(name, _)| *name == model_name) - .map_or(&[][..], |(_, references)| *references); + let references: &[SqlTestAssemblyReference] = model_references + .get(&model_name) + .copied() + .unwrap_or_default(); for reference in references { let reference = model_reference(reference); for mock_key in mock_keys(&reference)? { @@ -487,13 +529,7 @@ fn mock_reference_help(mock_name: &str, call: &str, reference: &Reference) -> St } /// Python's `sql_test_cte_location` and `reference_call_location` on ASCII contents. -fn located( - test: &SqlTestAssemblyTest, - cte_name: &str, - call: &str, - message: String, - help: String, -) -> SqlTestHelperDiagnostic { +fn located(test: &SqlTestAssemblyTest, cte_name: &str, call: &str) -> Span { let contents = test.contents.as_str(); let block_offset = contents.find(test.block_sql.as_str()).unwrap_or(0); let (start, length) = match cte_header(contents, cte_name, block_offset) { @@ -501,16 +537,13 @@ fn located( .map_or((header_start, cte_name.len()), |found| (found, call.len())), None => (block_offset, 0), }; - let end = start + length; let (line, column) = line_and_column(contents, start); - let (end_line, end_column) = line_and_column(contents, end); - SqlTestHelperDiagnostic { + let (end_line, end_column) = line_and_column(contents, start + length); + Span { line, column, end_line, end_column, - message, - help, } } diff --git a/crates/sqlbuild-analysis/src/compiler/models.rs b/crates/sqlbuild-analysis/src/compiler/models.rs index 7ef1d147f3..ca6719b481 100644 --- a/crates/sqlbuild-analysis/src/compiler/models.rs +++ b/crates/sqlbuild-analysis/src/compiler/models.rs @@ -196,8 +196,7 @@ pub struct SqlTestAssemblyBatch { pub struct SqlTestAssemblyModel { pub name: String, pub macro_deps: Vec, - /// The pre-macro SQL to scan for calls when no macro dependencies were recorded and it - /// contains a macro token; Python scans it only then. + /// Pre-macro SQL Python scans for calls: set only without macro deps and with a macro token. pub unscanned_macro_source: Option, pub references: Vec, } diff --git a/crates/sqlbuild-analysis/src/compiler/tests/test_sql_test_helper_references.rs b/crates/sqlbuild-analysis/src/compiler/tests/test_sql_test_helper_references.rs index a3b311b682..05d1594a41 100644 --- a/crates/sqlbuild-analysis/src/compiler/tests/test_sql_test_helper_references.rs +++ b/crates/sqlbuild-analysis/src/compiler/tests/test_sql_test_helper_references.rs @@ -1,11 +1,19 @@ use serde_json::{Value, json}; +use crate::compiler::_helpers::sql_tests::extraction::generic_syntax; +use crate::compiler::main::sql_test_assembly::assemble_sql_test_batch; +use crate::compiler::models::{ + AssembledSqlTestFacts, SqlTestAssemblyBatch, SqlTestAssemblyDeferral, SqlTestAssemblyModel, + SqlTestAssemblyModelPayload, SqlTestAssemblyOutcome, SqlTestAssemblyPayload, + SqlTestAssemblyReference, SqlTestAssemblyTest, SqlTestCte, SqlTestHelperDiagnostic, + SqlTestParameterValue, +}; use crate::compiler::tests::helpers::{ chain_helper_reference_case, defined_before, plan_helper_reference_case, plan_helper_reference_response, }; use crate::compiler::tests::test_types::{ - HelperReferenceTestCase, UnresolvedReaderReferenceTestCase, + HelperReferenceTestCase, SqlTestAssemblyTestCase, UnresolvedReaderReferenceTestCase, }; const HELPER_READS_MODEL: (&str, &str) = ( @@ -30,6 +38,14 @@ const ASSERT_DOUBLED: (&str, &str) = ( "SELECT order_id FROM doubled WHERE amount_doubled <> 20", ); +const MOCK_READS_HELPER_TEST: &str = "TEST (name \"mock_reads_helper\");\n\nWITH\n\ +orders_feed AS (SELECT * FROM __source(\"raw_orders\")),\n\ +__ref__stg_orders AS (SELECT * FROM orders_feed),\n\ +__expected__customer_totals AS (SELECT 1 AS customer_id)\nSELECT 1\n"; +/// Python's `build_sql_test_case_fingerprint` of the parameterized case below. +const PYTHON_CASE_FINGERPRINT: &str = + "8f2adcab557ba4476fcb70de1fc11014acda9f1b597a3ca5c3245afb632e5d0f"; + #[test] fn given_helper_cte_references_when_planning_then_references_resolve_in_dependency_order() { let test_cases = [ @@ -491,3 +507,201 @@ fn given_unresolvable_reader_references_when_planning_directly_then_planner_reje ); } } + +#[test] +fn given_compiled_sql_test_inputs_when_assembling_then_facts_match_python_or_defer() { + let cte = |name: &str, sql_body: &str| SqlTestCte { + name: name.to_owned(), + sql_body: sql_body.to_owned(), + }; + let strings = + |names: &[&str]| -> Vec { names.iter().map(|n| (*n).to_owned()).collect() }; + let model_test = |contents: &str, has_macro_mocks: bool| SqlTestAssemblyTest { + block_name: Some("mock_reads_helper".to_owned()), + file_stem: "test_customer_totals".to_owned(), + relative_path: "tests/unit/test_customer_totals.sql".to_owned(), + relative_stem: "test_customer_totals".to_owned(), + contents: contents.to_owned(), + block_sql: contents + .split_once("\n\n") + .map_or("", |(_, body)| body) + .to_owned(), + block_index: 1, + sql_body: "SELECT 1".to_owned(), + case_name: None, + parameter_schema: Vec::new(), + parameter_values: Vec::new(), + payload: SqlTestAssemblyPayload::Model(SqlTestAssemblyModelPayload { + authored_ctes: vec![ + cte("orders_feed", "SELECT * FROM __source(\"raw_orders\")"), + cte("__ref__stg_orders", "SELECT * FROM orders_feed"), + ], + expected_ctes: vec![cte( + "__expected__customer_totals", + "SELECT 1 AS customer_id", + )], + assertion_ctes: Vec::new(), + expected_model_names: strings(&["customer_totals"]), + assertion_target_model_names: Vec::new(), + reference_target_model_names: Vec::new(), + mock_model_names: strings(&["stg_orders"]), + has_macro_mocks, + }), + }; + let model = |name: &str, macro_deps: &[&str], source: Option<&str>, refs: &[(&str, &str)]| { + SqlTestAssemblyModel { + name: name.to_owned(), + macro_deps: strings(macro_deps), + unscanned_macro_source: source.map(str::to_owned), + references: refs + .iter() + .map(|(kind, name)| SqlTestAssemblyReference { + kind: (*kind).to_owned(), + name: (*name).to_owned(), + package: None, + }) + .collect(), + } + }; + let models = || { + vec![ + model("stg_orders", &[], None, &[("source", "raw_orders")]), + model("customer_totals", &[], None, &[("ref", "stg_orders")]), + model( + "order_cents", + &[], + Some("SELECT @enum(\"order_channel\").WEB, @@scale * @ cents(amount) FROM t"), + &[], + ), + model( + "order_dollars", + &[], + Some("SELECT '@cents(x)', @dollars (amount)"), + &[], + ), + ] + }; + let batch = |tests: Vec| SqlTestAssemblyBatch { + models: models(), + tests, + lexical_syntax: generic_syntax(), + }; + let macro_test = SqlTestAssemblyTest { + block_name: None, + payload: SqlTestAssemblyPayload::Direct { + mode: "macro".to_owned(), + tested_resource_names: strings(&["dollars"]), + }, + ..model_test(MOCK_READS_HELPER_TEST, false) + }; + let case_test = |decimal_digits: Vec| SqlTestAssemblyTest { + relative_path: "tests/unit/test_cases.sql".to_owned(), + case_name: Some("first".to_owned()), + parameter_schema: vec![ + ("p_decimal".to_owned(), "decimal".to_owned(), true), + ("p_float".to_owned(), "float".to_owned(), false), + ], + parameter_values: vec![ + ( + "p_decimal".to_owned(), + SqlTestParameterValue::Decimal { + negative: true, + digits: decimal_digits, + exponent: -4, + }, + ), + ("p_float".to_owned(), SqlTestParameterValue::Float(0.1)), + ( + "p_string".to_owned(), + SqlTestParameterValue::String("caf\u{e9}".to_owned()), + ), + ], + payload: SqlTestAssemblyPayload::Model(SqlTestAssemblyModelPayload { + authored_ctes: Vec::new(), + expected_ctes: Vec::new(), + assertion_ctes: Vec::new(), + expected_model_names: strings(&["orders", "customers"]), + assertion_target_model_names: strings(&["orders"]), + reference_target_model_names: Vec::new(), + mock_model_names: Vec::new(), + has_macro_mocks: false, + }), + ..model_test(MOCK_READS_HELPER_TEST, false) + }; + let test_cases = [ + SqlTestAssemblyTestCase { + description: "a mock reading a helper that calls a source reports P013 at the call", + batch: batch(vec![model_test(MOCK_READS_HELPER_TEST, false)]), + expected_outcomes: vec![SqlTestAssemblyOutcome::Assembled(AssembledSqlTestFacts { + name: "mock_reads_helper".to_owned(), + scope_deps: vec![("model", "customer_totals".to_owned())], + target_model_names: strings(&["customer_totals"]), + case_fingerprint: None, + diagnostic_resource_name: "mock_reads_helper".to_owned(), + diagnostics: vec![SqlTestHelperDiagnostic { + line: 4, + column: 31, + end_line: 4, + end_column: 53, + message: "SQL test mock '__ref__stg_orders' reads helper CTE 'orders_feed', \ + which calls __source(\"raw_orders\"); mocks and fixtures are \ + defined before the models the test runs, so the helper cannot \ + be resolved for them" + .to_owned(), + help: "Read a mock by its CTE name instead, for example FROM \ + __source__raw_orders rather than FROM __source(\"raw_orders\"), \ + defining __source__raw_orders AS (SELECT ...) if the test does not \ + mock it, or write the rows of '__ref__stg_orders' directly." + .to_owned(), + }], + })], + }, + SqlTestAssemblyTestCase { + description: "macro mocks, non-ASCII text and out-of-context decimals defer", + batch: batch(vec![ + model_test(MOCK_READS_HELPER_TEST, true), + model_test(&format!("-- caf\u{e9}\n{MOCK_READS_HELPER_TEST}"), false), + case_test(vec![1; 29]), + ]), + expected_outcomes: vec![ + SqlTestAssemblyOutcome::Deferred(SqlTestAssemblyDeferral::MacroMocks), + SqlTestAssemblyOutcome::Deferred(SqlTestAssemblyDeferral::NonAsciiText), + SqlTestAssemblyOutcome::Deferred(SqlTestAssemblyDeferral::DecimalContext), + ], + }, + SqlTestAssemblyTestCase { + description: "a case fingerprint and a macro test's scanned scope match Python", + batch: batch(vec![case_test(vec![2, 4, 7, 0, 0]), macro_test]), + expected_outcomes: vec![ + SqlTestAssemblyOutcome::Assembled(AssembledSqlTestFacts { + name: "mock_reads_helper [first]".to_owned(), + scope_deps: vec![ + ("model", "orders".to_owned()), + ("model", "customers".to_owned()), + ], + target_model_names: strings(&["orders", "customers"]), + case_fingerprint: Some(PYTHON_CASE_FINGERPRINT.to_owned()), + diagnostic_resource_name: "mock_reads_helper".to_owned(), + diagnostics: Vec::new(), + }), + SqlTestAssemblyOutcome::Assembled(AssembledSqlTestFacts { + name: "test_customer_totals".to_owned(), + scope_deps: vec![("model", "order_dollars".to_owned())], + target_model_names: Vec::new(), + case_fingerprint: None, + diagnostic_resource_name: "test_customer_totals".to_owned(), + diagnostics: Vec::new(), + }), + ], + }, + ]; + + for test_case in test_cases { + assert_eq!( + assemble_sql_test_batch(&test_case.batch), + test_case.expected_outcomes, + "{}", + test_case.description + ); + } +} diff --git a/crates/sqlbuild-analysis/src/compiler/tests/test_types.rs b/crates/sqlbuild-analysis/src/compiler/tests/test_types.rs index c109a747fb..3d59ff16d4 100644 --- a/crates/sqlbuild-analysis/src/compiler/tests/test_types.rs +++ b/crates/sqlbuild-analysis/src/compiler/tests/test_types.rs @@ -186,3 +186,9 @@ pub(crate) struct GlueErrorProjectionTestCase { pub(crate) warnings: &'static [(&'static str, &'static str)], pub(crate) expected_messages: &'static [&'static str], } + +pub(crate) struct SqlTestAssemblyTestCase { + pub(crate) description: &'static str, + pub(crate) batch: crate::compiler::models::SqlTestAssemblyBatch, + pub(crate) expected_outcomes: Vec, +} diff --git a/scripts/compiler_differential/_helpers/analysis_corpus/sql_test_glue.py b/scripts/compiler_differential/_helpers/analysis_corpus/sql_test_glue.py index 04d7b4b944..cc00d6d889 100644 --- a/scripts/compiler_differential/_helpers/analysis_corpus/sql_test_glue.py +++ b/scripts/compiler_differential/_helpers/analysis_corpus/sql_test_glue.py @@ -1,4 +1,4 @@ -"""Failure cases owned by the native SQL-test planning glue lane.""" +"""Failure cases owned by the native SQL-test planning and assembly glue lane.""" from scripts.compiler_differential._helpers.corpus.case_builder import failure_case from scripts.compiler_differential.models import FailureCase @@ -75,4 +75,23 @@ def sql_test_glue_failure_cases() -> tuple[FailureCase, ...]: ), ), ), + failure_case( + name="sql-test-glue-mock-reads-referencing-helper", + expected_code="P013", + expected_message=( + "SQL test mock '__ref__stg_orders' reads helper CTE 'orders_feed', which calls " + '__source("raw_orders"); mocks and fixtures are defined before the models the ' + "test runs, so the helper cannot be resolved for them" + ), + expected_location=(4, 31), + files=_test( + header='name "mock_reads_helper"', + ctes=( + 'orders_feed AS (SELECT * FROM __source("raw_orders")),\n' + "__ref__stg_orders AS (SELECT * FROM orders_feed),\n" + "__expected__customer_totals AS (\n" + " SELECT 10 AS customer_id, 5.0 AS total_amount\n)" + ), + ), + ), ) diff --git a/src/sqlbuild/compiler/sql_test_glue/_helpers/assembly.py b/src/sqlbuild/compiler/sql_test_glue/_helpers/assembly.py index 0ed42636e2..cb452078f9 100644 --- a/src/sqlbuild/compiler/sql_test_glue/_helpers/assembly.py +++ b/src/sqlbuild/compiler/sql_test_glue/_helpers/assembly.py @@ -2,6 +2,8 @@ from __future__ import annotations +from pathlib import Path + from sqlbuild.compiler.compile.constants import SQL_TEST_HELPER_REFERENCE_CODE from sqlbuild.compiler.compile.models import ( CompiledDirectLogicSqlTestPayload, @@ -10,6 +12,7 @@ CompiledObjectKey, CompiledSqlTest, CompiledSqlTestResource, + CompileModelSqlTestInputPayload, CompilerDiagnostic, CompileSqlTestInput, ) @@ -18,14 +21,31 @@ DiagnosticPhase, DiagnosticSeverity, ) +from sqlbuild.compiler.sql_test_glue._helpers.deferrals import record_sql_test_assembly_deferral from sqlbuild.compiler.sql_test_glue.models import NativeSqlTestAssembly from sqlbuild.compiler.sql_test_glue.types import ( + NativeSqlTestAssemblyRow, NativeSqlTestDiagnosticRow, NativeSqlTestFactsRow, ) from sqlbuild.spec.contracts.models import SourceLocation +def native_sql_test_assemblies( + *, test_inputs: tuple[CompileSqlTestInput, ...], rows: list[NativeSqlTestAssemblyRow] +) -> tuple[NativeSqlTestAssembly | None, ...]: + """One assembly per test input, or None, recorded, where the native assembly defers.""" + + assemblies: list[NativeSqlTestAssembly | None] = [] + for test_input, (facts, deferral) in zip(test_inputs, rows, strict=True): + if facts is None: + record_sql_test_assembly_deferral(kind=str(deferral)) + assemblies.append(None) + continue + assemblies.append(native_sql_test_assembly(test_input=test_input, facts=facts)) + return tuple(assemblies) + + def native_sql_test_assembly( *, test_input: CompileSqlTestInput, facts: NativeSqlTestFactsRow ) -> NativeSqlTestAssembly: @@ -36,7 +56,9 @@ def native_sql_test_assembly( CompiledObjectKey(resource_type=resource_type, name=dep_name) for resource_type, dep_name in scope_dep_rows ) - payload = test_input.payload + payload: CompileModelSqlTestInputPayload | CompileDirectLogicSqlTestInputPayload = ( + test_input.payload + ) compiled_payload: CompiledModelSqlTestPayload | CompiledDirectLogicSqlTestPayload tested_resources: tuple[CompiledSqlTestResource, ...] = () expected_model_names: tuple[str, ...] = () @@ -115,7 +137,7 @@ def _keyed_diagnostic( *, test_input: CompileSqlTestInput, resource_name: str, row: NativeSqlTestDiagnosticRow ) -> tuple[tuple[str, ...], CompilerDiagnostic]: line, column, end_line, end_column, message, help_text = row - path = test_input.test_file.relative_path + path: Path = test_input.test_file.relative_path return ( (SQL_TEST_HELPER_REFERENCE_CODE, path.as_posix(), f"{line:09d}:{column:09d}", message), CompilerDiagnostic( diff --git a/src/sqlbuild/compiler/sql_test_glue/main/_assemble_native_sql_tests.py b/src/sqlbuild/compiler/sql_test_glue/main/_assemble_native_sql_tests.py index c4f6a144d2..30b1e4dd08 100644 --- a/src/sqlbuild/compiler/sql_test_glue/main/_assemble_native_sql_tests.py +++ b/src/sqlbuild/compiler/sql_test_glue/main/_assemble_native_sql_tests.py @@ -4,8 +4,7 @@ import sqlbuild._native as native_module from sqlbuild.compiler.compile.models import CompileProjectInputs -from sqlbuild.compiler.sql_test_glue._helpers.assembly import native_sql_test_assembly -from sqlbuild.compiler.sql_test_glue._helpers.deferrals import record_sql_test_assembly_deferral +from sqlbuild.compiler.sql_test_glue._helpers.assembly import native_sql_test_assemblies from sqlbuild.compiler.sql_test_glue.models import ( NativeSqlTestAssembly, NativeSqlTestAssemblyRequest, @@ -25,11 +24,4 @@ def assemble_native_sql_tests( lexical_syntax=inputs.sql_lexical_syntax.native_mapping, ) ) - assemblies: list[NativeSqlTestAssembly | None] = [] - for test_input, (facts, deferral) in zip(inputs.test_inputs, rows, strict=True): - if facts is None: - record_sql_test_assembly_deferral(kind=str(deferral)) - assemblies.append(None) - continue - assemblies.append(native_sql_test_assembly(test_input=test_input, facts=facts)) - return tuple(assemblies) + return native_sql_test_assemblies(test_inputs=inputs.test_inputs, rows=rows) diff --git a/tests/integration/src/sqlbuild/compiler/sql_test_glue/helpers.py b/tests/integration/src/sqlbuild/compiler/sql_test_glue/helpers.py index 610bfc9731..8cc9dbc032 100644 --- a/tests/integration/src/sqlbuild/compiler/sql_test_glue/helpers.py +++ b/tests/integration/src/sqlbuild/compiler/sql_test_glue/helpers.py @@ -20,6 +20,7 @@ CompiledModelSqlTestPayload, CompiledProject, CompiledSqlTest, + CompileProjectInputs, CompileSqlTestCte, ) from sqlbuild.compiler.discovery.main.discover import discover_project_inputs @@ -28,13 +29,13 @@ from sqlbuild.compiler.planner._helpers.sql_tests import native_planning from sqlbuild.compiler.planner.models import NativeSqlTestArtifact from sqlbuild.compiler.sql_test_glue.models import ( + NativeSqlTestAssembly, NativeSqlTestAssemblyRequest, NativeSqlTestChainRequest, NativeSqlTestPlanningRequest, ) from sqlbuild.compiler.sql_test_glue.types import ( NativeSqlTestAssemblyRow, - NativeSqlTestFactsRow, NativeSqlTestPlanRow, ) @@ -466,27 +467,40 @@ def _compiled_tests_and_diagnostics(*, project_dir: Path) -> tuple[object, objec def record_native_assemblies(*, monkeypatch: pytest.MonkeyPatch) -> Counter[str]: - """Count tests the native assembly itself assembles, with diagnostics, cases, or defers. + """Count tests compile takes from the native assembly, with diagnostics, cases, or defers. - `native_assembled` counts only rows carrying native facts; a deferred test counts under - `deferred_` and is assembled by Python, so it never counts as native work. + `native_assembled` counts only assemblies the stage seam hands compile; a deferred test + counts under `deferred_` and is assembled by Python, so it never counts as native work. """ answers: Counter[str] = Counter() assemble: Callable[[NativeSqlTestAssemblyRequest], list[NativeSqlTestAssemblyRow]] = ( native_module.assemble_compiled_sql_tests ) + seam: Callable[..., tuple[NativeSqlTestAssembly | None, ...]] = ( + sql_test_stage.assemble_native_sql_tests + ) - def counted(request: NativeSqlTestAssemblyRequest) -> list[NativeSqlTestAssemblyRow]: + def counted_deferrals(request: NativeSqlTestAssemblyRequest) -> list[NativeSqlTestAssemblyRow]: rows: list[NativeSqlTestAssemblyRow] = assemble(request) - facts: list[NativeSqlTestFactsRow] = [row[0] for row in rows if row[0] is not None] - answers["native_assembled"] += len(facts) - answers["native_with_diagnostics"] += sum(bool(row[5]) for row in facts) - answers["native_case_fingerprints"] += sum(row[3] is not None for row in facts) - answers.update(f"deferred_{row[1]}" for row in rows if row[1] is not None) + deferrals: list[str] = list(filter(None, (row[1] for row in rows))) + answers.update(f"deferred_{kind}" for kind in deferrals) return rows - monkeypatch.setattr(native_module, "assemble_compiled_sql_tests", counted) + def counted_assemblies( + *, inputs: CompileProjectInputs + ) -> tuple[NativeSqlTestAssembly | None, ...]: + assemblies: tuple[NativeSqlTestAssembly | None, ...] = seam(inputs=inputs) + native: list[NativeSqlTestAssembly] = list(filter(None, assemblies)) + answers["native_assembled"] += len(native) + answers["native_with_diagnostics"] += sum(bool(item.diagnostics) for item in native) + answers["native_case_fingerprints"] += sum( + item.test.case_fingerprint is not None for item in native + ) + return assemblies + + monkeypatch.setattr(native_module, "assemble_compiled_sql_tests", counted_deferrals) + monkeypatch.setattr(sql_test_stage, "assemble_native_sql_tests", counted_assemblies) return answers diff --git a/tests/integration/src/sqlbuild/compiler/sql_test_glue/test_native_sql_test_planning_glue.py b/tests/integration/src/sqlbuild/compiler/sql_test_glue/test_native_sql_test_planning_glue.py index 48ac5b75ac..255a973be6 100644 --- a/tests/integration/src/sqlbuild/compiler/sql_test_glue/test_native_sql_test_planning_glue.py +++ b/tests/integration/src/sqlbuild/compiler/sql_test_glue/test_native_sql_test_planning_glue.py @@ -193,10 +193,10 @@ def test_given_generated_sql_tests_when_planning_natively_then_plans_match_the_j assertion_share=0.2, unflattenable_assertion_share=0.1, ), - expected_minimum_native_assembled=140, - expected_minimum_native_with_diagnostics=18, - expected_minimum_native_case_fingerprints=65, - expected_minimum_deferred_macro_mocks=4, + expected_minimum_native_assembled=150, + expected_minimum_native_with_diagnostics=13, + expected_minimum_native_case_fingerprints=80, + expected_minimum_deferred_macro_mocks=3, ) ], ids=lambda case: case.description, From 194b2804b8aa599475b9c97bbcb8f8f99c1aa573 Mon Sep 17 00:00:00 2001 From: Kevin Longe <273747162+kvlonge@users.noreply.github.com> Date: Fri, 9 Oct 2026 12:27:27 +0100 Subject: [PATCH 11/31] test(native): assert exact seeded SQL-test assembly counts and deferrals --- .../compiler/sql_test_glue/_test_types.py | 5 +--- .../test_native_sql_test_planning_glue.py | 26 +++++++++---------- 2 files changed, 13 insertions(+), 18 deletions(-) diff --git a/tests/integration/src/sqlbuild/compiler/sql_test_glue/_test_types.py b/tests/integration/src/sqlbuild/compiler/sql_test_glue/_test_types.py index 4916b98965..f4abf5a7a3 100644 --- a/tests/integration/src/sqlbuild/compiler/sql_test_glue/_test_types.py +++ b/tests/integration/src/sqlbuild/compiler/sql_test_glue/_test_types.py @@ -33,7 +33,4 @@ class GeneratedSqlTestAssemblyParityTestCase: count: int test_count: int shape: SqlTestCorpusShape - expected_minimum_native_assembled: int - expected_minimum_native_with_diagnostics: int - expected_minimum_native_case_fingerprints: int - expected_minimum_deferred_macro_mocks: int + expected_native_assemblies: dict[str, int] diff --git a/tests/integration/src/sqlbuild/compiler/sql_test_glue/test_native_sql_test_planning_glue.py b/tests/integration/src/sqlbuild/compiler/sql_test_glue/test_native_sql_test_planning_glue.py index 255a973be6..22cc63e2ea 100644 --- a/tests/integration/src/sqlbuild/compiler/sql_test_glue/test_native_sql_test_planning_glue.py +++ b/tests/integration/src/sqlbuild/compiler/sql_test_glue/test_native_sql_test_planning_glue.py @@ -193,10 +193,14 @@ def test_given_generated_sql_tests_when_planning_natively_then_plans_match_the_j assertion_share=0.2, unflattenable_assertion_share=0.1, ), - expected_minimum_native_assembled=150, - expected_minimum_native_with_diagnostics=13, - expected_minimum_native_case_fingerprints=80, - expected_minimum_deferred_macro_mocks=3, + expected_native_assemblies={ + "native_assembled": 181, + "native_with_diagnostics": 17, + "native_case_fingerprints": 96, + "deferred_macro_mocks": 4, + "deferred_non_ascii_text": 11, + "deferred_decimal_context": 5, + }, ) ], ids=lambda case: case.description, @@ -227,13 +231,7 @@ def test_given_generated_sql_tests_when_assembling_natively_then_compiled_tests_ differences.extend(mismatches(inputs=[index], expected=[expected], actual=[actual])) outcomes[outcome_kind(actual)] += 1 - assert ( - differences, - native_assemblies["native_assembled"] >= test_case.expected_minimum_native_assembled, - native_assemblies["native_with_diagnostics"] - >= test_case.expected_minimum_native_with_diagnostics, - native_assemblies["native_case_fingerprints"] - >= test_case.expected_minimum_native_case_fingerprints, - native_assemblies["deferred_macro_mocks"] - >= test_case.expected_minimum_deferred_macro_mocks, - ) == ([], True, True, True, True), (native_assemblies, outcomes) + assert (differences, dict(native_assemblies)) == ( + [], + test_case.expected_native_assemblies, + ), outcomes From 309713e35ebaad4f4dbc37a596730ed4ca3a34a8 Mon Sep 17 00:00:00 2001 From: Kevin Longe <273747162+kvlonge@users.noreply.github.com> Date: Fri, 9 Oct 2026 06:32:56 +0100 Subject: [PATCH 12/31] test(native): count remaining native-to-Python fallbacks and add golden compile outputs Every shipped-stage fallback reports a named site to the compiler differential. One allow-list holds those fallbacks and the preview analysis deferrals with exact counts per corpus, and the CI make targets fail on new, changed or vanished entries. Golden compile outputs cover fixtures, examples, seeds and the failure corpus, and a shared failure-corpus list holds user-facing errors every engine must report with the exact text. --- .github/workflows/verify.yml | 2 +- Makefile | 19 +- scripts/compiler_differential/README.md | 90 + .../_helpers/comparing/project_comparison.py | 27 + .../_helpers/corpus/emitted_codes.py | 25 +- .../_helpers/corpus/failure_cases.py | 51 + .../_helpers/goldens/__init__.py | 1 + .../_helpers/goldens/goldens.py | 175 + .../_helpers/running/native_fallbacks.py | 205 + .../_helpers/running/records.py | 39 +- .../_helpers/running/report.py | 7 +- .../_helpers/running/run.py | 25 +- scripts/compiler_differential/constants.py | 35 + .../main/differential.py | 63 +- scripts/compiler_differential/models.py | 36 +- .../native_fallbacks.toml | 285 ++ scripts/compiler_differential/types.py | 2 + .../compile/_helpers/assembly/project.py | 5 + .../compile/_helpers/assembly/targets.py | 7 + .../compile/_helpers/attachment/audits.py | 5 +- .../compile/_helpers/attachment/core.py | 12 +- .../_helpers/attachment/declaration_scope.py | 6 +- .../compile/_helpers/attachment/functions.py | 7 +- .../_helpers/attachment/model_config.py | 8 + .../attachment/scope_relationships.py | 7 + .../_helpers/macro_bridge/expansion.py | 3 + .../compile/_helpers/refs/references.py | 8 +- .../_helpers/render/context_templates.py | 10 + .../_helpers/render/cursor_intrinsics.py | 4 +- .../compile/_helpers/render/declarations.py | 5 +- .../compile/_helpers/render/macros.py | 10 + .../compile/_helpers/render/sql_vars.py | 10 +- .../compile/_helpers/scenarios/core.py | 6 +- .../compile/_helpers/sql_tests/core.py | 5 +- .../compile/main/expand_sql_with_spans.py | 5 + .../compile/main/expand_template_data.py | 5 + .../_helpers/native/declaration_files.py | 3 + .../classes/native_fallback_recorder.py | 50 + src/sqlbuild/compiler/frontier/constants.py | 2 + .../frontier/main/_report_native_fallback.py | 22 + src/sqlbuild/compiler/frontier/types.py | 34 + .../macro_bridge/classes/macro_bridge.py | 5 + .../main/run_with_macro_bridge.py | 3 + .../main/_parse_native_header_metadata.py | 8 + .../compiler_differential/_test_types.py | 31 + .../scripts/compiler_differential/helpers.py | 12 + .../test_engine_error_cases.py | 61 + .../test_golden_outputs.py | 76 + .../test_native_fallback_gate.py | 107 + tests/goldens/compiler/example/hero-shop.json | 220 ++ tests/goldens/compiler/example/tidy-shop.json | 115 + .../goldens/compiler/example/waffle-shop.json | 2136 ++++++++++ .../analysis-accepted-values-type.json | 308 ++ .../failure/analysis-aggregate-in-where.json | 275 ++ .../analysis-alias-column-list-width.json | 273 ++ .../failure/analysis-ambiguous-column.json | 274 ++ .../failure/analysis-arithmetic-operands.json | 276 ++ .../failure/analysis-cursor-type-family.json | 274 ++ .../failure/analysis-duplicate-cte-name.json | 274 ++ ...lysis-expression-audit-unknown-column.json | 295 ++ .../analysis-function-argument-count.json | 273 ++ .../analysis-function-argument-type.json | 276 ++ .../analysis-non-boolean-predicate.json | 277 ++ .../failure/analysis-non-constant-limit.json | 274 ++ ...sis-opt-out-conflicts-with-legacy-key.json | 18 + ...lysis-rejected-opt-out-hides-findings.json | 275 ++ .../analysis-relationships-unknown-field.json | 329 ++ ...ysis-session-cte-chain-unknown-column.json | 273 ++ ...-session-set-operation-unknown-column.json | 276 ++ ...session-star-published-unknown-column.json | 353 ++ .../analysis-set-operation-branch-types.json | 272 ++ .../analysis-test-fixture-unknown-column.json | 283 ++ .../failure/analysis-udf-argument-count.json | 280 ++ .../failure/analysis-udf-argument-family.json | 280 ++ .../analysis-unique-key-unknown-column.json | 274 ++ .../failure/analysis-unknown-function.json | 273 ++ .../failure/analysis-unknown-qualifier.json | 273 ++ .../failure/analysis-unsupported-cast.json | 276 ++ .../failure/analysis-window-without-over.json | 271 ++ .../attached-audit-reads-dependant.json | 271 ++ .../goldens/compiler/failure/audit-parse.json | 18 + .../failure/audit-role-unsupported-entry.json | 18 + .../audit-unknown-project-variable.json | 18 + .../audit-variable-in-doubled-backticks.json | 18 + .../failure/built-in-audit-shadow.json | 279 ++ .../failure/check-depends-on-check.json | 18 + .../failure/column-nullable-not-null.json | 19 + .../command-output-sink-unknown-provider.json | 18 + .../failure/commented-ref-argument.json | 270 ++ .../compiler/failure/comparison-type.json | 278 ++ .../goldens/compiler/failure/config-bom.json | 18 + ...config-concurrency-without-capability.json | 18 + ...config-cursor-bound-outside-utc-years.json | 19 + .../config-cursor-bounds-reversed.json | 18 + ...config-equally-specific-path-defaults.json | 19 + .../failure/config-header-tags-not-list.json | 18 + .../config-incremental-key-on-table.json | 18 + .../config-incremental-without-strategy.json | 18 + .../failure/config-macro-outside-hooks.json | 18 + .../config-merge-exclusion-overlaps-key.json | 18 + .../config-microbatch-without-strategy.json | 18 + .../config-migrate-force-on-table.json | 19 + ...config-old-name-view-without-duration.json | 19 + ...aceholder-on-built-in-materialization.json | 18 + .../config-retention-not-whole-days.json | 18 + .../failure/config-retention-on-view.json | 18 + ...nfig-several-faults-reports-the-first.json | 18 + .../config-snapshot-without-unique-key.json | 18 + ...template-missing-environment-variable.json | 18 + .../config-template-unknown-namespace.json | 18 + .../config-template-unknown-variable.json | 18 + .../config-template-unsupported-function.json | 18 + .../config-template-unterminated-string.json | 18 + .../config-template-wrong-argument-count.json | 18 + .../compiler/failure/config-toml-syntax.json | 18 + .../failure/config-unknown-adapter.json | 18 + .../failure/config-unknown-contract.json | 18 + .../failure/config-unknown-default-key.json | 19 + .../config-unknown-materialization.json | 18 + .../config-unknown-model-reference.json | 18 + .../failure/config-unknown-table-type.json | 18 + .../failure/config-untyped-hook-entry.json | 18 + ...config-watermark-limit-below-lookback.json | 18 + .../constant-header-nested-too-deep.json | 19 + .../context-variable-in-model-sql.json | 18 + .../contract-dynamic-columns-unproven.json | 289 ++ ...t-dynamic-columns-unsupported-dialect.json | 288 ++ .../failure/contract-extra-column.json | 303 ++ .../failure/contract-immediate-promotion.json | 302 ++ .../failure/contract-missing-column.json | 321 ++ .../failure/contract-nullability.json | 325 ++ .../failure/contract-type-mismatch.json | 325 ++ .../failure/contract-unknown-type.json | 298 ++ .../failure/contract-without-columns.json | 265 ++ .../cursor-intrinsic-outside-incremental.json | 18 + .../failure/cursor-model-without-inputs.json | 268 ++ .../failure/cursor-unknown-column.json | 272 ++ .../dbt-reference-without-manifest.json | 19 + .../compiler/failure/dbt-unknown-key.json | 19 + ...aration-first-of-two-reference-errors.json | 18 + .../failure/declaration-group-below-root.json | 18 + .../declaration-group-unsupported-entry.json | 18 + ...claration-inaccessible-local-constant.json | 18 + ...eclaration-invalid-constant-reference.json | 18 + .../failure/declaration-root-nested.json | 18 + ...ation-unclosed-quote-before-reference.json | 18 + .../declaration-unknown-enum-after-macro.json | 18 + .../failure/duplicate-declaration.json | 18 + .../compiler/failure/duplicate-hook.json | 18 + .../compiler/failure/duplicate-model.json | 18 + .../compiler/failure/duplicate-provider.json | 18 + .../failure/duplicate-python-node.json | 18 + .../compiler/failure/duplicate-scenario.json | 18 + .../compiler/failure/duplicate-source.json | 18 + ...ne-error-case-folded-duplicate-column.json | 18 + ...gine-error-unset-environment-variable.json | 18 + .../engine-error-week-date-cursor-start.json | 18 + .../failure/enum-duplicate-member.json | 18 + .../goldens/compiler/failure/enum-parse.json | 18 + .../compiler/failure/failing-macro.json | 18 + .../generic-audit-cursor-intrinsic.json | 18 + .../failure/generic-audit-header.json | 19 + .../generic-audit-missing-argument.json | 18 + ...eneric-audit-overrides-implicit-model.json | 18 + .../generic-audit-unknown-run-scope.json | 18 + .../hard-coded-relation-in-python.json | 270 ++ .../failure/header-audit-identity.json | 19 + .../failure/header-audit-severity.json | 18 + .../header-column-audits-not-a-list.json | 18 + .../failure/header-column-type-not-text.json | 18 + .../header-column-unknown-metadata-key.json | 18 + .../failure/header-duplicate-column.json | 18 + .../failure/hook-reads-dependant.json | 280 ++ .../failure/hook-role-unsupported-entry.json | 18 + .../failure/hook-unknown-argument.json | 18 + .../compiler/failure/hook-without-header.json | 18 + .../failure/inaccessible-declaration.json | 18 + ...erited-declaration-used-in-one-folder.json | 270 ++ .../failure/invalid-decimal-constant.json | 18 + .../failure/invalid-declaration-name.json | 19 + .../invalid-reference-calls-in-two-files.json | 285 ++ .../failure/invalid-resource-name.json | 19 + .../legacy-event-exporters-directory.json | 18 + .../lifecycle-sink-default-parameter.json | 18 + .../lifecycle-sink-unknown-provider.json | 18 + .../lineage-opted-out-except-and-ctes.json | 353 ++ .../lineage-opted-out-quoted-columns.json | 352 ++ .../failure/lineage-opted-out-star.json | 350 ++ .../lineage-opted-out-union-of-stars.json | 354 ++ .../failure/list-constant-mixed-types.json | 18 + .../failure/local-config-unknown-key.json | 19 + .../local-macro-needed-below-owner.json | 412 ++ .../failure/local-singular-audit.json | 18 + .../failure/macro-arguments-do-not-parse.json | 18 + .../macro-first-of-two-failing-models.json | 18 + .../failure/macro-generated-reference.json | 290 ++ .../compiler/failure/macro-import-cycle.json | 18 + .../failure/macro-in-sibling-scope.json | 18 + .../failure/macro-non-literal-argument.json | 18 + .../failure/macro-output-calls-macro.json | 18 + .../failure/macro-private-helper.json | 18 + .../failure/macro-raises-after-a-call.json | 18 + ...macro-returned-invalid-reference-call.json | 295 ++ .../failure/macro-returns-non-string.json | 18 + .../failure/macro-test-helper-calls-udf.json | 18 + ...macro-test-helper-malformed-reference.json | 305 ++ .../failure/macro-test-quoted-cte-name.json | 19 + .../managed-source-without-loader.json | 18 + .../failure/materialization-import-error.json | 18 + .../compiler/failure/missing-description.json | 272 ++ .../failure/model-blank-column-type.json | 18 + tests/goldens/compiler/failure/model-bom.json | 18 + .../model-column-audit-unknown-severity.json | 18 + .../failure/model-duplicate-column.json | 18 + .../failure/model-header-nested-too-deep.json | 19 + .../compiler/failure/model-header-syntax.json | 18 + .../failure/model-missing-header.json | 18 + .../model-nullable-column-with-not-null.json | 19 + .../compiler/failure/model-path-not-utf8.json | 18 + .../failure/model-private-enum-misuse.json | 18 + .../compiler/failure/model-reserved-name.json | 19 + .../compiler/failure/model-sql-syntax.json | 19 + .../model-template-missing-environment.json | 18 + .../model-template-unknown-function.json | 18 + .../model-template-unterminated-string.json | 18 + .../failure/model-unknown-header-key.json | 19 + .../failure/multiple-errors-order.json | 384 ++ .../failure/named-hook-missing-argument.json | 18 + .../named-role-nested-declaration.json | 18 + .../order-binding-metadata-contract.json | 361 ++ .../failure/order-closest-column-help.json | 277 ++ .../order-contract-error-then-warning.json | 345 ++ .../order-poisoned-downstream-uses.json | 277 ++ .../failure/order-two-failing-models.json | 375 ++ .../failure/orphan-audit-factory.json | 286 ++ .../compiler/failure/over-broad-global.json | 270 ++ .../failure/path-defaults-unmatched.json | 18 + .../failure/provider-invalid-settings.json | 18 + .../failure/python-node-import-error.json | 18 + .../python-node-missing-description.json | 271 ++ .../compiler/failure/python-node-syntax.json | 18 + .../python-outside-extension-root.json | 19 + .../reference-dbt-three-arguments.json | 269 ++ .../failure/reference-empty-argument.json | 269 ++ .../reference-error-after-rejected-calls.json | 18 + .../failure/reference-expression-name.json | 269 ++ ...reference-rejected-calls-across-files.json | 284 ++ ...ce-several-rejected-calls-in-one-file.json | 311 ++ .../failure/reference-two-name-arguments.json | 269 ++ .../failure/reference-unclosed-call.json | 18 + .../failure/resource-name-collision.json | 18 + .../compiler/failure/rule-custom-finding.json | 27 + .../compiler/failure/rule-unused-cte.json | 30 + .../runtime-placeholder-without-default.json | 18 + .../failure/scenario-check-reads-source.json | 18 + .../failure/scenario-dependent-checks.json | 18 + .../failure/scenario-first-of-two-errors.json | 18 + .../scenario-fixture-without-target.json | 18 + .../compiler/failure/scenario-macro-mock.json | 18 + .../failure/scenario-materialized-cte.json | 19 + .../compiler/failure/scenario-parse.json | 18 + .../failure/scenario-quoted-cte-name.json | 19 + .../scenario-statement-after-ctes.json | 18 + .../failure/scenario-without-checks.json | 18 + .../failure/schema-from-sibling-folder.json | 289 ++ .../schema-unterminated-declaration.json | 18 + .../failure/schema-yml-invalid-yaml.json | 18 + .../failure/schema-yml-model-metadata.json | 18 + .../failure/scope-duplicate-scoped-enum.json | 18 + .../scope-first-of-two-visibility-errors.json | 18 + .../scope-inaccessible-local-macro.json | 18 + .../scope-macro-test-cte-without-body.json | 18 + ...scope-malformed-expected-relationship.json | 18 + ...cope-scenario-expected-without-target.json | 18 + ...scope-test-outside-relationship-grant.json | 18 + .../failure/scoped-root-at-project-root.json | 18 + .../failure/seed-declaration-without-csv.json | 18 + .../failure/seed-header-mismatch.json | 18 + .../compiler/failure/seed-without-header.json | 18 + .../compiler/failure/seed-yaml-extension.json | 18 + .../compiler/failure/set-operation-width.json | 272 ++ .../failure/single-quoted-source-name.json | 270 ++ .../failure/singular-audit-across-trees.json | 278 ++ .../failure/singular-audit-one-resource.json | 269 ++ .../snapshot-schema-change-with-contract.json | 18 + .../source-description-unknown-variable.json | 18 + .../compiler/failure/source-parse.json | 18 + .../compiler/failure/source-unknown-key.json | 19 + .../failure/source-unsupported-yaml.json | 19 + .../compiler/failure/sql-function-header.json | 18 + .../failure/sql-function-missing-returns.json | 18 + .../sql-function-template-namespace.json | 18 + .../failure/sql-hook-unknown-key.json | 19 + .../compiler/failure/syntax-function.json | 19 + .../goldens/compiler/failure/syntax-hook.json | 19 + .../compiler/failure/syntax-inline-hook.json | 19 + .../failure/syntax-source-expression.json | 19 + .../failure/table-function-arity.json | 18 + .../table-function-empty-call-argument.json | 18 + .../table-function-single-quoted-name.json | 269 ++ .../failure/table-function-unclosed-call.json | 18 + .../table-function-without-arguments.json | 269 ++ .../target-managed-loader-collision.json | 18 + .../failure/target-missing-database.json | 19 + .../failure/target-missing-write-schema.json | 18 + .../failure/target-named-without-schema.json | 18 + .../compiler/failure/target-unknown-key.json | 19 + .../test-assertion-reads-unmocked-source.json | 274 ++ .../failure/test-case-missing-parameter.json | 18 + .../failure/test-expected-implicit-alias.json | 19 + .../test-expected-parenthesised-branch.json | 18 + .../test-expected-reads-unknown-model.json | 274 ++ .../failure/test-expected-unknown-column.json | 283 ++ .../failure/test-expects-unknown-model.json | 18 + .../test-helper-reads-unknown-model.json | 274 ++ .../failure/test-mocks-unknown-source.json | 18 + .../goldens/compiler/failure/test-parse.json | 18 + .../failure/test-quoted-cte-name.json | 19 + .../failure/test-reads-only-mocks.json | 274 ++ ...ed-dollar-quote-with-project-variable.json | 18 + .../compiler/failure/ungrouped-column.json | 274 ++ .../compiler/failure/unknown-column.json | 276 ++ .../compiler/failure/unknown-constant.json | 18 + .../compiler/failure/unknown-enum-member.json | 18 + .../compiler/failure/unknown-macro.json | 18 + .../goldens/compiler/failure/unknown-ref.json | 18 + .../failure/unknown-scalar-function.json | 18 + .../compiler/failure/unknown-source.json | 18 + .../failure/unknown-table-function.json | 18 + .../unneeded-sql-analysis-opt-out.json | 275 ++ .../compiler/failure/unquoted-ref-name.json | 270 ++ .../failure/unset-environment-variable.json | 18 + .../compiler/failure/unused-declaration.json | 269 ++ .../failure/variable-not-defined.json | 18 + .../goldens/compiler/fixture/dbt_interop.json | 66 + .../fixture/source_loader_strategies.json | 368 ++ .../goldens/compiler/fixture/waffle_shop.json | 2110 ++++++++++ tests/goldens/compiler/seed/0-postgres.json | 2906 ++++++++++++++ tests/goldens/compiler/seed/0-snowflake.json | 2907 ++++++++++++++ tests/goldens/compiler/seed/0.json | 3479 +++++++++++++++++ tests/goldens/compiler/seed/1.json | 37 + tests/goldens/compiler/seed/10.json | 3302 ++++++++++++++++ tests/goldens/compiler/seed/11.json | 45 + tests/goldens/compiler/seed/2.json | 84 + tests/goldens/compiler/seed/3.json | 2396 ++++++++++++ tests/goldens/compiler/seed/4.json | 3131 +++++++++++++++ tests/goldens/compiler/seed/5.json | 48 + tests/goldens/compiler/seed/6.json | 3234 +++++++++++++++ tests/goldens/compiler/seed/7.json | 48 + tests/goldens/compiler/seed/8.json | 2770 +++++++++++++ tests/goldens/compiler/seed/9.json | 3210 +++++++++++++++ .../_helpers/goldens/__init__.py | 1 + .../_helpers/goldens/_test_types.py | 38 + .../_helpers/goldens/helpers.py | 46 + .../_helpers/goldens/test_compiler_goldens.py | 183 + .../_helpers/running/_test_types.py | 40 +- .../_helpers/running/test_native_fallbacks.py | 199 + .../_helpers/running/test_records.py | 15 +- .../sqlbuild/compiler/frontier/_test_types.py | 16 +- .../frontier/test_native_fallback_recorder.py | 64 + 360 files changed, 63339 insertions(+), 42 deletions(-) create mode 100644 scripts/compiler_differential/README.md create mode 100644 scripts/compiler_differential/_helpers/goldens/__init__.py create mode 100644 scripts/compiler_differential/_helpers/goldens/goldens.py create mode 100644 scripts/compiler_differential/_helpers/running/native_fallbacks.py create mode 100644 scripts/compiler_differential/native_fallbacks.toml create mode 100644 src/sqlbuild/compiler/frontier/classes/native_fallback_recorder.py create mode 100644 src/sqlbuild/compiler/frontier/main/_report_native_fallback.py create mode 100644 tests/e2e/scripts/compiler_differential/test_engine_error_cases.py create mode 100644 tests/e2e/scripts/compiler_differential/test_golden_outputs.py create mode 100644 tests/e2e/scripts/compiler_differential/test_native_fallback_gate.py create mode 100644 tests/goldens/compiler/example/hero-shop.json create mode 100644 tests/goldens/compiler/example/tidy-shop.json create mode 100644 tests/goldens/compiler/example/waffle-shop.json create mode 100644 tests/goldens/compiler/failure/analysis-accepted-values-type.json create mode 100644 tests/goldens/compiler/failure/analysis-aggregate-in-where.json create mode 100644 tests/goldens/compiler/failure/analysis-alias-column-list-width.json create mode 100644 tests/goldens/compiler/failure/analysis-ambiguous-column.json create mode 100644 tests/goldens/compiler/failure/analysis-arithmetic-operands.json create mode 100644 tests/goldens/compiler/failure/analysis-cursor-type-family.json create mode 100644 tests/goldens/compiler/failure/analysis-duplicate-cte-name.json create mode 100644 tests/goldens/compiler/failure/analysis-expression-audit-unknown-column.json create mode 100644 tests/goldens/compiler/failure/analysis-function-argument-count.json create mode 100644 tests/goldens/compiler/failure/analysis-function-argument-type.json create mode 100644 tests/goldens/compiler/failure/analysis-non-boolean-predicate.json create mode 100644 tests/goldens/compiler/failure/analysis-non-constant-limit.json create mode 100644 tests/goldens/compiler/failure/analysis-opt-out-conflicts-with-legacy-key.json create mode 100644 tests/goldens/compiler/failure/analysis-rejected-opt-out-hides-findings.json create mode 100644 tests/goldens/compiler/failure/analysis-relationships-unknown-field.json create mode 100644 tests/goldens/compiler/failure/analysis-session-cte-chain-unknown-column.json create mode 100644 tests/goldens/compiler/failure/analysis-session-set-operation-unknown-column.json create mode 100644 tests/goldens/compiler/failure/analysis-session-star-published-unknown-column.json create mode 100644 tests/goldens/compiler/failure/analysis-set-operation-branch-types.json create mode 100644 tests/goldens/compiler/failure/analysis-test-fixture-unknown-column.json create mode 100644 tests/goldens/compiler/failure/analysis-udf-argument-count.json create mode 100644 tests/goldens/compiler/failure/analysis-udf-argument-family.json create mode 100644 tests/goldens/compiler/failure/analysis-unique-key-unknown-column.json create mode 100644 tests/goldens/compiler/failure/analysis-unknown-function.json create mode 100644 tests/goldens/compiler/failure/analysis-unknown-qualifier.json create mode 100644 tests/goldens/compiler/failure/analysis-unsupported-cast.json create mode 100644 tests/goldens/compiler/failure/analysis-window-without-over.json create mode 100644 tests/goldens/compiler/failure/attached-audit-reads-dependant.json create mode 100644 tests/goldens/compiler/failure/audit-parse.json create mode 100644 tests/goldens/compiler/failure/audit-role-unsupported-entry.json create mode 100644 tests/goldens/compiler/failure/audit-unknown-project-variable.json create mode 100644 tests/goldens/compiler/failure/audit-variable-in-doubled-backticks.json create mode 100644 tests/goldens/compiler/failure/built-in-audit-shadow.json create mode 100644 tests/goldens/compiler/failure/check-depends-on-check.json create mode 100644 tests/goldens/compiler/failure/column-nullable-not-null.json create mode 100644 tests/goldens/compiler/failure/command-output-sink-unknown-provider.json create mode 100644 tests/goldens/compiler/failure/commented-ref-argument.json create mode 100644 tests/goldens/compiler/failure/comparison-type.json create mode 100644 tests/goldens/compiler/failure/config-bom.json create mode 100644 tests/goldens/compiler/failure/config-concurrency-without-capability.json create mode 100644 tests/goldens/compiler/failure/config-cursor-bound-outside-utc-years.json create mode 100644 tests/goldens/compiler/failure/config-cursor-bounds-reversed.json create mode 100644 tests/goldens/compiler/failure/config-equally-specific-path-defaults.json create mode 100644 tests/goldens/compiler/failure/config-header-tags-not-list.json create mode 100644 tests/goldens/compiler/failure/config-incremental-key-on-table.json create mode 100644 tests/goldens/compiler/failure/config-incremental-without-strategy.json create mode 100644 tests/goldens/compiler/failure/config-macro-outside-hooks.json create mode 100644 tests/goldens/compiler/failure/config-merge-exclusion-overlaps-key.json create mode 100644 tests/goldens/compiler/failure/config-microbatch-without-strategy.json create mode 100644 tests/goldens/compiler/failure/config-migrate-force-on-table.json create mode 100644 tests/goldens/compiler/failure/config-old-name-view-without-duration.json create mode 100644 tests/goldens/compiler/failure/config-placeholder-on-built-in-materialization.json create mode 100644 tests/goldens/compiler/failure/config-retention-not-whole-days.json create mode 100644 tests/goldens/compiler/failure/config-retention-on-view.json create mode 100644 tests/goldens/compiler/failure/config-several-faults-reports-the-first.json create mode 100644 tests/goldens/compiler/failure/config-snapshot-without-unique-key.json create mode 100644 tests/goldens/compiler/failure/config-template-missing-environment-variable.json create mode 100644 tests/goldens/compiler/failure/config-template-unknown-namespace.json create mode 100644 tests/goldens/compiler/failure/config-template-unknown-variable.json create mode 100644 tests/goldens/compiler/failure/config-template-unsupported-function.json create mode 100644 tests/goldens/compiler/failure/config-template-unterminated-string.json create mode 100644 tests/goldens/compiler/failure/config-template-wrong-argument-count.json create mode 100644 tests/goldens/compiler/failure/config-toml-syntax.json create mode 100644 tests/goldens/compiler/failure/config-unknown-adapter.json create mode 100644 tests/goldens/compiler/failure/config-unknown-contract.json create mode 100644 tests/goldens/compiler/failure/config-unknown-default-key.json create mode 100644 tests/goldens/compiler/failure/config-unknown-materialization.json create mode 100644 tests/goldens/compiler/failure/config-unknown-model-reference.json create mode 100644 tests/goldens/compiler/failure/config-unknown-table-type.json create mode 100644 tests/goldens/compiler/failure/config-untyped-hook-entry.json create mode 100644 tests/goldens/compiler/failure/config-watermark-limit-below-lookback.json create mode 100644 tests/goldens/compiler/failure/constant-header-nested-too-deep.json create mode 100644 tests/goldens/compiler/failure/context-variable-in-model-sql.json create mode 100644 tests/goldens/compiler/failure/contract-dynamic-columns-unproven.json create mode 100644 tests/goldens/compiler/failure/contract-dynamic-columns-unsupported-dialect.json create mode 100644 tests/goldens/compiler/failure/contract-extra-column.json create mode 100644 tests/goldens/compiler/failure/contract-immediate-promotion.json create mode 100644 tests/goldens/compiler/failure/contract-missing-column.json create mode 100644 tests/goldens/compiler/failure/contract-nullability.json create mode 100644 tests/goldens/compiler/failure/contract-type-mismatch.json create mode 100644 tests/goldens/compiler/failure/contract-unknown-type.json create mode 100644 tests/goldens/compiler/failure/contract-without-columns.json create mode 100644 tests/goldens/compiler/failure/cursor-intrinsic-outside-incremental.json create mode 100644 tests/goldens/compiler/failure/cursor-model-without-inputs.json create mode 100644 tests/goldens/compiler/failure/cursor-unknown-column.json create mode 100644 tests/goldens/compiler/failure/dbt-reference-without-manifest.json create mode 100644 tests/goldens/compiler/failure/dbt-unknown-key.json create mode 100644 tests/goldens/compiler/failure/declaration-first-of-two-reference-errors.json create mode 100644 tests/goldens/compiler/failure/declaration-group-below-root.json create mode 100644 tests/goldens/compiler/failure/declaration-group-unsupported-entry.json create mode 100644 tests/goldens/compiler/failure/declaration-inaccessible-local-constant.json create mode 100644 tests/goldens/compiler/failure/declaration-invalid-constant-reference.json create mode 100644 tests/goldens/compiler/failure/declaration-root-nested.json create mode 100644 tests/goldens/compiler/failure/declaration-unclosed-quote-before-reference.json create mode 100644 tests/goldens/compiler/failure/declaration-unknown-enum-after-macro.json create mode 100644 tests/goldens/compiler/failure/duplicate-declaration.json create mode 100644 tests/goldens/compiler/failure/duplicate-hook.json create mode 100644 tests/goldens/compiler/failure/duplicate-model.json create mode 100644 tests/goldens/compiler/failure/duplicate-provider.json create mode 100644 tests/goldens/compiler/failure/duplicate-python-node.json create mode 100644 tests/goldens/compiler/failure/duplicate-scenario.json create mode 100644 tests/goldens/compiler/failure/duplicate-source.json create mode 100644 tests/goldens/compiler/failure/engine-error-case-folded-duplicate-column.json create mode 100644 tests/goldens/compiler/failure/engine-error-unset-environment-variable.json create mode 100644 tests/goldens/compiler/failure/engine-error-week-date-cursor-start.json create mode 100644 tests/goldens/compiler/failure/enum-duplicate-member.json create mode 100644 tests/goldens/compiler/failure/enum-parse.json create mode 100644 tests/goldens/compiler/failure/failing-macro.json create mode 100644 tests/goldens/compiler/failure/generic-audit-cursor-intrinsic.json create mode 100644 tests/goldens/compiler/failure/generic-audit-header.json create mode 100644 tests/goldens/compiler/failure/generic-audit-missing-argument.json create mode 100644 tests/goldens/compiler/failure/generic-audit-overrides-implicit-model.json create mode 100644 tests/goldens/compiler/failure/generic-audit-unknown-run-scope.json create mode 100644 tests/goldens/compiler/failure/hard-coded-relation-in-python.json create mode 100644 tests/goldens/compiler/failure/header-audit-identity.json create mode 100644 tests/goldens/compiler/failure/header-audit-severity.json create mode 100644 tests/goldens/compiler/failure/header-column-audits-not-a-list.json create mode 100644 tests/goldens/compiler/failure/header-column-type-not-text.json create mode 100644 tests/goldens/compiler/failure/header-column-unknown-metadata-key.json create mode 100644 tests/goldens/compiler/failure/header-duplicate-column.json create mode 100644 tests/goldens/compiler/failure/hook-reads-dependant.json create mode 100644 tests/goldens/compiler/failure/hook-role-unsupported-entry.json create mode 100644 tests/goldens/compiler/failure/hook-unknown-argument.json create mode 100644 tests/goldens/compiler/failure/hook-without-header.json create mode 100644 tests/goldens/compiler/failure/inaccessible-declaration.json create mode 100644 tests/goldens/compiler/failure/inherited-declaration-used-in-one-folder.json create mode 100644 tests/goldens/compiler/failure/invalid-decimal-constant.json create mode 100644 tests/goldens/compiler/failure/invalid-declaration-name.json create mode 100644 tests/goldens/compiler/failure/invalid-reference-calls-in-two-files.json create mode 100644 tests/goldens/compiler/failure/invalid-resource-name.json create mode 100644 tests/goldens/compiler/failure/legacy-event-exporters-directory.json create mode 100644 tests/goldens/compiler/failure/lifecycle-sink-default-parameter.json create mode 100644 tests/goldens/compiler/failure/lifecycle-sink-unknown-provider.json create mode 100644 tests/goldens/compiler/failure/lineage-opted-out-except-and-ctes.json create mode 100644 tests/goldens/compiler/failure/lineage-opted-out-quoted-columns.json create mode 100644 tests/goldens/compiler/failure/lineage-opted-out-star.json create mode 100644 tests/goldens/compiler/failure/lineage-opted-out-union-of-stars.json create mode 100644 tests/goldens/compiler/failure/list-constant-mixed-types.json create mode 100644 tests/goldens/compiler/failure/local-config-unknown-key.json create mode 100644 tests/goldens/compiler/failure/local-macro-needed-below-owner.json create mode 100644 tests/goldens/compiler/failure/local-singular-audit.json create mode 100644 tests/goldens/compiler/failure/macro-arguments-do-not-parse.json create mode 100644 tests/goldens/compiler/failure/macro-first-of-two-failing-models.json create mode 100644 tests/goldens/compiler/failure/macro-generated-reference.json create mode 100644 tests/goldens/compiler/failure/macro-import-cycle.json create mode 100644 tests/goldens/compiler/failure/macro-in-sibling-scope.json create mode 100644 tests/goldens/compiler/failure/macro-non-literal-argument.json create mode 100644 tests/goldens/compiler/failure/macro-output-calls-macro.json create mode 100644 tests/goldens/compiler/failure/macro-private-helper.json create mode 100644 tests/goldens/compiler/failure/macro-raises-after-a-call.json create mode 100644 tests/goldens/compiler/failure/macro-returned-invalid-reference-call.json create mode 100644 tests/goldens/compiler/failure/macro-returns-non-string.json create mode 100644 tests/goldens/compiler/failure/macro-test-helper-calls-udf.json create mode 100644 tests/goldens/compiler/failure/macro-test-helper-malformed-reference.json create mode 100644 tests/goldens/compiler/failure/macro-test-quoted-cte-name.json create mode 100644 tests/goldens/compiler/failure/managed-source-without-loader.json create mode 100644 tests/goldens/compiler/failure/materialization-import-error.json create mode 100644 tests/goldens/compiler/failure/missing-description.json create mode 100644 tests/goldens/compiler/failure/model-blank-column-type.json create mode 100644 tests/goldens/compiler/failure/model-bom.json create mode 100644 tests/goldens/compiler/failure/model-column-audit-unknown-severity.json create mode 100644 tests/goldens/compiler/failure/model-duplicate-column.json create mode 100644 tests/goldens/compiler/failure/model-header-nested-too-deep.json create mode 100644 tests/goldens/compiler/failure/model-header-syntax.json create mode 100644 tests/goldens/compiler/failure/model-missing-header.json create mode 100644 tests/goldens/compiler/failure/model-nullable-column-with-not-null.json create mode 100644 tests/goldens/compiler/failure/model-path-not-utf8.json create mode 100644 tests/goldens/compiler/failure/model-private-enum-misuse.json create mode 100644 tests/goldens/compiler/failure/model-reserved-name.json create mode 100644 tests/goldens/compiler/failure/model-sql-syntax.json create mode 100644 tests/goldens/compiler/failure/model-template-missing-environment.json create mode 100644 tests/goldens/compiler/failure/model-template-unknown-function.json create mode 100644 tests/goldens/compiler/failure/model-template-unterminated-string.json create mode 100644 tests/goldens/compiler/failure/model-unknown-header-key.json create mode 100644 tests/goldens/compiler/failure/multiple-errors-order.json create mode 100644 tests/goldens/compiler/failure/named-hook-missing-argument.json create mode 100644 tests/goldens/compiler/failure/named-role-nested-declaration.json create mode 100644 tests/goldens/compiler/failure/order-binding-metadata-contract.json create mode 100644 tests/goldens/compiler/failure/order-closest-column-help.json create mode 100644 tests/goldens/compiler/failure/order-contract-error-then-warning.json create mode 100644 tests/goldens/compiler/failure/order-poisoned-downstream-uses.json create mode 100644 tests/goldens/compiler/failure/order-two-failing-models.json create mode 100644 tests/goldens/compiler/failure/orphan-audit-factory.json create mode 100644 tests/goldens/compiler/failure/over-broad-global.json create mode 100644 tests/goldens/compiler/failure/path-defaults-unmatched.json create mode 100644 tests/goldens/compiler/failure/provider-invalid-settings.json create mode 100644 tests/goldens/compiler/failure/python-node-import-error.json create mode 100644 tests/goldens/compiler/failure/python-node-missing-description.json create mode 100644 tests/goldens/compiler/failure/python-node-syntax.json create mode 100644 tests/goldens/compiler/failure/python-outside-extension-root.json create mode 100644 tests/goldens/compiler/failure/reference-dbt-three-arguments.json create mode 100644 tests/goldens/compiler/failure/reference-empty-argument.json create mode 100644 tests/goldens/compiler/failure/reference-error-after-rejected-calls.json create mode 100644 tests/goldens/compiler/failure/reference-expression-name.json create mode 100644 tests/goldens/compiler/failure/reference-rejected-calls-across-files.json create mode 100644 tests/goldens/compiler/failure/reference-several-rejected-calls-in-one-file.json create mode 100644 tests/goldens/compiler/failure/reference-two-name-arguments.json create mode 100644 tests/goldens/compiler/failure/reference-unclosed-call.json create mode 100644 tests/goldens/compiler/failure/resource-name-collision.json create mode 100644 tests/goldens/compiler/failure/rule-custom-finding.json create mode 100644 tests/goldens/compiler/failure/rule-unused-cte.json create mode 100644 tests/goldens/compiler/failure/runtime-placeholder-without-default.json create mode 100644 tests/goldens/compiler/failure/scenario-check-reads-source.json create mode 100644 tests/goldens/compiler/failure/scenario-dependent-checks.json create mode 100644 tests/goldens/compiler/failure/scenario-first-of-two-errors.json create mode 100644 tests/goldens/compiler/failure/scenario-fixture-without-target.json create mode 100644 tests/goldens/compiler/failure/scenario-macro-mock.json create mode 100644 tests/goldens/compiler/failure/scenario-materialized-cte.json create mode 100644 tests/goldens/compiler/failure/scenario-parse.json create mode 100644 tests/goldens/compiler/failure/scenario-quoted-cte-name.json create mode 100644 tests/goldens/compiler/failure/scenario-statement-after-ctes.json create mode 100644 tests/goldens/compiler/failure/scenario-without-checks.json create mode 100644 tests/goldens/compiler/failure/schema-from-sibling-folder.json create mode 100644 tests/goldens/compiler/failure/schema-unterminated-declaration.json create mode 100644 tests/goldens/compiler/failure/schema-yml-invalid-yaml.json create mode 100644 tests/goldens/compiler/failure/schema-yml-model-metadata.json create mode 100644 tests/goldens/compiler/failure/scope-duplicate-scoped-enum.json create mode 100644 tests/goldens/compiler/failure/scope-first-of-two-visibility-errors.json create mode 100644 tests/goldens/compiler/failure/scope-inaccessible-local-macro.json create mode 100644 tests/goldens/compiler/failure/scope-macro-test-cte-without-body.json create mode 100644 tests/goldens/compiler/failure/scope-malformed-expected-relationship.json create mode 100644 tests/goldens/compiler/failure/scope-scenario-expected-without-target.json create mode 100644 tests/goldens/compiler/failure/scope-test-outside-relationship-grant.json create mode 100644 tests/goldens/compiler/failure/scoped-root-at-project-root.json create mode 100644 tests/goldens/compiler/failure/seed-declaration-without-csv.json create mode 100644 tests/goldens/compiler/failure/seed-header-mismatch.json create mode 100644 tests/goldens/compiler/failure/seed-without-header.json create mode 100644 tests/goldens/compiler/failure/seed-yaml-extension.json create mode 100644 tests/goldens/compiler/failure/set-operation-width.json create mode 100644 tests/goldens/compiler/failure/single-quoted-source-name.json create mode 100644 tests/goldens/compiler/failure/singular-audit-across-trees.json create mode 100644 tests/goldens/compiler/failure/singular-audit-one-resource.json create mode 100644 tests/goldens/compiler/failure/snapshot-schema-change-with-contract.json create mode 100644 tests/goldens/compiler/failure/source-description-unknown-variable.json create mode 100644 tests/goldens/compiler/failure/source-parse.json create mode 100644 tests/goldens/compiler/failure/source-unknown-key.json create mode 100644 tests/goldens/compiler/failure/source-unsupported-yaml.json create mode 100644 tests/goldens/compiler/failure/sql-function-header.json create mode 100644 tests/goldens/compiler/failure/sql-function-missing-returns.json create mode 100644 tests/goldens/compiler/failure/sql-function-template-namespace.json create mode 100644 tests/goldens/compiler/failure/sql-hook-unknown-key.json create mode 100644 tests/goldens/compiler/failure/syntax-function.json create mode 100644 tests/goldens/compiler/failure/syntax-hook.json create mode 100644 tests/goldens/compiler/failure/syntax-inline-hook.json create mode 100644 tests/goldens/compiler/failure/syntax-source-expression.json create mode 100644 tests/goldens/compiler/failure/table-function-arity.json create mode 100644 tests/goldens/compiler/failure/table-function-empty-call-argument.json create mode 100644 tests/goldens/compiler/failure/table-function-single-quoted-name.json create mode 100644 tests/goldens/compiler/failure/table-function-unclosed-call.json create mode 100644 tests/goldens/compiler/failure/table-function-without-arguments.json create mode 100644 tests/goldens/compiler/failure/target-managed-loader-collision.json create mode 100644 tests/goldens/compiler/failure/target-missing-database.json create mode 100644 tests/goldens/compiler/failure/target-missing-write-schema.json create mode 100644 tests/goldens/compiler/failure/target-named-without-schema.json create mode 100644 tests/goldens/compiler/failure/target-unknown-key.json create mode 100644 tests/goldens/compiler/failure/test-assertion-reads-unmocked-source.json create mode 100644 tests/goldens/compiler/failure/test-case-missing-parameter.json create mode 100644 tests/goldens/compiler/failure/test-expected-implicit-alias.json create mode 100644 tests/goldens/compiler/failure/test-expected-parenthesised-branch.json create mode 100644 tests/goldens/compiler/failure/test-expected-reads-unknown-model.json create mode 100644 tests/goldens/compiler/failure/test-expected-unknown-column.json create mode 100644 tests/goldens/compiler/failure/test-expects-unknown-model.json create mode 100644 tests/goldens/compiler/failure/test-helper-reads-unknown-model.json create mode 100644 tests/goldens/compiler/failure/test-mocks-unknown-source.json create mode 100644 tests/goldens/compiler/failure/test-parse.json create mode 100644 tests/goldens/compiler/failure/test-quoted-cte-name.json create mode 100644 tests/goldens/compiler/failure/test-reads-only-mocks.json create mode 100644 tests/goldens/compiler/failure/unclosed-dollar-quote-with-project-variable.json create mode 100644 tests/goldens/compiler/failure/ungrouped-column.json create mode 100644 tests/goldens/compiler/failure/unknown-column.json create mode 100644 tests/goldens/compiler/failure/unknown-constant.json create mode 100644 tests/goldens/compiler/failure/unknown-enum-member.json create mode 100644 tests/goldens/compiler/failure/unknown-macro.json create mode 100644 tests/goldens/compiler/failure/unknown-ref.json create mode 100644 tests/goldens/compiler/failure/unknown-scalar-function.json create mode 100644 tests/goldens/compiler/failure/unknown-source.json create mode 100644 tests/goldens/compiler/failure/unknown-table-function.json create mode 100644 tests/goldens/compiler/failure/unneeded-sql-analysis-opt-out.json create mode 100644 tests/goldens/compiler/failure/unquoted-ref-name.json create mode 100644 tests/goldens/compiler/failure/unset-environment-variable.json create mode 100644 tests/goldens/compiler/failure/unused-declaration.json create mode 100644 tests/goldens/compiler/failure/variable-not-defined.json create mode 100644 tests/goldens/compiler/fixture/dbt_interop.json create mode 100644 tests/goldens/compiler/fixture/source_loader_strategies.json create mode 100644 tests/goldens/compiler/fixture/waffle_shop.json create mode 100644 tests/goldens/compiler/seed/0-postgres.json create mode 100644 tests/goldens/compiler/seed/0-snowflake.json create mode 100644 tests/goldens/compiler/seed/0.json create mode 100644 tests/goldens/compiler/seed/1.json create mode 100644 tests/goldens/compiler/seed/10.json create mode 100644 tests/goldens/compiler/seed/11.json create mode 100644 tests/goldens/compiler/seed/2.json create mode 100644 tests/goldens/compiler/seed/3.json create mode 100644 tests/goldens/compiler/seed/4.json create mode 100644 tests/goldens/compiler/seed/5.json create mode 100644 tests/goldens/compiler/seed/6.json create mode 100644 tests/goldens/compiler/seed/7.json create mode 100644 tests/goldens/compiler/seed/8.json create mode 100644 tests/goldens/compiler/seed/9.json create mode 100644 tests/unit/scripts/compiler_differential/_helpers/goldens/__init__.py create mode 100644 tests/unit/scripts/compiler_differential/_helpers/goldens/_test_types.py create mode 100644 tests/unit/scripts/compiler_differential/_helpers/goldens/helpers.py create mode 100644 tests/unit/scripts/compiler_differential/_helpers/goldens/test_compiler_goldens.py create mode 100644 tests/unit/scripts/compiler_differential/_helpers/running/test_native_fallbacks.py create mode 100644 tests/unit/src/sqlbuild/compiler/frontier/test_native_fallback_recorder.py diff --git a/.github/workflows/verify.yml b/.github/workflows/verify.yml index ccc2a8763d..fcdec38771 100644 --- a/.github/workflows/verify.yml +++ b/.github/workflows/verify.yml @@ -96,7 +96,7 @@ jobs: ;; esac case "${path}" in - scripts/compiler_differential/*|scripts/run_compiler_differential.py|tests/*/scripts/compiler_differential/*|.github/workflows/verify.yml|Makefile) + scripts/compiler_differential/*|scripts/run_compiler_differential.py|tests/*/scripts/compiler_differential/*|tests/goldens/*|src/sqlbuild/cli/commands/_helpers/compile/*|.github/workflows/verify.yml|Makefile) compiler_differential=true ;; esac diff --git a/Makefile b/Makefile index 8512fc21b1..7ca518ad55 100644 --- a/Makefile +++ b/Makefile @@ -7,7 +7,8 @@ SHELL := /bin/bash test-e2e-duckdb-integrations test-e2e-performance \ test-e2e-cold-compile-performance test-e2e-cache-compile-performance \ test-e2e-dense-compile-performance test-e2e-varied-cache-performance \ - compiler-differential compiler-differential-shipped compiler-differential-dense + compiler-differential compiler-differential-shipped compiler-differential-dense \ + compiler-goldens format: uv run ruff format . @@ -293,11 +294,15 @@ COMPILER_DIFFERENTIAL_SEEDS ?= 12 COMPILER_DIFFERENTIAL_SHIPPED_SEEDS ?= 12 COMPILER_DIFFERENTIAL_DENSE_MODELS ?= 3000 COMPILER_DIFFERENTIAL_ARGS ?= +# Native-to-Python fallbacks and analysis deferrals must match the allow-list exactly, and every +# output its golden. Pass `--native-fallbacks update` in COMPILER_DIFFERENTIAL_ARGS to rewrite +# the list (see scripts/compiler_differential/README.md). +COMPILER_DIFFERENTIAL_GATES := --native-fallbacks check --goldens check compiler-differential: env PYTHONUNBUFFERED=1 uv run python -m scripts.run_compiler_differential \ --jobs $(COMPILER_DIFFERENTIAL_JOBS) --seeds $(COMPILER_DIFFERENTIAL_SEEDS) \ - $(COMPILER_DIFFERENTIAL_ARGS) + $(COMPILER_DIFFERENTIAL_GATES) $(COMPILER_DIFFERENTIAL_ARGS) # python vs native-preview above covers every native stage; this keeps the shipped `native` default # (native discovery and rendering) covered on a bounded corpus. @@ -305,7 +310,15 @@ compiler-differential-shipped: env PYTHONUNBUFFERED=1 uv run python -m scripts.run_compiler_differential \ --engines python native --corpus seeds failures \ --jobs $(COMPILER_DIFFERENTIAL_JOBS) --seeds $(COMPILER_DIFFERENTIAL_SHIPPED_SEEDS) \ - $(COMPILER_DIFFERENTIAL_ARGS) + $(COMPILER_DIFFERENTIAL_GATES) $(COMPILER_DIFFERENTIAL_ARGS) + +# Rewrite tests/goldens/compiler from the python oracle (from native once Python is deleted); +# review the diff like code. See scripts/compiler_differential/README.md. +compiler-goldens: + env PYTHONUNBUFFERED=1 uv run python -m scripts.run_compiler_differential \ + --engines python native --corpus fixtures examples seeds failures \ + --jobs $(COMPILER_DIFFERENTIAL_JOBS) --seeds $(COMPILER_DIFFERENTIAL_SEEDS) \ + --goldens update $(COMPILER_DIFFERENTIAL_ARGS) compiler-differential-dense: env PYTHONUNBUFFERED=1 uv run python -m scripts.run_compiler_differential \ diff --git a/scripts/compiler_differential/README.md b/scripts/compiler_differential/README.md new file mode 100644 index 0000000000..9a7da926d9 --- /dev/null +++ b/scripts/compiler_differential/README.md @@ -0,0 +1,90 @@ +# Compiler differential + +`make compiler-differential` compiles fixtures, examples, generated seeds and the failure corpus +under two compiler engines and fails on the first differing artifact. `make +compiler-differential-shipped` does the same for the shipped `native` default. Both also enforce +the two shared gates below. + +## Shared gates for native ports + +A port that moves work from the Python compiler to native code reuses these three mechanisms. It +does not add its own parity harness, property suite or E2E file. + +### Native-to-Python fallbacks and analysis deferrals + +`native_fallbacks.toml` is the one allow-list of all work native code still hands back to Python. +It covers both kinds of record: +- shipped-stage fallbacks, from `report_native_fallback(site=NativeFallbackSite., kind=...)`; +- preview analysis-stage deferrals, from `record_analysis_deferral` and `record_lineage_deferral`. + +Every entry names an engine, stage, site and kind, with its exact count per corpus. Use +`max_counts` instead of `counts` only for a count that is not stable, and give the reason in a +comment and in the PR body. + +The records cost nothing unless `SQLBUILD_ANALYSIS_RECORD_DIR` is set, which only the harness +sets. `--native-fallbacks check` runs in both make targets and fails CI in both directions: +- an entry, engine or corpus count that is not on the list fails; +- a listed entry whose count changed or that no longer occurs also fails, and the message says to + update or remove it, so the list only shrinks. + +The counts hold for the make targets' corpus (`--seeds 12`); other seed ranges are refused. + +**How a PR updates the list** + +1. A port deletes the Python code it replaces in the same PR. Its sites drop to zero and the gate + names the entries to remove; removing them is the port's test that the fallbacks are gone. + When the last entry for a `NativeFallbackSite` goes, delete the member and its + `report_native_fallback` call too. +2. If the corpus does not reach a site yet, first add a failure case or seed feature that does. +3. To rewrite the list from a run, use the same arguments as CI and add `--native-fallbacks + update` to both targets: + + ```sh + make compiler-differential COMPILER_DIFFERENTIAL_ARGS="--stage-captures \ + --require-discovery-coverage --require-render-coverage --require-analysis-coverage \ + --native-fallbacks update" + make compiler-differential-shipped COMPILER_DIFFERENTIAL_ARGS="--stage-captures \ + --require-discovery-coverage --require-render-coverage --native-fallbacks update" + ``` + + Review the diff. Any added entry or higher count needs a reason in the PR body; never add one + just to make CI pass. +4. The gate is proven by sabotage in `test_native_fallback_gate.py`: forcing a native stage to + defer everything must fail it. A new "native did the work" gate needs the same proof. + +### Golden outputs + +`tests/goldens/compiler//.json` holds one project's compile outputs: +- each command's exit code and diagnostics; +- the compiled SQL, one array item per line; +- the manifest without its volatile metadata and the SQL it repeats. + +Project paths, the SQLBuild version, invocation ids and timings are masked. + +- `--goldens check` (in both make targets) compares every engine with the goldens, so an + output-neutral port needs no new parity suite. +- `make compiler-goldens` rewrites them from the python oracle. Run it only when output changes + on purpose, and review the diff like code. +- Seeds outside the recorded range have no golden and are skipped; any other project without a + golden fails. +- Once the Python stages are deleted, goldens are rewritten from `native`. + +### All-engine user-facing errors + +`engine_error_cases()` in `_helpers/corpus/failure_cases.py` lists user-facing errors with their exact text. Each case +is part of the failure corpus, so it runs in both differentials and has a golden. It also runs in +`tests/e2e/scripts/compiler_differential/test_engine_error_cases.py` on the `python`, `native` +and `native-preview` engines, which must all report its `expected_code` and `expected_message`. + +A port that adds or changes a user-facing error adds one `failure_case(...)` to it: + +```python +failure_case( + name="engine-error-", + expected_code="P001", + files=mart_body_files("SELECT ...\n"), + expected_message=" for the project path>", +) +``` + +Then it runs `make compiler-goldens` to record the new case's golden. diff --git a/scripts/compiler_differential/_helpers/comparing/project_comparison.py b/scripts/compiler_differential/_helpers/comparing/project_comparison.py index ac14b3d1fd..eab8c75d95 100644 --- a/scripts/compiler_differential/_helpers/comparing/project_comparison.py +++ b/scripts/compiler_differential/_helpers/comparing/project_comparison.py @@ -13,10 +13,12 @@ from scripts.compiler_differential._helpers.coverage.analysis import project_analysis_kinds from scripts.compiler_differential._helpers.coverage.discovery import project_discovery_kinds from scripts.compiler_differential._helpers.coverage.render import project_render_kinds +from scripts.compiler_differential._helpers.goldens.goldens import golden_differences from scripts.compiler_differential._helpers.running.execution import run_engine from scripts.compiler_differential.constants import ( ERROR_SEVERITY, LEFT_SIDE, + PROJECT_DIRECTORY, RIGHT_SIDE, WARNING_SEVERITY, ) @@ -64,6 +66,17 @@ def compare_project(*, project: CorpusProject, options: DifferentialOptions) -> *_timeout_differences(project=project, runs=(left, right)), *_corpus_differences(project=project, run=left), *compare_engine_runs(project=project.name, left=left, right=right), + *( + golden_differences( + project=project, + runs=(left, right), + golden_dir=options.golden_dir, + mode=options.golden_mode, + masked_paths=_masked_paths(case_dir=case_dir, work_dir=options.work_dir), + ) + if options.golden_mode is not None and options.golden_dir is not None + else () + ), ] return ProjectComparison( project=project.name, @@ -80,6 +93,20 @@ def compare_project(*, project: CorpusProject, options: DifferentialOptions) -> ) +def _masked_paths(*, case_dir: Path, work_dir: Path) -> tuple[str, ...]: + """The run's absolute paths, longest first, so goldens never mention a temporary directory.""" + + workspace: Path = case_dir / PROJECT_DIRECTORY + paths: set[str] = { + str(workspace), + str(workspace.resolve()), + str(work_dir), + str(work_dir.resolve()), + } + ordered: list[str] = sorted(paths, key=lambda path: (-len(path), path)) + return tuple(ordered) + + def _materialized_source(*, project: CorpusProject, case_dir: Path) -> Path: if project.source_dir is not None: return project.source_dir diff --git a/scripts/compiler_differential/_helpers/corpus/emitted_codes.py b/scripts/compiler_differential/_helpers/corpus/emitted_codes.py index c7c2af8612..e34b38f0ac 100644 --- a/scripts/compiler_differential/_helpers/corpus/emitted_codes.py +++ b/scripts/compiler_differential/_helpers/corpus/emitted_codes.py @@ -15,11 +15,14 @@ from scripts.compiler_differential._helpers.running.execution import run_engine from scripts.compiler_differential.constants import ( ANALYSIS_CODE_SCAN_ROOTS, + COLD_COMPILE, CORPUS_FAILURES, DIAGNOSTIC_CODE_PATTERN, ERROR_SEVERITY, EXPECT_SUCCESS, + GOLDEN_PATH_MASK, LEFT_SIDE, + PROJECT_DIRECTORY, RENDER_CODE_SCAN_EXCLUDED, RENDER_RAISED_CODES, WARNING_SEVERITY, @@ -29,6 +32,8 @@ DifferentialOptions, EmittedCodes, EngineRun, + ExpectedOutcome, + FailureCase, ) from sqlbuild.compiler.auditing.constants import BUILT_IN_AUDIT_SHADOW_CODE from sqlbuild.compiler.compile import constants as compile_constants @@ -60,6 +65,20 @@ def emitted_failure_codes(*, options: DifferentialOptions) -> dict[str, EmittedC return {project.name: codes for project, codes in zip(corpus, emitted, strict=True)} +def case_emitted_codes(*, case: FailureCase, options: DifferentialOptions) -> EmittedCodes: + """Compile one failure case under the first engine and return what it really emitted.""" + + return _emitted_codes( + project=CorpusProject( + name=f"{CORPUS_FAILURES}/{case.name}", + commands=(COLD_COMPILE,), + expected=ExpectedOutcome(error_code=case.expected_code), + writer=case.write, + ), + options=options, + ) + + def discovery_error_codes() -> frozenset[str]: """Return the code of every `DiscoveryError` subclass, walking the whole hierarchy.""" @@ -182,14 +201,16 @@ def _emitted_codes(*, project: CorpusProject, options: DifferentialOptions) -> E options=options, ) first: dict[str, object] = first_diagnostic(outcome=run.outcomes[0], severity=ERROR_SEVERITY) + project_dir: str = str(case_dir / PROJECT_DIRECTORY) + message: str | None = first_diagnostic_message(outcome=run.outcomes[0], severity=ERROR_SEVERITY) notes: object = first.get("notes") line: object = first.get("line") column: object = first.get("column") return EmittedCodes( errors=diagnostic_codes(outcome=run.outcomes[0], severity=ERROR_SEVERITY), warnings=diagnostic_codes(outcome=run.outcomes[0], severity=WARNING_SEVERITY), - first_error_message=first_diagnostic_message( - outcome=run.outcomes[0], severity=ERROR_SEVERITY + first_error_message=( + None if message is None else message.replace(project_dir, GOLDEN_PATH_MASK) ), first_error_help=None if first.get("help") is None else str(first.get("help")), first_error_notes=tuple(str(note) for note in notes) if isinstance(notes, list) else (), diff --git a/scripts/compiler_differential/_helpers/corpus/failure_cases.py b/scripts/compiler_differential/_helpers/corpus/failure_cases.py index c22ae06ca7..ed800bcd5f 100644 --- a/scripts/compiler_differential/_helpers/corpus/failure_cases.py +++ b/scripts/compiler_differential/_helpers/corpus/failure_cases.py @@ -36,6 +36,8 @@ ) from scripts.compiler_differential.models import FailureCase +_MART_HEADER_START: str = 'MODEL (\n description "Order totals per customer",\n' + _RULES_CONFIG: str = '\n[rules]\nselect = ["{codes}"]\n' @@ -57,6 +59,7 @@ def all_failure_cases() -> tuple[FailureCase, ...]: *reference_failure_cases(), *attachment_failure_cases(), *analysis_failure_cases(), + *engine_error_cases(), ) @@ -591,3 +594,51 @@ def _compile_failure_cases() -> tuple[FailureCase, ...]: ), ), ) + + +def engine_error_cases() -> tuple[FailureCase, ...]: + """User-facing errors every engine must report with the exact text; see the README.""" + + return ( + failure_case( + name="engine-error-unset-environment-variable", + expected_code="P001", + files=mart_body_files( + "SELECT customer_id, '@@ENV:SQB_ENGINE_ERROR_UNSET_REGION' AS region\n" + 'FROM __ref("stg_orders")\n' + ), + expected_message=( + "unknown environment variable '@@ENV:SQB_ENGINE_ERROR_UNSET_REGION' in " + f"'/{FAILURE_MART_PATH}'" + ), + ), + failure_case( + name="engine-error-case-folded-duplicate-column", + expected_code="P001", + files={ + FAILURE_MART_PATH: _MART_HEADER_START + + ' columns (\n Größe (description "Size"),\n' + + ' größe (description "Size again"),\n ),\n);\n\n' + + 'SELECT customer_id AS größe\nFROM __ref("stg_orders")\n' + }, + expected_message=( + f"{FAILURE_MART_PATH} model has duplicate column 'größe' " + "(column names are case-insensitive)" + ), + ), + failure_case( + name="engine-error-week-date-cursor-start", + expected_code="P001", + files={ + FAILURE_MART_PATH: _MART_HEADER_START + + " materialized incremental,\n incremental_strategy append,\n" + + " cursor order_ts,\n cursor_type timestamp,\n cursor_grain day,\n" + + ' cursor_start "2024-W01-1T25:00",\n);\n\n' + + 'SELECT customer_id, CURRENT_TIMESTAMP AS order_ts\nFROM __ref("stg_orders")\n' + }, + expected_message=( + "model 'customer_totals': cursor_start value '2024-W01-1T25:00' is not a valid " + "ISO timestamp: hour must be in 0..23" + ), + ), + ) diff --git a/scripts/compiler_differential/_helpers/goldens/__init__.py b/scripts/compiler_differential/_helpers/goldens/__init__.py new file mode 100644 index 0000000000..3da85fcf6d --- /dev/null +++ b/scripts/compiler_differential/_helpers/goldens/__init__.py @@ -0,0 +1 @@ +"""Golden compile outputs for the shipped native stages.""" diff --git a/scripts/compiler_differential/_helpers/goldens/goldens.py b/scripts/compiler_differential/_helpers/goldens/goldens.py new file mode 100644 index 0000000000..c2472fc671 --- /dev/null +++ b/scripts/compiler_differential/_helpers/goldens/goldens.py @@ -0,0 +1,175 @@ +"""Golden compile outputs: one reviewable JSON file per corpus project, checked or rewritten.""" + +from __future__ import annotations + +import json +from importlib.metadata import version +from pathlib import Path + +from scripts.compiler_differential._helpers.comparing.compare import ( + as_json_object, + first_json_difference, +) +from scripts.compiler_differential._helpers.comparing.normalize import ( + normalize_artifact_text, + normalize_stderr, +) +from scripts.compiler_differential.constants import ( + GOLDEN_CORPUS_PREFIXES, + GOLDEN_MANIFEST_DROPPED_KEYS, + GOLDEN_MANIFEST_RESOURCE_SECTIONS, + GOLDEN_MISSING_HINT, + GOLDEN_MODE_CHECK, + GOLDEN_OPTIONAL_CORPORA, + GOLDEN_PATH_MASK, + GOLDEN_RESOURCE_DROPPED_FIELDS, + GOLDEN_SUFFIX, + GOLDEN_VERSION_MASK, +) +from scripts.compiler_differential.models import ( + CommandOutcome, + CorpusProject, + Difference, + Divergence, + EngineRun, +) + +_SQLBUILD_VERSION: str = version("sqlbuild") + + +def golden_differences( + *, + project: CorpusProject, + runs: tuple[EngineRun, ...], + golden_dir: Path, + mode: str, + masked_paths: tuple[str, ...], +) -> list[Difference]: + """Check every run against the project's golden, or rewrite it from the first (oracle) run.""" + + path: Path | None = golden_path(golden_dir=golden_dir, project=project.name) + if path is None: + return [] + payloads: list[dict[str, object]] = [ + golden_payload(run=run, masked_paths=masked_paths) for run in runs + ] + if mode != GOLDEN_MODE_CHECK: + path.parent.mkdir(parents=True, exist_ok=True) + _ = path.write_text(golden_text(payloads[0]), encoding="utf-8") + return [] + if not path.is_file(): + if path.parent.name in GOLDEN_OPTIONAL_CORPORA: + return [] + return [ + Difference( + project=project.name, + artifact=f"golden {path.name}", + location="file", + left="", + right=GOLDEN_MISSING_HINT, + labels=("golden", "hint"), + ) + ] + golden: object = json.loads(path.read_text(encoding="utf-8")) + found: list[Difference] = [] + for run, payload in zip(runs, payloads, strict=True): + divergence: Divergence | None = first_json_difference(left=golden, right=payload) + if divergence is not None: + found.append( + Difference( + project=project.name, + artifact=f"golden ({run.engine})", + location=divergence.location, + left=divergence.left, + right=divergence.right, + labels=("golden", run.engine), + ) + ) + return found + + +def golden_path(*, golden_dir: Path, project: str) -> Path | None: + """Where a corpus project's golden lives; projects outside the golden corpora have none.""" + + corpus, _, name = project.partition("/") + if corpus not in GOLDEN_CORPUS_PREFIXES or not name: + return None + return golden_dir / corpus / f"{name}{GOLDEN_SUFFIX}" + + +def golden_payload(*, run: EngineRun, masked_paths: tuple[str, ...]) -> dict[str, object]: + """Return a run's diagnostics, compiled SQL and slimmed manifest with run noise masked.""" + + return { + "commands": [ + _command(outcome=outcome, masked_paths=masked_paths) for outcome in run.outcomes + ], + "compiled": { + path: _lines( + text=_masked( + text=contents.decode("utf-8", "surrogateescape"), masked_paths=masked_paths + ) + ) + for path, contents in sorted(run.compiled.items()) + }, + "manifest": _manifest(text=run.manifest, masked_paths=masked_paths), + } + + +def golden_text(payload: dict[str, object]) -> str: + """Serialize a golden with one value per line so a changed output reads as a small diff.""" + + return json.dumps(payload, indent=1, ensure_ascii=False) + "\n" + + +def _command(*, outcome: CommandOutcome, masked_paths: tuple[str, ...]) -> dict[str, object]: + stdout: str = _masked(text=outcome.stdout, masked_paths=masked_paths) + try: + report: dict[str, object] | None = as_json_object(json.loads(stdout)) + except json.JSONDecodeError: + report = None + return { + "label": outcome.label, + "exit_code": "timed out" if outcome.timed_out else outcome.exit_code, + "diagnostics": ( + report.get("diagnostics", []) + if report is not None + else _lines( + text=normalize_stderr(_masked(text=outcome.stderr, masked_paths=masked_paths)) + ) + ), + } + + +def _manifest(*, text: str | None, masked_paths: tuple[str, ...]) -> object | None: + if text is None: + return None + manifest: dict[str, object] = ( + as_json_object(json.loads(_masked(text=text, masked_paths=masked_paths))) or {} + ) + kept: dict[str, object] = { + key: value for key, value in manifest.items() if key not in GOLDEN_MANIFEST_DROPPED_KEYS + } + for section in GOLDEN_MANIFEST_RESOURCE_SECTIONS: + resources: dict[str, object] = as_json_object(kept.get(section)) or {} + kept[section] = {name: _without_code(resource) for name, resource in resources.items()} + return kept + + +def _without_code(resource: object) -> object: + fields: dict[str, object] | None = as_json_object(resource) + if fields is None: + return resource + return { + key: value for key, value in fields.items() if key not in GOLDEN_RESOURCE_DROPPED_FIELDS + } + + +def _masked(*, text: str, masked_paths: tuple[str, ...]) -> str: + for path in masked_paths: + text = text.replace(path, GOLDEN_PATH_MASK) + return normalize_artifact_text(text).replace(_SQLBUILD_VERSION, GOLDEN_VERSION_MASK) + + +def _lines(*, text: str) -> list[str]: + return text.split("\n") diff --git a/scripts/compiler_differential/_helpers/running/native_fallbacks.py b/scripts/compiler_differential/_helpers/running/native_fallbacks.py new file mode 100644 index 0000000000..a70d6e5e10 --- /dev/null +++ b/scripts/compiler_differential/_helpers/running/native_fallbacks.py @@ -0,0 +1,205 @@ +"""The one allow-list of native-to-Python fallbacks and analysis deferrals, checked or rewritten.""" + +from __future__ import annotations + +import tomllib +from collections import Counter +from pathlib import Path + +from scripts.compiler_differential.constants import ( + CORPUS_SEEDS, + NATIVE_FALLBACK_COUNTS_FIELD, + NATIVE_FALLBACK_DEFERRAL_STAGES, + NATIVE_FALLBACK_DEFERRAL_UNKNOWN_STAGE, + NATIVE_FALLBACK_LIST_HEADER, + NATIVE_FALLBACK_MAX_COUNTS_FIELD, + NATIVE_FALLBACK_MODE_UPDATE, + NATIVE_FALLBACK_PROJECT_CORPUS, + NATIVE_FALLBACK_SITE_SEPARATOR, +) +from scripts.compiler_differential.models import ( + AllowList, + FallbackGateRequest, + ProjectComparison, + RecordedRun, +) +from scripts.compiler_differential.types import FallbackKey + + +def native_fallback_gate( + *, request: FallbackGateRequest | None, comparisons: list[ProjectComparison] +) -> tuple[str, ...]: + """Check the run against the allow-list, or rewrite it; print and return every problem.""" + + if request is None: + return () + observed: dict[FallbackKey, dict[str, int]] = observed_fallbacks( + comparisons=comparisons, engines=request.run.engines + ) + if request.mode == NATIVE_FALLBACK_MODE_UPDATE: + write_allow_list(path=request.allow_list, observed=observed, run=request.run) + print(f"Native fallback allow-list rewritten: {request.allow_list}") + return () + problems: list[str] = fallback_problems( + observed=observed, allowed=read_allow_list(request.allow_list), run=request.run + ) + for problem in problems: + print(f"Native fallback allow-list: {problem}") + return tuple(problems) + + +def observed_fallbacks( + *, comparisons: list[ProjectComparison], engines: tuple[str, ...] +) -> dict[FallbackKey, dict[str, int]]: + """Sum every engine's fallback and analysis deferral records by entry and corpus.""" + + counts: Counter[tuple[FallbackKey, str]] = Counter() + for comparison in comparisons: + corpus: str = comparison.project.split("/", 1)[0] + for engine, records in zip(engines, comparison.records or (), strict=False): + for (site, kind), count in records.fallbacks.items(): + stage: str = site.split(NATIVE_FALLBACK_SITE_SEPARATOR, 1)[0] + counts[((engine, stage, site, kind), corpus)] += count + for (kind, site), count in records.deferrals.items(): + stage = NATIVE_FALLBACK_DEFERRAL_STAGES.get( + site, NATIVE_FALLBACK_DEFERRAL_UNKNOWN_STAGE + ) + counts[((engine, stage, site, kind), corpus)] += count + observed: dict[FallbackKey, dict[str, int]] = {} + for (key, corpus), count in sorted(counts.items()): + observed.setdefault(key, {})[corpus] = count + return observed + + +def read_allow_list(path: Path) -> AllowList: + """Parse the allow-list file; a missing file allows nothing.""" + + if not path.is_file(): + return AllowList(seed_start=0, seeds=0, counts={}, max_counts={}) + document: dict[str, object] = tomllib.loads(path.read_text(encoding="utf-8")) + counts: dict[FallbackKey, dict[str, int]] = {} + max_counts: dict[FallbackKey, dict[str, int]] = {} + entries: object = document.get("entry", []) + for entry in entries if isinstance(entries, list) else []: + key: FallbackKey = ( + str(entry["engine"]), + str(entry["stage"]), + str(entry["site"]), + str(entry["kind"]), + ) + bounded: bool = NATIVE_FALLBACK_MAX_COUNTS_FIELD in entry + field: str = NATIVE_FALLBACK_MAX_COUNTS_FIELD if bounded else NATIVE_FALLBACK_COUNTS_FIELD + target: dict[FallbackKey, dict[str, int]] = max_counts if bounded else counts + target[key] = {str(name): int(value) for name, value in entry[field].items()} + return AllowList( + seed_start=int(str(document.get("seed_start", 0))), + seeds=int(str(document.get("seeds", 0))), + counts=counts, + max_counts=max_counts, + ) + + +def fallback_problems( + *, observed: dict[FallbackKey, dict[str, int]], allowed: AllowList, run: RecordedRun +) -> list[str]: + """Every way the run's records differ from the allow-list, as instructions to the author.""" + + if CORPUS_SEEDS in run.corpora and (run.seed_start, run.seeds) != ( + allowed.seed_start, + allowed.seeds, + ): + return [ + f"the allow-list holds counts for --seed-start {allowed.seed_start} --seeds " + f"{allowed.seeds}; this run used --seed-start {run.seed_start} --seeds {run.seeds}" + ] + corpora: frozenset[str] = _corpus_prefixes(run.corpora) + problems: list[str] = [] + for key in sorted({*allowed.counts, *allowed.max_counts, *observed}): + if key[0] not in run.engines: + continue + actual: dict[str, int] = observed.get(key, {}) + exact: dict[str, int] | None = allowed.counts.get(key) + bound: dict[str, int] | None = allowed.max_counts.get(key) + for corpus in sorted({*actual, *(exact or {}), *(bound or {})} & corpora): + problem: str | None = _corpus_problem( + label=_label(key=key, corpus=corpus), + actual=actual.get(corpus, 0), + exact=None if exact is None else exact.get(corpus, 0), + bound=None if bound is None else bound.get(corpus, 0), + ) + if problem is not None: + problems.append(problem) + return problems + + +def write_allow_list( + *, path: Path, observed: dict[FallbackKey, dict[str, int]], run: RecordedRun +) -> None: + """Record the run's counts, keeping entries for engines and corpora this run did not cover.""" + + previous: AllowList = read_allow_list(path) + covered: frozenset[str] = _corpus_prefixes(run.corpora) + counts: dict[FallbackKey, dict[str, int]] = {} + for key, by_corpus in previous.counts.items(): + kept: dict[str, int] = { + corpus: count + for corpus, count in by_corpus.items() + if key[0] not in run.engines or corpus not in covered + } + if kept: + counts[key] = kept + for key, by_corpus in observed.items(): + if key[0] in run.engines and key not in previous.max_counts: + counts[key] = {**counts.get(key, {}), **by_corpus} + entries: list[tuple[FallbackKey, str, dict[str, int]]] = [ + *((key, NATIVE_FALLBACK_COUNTS_FIELD, values) for key, values in counts.items()), + *( + (key, NATIVE_FALLBACK_MAX_COUNTS_FIELD, values) + for key, values in previous.max_counts.items() + ), + ] + seeded: bool = CORPUS_SEEDS in run.corpora + lines: list[str] = [ + NATIVE_FALLBACK_LIST_HEADER, + f"seed_start = {run.seed_start if seeded else previous.seed_start}", + f"seeds = {run.seeds if seeded else previous.seeds}", + ] + for (engine, stage, site, kind), field, values in sorted(entries): + inline: str = ", ".join(f"{corpus} = {count}" for corpus, count in sorted(values.items())) + lines.extend( + ( + "", + "[[entry]]", + f'engine = "{engine}"', + f'stage = "{stage}"', + f'site = "{site}"', + f'kind = "{kind}"', + f"{field} = {{ {inline} }}", + ) + ) + _ = path.write_text("\n".join(lines) + "\n", encoding="utf-8") + + +def _corpus_problem(*, label: str, actual: int, exact: int | None, bound: int | None) -> str | None: + if exact is None and bound is None: + return f"{label}: {actual} not on the allow-list; port it or list it with a reason" + if actual == 0: + return f"{label}: listed but no longer occurs; remove it from the allow-list" + if bound is not None: + return None if actual <= bound else f"{label}: {actual} exceeds the allowed {bound}" + if actual != exact: + return f"{label}: {actual} where the allow-list expects {exact}; update the count" + return None + + +def _label(*, key: FallbackKey, corpus: str) -> str: + engine, stage, site, kind = key + return f"{engine} {stage} {site} {kind} ({corpus})" + + +def _corpus_prefixes(corpora: tuple[str, ...]) -> frozenset[str]: + """Project-name prefixes of the selected corpora (`seeds` names projects `seed/...`).""" + + return frozenset( + {*(corpus.removesuffix("s") for corpus in corpora), NATIVE_FALLBACK_PROJECT_CORPUS} + ) diff --git a/scripts/compiler_differential/_helpers/running/records.py b/scripts/compiler_differential/_helpers/running/records.py index b1ab9e0145..9cecee24f2 100644 --- a/scripts/compiler_differential/_helpers/running/records.py +++ b/scripts/compiler_differential/_helpers/running/records.py @@ -1,4 +1,4 @@ -"""Read the debug analysis records one engine's processes wrote: wheel call sites and deferrals.""" +"""Read the debug records one engine's processes wrote: wheel sites, deferrals and fallbacks.""" from __future__ import annotations @@ -10,16 +10,18 @@ from scripts.compiler_differential._helpers.comparing.compare import as_json_object from scripts.compiler_differential.constants import ( ANALYSIS_DEFERRAL_RECORD_PREFIX, + NATIVE_FALLBACK_RECORD_PREFIX, WHEEL_SITE_RECORD_PREFIX, ) from scripts.compiler_differential.models import AnalysisRecords, ProjectComparison def read_analysis_records(root: Path) -> AnalysisRecords: - """Sum every process's wheel-site counts and `{"kind", "site"}` deferral lines below `root`.""" + """Sum every process's wheel-site, deferral and native-fallback records below `root`.""" sites: Counter[tuple[str, str]] = Counter() deferrals: Counter[tuple[str, str]] = Counter() + fallbacks: Counter[tuple[str, str]] = Counter() for path in sorted(root.rglob(f"{WHEEL_SITE_RECORD_PREFIX}*.json")) if root.is_dir() else (): payload: dict[str, object] = as_json_object(json.loads(path.read_text("utf-8"))) or {} rows: object = payload.get("calls") @@ -32,18 +34,29 @@ def read_analysis_records(root: Path) -> AnalysisRecords: for line in path.read_text("utf-8").splitlines(): record: dict[str, object] = as_json_object(json.loads(line)) or {} deferrals[(str(record.get("kind")), str(record.get("site")))] += 1 - return AnalysisRecords(wheel_sites=dict(sites), deferrals=dict(deferrals)) + for path in ( + sorted(root.rglob(f"{NATIVE_FALLBACK_RECORD_PREFIX}*.json")) if root.is_dir() else () + ): + payload = as_json_object(json.loads(path.read_text("utf-8"))) or {} + rows = payload.get("fallbacks") + for row in rows if isinstance(rows, list) else (): + site, kind, count = cast(list[object], row) + fallbacks[(str(site), str(kind))] += int(str(count)) + return AnalysisRecords( + wheel_sites=dict(sites), deferrals=dict(deferrals), fallbacks=dict(fallbacks) + ) def wheel_site_report( *, comparisons: list[ProjectComparison], engines: tuple[str, str] ) -> dict[str, object]: - """Return wheel calls per engine, site and API, split by corpus, plus deferral counts.""" + """Return wheel calls, deferrals and native fallbacks per engine, split by corpus.""" report: dict[str, object] = {} for side, engine in enumerate(engines): sites: dict[str, dict[str, int]] = {} deferrals: dict[str, dict[str, int]] = {} + fallbacks: dict[str, dict[str, int]] = {} for comparison in comparisons: records: AnalysisRecords | None = ( comparison.records[side] if comparison.records else None @@ -57,15 +70,19 @@ def wheel_site_report( for (kind, site), count in records.deferrals.items(): by_corpus = deferrals.setdefault(f"{kind} {site}", {}) by_corpus[corpus] = by_corpus.get(corpus, 0) + count + for (site, kind), count in records.fallbacks.items(): + by_corpus = fallbacks.setdefault(f"{site} {kind}", {}) + by_corpus[corpus] = by_corpus.get(corpus, 0) + count report[engine] = { "wheel_sites": dict(sorted(sites.items())), "deferrals": dict(sorted(deferrals.items())), + "native_fallbacks": dict(sorted(fallbacks.items())), } return report def format_wheel_site_report(report: dict[str, object]) -> str: - """Render the wheel-site and deferral counts for the run log; nothing here gates.""" + """Render the wheel-site, deferral and fallback counts for the run log; nothing here gates.""" lines: list[str] = [] for engine, raw in report.items(): @@ -73,6 +90,7 @@ def format_wheel_site_report(report: dict[str, object]) -> str: for label, key in ( ("Polyglot wheel calls", "wheel_sites"), ("Analysis deferrals", "deferrals"), + ("Native-to-Python fallbacks", "native_fallbacks"), ): counts: dict[str, object] = as_json_object(entry.get(key)) or {} lines.append(f"{label} ({engine}): {'none recorded' if not counts else ''}".rstrip()) @@ -82,3 +100,14 @@ def format_wheel_site_report(report: dict[str, object]) -> str: total: int = sum(int(str(count)) for count in corpora.values()) lines.append(f" {total:>7} {name} ({split})") return "\n".join(lines) + + +def write_wheel_site_report( + *, path: Path, comparisons: list[ProjectComparison], engines: tuple[str, str] +) -> None: + """Write the wheel-site, deferral and fallback counts to `path` as JSON and print them.""" + + report: dict[str, object] = wheel_site_report(comparisons=comparisons, engines=engines) + path.parent.mkdir(parents=True, exist_ok=True) + _ = path.write_text(json.dumps(report, indent=2, sort_keys=True) + "\n", encoding="utf-8") + print(format_wheel_site_report(report)) diff --git a/scripts/compiler_differential/_helpers/running/report.py b/scripts/compiler_differential/_helpers/running/report.py index ba7d523fbb..61289da4d1 100644 --- a/scripts/compiler_differential/_helpers/running/report.py +++ b/scripts/compiler_differential/_helpers/running/report.py @@ -31,15 +31,16 @@ def format_summary( engines: tuple[str, str], seconds: float, missing_coverage: dict[str, tuple[str, ...]] | None = None, + gate_failures: tuple[str, ...] = (), ) -> str: - """Summarize the run; any difference or missing coverage, keyed by stage, reports FAILED.""" + """Summarize the run; a difference, missing coverage or gate failure reports FAILED.""" differing: list[ProjectComparison] = [item for item in comparisons if item.differences] gaps: dict[str, tuple[str, ...]] = { stage: kinds for stage, kinds in (missing_coverage or {}).items() if kinds } left_engine, right_engine = engines - if not differing and not gaps: + if not differing and not gaps and not gate_failures: return ( f"Compiler differential passed: {len(comparisons)} projects identical " f"({left_engine} vs {right_engine}) in {seconds:.1f}s" @@ -54,6 +55,8 @@ def format_summary( lines.extend( f"Required {stage} coverage missing: {', '.join(kinds)}" for stage, kinds in gaps.items() ) + if gate_failures: + lines.append(f"Native fallback allow-list: {len(gate_failures)} problems (listed above)") return "\n".join(lines) diff --git a/scripts/compiler_differential/_helpers/running/run.py b/scripts/compiler_differential/_helpers/running/run.py index d6dbbb668f..f5ad742798 100644 --- a/scripts/compiler_differential/_helpers/running/run.py +++ b/scripts/compiler_differential/_helpers/running/run.py @@ -14,7 +14,9 @@ from scripts.compiler_differential.models import ( CorpusProject, DifferentialOptions, + FallbackGateRequest, ProjectComparison, + RecordedRun, ) @@ -31,7 +33,28 @@ def differential_options(*, args: argparse.Namespace, work_dir: Path) -> Differe require_discovery_coverage=args.require_discovery_coverage, require_render_coverage=args.require_render_coverage, require_analysis_coverage=args.require_analysis_coverage, - analysis_records=args.wheel_site_report is not None, + analysis_records=args.wheel_site_report is not None or args.native_fallbacks is not None, + golden_mode=args.goldens, + golden_dir=args.golden_dir, + ) + + +def fallback_gate_request( + *, args: argparse.Namespace, engines: tuple[str, str] +) -> FallbackGateRequest | None: + """Return the run's fallback allow-list gate, or None when it is off.""" + + if args.native_fallbacks is None: + return None + return FallbackGateRequest( + mode=args.native_fallbacks, + allow_list=args.native_fallback_list, + run=RecordedRun( + engines=engines, + corpora=tuple(args.corpus), + seed_start=args.seed_start, + seeds=args.seeds, + ), ) diff --git a/scripts/compiler_differential/constants.py b/scripts/compiler_differential/constants.py index 008e546c06..fd38e6e757 100644 --- a/scripts/compiler_differential/constants.py +++ b/scripts/compiler_differential/constants.py @@ -10,7 +10,42 @@ ANALYSIS_RECORD_ENV_VAR: str = "SQLBUILD_ANALYSIS_RECORD_DIR" WHEEL_SITE_RECORD_PREFIX: str = "polyglot-sites-" ANALYSIS_DEFERRAL_RECORD_PREFIX: str = "analysis-deferrals-" +NATIVE_FALLBACK_RECORD_PREFIX: str = "native-fallbacks-" +NATIVE_FALLBACK_LIST: str = "scripts/compiler_differential/native_fallbacks.toml" +NATIVE_FALLBACK_MODE_UPDATE: str = "update" +NATIVE_FALLBACK_MODES: tuple[str, ...] = ("check", NATIVE_FALLBACK_MODE_UPDATE) +NATIVE_FALLBACK_SITE_SEPARATOR: str = "." +NATIVE_FALLBACK_MAX_COUNTS_FIELD: str = "max_counts" +NATIVE_FALLBACK_COUNTS_FIELD: str = "counts" +NATIVE_FALLBACK_PROJECT_CORPUS: str = "project" +NATIVE_FALLBACK_DEFERRAL_STAGES: dict[str, str] = { + "analysis_session": "model_analysis", + "fast_columns.py": "lineage_facts", +} +NATIVE_FALLBACK_DEFERRAL_UNKNOWN_STAGE: str = "analysis" +NATIVE_FALLBACK_LIST_HEADER: str = ( + "# Every native-to-Python fallback and preview analysis deferral the CI corpus may still\n" + "# reach, with its exact count per corpus. `--native-fallbacks check` fails on anything new,\n" + "# changed or gone. Ports delete their entries; see scripts/compiler_differential/README.md." +) RECORDS_DIRECTORY: str = "records" +GOLDEN_DIRECTORY: str = "tests/goldens/compiler" +GOLDEN_MODE_CHECK: str = "check" +GOLDEN_MODE_UPDATE: str = "update" +GOLDEN_MODES: tuple[str, ...] = (GOLDEN_MODE_CHECK, GOLDEN_MODE_UPDATE) +GOLDEN_CORPUS_PREFIXES: frozenset[str] = frozenset( + {"fixture", "example", "seed", "failure", "project"} +) +GOLDEN_OPTIONAL_CORPORA: frozenset[str] = frozenset({"seed"}) +GOLDEN_SUFFIX: str = ".json" +GOLDEN_MANIFEST_DROPPED_KEYS: frozenset[str] = frozenset({"metadata", "child_map"}) +GOLDEN_MANIFEST_RESOURCE_SECTIONS: tuple[str, ...] = ("nodes", "sources", "macros") +GOLDEN_RESOURCE_DROPPED_FIELDS: frozenset[str] = frozenset( + {"raw_code", "compiled_code", "checksum", "created_at"} +) +GOLDEN_PATH_MASK: str = "" +GOLDEN_VERSION_MASK: str = "" +GOLDEN_MISSING_HINT: str = "no golden; record it with `make compiler-goldens`" ENGINE_NAMES: tuple[str, ...] = ("python", "native", "native-preview") DEFAULT_ENGINES: tuple[str, str] = ("python", "native-preview") SQB_ENTRY: str = "import sys; from sqlbuild.cli.entry.main.entry import main; sys.exit(main())" diff --git a/scripts/compiler_differential/main/differential.py b/scripts/compiler_differential/main/differential.py index 446b25b09e..622417aa59 100644 --- a/scripts/compiler_differential/main/differential.py +++ b/scripts/compiler_differential/main/differential.py @@ -3,7 +3,6 @@ from __future__ import annotations import argparse -import json import os import sys import tempfile @@ -13,11 +12,9 @@ from scripts.compiler_differential._helpers.coverage.analysis import required_analysis_kinds from scripts.compiler_differential._helpers.coverage.discovery import required_discovery_kinds from scripts.compiler_differential._helpers.coverage.render import required_render_kinds +from scripts.compiler_differential._helpers.running.native_fallbacks import native_fallback_gate from scripts.compiler_differential._helpers.running.options import parse_expected_outcome -from scripts.compiler_differential._helpers.running.records import ( - format_wheel_site_report, - wheel_site_report, -) +from scripts.compiler_differential._helpers.running.records import write_wheel_site_report from scripts.compiler_differential._helpers.running.report import ( format_analysis_coverage, format_discovery_coverage, @@ -27,6 +24,7 @@ from scripts.compiler_differential._helpers.running.run import ( compare_corpus, differential_options, + fallback_gate_request, selected_corpus, ) from scripts.compiler_differential.constants import ( @@ -37,6 +35,10 @@ DEFAULT_SEED_COUNT, ENGINE_NAMES, EXPECT_SUCCESS, + GOLDEN_DIRECTORY, + GOLDEN_MODES, + NATIVE_FALLBACK_LIST, + NATIVE_FALLBACK_MODES, PER_PULL_REQUEST_CORPORA, ) from scripts.compiler_differential.models import DifferentialOptions, ProjectComparison @@ -60,25 +62,26 @@ def run_compiler_differential(argv: list[str] | None = None) -> int: else {} ) if args.wheel_site_report is not None: - report: dict[str, object] = wheel_site_report( - comparisons=comparisons, engines=options.engines - ) - args.wheel_site_report.parent.mkdir(parents=True, exist_ok=True) - _ = args.wheel_site_report.write_text( - json.dumps(report, indent=2, sort_keys=True) + "\n", encoding="utf-8" + write_wheel_site_report( + path=args.wheel_site_report, comparisons=comparisons, engines=options.engines ) - print(format_wheel_site_report(report)) + fallback_failures: tuple[str, ...] = native_fallback_gate( + request=fallback_gate_request(args=args, engines=options.engines), + comparisons=comparisons, + ) print( format_summary( comparisons=comparisons, engines=options.engines, seconds=time.monotonic() - started, missing_coverage=missing_coverage, + gate_failures=fallback_failures, ) ) return ( 1 if any(missing_coverage.values()) + or fallback_failures or any(comparison.differences for comparison in comparisons) else 0 ) @@ -179,6 +182,37 @@ def _parse_args(argv: list[str] | None) -> argparse.Namespace: "process, print them and write them to PATH as JSON (reported, never gated)" ), ) + parser.add_argument( + "--native-fallbacks", + choices=NATIVE_FALLBACK_MODES, + default=None, + help=( + "record native-to-Python fallbacks and analysis deferrals in every engine process and " + "check them against the allow-list (new, changed or vanished entries fail), or " + "rewrite the list from this run" + ), + ) + parser.add_argument( + "--native-fallback-list", + type=Path, + default=None, + help=f"fallback allow-list file (default: {NATIVE_FALLBACK_LIST} in the repository)", + ) + parser.add_argument( + "--goldens", + choices=GOLDEN_MODES, + default=None, + help=( + "check every engine's fixture, example, seed and failure outputs against the golden " + "files, or rewrite them from the first (oracle) engine" + ), + ) + parser.add_argument( + "--golden-dir", + type=Path, + default=None, + help=f"golden output directory (default: {GOLDEN_DIRECTORY} in the repository)", + ) parser.add_argument( "--engine-env", action="append", @@ -189,6 +223,11 @@ def _parse_args(argv: list[str] | None) -> argparse.Namespace: parser.add_argument("--work-dir", type=Path, default=None, help="keep run directories here") parser.add_argument("--python", type=Path, default=Path(sys.executable)) args: argparse.Namespace = parser.parse_args(argv) + repo_root: Path = Path(__file__).resolve().parents[3] + if args.golden_dir is None: + args.golden_dir = repo_root / GOLDEN_DIRECTORY + if args.native_fallback_list is None: + args.native_fallback_list = repo_root / NATIVE_FALLBACK_LIST for flag, required in ( ("--require-discovery-coverage", args.require_discovery_coverage), ("--require-render-coverage", args.require_render_coverage), diff --git a/scripts/compiler_differential/models.py b/scripts/compiler_differential/models.py index 0a9cd20d14..81539246d7 100644 --- a/scripts/compiler_differential/models.py +++ b/scripts/compiler_differential/models.py @@ -6,6 +6,8 @@ from dataclasses import dataclass, field from pathlib import Path +from scripts.compiler_differential.types import FallbackKey + @dataclass(frozen=True) class DifferentialCommand: @@ -51,10 +53,11 @@ class CommandOutcome: @dataclass(frozen=True) class AnalysisRecords: - """Wheel calls by `(site, api)` and analysis deferrals by `(kind, site)` for one run.""" + """Wheel calls by `(site, api)`, deferrals by `(kind, site)`, fallbacks by `(site, kind)`.""" wheel_sites: dict[tuple[str, str], int] = field(default_factory=dict) deferrals: dict[tuple[str, str], int] = field(default_factory=dict) + fallbacks: dict[tuple[str, str], int] = field(default_factory=dict) @dataclass(frozen=True) @@ -118,6 +121,8 @@ class DifferentialOptions: require_render_coverage: bool = False require_analysis_coverage: bool = False analysis_records: bool = False + golden_mode: str | None = None + golden_dir: Path | None = None @dataclass(frozen=True) @@ -205,3 +210,32 @@ def first_error(self) -> str | None: """Return the first reported error code, if any.""" return self.errors[0] if self.errors else None + + +@dataclass(frozen=True) +class RecordedRun: + """The engines, corpora and seed range one harness run covered.""" + + engines: tuple[str, ...] + corpora: tuple[str, ...] + seed_start: int + seeds: int + + +@dataclass(frozen=True) +class AllowList: + """Allowed counts by `(engine, stage, site, kind)` and corpus, and the seeds they hold for.""" + + seed_start: int + seeds: int + counts: dict[FallbackKey, dict[str, int]] + max_counts: dict[FallbackKey, dict[str, int]] + + +@dataclass(frozen=True) +class FallbackGateRequest: + """One run's fallback gate: check or rewrite `allow_list` from what the engines recorded.""" + + mode: str + allow_list: Path + run: RecordedRun diff --git a/scripts/compiler_differential/native_fallbacks.toml b/scripts/compiler_differential/native_fallbacks.toml new file mode 100644 index 0000000000..85d81f8cb2 --- /dev/null +++ b/scripts/compiler_differential/native_fallbacks.toml @@ -0,0 +1,285 @@ +# Every native-to-Python fallback and preview analysis deferral the CI corpus may still +# reach, with its exact count per corpus. `--native-fallbacks check` fails on anything new, +# changed or gone. Ports delete their entries; see scripts/compiler_differential/README.md. +seed_start = 0 +seeds = 12 + +[[entry]] +engine = "native" +stage = "attachments" +site = "attachments.audit_rendering" +kind = "deferred" +counts = { seed = 16 } + +[[entry]] +engine = "native" +stage = "attachments" +site = "attachments.scenario_independence" +kind = "deferred" +counts = { failure = 1 } + +[[entry]] +engine = "native" +stage = "declaration_scopes" +site = "declaration_scopes.rebind_lookup" +kind = "deferred" +counts = { failure = 1, seed = 781 } + +[[entry]] +engine = "native" +stage = "declaration_scopes" +site = "declaration_scopes.relationship_ctes" +kind = "expected_names" +counts = { failure = 3, seed = 26 } + +[[entry]] +engine = "native" +stage = "macro_calls" +site = "macro_calls.arguments" +kind = "deferred" +counts = { failure = 14, seed = 484 } + +[[entry]] +engine = "native" +stage = "macro_calls" +site = "macro_calls.resolution" +kind = "deferred" +counts = { failure = 4, seed = 3 } + +[[entry]] +engine = "native" +stage = "macro_calls" +site = "macro_calls.unbridged_expansion" +kind = "macro_mocks" +counts = { seed = 238 } + +[[entry]] +engine = "native" +stage = "model_config" +site = "model_config.header_metadata" +kind = "stale" +counts = { seed = 19 } + +[[entry]] +engine = "native" +stage = "model_config" +site = "model_config.header_metadata" +kind = "unsupported" +counts = { failure = 1 } + +[[entry]] +engine = "native" +stage = "model_config" +site = "model_config.python_templates" +kind = "api" +counts = { failure = 2, seed = 36 } + +[[entry]] +engine = "native" +stage = "model_config" +site = "model_config.python_templates" +kind = "config" +counts = { failure = 93, seed = 36 } + +[[entry]] +engine = "native" +stage = "model_config" +site = "model_config.python_templates" +kind = "connection" +counts = { failure = 95, seed = 63 } + +[[entry]] +engine = "native" +stage = "model_config" +site = "model_config.python_templates" +kind = "seed_target" +counts = { seed = 30 } + +[[entry]] +engine = "native" +stage = "model_config" +site = "model_config.python_templates" +kind = "source_target" +counts = { seed = 23 } + +[[entry]] +engine = "native" +stage = "model_config" +site = "model_config.validators" +kind = "deferred" +counts = { failure = 2, seed = 3 } + +[[entry]] +engine = "native" +stage = "model_loop" +site = "model_loop.cursor_intrinsic_validation" +kind = "deferred" +counts = { failure = 356, seed = 1100 } + +[[entry]] +engine = "native" +stage = "model_loop" +site = "model_loop.declaration_references" +kind = "after_variables" +counts = { seed = 3 } + +[[entry]] +engine = "native" +stage = "model_loop" +site = "model_loop.sql_variables" +kind = "deferred" +counts = { failure = 3, seed = 195 } + +[[entry]] +engine = "native" +stage = "model_loop" +site = "model_loop.sql_variables" +kind = "error" +counts = { failure = 2 } + +[[entry]] +engine = "native-preview" +stage = "attachments" +site = "attachments.audit_rendering" +kind = "deferred" +counts = { seed = 16 } + +[[entry]] +engine = "native-preview" +stage = "attachments" +site = "attachments.scenario_independence" +kind = "deferred" +counts = { failure = 1 } + +[[entry]] +engine = "native-preview" +stage = "declaration_scopes" +site = "declaration_scopes.rebind_lookup" +kind = "deferred" +counts = { failure = 1, seed = 781 } + +[[entry]] +engine = "native-preview" +stage = "declaration_scopes" +site = "declaration_scopes.relationship_ctes" +kind = "expected_names" +counts = { example = 1, failure = 3, fixture = 3, seed = 26 } + +[[entry]] +engine = "native-preview" +stage = "macro_calls" +site = "macro_calls.arguments" +kind = "deferred" +counts = { example = 6, failure = 14, fixture = 5, seed = 484 } + +[[entry]] +engine = "native-preview" +stage = "macro_calls" +site = "macro_calls.resolution" +kind = "deferred" +counts = { failure = 4, seed = 3 } + +[[entry]] +engine = "native-preview" +stage = "macro_calls" +site = "macro_calls.unbridged_expansion" +kind = "macro_mocks" +counts = { seed = 238 } + +[[entry]] +engine = "native-preview" +stage = "model_analysis" +site = "analysis_session" +kind = "input_enrichment" +counts = { example = 52, failure = 160, fixture = 52, seed = 614 } + +[[entry]] +engine = "native-preview" +stage = "model_analysis" +site = "analysis_session" +kind = "legacy_analysis" +counts = { failure = 2, seed = 10 } + +[[entry]] +engine = "native-preview" +stage = "model_config" +site = "model_config.header_metadata" +kind = "stale" +counts = { seed = 19 } + +[[entry]] +engine = "native-preview" +stage = "model_config" +site = "model_config.header_metadata" +kind = "unsupported" +counts = { failure = 1 } + +[[entry]] +engine = "native-preview" +stage = "model_config" +site = "model_config.python_templates" +kind = "api" +counts = { example = 16, failure = 2, seed = 36 } + +[[entry]] +engine = "native-preview" +stage = "model_config" +site = "model_config.python_templates" +kind = "config" +counts = { example = 12, failure = 93, fixture = 8, seed = 36 } + +[[entry]] +engine = "native-preview" +stage = "model_config" +site = "model_config.python_templates" +kind = "connection" +counts = { example = 18, failure = 95, fixture = 9, seed = 63 } + +[[entry]] +engine = "native-preview" +stage = "model_config" +site = "model_config.python_templates" +kind = "seed_target" +counts = { example = 8, seed = 30 } + +[[entry]] +engine = "native-preview" +stage = "model_config" +site = "model_config.python_templates" +kind = "source_target" +counts = { example = 8, seed = 23 } + +[[entry]] +engine = "native-preview" +stage = "model_config" +site = "model_config.validators" +kind = "deferred" +counts = { failure = 2, fixture = 4, seed = 3 } + +[[entry]] +engine = "native-preview" +stage = "model_loop" +site = "model_loop.cursor_intrinsic_validation" +kind = "deferred" +counts = { example = 56, failure = 356, fixture = 64, seed = 1100 } + +[[entry]] +engine = "native-preview" +stage = "model_loop" +site = "model_loop.declaration_references" +kind = "after_variables" +counts = { seed = 3 } + +[[entry]] +engine = "native-preview" +stage = "model_loop" +site = "model_loop.sql_variables" +kind = "deferred" +counts = { failure = 3, seed = 195 } + +[[entry]] +engine = "native-preview" +stage = "model_loop" +site = "model_loop.sql_variables" +kind = "error" +counts = { failure = 2 } diff --git a/scripts/compiler_differential/types.py b/scripts/compiler_differential/types.py index 7ee809df54..8d7c289207 100644 --- a/scripts/compiler_differential/types.py +++ b/scripts/compiler_differential/types.py @@ -3,3 +3,5 @@ from collections.abc import Callable type NodeResolvers = tuple[Callable[[object], object], Callable[[object], object]] + +type FallbackKey = tuple[str, str, str, str] diff --git a/src/sqlbuild/compiler/compile/_helpers/assembly/project.py b/src/sqlbuild/compiler/compile/_helpers/assembly/project.py index 87bf84e5ab..01d0001065 100644 --- a/src/sqlbuild/compiler/compile/_helpers/assembly/project.py +++ b/src/sqlbuild/compiler/compile/_helpers/assembly/project.py @@ -163,6 +163,9 @@ DiagnosticSeverity, SqlTestMode, ) +from sqlbuild.compiler.frontier.main._report_native_fallback import report_native_fallback +from sqlbuild.compiler.frontier.main.native_stage_enabled import native_stage_enabled +from sqlbuild.compiler.frontier.types import NativeFallbackSite, NativeStage from sqlbuild.compiler.lineage.types import ColumnLineageMode, InferredNullability from sqlbuild.compiler.planner.types import ContractPolicy from sqlbuild.compiler.profiling.main.record import record_compile_timing @@ -1570,6 +1573,8 @@ def _build_source_relation_entry( def _expand_target_value(*, value: str | None, effective_vars: dict[str, object]) -> str | None: if value is None: return None + if native_stage_enabled(NativeStage.MODEL_CONFIG): + report_native_fallback(site=NativeFallbackSite.PYTHON_TEMPLATES, kind="source_target") return str( expand_template_data( value=value, diff --git a/src/sqlbuild/compiler/compile/_helpers/assembly/targets.py b/src/sqlbuild/compiler/compile/_helpers/assembly/targets.py index 28bd9e7bed..49ca82c5d2 100644 --- a/src/sqlbuild/compiler/compile/_helpers/assembly/targets.py +++ b/src/sqlbuild/compiler/compile/_helpers/assembly/targets.py @@ -8,6 +8,9 @@ from sqlbuild.compiler.compile._helpers.render.templating import expand_template_data from sqlbuild.compiler.compile.constants import PRESERVE_TARGET_VALUE from sqlbuild.compiler.compile.models import CompiledRelationLocation, CompileModelInput +from sqlbuild.compiler.frontier.main._report_native_fallback import report_native_fallback +from sqlbuild.compiler.frontier.main.native_stage_enabled import native_stage_enabled +from sqlbuild.compiler.frontier.types import NativeFallbackSite, NativeStage from sqlbuild.spec.contracts.models import DefaultsConfig, SchemaSeedEntry, TargetConfig @@ -150,6 +153,8 @@ def _expand_seed_environment_value( ) -> str | None: if raw_value == PRESERVE_TARGET_VALUE: return None + if native_stage_enabled(NativeStage.MODEL_CONFIG): + report_native_fallback(site=NativeFallbackSite.PYTHON_TEMPLATES, kind="seed_target") return str( expand_template_data( value=raw_value, @@ -174,6 +179,8 @@ def _expand_seed_target_value( ) -> str | None: if raw_value == PRESERVE_TARGET_VALUE: return None + if native_stage_enabled(NativeStage.MODEL_CONFIG): + report_native_fallback(site=NativeFallbackSite.PYTHON_TEMPLATES, kind="seed_target") return str( expand_template_data( value=raw_value, diff --git a/src/sqlbuild/compiler/compile/_helpers/attachment/audits.py b/src/sqlbuild/compiler/compile/_helpers/attachment/audits.py index 878f094901..cd3c9eba91 100644 --- a/src/sqlbuild/compiler/compile/_helpers/attachment/audits.py +++ b/src/sqlbuild/compiler/compile/_helpers/attachment/audits.py @@ -72,8 +72,9 @@ DiscoveredAuditFile, DiscoveredProjectInputs, ) +from sqlbuild.compiler.frontier.main._report_native_fallback import report_native_fallback from sqlbuild.compiler.frontier.main.native_stage_enabled import native_stage_enabled -from sqlbuild.compiler.frontier.types import NativeStage +from sqlbuild.compiler.frontier.types import NativeFallbackSite, NativeStage from sqlbuild.compiler.references.types import SqlReferenceKind from sqlbuild.compiler.scopes.models import ( DeclarationIdentity, @@ -660,6 +661,8 @@ def build_attached_audit_input( if native_stage_enabled(NativeStage.ATTACHMENTS) else None ) + if native_rendering is None and native_stage_enabled(NativeStage.ATTACHMENTS): + report_native_fallback(site=NativeFallbackSite.AUDIT_RENDERING) if native_rendering is not None and native_rendering.render_error is not None: raise CompileInputError(native_rendering.render_error, bridge_independent=True) rendered_sql_body, rendered_evidence_sql = ( diff --git a/src/sqlbuild/compiler/compile/_helpers/attachment/core.py b/src/sqlbuild/compiler/compile/_helpers/attachment/core.py index 217d15ffd0..b762c83863 100644 --- a/src/sqlbuild/compiler/compile/_helpers/attachment/core.py +++ b/src/sqlbuild/compiler/compile/_helpers/attachment/core.py @@ -142,8 +142,9 @@ PythonHookEntry, SqlHookEntry, ) +from sqlbuild.compiler.frontier.main._report_native_fallback import report_native_fallback from sqlbuild.compiler.frontier.main.native_stage_enabled import native_stage_enabled -from sqlbuild.compiler.frontier.types import NativeStage +from sqlbuild.compiler.frontier.types import NativeFallbackSite, NativeStage from sqlbuild.compiler.model_config.main._parse_native_header_metadata import ( parse_native_header_metadata, ) @@ -746,6 +747,11 @@ def _build_model_input( else None ) if declaration_expansion is None: + if native_stage_enabled(NativeStage.MODEL_LOOP): + report_native_fallback( + site=NativeFallbackSite.DECLARATION_REFERENCES, + kind="scan" if prepared_var_substituted_sql is not None else "after_variables", + ) declaration_expansion = expand_declaration_references_result( sql=var_substituted_sql, file_path=model_file.file_path, @@ -764,6 +770,8 @@ def _build_model_input( consumer=model_identity, ) expanded_query_sql: str = macro_expansion.sql + if native_stage_enabled(NativeStage.MODEL_LOOP): + report_native_fallback(site=NativeFallbackSite.CURSOR_INTRINSIC_VALIDATION) expanded_query_sql = get_validated_model_cursor_intrinsics( sql=expanded_query_sql, config_values=effective_config.values, @@ -1263,6 +1271,8 @@ def build_effective_connection( connection.update(local_config.connections.get(connection_name, {})) connection.update(target_config.connection) connection.update(local_config.connection) + if native_stage_enabled(NativeStage.MODEL_CONFIG): + report_native_fallback(site=NativeFallbackSite.PYTHON_TEMPLATES, kind="connection") return cast( dict[str, object], expand_template_data( diff --git a/src/sqlbuild/compiler/compile/_helpers/attachment/declaration_scope.py b/src/sqlbuild/compiler/compile/_helpers/attachment/declaration_scope.py index ec401a01c1..68cb653078 100644 --- a/src/sqlbuild/compiler/compile/_helpers/attachment/declaration_scope.py +++ b/src/sqlbuild/compiler/compile/_helpers/attachment/declaration_scope.py @@ -27,8 +27,9 @@ ScopeRelationshipBuild, ) from sqlbuild.compiler.discovery.models import DiscoveredProjectInputs +from sqlbuild.compiler.frontier.main._report_native_fallback import report_native_fallback from sqlbuild.compiler.frontier.main.native_stage_enabled import native_stage_enabled -from sqlbuild.compiler.frontier.types import NativeStage +from sqlbuild.compiler.frontier.types import NativeFallbackSite, NativeStage from sqlbuild.compiler.scopes.classes.native_scope_index import NativeScopeIndex from sqlbuild.compiler.scopes.exceptions import ScopeValidationError from sqlbuild.compiler.scopes.main._build_scope_index import build_scope_index @@ -121,6 +122,7 @@ def _build_native_declaration_scope( discovered_inputs=discovered_inputs, loaded_macros=loaded_macros ) if native is None: + report_native_fallback(site=NativeFallbackSite.SCOPE_INDEX) return None try: validate_scope_index(index=native.index) @@ -139,10 +141,12 @@ def _build_native_declaration_scope( if fault is not None: raise CompileInputError(fault, bridge_independent=True) if not native.grant(facts): + report_native_fallback(site=NativeFallbackSite.SCOPE_GRANTS) return None index: ScopeIndex = native.index_with_relationships() lookup: ScopeLookup | None = native.lookup(index=index) if lookup is None: + report_native_fallback(site=NativeFallbackSite.SCOPE_LOOKUP) return None return DeclarationScopeBuild( loaded_macros=loaded_macros, diff --git a/src/sqlbuild/compiler/compile/_helpers/attachment/functions.py b/src/sqlbuild/compiler/compile/_helpers/attachment/functions.py index f748035c3f..e32aeb4c10 100644 --- a/src/sqlbuild/compiler/compile/_helpers/attachment/functions.py +++ b/src/sqlbuild/compiler/compile/_helpers/attachment/functions.py @@ -72,8 +72,9 @@ DiscoveredPythonFunctionFile, DiscoveredSqlFunctionFile, ) +from sqlbuild.compiler.frontier.main._report_native_fallback import report_native_fallback from sqlbuild.compiler.frontier.main.native_stage_enabled import native_stage_enabled -from sqlbuild.compiler.frontier.types import NativeStage +from sqlbuild.compiler.frontier.types import NativeFallbackSite, NativeStage from sqlbuild.compiler.scopes.models import ResourceIdentity from sqlbuild.compiler.scopes.types import ResourceKind from sqlbuild.compiler.sql_analysis.main.import_polyglot import import_polyglot @@ -337,6 +338,8 @@ def _sql_function_header( if native else None ) + if native and native_header is None: + report_native_fallback(site=NativeFallbackSite.FUNCTION_HEADER, kind="sql") if native_header is not None: _raise_header_failure(header=native_header, stage=HEADER_START_STAGE) arguments: tuple[FunctionArgument, ...] = _expanded_native_arguments( @@ -485,6 +488,8 @@ def _build_python_function_input( if native else None ) + if native and native_header is None: + report_native_fallback(site=NativeFallbackSite.FUNCTION_HEADER, kind="python") arguments: tuple[FunctionArgument, ...] returns: str runtime_version: str diff --git a/src/sqlbuild/compiler/compile/_helpers/attachment/model_config.py b/src/sqlbuild/compiler/compile/_helpers/attachment/model_config.py index eb2dba4c8b..71c668c231 100644 --- a/src/sqlbuild/compiler/compile/_helpers/attachment/model_config.py +++ b/src/sqlbuild/compiler/compile/_helpers/attachment/model_config.py @@ -65,6 +65,8 @@ PythonHookEntry, SqlHookEntry, ) +from sqlbuild.compiler.frontier.main._report_native_fallback import report_native_fallback +from sqlbuild.compiler.frontier.types import NativeFallbackSite from sqlbuild.compiler.model_config.main._native_config_contains_macro_call import ( native_config_contains_macro_call, ) @@ -136,6 +138,7 @@ def contains_config_templates( native: bool | None = native_config_contains_template(values) if native is not None: return native + report_native_fallback(site=NativeFallbackSite.CONFIG_PRESENCE_SCAN, kind="templates") return _contains_template_data_cached( value=values, cache=scan_cache.template_presence if scan_cache is not None else None ) @@ -152,6 +155,7 @@ def contains_config_macro_calls( ) if native is not None: return native + report_native_fallback(site=NativeFallbackSite.CONFIG_PRESENCE_SCAN, kind="macros") return _contains_model_config_macro_cached( values=values, cache=scan_cache.macro_presence if scan_cache is not None else None ) @@ -372,6 +376,8 @@ def build_model_header_schema_entry( ) else None ) + if native_metadata is not None and native is None: + report_native_fallback(site=NativeFallbackSite.CONFIG_HEADER_METADATA, kind="stale") if native is not None and native.columns_error is not None: raise native_config_error(error=native.columns_error, bridge_independent=True) local_columns: tuple[SchemaColumn, ...] = ( @@ -931,6 +937,7 @@ def build_native_model_config( model_file.header_values, matched_path_default, model_file.file_path.stem ) if built is None: + report_native_fallback(site=NativeFallbackSite.CONFIG_BUILD) return None if isinstance(built, _native.NativeConfigError): raise native_config_error(error=built, bridge_independent=True) @@ -1019,6 +1026,7 @@ def validate_model_config( raise native_validation_error(error=outcome, values=request.config.values) if outcome: return + report_native_fallback(site=NativeFallbackSite.CONFIG_VALIDATORS) run_python_model_validators(context=context, request=request) diff --git a/src/sqlbuild/compiler/compile/_helpers/attachment/scope_relationships.py b/src/sqlbuild/compiler/compile/_helpers/attachment/scope_relationships.py index a722180754..268172dc73 100644 --- a/src/sqlbuild/compiler/compile/_helpers/attachment/scope_relationships.py +++ b/src/sqlbuild/compiler/compile/_helpers/attachment/scope_relationships.py @@ -33,6 +33,8 @@ ) from sqlbuild.compiler.compile.types import SqlTestMode from sqlbuild.compiler.discovery.models import DiscoveredProjectInputs, DiscoveredSqlTestFile +from sqlbuild.compiler.frontier.main._report_native_fallback import report_native_fallback +from sqlbuild.compiler.frontier.types import NativeFallbackSite from sqlbuild.compiler.scopes.main._native_expected_model_names import ( native_expected_model_names, ) @@ -211,6 +213,10 @@ def _expected_names_by_file( if native_names is not None: file_names.append(native_names) continue + if scan_natively: + report_native_fallback( + site=NativeFallbackSite.SCOPE_RELATIONSHIP_CTES, kind="expected_names" + ) try: file_names.append( extract_sql_test_expected_model_names( @@ -317,6 +323,7 @@ def _natively_tested_macro_names( """Macros a macro test's actual CTE calls, scanned natively where Python's scanner is exact.""" if native_ctes is None: + report_native_fallback(site=NativeFallbackSite.SCOPE_RELATIONSHIP_CTES, kind="macro_test") return _tested_macro_names(sql=sql, file_label=file_label, syntax=syntax) if isinstance(native_ctes, str): raise CompileInputError(native_ctes, bridge_independent=True) diff --git a/src/sqlbuild/compiler/compile/_helpers/macro_bridge/expansion.py b/src/sqlbuild/compiler/compile/_helpers/macro_bridge/expansion.py index 6c46bb639c..ce4cfe0365 100644 --- a/src/sqlbuild/compiler/compile/_helpers/macro_bridge/expansion.py +++ b/src/sqlbuild/compiler/compile/_helpers/macro_bridge/expansion.py @@ -21,6 +21,8 @@ MacroExpansionState, ) from sqlbuild.compiler.frontier.exceptions import NativeStageMismatchError +from sqlbuild.compiler.frontier.main._report_native_fallback import report_native_fallback +from sqlbuild.compiler.frontier.types import NativeFallbackSite from sqlbuild.compiler.macro_bridge.classes.macro_bridge import MacroBridge from sqlbuild.compiler.macro_bridge.constants import ( ARGUMENT_REFERENCE_EVENT, @@ -60,6 +62,7 @@ def expand_bridged_sql_macros( macro_context=state.macro_context, ) if call_class is None: + report_native_fallback(site=NativeFallbackSite.MACRO_CALL_RESOLUTION) sites = None break call_classes.append(call_class) diff --git a/src/sqlbuild/compiler/compile/_helpers/refs/references.py b/src/sqlbuild/compiler/compile/_helpers/refs/references.py index 5fd0299871..a4032c8d3c 100644 --- a/src/sqlbuild/compiler/compile/_helpers/refs/references.py +++ b/src/sqlbuild/compiler/compile/_helpers/refs/references.py @@ -33,8 +33,9 @@ DiagnosticSeverity, SqlReferenceScanFailure, ) +from sqlbuild.compiler.frontier.main._report_native_fallback import report_native_fallback from sqlbuild.compiler.frontier.main.native_stage_enabled import native_stage_enabled -from sqlbuild.compiler.frontier.types import NativeStage +from sqlbuild.compiler.frontier.types import NativeFallbackSite, NativeStage from sqlbuild.compiler.references.types import SqlReferenceKind from sqlbuild.compiler.sql_analysis.main._find_matching_paren import find_matching_paren from sqlbuild.compiler.sql_analysis.main._skip_dialect_non_code import dialect_non_code_end @@ -99,7 +100,10 @@ def _reference_scan_outcome( native: SqlReferenceScan | SqlReferenceScanFailure | None = extract_native_sql_references( sql=sql, syntax=syntax ) - return native if native is not None else python_reference_scan(sql=sql, syntax=syntax) + if native is not None: + return native + report_native_fallback(site=NativeFallbackSite.REFERENCE_SCAN) + return python_reference_scan(sql=sql, syntax=syntax) if syntax.reads_differently_from_generic(sql): return python_reference_scan(sql=sql, syntax=syntax) native_references: list[tuple[str, str, str | None, int | None]] | None = ( diff --git a/src/sqlbuild/compiler/compile/_helpers/render/context_templates.py b/src/sqlbuild/compiler/compile/_helpers/render/context_templates.py index 1a544e1ad8..93d4bea1f3 100644 --- a/src/sqlbuild/compiler/compile/_helpers/render/context_templates.py +++ b/src/sqlbuild/compiler/compile/_helpers/render/context_templates.py @@ -15,6 +15,9 @@ PRESERVE_TARGET_VALUE, ) from sqlbuild.compiler.compile.types import CompileContextKey +from sqlbuild.compiler.frontier.main._report_native_fallback import report_native_fallback +from sqlbuild.compiler.frontier.main.native_stage_enabled import native_stage_enabled +from sqlbuild.compiler.frontier.types import NativeFallbackSite, NativeStage from sqlbuild.compiler.model_config.constants import ENVIRONMENT_READ from sqlbuild.compiler.model_config.main._expand_native_config_templates import ( expand_native_config_templates, @@ -284,6 +287,13 @@ def expand_config_templates( ): record_template_reads(outcome.reads) raise native_config_error(error=outcome.error, bridge_independent=True) + if native: + report_native_fallback( + site=NativeFallbackSite.CONFIG_TEMPLATES, + kind="unsupported" if outcome is None or isinstance(outcome, str) else "missing_value", + ) + elif native_stage_enabled(NativeStage.MODEL_CONFIG): + report_native_fallback(site=NativeFallbackSite.PYTHON_TEMPLATES, kind="config") return expand_template_data( value=value, variables=variables, diff --git a/src/sqlbuild/compiler/compile/_helpers/render/cursor_intrinsics.py b/src/sqlbuild/compiler/compile/_helpers/render/cursor_intrinsics.py index 6b18d042b6..c9c3e3b33c 100644 --- a/src/sqlbuild/compiler/compile/_helpers/render/cursor_intrinsics.py +++ b/src/sqlbuild/compiler/compile/_helpers/render/cursor_intrinsics.py @@ -11,8 +11,9 @@ SQL_QUOTE_TOKENS, ) from sqlbuild.compiler.compile.exceptions import CompileInputError +from sqlbuild.compiler.frontier.main._report_native_fallback import report_native_fallback from sqlbuild.compiler.frontier.main.native_stage_enabled import native_stage_enabled -from sqlbuild.compiler.frontier.types import NativeStage +from sqlbuild.compiler.frontier.types import NativeFallbackSite, NativeStage from sqlbuild.compiler.planner.constants import ( MICROBATCH_END_SENTINEL, MICROBATCH_START_SENTINEL, @@ -70,6 +71,7 @@ def reject_cursor_intrinsics(*, sql: str, context: str) -> None: return if error is not None: raise CompileInputError(error) + report_native_fallback(site=NativeFallbackSite.CURSOR_INTRINSIC_REJECTION) _assert_no_reserved_cursor_markers(sql=sql, context=context) _, found = _transform_cursor_intrinsics( sql=sql, diff --git a/src/sqlbuild/compiler/compile/_helpers/render/declarations.py b/src/sqlbuild/compiler/compile/_helpers/render/declarations.py index 87043b82d7..24f8ec36eb 100644 --- a/src/sqlbuild/compiler/compile/_helpers/render/declarations.py +++ b/src/sqlbuild/compiler/compile/_helpers/render/declarations.py @@ -32,8 +32,9 @@ EnumMember, ModelSchemaDeclaration, ) +from sqlbuild.compiler.frontier.main._report_native_fallback import report_native_fallback from sqlbuild.compiler.frontier.main.native_stage_enabled import native_stage_enabled -from sqlbuild.compiler.frontier.types import NativeStage +from sqlbuild.compiler.frontier.types import NativeFallbackSite, NativeStage from sqlbuild.compiler.model_loop.constants import ( ENUM_REFERENCE_KIND_CODE, INVALID_CONSTANT_REFERENCE_STOP_CODE, @@ -167,6 +168,8 @@ def build_declaration_scope_resolver( if loaded_macros is not None: for macro in loaded_macros.values(): declarations[DeclarationIdentity(DeclarationKind.MACRO, macro.name)] = macro + if lookup is None and native_stage_enabled(NativeStage.DECLARATION_SCOPES): + report_native_fallback(site=NativeFallbackSite.SCOPE_REBIND_LOOKUP) scope_lookup: ScopeLookup = build_scope_lookup(index=scope_index) if lookup is None else lookup return DeclarationScopeResolver( project_dir=discovered_inputs.project_dir, diff --git a/src/sqlbuild/compiler/compile/_helpers/render/macros.py b/src/sqlbuild/compiler/compile/_helpers/render/macros.py index f011f320a0..c67e699093 100644 --- a/src/sqlbuild/compiler/compile/_helpers/render/macros.py +++ b/src/sqlbuild/compiler/compile/_helpers/render/macros.py @@ -49,6 +49,9 @@ DiscoveredMacroFile, EnumDeclaration, ) +from sqlbuild.compiler.frontier.main._report_native_fallback import report_native_fallback +from sqlbuild.compiler.frontier.main.native_stage_enabled import native_stage_enabled +from sqlbuild.compiler.frontier.types import NativeFallbackSite, NativeStage from sqlbuild.compiler.macro_bridge.classes.macro_bridge import MacroBridge from sqlbuild.compiler.macro_bridge.constants import ( DECLARATION_READ_EVENT, @@ -1209,6 +1212,11 @@ def expand_sql_macros_result( consumer=consumer, ) bridge: MacroBridge | None = active_macro_bridge() if not state.macro_overrides else None + if bridge is None and MACRO_TOKEN in sql and native_stage_enabled(NativeStage.MACRO_CALLS): + report_native_fallback( + site=NativeFallbackSite.MACRO_UNBRIDGED_EXPANSION, + kind="macro_mocks" if state.macro_overrides else "outside_compile_inputs", + ) expanded_sql, spans = ( _expand_sql_macros( sql=sql, @@ -1772,6 +1780,8 @@ def _parse_macro_arguments( ) -> tuple[tuple[object, ...], dict[str, object]]: if not args_source.strip(): return (), {} + if native_stage_enabled(NativeStage.MACRO_CALLS): + report_native_fallback(site=NativeFallbackSite.MACRO_ARGUMENTS) rewritten_args_source: str placeholder_values: dict[str, object] rewritten_args_source, placeholder_values = _rewrite_nested_macro_calls( diff --git a/src/sqlbuild/compiler/compile/_helpers/render/sql_vars.py b/src/sqlbuild/compiler/compile/_helpers/render/sql_vars.py index a25848e0df..cb41087c36 100644 --- a/src/sqlbuild/compiler/compile/_helpers/render/sql_vars.py +++ b/src/sqlbuild/compiler/compile/_helpers/render/sql_vars.py @@ -37,8 +37,9 @@ MacroExpansionResult, ) from sqlbuild.compiler.compile.types import TypedSqlValueRenderer +from sqlbuild.compiler.frontier.main._report_native_fallback import report_native_fallback from sqlbuild.compiler.frontier.main.native_stage_enabled import native_stage_enabled -from sqlbuild.compiler.frontier.types import NativeStage +from sqlbuild.compiler.frontier.types import NativeFallbackSite, NativeStage from sqlbuild.compiler.model_loop.main._scan_native_declaration_references import ( scan_native_declaration_references, ) @@ -116,6 +117,8 @@ def expand_authored_sql_result( # noqa: PLR0913 if native else None ) + if native and scanned_result is None: + report_native_fallback(site=NativeFallbackSite.DECLARATION_REFERENCES, kind="scan") declaration_result: DeclarationExpansionResult = ( scanned_result if scanned_result is not None @@ -227,6 +230,7 @@ def prepare_static_project_vars_batch( if not isinstance(raw_results, list) or len(raw_results) != len(sqls): raise CompileInputError("native static SQL interpolation returned an invalid batch") results: list[str | None] = [] + counted: bool = native_stage_enabled(NativeStage.MODEL_LOOP) for sql, raw_result in zip(sqls, raw_results, strict=True): if not ( isinstance(raw_result, tuple) @@ -241,6 +245,8 @@ def prepare_static_project_vars_batch( elif status == _NATIVE_SUBSTITUTED and rendered is not None: results.append(rendered) elif status == _NATIVE_FALLBACK and rendered is None: + if counted: + report_native_fallback(site=NativeFallbackSite.SQL_VARIABLES) results.append(None) elif status in _NATIVE_ERROR_STATUSES: if error_file_path is not None: @@ -250,6 +256,8 @@ def prepare_static_project_vars_batch( file_path=error_file_path, effective_vars=effective_vars, ) + if counted: + report_native_fallback(site=NativeFallbackSite.SQL_VARIABLES, kind="error") results.append(None) else: raise CompileInputError("native static SQL interpolation returned an invalid status") diff --git a/src/sqlbuild/compiler/compile/_helpers/scenarios/core.py b/src/sqlbuild/compiler/compile/_helpers/scenarios/core.py index 40e81b2b60..f9382e915c 100644 --- a/src/sqlbuild/compiler/compile/_helpers/scenarios/core.py +++ b/src/sqlbuild/compiler/compile/_helpers/scenarios/core.py @@ -29,8 +29,9 @@ CompileSqlScenarioCte, CompileSqlScenarioCtes, ) +from sqlbuild.compiler.frontier.main._report_native_fallback import report_native_fallback from sqlbuild.compiler.frontier.main.native_stage_enabled import native_stage_enabled -from sqlbuild.compiler.frontier.types import NativeStage +from sqlbuild.compiler.frontier.types import NativeFallbackSite, NativeStage from sqlbuild.compiler.sql_analysis.models import SqlLexicalSyntax _CONTEXT: str = "SQL scenario" @@ -51,6 +52,8 @@ def extract_sql_scenario_ctes( ) if native_ctes is not None: return native_ctes + if native_stage_enabled(NativeStage.ATTACHMENTS): + report_native_fallback(site=NativeFallbackSite.SCENARIO_CTES) ctes: tuple[CompileSqlScenarioCte, ...] = extract_top_level_ctes_with_scanner( sql=sql, file_label=file_label, @@ -91,6 +94,7 @@ def _scenario_from_native_outcome( independence: list[list[str]] | None = outcome.get("independence") if independence is not None: + report_native_fallback(site=NativeFallbackSite.SCENARIO_INDEPENDENCE) validate_independent_expected_and_assertion_ctes( ctes=_scenario_ctes(independence), expected_prefix=EXPECTED_TEST_CTE_PREFIX, diff --git a/src/sqlbuild/compiler/compile/_helpers/sql_tests/core.py b/src/sqlbuild/compiler/compile/_helpers/sql_tests/core.py index 47a41fee26..f9a5ca7857 100644 --- a/src/sqlbuild/compiler/compile/_helpers/sql_tests/core.py +++ b/src/sqlbuild/compiler/compile/_helpers/sql_tests/core.py @@ -29,8 +29,9 @@ from sqlbuild.compiler.discovery.main.omitted_ceremonial_select import ( omitted_ceremonial_select_offset, ) +from sqlbuild.compiler.frontier.main._report_native_fallback import report_native_fallback from sqlbuild.compiler.frontier.main.native_stage_enabled import native_stage_enabled -from sqlbuild.compiler.frontier.types import NativeStage +from sqlbuild.compiler.frontier.types import NativeFallbackSite, NativeStage from sqlbuild.compiler.references.types import SqlReferenceKind from sqlbuild.compiler.sql_analysis.main._find_matching_paren import find_matching_paren from sqlbuild.compiler.sql_analysis.main._is_identifier_character import ( @@ -158,6 +159,8 @@ def complete_omitted_ceremonial_select(*, sql: str, syntax: SqlLexicalSyntax) -> else (False, None) ) if not answered: + if native_stage_enabled(NativeStage.ATTACHMENTS): + report_native_fallback(site=NativeFallbackSite.OMITTED_SELECT) offset = omitted_ceremonial_select_offset(sql=sql, syntax=syntax) if offset is None: return sql diff --git a/src/sqlbuild/compiler/compile/main/expand_sql_with_spans.py b/src/sqlbuild/compiler/compile/main/expand_sql_with_spans.py index ec9f8da08e..e3e7690cbb 100644 --- a/src/sqlbuild/compiler/compile/main/expand_sql_with_spans.py +++ b/src/sqlbuild/compiler/compile/main/expand_sql_with_spans.py @@ -11,6 +11,9 @@ ExpansionSpan, SqlExpansionContext, ) +from sqlbuild.compiler.frontier.main._report_native_fallback import report_native_fallback +from sqlbuild.compiler.frontier.main.native_stage_enabled import native_stage_enabled +from sqlbuild.compiler.frontier.types import NativeFallbackSite, NativeStage def expand_sql_with_spans( @@ -36,6 +39,8 @@ def expand_sql_with_spans( inaccessible_enums=declarations.inaccessible_enums, inaccessible_constants=declarations.inaccessible_constants, ) + if native_stage_enabled(NativeStage.MODEL_LOOP): + report_native_fallback(site=NativeFallbackSite.LINT_EXPANSION) return expand_authored_sql_with_spans( sql=sql, file_path=file_path, diff --git a/src/sqlbuild/compiler/compile/main/expand_template_data.py b/src/sqlbuild/compiler/compile/main/expand_template_data.py index cd173d3d2f..ce71031cfc 100644 --- a/src/sqlbuild/compiler/compile/main/expand_template_data.py +++ b/src/sqlbuild/compiler/compile/main/expand_template_data.py @@ -5,6 +5,9 @@ from sqlbuild.compiler.compile._helpers.render.templating import ( expand_template_data as _expand_template_data, ) +from sqlbuild.compiler.frontier.main._report_native_fallback import report_native_fallback +from sqlbuild.compiler.frontier.main.native_stage_enabled import native_stage_enabled +from sqlbuild.compiler.frontier.types import NativeFallbackSite, NativeStage def expand_template_data( @@ -19,6 +22,8 @@ def expand_template_data( ) -> object: """Recursively expand template strings inside supported Python container values.""" + if native_stage_enabled(NativeStage.MODEL_CONFIG): + report_native_fallback(site=NativeFallbackSite.PYTHON_TEMPLATES, kind="api") return _expand_template_data( value=value, variables=variables, diff --git a/src/sqlbuild/compiler/discovery/_helpers/native/declaration_files.py b/src/sqlbuild/compiler/discovery/_helpers/native/declaration_files.py index 3316b96fe2..63c9348a73 100644 --- a/src/sqlbuild/compiler/discovery/_helpers/native/declaration_files.py +++ b/src/sqlbuild/compiler/discovery/_helpers/native/declaration_files.py @@ -49,6 +49,8 @@ ModelSchemaDeclaration, ) from sqlbuild.compiler.discovery.types import NativeFileScope, NativeLocation, NativeScopeFields +from sqlbuild.compiler.frontier.main._report_native_fallback import report_native_fallback +from sqlbuild.compiler.frontier.types import NativeFallbackSite from sqlbuild.compiler.scopes.types import DeclarationKind, ScopeKind type _NativeDeclarationFile = tuple[str, NativeFileScope | None, tuple[object, ...]] @@ -274,6 +276,7 @@ def _record[RecordT]( scope, payload = item tag: object = payload[0] if tag == NATIVE_DEFERRED_TAG: + report_native_fallback(site=NativeFallbackSite.DECLARATION_FILE) return parse_with_python(relative_path=relative_path, scope=scope) if tag == NATIVE_PARSED_TAG: return build( diff --git a/src/sqlbuild/compiler/frontier/classes/native_fallback_recorder.py b/src/sqlbuild/compiler/frontier/classes/native_fallback_recorder.py new file mode 100644 index 0000000000..687ce91518 --- /dev/null +++ b/src/sqlbuild/compiler/frontier/classes/native_fallback_recorder.py @@ -0,0 +1,50 @@ +"""Debug-only count of the native-to-Python fallbacks one process took, written at exit.""" + +from __future__ import annotations + +import atexit +import json +import os +import threading +from collections import Counter +from pathlib import Path + +from sqlbuild.compiler.frontier.constants import NATIVE_FALLBACK_RECORD_PREFIX +from sqlbuild.compiler.sql_analysis.constants import ANALYSIS_RECORD_DIR_ENV_VAR + + +class NativeFallbackRecorder: + """Count fallbacks by `(site, kind)` and write them once, when the process exits.""" + + def __init__(self, *, directory: Path) -> None: + self._directory: Path = directory + self._counts: Counter[tuple[str, str]] = Counter() + self._lock: threading.Lock = threading.Lock() + _ = atexit.register(self.write) + + @classmethod + def from_environment(cls) -> NativeFallbackRecorder | None: + """Return a recorder when the debug record directory is set, otherwise None.""" + + directory: str | None = os.environ.get(ANALYSIS_RECORD_DIR_ENV_VAR) + return cls(directory=Path(directory)) if directory else None + + def record(self, *, site: str, kind: str) -> None: + """Count one fallback at `site` for the reason `kind`.""" + + with self._lock: + self._counts[(site, kind)] += 1 + + def write(self) -> None: + """Write the counts so far as `{"fallbacks": [[site, kind, count], ...]}`; none, no file.""" + + with self._lock: + counts: Counter[tuple[str, str]] = self._counts.copy() + if not counts: + return + self._directory.mkdir(parents=True, exist_ok=True) + rows: list[list[object]] = [ + [site, kind, count] for (site, kind), count in sorted(counts.items()) + ] + target: Path = self._directory / f"{NATIVE_FALLBACK_RECORD_PREFIX}{os.getpid()}.json" + _ = target.write_text(json.dumps({"fallbacks": rows}) + "\n", encoding="utf-8") diff --git a/src/sqlbuild/compiler/frontier/constants.py b/src/sqlbuild/compiler/frontier/constants.py index 195b5404c3..52b2076c9e 100644 --- a/src/sqlbuild/compiler/frontier/constants.py +++ b/src/sqlbuild/compiler/frontier/constants.py @@ -41,6 +41,8 @@ TARGET_DIRECTORY_NAME: str = "target" CACHE_DIRECTORY_NAME: str = "cache" COMPILER_CACHE_DIRECTORY_NAME: str = "compiler" +NATIVE_FALLBACK_RECORD_PREFIX: str = "native-fallbacks-" +NATIVE_FALLBACK_DEFAULT_KIND: str = "deferred" STAGE_CAPTURE_DIR_ENV_VAR: str = "SQLBUILD_COMPILER_STAGE_CAPTURE_DIR" COMPILE_REUSE_DISABLE_ENV_VAR: str = "SQLBUILD_DISABLE_COMPILE_REUSE" STAGE_CAPTURE_SUFFIX: str = ".json" diff --git a/src/sqlbuild/compiler/frontier/main/_report_native_fallback.py b/src/sqlbuild/compiler/frontier/main/_report_native_fallback.py new file mode 100644 index 0000000000..51cfbec2d1 --- /dev/null +++ b/src/sqlbuild/compiler/frontier/main/_report_native_fallback.py @@ -0,0 +1,22 @@ +"""Count one place where a shipped native stage handed its work back to Python.""" + +from functools import cache + +from sqlbuild.compiler.frontier.classes.native_fallback_recorder import NativeFallbackRecorder +from sqlbuild.compiler.frontier.constants import NATIVE_FALLBACK_DEFAULT_KIND +from sqlbuild.compiler.frontier.types import NativeFallbackSite + + +def report_native_fallback( + *, site: NativeFallbackSite, kind: str = NATIVE_FALLBACK_DEFAULT_KIND +) -> None: + """Record a fallback at `site` when the debug record directory is set; otherwise do nothing.""" + + recorder: NativeFallbackRecorder | None = _process_recorder() + if recorder is not None: + recorder.record(site=site.value, kind=kind) + + +@cache +def _process_recorder() -> NativeFallbackRecorder | None: + return NativeFallbackRecorder.from_environment() diff --git a/src/sqlbuild/compiler/frontier/types.py b/src/sqlbuild/compiler/frontier/types.py index 17b611e46f..c24e6de0ea 100644 --- a/src/sqlbuild/compiler/frontier/types.py +++ b/src/sqlbuild/compiler/frontier/types.py @@ -45,3 +45,37 @@ class CompilerStage(StrEnum): DISCOVERED_PROJECT_INPUTS = "discovered_project_inputs" COMPILE_PROJECT_INPUTS = "compile_project_inputs" COMPILED_PROJECT = "compiled_project" + + +class NativeFallbackSite(StrEnum): + """A place where a shipped native stage still hands its work to the Python implementation.""" + + DECLARATION_FILE = "declaration_files.file" + SCOPE_INDEX = "declaration_scopes.index" + SCOPE_GRANTS = "declaration_scopes.grants" + SCOPE_LOOKUP = "declaration_scopes.lookup" + SCOPE_REBIND_LOOKUP = "declaration_scopes.rebind_lookup" + SCOPE_RELATIONSHIP_CTES = "declaration_scopes.relationship_ctes" + SQL_VARIABLES = "model_loop.sql_variables" + DECLARATION_REFERENCES = "model_loop.declaration_references" + CURSOR_INTRINSIC_VALIDATION = "model_loop.cursor_intrinsic_validation" + LINT_EXPANSION = "model_loop.lint_expansion" + MACRO_CALL_SCAN = "macro_calls.scan" + MACRO_CALL_RESOLUTION = "macro_calls.resolution" + MACRO_CALL_SPLICE = "macro_calls.splice" + MACRO_ARGUMENTS = "macro_calls.arguments" + MACRO_UNBRIDGED_EXPANSION = "macro_calls.unbridged_expansion" + MACRO_BRIDGE_UNAVAILABLE = "macro_calls.bridge_unavailable" + REFERENCE_SCAN = "reference_extraction.scan" + CONFIG_BUILD = "model_config.build" + CONFIG_HEADER_METADATA = "model_config.header_metadata" + CONFIG_TEMPLATES = "model_config.templates" + CONFIG_PRESENCE_SCAN = "model_config.presence_scan" + CONFIG_VALIDATORS = "model_config.validators" + PYTHON_TEMPLATES = "model_config.python_templates" + AUDIT_RENDERING = "attachments.audit_rendering" + FUNCTION_HEADER = "attachments.function_header" + CURSOR_INTRINSIC_REJECTION = "attachments.cursor_intrinsic_rejection" + SCENARIO_CTES = "attachments.scenario_ctes" + SCENARIO_INDEPENDENCE = "attachments.scenario_independence" + OMITTED_SELECT = "attachments.omitted_select" diff --git a/src/sqlbuild/compiler/macro_bridge/classes/macro_bridge.py b/src/sqlbuild/compiler/macro_bridge/classes/macro_bridge.py index 731fe2ca5b..cf8c115760 100644 --- a/src/sqlbuild/compiler/macro_bridge/classes/macro_bridge.py +++ b/src/sqlbuild/compiler/macro_bridge/classes/macro_bridge.py @@ -14,6 +14,8 @@ LoadedMacro, MacroContext, ) +from sqlbuild.compiler.frontier.main._report_native_fallback import report_native_fallback +from sqlbuild.compiler.frontier.types import NativeFallbackSite from sqlbuild.compiler.macro_bridge._helpers.splicing import splice_text from sqlbuild.compiler.macro_bridge._helpers.store_environment import ( digest_module_files, @@ -139,8 +141,10 @@ def scan(self, sql: str) -> tuple[MacroCallSite, ...] | None: _native.scan_macro_call_sites(sql, self._python_version, self._unicode_version) ) except UnicodeEncodeError: + report_native_fallback(site=NativeFallbackSite.MACRO_CALL_SCAN, kind="unencodable") return None if rows is None: + report_native_fallback(site=NativeFallbackSite.MACRO_CALL_SCAN) return None return tuple( MacroCallSite( @@ -271,6 +275,7 @@ def splice( try: rendered, spans = _native.splice_macro_calls(sql, bounds, outputs) except UnicodeEncodeError: + report_native_fallback(site=NativeFallbackSite.MACRO_CALL_SPLICE, kind="unencodable") rendered, spans = splice_text(sql=sql, bounds=bounds, outputs=outputs) return rendered, tuple( ExpansionSpan( diff --git a/src/sqlbuild/compiler/macro_bridge/main/run_with_macro_bridge.py b/src/sqlbuild/compiler/macro_bridge/main/run_with_macro_bridge.py index fcaa59d694..3fff9a5d9f 100644 --- a/src/sqlbuild/compiler/macro_bridge/main/run_with_macro_bridge.py +++ b/src/sqlbuild/compiler/macro_bridge/main/run_with_macro_bridge.py @@ -9,6 +9,8 @@ from contextvars import Token import sqlbuild._native as _native +from sqlbuild.compiler.frontier.main._report_native_fallback import report_native_fallback +from sqlbuild.compiler.frontier.types import NativeFallbackSite from sqlbuild.compiler.macro_bridge.classes.macro_bridge import MacroBridge from sqlbuild.compiler.macro_bridge.constants import ACTIVE_MACRO_BRIDGE, STORE_MODULE_PATHS @@ -18,6 +20,7 @@ def run_with_macro_bridge[T](*, stage: Callable[[], T]) -> T: python_version: tuple[int, int] = (sys.version_info[0], sys.version_info[1]) if not _native.native_text_supported(python_version, unicodedata.unidata_version): + report_native_fallback(site=NativeFallbackSite.MACRO_BRIDGE_UNAVAILABLE) return stage() bridge: MacroBridge = MacroBridge( python_version=python_version, unicode_version=unicodedata.unidata_version diff --git a/src/sqlbuild/compiler/model_config/main/_parse_native_header_metadata.py b/src/sqlbuild/compiler/model_config/main/_parse_native_header_metadata.py index 6314d37b52..68c1b0302b 100644 --- a/src/sqlbuild/compiler/model_config/main/_parse_native_header_metadata.py +++ b/src/sqlbuild/compiler/model_config/main/_parse_native_header_metadata.py @@ -9,6 +9,8 @@ import sqlbuild._native as _native from sqlbuild.compiler.auditing.types import AuditSeverity from sqlbuild.compiler.discovery.models import DiscoveredSqlModelFile +from sqlbuild.compiler.frontier.main._report_native_fallback import report_native_fallback +from sqlbuild.compiler.frontier.types import NativeFallbackSite from sqlbuild.compiler.model_config.constants import ( AUDITS_HEADER_KEY, COLUMNS_HEADER_KEY, @@ -47,7 +49,13 @@ def parse_native_header_metadata( }, ) except (TypeError, ValueError): + report_native_fallback(site=NativeFallbackSite.CONFIG_HEADER_METADATA, kind="batch") return {} + for metadata in parsed: + if metadata == UNSUPPORTED_OUTCOME: + report_native_fallback( + site=NativeFallbackSite.CONFIG_HEADER_METADATA, kind="unsupported" + ) return { model_file.file_path: _native_metadata(model_file=model_file, metadata=metadata) for model_file, metadata in zip(model_files, parsed, strict=True) diff --git a/tests/e2e/scripts/compiler_differential/_test_types.py b/tests/e2e/scripts/compiler_differential/_test_types.py index e488fe198a..0369dcd081 100644 --- a/tests/e2e/scripts/compiler_differential/_test_types.py +++ b/tests/e2e/scripts/compiler_differential/_test_types.py @@ -4,6 +4,8 @@ from dataclasses import dataclass +from scripts.compiler_differential.models import FailureCase + @dataclass(frozen=True) class HarnessRunTestCase: @@ -114,3 +116,32 @@ class WheelSiteReportTestCase: expected_lines: tuple[str, ...] expected_sites: frozenset[str] expected_deferrals: frozenset[str] + + +@dataclass(frozen=True) +class NativeFallbackGateTestCase: + """A recorded allow-list, an edit or sabotage, and the verdict of the next checked run.""" + + description: str + perturbation: str + appended_entries: str + expected_exit_code: int + expected_lines: tuple[str, ...] + + +@dataclass(frozen=True) +class GoldenOutputTestCase: + """Goldens recorded for one project, an edit to them, and the check verdict.""" + + description: str + golden_edit: tuple[str, str] + expected_exit_code: int + expected_lines: tuple[str, ...] + + +@dataclass(frozen=True) +class EngineErrorCaseTestCase: + """One shared all-engine error case from the failure corpus, holding its exact error.""" + + description: str + expected_error_case: FailureCase diff --git a/tests/e2e/scripts/compiler_differential/helpers.py b/tests/e2e/scripts/compiler_differential/helpers.py index 407113d85c..190f8ff012 100644 --- a/tests/e2e/scripts/compiler_differential/helpers.py +++ b/tests/e2e/scripts/compiler_differential/helpers.py @@ -99,6 +99,18 @@ def _perturbed(self, **changes): _record.write(json.dumps({"kind": "legacy_fallback", "site": "orders.sql"}) + "\\n") """ +REFERENCE_SCAN_SABOTAGE: str = """ +import sqlbuild.compiler.compile._helpers.refs.references as references + +references.extract_native_sql_references = lambda **_: None +""" + +MODEL_ANALYSIS_SABOTAGE: str = """ +import sqlbuild._native as native + +native.start_model_analysis_session = lambda *_args: None +""" + _BROKEN_REF_FILES: dict[str, str] = { "sqlbuild_project.toml": ( 'name = "broken_orders"\nadapter = "duckdb"\n\n' diff --git a/tests/e2e/scripts/compiler_differential/test_engine_error_cases.py b/tests/e2e/scripts/compiler_differential/test_engine_error_cases.py new file mode 100644 index 0000000000..6cd931ccf1 --- /dev/null +++ b/tests/e2e/scripts/compiler_differential/test_engine_error_cases.py @@ -0,0 +1,61 @@ +"""Every engine reports each shared user-facing error with the same exact code, text and help. + +Ports add cases to `engine_error_cases()` in the failure corpus, not here. +""" + +from __future__ import annotations + +import sys +from pathlib import Path + +import pytest + +from scripts.compiler_differential._helpers.corpus.emitted_codes import case_emitted_codes +from scripts.compiler_differential._helpers.corpus.failure_cases import engine_error_cases +from scripts.compiler_differential.models import DifferentialOptions, EmittedCodes +from tests.e2e.scripts.compiler_differential._test_types import EngineErrorCaseTestCase + +_ENGINES: tuple[str, ...] = ("python", "native", "native-preview") + + +@pytest.mark.parametrize( + "test_case", + [ + EngineErrorCaseTestCase(description=case.name, expected_error_case=case) + for case in engine_error_cases() + ], + ids=lambda case: case.description, +) +def test_given_shared_error_case_when_compiling_on_every_engine_then_error_text_is_exact( + test_case: EngineErrorCaseTestCase, tmp_path: Path +) -> None: + emitted: dict[str, EmittedCodes] = { + engine: case_emitted_codes( + case=test_case.expected_error_case, + options=DifferentialOptions( + engines=(engine, engine), + work_dir=tmp_path / engine, + jobs=1, + stage_captures=False, + python=Path(sys.executable), + engine_environment={}, + ), + ) + for engine in _ENGINES + } + + assert { + engine: (codes.first_error, codes.first_error_message, codes.first_error_help) + for engine, codes in emitted.items() + } == { + engine: ( + test_case.expected_error_case.expected_code, + test_case.expected_error_case.expected_message, + test_case.expected_error_case.expected_help, + ) + for engine in _ENGINES + } + + +if __name__ == "__main__": + pytest.main([__file__, "-vv"]) diff --git a/tests/e2e/scripts/compiler_differential/test_golden_outputs.py b/tests/e2e/scripts/compiler_differential/test_golden_outputs.py new file mode 100644 index 0000000000..493038863e --- /dev/null +++ b/tests/e2e/scripts/compiler_differential/test_golden_outputs.py @@ -0,0 +1,76 @@ +"""Goldens recorded from the oracle pass an unchanged check and pinpoint any edited output.""" + +from __future__ import annotations + +from pathlib import Path + +import pytest + +from scripts.compiler_differential.main.differential import run_compiler_differential +from tests.e2e.scripts.compiler_differential._test_types import GoldenOutputTestCase +from tests.e2e.scripts.compiler_differential.helpers import ( + harness_arguments, + write_failure_case_project, +) + +_CASE: str = "engine-error-week-date-cursor-start" +_PROJECT: str = "orders" +_ENGINE_ARGUMENTS: tuple[str, ...] = ("--engines", "python", "native", "--expect", "failure:P001") + + +@pytest.mark.parametrize( + "test_case", + [ + GoldenOutputTestCase( + description="unchanged_goldens_pass_for_both_engines", + golden_edit=("", ""), + expected_exit_code=0, + expected_lines=("Compiler differential passed: 1 projects identical",), + ), + GoldenOutputTestCase( + description="edited_error_text_fails_for_both_engines", + golden_edit=("hour must be in 0..23", "hour must be in 1..24"), + expected_exit_code=1, + expected_lines=( + " - golden (python) at /commands/0/diagnostics/0/message", + " - golden (native) at /commands/0/diagnostics/0/message", + "Compiler differential FAILED: 1 of 1 projects differ", + ), + ), + ], + ids=lambda case: case.description, +) +def test_given_recorded_goldens_when_checking_then_only_edited_outputs_differ( + test_case: GoldenOutputTestCase, tmp_path: Path, capsys: pytest.CaptureFixture[str] +) -> None: + project: Path = write_failure_case_project(tmp_path / _PROJECT, name=_CASE) + golden_dir: Path = tmp_path / "goldens" + recorded: int = run_compiler_differential( + harness_arguments( + work_dir=tmp_path / "record", + project=project, + extra=(*_ENGINE_ARGUMENTS, "--goldens", "update", "--golden-dir", str(golden_dir)), + ) + ) + golden: Path = golden_dir / "project" / f"{_PROJECT}.json" + _ = golden.write_text( + golden.read_text(encoding="utf-8").replace(*test_case.golden_edit), encoding="utf-8" + ) + _ = capsys.readouterr() + + exit_code: int = run_compiler_differential( + harness_arguments( + work_dir=tmp_path / "check", + project=project, + extra=(*_ENGINE_ARGUMENTS, "--goldens", "check", "--golden-dir", str(golden_dir)), + ) + ) + + output: str = capsys.readouterr().out + assert recorded == 0 + assert exit_code == test_case.expected_exit_code, output + assert all(line in output for line in test_case.expected_lines), output + + +if __name__ == "__main__": + pytest.main([__file__, "-vv"]) diff --git a/tests/e2e/scripts/compiler_differential/test_native_fallback_gate.py b/tests/e2e/scripts/compiler_differential/test_native_fallback_gate.py new file mode 100644 index 0000000000..88525982a8 --- /dev/null +++ b/tests/e2e/scripts/compiler_differential/test_native_fallback_gate.py @@ -0,0 +1,107 @@ +"""The fallback allow-list gate fails on sabotaged native stages and on stale entries.""" + +from __future__ import annotations + +from pathlib import Path + +import pytest + +from scripts.compiler_differential.main.differential import run_compiler_differential +from tests.e2e.scripts.compiler_differential._test_types import NativeFallbackGateTestCase +from tests.e2e.scripts.compiler_differential.helpers import ( + MODEL_ANALYSIS_SABOTAGE, + REFERENCE_SCAN_SABOTAGE, + harness_arguments, + write_native_perturbation, +) + +_VANISHED_ENTRY: str = ( + '\n[[entry]]\nengine = "native-preview"\nstage = "reference_extraction"\n' + 'site = "reference_extraction.scan"\nkind = "deferred"\ncounts = { project = 1 }\n' +) + + +@pytest.mark.parametrize( + "test_case", + [ + NativeFallbackGateTestCase( + description="recorded_list_passes", + perturbation="", + appended_entries="", + expected_exit_code=0, + expected_lines=("Compiler differential passed: 1 projects identical",), + ), + NativeFallbackGateTestCase( + description="entry_that_no_longer_occurs_fails", + perturbation="", + appended_entries=_VANISHED_ENTRY, + expected_exit_code=1, + expected_lines=( + "Native fallback allow-list: native-preview reference_extraction " + "reference_extraction.scan deferred (project): listed but no longer occurs; " + "remove it from the allow-list", + ), + ), + NativeFallbackGateTestCase( + description="shipped_reference_scan_sabotaged_to_python_fails", + perturbation=REFERENCE_SCAN_SABOTAGE, + appended_entries="", + expected_exit_code=1, + expected_lines=( + "Native fallback allow-list: native-preview reference_extraction " + "reference_extraction.scan deferred (project): ", + "not on the allow-list; port it or list it with a reason", + ), + ), + NativeFallbackGateTestCase( + description="preview_model_analysis_sabotaged_to_python_fails", + perturbation=MODEL_ANALYSIS_SABOTAGE, + appended_entries="", + expected_exit_code=1, + expected_lines=( + "Native fallback allow-list: native-preview model_analysis analysis_session " + "session (project): ", + "Compiler differential FAILED: 0 of 1 projects differ", + ), + ), + ], + ids=lambda case: case.description, +) +def test_given_recorded_allow_list_when_native_defers_more_or_less_then_the_gate_fails( + test_case: NativeFallbackGateTestCase, tmp_path: Path, capsys: pytest.CaptureFixture[str] +) -> None: + allow_list: Path = tmp_path / "native_fallbacks.toml" + recorded: int = run_compiler_differential( + harness_arguments( + work_dir=tmp_path / "record", + extra=("--native-fallbacks", "update", "--native-fallback-list", str(allow_list)), + ) + ) + _ = allow_list.write_text( + allow_list.read_text(encoding="utf-8") + test_case.appended_entries, encoding="utf-8" + ) + sabotage: Path = write_native_perturbation(tmp_path / "sabotage", source=test_case.perturbation) + _ = capsys.readouterr() + + exit_code: int = run_compiler_differential( + harness_arguments( + work_dir=tmp_path / "check", + extra=( + "--native-fallbacks", + "check", + "--native-fallback-list", + str(allow_list), + "--engine-env", + f"native-preview:PYTHONPATH={sabotage}", + ), + ) + ) + + output: str = capsys.readouterr().out + assert recorded == 0 + assert exit_code == test_case.expected_exit_code, output + assert all(line in output for line in test_case.expected_lines), output + + +if __name__ == "__main__": + pytest.main([__file__, "-vv"]) diff --git a/tests/goldens/compiler/example/hero-shop.json b/tests/goldens/compiler/example/hero-shop.json new file mode 100644 index 0000000000..e9235feb79 --- /dev/null +++ b/tests/goldens/compiler/example/hero-shop.json @@ -0,0 +1,220 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 0, + "diagnostics": [] + }, + { + "label": "compile-warm", + "exit_code": 0, + "diagnostics": [] + }, + { + "label": "compile-store-warm", + "exit_code": 0, + "diagnostics": [] + }, + { + "label": "plan", + "exit_code": 0, + "diagnostics": [] + } + ], + "compiled": { + "models/finance/daily_revenue.sql": [ + "SELECT", + " CAST(paid_at AS DATE) AS revenue_date,", + " SUM(amount_cents) AS total_revenue_cents", + "FROM __seed(\"payments\")", + "GROUP BY", + " CAST(paid_at AS DATE)", + "" + ] + }, + "manifest": { + "nodes": { + "model.waffle_shop.daily_revenue": { + "database": null, + "schema": "prod", + "name": "daily_revenue", + "resource_type": "model", + "package_name": "waffle_shop", + "path": "models/finance/daily_revenue.sql", + "original_file_path": "models/finance/daily_revenue.sql", + "unique_id": "model.waffle_shop.daily_revenue", + "fqn": [ + "waffle_shop", + "models", + "finance", + "daily_revenue" + ], + "alias": "daily_revenue", + "config": { + "enabled": true, + "alias": null, + "schema": "prod", + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "incremental", + "incremental_strategy": "delete_insert", + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Daily revenue from successful payments", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "prod.daily_revenue", + "depends_on": { + "macros": [], + "nodes": [ + "seed.waffle_shop.payments" + ] + } + }, + "seed.waffle_shop.payments": { + "database": null, + "schema": "prod", + "name": "payments", + "resource_type": "seed", + "package_name": "waffle_shop", + "path": "seeds/payments.csv", + "original_file_path": "seeds/payments.csv", + "unique_id": "seed.waffle_shop.payments", + "fqn": [ + "waffle_shop", + "seeds", + "payments" + ], + "alias": "payments", + "config": { + "enabled": true, + "alias": null, + "schema": "prod", + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "seed", + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + }, + "delimiter": ",", + "quote_columns": null + }, + "tags": [], + "description": "Successful payments.", + "columns": { + "payment_id": { + "name": "payment_id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "paid_at": { + "name": "paid_at", + "description": "", + "meta": {}, + "data_type": "TIMESTAMP", + "constraints": [], + "quote": null, + "tags": [] + }, + "amount_cents": { + "name": "amount_cents", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "prod.payments", + "root_path": null, + "depends_on": { + "macros": [] + } + } + }, + "sources": {}, + "macros": {}, + "exposures": {}, + "metrics": {}, + "groups": {}, + "selectors": {}, + "disabled": {}, + "parent_map": { + "model.waffle_shop.daily_revenue": [ + "seed.waffle_shop.payments" + ], + "seed.waffle_shop.payments": [] + }, + "group_map": {}, + "docs": {}, + "saved_queries": {}, + "semantic_models": {}, + "unit_tests": {} + } +} diff --git a/tests/goldens/compiler/example/tidy-shop.json b/tests/goldens/compiler/example/tidy-shop.json new file mode 100644 index 0000000000..c7db14dd9c --- /dev/null +++ b/tests/goldens/compiler/example/tidy-shop.json @@ -0,0 +1,115 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 0, + "diagnostics": [] + }, + { + "label": "compile-warm", + "exit_code": 0, + "diagnostics": [] + }, + { + "label": "compile-store-warm", + "exit_code": 0, + "diagnostics": [] + }, + { + "label": "plan", + "exit_code": 0, + "diagnostics": [] + } + ], + "compiled": { + "models/daily_revenue.sql": [ + "SELECT CAST('2026-04-01' AS DATE) AS revenue_date, 2850 AS total_revenue_cents", + "" + ] + }, + "manifest": { + "nodes": { + "model.waffle_shop.daily_revenue": { + "database": null, + "schema": "prod", + "name": "daily_revenue", + "resource_type": "model", + "package_name": "waffle_shop", + "path": "models/daily_revenue.sql", + "original_file_path": "models/daily_revenue.sql", + "unique_id": "model.waffle_shop.daily_revenue", + "fqn": [ + "waffle_shop", + "models", + "daily_revenue" + ], + "alias": "daily_revenue", + "config": { + "enabled": true, + "alias": null, + "schema": "prod", + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "table", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Daily revenue from successful payments", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "prod.daily_revenue", + "depends_on": { + "macros": [], + "nodes": [] + } + } + }, + "sources": {}, + "macros": {}, + "exposures": {}, + "metrics": {}, + "groups": {}, + "selectors": {}, + "disabled": {}, + "parent_map": { + "model.waffle_shop.daily_revenue": [] + }, + "group_map": {}, + "docs": {}, + "saved_queries": {}, + "semantic_models": {}, + "unit_tests": {} + } +} diff --git a/tests/goldens/compiler/example/waffle-shop.json b/tests/goldens/compiler/example/waffle-shop.json new file mode 100644 index 0000000000..e18976bf03 --- /dev/null +++ b/tests/goldens/compiler/example/waffle-shop.json @@ -0,0 +1,2136 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 0, + "diagnostics": [] + }, + { + "label": "compile-warm", + "exit_code": 0, + "diagnostics": [] + }, + { + "label": "compile-store-warm", + "exit_code": 0, + "diagnostics": [] + }, + { + "label": "plan", + "exit_code": 0, + "diagnostics": [] + } + ], + "compiled": { + "audits/generic/customer_status_snapshot/not_null__customer_id.sql": [ + "SELECT customer_id", + "FROM __ref(\"customer_status_snapshot\")", + "WHERE customer_id IS NULL", + "" + ], + "audits/generic/customer_status_snapshot/unique__customer_id.sql": [ + "SELECT customer_id, COUNT(*) AS duplicate_count", + "FROM __ref(\"customer_status_snapshot\")", + "WHERE customer_id IS NOT NULL", + "GROUP BY customer_id", + "HAVING COUNT(*) > 1", + "" + ], + "audits/generic/daily_order_partitioned/not_null__order_date.sql": [ + "SELECT order_date", + "FROM __ref(\"daily_order_partitioned\")", + "WHERE order_date IS NULL", + "" + ], + "audits/generic/daily_order_partitioned/waffles_ordered_is_positive.sql": [ + "SELECT *", + "FROM __ref(\"daily_order_partitioned\")", + "WHERE NOT (waffles_ordered > 0)", + "" + ], + "audits/generic/daily_order_rollup/daily_orders_placed_is_non_negative.sql": [ + "SELECT *", + "FROM __ref(\"daily_order_rollup\")", + "WHERE NOT (orders_placed >= 0)", + "" + ], + "audits/generic/daily_order_rollup/not_null__activity_day.sql": [ + "SELECT activity_day", + "FROM __ref(\"daily_order_rollup\")", + "WHERE activity_day IS NULL", + "" + ], + "audits/generic/daily_revenue/revenue_is_non_negative.sql": [ + "SELECT *", + "FROM __ref(\"daily_revenue\")", + "WHERE NOT (total_revenue_cents >= 0)", + "" + ], + "audits/generic/dim_customers/not_null__customer_id.sql": [ + "SELECT customer_id", + "FROM __ref(\"dim_customers\")", + "WHERE customer_id IS NULL", + "" + ], + "audits/generic/dim_customers/unique__customer_id.sql": [ + "SELECT customer_id, COUNT(*) AS duplicate_count", + "FROM __ref(\"dim_customers\")", + "WHERE customer_id IS NOT NULL", + "GROUP BY customer_id", + "HAVING COUNT(*) > 1", + "" + ], + "audits/generic/fact_orders/not_null__order_id.sql": [ + "SELECT order_id", + "FROM __ref(\"fact_orders\")", + "WHERE order_id IS NULL", + "" + ], + "audits/generic/hourly_activity_with_daily_context/day_orders_cover_hourly_orders.sql": [ + "SELECT *", + "FROM __ref(\"hourly_activity_with_daily_context\")", + "WHERE NOT (day_orders_placed >= orders_placed)", + "" + ], + "audits/generic/hourly_activity_with_daily_context/not_null__activity_hour.sql": [ + "SELECT activity_hour", + "FROM __ref(\"hourly_activity_with_daily_context\")", + "WHERE activity_hour IS NULL", + "" + ], + "audits/generic/hourly_order_activity/not_null__activity_hour.sql": [ + "SELECT activity_hour", + "FROM __ref(\"hourly_order_activity\")", + "WHERE activity_hour IS NULL", + "" + ], + "audits/generic/hourly_order_activity/orders_placed_is_non_negative.sql": [ + "SELECT *", + "FROM __ref(\"hourly_order_activity\")", + "WHERE NOT (orders_placed >= 0)", + "" + ], + "audits/generic/order_status_index/not_null__order_id.sql": [ + "SELECT order_id", + "FROM __ref(\"order_status_index\")", + "WHERE order_id IS NULL", + "" + ], + "audits/generic/order_status_index/unique__order_id.sql": [ + "SELECT order_id, COUNT(*) AS duplicate_count", + "FROM __ref(\"order_status_index\")", + "WHERE order_id IS NOT NULL", + "GROUP BY order_id", + "HAVING COUNT(*) > 1", + "" + ], + "audits/generic/stg_customers/not_null__customer_id.sql": [ + "SELECT customer_id", + "FROM __ref(\"stg_customers\")", + "WHERE customer_id IS NULL", + "" + ], + "audits/generic/stg_customers/not_null__email.sql": [ + "SELECT email", + "FROM __ref(\"stg_customers\")", + "WHERE email IS NULL", + "" + ], + "audits/generic/stg_customers/unique__customer_id.sql": [ + "SELECT customer_id, COUNT(*) AS duplicate_count", + "FROM __ref(\"stg_customers\")", + "WHERE customer_id IS NOT NULL", + "GROUP BY customer_id", + "HAVING COUNT(*) > 1", + "" + ], + "audits/generic/stg_orders/accepted_values__status.sql": [ + "SELECT status", + "FROM __ref(\"stg_orders\")", + "WHERE status IS NOT NULL", + " AND status NOT IN ('placed', 'preparing', 'ready', 'completed', 'cancelled')", + "" + ], + "audits/generic/stg_orders/not_null__customer_id.sql": [ + "SELECT customer_id", + "FROM __ref(\"stg_orders\")", + "WHERE customer_id IS NULL", + "" + ], + "audits/generic/stg_orders/not_null__order_id.sql": [ + "SELECT order_id", + "FROM __ref(\"stg_orders\")", + "WHERE order_id IS NULL", + "" + ], + "audits/generic/stg_orders/relationships__waffle_type_id.sql": [ + "SELECT waffle_type_id", + "FROM __ref(\"stg_orders\")", + "WHERE waffle_type_id IS NOT NULL", + " AND waffle_type_id NOT IN (", + " SELECT waffle_type_id", + " FROM __seed(\"waffle_types\")", + " WHERE waffle_type_id IS NOT NULL", + " )", + "" + ], + "audits/generic/stg_orders/unique__order_id.sql": [ + "SELECT order_id, COUNT(*) AS duplicate_count", + "FROM __ref(\"stg_orders\")", + "WHERE order_id IS NOT NULL", + "GROUP BY order_id", + "HAVING COUNT(*) > 1", + "" + ], + "audits/generic/stg_payments/not_null__order_id.sql": [ + "SELECT order_id", + "FROM __ref(\"stg_payments\")", + "WHERE order_id IS NULL", + "" + ], + "audits/generic/stg_payments/not_null__payment_id.sql": [ + "SELECT payment_id", + "FROM __ref(\"stg_payments\")", + "WHERE payment_id IS NULL", + "" + ], + "audits/generic/stg_payments/relationships__order_id.sql": [ + "SELECT order_id", + "FROM __ref(\"stg_payments\")", + "WHERE order_id IS NOT NULL", + " AND order_id NOT IN (", + " SELECT id", + " FROM __source(\"raw__orders\")", + " WHERE id IS NOT NULL", + " )", + "" + ], + "audits/generic/stg_payments/unique__payment_id.sql": [ + "SELECT payment_id, COUNT(*) AS duplicate_count", + "FROM __ref(\"stg_payments\")", + "WHERE payment_id IS NOT NULL", + "GROUP BY payment_id", + "HAVING COUNT(*) > 1", + "" + ], + "audits/generic/waffle_types/not_null__waffle_type_id.sql": [ + "SELECT waffle_type_id", + "FROM __seed(\"waffle_types\")", + "WHERE waffle_type_id IS NULL", + "" + ], + "audits/generic/waffle_types/unique__waffle_type_id.sql": [ + "SELECT waffle_type_id, COUNT(*) AS duplicate_count", + "FROM __seed(\"waffle_types\")", + "WHERE waffle_type_id IS NOT NULL", + "GROUP BY waffle_type_id", + "HAVING COUNT(*) > 1", + "" + ], + "audits/singular/fact_orders_have_customers.sql": [ + "SELECT f.order_id, f.customer_id", + "FROM __ref(\"fact_orders\") f", + "LEFT JOIN __ref(\"dim_customers\") d ON f.customer_id = d.customer_id", + "WHERE d.customer_id IS NULL", + "" + ], + "functions/sql/table_fn__customer_orders.sql": [ + "CREATE OR REPLACE MACRO dev.table_fn__customer_orders(p_customer_id) AS TABLE", + "SELECT", + " order_id,", + " ordered_at,", + " waffle_name,", + " line_total_cents,", + " order_status,", + " is_completed_order", + "FROM __ref(\"fact_orders\")", + "WHERE customer_id = p_customer_id", + "" + ], + "functions/sql/udf__is_completed_order.sql": [ + "CREATE OR REPLACE MACRO dev.udf__is_completed_order(order_status) AS (", + "order_status = 'completed'", + ")", + "" + ], + "models/intermediate/customer_status_snapshot.sql": [ + "SELECT", + " customer_id,", + " MAX(ordered_at) AS last_ordered_at,", + " MAX(order_status) AS latest_order_status,", + " COUNT(*) AS total_orders,", + " SUM(line_total_cents) AS total_revenue_cents", + "FROM __ref(\"fact_orders\")", + "GROUP BY customer_id", + "" + ], + "models/intermediate/order_status_index.sql": [ + "SELECT", + " order_id,", + " customer_id,", + " order_status,", + " ordered_at,", + " line_total_cents", + "FROM __ref(\"fact_orders\")", + "" + ], + "models/marts/daily_order_partitioned.sql": [ + "SELECT", + " CAST(o.ordered_at AS DATE) AS order_date,", + " COUNT(DISTINCT o.order_id) AS order_count,", + " SUM(o.quantity) AS waffles_ordered,", + " COUNT(DISTINCT o.customer_id) AS unique_customers", + "FROM __ref(\"stg_orders\") o", + "WHERE CAST(o.ordered_at AS DATE) >= CAST(@@@partition_start AS DATE)", + " AND CAST(o.ordered_at AS DATE) < CAST(@@@partition_end AS DATE)", + "GROUP BY CAST(o.ordered_at AS DATE)", + "" + ], + "models/marts/daily_order_rollup.sql": [ + "SELECT", + " DATE_TRUNC('day', activity_hour) AS activity_day,", + " SUM(orders_placed) AS orders_placed,", + " SUM(waffles_ordered) AS waffles_ordered,", + " SUM(revenue_cents) AS revenue_cents", + "FROM __ref(\"hourly_order_activity\")", + "GROUP BY DATE_TRUNC('day', activity_hour)", + "" + ], + "models/marts/daily_revenue.sql": [ + "SELECT", + " CAST(o.ordered_at AS DATE) AS revenue_date,", + " COUNT(DISTINCT o.order_id) AS order_count,", + " SUM(o.quantity) AS waffles_sold,", + " SUM(p.amount_cents) AS total_revenue_cents,", + " ROUND((SUM(p.amount_cents)) / 100.0, 2) AS total_revenue_dollars,", + " SUM(p.amount_cents) / COUNT(DISTINCT o.order_id) AS avg_order_value_cents", + "FROM __ref(\"stg_orders\") o", + "INNER JOIN __ref(\"stg_payments\") p ON o.order_id = p.order_id AND p.payment_status = 'success'", + "GROUP BY CAST(o.ordered_at AS DATE)", + "" + ], + "models/marts/dim_customers.sql": [ + "SELECT", + " c.customer_id,", + " c.first_name,", + " c.last_name,", + " c.email,", + " c.created_at AS customer_since,", + " COUNT(DISTINCT o.order_id) AS lifetime_orders,", + " COALESCE(SUM(p.amount_cents), 0) AS lifetime_spend_cents", + "FROM __ref(\"stg_customers\") c", + "LEFT JOIN __ref(\"stg_orders\") o ON c.customer_id = o.customer_id", + "LEFT JOIN __ref(\"stg_payments\") p ON o.order_id = p.order_id AND p.payment_status = 'success'", + "GROUP BY c.customer_id, c.first_name, c.last_name, c.email, c.created_at", + "" + ], + "models/marts/fact_orders.sql": [ + "SELECT", + " o.order_id,", + " o.customer_id,", + " o.waffle_type_id,", + " w.waffle_name,", + " w.category AS waffle_category,", + " o.quantity,", + " w.price_cents * o.quantity AS line_total_cents,", + " o.ordered_at,", + " o.status AS order_status,", + " __udf(\"udf__is_completed_order\")(o.status) AS is_completed_order,", + " p.payment_method,", + " p.payment_status,", + " p.amount_cents AS payment_amount_cents", + "FROM __ref(\"stg_orders\") o", + "LEFT JOIN __seed(\"waffle_types\") w ON o.waffle_type_id = w.waffle_type_id", + "LEFT JOIN __ref(\"stg_payments\") p ON o.order_id = p.order_id", + "" + ], + "models/marts/hourly_activity_with_daily_context.sql": [ + "SELECT", + " h.activity_hour,", + " h.orders_placed,", + " d.orders_placed AS day_orders_placed,", + " h.waffles_ordered,", + " h.revenue_cents", + "FROM __ref(\"hourly_order_activity\") h", + "INNER JOIN __ref(\"daily_order_rollup\") d", + " ON DATE_TRUNC('day', h.activity_hour) = d.activity_day", + "" + ], + "models/marts/hourly_order_activity.sql": [ + "SELECT", + " DATE_TRUNC('hour', o.ordered_at) AS activity_hour,", + " COUNT(*) AS orders_placed,", + " SUM(o.quantity) AS waffles_ordered,", + " SUM(o.line_total_cents) AS revenue_cents", + "FROM __ref(\"fact_orders\") o", + "GROUP BY DATE_TRUNC('hour', o.ordered_at)", + "" + ], + "models/staging/stg_customers.sql": [ + "SELECT", + " id AS customer_id,", + " first_name,", + " last_name,", + " email,", + " created_at", + "FROM __source(\"raw__customers\")", + "" + ], + "models/staging/stg_orders.sql": [ + "SELECT", + " id AS order_id,", + " customer_id,", + " waffle_type_id,", + " quantity,", + " ordered_at,", + " status", + "FROM __source(\"raw__orders\")", + "" + ], + "models/staging/stg_payments.sql": [ + "SELECT", + " id AS payment_id,", + " order_id,", + " amount_cents,", + " payment_method,", + " paid_at,", + " status AS payment_status", + "FROM __source(\"raw__payments\")", + "" + ], + "tests/_chain_/daily_revenue__stg_orders__stg_payments/test_daily_revenue_chain.sql": [ + "WITH __source__raw__orders AS (SELECT", + " 1 AS id,", + " 100 AS customer_id,", + " 2 AS waffle_type_id,", + " 3 AS quantity,", + " CAST('2026-04-01 10:00:00' AS TIMESTAMP) AS ordered_at,", + " 'completed' AS status", + " UNION ALL", + " SELECT", + " 2 AS id,", + " 101 AS customer_id,", + " 1 AS waffle_type_id,", + " 1 AS quantity,", + " CAST('2026-04-01 11:00:00' AS TIMESTAMP) AS ordered_at,", + " 'completed' AS status),", + "__ref__stg_orders AS (SELECT", + " id AS order_id,", + " customer_id,", + " waffle_type_id,", + " quantity,", + " ordered_at,", + " status", + "FROM __source__raw__orders),", + "__source__raw__payments AS (SELECT", + " 10 AS id,", + " 1 AS order_id,", + " 2850 AS amount_cents,", + " 'card' AS payment_method,", + " CAST('2026-04-01 10:05:00' AS TIMESTAMP) AS paid_at,", + " 'success' AS status", + " UNION ALL", + " SELECT", + " 11 AS id,", + " 2 AS order_id,", + " 850 AS amount_cents,", + " 'cash' AS payment_method,", + " CAST('2026-04-01 11:10:00' AS TIMESTAMP) AS paid_at,", + " 'failed' AS status),", + "__ref__stg_payments AS (SELECT", + " id AS payment_id,", + " order_id,", + " amount_cents,", + " payment_method,", + " paid_at,", + " status AS payment_status", + "FROM __source__raw__payments),", + "__ref__daily_revenue AS (SELECT", + " CAST(o.ordered_at AS DATE) AS revenue_date,", + " COUNT(DISTINCT o.order_id) AS order_count,", + " SUM(o.quantity) AS waffles_sold,", + " SUM(p.amount_cents) AS total_revenue_cents,", + " ROUND((SUM(p.amount_cents)) / 100.0, 2) AS total_revenue_dollars,", + " SUM(p.amount_cents) / COUNT(DISTINCT o.order_id) AS avg_order_value_cents", + "FROM __ref__stg_orders o", + "INNER JOIN __ref__stg_payments p ON o.order_id = p.order_id AND p.payment_status = 'success'", + "GROUP BY CAST(o.ordered_at AS DATE)),", + "__actual__stg_orders AS (SELECT * FROM __ref__stg_orders),", + "__expected__stg_orders AS (SELECT", + " 1 AS order_id,", + " 100 AS customer_id,", + " 2 AS waffle_type_id,", + " 3 AS quantity,", + " CAST('2026-04-01 10:00:00' AS TIMESTAMP) AS ordered_at,", + " 'completed' AS status", + " UNION ALL", + " SELECT", + " 2 AS order_id,", + " 101 AS customer_id,", + " 1 AS waffle_type_id,", + " 1 AS quantity,", + " CAST('2026-04-01 11:00:00' AS TIMESTAMP) AS ordered_at,", + " 'completed' AS status),", + "__actual__stg_payments AS (SELECT * FROM __ref__stg_payments),", + "__expected__stg_payments AS (SELECT", + " 10 AS payment_id,", + " 1 AS order_id,", + " 2850 AS amount_cents,", + " 'card' AS payment_method,", + " CAST('2026-04-01 10:05:00' AS TIMESTAMP) AS paid_at,", + " 'success' AS payment_status", + " UNION ALL", + " SELECT", + " 11 AS payment_id,", + " 2 AS order_id,", + " 850 AS amount_cents,", + " 'cash' AS payment_method,", + " CAST('2026-04-01 11:10:00' AS TIMESTAMP) AS paid_at,", + " 'failed' AS payment_status),", + "__actual__daily_revenue AS (SELECT * FROM __ref__daily_revenue),", + "__expected__daily_revenue AS (SELECT", + " CAST('2026-04-01' AS DATE) AS revenue_date,", + " 1 AS order_count,", + " 3 AS waffles_sold,", + " 2850 AS total_revenue_cents,", + " 28.5 AS total_revenue_dollars,", + " 2850 AS avg_order_value_cents),", + "__assert__daily_revenue_is_non_negative AS (SELECT *", + " FROM __ref__daily_revenue", + " WHERE total_revenue_cents < 0)", + "SELECT 0 AS step_index, 'stg_orders' AS model_name, (SELECT COUNT(*) FROM __actual__stg_orders) AS actual_count, (SELECT COUNT(*) FROM __expected__stg_orders) AS expected_count, (SELECT COUNT(*) FROM (SELECT order_id, customer_id, waffle_type_id, quantity, ordered_at, status FROM __actual__stg_orders EXCEPT SELECT order_id, customer_id, waffle_type_id, quantity, ordered_at, status FROM __expected__stg_orders) AS __sqlbuild_mismatch) AS unexpected_count, (SELECT COUNT(*) FROM (SELECT order_id, customer_id, waffle_type_id, quantity, ordered_at, status FROM __expected__stg_orders EXCEPT SELECT order_id, customer_id, waffle_type_id, quantity, ordered_at, status FROM __actual__stg_orders) AS __sqlbuild_missing) AS missing_count", + "UNION ALL", + "SELECT 1 AS step_index, 'stg_payments' AS model_name, (SELECT COUNT(*) FROM __actual__stg_payments) AS actual_count, (SELECT COUNT(*) FROM __expected__stg_payments) AS expected_count, (SELECT COUNT(*) FROM (SELECT payment_id, order_id, amount_cents, payment_method, paid_at, payment_status FROM __actual__stg_payments EXCEPT SELECT payment_id, order_id, amount_cents, payment_method, paid_at, payment_status FROM __expected__stg_payments) AS __sqlbuild_mismatch) AS unexpected_count, (SELECT COUNT(*) FROM (SELECT payment_id, order_id, amount_cents, payment_method, paid_at, payment_status FROM __expected__stg_payments EXCEPT SELECT payment_id, order_id, amount_cents, payment_method, paid_at, payment_status FROM __actual__stg_payments) AS __sqlbuild_missing) AS missing_count", + "UNION ALL", + "SELECT 2 AS step_index, 'daily_revenue' AS model_name, (SELECT COUNT(*) FROM __actual__daily_revenue) AS actual_count, (SELECT COUNT(*) FROM __expected__daily_revenue) AS expected_count, (SELECT COUNT(*) FROM (SELECT revenue_date, order_count, waffles_sold, total_revenue_cents, total_revenue_dollars, avg_order_value_cents FROM __actual__daily_revenue EXCEPT SELECT revenue_date, order_count, waffles_sold, total_revenue_cents, total_revenue_dollars, avg_order_value_cents FROM __expected__daily_revenue) AS __sqlbuild_mismatch) AS unexpected_count, (SELECT COUNT(*) FROM (SELECT revenue_date, order_count, waffles_sold, total_revenue_cents, total_revenue_dollars, avg_order_value_cents FROM __expected__daily_revenue EXCEPT SELECT revenue_date, order_count, waffles_sold, total_revenue_cents, total_revenue_dollars, avg_order_value_cents FROM __actual__daily_revenue) AS __sqlbuild_missing) AS missing_count", + "UNION ALL", + "SELECT 3 AS step_index, 'assertion daily_revenue_is_non_negative' AS model_name, (SELECT COUNT(*) FROM __assert__daily_revenue_is_non_negative) AS actual_count, 0 AS expected_count, (SELECT COUNT(*) FROM __assert__daily_revenue_is_non_negative) AS unexpected_count, 0 AS missing_count", + "" + ], + "tests/fact_orders/test_fact_orders.sql": [ + "WITH __ref__stg_orders AS (SELECT", + " 1 AS order_id,", + " 100 AS customer_id,", + " 2 AS waffle_type_id,", + " 3 AS quantity,", + " CAST('2026-04-01 10:00:00' AS TIMESTAMP) AS ordered_at,", + " 'completed' AS status),", + "__ref__stg_payments AS (SELECT", + " 10 AS payment_id,", + " 1 AS order_id,", + " 2850 AS amount_cents,", + " 'card' AS payment_method,", + " CAST('2026-04-01 10:05:00' AS TIMESTAMP) AS paid_at,", + " 'success' AS payment_status),", + "__seed__waffle_types AS (SELECT", + " 2 AS waffle_type_id,", + " 'Liege' AS waffle_name,", + " 'sweet' AS category,", + " 950 AS price_cents),", + "__ref__fact_orders AS (SELECT", + " o.order_id,", + " o.customer_id,", + " o.waffle_type_id,", + " w.waffle_name,", + " w.category AS waffle_category,", + " o.quantity,", + " w.price_cents * o.quantity AS line_total_cents,", + " o.ordered_at,", + " o.status AS order_status,", + " dev.udf__is_completed_order(o.status) AS is_completed_order,", + " p.payment_method,", + " p.payment_status,", + " p.amount_cents AS payment_amount_cents", + "FROM __ref__stg_orders o", + "LEFT JOIN __seed__waffle_types w ON o.waffle_type_id = w.waffle_type_id", + "LEFT JOIN __ref__stg_payments p ON o.order_id = p.order_id),", + "__actual__fact_orders AS (SELECT * FROM __ref__fact_orders),", + "__expected__fact_orders AS (SELECT", + " 1 AS order_id,", + " 100 AS customer_id,", + " 2 AS waffle_type_id,", + " 'Liege' AS waffle_name,", + " 'sweet' AS waffle_category,", + " 3 AS quantity,", + " 2850 AS line_total_cents,", + " CAST('2026-04-01 10:00:00' AS TIMESTAMP) AS ordered_at,", + " 'completed' AS order_status,", + " TRUE AS is_completed_order,", + " 'card' AS payment_method,", + " 'success' AS payment_status,", + " 2850 AS payment_amount_cents),", + "__assert__line_totals_are_non_negative AS (SELECT *", + " FROM __ref__fact_orders", + " WHERE line_total_cents < 0)", + "SELECT 0 AS step_index, 'fact_orders' AS model_name, (SELECT COUNT(*) FROM __actual__fact_orders) AS actual_count, (SELECT COUNT(*) FROM __expected__fact_orders) AS expected_count, (SELECT COUNT(*) FROM (SELECT order_id, customer_id, waffle_type_id, waffle_name, waffle_category, quantity, line_total_cents, ordered_at, order_status, is_completed_order, payment_method, payment_status, payment_amount_cents FROM __actual__fact_orders EXCEPT SELECT order_id, customer_id, waffle_type_id, waffle_name, waffle_category, quantity, line_total_cents, ordered_at, order_status, is_completed_order, payment_method, payment_status, payment_amount_cents FROM __expected__fact_orders) AS __sqlbuild_mismatch) AS unexpected_count, (SELECT COUNT(*) FROM (SELECT order_id, customer_id, waffle_type_id, waffle_name, waffle_category, quantity, line_total_cents, ordered_at, order_status, is_completed_order, payment_method, payment_status, payment_amount_cents FROM __expected__fact_orders EXCEPT SELECT order_id, customer_id, waffle_type_id, waffle_name, waffle_category, quantity, line_total_cents, ordered_at, order_status, is_completed_order, payment_method, payment_status, payment_amount_cents FROM __actual__fact_orders) AS __sqlbuild_missing) AS missing_count", + "UNION ALL", + "SELECT 1 AS step_index, 'assertion line_totals_are_non_negative' AS model_name, (SELECT COUNT(*) FROM __assert__line_totals_are_non_negative) AS actual_count, 0 AS expected_count, (SELECT COUNT(*) FROM __assert__line_totals_are_non_negative) AS unexpected_count, 0 AS missing_count", + "" + ], + "tests/macro calculates_line_total_cents/calculates_line_total_cents.sql": [ + "WITH input_values AS (SELECT 950 AS price_cents, 3 AS quantity),", + "__actual__macro_calculates_line_total_cents AS (SELECT price_cents * quantity AS line_total_cents", + " FROM input_values),", + "__expected__macro_calculates_line_total_cents AS (SELECT 2850 AS line_total_cents)", + "SELECT 0 AS step_index, 'macro calculates_line_total_cents' AS model_name, (SELECT COUNT(*) FROM __actual__macro_calculates_line_total_cents) AS actual_count, (SELECT COUNT(*) FROM __expected__macro_calculates_line_total_cents) AS expected_count, (SELECT COUNT(*) FROM (SELECT * FROM __actual__macro_calculates_line_total_cents EXCEPT SELECT * FROM __expected__macro_calculates_line_total_cents) AS __sqlbuild_mismatch) AS unexpected_count, (SELECT COUNT(*) FROM (SELECT * FROM __expected__macro_calculates_line_total_cents EXCEPT SELECT * FROM __actual__macro_calculates_line_total_cents) AS __sqlbuild_missing) AS missing_count", + "" + ], + "tests/stg_orders/test_stg_orders.sql": [ + "WITH __source__raw__orders AS (SELECT", + " 1 AS id,", + " 100 AS customer_id,", + " 2 AS waffle_type_id,", + " 3 AS quantity,", + " CAST('2026-04-01 10:00:00' AS TIMESTAMP) AS ordered_at,", + " 'completed' AS status),", + "__ref__stg_orders AS (SELECT", + " id AS order_id,", + " customer_id,", + " waffle_type_id,", + " quantity,", + " ordered_at,", + " status", + "FROM __source__raw__orders),", + "__actual__stg_orders AS (SELECT * FROM __ref__stg_orders),", + "__expected__stg_orders AS (SELECT", + " 1 AS order_id,", + " 100 AS customer_id,", + " 2 AS waffle_type_id,", + " 3 AS quantity,", + " CAST('2026-04-01 10:00:00' AS TIMESTAMP) AS ordered_at,", + " 'completed' AS status),", + "__assert__order_ids_are_not_null AS (SELECT *", + " FROM __ref__stg_orders", + " WHERE order_id IS NULL)", + "SELECT 0 AS step_index, 'stg_orders' AS model_name, (SELECT COUNT(*) FROM __actual__stg_orders) AS actual_count, (SELECT COUNT(*) FROM __expected__stg_orders) AS expected_count, (SELECT COUNT(*) FROM (SELECT order_id, customer_id, waffle_type_id, quantity, ordered_at, status FROM __actual__stg_orders EXCEPT SELECT order_id, customer_id, waffle_type_id, quantity, ordered_at, status FROM __expected__stg_orders) AS __sqlbuild_mismatch) AS unexpected_count, (SELECT COUNT(*) FROM (SELECT order_id, customer_id, waffle_type_id, quantity, ordered_at, status FROM __expected__stg_orders EXCEPT SELECT order_id, customer_id, waffle_type_id, quantity, ordered_at, status FROM __actual__stg_orders) AS __sqlbuild_missing) AS missing_count", + "UNION ALL", + "SELECT 1 AS step_index, 'assertion order_ids_are_not_null' AS model_name, (SELECT COUNT(*) FROM __assert__order_ids_are_not_null) AS actual_count, 0 AS expected_count, (SELECT COUNT(*) FROM __assert__order_ids_are_not_null) AS unexpected_count, 0 AS missing_count", + "" + ] + }, + "manifest": { + "nodes": { + "model.waffle_shop.customer_status_snapshot": { + "database": null, + "schema": "dev", + "name": "customer_status_snapshot", + "resource_type": "model", + "package_name": "waffle_shop", + "path": "models/intermediate/customer_status_snapshot.sql", + "original_file_path": "models/intermediate/customer_status_snapshot.sql", + "unique_id": "model.waffle_shop.customer_status_snapshot", + "fqn": [ + "waffle_shop", + "models", + "intermediate", + "customer_status_snapshot" + ], + "alias": "customer_status_snapshot", + "config": { + "enabled": true, + "alias": null, + "schema": "dev", + "database": null, + "tags": [ + "intermediate", + "acceptance" + ], + "meta": {}, + "group": null, + "materialized": "incremental", + "incremental_strategy": "merge", + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": [ + "customer_id" + ], + "on_schema_change": "sync_all_columns", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [ + "intermediate", + "acceptance" + ], + "description": "Merge-based customer snapshot with timestamp cursor and explicit backfill policies.", + "columns": { + "customer_id": { + "name": "customer_id", + "description": "", + "meta": {}, + "data_type": null, + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "dev.customer_status_snapshot", + "depends_on": { + "macros": [], + "nodes": [ + "model.waffle_shop.fact_orders" + ] + } + }, + "model.waffle_shop.order_status_index": { + "database": null, + "schema": "dev", + "name": "order_status_index", + "resource_type": "model", + "package_name": "waffle_shop", + "path": "models/intermediate/order_status_index.sql", + "original_file_path": "models/intermediate/order_status_index.sql", + "unique_id": "model.waffle_shop.order_status_index", + "fqn": [ + "waffle_shop", + "models", + "intermediate", + "order_status_index" + ], + "alias": "order_status_index", + "config": { + "enabled": true, + "alias": null, + "schema": "dev", + "database": null, + "tags": [ + "intermediate", + "acceptance" + ], + "meta": {}, + "group": null, + "materialized": "incremental", + "incremental_strategy": "delete_insert", + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": [ + "order_id" + ], + "on_schema_change": "append_new_columns", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [ + "intermediate", + "acceptance" + ], + "description": "Integer-cursor incremental projection over fact orders.", + "columns": { + "order_id": { + "name": "order_id", + "description": "", + "meta": {}, + "data_type": null, + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "dev.order_status_index", + "depends_on": { + "macros": [], + "nodes": [ + "model.waffle_shop.fact_orders" + ] + } + }, + "model.waffle_shop.daily_order_partitioned": { + "database": null, + "schema": "dev", + "name": "daily_order_partitioned", + "resource_type": "model", + "package_name": "waffle_shop", + "path": "models/marts/daily_order_partitioned.sql", + "original_file_path": "models/marts/daily_order_partitioned.sql", + "unique_id": "model.waffle_shop.daily_order_partitioned", + "fqn": [ + "waffle_shop", + "models", + "marts", + "daily_order_partitioned" + ], + "alias": "daily_order_partitioned", + "config": { + "enabled": true, + "alias": null, + "schema": "dev", + "database": null, + "tags": [ + "marts" + ], + "meta": {}, + "group": null, + "materialized": "partition_tracked", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [ + "marts" + ], + "description": "Partition-tracked daily order summary using custom materialization.", + "columns": { + "order_date": { + "name": "order_date", + "description": "", + "meta": {}, + "data_type": null, + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "dev.daily_order_partitioned", + "depends_on": { + "macros": [], + "nodes": [ + "model.waffle_shop.stg_orders" + ] + } + }, + "model.waffle_shop.daily_order_rollup": { + "database": null, + "schema": "dev", + "name": "daily_order_rollup", + "resource_type": "model", + "package_name": "waffle_shop", + "path": "models/marts/daily_order_rollup.sql", + "original_file_path": "models/marts/daily_order_rollup.sql", + "unique_id": "model.waffle_shop.daily_order_rollup", + "fqn": [ + "waffle_shop", + "models", + "marts", + "daily_order_rollup" + ], + "alias": "daily_order_rollup", + "config": { + "enabled": true, + "alias": null, + "schema": "dev", + "database": null, + "tags": [ + "marts", + "acceptance" + ], + "meta": {}, + "group": null, + "materialized": "incremental", + "incremental_strategy": "delete_insert", + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [ + "marts", + "acceptance" + ], + "description": "Downstream daily microbatch rollup with a wider batch size than its hourly upstream.", + "columns": { + "activity_day": { + "name": "activity_day", + "description": "", + "meta": {}, + "data_type": null, + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "dev.daily_order_rollup", + "depends_on": { + "macros": [], + "nodes": [ + "model.waffle_shop.hourly_order_activity" + ] + } + }, + "model.waffle_shop.daily_revenue": { + "database": null, + "schema": "dev", + "name": "daily_revenue", + "resource_type": "model", + "package_name": "waffle_shop", + "path": "models/marts/daily_revenue.sql", + "original_file_path": "models/marts/daily_revenue.sql", + "unique_id": "model.waffle_shop.daily_revenue", + "fqn": [ + "waffle_shop", + "models", + "marts", + "daily_revenue" + ], + "alias": "daily_revenue", + "config": { + "enabled": true, + "alias": null, + "schema": "dev", + "database": null, + "tags": [ + "marts" + ], + "meta": {}, + "group": null, + "materialized": "table", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [ + "marts" + ], + "description": "Daily revenue aggregation.", + "columns": { + "revenue_date": { + "name": "revenue_date", + "description": "", + "meta": {}, + "data_type": null, + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "dev.daily_revenue", + "depends_on": { + "macros": [], + "nodes": [ + "model.waffle_shop.stg_orders", + "model.waffle_shop.stg_payments" + ] + } + }, + "model.waffle_shop.dim_customers": { + "database": null, + "schema": "dev", + "name": "dim_customers", + "resource_type": "model", + "package_name": "waffle_shop", + "path": "models/marts/dim_customers.sql", + "original_file_path": "models/marts/dim_customers.sql", + "unique_id": "model.waffle_shop.dim_customers", + "fqn": [ + "waffle_shop", + "models", + "marts", + "dim_customers" + ], + "alias": "dim_customers", + "config": { + "enabled": true, + "alias": null, + "schema": "dev", + "database": null, + "tags": [ + "marts" + ], + "meta": {}, + "group": null, + "materialized": "table", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": [ + "customer_id" + ], + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [ + "marts" + ], + "description": "Customer dimension with lifetime metrics.", + "columns": { + "customer_id": { + "name": "customer_id", + "description": "", + "meta": {}, + "data_type": null, + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "dev.dim_customers", + "depends_on": { + "macros": [], + "nodes": [ + "model.waffle_shop.stg_customers", + "model.waffle_shop.stg_orders", + "model.waffle_shop.stg_payments" + ] + } + }, + "model.waffle_shop.fact_orders": { + "database": null, + "schema": "dev", + "name": "fact_orders", + "resource_type": "model", + "package_name": "waffle_shop", + "path": "models/marts/fact_orders.sql", + "original_file_path": "models/marts/fact_orders.sql", + "unique_id": "model.waffle_shop.fact_orders", + "fqn": [ + "waffle_shop", + "models", + "marts", + "fact_orders" + ], + "alias": "fact_orders", + "config": { + "enabled": true, + "alias": null, + "schema": "dev", + "database": null, + "tags": [ + "marts" + ], + "meta": {}, + "group": null, + "materialized": "table", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [ + "marts" + ], + "description": "Order fact table with waffle and payment details.", + "columns": { + "order_id": { + "name": "order_id", + "description": "", + "meta": {}, + "data_type": null, + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "dev.fact_orders", + "depends_on": { + "macros": [], + "nodes": [ + "model.waffle_shop.udf__is_completed_order", + "model.waffle_shop.stg_orders", + "seed.waffle_shop.waffle_types", + "model.waffle_shop.stg_payments" + ] + } + }, + "model.waffle_shop.hourly_activity_with_daily_context": { + "database": null, + "schema": "dev", + "name": "hourly_activity_with_daily_context", + "resource_type": "model", + "package_name": "waffle_shop", + "path": "models/marts/hourly_activity_with_daily_context.sql", + "original_file_path": "models/marts/hourly_activity_with_daily_context.sql", + "unique_id": "model.waffle_shop.hourly_activity_with_daily_context", + "fqn": [ + "waffle_shop", + "models", + "marts", + "hourly_activity_with_daily_context" + ], + "alias": "hourly_activity_with_daily_context", + "config": { + "enabled": true, + "alias": null, + "schema": "dev", + "database": null, + "tags": [ + "marts", + "acceptance" + ], + "meta": {}, + "group": null, + "materialized": "incremental", + "incremental_strategy": "delete_insert", + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [ + "marts", + "acceptance" + ], + "description": "Hourly downstream microbatch model that depends on a coarser-grain daily upstream.", + "columns": { + "activity_hour": { + "name": "activity_hour", + "description": "", + "meta": {}, + "data_type": null, + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "dev.hourly_activity_with_daily_context", + "depends_on": { + "macros": [], + "nodes": [ + "model.waffle_shop.hourly_order_activity", + "model.waffle_shop.daily_order_rollup" + ] + } + }, + "model.waffle_shop.hourly_order_activity": { + "database": null, + "schema": "dev", + "name": "hourly_order_activity", + "resource_type": "model", + "package_name": "waffle_shop", + "path": "models/marts/hourly_order_activity.sql", + "original_file_path": "models/marts/hourly_order_activity.sql", + "unique_id": "model.waffle_shop.hourly_order_activity", + "fqn": [ + "waffle_shop", + "models", + "marts", + "hourly_order_activity" + ], + "alias": "hourly_order_activity", + "config": { + "enabled": true, + "alias": null, + "schema": "dev", + "database": null, + "tags": [ + "marts" + ], + "meta": {}, + "group": null, + "materialized": "incremental", + "incremental_strategy": "delete_insert", + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [ + "marts" + ], + "description": "Hourly order activity aggregated via microbatch incremental.", + "columns": { + "activity_hour": { + "name": "activity_hour", + "description": "", + "meta": {}, + "data_type": null, + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "dev.hourly_order_activity", + "depends_on": { + "macros": [], + "nodes": [ + "model.waffle_shop.fact_orders" + ] + } + }, + "model.waffle_shop.stg_customers": { + "database": null, + "schema": "dev", + "name": "stg_customers", + "resource_type": "model", + "package_name": "waffle_shop", + "path": "models/staging/stg_customers.sql", + "original_file_path": "models/staging/stg_customers.sql", + "unique_id": "model.waffle_shop.stg_customers", + "fqn": [ + "waffle_shop", + "models", + "staging", + "stg_customers" + ], + "alias": "stg_customers", + "config": { + "enabled": true, + "alias": null, + "schema": "dev", + "database": null, + "tags": [ + "staging" + ], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [ + "staging" + ], + "description": "Cleaned customer records.", + "columns": { + "customer_id": { + "name": "customer_id", + "description": "", + "meta": {}, + "data_type": null, + "constraints": [], + "quote": null, + "tags": [] + }, + "email": { + "name": "email", + "description": "", + "meta": {}, + "data_type": null, + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "dev.stg_customers", + "depends_on": { + "macros": [], + "nodes": [ + "source.waffle_shop.raw__customers" + ] + } + }, + "model.waffle_shop.stg_orders": { + "database": null, + "schema": "dev", + "name": "stg_orders", + "resource_type": "model", + "package_name": "waffle_shop", + "path": "models/staging/stg_orders.sql", + "original_file_path": "models/staging/stg_orders.sql", + "unique_id": "model.waffle_shop.stg_orders", + "fqn": [ + "waffle_shop", + "models", + "staging", + "stg_orders" + ], + "alias": "stg_orders", + "config": { + "enabled": true, + "alias": null, + "schema": "dev", + "database": null, + "tags": [ + "staging" + ], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [ + "staging" + ], + "description": "Cleaned order records.", + "columns": { + "order_id": { + "name": "order_id", + "description": "", + "meta": {}, + "data_type": null, + "constraints": [], + "quote": null, + "tags": [] + }, + "customer_id": { + "name": "customer_id", + "description": "", + "meta": {}, + "data_type": null, + "constraints": [], + "quote": null, + "tags": [] + }, + "waffle_type_id": { + "name": "waffle_type_id", + "description": "", + "meta": {}, + "data_type": null, + "constraints": [], + "quote": null, + "tags": [] + }, + "status": { + "name": "status", + "description": "", + "meta": {}, + "data_type": null, + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "dev.stg_orders", + "depends_on": { + "macros": [], + "nodes": [ + "source.waffle_shop.raw__orders" + ] + } + }, + "model.waffle_shop.stg_payments": { + "database": null, + "schema": "dev", + "name": "stg_payments", + "resource_type": "model", + "package_name": "waffle_shop", + "path": "models/staging/stg_payments.sql", + "original_file_path": "models/staging/stg_payments.sql", + "unique_id": "model.waffle_shop.stg_payments", + "fqn": [ + "waffle_shop", + "models", + "staging", + "stg_payments" + ], + "alias": "stg_payments", + "config": { + "enabled": true, + "alias": null, + "schema": "dev", + "database": null, + "tags": [ + "staging" + ], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [ + "staging" + ], + "description": "Cleaned payment records.", + "columns": { + "payment_id": { + "name": "payment_id", + "description": "", + "meta": {}, + "data_type": null, + "constraints": [], + "quote": null, + "tags": [] + }, + "order_id": { + "name": "order_id", + "description": "", + "meta": {}, + "data_type": null, + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "dev.stg_payments", + "depends_on": { + "macros": [], + "nodes": [ + "source.waffle_shop.raw__payments" + ] + } + }, + "seed.waffle_shop.waffle_types": { + "database": null, + "schema": "dev", + "name": "waffle_types", + "resource_type": "seed", + "package_name": "waffle_shop", + "path": "seeds/waffle_types.csv", + "original_file_path": "seeds/waffle_types.csv", + "unique_id": "seed.waffle_shop.waffle_types", + "fqn": [ + "waffle_shop", + "seeds", + "waffle_types" + ], + "alias": "waffle_types", + "config": { + "enabled": true, + "alias": null, + "schema": "dev", + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "seed", + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + }, + "delimiter": ",", + "quote_columns": null + }, + "tags": [], + "description": "Waffle menu items with pricing.", + "columns": { + "waffle_type_id": { + "name": "waffle_type_id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "waffle_name": { + "name": "waffle_name", + "description": "", + "meta": {}, + "data_type": "VARCHAR", + "constraints": [], + "quote": null, + "tags": [] + }, + "category": { + "name": "category", + "description": "", + "meta": {}, + "data_type": "VARCHAR", + "constraints": [], + "quote": null, + "tags": [] + }, + "price_cents": { + "name": "price_cents", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "dev.waffle_types", + "root_path": null, + "depends_on": { + "macros": [] + } + } + }, + "sources": { + "source.waffle_shop.raw__customers": { + "database": null, + "schema": "dev", + "name": "raw__customers", + "resource_type": "source", + "package_name": "waffle_shop", + "path": "sources/raw.yml", + "original_file_path": "sources/raw.yml", + "unique_id": "source.waffle_shop.raw__customers", + "fqn": [ + "waffle_shop", + "raw__customers" + ], + "source_name": "raw__customers", + "source_description": "", + "loader": "", + "identifier": "raw__customers", + "quoting": {}, + "loaded_at_field": null, + "freshness": null, + "external": null, + "description": "Raw customer records loaded by the Python loader", + "columns": { + "id": { + "name": "id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "first_name": { + "name": "first_name", + "description": "", + "meta": {}, + "data_type": "VARCHAR", + "constraints": [], + "quote": null, + "tags": [] + }, + "last_name": { + "name": "last_name", + "description": "", + "meta": {}, + "data_type": "VARCHAR", + "constraints": [], + "quote": null, + "tags": [] + }, + "email": { + "name": "email", + "description": "", + "meta": {}, + "data_type": "VARCHAR", + "constraints": [], + "quote": null, + "tags": [] + }, + "created_at": { + "name": "created_at", + "description": "", + "meta": {}, + "data_type": "TIMESTAMP", + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": { + "sqlbuild": { + "loader": "raw__customers", + "auto_load": false, + "write_strategy": "table" + } + }, + "source_meta": {}, + "tags": [], + "config": {}, + "patch_path": null, + "unrendered_config": {}, + "relation_name": null + }, + "source.waffle_shop.raw__orders": { + "database": null, + "schema": "dev", + "name": "raw__orders", + "resource_type": "source", + "package_name": "waffle_shop", + "path": "sources/raw.yml", + "original_file_path": "sources/raw.yml", + "unique_id": "source.waffle_shop.raw__orders", + "fqn": [ + "waffle_shop", + "raw__orders" + ], + "source_name": "raw__orders", + "source_description": "", + "loader": "", + "identifier": "raw__orders", + "quoting": {}, + "loaded_at_field": null, + "freshness": null, + "external": null, + "description": "Raw order records loaded by the Python loader", + "columns": { + "id": { + "name": "id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "customer_id": { + "name": "customer_id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "waffle_type_id": { + "name": "waffle_type_id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "quantity": { + "name": "quantity", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "ordered_at": { + "name": "ordered_at", + "description": "", + "meta": {}, + "data_type": "TIMESTAMP", + "constraints": [], + "quote": null, + "tags": [] + }, + "status": { + "name": "status", + "description": "", + "meta": {}, + "data_type": "VARCHAR", + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": { + "sqlbuild": { + "loader": "raw__orders", + "auto_load": false, + "write_strategy": "delete_insert", + "cursor_column": "ordered_at" + } + }, + "source_meta": {}, + "tags": [], + "config": {}, + "patch_path": null, + "unrendered_config": {}, + "relation_name": null + }, + "source.waffle_shop.raw__payments": { + "database": null, + "schema": "", + "name": "raw__payments", + "resource_type": "source", + "package_name": "waffle_shop", + "path": "sources/raw.yml", + "original_file_path": "sources/raw.yml", + "unique_id": "source.waffle_shop.raw__payments", + "fqn": [ + "waffle_shop", + "raw__payments" + ], + "source_name": "raw__payments", + "source_description": "", + "loader": "", + "identifier": "raw__payments", + "quoting": {}, + "loaded_at_field": null, + "freshness": null, + "external": null, + "description": "Payment records for each order, defined inline", + "columns": { + "amount_cents": { + "name": "amount_cents", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": {}, + "source_meta": {}, + "tags": [], + "config": {}, + "patch_path": null, + "unrendered_config": {}, + "relation_name": null + } + }, + "macros": { + "macro.waffle_shop.line_total_cents": { + "name": "line_total_cents", + "resource_type": "macro", + "package_name": "waffle_shop", + "path": "macros/currency.py", + "original_file_path": "macros/currency.py", + "unique_id": "macro.waffle_shop.line_total_cents", + "macro_sql": "\"\"\"Project-wide currency macros.\"\"\"\n\n\ndef line_total_cents(price_cents: str, quantity: str) -> str:\n \"\"\"Calculate line total cents from a unit price and quantity.\"\"\"\n return f\"{price_cents} * {quantity}\"\n", + "depends_on": { + "macros": [] + }, + "description": "", + "meta": { + "sqlbuild_visibility": "global", + "sqlbuild_scope_path": null + }, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "arguments": [], + "supported_languages": null + }, + "macro.waffle_shop.cents_to_dollars": { + "name": "cents_to_dollars", + "resource_type": "macro", + "package_name": "waffle_shop", + "path": "models/marts/_sqlbuild/_macros/currency.py", + "original_file_path": "models/marts/_sqlbuild/_macros/currency.py", + "unique_id": "macro.waffle_shop.cents_to_dollars", + "macro_sql": "\"\"\"Currency formatting macros.\"\"\"\n\n\ndef cents_to_dollars(column: str) -> str:\n \"\"\"Convert a cents integer column to a dollars decimal with two decimal places.\"\"\"\n return f\"ROUND(({column}) / 100.0, 2)\"\n", + "depends_on": { + "macros": [] + }, + "description": "", + "meta": { + "sqlbuild_visibility": "local", + "sqlbuild_scope_path": "models/marts" + }, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "arguments": [], + "supported_languages": null + }, + "macro.waffle_shop.timestamp_trunc": { + "name": "timestamp_trunc", + "resource_type": "macro", + "package_name": "waffle_shop", + "path": "models/marts/_sqlbuild/_macros/datetime.py", + "original_file_path": "models/marts/_sqlbuild/_macros/datetime.py", + "unique_id": "macro.waffle_shop.timestamp_trunc", + "macro_sql": "\"\"\"Datetime portability macros.\"\"\"\n\n\ndef timestamp_trunc(ctx, grain: str, expr: str) -> str:\n if ctx.adapter_name == \"bigquery\":\n return f\"TIMESTAMP_TRUNC({expr}, {grain.upper()})\"\n return f\"DATE_TRUNC('{grain}', {expr})\"\n", + "depends_on": { + "macros": [] + }, + "description": "", + "meta": { + "sqlbuild_visibility": "local", + "sqlbuild_scope_path": "models/marts" + }, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "arguments": [], + "supported_languages": null + } + }, + "exposures": {}, + "metrics": {}, + "groups": {}, + "selectors": {}, + "disabled": {}, + "parent_map": { + "model.waffle_shop.customer_status_snapshot": [ + "model.waffle_shop.fact_orders" + ], + "model.waffle_shop.order_status_index": [ + "model.waffle_shop.fact_orders" + ], + "model.waffle_shop.daily_order_partitioned": [ + "model.waffle_shop.stg_orders" + ], + "model.waffle_shop.daily_order_rollup": [ + "model.waffle_shop.hourly_order_activity" + ], + "model.waffle_shop.daily_revenue": [ + "model.waffle_shop.stg_orders", + "model.waffle_shop.stg_payments" + ], + "model.waffle_shop.dim_customers": [ + "model.waffle_shop.stg_customers", + "model.waffle_shop.stg_orders", + "model.waffle_shop.stg_payments" + ], + "model.waffle_shop.fact_orders": [ + "model.waffle_shop.udf__is_completed_order", + "model.waffle_shop.stg_orders", + "seed.waffle_shop.waffle_types", + "model.waffle_shop.stg_payments" + ], + "model.waffle_shop.hourly_activity_with_daily_context": [ + "model.waffle_shop.hourly_order_activity", + "model.waffle_shop.daily_order_rollup" + ], + "model.waffle_shop.hourly_order_activity": [ + "model.waffle_shop.fact_orders" + ], + "model.waffle_shop.stg_customers": [ + "source.waffle_shop.raw__customers" + ], + "model.waffle_shop.stg_orders": [ + "source.waffle_shop.raw__orders" + ], + "model.waffle_shop.stg_payments": [ + "source.waffle_shop.raw__payments" + ], + "source.waffle_shop.raw__customers": [], + "source.waffle_shop.raw__orders": [], + "source.waffle_shop.raw__payments": [], + "seed.waffle_shop.waffle_types": [], + "model.waffle_shop.table_fn__customer_orders": [ + "model.waffle_shop.fact_orders" + ], + "model.waffle_shop.udf__is_completed_order": [] + }, + "group_map": {}, + "docs": {}, + "saved_queries": {}, + "semantic_models": {}, + "unit_tests": {} + } +} diff --git a/tests/goldens/compiler/failure/analysis-accepted-values-type.json b/tests/goldens/compiler/failure/analysis-accepted-values-type.json new file mode 100644 index 0000000000..15c849fec9 --- /dev/null +++ b/tests/goldens/compiler/failure/analysis-accepted-values-type.json @@ -0,0 +1,308 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "B301", + "message": "accepted_values: IN list contains an invalid coercible literal", + "resource_type": "model", + "resource_name": "customer_totals", + "path": "models/marts/customer_totals.sql", + "line": 4, + "column": 5, + "location": { + "path": "models/marts/customer_totals.sql", + "line": 4, + "column": 5 + }, + "help": "align this declared metadata or audit value with the column or argument type" + }, + { + "phase": "compile", + "severity": "error", + "code": "B218", + "message": "audit 'accepted_values' on model 'customer_totals' column 'customer_id': IN list contains an invalid coercible literal", + "resource_type": "audit", + "resource_name": "accepted_values", + "path": "audits/generic/accepted_values.sql", + "line": 6, + "column": 30, + "location": { + "path": "audits/generic/accepted_values.sql", + "line": 6, + "column": 30, + "end_line": 7, + "end_column": 8 + }, + "help": "choose a supported source-to-target cast for this dialect" + } + ] + } + ], + "compiled": { + "audits/generic/customer_totals/accepted_values__customer_id.sql": [ + "SELECT customer_id", + "FROM __ref(\"customer_totals\")", + "WHERE customer_id IS NOT NULL", + " AND customer_id NOT IN ('DATE', '2026-01-01')", + "" + ], + "models/marts/customer_totals.sql": [ + "SELECT customer_id, SUM(amount) AS total_amount", + "FROM __ref(\"stg_orders\")", + "GROUP BY customer_id", + "" + ], + "models/staging/stg_orders.sql": [ + "SELECT order_id, customer_id, amount, status", + "FROM __source(\"raw_orders\")", + "" + ] + }, + "manifest": { + "nodes": { + "model.failure_corpus.customer_totals": { + "database": null, + "schema": "main", + "name": "customer_totals", + "resource_type": "model", + "package_name": "failure_corpus", + "path": "models/marts/customer_totals.sql", + "original_file_path": "models/marts/customer_totals.sql", + "unique_id": "model.failure_corpus.customer_totals", + "fqn": [ + "failure_corpus", + "models", + "marts", + "customer_totals" + ], + "alias": "customer_totals", + "config": { + "enabled": true, + "alias": null, + "schema": null, + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Order totals per customer", + "columns": { + "customer_id": { + "name": "customer_id", + "description": "", + "meta": {}, + "data_type": null, + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.customer_totals", + "depends_on": { + "macros": [], + "nodes": [ + "model.failure_corpus.stg_orders" + ] + } + }, + "model.failure_corpus.stg_orders": { + "database": null, + "schema": "main", + "name": "stg_orders", + "resource_type": "model", + "package_name": "failure_corpus", + "path": "models/staging/stg_orders.sql", + "original_file_path": "models/staging/stg_orders.sql", + "unique_id": "model.failure_corpus.stg_orders", + "fqn": [ + "failure_corpus", + "models", + "staging", + "stg_orders" + ], + "alias": "stg_orders", + "config": { + "enabled": true, + "alias": null, + "schema": null, + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Staged orders", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.stg_orders", + "depends_on": { + "macros": [], + "nodes": [ + "source.failure_corpus.raw_orders" + ] + } + } + }, + "sources": { + "source.failure_corpus.raw_orders": { + "database": null, + "schema": "", + "name": "raw_orders", + "resource_type": "source", + "package_name": "failure_corpus", + "path": "sources/raw.yml", + "original_file_path": "sources/raw.yml", + "unique_id": "source.failure_corpus.raw_orders", + "fqn": [ + "failure_corpus", + "raw_orders" + ], + "source_name": "raw_orders", + "source_description": "", + "loader": "", + "identifier": "raw_orders", + "quoting": {}, + "loaded_at_field": null, + "freshness": null, + "external": null, + "description": "Orders feed.", + "columns": { + "order_id": { + "name": "order_id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "customer_id": { + "name": "customer_id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "amount": { + "name": "amount", + "description": "", + "meta": {}, + "data_type": "DOUBLE", + "constraints": [], + "quote": null, + "tags": [] + }, + "status": { + "name": "status", + "description": "", + "meta": {}, + "data_type": "VARCHAR", + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": {}, + "source_meta": {}, + "tags": [], + "config": {}, + "patch_path": null, + "unrendered_config": {}, + "relation_name": null + } + }, + "macros": {}, + "exposures": {}, + "metrics": {}, + "groups": {}, + "selectors": {}, + "disabled": {}, + "parent_map": { + "model.failure_corpus.customer_totals": [ + "model.failure_corpus.stg_orders" + ], + "model.failure_corpus.stg_orders": [ + "source.failure_corpus.raw_orders" + ], + "source.failure_corpus.raw_orders": [] + }, + "group_map": {}, + "docs": {}, + "saved_queries": {}, + "semantic_models": {}, + "unit_tests": {} + } +} diff --git a/tests/goldens/compiler/failure/analysis-aggregate-in-where.json b/tests/goldens/compiler/failure/analysis-aggregate-in-where.json new file mode 100644 index 0000000000..773acca307 --- /dev/null +++ b/tests/goldens/compiler/failure/analysis-aggregate-in-where.json @@ -0,0 +1,275 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "B231", + "message": "Aggregate function is not allowed in this clause or inside another aggregate", + "resource_type": "model", + "resource_name": "customer_totals", + "path": "models/marts/customer_totals.sql", + "line": 7, + "column": 11, + "location": { + "path": "models/marts/customer_totals.sql", + "line": 7, + "column": 11, + "end_line": 7, + "end_column": 17 + }, + "help": "move this aggregate to SELECT or HAVING, or compute it in an input CTE" + } + ] + } + ], + "compiled": { + "models/marts/customer_totals.sql": [ + "SELECT customer_id", + "FROM __ref(\"stg_orders\")", + "WHERE SUM(amount) > 1", + "GROUP BY customer_id", + "" + ], + "models/staging/stg_orders.sql": [ + "SELECT order_id, customer_id, amount, status", + "FROM __source(\"raw_orders\")", + "" + ] + }, + "manifest": { + "nodes": { + "model.failure_corpus.customer_totals": { + "database": null, + "schema": "main", + "name": "customer_totals", + "resource_type": "model", + "package_name": "failure_corpus", + "path": "models/marts/customer_totals.sql", + "original_file_path": "models/marts/customer_totals.sql", + "unique_id": "model.failure_corpus.customer_totals", + "fqn": [ + "failure_corpus", + "models", + "marts", + "customer_totals" + ], + "alias": "customer_totals", + "config": { + "enabled": true, + "alias": null, + "schema": null, + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Order totals per customer", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.customer_totals", + "depends_on": { + "macros": [], + "nodes": [ + "model.failure_corpus.stg_orders" + ] + } + }, + "model.failure_corpus.stg_orders": { + "database": null, + "schema": "main", + "name": "stg_orders", + "resource_type": "model", + "package_name": "failure_corpus", + "path": "models/staging/stg_orders.sql", + "original_file_path": "models/staging/stg_orders.sql", + "unique_id": "model.failure_corpus.stg_orders", + "fqn": [ + "failure_corpus", + "models", + "staging", + "stg_orders" + ], + "alias": "stg_orders", + "config": { + "enabled": true, + "alias": null, + "schema": null, + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Staged orders", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.stg_orders", + "depends_on": { + "macros": [], + "nodes": [ + "source.failure_corpus.raw_orders" + ] + } + } + }, + "sources": { + "source.failure_corpus.raw_orders": { + "database": null, + "schema": "", + "name": "raw_orders", + "resource_type": "source", + "package_name": "failure_corpus", + "path": "sources/raw.yml", + "original_file_path": "sources/raw.yml", + "unique_id": "source.failure_corpus.raw_orders", + "fqn": [ + "failure_corpus", + "raw_orders" + ], + "source_name": "raw_orders", + "source_description": "", + "loader": "", + "identifier": "raw_orders", + "quoting": {}, + "loaded_at_field": null, + "freshness": null, + "external": null, + "description": "Orders feed.", + "columns": { + "order_id": { + "name": "order_id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "customer_id": { + "name": "customer_id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "amount": { + "name": "amount", + "description": "", + "meta": {}, + "data_type": "DOUBLE", + "constraints": [], + "quote": null, + "tags": [] + }, + "status": { + "name": "status", + "description": "", + "meta": {}, + "data_type": "VARCHAR", + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": {}, + "source_meta": {}, + "tags": [], + "config": {}, + "patch_path": null, + "unrendered_config": {}, + "relation_name": null + } + }, + "macros": {}, + "exposures": {}, + "metrics": {}, + "groups": {}, + "selectors": {}, + "disabled": {}, + "parent_map": { + "model.failure_corpus.customer_totals": [ + "model.failure_corpus.stg_orders" + ], + "model.failure_corpus.stg_orders": [ + "source.failure_corpus.raw_orders" + ], + "source.failure_corpus.raw_orders": [] + }, + "group_map": {}, + "docs": {}, + "saved_queries": {}, + "semantic_models": {}, + "unit_tests": {} + } +} diff --git a/tests/goldens/compiler/failure/analysis-alias-column-list-width.json b/tests/goldens/compiler/failure/analysis-alias-column-list-width.json new file mode 100644 index 0000000000..119220384a --- /dev/null +++ b/tests/goldens/compiler/failure/analysis-alias-column-list-width.json @@ -0,0 +1,273 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "B005", + "message": "Derived-table column alias count exceeds its output column count", + "resource_type": "model", + "resource_name": "customer_totals", + "path": "models/marts/customer_totals.sql", + "line": 6, + "column": 73, + "location": { + "path": "models/marts/customer_totals.sql", + "line": 6, + "column": 73, + "end_line": 6, + "end_column": 74 + }, + "help": "match the relation alias list to its available output columns" + } + ] + } + ], + "compiled": { + "models/marts/customer_totals.sql": [ + "SELECT o.a AS first_column", + "FROM (SELECT order_id, customer_id FROM __ref(\"stg_orders\")) AS o(a, b, c)", + "" + ], + "models/staging/stg_orders.sql": [ + "SELECT order_id, customer_id, amount, status", + "FROM __source(\"raw_orders\")", + "" + ] + }, + "manifest": { + "nodes": { + "model.failure_corpus.customer_totals": { + "database": null, + "schema": "main", + "name": "customer_totals", + "resource_type": "model", + "package_name": "failure_corpus", + "path": "models/marts/customer_totals.sql", + "original_file_path": "models/marts/customer_totals.sql", + "unique_id": "model.failure_corpus.customer_totals", + "fqn": [ + "failure_corpus", + "models", + "marts", + "customer_totals" + ], + "alias": "customer_totals", + "config": { + "enabled": true, + "alias": null, + "schema": null, + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Order totals per customer", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.customer_totals", + "depends_on": { + "macros": [], + "nodes": [ + "model.failure_corpus.stg_orders" + ] + } + }, + "model.failure_corpus.stg_orders": { + "database": null, + "schema": "main", + "name": "stg_orders", + "resource_type": "model", + "package_name": "failure_corpus", + "path": "models/staging/stg_orders.sql", + "original_file_path": "models/staging/stg_orders.sql", + "unique_id": "model.failure_corpus.stg_orders", + "fqn": [ + "failure_corpus", + "models", + "staging", + "stg_orders" + ], + "alias": "stg_orders", + "config": { + "enabled": true, + "alias": null, + "schema": null, + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Staged orders", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.stg_orders", + "depends_on": { + "macros": [], + "nodes": [ + "source.failure_corpus.raw_orders" + ] + } + } + }, + "sources": { + "source.failure_corpus.raw_orders": { + "database": null, + "schema": "", + "name": "raw_orders", + "resource_type": "source", + "package_name": "failure_corpus", + "path": "sources/raw.yml", + "original_file_path": "sources/raw.yml", + "unique_id": "source.failure_corpus.raw_orders", + "fqn": [ + "failure_corpus", + "raw_orders" + ], + "source_name": "raw_orders", + "source_description": "", + "loader": "", + "identifier": "raw_orders", + "quoting": {}, + "loaded_at_field": null, + "freshness": null, + "external": null, + "description": "Orders feed.", + "columns": { + "order_id": { + "name": "order_id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "customer_id": { + "name": "customer_id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "amount": { + "name": "amount", + "description": "", + "meta": {}, + "data_type": "DOUBLE", + "constraints": [], + "quote": null, + "tags": [] + }, + "status": { + "name": "status", + "description": "", + "meta": {}, + "data_type": "VARCHAR", + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": {}, + "source_meta": {}, + "tags": [], + "config": {}, + "patch_path": null, + "unrendered_config": {}, + "relation_name": null + } + }, + "macros": {}, + "exposures": {}, + "metrics": {}, + "groups": {}, + "selectors": {}, + "disabled": {}, + "parent_map": { + "model.failure_corpus.customer_totals": [ + "model.failure_corpus.stg_orders" + ], + "model.failure_corpus.stg_orders": [ + "source.failure_corpus.raw_orders" + ], + "source.failure_corpus.raw_orders": [] + }, + "group_map": {}, + "docs": {}, + "saved_queries": {}, + "semantic_models": {}, + "unit_tests": {} + } +} diff --git a/tests/goldens/compiler/failure/analysis-ambiguous-column.json b/tests/goldens/compiler/failure/analysis-ambiguous-column.json new file mode 100644 index 0000000000..56297788ab --- /dev/null +++ b/tests/goldens/compiler/failure/analysis-ambiguous-column.json @@ -0,0 +1,274 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "B003", + "message": "Ambiguous unqualified column 'order_id' found in 2 referenced tables", + "resource_type": "model", + "resource_name": "customer_totals", + "path": "models/marts/customer_totals.sql", + "line": 5, + "column": 8, + "location": { + "path": "models/marts/customer_totals.sql", + "line": 5, + "column": 8, + "end_line": 5, + "end_column": 16 + }, + "help": "qualify this column with the intended input alias" + } + ] + } + ], + "compiled": { + "models/marts/customer_totals.sql": [ + "SELECT order_id", + "FROM __ref(\"stg_orders\") a", + "JOIN __ref(\"stg_orders\") b ON a.customer_id = b.customer_id", + "" + ], + "models/staging/stg_orders.sql": [ + "SELECT order_id, customer_id, amount, status", + "FROM __source(\"raw_orders\")", + "" + ] + }, + "manifest": { + "nodes": { + "model.failure_corpus.customer_totals": { + "database": null, + "schema": "main", + "name": "customer_totals", + "resource_type": "model", + "package_name": "failure_corpus", + "path": "models/marts/customer_totals.sql", + "original_file_path": "models/marts/customer_totals.sql", + "unique_id": "model.failure_corpus.customer_totals", + "fqn": [ + "failure_corpus", + "models", + "marts", + "customer_totals" + ], + "alias": "customer_totals", + "config": { + "enabled": true, + "alias": null, + "schema": null, + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Order totals per customer", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.customer_totals", + "depends_on": { + "macros": [], + "nodes": [ + "model.failure_corpus.stg_orders" + ] + } + }, + "model.failure_corpus.stg_orders": { + "database": null, + "schema": "main", + "name": "stg_orders", + "resource_type": "model", + "package_name": "failure_corpus", + "path": "models/staging/stg_orders.sql", + "original_file_path": "models/staging/stg_orders.sql", + "unique_id": "model.failure_corpus.stg_orders", + "fqn": [ + "failure_corpus", + "models", + "staging", + "stg_orders" + ], + "alias": "stg_orders", + "config": { + "enabled": true, + "alias": null, + "schema": null, + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Staged orders", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.stg_orders", + "depends_on": { + "macros": [], + "nodes": [ + "source.failure_corpus.raw_orders" + ] + } + } + }, + "sources": { + "source.failure_corpus.raw_orders": { + "database": null, + "schema": "", + "name": "raw_orders", + "resource_type": "source", + "package_name": "failure_corpus", + "path": "sources/raw.yml", + "original_file_path": "sources/raw.yml", + "unique_id": "source.failure_corpus.raw_orders", + "fqn": [ + "failure_corpus", + "raw_orders" + ], + "source_name": "raw_orders", + "source_description": "", + "loader": "", + "identifier": "raw_orders", + "quoting": {}, + "loaded_at_field": null, + "freshness": null, + "external": null, + "description": "Orders feed.", + "columns": { + "order_id": { + "name": "order_id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "customer_id": { + "name": "customer_id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "amount": { + "name": "amount", + "description": "", + "meta": {}, + "data_type": "DOUBLE", + "constraints": [], + "quote": null, + "tags": [] + }, + "status": { + "name": "status", + "description": "", + "meta": {}, + "data_type": "VARCHAR", + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": {}, + "source_meta": {}, + "tags": [], + "config": {}, + "patch_path": null, + "unrendered_config": {}, + "relation_name": null + } + }, + "macros": {}, + "exposures": {}, + "metrics": {}, + "groups": {}, + "selectors": {}, + "disabled": {}, + "parent_map": { + "model.failure_corpus.customer_totals": [ + "model.failure_corpus.stg_orders" + ], + "model.failure_corpus.stg_orders": [ + "source.failure_corpus.raw_orders" + ], + "source.failure_corpus.raw_orders": [] + }, + "group_map": {}, + "docs": {}, + "saved_queries": {}, + "semantic_models": {}, + "unit_tests": {} + } +} diff --git a/tests/goldens/compiler/failure/analysis-arithmetic-operands.json b/tests/goldens/compiler/failure/analysis-arithmetic-operands.json new file mode 100644 index 0000000000..eb60410254 --- /dev/null +++ b/tests/goldens/compiler/failure/analysis-arithmetic-operands.json @@ -0,0 +1,276 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "B212", + "message": "Arithmetic operation expects NUMERIC-compatible operands, found STRING and INTEGER", + "resource_type": "model", + "resource_name": "customer_totals", + "path": "models/marts/customer_totals.sql", + "line": 5, + "column": 21, + "location": { + "path": "models/marts/customer_totals.sql", + "line": 5, + "column": 21, + "end_line": 5, + "end_column": 31 + }, + "help": "use operands supported by this arithmetic operator, or convert them explicitly", + "notes": [ + "status is TEXT, 1 is INTEGER" + ] + } + ] + } + ], + "compiled": { + "models/marts/customer_totals.sql": [ + "SELECT customer_id, status + 1 AS total_amount", + "FROM __ref(\"stg_orders\")", + "" + ], + "models/staging/stg_orders.sql": [ + "SELECT order_id, customer_id, amount, status", + "FROM __source(\"raw_orders\")", + "" + ] + }, + "manifest": { + "nodes": { + "model.failure_corpus.customer_totals": { + "database": null, + "schema": "main", + "name": "customer_totals", + "resource_type": "model", + "package_name": "failure_corpus", + "path": "models/marts/customer_totals.sql", + "original_file_path": "models/marts/customer_totals.sql", + "unique_id": "model.failure_corpus.customer_totals", + "fqn": [ + "failure_corpus", + "models", + "marts", + "customer_totals" + ], + "alias": "customer_totals", + "config": { + "enabled": true, + "alias": null, + "schema": null, + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Order totals per customer", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.customer_totals", + "depends_on": { + "macros": [], + "nodes": [ + "model.failure_corpus.stg_orders" + ] + } + }, + "model.failure_corpus.stg_orders": { + "database": null, + "schema": "main", + "name": "stg_orders", + "resource_type": "model", + "package_name": "failure_corpus", + "path": "models/staging/stg_orders.sql", + "original_file_path": "models/staging/stg_orders.sql", + "unique_id": "model.failure_corpus.stg_orders", + "fqn": [ + "failure_corpus", + "models", + "staging", + "stg_orders" + ], + "alias": "stg_orders", + "config": { + "enabled": true, + "alias": null, + "schema": null, + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Staged orders", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.stg_orders", + "depends_on": { + "macros": [], + "nodes": [ + "source.failure_corpus.raw_orders" + ] + } + } + }, + "sources": { + "source.failure_corpus.raw_orders": { + "database": null, + "schema": "", + "name": "raw_orders", + "resource_type": "source", + "package_name": "failure_corpus", + "path": "sources/raw.yml", + "original_file_path": "sources/raw.yml", + "unique_id": "source.failure_corpus.raw_orders", + "fqn": [ + "failure_corpus", + "raw_orders" + ], + "source_name": "raw_orders", + "source_description": "", + "loader": "", + "identifier": "raw_orders", + "quoting": {}, + "loaded_at_field": null, + "freshness": null, + "external": null, + "description": "Orders feed.", + "columns": { + "order_id": { + "name": "order_id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "customer_id": { + "name": "customer_id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "amount": { + "name": "amount", + "description": "", + "meta": {}, + "data_type": "DOUBLE", + "constraints": [], + "quote": null, + "tags": [] + }, + "status": { + "name": "status", + "description": "", + "meta": {}, + "data_type": "VARCHAR", + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": {}, + "source_meta": {}, + "tags": [], + "config": {}, + "patch_path": null, + "unrendered_config": {}, + "relation_name": null + } + }, + "macros": {}, + "exposures": {}, + "metrics": {}, + "groups": {}, + "selectors": {}, + "disabled": {}, + "parent_map": { + "model.failure_corpus.customer_totals": [ + "model.failure_corpus.stg_orders" + ], + "model.failure_corpus.stg_orders": [ + "source.failure_corpus.raw_orders" + ], + "source.failure_corpus.raw_orders": [] + }, + "group_map": {}, + "docs": {}, + "saved_queries": {}, + "semantic_models": {}, + "unit_tests": {} + } +} diff --git a/tests/goldens/compiler/failure/analysis-cursor-type-family.json b/tests/goldens/compiler/failure/analysis-cursor-type-family.json new file mode 100644 index 0000000000..207baf4a14 --- /dev/null +++ b/tests/goldens/compiler/failure/analysis-cursor-type-family.json @@ -0,0 +1,274 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "B301", + "message": "cursor_type TIMESTAMP does not match column 'customer_id' type INT", + "resource_type": "model", + "resource_name": "customer_totals", + "path": "models/marts/customer_totals.sql", + "line": 5, + "column": 15, + "location": { + "path": "models/marts/customer_totals.sql", + "line": 5, + "column": 15 + }, + "help": "align this declared metadata or audit value with the column or argument type" + } + ] + } + ], + "compiled": { + "models/marts/customer_totals.sql": [ + "SELECT customer_id, SUM(amount) AS total_amount", + "FROM __ref(\"stg_orders\")", + "GROUP BY customer_id", + "" + ], + "models/staging/stg_orders.sql": [ + "SELECT order_id, customer_id, amount, status", + "FROM __source(\"raw_orders\")", + "" + ] + }, + "manifest": { + "nodes": { + "model.failure_corpus.customer_totals": { + "database": null, + "schema": "main", + "name": "customer_totals", + "resource_type": "model", + "package_name": "failure_corpus", + "path": "models/marts/customer_totals.sql", + "original_file_path": "models/marts/customer_totals.sql", + "unique_id": "model.failure_corpus.customer_totals", + "fqn": [ + "failure_corpus", + "models", + "marts", + "customer_totals" + ], + "alias": "customer_totals", + "config": { + "enabled": true, + "alias": null, + "schema": null, + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "incremental", + "incremental_strategy": "delete_insert", + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": [ + "customer_id" + ], + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Order totals per customer", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.customer_totals", + "depends_on": { + "macros": [], + "nodes": [ + "model.failure_corpus.stg_orders" + ] + } + }, + "model.failure_corpus.stg_orders": { + "database": null, + "schema": "main", + "name": "stg_orders", + "resource_type": "model", + "package_name": "failure_corpus", + "path": "models/staging/stg_orders.sql", + "original_file_path": "models/staging/stg_orders.sql", + "unique_id": "model.failure_corpus.stg_orders", + "fqn": [ + "failure_corpus", + "models", + "staging", + "stg_orders" + ], + "alias": "stg_orders", + "config": { + "enabled": true, + "alias": null, + "schema": null, + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Staged orders", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.stg_orders", + "depends_on": { + "macros": [], + "nodes": [ + "source.failure_corpus.raw_orders" + ] + } + } + }, + "sources": { + "source.failure_corpus.raw_orders": { + "database": null, + "schema": "", + "name": "raw_orders", + "resource_type": "source", + "package_name": "failure_corpus", + "path": "sources/raw.yml", + "original_file_path": "sources/raw.yml", + "unique_id": "source.failure_corpus.raw_orders", + "fqn": [ + "failure_corpus", + "raw_orders" + ], + "source_name": "raw_orders", + "source_description": "", + "loader": "", + "identifier": "raw_orders", + "quoting": {}, + "loaded_at_field": null, + "freshness": null, + "external": null, + "description": "Orders feed.", + "columns": { + "order_id": { + "name": "order_id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "customer_id": { + "name": "customer_id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "amount": { + "name": "amount", + "description": "", + "meta": {}, + "data_type": "DOUBLE", + "constraints": [], + "quote": null, + "tags": [] + }, + "status": { + "name": "status", + "description": "", + "meta": {}, + "data_type": "VARCHAR", + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": {}, + "source_meta": {}, + "tags": [], + "config": {}, + "patch_path": null, + "unrendered_config": {}, + "relation_name": null + } + }, + "macros": {}, + "exposures": {}, + "metrics": {}, + "groups": {}, + "selectors": {}, + "disabled": {}, + "parent_map": { + "model.failure_corpus.customer_totals": [ + "model.failure_corpus.stg_orders" + ], + "model.failure_corpus.stg_orders": [ + "source.failure_corpus.raw_orders" + ], + "source.failure_corpus.raw_orders": [] + }, + "group_map": {}, + "docs": {}, + "saved_queries": {}, + "semantic_models": {}, + "unit_tests": {} + } +} diff --git a/tests/goldens/compiler/failure/analysis-duplicate-cte-name.json b/tests/goldens/compiler/failure/analysis-duplicate-cte-name.json new file mode 100644 index 0000000000..ac30091e30 --- /dev/null +++ b/tests/goldens/compiler/failure/analysis-duplicate-cte-name.json @@ -0,0 +1,274 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "B233", + "message": "Duplicate CTE name 'totals'", + "resource_type": "model", + "resource_name": "customer_totals", + "path": "models/marts/customer_totals.sql", + "line": 7, + "column": 8, + "location": { + "path": "models/marts/customer_totals.sql", + "line": 7, + "column": 8, + "end_line": 7, + "end_column": 19 + }, + "help": "use distinct names for relations or CTEs within this scope" + } + ] + } + ], + "compiled": { + "models/marts/customer_totals.sql": [ + "WITH totals AS (SELECT customer_id FROM __ref(\"stg_orders\")),", + "totals AS (SELECT customer_id FROM __ref(\"stg_orders\"))", + "SELECT customer_id FROM totals", + "" + ], + "models/staging/stg_orders.sql": [ + "SELECT order_id, customer_id, amount, status", + "FROM __source(\"raw_orders\")", + "" + ] + }, + "manifest": { + "nodes": { + "model.failure_corpus.customer_totals": { + "database": null, + "schema": "main", + "name": "customer_totals", + "resource_type": "model", + "package_name": "failure_corpus", + "path": "models/marts/customer_totals.sql", + "original_file_path": "models/marts/customer_totals.sql", + "unique_id": "model.failure_corpus.customer_totals", + "fqn": [ + "failure_corpus", + "models", + "marts", + "customer_totals" + ], + "alias": "customer_totals", + "config": { + "enabled": true, + "alias": null, + "schema": null, + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Order totals per customer", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.customer_totals", + "depends_on": { + "macros": [], + "nodes": [ + "model.failure_corpus.stg_orders" + ] + } + }, + "model.failure_corpus.stg_orders": { + "database": null, + "schema": "main", + "name": "stg_orders", + "resource_type": "model", + "package_name": "failure_corpus", + "path": "models/staging/stg_orders.sql", + "original_file_path": "models/staging/stg_orders.sql", + "unique_id": "model.failure_corpus.stg_orders", + "fqn": [ + "failure_corpus", + "models", + "staging", + "stg_orders" + ], + "alias": "stg_orders", + "config": { + "enabled": true, + "alias": null, + "schema": null, + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Staged orders", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.stg_orders", + "depends_on": { + "macros": [], + "nodes": [ + "source.failure_corpus.raw_orders" + ] + } + } + }, + "sources": { + "source.failure_corpus.raw_orders": { + "database": null, + "schema": "", + "name": "raw_orders", + "resource_type": "source", + "package_name": "failure_corpus", + "path": "sources/raw.yml", + "original_file_path": "sources/raw.yml", + "unique_id": "source.failure_corpus.raw_orders", + "fqn": [ + "failure_corpus", + "raw_orders" + ], + "source_name": "raw_orders", + "source_description": "", + "loader": "", + "identifier": "raw_orders", + "quoting": {}, + "loaded_at_field": null, + "freshness": null, + "external": null, + "description": "Orders feed.", + "columns": { + "order_id": { + "name": "order_id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "customer_id": { + "name": "customer_id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "amount": { + "name": "amount", + "description": "", + "meta": {}, + "data_type": "DOUBLE", + "constraints": [], + "quote": null, + "tags": [] + }, + "status": { + "name": "status", + "description": "", + "meta": {}, + "data_type": "VARCHAR", + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": {}, + "source_meta": {}, + "tags": [], + "config": {}, + "patch_path": null, + "unrendered_config": {}, + "relation_name": null + } + }, + "macros": {}, + "exposures": {}, + "metrics": {}, + "groups": {}, + "selectors": {}, + "disabled": {}, + "parent_map": { + "model.failure_corpus.customer_totals": [ + "model.failure_corpus.stg_orders" + ], + "model.failure_corpus.stg_orders": [ + "source.failure_corpus.raw_orders" + ], + "source.failure_corpus.raw_orders": [] + }, + "group_map": {}, + "docs": {}, + "saved_queries": {}, + "semantic_models": {}, + "unit_tests": {} + } +} diff --git a/tests/goldens/compiler/failure/analysis-expression-audit-unknown-column.json b/tests/goldens/compiler/failure/analysis-expression-audit-unknown-column.json new file mode 100644 index 0000000000..c34e4d80c9 --- /dev/null +++ b/tests/goldens/compiler/failure/analysis-expression-audit-unknown-column.json @@ -0,0 +1,295 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "B002", + "message": "Unknown column 'discount' in output", + "resource_type": "model", + "resource_name": "customer_totals", + "path": "models/marts/customer_totals.sql", + "line": 3, + "column": 43, + "location": { + "path": "models/marts/customer_totals.sql", + "line": 3, + "column": 43 + } + }, + { + "phase": "compile", + "severity": "error", + "code": "B002", + "message": "audit 'expression_is_true' on model 'customer_totals': Unknown column 'discount' in table 'customer_totals' (context: WHERE)", + "resource_type": "audit", + "resource_name": "expression_is_true", + "path": "models/marts/_sqlbuild/_audits/generic/expression_is_true.sql", + "line": 5, + "column": 21, + "location": { + "path": "models/marts/_sqlbuild/_audits/generic/expression_is_true.sql", + "line": 5, + "column": 21, + "end_line": 6, + "end_column": 5 + } + } + ] + } + ], + "compiled": { + "audits/generic/customer_totals/expression_is_true.sql": [ + "SELECT *", + "FROM __ref(\"customer_totals\")", + "WHERE NOT (discount >= 0)", + "" + ], + "models/marts/customer_totals.sql": [ + "SELECT customer_id, SUM(amount) AS total_amount", + "FROM __ref(\"stg_orders\")", + "GROUP BY customer_id", + "" + ], + "models/staging/stg_orders.sql": [ + "SELECT order_id, customer_id, amount, status", + "FROM __source(\"raw_orders\")", + "" + ] + }, + "manifest": { + "nodes": { + "model.failure_corpus.customer_totals": { + "database": null, + "schema": "main", + "name": "customer_totals", + "resource_type": "model", + "package_name": "failure_corpus", + "path": "models/marts/customer_totals.sql", + "original_file_path": "models/marts/customer_totals.sql", + "unique_id": "model.failure_corpus.customer_totals", + "fqn": [ + "failure_corpus", + "models", + "marts", + "customer_totals" + ], + "alias": "customer_totals", + "config": { + "enabled": true, + "alias": null, + "schema": null, + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Order totals per customer", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.customer_totals", + "depends_on": { + "macros": [], + "nodes": [ + "model.failure_corpus.stg_orders" + ] + } + }, + "model.failure_corpus.stg_orders": { + "database": null, + "schema": "main", + "name": "stg_orders", + "resource_type": "model", + "package_name": "failure_corpus", + "path": "models/staging/stg_orders.sql", + "original_file_path": "models/staging/stg_orders.sql", + "unique_id": "model.failure_corpus.stg_orders", + "fqn": [ + "failure_corpus", + "models", + "staging", + "stg_orders" + ], + "alias": "stg_orders", + "config": { + "enabled": true, + "alias": null, + "schema": null, + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Staged orders", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.stg_orders", + "depends_on": { + "macros": [], + "nodes": [ + "source.failure_corpus.raw_orders" + ] + } + } + }, + "sources": { + "source.failure_corpus.raw_orders": { + "database": null, + "schema": "", + "name": "raw_orders", + "resource_type": "source", + "package_name": "failure_corpus", + "path": "sources/raw.yml", + "original_file_path": "sources/raw.yml", + "unique_id": "source.failure_corpus.raw_orders", + "fqn": [ + "failure_corpus", + "raw_orders" + ], + "source_name": "raw_orders", + "source_description": "", + "loader": "", + "identifier": "raw_orders", + "quoting": {}, + "loaded_at_field": null, + "freshness": null, + "external": null, + "description": "Orders feed.", + "columns": { + "order_id": { + "name": "order_id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "customer_id": { + "name": "customer_id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "amount": { + "name": "amount", + "description": "", + "meta": {}, + "data_type": "DOUBLE", + "constraints": [], + "quote": null, + "tags": [] + }, + "status": { + "name": "status", + "description": "", + "meta": {}, + "data_type": "VARCHAR", + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": {}, + "source_meta": {}, + "tags": [], + "config": {}, + "patch_path": null, + "unrendered_config": {}, + "relation_name": null + } + }, + "macros": {}, + "exposures": {}, + "metrics": {}, + "groups": {}, + "selectors": {}, + "disabled": {}, + "parent_map": { + "model.failure_corpus.customer_totals": [ + "model.failure_corpus.stg_orders" + ], + "model.failure_corpus.stg_orders": [ + "source.failure_corpus.raw_orders" + ], + "source.failure_corpus.raw_orders": [] + }, + "group_map": {}, + "docs": {}, + "saved_queries": {}, + "semantic_models": {}, + "unit_tests": {} + } +} diff --git a/tests/goldens/compiler/failure/analysis-function-argument-count.json b/tests/goldens/compiler/failure/analysis-function-argument-count.json new file mode 100644 index 0000000000..f4a1354727 --- /dev/null +++ b/tests/goldens/compiler/failure/analysis-function-argument-count.json @@ -0,0 +1,273 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "B102", + "message": "Invalid arity for function 'SUBSTRING': got 1, expected 2, 3", + "resource_type": "model", + "resource_name": "customer_totals", + "path": "models/marts/customer_totals.sql", + "line": 5, + "column": 31, + "location": { + "path": "models/marts/customer_totals.sql", + "line": 5, + "column": 31, + "end_line": 5, + "end_column": 37 + }, + "help": "pass the number of arguments required by this function's signature" + } + ] + } + ], + "compiled": { + "models/marts/customer_totals.sql": [ + "SELECT customer_id, SUBSTRING(status) AS total_amount", + "FROM __ref(\"stg_orders\")", + "" + ], + "models/staging/stg_orders.sql": [ + "SELECT order_id, customer_id, amount, status", + "FROM __source(\"raw_orders\")", + "" + ] + }, + "manifest": { + "nodes": { + "model.failure_corpus.customer_totals": { + "database": null, + "schema": "main", + "name": "customer_totals", + "resource_type": "model", + "package_name": "failure_corpus", + "path": "models/marts/customer_totals.sql", + "original_file_path": "models/marts/customer_totals.sql", + "unique_id": "model.failure_corpus.customer_totals", + "fqn": [ + "failure_corpus", + "models", + "marts", + "customer_totals" + ], + "alias": "customer_totals", + "config": { + "enabled": true, + "alias": null, + "schema": null, + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Order totals per customer", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.customer_totals", + "depends_on": { + "macros": [], + "nodes": [ + "model.failure_corpus.stg_orders" + ] + } + }, + "model.failure_corpus.stg_orders": { + "database": null, + "schema": "main", + "name": "stg_orders", + "resource_type": "model", + "package_name": "failure_corpus", + "path": "models/staging/stg_orders.sql", + "original_file_path": "models/staging/stg_orders.sql", + "unique_id": "model.failure_corpus.stg_orders", + "fqn": [ + "failure_corpus", + "models", + "staging", + "stg_orders" + ], + "alias": "stg_orders", + "config": { + "enabled": true, + "alias": null, + "schema": null, + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Staged orders", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.stg_orders", + "depends_on": { + "macros": [], + "nodes": [ + "source.failure_corpus.raw_orders" + ] + } + } + }, + "sources": { + "source.failure_corpus.raw_orders": { + "database": null, + "schema": "", + "name": "raw_orders", + "resource_type": "source", + "package_name": "failure_corpus", + "path": "sources/raw.yml", + "original_file_path": "sources/raw.yml", + "unique_id": "source.failure_corpus.raw_orders", + "fqn": [ + "failure_corpus", + "raw_orders" + ], + "source_name": "raw_orders", + "source_description": "", + "loader": "", + "identifier": "raw_orders", + "quoting": {}, + "loaded_at_field": null, + "freshness": null, + "external": null, + "description": "Orders feed.", + "columns": { + "order_id": { + "name": "order_id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "customer_id": { + "name": "customer_id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "amount": { + "name": "amount", + "description": "", + "meta": {}, + "data_type": "DOUBLE", + "constraints": [], + "quote": null, + "tags": [] + }, + "status": { + "name": "status", + "description": "", + "meta": {}, + "data_type": "VARCHAR", + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": {}, + "source_meta": {}, + "tags": [], + "config": {}, + "patch_path": null, + "unrendered_config": {}, + "relation_name": null + } + }, + "macros": {}, + "exposures": {}, + "metrics": {}, + "groups": {}, + "selectors": {}, + "disabled": {}, + "parent_map": { + "model.failure_corpus.customer_totals": [ + "model.failure_corpus.stg_orders" + ], + "model.failure_corpus.stg_orders": [ + "source.failure_corpus.raw_orders" + ], + "source.failure_corpus.raw_orders": [] + }, + "group_map": {}, + "docs": {}, + "saved_queries": {}, + "semantic_models": {}, + "unit_tests": {} + } +} diff --git a/tests/goldens/compiler/failure/analysis-function-argument-type.json b/tests/goldens/compiler/failure/analysis-function-argument-type.json new file mode 100644 index 0000000000..1e1430c78f --- /dev/null +++ b/tests/goldens/compiler/failure/analysis-function-argument-type.json @@ -0,0 +1,276 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "B213", + "message": "Function 'abs' argument 1 expects NUMERIC, found STRING", + "resource_type": "model", + "resource_name": "customer_totals", + "path": "models/marts/customer_totals.sql", + "line": 5, + "column": 25, + "location": { + "path": "models/marts/customer_totals.sql", + "line": 5, + "column": 25, + "end_line": 5, + "end_column": 31 + }, + "help": "supply argument types supported by this function or conditional expression", + "notes": [ + "status is TEXT" + ] + } + ] + } + ], + "compiled": { + "models/marts/customer_totals.sql": [ + "SELECT customer_id, ABS(status) AS total_amount", + "FROM __ref(\"stg_orders\")", + "" + ], + "models/staging/stg_orders.sql": [ + "SELECT order_id, customer_id, amount, status", + "FROM __source(\"raw_orders\")", + "" + ] + }, + "manifest": { + "nodes": { + "model.failure_corpus.customer_totals": { + "database": null, + "schema": "main", + "name": "customer_totals", + "resource_type": "model", + "package_name": "failure_corpus", + "path": "models/marts/customer_totals.sql", + "original_file_path": "models/marts/customer_totals.sql", + "unique_id": "model.failure_corpus.customer_totals", + "fqn": [ + "failure_corpus", + "models", + "marts", + "customer_totals" + ], + "alias": "customer_totals", + "config": { + "enabled": true, + "alias": null, + "schema": null, + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Order totals per customer", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.customer_totals", + "depends_on": { + "macros": [], + "nodes": [ + "model.failure_corpus.stg_orders" + ] + } + }, + "model.failure_corpus.stg_orders": { + "database": null, + "schema": "main", + "name": "stg_orders", + "resource_type": "model", + "package_name": "failure_corpus", + "path": "models/staging/stg_orders.sql", + "original_file_path": "models/staging/stg_orders.sql", + "unique_id": "model.failure_corpus.stg_orders", + "fqn": [ + "failure_corpus", + "models", + "staging", + "stg_orders" + ], + "alias": "stg_orders", + "config": { + "enabled": true, + "alias": null, + "schema": null, + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Staged orders", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.stg_orders", + "depends_on": { + "macros": [], + "nodes": [ + "source.failure_corpus.raw_orders" + ] + } + } + }, + "sources": { + "source.failure_corpus.raw_orders": { + "database": null, + "schema": "", + "name": "raw_orders", + "resource_type": "source", + "package_name": "failure_corpus", + "path": "sources/raw.yml", + "original_file_path": "sources/raw.yml", + "unique_id": "source.failure_corpus.raw_orders", + "fqn": [ + "failure_corpus", + "raw_orders" + ], + "source_name": "raw_orders", + "source_description": "", + "loader": "", + "identifier": "raw_orders", + "quoting": {}, + "loaded_at_field": null, + "freshness": null, + "external": null, + "description": "Orders feed.", + "columns": { + "order_id": { + "name": "order_id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "customer_id": { + "name": "customer_id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "amount": { + "name": "amount", + "description": "", + "meta": {}, + "data_type": "DOUBLE", + "constraints": [], + "quote": null, + "tags": [] + }, + "status": { + "name": "status", + "description": "", + "meta": {}, + "data_type": "VARCHAR", + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": {}, + "source_meta": {}, + "tags": [], + "config": {}, + "patch_path": null, + "unrendered_config": {}, + "relation_name": null + } + }, + "macros": {}, + "exposures": {}, + "metrics": {}, + "groups": {}, + "selectors": {}, + "disabled": {}, + "parent_map": { + "model.failure_corpus.customer_totals": [ + "model.failure_corpus.stg_orders" + ], + "model.failure_corpus.stg_orders": [ + "source.failure_corpus.raw_orders" + ], + "source.failure_corpus.raw_orders": [] + }, + "group_map": {}, + "docs": {}, + "saved_queries": {}, + "semantic_models": {}, + "unit_tests": {} + } +} diff --git a/tests/goldens/compiler/failure/analysis-non-boolean-predicate.json b/tests/goldens/compiler/failure/analysis-non-boolean-predicate.json new file mode 100644 index 0000000000..5f5423e60c --- /dev/null +++ b/tests/goldens/compiler/failure/analysis-non-boolean-predicate.json @@ -0,0 +1,277 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "B211", + "message": "WHERE expects a BOOLEAN predicate, found STRING", + "resource_type": "model", + "resource_name": "customer_totals", + "path": "models/marts/customer_totals.sql", + "line": 7, + "column": 7, + "location": { + "path": "models/marts/customer_totals.sql", + "line": 7, + "column": 7, + "end_line": 7, + "end_column": 13 + }, + "help": "use a Boolean predicate in this condition", + "notes": [ + "status is TEXT" + ] + } + ] + } + ], + "compiled": { + "models/marts/customer_totals.sql": [ + "SELECT customer_id, amount AS total_amount", + "FROM __ref(\"stg_orders\")", + "WHERE status", + "" + ], + "models/staging/stg_orders.sql": [ + "SELECT order_id, customer_id, amount, status", + "FROM __source(\"raw_orders\")", + "" + ] + }, + "manifest": { + "nodes": { + "model.failure_corpus.customer_totals": { + "database": null, + "schema": "main", + "name": "customer_totals", + "resource_type": "model", + "package_name": "failure_corpus", + "path": "models/marts/customer_totals.sql", + "original_file_path": "models/marts/customer_totals.sql", + "unique_id": "model.failure_corpus.customer_totals", + "fqn": [ + "failure_corpus", + "models", + "marts", + "customer_totals" + ], + "alias": "customer_totals", + "config": { + "enabled": true, + "alias": null, + "schema": null, + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Order totals per customer", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.customer_totals", + "depends_on": { + "macros": [], + "nodes": [ + "model.failure_corpus.stg_orders" + ] + } + }, + "model.failure_corpus.stg_orders": { + "database": null, + "schema": "main", + "name": "stg_orders", + "resource_type": "model", + "package_name": "failure_corpus", + "path": "models/staging/stg_orders.sql", + "original_file_path": "models/staging/stg_orders.sql", + "unique_id": "model.failure_corpus.stg_orders", + "fqn": [ + "failure_corpus", + "models", + "staging", + "stg_orders" + ], + "alias": "stg_orders", + "config": { + "enabled": true, + "alias": null, + "schema": null, + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Staged orders", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.stg_orders", + "depends_on": { + "macros": [], + "nodes": [ + "source.failure_corpus.raw_orders" + ] + } + } + }, + "sources": { + "source.failure_corpus.raw_orders": { + "database": null, + "schema": "", + "name": "raw_orders", + "resource_type": "source", + "package_name": "failure_corpus", + "path": "sources/raw.yml", + "original_file_path": "sources/raw.yml", + "unique_id": "source.failure_corpus.raw_orders", + "fqn": [ + "failure_corpus", + "raw_orders" + ], + "source_name": "raw_orders", + "source_description": "", + "loader": "", + "identifier": "raw_orders", + "quoting": {}, + "loaded_at_field": null, + "freshness": null, + "external": null, + "description": "Orders feed.", + "columns": { + "order_id": { + "name": "order_id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "customer_id": { + "name": "customer_id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "amount": { + "name": "amount", + "description": "", + "meta": {}, + "data_type": "DOUBLE", + "constraints": [], + "quote": null, + "tags": [] + }, + "status": { + "name": "status", + "description": "", + "meta": {}, + "data_type": "VARCHAR", + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": {}, + "source_meta": {}, + "tags": [], + "config": {}, + "patch_path": null, + "unrendered_config": {}, + "relation_name": null + } + }, + "macros": {}, + "exposures": {}, + "metrics": {}, + "groups": {}, + "selectors": {}, + "disabled": {}, + "parent_map": { + "model.failure_corpus.customer_totals": [ + "model.failure_corpus.stg_orders" + ], + "model.failure_corpus.stg_orders": [ + "source.failure_corpus.raw_orders" + ], + "source.failure_corpus.raw_orders": [] + }, + "group_map": {}, + "docs": {}, + "saved_queries": {}, + "semantic_models": {}, + "unit_tests": {} + } +} diff --git a/tests/goldens/compiler/failure/analysis-non-constant-limit.json b/tests/goldens/compiler/failure/analysis-non-constant-limit.json new file mode 100644 index 0000000000..6d7372cef6 --- /dev/null +++ b/tests/goldens/compiler/failure/analysis-non-constant-limit.json @@ -0,0 +1,274 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "B234", + "message": "LIMIT/OFFSET must be a non-negative constant NUMERIC expression", + "resource_type": "model", + "resource_name": "customer_totals", + "path": "models/marts/customer_totals.sql", + "line": 7, + "column": 7, + "location": { + "path": "models/marts/customer_totals.sql", + "line": 7, + "column": 7, + "end_line": 7, + "end_column": 18 + }, + "help": "use a valid constant bound for LIMIT or OFFSET" + } + ] + } + ], + "compiled": { + "models/marts/customer_totals.sql": [ + "SELECT customer_id", + "FROM __ref(\"stg_orders\")", + "LIMIT customer_id", + "" + ], + "models/staging/stg_orders.sql": [ + "SELECT order_id, customer_id, amount, status", + "FROM __source(\"raw_orders\")", + "" + ] + }, + "manifest": { + "nodes": { + "model.failure_corpus.customer_totals": { + "database": null, + "schema": "main", + "name": "customer_totals", + "resource_type": "model", + "package_name": "failure_corpus", + "path": "models/marts/customer_totals.sql", + "original_file_path": "models/marts/customer_totals.sql", + "unique_id": "model.failure_corpus.customer_totals", + "fqn": [ + "failure_corpus", + "models", + "marts", + "customer_totals" + ], + "alias": "customer_totals", + "config": { + "enabled": true, + "alias": null, + "schema": null, + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Order totals per customer", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.customer_totals", + "depends_on": { + "macros": [], + "nodes": [ + "model.failure_corpus.stg_orders" + ] + } + }, + "model.failure_corpus.stg_orders": { + "database": null, + "schema": "main", + "name": "stg_orders", + "resource_type": "model", + "package_name": "failure_corpus", + "path": "models/staging/stg_orders.sql", + "original_file_path": "models/staging/stg_orders.sql", + "unique_id": "model.failure_corpus.stg_orders", + "fqn": [ + "failure_corpus", + "models", + "staging", + "stg_orders" + ], + "alias": "stg_orders", + "config": { + "enabled": true, + "alias": null, + "schema": null, + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Staged orders", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.stg_orders", + "depends_on": { + "macros": [], + "nodes": [ + "source.failure_corpus.raw_orders" + ] + } + } + }, + "sources": { + "source.failure_corpus.raw_orders": { + "database": null, + "schema": "", + "name": "raw_orders", + "resource_type": "source", + "package_name": "failure_corpus", + "path": "sources/raw.yml", + "original_file_path": "sources/raw.yml", + "unique_id": "source.failure_corpus.raw_orders", + "fqn": [ + "failure_corpus", + "raw_orders" + ], + "source_name": "raw_orders", + "source_description": "", + "loader": "", + "identifier": "raw_orders", + "quoting": {}, + "loaded_at_field": null, + "freshness": null, + "external": null, + "description": "Orders feed.", + "columns": { + "order_id": { + "name": "order_id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "customer_id": { + "name": "customer_id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "amount": { + "name": "amount", + "description": "", + "meta": {}, + "data_type": "DOUBLE", + "constraints": [], + "quote": null, + "tags": [] + }, + "status": { + "name": "status", + "description": "", + "meta": {}, + "data_type": "VARCHAR", + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": {}, + "source_meta": {}, + "tags": [], + "config": {}, + "patch_path": null, + "unrendered_config": {}, + "relation_name": null + } + }, + "macros": {}, + "exposures": {}, + "metrics": {}, + "groups": {}, + "selectors": {}, + "disabled": {}, + "parent_map": { + "model.failure_corpus.customer_totals": [ + "model.failure_corpus.stg_orders" + ], + "model.failure_corpus.stg_orders": [ + "source.failure_corpus.raw_orders" + ], + "source.failure_corpus.raw_orders": [] + }, + "group_map": {}, + "docs": {}, + "saved_queries": {}, + "semantic_models": {}, + "unit_tests": {} + } +} diff --git a/tests/goldens/compiler/failure/analysis-opt-out-conflicts-with-legacy-key.json b/tests/goldens/compiler/failure/analysis-opt-out-conflicts-with-legacy-key.json new file mode 100644 index 0000000000..fe9f4466a9 --- /dev/null +++ b/tests/goldens/compiler/failure/analysis-opt-out-conflicts-with-legacy-key.json @@ -0,0 +1,18 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "P003", + "message": "MODEL sql_analysis conflicts with legacy sql_validation" + } + ] + } + ], + "compiled": {}, + "manifest": null +} diff --git a/tests/goldens/compiler/failure/analysis-rejected-opt-out-hides-findings.json b/tests/goldens/compiler/failure/analysis-rejected-opt-out-hides-findings.json new file mode 100644 index 0000000000..4f7a31b683 --- /dev/null +++ b/tests/goldens/compiler/failure/analysis-rejected-opt-out-hides-findings.json @@ -0,0 +1,275 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "P009", + "message": "`sql_analysis false` is not needed for model 'customer_totals'", + "resource_type": "model", + "resource_name": "customer_totals", + "path": "models/marts/customer_totals.sql", + "line": 3, + "column": 3, + "location": { + "path": "models/marts/customer_totals.sql", + "line": 3, + "column": 3 + }, + "help": "remove `sql_analysis false` and fix the findings it was hiding: 1 unknown column (run `sqb compile` to see them)\n = help: to allow `sql_analysis false` on any model, SQL test or audit, set this in sqlbuild_project.toml:\n [settings]\n require_sql_analysis = false", + "notes": [ + "sqlbuild_project.toml sets [settings] require_sql_analysis = true, which only allows `sql_analysis false` on SQL that SQLBuild cannot parse; this model parses successfully." + ] + } + ] + } + ], + "compiled": { + "models/marts/customer_totals.sql": [ + "SELECT customer_id, SUM(refund) AS total_amount", + "FROM __ref(\"stg_orders\")", + "GROUP BY customer_id", + "" + ], + "models/staging/stg_orders.sql": [ + "SELECT order_id, customer_id, amount, status", + "FROM __source(\"raw_orders\")", + "" + ] + }, + "manifest": { + "nodes": { + "model.failure_corpus.customer_totals": { + "database": null, + "schema": "main", + "name": "customer_totals", + "resource_type": "model", + "package_name": "failure_corpus", + "path": "models/marts/customer_totals.sql", + "original_file_path": "models/marts/customer_totals.sql", + "unique_id": "model.failure_corpus.customer_totals", + "fqn": [ + "failure_corpus", + "models", + "marts", + "customer_totals" + ], + "alias": "customer_totals", + "config": { + "enabled": true, + "alias": null, + "schema": null, + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Order totals per customer", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.customer_totals", + "depends_on": { + "macros": [], + "nodes": [ + "model.failure_corpus.stg_orders" + ] + } + }, + "model.failure_corpus.stg_orders": { + "database": null, + "schema": "main", + "name": "stg_orders", + "resource_type": "model", + "package_name": "failure_corpus", + "path": "models/staging/stg_orders.sql", + "original_file_path": "models/staging/stg_orders.sql", + "unique_id": "model.failure_corpus.stg_orders", + "fqn": [ + "failure_corpus", + "models", + "staging", + "stg_orders" + ], + "alias": "stg_orders", + "config": { + "enabled": true, + "alias": null, + "schema": null, + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Staged orders", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.stg_orders", + "depends_on": { + "macros": [], + "nodes": [ + "source.failure_corpus.raw_orders" + ] + } + } + }, + "sources": { + "source.failure_corpus.raw_orders": { + "database": null, + "schema": "", + "name": "raw_orders", + "resource_type": "source", + "package_name": "failure_corpus", + "path": "sources/raw.yml", + "original_file_path": "sources/raw.yml", + "unique_id": "source.failure_corpus.raw_orders", + "fqn": [ + "failure_corpus", + "raw_orders" + ], + "source_name": "raw_orders", + "source_description": "", + "loader": "", + "identifier": "raw_orders", + "quoting": {}, + "loaded_at_field": null, + "freshness": null, + "external": null, + "description": "Orders feed.", + "columns": { + "order_id": { + "name": "order_id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "customer_id": { + "name": "customer_id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "amount": { + "name": "amount", + "description": "", + "meta": {}, + "data_type": "DOUBLE", + "constraints": [], + "quote": null, + "tags": [] + }, + "status": { + "name": "status", + "description": "", + "meta": {}, + "data_type": "VARCHAR", + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": {}, + "source_meta": {}, + "tags": [], + "config": {}, + "patch_path": null, + "unrendered_config": {}, + "relation_name": null + } + }, + "macros": {}, + "exposures": {}, + "metrics": {}, + "groups": {}, + "selectors": {}, + "disabled": {}, + "parent_map": { + "model.failure_corpus.customer_totals": [ + "model.failure_corpus.stg_orders" + ], + "model.failure_corpus.stg_orders": [ + "source.failure_corpus.raw_orders" + ], + "source.failure_corpus.raw_orders": [] + }, + "group_map": {}, + "docs": {}, + "saved_queries": {}, + "semantic_models": {}, + "unit_tests": {} + } +} diff --git a/tests/goldens/compiler/failure/analysis-relationships-unknown-field.json b/tests/goldens/compiler/failure/analysis-relationships-unknown-field.json new file mode 100644 index 0000000000..eea8403898 --- /dev/null +++ b/tests/goldens/compiler/failure/analysis-relationships-unknown-field.json @@ -0,0 +1,329 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "B300", + "message": "relationships target has no column 'customer_key'", + "resource_type": "model", + "resource_name": "customer_totals", + "path": "models/marts/customer_totals.sql", + "line": 4, + "column": 71, + "location": { + "path": "models/marts/customer_totals.sql", + "line": 4, + "column": 71 + }, + "help": "change the metadata column name to an existing output column" + }, + { + "phase": "compile", + "severity": "error", + "code": "B002", + "message": "audit 'relationships' on model 'customer_totals' column 'customer_id': Unknown column 'customer_key' in table 'stg_orders' (context: SELECT)", + "resource_type": "audit", + "resource_name": "relationships", + "path": "audits/generic/relationships.sql", + "line": 9, + "column": 8, + "location": { + "path": "audits/generic/relationships.sql", + "line": 9, + "column": 8, + "end_line": 9, + "end_column": 20 + } + }, + { + "phase": "compile", + "severity": "error", + "code": "B002", + "message": "audit 'relationships' on model 'customer_totals' column 'customer_id': Unknown column 'customer_key' in table 'stg_orders' (context: SELECT)", + "resource_type": "audit", + "resource_name": "relationships", + "path": "audits/generic/relationships.sql", + "line": 11, + "column": 27, + "location": { + "path": "audits/generic/relationships.sql", + "line": 11, + "column": 27, + "end_line": 11, + "end_column": 39 + } + } + ] + } + ], + "compiled": { + "audits/generic/customer_totals/relationships__customer_id.sql": [ + "SELECT customer_id", + "FROM __ref(\"customer_totals\")", + "WHERE customer_id IS NOT NULL", + " AND customer_id NOT IN (", + " SELECT customer_key", + " FROM __ref(\"stg_orders\")", + " WHERE customer_key IS NOT NULL", + " )", + "" + ], + "models/marts/customer_totals.sql": [ + "SELECT customer_id, SUM(amount) AS total_amount", + "FROM __ref(\"stg_orders\")", + "GROUP BY customer_id", + "" + ], + "models/staging/stg_orders.sql": [ + "SELECT order_id, customer_id, amount, status", + "FROM __source(\"raw_orders\")", + "" + ] + }, + "manifest": { + "nodes": { + "model.failure_corpus.customer_totals": { + "database": null, + "schema": "main", + "name": "customer_totals", + "resource_type": "model", + "package_name": "failure_corpus", + "path": "models/marts/customer_totals.sql", + "original_file_path": "models/marts/customer_totals.sql", + "unique_id": "model.failure_corpus.customer_totals", + "fqn": [ + "failure_corpus", + "models", + "marts", + "customer_totals" + ], + "alias": "customer_totals", + "config": { + "enabled": true, + "alias": null, + "schema": null, + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Order totals per customer", + "columns": { + "customer_id": { + "name": "customer_id", + "description": "", + "meta": {}, + "data_type": null, + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.customer_totals", + "depends_on": { + "macros": [], + "nodes": [ + "model.failure_corpus.stg_orders" + ] + } + }, + "model.failure_corpus.stg_orders": { + "database": null, + "schema": "main", + "name": "stg_orders", + "resource_type": "model", + "package_name": "failure_corpus", + "path": "models/staging/stg_orders.sql", + "original_file_path": "models/staging/stg_orders.sql", + "unique_id": "model.failure_corpus.stg_orders", + "fqn": [ + "failure_corpus", + "models", + "staging", + "stg_orders" + ], + "alias": "stg_orders", + "config": { + "enabled": true, + "alias": null, + "schema": null, + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Staged orders", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.stg_orders", + "depends_on": { + "macros": [], + "nodes": [ + "source.failure_corpus.raw_orders" + ] + } + } + }, + "sources": { + "source.failure_corpus.raw_orders": { + "database": null, + "schema": "", + "name": "raw_orders", + "resource_type": "source", + "package_name": "failure_corpus", + "path": "sources/raw.yml", + "original_file_path": "sources/raw.yml", + "unique_id": "source.failure_corpus.raw_orders", + "fqn": [ + "failure_corpus", + "raw_orders" + ], + "source_name": "raw_orders", + "source_description": "", + "loader": "", + "identifier": "raw_orders", + "quoting": {}, + "loaded_at_field": null, + "freshness": null, + "external": null, + "description": "Orders feed.", + "columns": { + "order_id": { + "name": "order_id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "customer_id": { + "name": "customer_id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "amount": { + "name": "amount", + "description": "", + "meta": {}, + "data_type": "DOUBLE", + "constraints": [], + "quote": null, + "tags": [] + }, + "status": { + "name": "status", + "description": "", + "meta": {}, + "data_type": "VARCHAR", + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": {}, + "source_meta": {}, + "tags": [], + "config": {}, + "patch_path": null, + "unrendered_config": {}, + "relation_name": null + } + }, + "macros": {}, + "exposures": {}, + "metrics": {}, + "groups": {}, + "selectors": {}, + "disabled": {}, + "parent_map": { + "model.failure_corpus.customer_totals": [ + "model.failure_corpus.stg_orders" + ], + "model.failure_corpus.stg_orders": [ + "source.failure_corpus.raw_orders" + ], + "source.failure_corpus.raw_orders": [] + }, + "group_map": {}, + "docs": {}, + "saved_queries": {}, + "semantic_models": {}, + "unit_tests": {} + } +} diff --git a/tests/goldens/compiler/failure/analysis-session-cte-chain-unknown-column.json b/tests/goldens/compiler/failure/analysis-session-cte-chain-unknown-column.json new file mode 100644 index 0000000000..a06ea313c2 --- /dev/null +++ b/tests/goldens/compiler/failure/analysis-session-cte-chain-unknown-column.json @@ -0,0 +1,273 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "B002", + "message": "Unknown column 'order_count' in totals", + "resource_type": "model", + "resource_name": "customer_totals", + "path": "models/marts/customer_totals.sql", + "line": 7, + "column": 35, + "location": { + "path": "models/marts/customer_totals.sql", + "line": 7, + "column": 35, + "end_line": 7, + "end_column": 46 + } + } + ] + } + ], + "compiled": { + "models/marts/customer_totals.sql": [ + "WITH base AS (SELECT * FROM __ref(\"stg_orders\")),", + "totals AS (SELECT customer_id, SUM(amount) AS total_amount FROM base GROUP BY 1)", + "SELECT customer_id, total_amount, order_count FROM totals", + "" + ], + "models/staging/stg_orders.sql": [ + "SELECT order_id, customer_id, amount, status", + "FROM __source(\"raw_orders\")", + "" + ] + }, + "manifest": { + "nodes": { + "model.failure_corpus.customer_totals": { + "database": null, + "schema": "main", + "name": "customer_totals", + "resource_type": "model", + "package_name": "failure_corpus", + "path": "models/marts/customer_totals.sql", + "original_file_path": "models/marts/customer_totals.sql", + "unique_id": "model.failure_corpus.customer_totals", + "fqn": [ + "failure_corpus", + "models", + "marts", + "customer_totals" + ], + "alias": "customer_totals", + "config": { + "enabled": true, + "alias": null, + "schema": null, + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Order totals per customer", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.customer_totals", + "depends_on": { + "macros": [], + "nodes": [ + "model.failure_corpus.stg_orders" + ] + } + }, + "model.failure_corpus.stg_orders": { + "database": null, + "schema": "main", + "name": "stg_orders", + "resource_type": "model", + "package_name": "failure_corpus", + "path": "models/staging/stg_orders.sql", + "original_file_path": "models/staging/stg_orders.sql", + "unique_id": "model.failure_corpus.stg_orders", + "fqn": [ + "failure_corpus", + "models", + "staging", + "stg_orders" + ], + "alias": "stg_orders", + "config": { + "enabled": true, + "alias": null, + "schema": null, + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Staged orders", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.stg_orders", + "depends_on": { + "macros": [], + "nodes": [ + "source.failure_corpus.raw_orders" + ] + } + } + }, + "sources": { + "source.failure_corpus.raw_orders": { + "database": null, + "schema": "", + "name": "raw_orders", + "resource_type": "source", + "package_name": "failure_corpus", + "path": "sources/raw.yml", + "original_file_path": "sources/raw.yml", + "unique_id": "source.failure_corpus.raw_orders", + "fqn": [ + "failure_corpus", + "raw_orders" + ], + "source_name": "raw_orders", + "source_description": "", + "loader": "", + "identifier": "raw_orders", + "quoting": {}, + "loaded_at_field": null, + "freshness": null, + "external": null, + "description": "Orders feed.", + "columns": { + "order_id": { + "name": "order_id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "customer_id": { + "name": "customer_id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "amount": { + "name": "amount", + "description": "", + "meta": {}, + "data_type": "DOUBLE", + "constraints": [], + "quote": null, + "tags": [] + }, + "status": { + "name": "status", + "description": "", + "meta": {}, + "data_type": "VARCHAR", + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": {}, + "source_meta": {}, + "tags": [], + "config": {}, + "patch_path": null, + "unrendered_config": {}, + "relation_name": null + } + }, + "macros": {}, + "exposures": {}, + "metrics": {}, + "groups": {}, + "selectors": {}, + "disabled": {}, + "parent_map": { + "model.failure_corpus.customer_totals": [ + "model.failure_corpus.stg_orders" + ], + "model.failure_corpus.stg_orders": [ + "source.failure_corpus.raw_orders" + ], + "source.failure_corpus.raw_orders": [] + }, + "group_map": {}, + "docs": {}, + "saved_queries": {}, + "semantic_models": {}, + "unit_tests": {} + } +} diff --git a/tests/goldens/compiler/failure/analysis-session-set-operation-unknown-column.json b/tests/goldens/compiler/failure/analysis-session-set-operation-unknown-column.json new file mode 100644 index 0000000000..3ad0352ce1 --- /dev/null +++ b/tests/goldens/compiler/failure/analysis-session-set-operation-unknown-column.json @@ -0,0 +1,276 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "B002", + "message": "Unknown column 'refund_amount' in stg_orders", + "resource_type": "model", + "resource_name": "customer_totals", + "path": "models/marts/customer_totals.sql", + "line": 7, + "column": 21, + "location": { + "path": "models/marts/customer_totals.sql", + "line": 7, + "column": 21, + "end_line": 7, + "end_column": 34 + }, + "notes": [ + "stg_orders has: amount, status, customer_id, order_id" + ] + } + ] + } + ], + "compiled": { + "models/marts/customer_totals.sql": [ + "SELECT customer_id, amount AS total_amount FROM __ref(\"stg_orders\")", + "UNION ALL", + "SELECT customer_id, refund_amount FROM __ref(\"stg_orders\")", + "" + ], + "models/staging/stg_orders.sql": [ + "SELECT order_id, customer_id, amount, status", + "FROM __source(\"raw_orders\")", + "" + ] + }, + "manifest": { + "nodes": { + "model.failure_corpus.customer_totals": { + "database": null, + "schema": "main", + "name": "customer_totals", + "resource_type": "model", + "package_name": "failure_corpus", + "path": "models/marts/customer_totals.sql", + "original_file_path": "models/marts/customer_totals.sql", + "unique_id": "model.failure_corpus.customer_totals", + "fqn": [ + "failure_corpus", + "models", + "marts", + "customer_totals" + ], + "alias": "customer_totals", + "config": { + "enabled": true, + "alias": null, + "schema": null, + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Order totals per customer", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.customer_totals", + "depends_on": { + "macros": [], + "nodes": [ + "model.failure_corpus.stg_orders" + ] + } + }, + "model.failure_corpus.stg_orders": { + "database": null, + "schema": "main", + "name": "stg_orders", + "resource_type": "model", + "package_name": "failure_corpus", + "path": "models/staging/stg_orders.sql", + "original_file_path": "models/staging/stg_orders.sql", + "unique_id": "model.failure_corpus.stg_orders", + "fqn": [ + "failure_corpus", + "models", + "staging", + "stg_orders" + ], + "alias": "stg_orders", + "config": { + "enabled": true, + "alias": null, + "schema": null, + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Staged orders", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.stg_orders", + "depends_on": { + "macros": [], + "nodes": [ + "source.failure_corpus.raw_orders" + ] + } + } + }, + "sources": { + "source.failure_corpus.raw_orders": { + "database": null, + "schema": "", + "name": "raw_orders", + "resource_type": "source", + "package_name": "failure_corpus", + "path": "sources/raw.yml", + "original_file_path": "sources/raw.yml", + "unique_id": "source.failure_corpus.raw_orders", + "fqn": [ + "failure_corpus", + "raw_orders" + ], + "source_name": "raw_orders", + "source_description": "", + "loader": "", + "identifier": "raw_orders", + "quoting": {}, + "loaded_at_field": null, + "freshness": null, + "external": null, + "description": "Orders feed.", + "columns": { + "order_id": { + "name": "order_id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "customer_id": { + "name": "customer_id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "amount": { + "name": "amount", + "description": "", + "meta": {}, + "data_type": "DOUBLE", + "constraints": [], + "quote": null, + "tags": [] + }, + "status": { + "name": "status", + "description": "", + "meta": {}, + "data_type": "VARCHAR", + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": {}, + "source_meta": {}, + "tags": [], + "config": {}, + "patch_path": null, + "unrendered_config": {}, + "relation_name": null + } + }, + "macros": {}, + "exposures": {}, + "metrics": {}, + "groups": {}, + "selectors": {}, + "disabled": {}, + "parent_map": { + "model.failure_corpus.customer_totals": [ + "model.failure_corpus.stg_orders" + ], + "model.failure_corpus.stg_orders": [ + "source.failure_corpus.raw_orders" + ], + "source.failure_corpus.raw_orders": [] + }, + "group_map": {}, + "docs": {}, + "saved_queries": {}, + "semantic_models": {}, + "unit_tests": {} + } +} diff --git a/tests/goldens/compiler/failure/analysis-session-star-published-unknown-column.json b/tests/goldens/compiler/failure/analysis-session-star-published-unknown-column.json new file mode 100644 index 0000000000..496a2c776b --- /dev/null +++ b/tests/goldens/compiler/failure/analysis-session-star-published-unknown-column.json @@ -0,0 +1,353 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "B002", + "message": "Unknown column 'missing_amount' in order_layers", + "resource_type": "model", + "resource_name": "customer_totals", + "path": "models/marts/customer_totals.sql", + "line": 5, + "column": 25, + "location": { + "path": "models/marts/customer_totals.sql", + "line": 5, + "column": 25, + "end_line": 5, + "end_column": 39 + }, + "notes": [ + "order_layers has: doubled_amount, amount, status, customer_id, order_id" + ] + } + ] + } + ], + "compiled": { + "models/marts/customer_totals.sql": [ + "SELECT customer_id, SUM(missing_amount) AS total_amount", + "FROM __ref(\"order_layers\")", + "GROUP BY customer_id", + "" + ], + "models/marts/order_layers.sql": [ + "SELECT s.*, s.amount * 2 AS doubled_amount", + "FROM __ref(\"stg_orders\") s", + "" + ], + "models/staging/stg_orders.sql": [ + "SELECT *", + "FROM __source(\"raw_orders\")", + "" + ] + }, + "manifest": { + "nodes": { + "model.failure_corpus.customer_totals": { + "database": null, + "schema": "main", + "name": "customer_totals", + "resource_type": "model", + "package_name": "failure_corpus", + "path": "models/marts/customer_totals.sql", + "original_file_path": "models/marts/customer_totals.sql", + "unique_id": "model.failure_corpus.customer_totals", + "fqn": [ + "failure_corpus", + "models", + "marts", + "customer_totals" + ], + "alias": "customer_totals", + "config": { + "enabled": true, + "alias": null, + "schema": null, + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Order totals per customer", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.customer_totals", + "depends_on": { + "macros": [], + "nodes": [ + "model.failure_corpus.order_layers" + ] + } + }, + "model.failure_corpus.order_layers": { + "database": null, + "schema": "main", + "name": "order_layers", + "resource_type": "model", + "package_name": "failure_corpus", + "path": "models/marts/order_layers.sql", + "original_file_path": "models/marts/order_layers.sql", + "unique_id": "model.failure_corpus.order_layers", + "fqn": [ + "failure_corpus", + "models", + "marts", + "order_layers" + ], + "alias": "order_layers", + "config": { + "enabled": true, + "alias": null, + "schema": null, + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Orders layered over the staged star", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.order_layers", + "depends_on": { + "macros": [], + "nodes": [ + "model.failure_corpus.stg_orders" + ] + } + }, + "model.failure_corpus.stg_orders": { + "database": null, + "schema": "main", + "name": "stg_orders", + "resource_type": "model", + "package_name": "failure_corpus", + "path": "models/staging/stg_orders.sql", + "original_file_path": "models/staging/stg_orders.sql", + "unique_id": "model.failure_corpus.stg_orders", + "fqn": [ + "failure_corpus", + "models", + "staging", + "stg_orders" + ], + "alias": "stg_orders", + "config": { + "enabled": true, + "alias": null, + "schema": null, + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Staged orders", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.stg_orders", + "depends_on": { + "macros": [], + "nodes": [ + "source.failure_corpus.raw_orders" + ] + } + } + }, + "sources": { + "source.failure_corpus.raw_orders": { + "database": null, + "schema": "", + "name": "raw_orders", + "resource_type": "source", + "package_name": "failure_corpus", + "path": "sources/raw.yml", + "original_file_path": "sources/raw.yml", + "unique_id": "source.failure_corpus.raw_orders", + "fqn": [ + "failure_corpus", + "raw_orders" + ], + "source_name": "raw_orders", + "source_description": "", + "loader": "", + "identifier": "raw_orders", + "quoting": {}, + "loaded_at_field": null, + "freshness": null, + "external": null, + "description": "Orders feed.", + "columns": { + "order_id": { + "name": "order_id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "customer_id": { + "name": "customer_id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "amount": { + "name": "amount", + "description": "", + "meta": {}, + "data_type": "DOUBLE", + "constraints": [], + "quote": null, + "tags": [] + }, + "status": { + "name": "status", + "description": "", + "meta": {}, + "data_type": "VARCHAR", + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": {}, + "source_meta": {}, + "tags": [], + "config": {}, + "patch_path": null, + "unrendered_config": {}, + "relation_name": null + } + }, + "macros": {}, + "exposures": {}, + "metrics": {}, + "groups": {}, + "selectors": {}, + "disabled": {}, + "parent_map": { + "model.failure_corpus.customer_totals": [ + "model.failure_corpus.order_layers" + ], + "model.failure_corpus.order_layers": [ + "model.failure_corpus.stg_orders" + ], + "model.failure_corpus.stg_orders": [ + "source.failure_corpus.raw_orders" + ], + "source.failure_corpus.raw_orders": [] + }, + "group_map": {}, + "docs": {}, + "saved_queries": {}, + "semantic_models": {}, + "unit_tests": {} + } +} diff --git a/tests/goldens/compiler/failure/analysis-set-operation-branch-types.json b/tests/goldens/compiler/failure/analysis-set-operation-branch-types.json new file mode 100644 index 0000000000..a4037c2270 --- /dev/null +++ b/tests/goldens/compiler/failure/analysis-set-operation-branch-types.json @@ -0,0 +1,272 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "B215", + "message": "UNION column 2 has incompatible types: NUMERIC vs DATE", + "resource_type": "model", + "resource_name": "customer_totals", + "path": "models/marts/customer_totals.sql", + "line": 5, + "column": 1, + "location": { + "path": "models/marts/customer_totals.sql", + "line": 5, + "column": 1 + }, + "help": "align the corresponding types in each set-operation branch" + } + ] + } + ], + "compiled": { + "models/marts/customer_totals.sql": [ + "SELECT customer_id, amount FROM __ref(\"stg_orders\")", + "UNION ALL", + "SELECT customer_id, DATE '2026-01-01' FROM __ref(\"stg_orders\")", + "" + ], + "models/staging/stg_orders.sql": [ + "SELECT order_id, customer_id, amount, status", + "FROM __source(\"raw_orders\")", + "" + ] + }, + "manifest": { + "nodes": { + "model.failure_corpus.customer_totals": { + "database": null, + "schema": "main", + "name": "customer_totals", + "resource_type": "model", + "package_name": "failure_corpus", + "path": "models/marts/customer_totals.sql", + "original_file_path": "models/marts/customer_totals.sql", + "unique_id": "model.failure_corpus.customer_totals", + "fqn": [ + "failure_corpus", + "models", + "marts", + "customer_totals" + ], + "alias": "customer_totals", + "config": { + "enabled": true, + "alias": null, + "schema": null, + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Order totals per customer", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.customer_totals", + "depends_on": { + "macros": [], + "nodes": [ + "model.failure_corpus.stg_orders" + ] + } + }, + "model.failure_corpus.stg_orders": { + "database": null, + "schema": "main", + "name": "stg_orders", + "resource_type": "model", + "package_name": "failure_corpus", + "path": "models/staging/stg_orders.sql", + "original_file_path": "models/staging/stg_orders.sql", + "unique_id": "model.failure_corpus.stg_orders", + "fqn": [ + "failure_corpus", + "models", + "staging", + "stg_orders" + ], + "alias": "stg_orders", + "config": { + "enabled": true, + "alias": null, + "schema": null, + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Staged orders", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.stg_orders", + "depends_on": { + "macros": [], + "nodes": [ + "source.failure_corpus.raw_orders" + ] + } + } + }, + "sources": { + "source.failure_corpus.raw_orders": { + "database": null, + "schema": "", + "name": "raw_orders", + "resource_type": "source", + "package_name": "failure_corpus", + "path": "sources/raw.yml", + "original_file_path": "sources/raw.yml", + "unique_id": "source.failure_corpus.raw_orders", + "fqn": [ + "failure_corpus", + "raw_orders" + ], + "source_name": "raw_orders", + "source_description": "", + "loader": "", + "identifier": "raw_orders", + "quoting": {}, + "loaded_at_field": null, + "freshness": null, + "external": null, + "description": "Orders feed.", + "columns": { + "order_id": { + "name": "order_id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "customer_id": { + "name": "customer_id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "amount": { + "name": "amount", + "description": "", + "meta": {}, + "data_type": "DOUBLE", + "constraints": [], + "quote": null, + "tags": [] + }, + "status": { + "name": "status", + "description": "", + "meta": {}, + "data_type": "VARCHAR", + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": {}, + "source_meta": {}, + "tags": [], + "config": {}, + "patch_path": null, + "unrendered_config": {}, + "relation_name": null + } + }, + "macros": {}, + "exposures": {}, + "metrics": {}, + "groups": {}, + "selectors": {}, + "disabled": {}, + "parent_map": { + "model.failure_corpus.customer_totals": [ + "model.failure_corpus.stg_orders" + ], + "model.failure_corpus.stg_orders": [ + "source.failure_corpus.raw_orders" + ], + "source.failure_corpus.raw_orders": [] + }, + "group_map": {}, + "docs": {}, + "saved_queries": {}, + "semantic_models": {}, + "unit_tests": {} + } +} diff --git a/tests/goldens/compiler/failure/analysis-test-fixture-unknown-column.json b/tests/goldens/compiler/failure/analysis-test-fixture-unknown-column.json new file mode 100644 index 0000000000..a984fd998d --- /dev/null +++ b/tests/goldens/compiler/failure/analysis-test-fixture-unknown-column.json @@ -0,0 +1,283 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "B302", + "message": "SQL test '__source__raw_orders' names unknown column 'discount'", + "resource_type": "sql_test", + "resource_name": "test_stg_orders", + "path": "tests/unit/test_stg_orders.sql", + "line": 5, + "column": 98, + "location": { + "path": "tests/unit/test_stg_orders.sql", + "line": 5, + "column": 98, + "end_line": 5, + "end_column": 106 + }, + "help": "align the SQL-test fixture or expected columns with the tested resource's output" + } + ] + } + ], + "compiled": { + "models/marts/customer_totals.sql": [ + "SELECT customer_id, SUM(amount) AS total_amount", + "FROM __ref(\"stg_orders\")", + "GROUP BY customer_id", + "" + ], + "models/staging/stg_orders.sql": [ + "SELECT order_id, customer_id, amount, status", + "FROM __source(\"raw_orders\")", + "" + ], + "tests/stg_orders/test_stg_orders.sql": [ + "WITH __source__raw_orders AS (SELECT 1 AS order_id, 10 AS customer_id, CAST(5 AS DOUBLE) AS amount, 'placed' AS status, 2 AS discount),", + "__ref__stg_orders AS (SELECT order_id, customer_id, amount, status", + "FROM __source__raw_orders),", + "__actual__stg_orders AS (SELECT * FROM __ref__stg_orders),", + "__expected__stg_orders AS (SELECT 1 AS order_id)", + "SELECT 0 AS step_index, 'stg_orders' AS model_name, (SELECT COUNT(*) FROM __actual__stg_orders) AS actual_count, (SELECT COUNT(*) FROM __expected__stg_orders) AS expected_count, (SELECT COUNT(*) FROM (SELECT order_id FROM __actual__stg_orders EXCEPT SELECT order_id FROM __expected__stg_orders) AS __sqlbuild_mismatch) AS unexpected_count, (SELECT COUNT(*) FROM (SELECT order_id FROM __expected__stg_orders EXCEPT SELECT order_id FROM __actual__stg_orders) AS __sqlbuild_missing) AS missing_count", + "" + ] + }, + "manifest": { + "nodes": { + "model.failure_corpus.customer_totals": { + "database": null, + "schema": "main", + "name": "customer_totals", + "resource_type": "model", + "package_name": "failure_corpus", + "path": "models/marts/customer_totals.sql", + "original_file_path": "models/marts/customer_totals.sql", + "unique_id": "model.failure_corpus.customer_totals", + "fqn": [ + "failure_corpus", + "models", + "marts", + "customer_totals" + ], + "alias": "customer_totals", + "config": { + "enabled": true, + "alias": null, + "schema": null, + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Order totals per customer", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.customer_totals", + "depends_on": { + "macros": [], + "nodes": [ + "model.failure_corpus.stg_orders" + ] + } + }, + "model.failure_corpus.stg_orders": { + "database": null, + "schema": "main", + "name": "stg_orders", + "resource_type": "model", + "package_name": "failure_corpus", + "path": "models/staging/stg_orders.sql", + "original_file_path": "models/staging/stg_orders.sql", + "unique_id": "model.failure_corpus.stg_orders", + "fqn": [ + "failure_corpus", + "models", + "staging", + "stg_orders" + ], + "alias": "stg_orders", + "config": { + "enabled": true, + "alias": null, + "schema": null, + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Staged orders", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.stg_orders", + "depends_on": { + "macros": [], + "nodes": [ + "source.failure_corpus.raw_orders" + ] + } + } + }, + "sources": { + "source.failure_corpus.raw_orders": { + "database": null, + "schema": "", + "name": "raw_orders", + "resource_type": "source", + "package_name": "failure_corpus", + "path": "sources/raw.yml", + "original_file_path": "sources/raw.yml", + "unique_id": "source.failure_corpus.raw_orders", + "fqn": [ + "failure_corpus", + "raw_orders" + ], + "source_name": "raw_orders", + "source_description": "", + "loader": "", + "identifier": "raw_orders", + "quoting": {}, + "loaded_at_field": null, + "freshness": null, + "external": null, + "description": "Orders feed.", + "columns": { + "order_id": { + "name": "order_id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "customer_id": { + "name": "customer_id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "amount": { + "name": "amount", + "description": "", + "meta": {}, + "data_type": "DOUBLE", + "constraints": [], + "quote": null, + "tags": [] + }, + "status": { + "name": "status", + "description": "", + "meta": {}, + "data_type": "VARCHAR", + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": {}, + "source_meta": {}, + "tags": [], + "config": {}, + "patch_path": null, + "unrendered_config": {}, + "relation_name": null + } + }, + "macros": {}, + "exposures": {}, + "metrics": {}, + "groups": {}, + "selectors": {}, + "disabled": {}, + "parent_map": { + "model.failure_corpus.customer_totals": [ + "model.failure_corpus.stg_orders" + ], + "model.failure_corpus.stg_orders": [ + "source.failure_corpus.raw_orders" + ], + "source.failure_corpus.raw_orders": [] + }, + "group_map": {}, + "docs": {}, + "saved_queries": {}, + "semantic_models": {}, + "unit_tests": {} + } +} diff --git a/tests/goldens/compiler/failure/analysis-udf-argument-count.json b/tests/goldens/compiler/failure/analysis-udf-argument-count.json new file mode 100644 index 0000000000..58323fe460 --- /dev/null +++ b/tests/goldens/compiler/failure/analysis-udf-argument-count.json @@ -0,0 +1,280 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "B102", + "message": "Function 'scaled_amount' expects 2 arguments but received 1", + "resource_type": "model", + "resource_name": "customer_totals", + "path": "models/marts/customer_totals.sql", + "line": 5, + "column": 28, + "location": { + "path": "models/marts/customer_totals.sql", + "line": 5, + "column": 28 + }, + "help": "pass the number of arguments required by this function's signature" + } + ] + } + ], + "compiled": { + "functions/sql/scaled_amount.sql": [ + "CREATE OR REPLACE MACRO main.scaled_amount(p_amount, p_factor) AS (", + "p_amount * p_factor", + ")", + "" + ], + "models/marts/customer_totals.sql": [ + "SELECT customer_id, __udf(\"scaled_amount\")(amount) AS total_amount", + "FROM __ref(\"stg_orders\")", + "" + ], + "models/staging/stg_orders.sql": [ + "SELECT order_id, customer_id, amount, status", + "FROM __source(\"raw_orders\")", + "" + ] + }, + "manifest": { + "nodes": { + "model.failure_corpus.customer_totals": { + "database": null, + "schema": "main", + "name": "customer_totals", + "resource_type": "model", + "package_name": "failure_corpus", + "path": "models/marts/customer_totals.sql", + "original_file_path": "models/marts/customer_totals.sql", + "unique_id": "model.failure_corpus.customer_totals", + "fqn": [ + "failure_corpus", + "models", + "marts", + "customer_totals" + ], + "alias": "customer_totals", + "config": { + "enabled": true, + "alias": null, + "schema": null, + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Order totals per customer", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.customer_totals", + "depends_on": { + "macros": [], + "nodes": [ + "model.failure_corpus.scaled_amount", + "model.failure_corpus.stg_orders" + ] + } + }, + "model.failure_corpus.stg_orders": { + "database": null, + "schema": "main", + "name": "stg_orders", + "resource_type": "model", + "package_name": "failure_corpus", + "path": "models/staging/stg_orders.sql", + "original_file_path": "models/staging/stg_orders.sql", + "unique_id": "model.failure_corpus.stg_orders", + "fqn": [ + "failure_corpus", + "models", + "staging", + "stg_orders" + ], + "alias": "stg_orders", + "config": { + "enabled": true, + "alias": null, + "schema": null, + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Staged orders", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.stg_orders", + "depends_on": { + "macros": [], + "nodes": [ + "source.failure_corpus.raw_orders" + ] + } + } + }, + "sources": { + "source.failure_corpus.raw_orders": { + "database": null, + "schema": "", + "name": "raw_orders", + "resource_type": "source", + "package_name": "failure_corpus", + "path": "sources/raw.yml", + "original_file_path": "sources/raw.yml", + "unique_id": "source.failure_corpus.raw_orders", + "fqn": [ + "failure_corpus", + "raw_orders" + ], + "source_name": "raw_orders", + "source_description": "", + "loader": "", + "identifier": "raw_orders", + "quoting": {}, + "loaded_at_field": null, + "freshness": null, + "external": null, + "description": "Orders feed.", + "columns": { + "order_id": { + "name": "order_id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "customer_id": { + "name": "customer_id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "amount": { + "name": "amount", + "description": "", + "meta": {}, + "data_type": "DOUBLE", + "constraints": [], + "quote": null, + "tags": [] + }, + "status": { + "name": "status", + "description": "", + "meta": {}, + "data_type": "VARCHAR", + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": {}, + "source_meta": {}, + "tags": [], + "config": {}, + "patch_path": null, + "unrendered_config": {}, + "relation_name": null + } + }, + "macros": {}, + "exposures": {}, + "metrics": {}, + "groups": {}, + "selectors": {}, + "disabled": {}, + "parent_map": { + "model.failure_corpus.customer_totals": [ + "model.failure_corpus.scaled_amount", + "model.failure_corpus.stg_orders" + ], + "model.failure_corpus.stg_orders": [ + "source.failure_corpus.raw_orders" + ], + "source.failure_corpus.raw_orders": [], + "model.failure_corpus.scaled_amount": [] + }, + "group_map": {}, + "docs": {}, + "saved_queries": {}, + "semantic_models": {}, + "unit_tests": {} + } +} diff --git a/tests/goldens/compiler/failure/analysis-udf-argument-family.json b/tests/goldens/compiler/failure/analysis-udf-argument-family.json new file mode 100644 index 0000000000..6bb4f4a102 --- /dev/null +++ b/tests/goldens/compiler/failure/analysis-udf-argument-family.json @@ -0,0 +1,280 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "B301", + "message": "Function 'scaled_amount' argument 'p_amount' expects DOUBLE, received TEXT; convert the argument explicitly", + "resource_type": "model", + "resource_name": "customer_totals", + "path": "models/marts/customer_totals.sql", + "line": 5, + "column": 28, + "location": { + "path": "models/marts/customer_totals.sql", + "line": 5, + "column": 28 + }, + "help": "align this declared metadata or audit value with the column or argument type" + } + ] + } + ], + "compiled": { + "functions/sql/scaled_amount.sql": [ + "CREATE OR REPLACE MACRO main.scaled_amount(p_amount, p_factor) AS (", + "p_amount * p_factor", + ")", + "" + ], + "models/marts/customer_totals.sql": [ + "SELECT customer_id, __udf(\"scaled_amount\")(status, 2) AS total_amount", + "FROM __ref(\"stg_orders\")", + "" + ], + "models/staging/stg_orders.sql": [ + "SELECT order_id, customer_id, amount, status", + "FROM __source(\"raw_orders\")", + "" + ] + }, + "manifest": { + "nodes": { + "model.failure_corpus.customer_totals": { + "database": null, + "schema": "main", + "name": "customer_totals", + "resource_type": "model", + "package_name": "failure_corpus", + "path": "models/marts/customer_totals.sql", + "original_file_path": "models/marts/customer_totals.sql", + "unique_id": "model.failure_corpus.customer_totals", + "fqn": [ + "failure_corpus", + "models", + "marts", + "customer_totals" + ], + "alias": "customer_totals", + "config": { + "enabled": true, + "alias": null, + "schema": null, + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Order totals per customer", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.customer_totals", + "depends_on": { + "macros": [], + "nodes": [ + "model.failure_corpus.scaled_amount", + "model.failure_corpus.stg_orders" + ] + } + }, + "model.failure_corpus.stg_orders": { + "database": null, + "schema": "main", + "name": "stg_orders", + "resource_type": "model", + "package_name": "failure_corpus", + "path": "models/staging/stg_orders.sql", + "original_file_path": "models/staging/stg_orders.sql", + "unique_id": "model.failure_corpus.stg_orders", + "fqn": [ + "failure_corpus", + "models", + "staging", + "stg_orders" + ], + "alias": "stg_orders", + "config": { + "enabled": true, + "alias": null, + "schema": null, + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Staged orders", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.stg_orders", + "depends_on": { + "macros": [], + "nodes": [ + "source.failure_corpus.raw_orders" + ] + } + } + }, + "sources": { + "source.failure_corpus.raw_orders": { + "database": null, + "schema": "", + "name": "raw_orders", + "resource_type": "source", + "package_name": "failure_corpus", + "path": "sources/raw.yml", + "original_file_path": "sources/raw.yml", + "unique_id": "source.failure_corpus.raw_orders", + "fqn": [ + "failure_corpus", + "raw_orders" + ], + "source_name": "raw_orders", + "source_description": "", + "loader": "", + "identifier": "raw_orders", + "quoting": {}, + "loaded_at_field": null, + "freshness": null, + "external": null, + "description": "Orders feed.", + "columns": { + "order_id": { + "name": "order_id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "customer_id": { + "name": "customer_id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "amount": { + "name": "amount", + "description": "", + "meta": {}, + "data_type": "DOUBLE", + "constraints": [], + "quote": null, + "tags": [] + }, + "status": { + "name": "status", + "description": "", + "meta": {}, + "data_type": "VARCHAR", + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": {}, + "source_meta": {}, + "tags": [], + "config": {}, + "patch_path": null, + "unrendered_config": {}, + "relation_name": null + } + }, + "macros": {}, + "exposures": {}, + "metrics": {}, + "groups": {}, + "selectors": {}, + "disabled": {}, + "parent_map": { + "model.failure_corpus.customer_totals": [ + "model.failure_corpus.scaled_amount", + "model.failure_corpus.stg_orders" + ], + "model.failure_corpus.stg_orders": [ + "source.failure_corpus.raw_orders" + ], + "source.failure_corpus.raw_orders": [], + "model.failure_corpus.scaled_amount": [] + }, + "group_map": {}, + "docs": {}, + "saved_queries": {}, + "semantic_models": {}, + "unit_tests": {} + } +} diff --git a/tests/goldens/compiler/failure/analysis-unique-key-unknown-column.json b/tests/goldens/compiler/failure/analysis-unique-key-unknown-column.json new file mode 100644 index 0000000000..ca04c7c838 --- /dev/null +++ b/tests/goldens/compiler/failure/analysis-unique-key-unknown-column.json @@ -0,0 +1,274 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "B300", + "message": "unique_key references unknown column 'order_key'", + "resource_type": "model", + "resource_name": "customer_totals", + "path": "models/marts/customer_totals.sql", + "line": 4, + "column": 15, + "location": { + "path": "models/marts/customer_totals.sql", + "line": 4, + "column": 15 + }, + "help": "change the metadata column name to an existing output column" + } + ] + } + ], + "compiled": { + "models/marts/customer_totals.sql": [ + "SELECT customer_id, SUM(amount) AS total_amount", + "FROM __ref(\"stg_orders\")", + "GROUP BY customer_id", + "" + ], + "models/staging/stg_orders.sql": [ + "SELECT order_id, customer_id, amount, status", + "FROM __source(\"raw_orders\")", + "" + ] + }, + "manifest": { + "nodes": { + "model.failure_corpus.customer_totals": { + "database": null, + "schema": "main", + "name": "customer_totals", + "resource_type": "model", + "package_name": "failure_corpus", + "path": "models/marts/customer_totals.sql", + "original_file_path": "models/marts/customer_totals.sql", + "unique_id": "model.failure_corpus.customer_totals", + "fqn": [ + "failure_corpus", + "models", + "marts", + "customer_totals" + ], + "alias": "customer_totals", + "config": { + "enabled": true, + "alias": null, + "schema": null, + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "table", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": [ + "order_key" + ], + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Order totals per customer", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.customer_totals", + "depends_on": { + "macros": [], + "nodes": [ + "model.failure_corpus.stg_orders" + ] + } + }, + "model.failure_corpus.stg_orders": { + "database": null, + "schema": "main", + "name": "stg_orders", + "resource_type": "model", + "package_name": "failure_corpus", + "path": "models/staging/stg_orders.sql", + "original_file_path": "models/staging/stg_orders.sql", + "unique_id": "model.failure_corpus.stg_orders", + "fqn": [ + "failure_corpus", + "models", + "staging", + "stg_orders" + ], + "alias": "stg_orders", + "config": { + "enabled": true, + "alias": null, + "schema": null, + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Staged orders", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.stg_orders", + "depends_on": { + "macros": [], + "nodes": [ + "source.failure_corpus.raw_orders" + ] + } + } + }, + "sources": { + "source.failure_corpus.raw_orders": { + "database": null, + "schema": "", + "name": "raw_orders", + "resource_type": "source", + "package_name": "failure_corpus", + "path": "sources/raw.yml", + "original_file_path": "sources/raw.yml", + "unique_id": "source.failure_corpus.raw_orders", + "fqn": [ + "failure_corpus", + "raw_orders" + ], + "source_name": "raw_orders", + "source_description": "", + "loader": "", + "identifier": "raw_orders", + "quoting": {}, + "loaded_at_field": null, + "freshness": null, + "external": null, + "description": "Orders feed.", + "columns": { + "order_id": { + "name": "order_id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "customer_id": { + "name": "customer_id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "amount": { + "name": "amount", + "description": "", + "meta": {}, + "data_type": "DOUBLE", + "constraints": [], + "quote": null, + "tags": [] + }, + "status": { + "name": "status", + "description": "", + "meta": {}, + "data_type": "VARCHAR", + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": {}, + "source_meta": {}, + "tags": [], + "config": {}, + "patch_path": null, + "unrendered_config": {}, + "relation_name": null + } + }, + "macros": {}, + "exposures": {}, + "metrics": {}, + "groups": {}, + "selectors": {}, + "disabled": {}, + "parent_map": { + "model.failure_corpus.customer_totals": [ + "model.failure_corpus.stg_orders" + ], + "model.failure_corpus.stg_orders": [ + "source.failure_corpus.raw_orders" + ], + "source.failure_corpus.raw_orders": [] + }, + "group_map": {}, + "docs": {}, + "saved_queries": {}, + "semantic_models": {}, + "unit_tests": {} + } +} diff --git a/tests/goldens/compiler/failure/analysis-unknown-function.json b/tests/goldens/compiler/failure/analysis-unknown-function.json new file mode 100644 index 0000000000..6f34c1a3a8 --- /dev/null +++ b/tests/goldens/compiler/failure/analysis-unknown-function.json @@ -0,0 +1,273 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "B101", + "message": "Unknown function 'not_a_function' for dialect DuckDB", + "resource_type": "model", + "resource_name": "customer_totals", + "path": "models/marts/customer_totals.sql", + "line": 5, + "column": 36, + "location": { + "path": "models/marts/customer_totals.sql", + "line": 5, + "column": 36, + "end_line": 5, + "end_column": 42 + }, + "help": "check the function spelling or declare the project function before using it" + } + ] + } + ], + "compiled": { + "models/marts/customer_totals.sql": [ + "SELECT customer_id, not_a_function(amount) AS total_amount", + "FROM __ref(\"stg_orders\")", + "" + ], + "models/staging/stg_orders.sql": [ + "SELECT order_id, customer_id, amount, status", + "FROM __source(\"raw_orders\")", + "" + ] + }, + "manifest": { + "nodes": { + "model.failure_corpus.customer_totals": { + "database": null, + "schema": "main", + "name": "customer_totals", + "resource_type": "model", + "package_name": "failure_corpus", + "path": "models/marts/customer_totals.sql", + "original_file_path": "models/marts/customer_totals.sql", + "unique_id": "model.failure_corpus.customer_totals", + "fqn": [ + "failure_corpus", + "models", + "marts", + "customer_totals" + ], + "alias": "customer_totals", + "config": { + "enabled": true, + "alias": null, + "schema": null, + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Order totals per customer", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.customer_totals", + "depends_on": { + "macros": [], + "nodes": [ + "model.failure_corpus.stg_orders" + ] + } + }, + "model.failure_corpus.stg_orders": { + "database": null, + "schema": "main", + "name": "stg_orders", + "resource_type": "model", + "package_name": "failure_corpus", + "path": "models/staging/stg_orders.sql", + "original_file_path": "models/staging/stg_orders.sql", + "unique_id": "model.failure_corpus.stg_orders", + "fqn": [ + "failure_corpus", + "models", + "staging", + "stg_orders" + ], + "alias": "stg_orders", + "config": { + "enabled": true, + "alias": null, + "schema": null, + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Staged orders", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.stg_orders", + "depends_on": { + "macros": [], + "nodes": [ + "source.failure_corpus.raw_orders" + ] + } + } + }, + "sources": { + "source.failure_corpus.raw_orders": { + "database": null, + "schema": "", + "name": "raw_orders", + "resource_type": "source", + "package_name": "failure_corpus", + "path": "sources/raw.yml", + "original_file_path": "sources/raw.yml", + "unique_id": "source.failure_corpus.raw_orders", + "fqn": [ + "failure_corpus", + "raw_orders" + ], + "source_name": "raw_orders", + "source_description": "", + "loader": "", + "identifier": "raw_orders", + "quoting": {}, + "loaded_at_field": null, + "freshness": null, + "external": null, + "description": "Orders feed.", + "columns": { + "order_id": { + "name": "order_id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "customer_id": { + "name": "customer_id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "amount": { + "name": "amount", + "description": "", + "meta": {}, + "data_type": "DOUBLE", + "constraints": [], + "quote": null, + "tags": [] + }, + "status": { + "name": "status", + "description": "", + "meta": {}, + "data_type": "VARCHAR", + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": {}, + "source_meta": {}, + "tags": [], + "config": {}, + "patch_path": null, + "unrendered_config": {}, + "relation_name": null + } + }, + "macros": {}, + "exposures": {}, + "metrics": {}, + "groups": {}, + "selectors": {}, + "disabled": {}, + "parent_map": { + "model.failure_corpus.customer_totals": [ + "model.failure_corpus.stg_orders" + ], + "model.failure_corpus.stg_orders": [ + "source.failure_corpus.raw_orders" + ], + "source.failure_corpus.raw_orders": [] + }, + "group_map": {}, + "docs": {}, + "saved_queries": {}, + "semantic_models": {}, + "unit_tests": {} + } +} diff --git a/tests/goldens/compiler/failure/analysis-unknown-qualifier.json b/tests/goldens/compiler/failure/analysis-unknown-qualifier.json new file mode 100644 index 0000000000..a9ba57f63f --- /dev/null +++ b/tests/goldens/compiler/failure/analysis-unknown-qualifier.json @@ -0,0 +1,273 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "B004", + "message": "Unknown table or alias 'x' referenced by column 'order_id'", + "resource_type": "model", + "resource_name": "customer_totals", + "path": "models/marts/customer_totals.sql", + "line": 5, + "column": 8, + "location": { + "path": "models/marts/customer_totals.sql", + "line": 5, + "column": 8, + "end_line": 5, + "end_column": 9 + }, + "help": "give each referenced relation a visible alias in this scope" + } + ] + } + ], + "compiled": { + "models/marts/customer_totals.sql": [ + "SELECT x.order_id", + "FROM __ref(\"stg_orders\") o", + "" + ], + "models/staging/stg_orders.sql": [ + "SELECT order_id, customer_id, amount, status", + "FROM __source(\"raw_orders\")", + "" + ] + }, + "manifest": { + "nodes": { + "model.failure_corpus.customer_totals": { + "database": null, + "schema": "main", + "name": "customer_totals", + "resource_type": "model", + "package_name": "failure_corpus", + "path": "models/marts/customer_totals.sql", + "original_file_path": "models/marts/customer_totals.sql", + "unique_id": "model.failure_corpus.customer_totals", + "fqn": [ + "failure_corpus", + "models", + "marts", + "customer_totals" + ], + "alias": "customer_totals", + "config": { + "enabled": true, + "alias": null, + "schema": null, + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Order totals per customer", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.customer_totals", + "depends_on": { + "macros": [], + "nodes": [ + "model.failure_corpus.stg_orders" + ] + } + }, + "model.failure_corpus.stg_orders": { + "database": null, + "schema": "main", + "name": "stg_orders", + "resource_type": "model", + "package_name": "failure_corpus", + "path": "models/staging/stg_orders.sql", + "original_file_path": "models/staging/stg_orders.sql", + "unique_id": "model.failure_corpus.stg_orders", + "fqn": [ + "failure_corpus", + "models", + "staging", + "stg_orders" + ], + "alias": "stg_orders", + "config": { + "enabled": true, + "alias": null, + "schema": null, + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Staged orders", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.stg_orders", + "depends_on": { + "macros": [], + "nodes": [ + "source.failure_corpus.raw_orders" + ] + } + } + }, + "sources": { + "source.failure_corpus.raw_orders": { + "database": null, + "schema": "", + "name": "raw_orders", + "resource_type": "source", + "package_name": "failure_corpus", + "path": "sources/raw.yml", + "original_file_path": "sources/raw.yml", + "unique_id": "source.failure_corpus.raw_orders", + "fqn": [ + "failure_corpus", + "raw_orders" + ], + "source_name": "raw_orders", + "source_description": "", + "loader": "", + "identifier": "raw_orders", + "quoting": {}, + "loaded_at_field": null, + "freshness": null, + "external": null, + "description": "Orders feed.", + "columns": { + "order_id": { + "name": "order_id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "customer_id": { + "name": "customer_id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "amount": { + "name": "amount", + "description": "", + "meta": {}, + "data_type": "DOUBLE", + "constraints": [], + "quote": null, + "tags": [] + }, + "status": { + "name": "status", + "description": "", + "meta": {}, + "data_type": "VARCHAR", + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": {}, + "source_meta": {}, + "tags": [], + "config": {}, + "patch_path": null, + "unrendered_config": {}, + "relation_name": null + } + }, + "macros": {}, + "exposures": {}, + "metrics": {}, + "groups": {}, + "selectors": {}, + "disabled": {}, + "parent_map": { + "model.failure_corpus.customer_totals": [ + "model.failure_corpus.stg_orders" + ], + "model.failure_corpus.stg_orders": [ + "source.failure_corpus.raw_orders" + ], + "source.failure_corpus.raw_orders": [] + }, + "group_map": {}, + "docs": {}, + "saved_queries": {}, + "semantic_models": {}, + "unit_tests": {} + } +} diff --git a/tests/goldens/compiler/failure/analysis-unsupported-cast.json b/tests/goldens/compiler/failure/analysis-unsupported-cast.json new file mode 100644 index 0000000000..413dbe4691 --- /dev/null +++ b/tests/goldens/compiler/failure/analysis-unsupported-cast.json @@ -0,0 +1,276 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "B218", + "message": "Invalid temporal literal", + "resource_type": "model", + "resource_name": "customer_totals", + "path": "models/marts/customer_totals.sql", + "line": 5, + "column": 21, + "location": { + "path": "models/marts/customer_totals.sql", + "line": 5, + "column": 21, + "end_line": 5, + "end_column": 38 + }, + "help": "choose a supported source-to-target cast for this dialect", + "notes": [ + "DATE '2026-13-45' is DATE" + ] + } + ] + } + ], + "compiled": { + "models/marts/customer_totals.sql": [ + "SELECT customer_id, DATE '2026-13-45' AS placed_on", + "FROM __ref(\"stg_orders\")", + "" + ], + "models/staging/stg_orders.sql": [ + "SELECT order_id, customer_id, amount, status", + "FROM __source(\"raw_orders\")", + "" + ] + }, + "manifest": { + "nodes": { + "model.failure_corpus.customer_totals": { + "database": null, + "schema": "main", + "name": "customer_totals", + "resource_type": "model", + "package_name": "failure_corpus", + "path": "models/marts/customer_totals.sql", + "original_file_path": "models/marts/customer_totals.sql", + "unique_id": "model.failure_corpus.customer_totals", + "fqn": [ + "failure_corpus", + "models", + "marts", + "customer_totals" + ], + "alias": "customer_totals", + "config": { + "enabled": true, + "alias": null, + "schema": null, + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Order totals per customer", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.customer_totals", + "depends_on": { + "macros": [], + "nodes": [ + "model.failure_corpus.stg_orders" + ] + } + }, + "model.failure_corpus.stg_orders": { + "database": null, + "schema": "main", + "name": "stg_orders", + "resource_type": "model", + "package_name": "failure_corpus", + "path": "models/staging/stg_orders.sql", + "original_file_path": "models/staging/stg_orders.sql", + "unique_id": "model.failure_corpus.stg_orders", + "fqn": [ + "failure_corpus", + "models", + "staging", + "stg_orders" + ], + "alias": "stg_orders", + "config": { + "enabled": true, + "alias": null, + "schema": null, + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Staged orders", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.stg_orders", + "depends_on": { + "macros": [], + "nodes": [ + "source.failure_corpus.raw_orders" + ] + } + } + }, + "sources": { + "source.failure_corpus.raw_orders": { + "database": null, + "schema": "", + "name": "raw_orders", + "resource_type": "source", + "package_name": "failure_corpus", + "path": "sources/raw.yml", + "original_file_path": "sources/raw.yml", + "unique_id": "source.failure_corpus.raw_orders", + "fqn": [ + "failure_corpus", + "raw_orders" + ], + "source_name": "raw_orders", + "source_description": "", + "loader": "", + "identifier": "raw_orders", + "quoting": {}, + "loaded_at_field": null, + "freshness": null, + "external": null, + "description": "Orders feed.", + "columns": { + "order_id": { + "name": "order_id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "customer_id": { + "name": "customer_id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "amount": { + "name": "amount", + "description": "", + "meta": {}, + "data_type": "DOUBLE", + "constraints": [], + "quote": null, + "tags": [] + }, + "status": { + "name": "status", + "description": "", + "meta": {}, + "data_type": "VARCHAR", + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": {}, + "source_meta": {}, + "tags": [], + "config": {}, + "patch_path": null, + "unrendered_config": {}, + "relation_name": null + } + }, + "macros": {}, + "exposures": {}, + "metrics": {}, + "groups": {}, + "selectors": {}, + "disabled": {}, + "parent_map": { + "model.failure_corpus.customer_totals": [ + "model.failure_corpus.stg_orders" + ], + "model.failure_corpus.stg_orders": [ + "source.failure_corpus.raw_orders" + ], + "source.failure_corpus.raw_orders": [] + }, + "group_map": {}, + "docs": {}, + "saved_queries": {}, + "semantic_models": {}, + "unit_tests": {} + } +} diff --git a/tests/goldens/compiler/failure/analysis-window-without-over.json b/tests/goldens/compiler/failure/analysis-window-without-over.json new file mode 100644 index 0000000000..b9762e9a4f --- /dev/null +++ b/tests/goldens/compiler/failure/analysis-window-without-over.json @@ -0,0 +1,271 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "B232", + "message": "Window function requires an OVER clause", + "resource_type": "model", + "resource_name": "customer_totals", + "path": "models/marts/customer_totals.sql", + "line": 5, + "column": 1, + "location": { + "path": "models/marts/customer_totals.sql", + "line": 5, + "column": 1 + }, + "help": "use this window function with a valid OVER clause and window context" + } + ] + } + ], + "compiled": { + "models/marts/customer_totals.sql": [ + "SELECT customer_id, ROW_NUMBER() AS position", + "FROM __ref(\"stg_orders\")", + "" + ], + "models/staging/stg_orders.sql": [ + "SELECT order_id, customer_id, amount, status", + "FROM __source(\"raw_orders\")", + "" + ] + }, + "manifest": { + "nodes": { + "model.failure_corpus.customer_totals": { + "database": null, + "schema": "main", + "name": "customer_totals", + "resource_type": "model", + "package_name": "failure_corpus", + "path": "models/marts/customer_totals.sql", + "original_file_path": "models/marts/customer_totals.sql", + "unique_id": "model.failure_corpus.customer_totals", + "fqn": [ + "failure_corpus", + "models", + "marts", + "customer_totals" + ], + "alias": "customer_totals", + "config": { + "enabled": true, + "alias": null, + "schema": null, + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Order totals per customer", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.customer_totals", + "depends_on": { + "macros": [], + "nodes": [ + "model.failure_corpus.stg_orders" + ] + } + }, + "model.failure_corpus.stg_orders": { + "database": null, + "schema": "main", + "name": "stg_orders", + "resource_type": "model", + "package_name": "failure_corpus", + "path": "models/staging/stg_orders.sql", + "original_file_path": "models/staging/stg_orders.sql", + "unique_id": "model.failure_corpus.stg_orders", + "fqn": [ + "failure_corpus", + "models", + "staging", + "stg_orders" + ], + "alias": "stg_orders", + "config": { + "enabled": true, + "alias": null, + "schema": null, + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Staged orders", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.stg_orders", + "depends_on": { + "macros": [], + "nodes": [ + "source.failure_corpus.raw_orders" + ] + } + } + }, + "sources": { + "source.failure_corpus.raw_orders": { + "database": null, + "schema": "", + "name": "raw_orders", + "resource_type": "source", + "package_name": "failure_corpus", + "path": "sources/raw.yml", + "original_file_path": "sources/raw.yml", + "unique_id": "source.failure_corpus.raw_orders", + "fqn": [ + "failure_corpus", + "raw_orders" + ], + "source_name": "raw_orders", + "source_description": "", + "loader": "", + "identifier": "raw_orders", + "quoting": {}, + "loaded_at_field": null, + "freshness": null, + "external": null, + "description": "Orders feed.", + "columns": { + "order_id": { + "name": "order_id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "customer_id": { + "name": "customer_id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "amount": { + "name": "amount", + "description": "", + "meta": {}, + "data_type": "DOUBLE", + "constraints": [], + "quote": null, + "tags": [] + }, + "status": { + "name": "status", + "description": "", + "meta": {}, + "data_type": "VARCHAR", + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": {}, + "source_meta": {}, + "tags": [], + "config": {}, + "patch_path": null, + "unrendered_config": {}, + "relation_name": null + } + }, + "macros": {}, + "exposures": {}, + "metrics": {}, + "groups": {}, + "selectors": {}, + "disabled": {}, + "parent_map": { + "model.failure_corpus.customer_totals": [ + "model.failure_corpus.stg_orders" + ], + "model.failure_corpus.stg_orders": [ + "source.failure_corpus.raw_orders" + ], + "source.failure_corpus.raw_orders": [] + }, + "group_map": {}, + "docs": {}, + "saved_queries": {}, + "semantic_models": {}, + "unit_tests": {} + } +} diff --git a/tests/goldens/compiler/failure/attached-audit-reads-dependant.json b/tests/goldens/compiler/failure/attached-audit-reads-dependant.json new file mode 100644 index 0000000000..e49035df7a --- /dev/null +++ b/tests/goldens/compiler/failure/attached-audit-reads-dependant.json @@ -0,0 +1,271 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "P005", + "message": "Audit 'matches_totals' on model 'stg_orders' reads model 'customer_totals', which depends on 'stg_orders'; an attached audit gates its target, so it cannot wait for a resource built from that target", + "resource_type": "model", + "resource_name": "stg_orders", + "path": "models/staging/_sqlbuild/_audits/generic/matches_totals.sql", + "help": "write a singular audit under audits/singular/ that references both 'stg_orders' and 'customer_totals'; it runs once both are built" + } + ] + } + ], + "compiled": { + "audits/generic/stg_orders/matches_totals.sql": [ + "SELECT *", + "FROM __ref(\"stg_orders\") o", + "WHERE o.customer_id NOT IN (SELECT customer_id FROM __ref(\"customer_totals\"))", + "" + ], + "models/marts/customer_totals.sql": [ + "SELECT customer_id, SUM(amount) AS total_amount", + "FROM __ref(\"stg_orders\")", + "GROUP BY customer_id", + "" + ], + "models/staging/stg_orders.sql": [ + "SELECT order_id, customer_id, amount, status", + "FROM __source(\"raw_orders\")", + "" + ] + }, + "manifest": { + "nodes": { + "model.failure_corpus.customer_totals": { + "database": null, + "schema": "main", + "name": "customer_totals", + "resource_type": "model", + "package_name": "failure_corpus", + "path": "models/marts/customer_totals.sql", + "original_file_path": "models/marts/customer_totals.sql", + "unique_id": "model.failure_corpus.customer_totals", + "fqn": [ + "failure_corpus", + "models", + "marts", + "customer_totals" + ], + "alias": "customer_totals", + "config": { + "enabled": true, + "alias": null, + "schema": null, + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Order totals per customer", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.customer_totals", + "depends_on": { + "macros": [], + "nodes": [ + "model.failure_corpus.stg_orders" + ] + } + }, + "model.failure_corpus.stg_orders": { + "database": null, + "schema": "main", + "name": "stg_orders", + "resource_type": "model", + "package_name": "failure_corpus", + "path": "models/staging/stg_orders.sql", + "original_file_path": "models/staging/stg_orders.sql", + "unique_id": "model.failure_corpus.stg_orders", + "fqn": [ + "failure_corpus", + "models", + "staging", + "stg_orders" + ], + "alias": "stg_orders", + "config": { + "enabled": true, + "alias": null, + "schema": null, + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Staged orders", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.stg_orders", + "depends_on": { + "macros": [], + "nodes": [ + "source.failure_corpus.raw_orders" + ] + } + } + }, + "sources": { + "source.failure_corpus.raw_orders": { + "database": null, + "schema": "", + "name": "raw_orders", + "resource_type": "source", + "package_name": "failure_corpus", + "path": "sources/raw.yml", + "original_file_path": "sources/raw.yml", + "unique_id": "source.failure_corpus.raw_orders", + "fqn": [ + "failure_corpus", + "raw_orders" + ], + "source_name": "raw_orders", + "source_description": "", + "loader": "", + "identifier": "raw_orders", + "quoting": {}, + "loaded_at_field": null, + "freshness": null, + "external": null, + "description": "Orders feed.", + "columns": { + "order_id": { + "name": "order_id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "customer_id": { + "name": "customer_id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "amount": { + "name": "amount", + "description": "", + "meta": {}, + "data_type": "DOUBLE", + "constraints": [], + "quote": null, + "tags": [] + }, + "status": { + "name": "status", + "description": "", + "meta": {}, + "data_type": "VARCHAR", + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": {}, + "source_meta": {}, + "tags": [], + "config": {}, + "patch_path": null, + "unrendered_config": {}, + "relation_name": null + } + }, + "macros": {}, + "exposures": {}, + "metrics": {}, + "groups": {}, + "selectors": {}, + "disabled": {}, + "parent_map": { + "model.failure_corpus.customer_totals": [ + "model.failure_corpus.stg_orders" + ], + "model.failure_corpus.stg_orders": [ + "source.failure_corpus.raw_orders" + ], + "source.failure_corpus.raw_orders": [] + }, + "group_map": {}, + "docs": {}, + "saved_queries": {}, + "semantic_models": {}, + "unit_tests": {} + } +} diff --git a/tests/goldens/compiler/failure/audit-parse.json b/tests/goldens/compiler/failure/audit-parse.json new file mode 100644 index 0000000000..2804df5d96 --- /dev/null +++ b/tests/goldens/compiler/failure/audit-parse.json @@ -0,0 +1,18 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "D004", + "message": "AUDIT(...) in '/models/marts/_sqlbuild/audits/singular/broken.sql' contains invalid SQLBuild header syntax: unexpected token 'name' without a value; quote values with spaces" + } + ] + } + ], + "compiled": {}, + "manifest": null +} diff --git a/tests/goldens/compiler/failure/audit-role-unsupported-entry.json b/tests/goldens/compiler/failure/audit-role-unsupported-entry.json new file mode 100644 index 0000000000..9d174e5294 --- /dev/null +++ b/tests/goldens/compiler/failure/audit-role-unsupported-entry.json @@ -0,0 +1,18 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "D013", + "message": "Unsupported entries in audits/: audits/at_least.sql; audit files must live in audits/generic/ or audits/singular/" + } + ] + } + ], + "compiled": {}, + "manifest": null +} diff --git a/tests/goldens/compiler/failure/audit-unknown-project-variable.json b/tests/goldens/compiler/failure/audit-unknown-project-variable.json new file mode 100644 index 0000000000..3525ad0f6b --- /dev/null +++ b/tests/goldens/compiler/failure/audit-unknown-project-variable.json @@ -0,0 +1,18 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "P001", + "message": "unknown project variable '@@floor_amount' in '/models/staging/_sqlbuild/_audits/generic/floor.sql'. Available vars: region" + } + ] + } + ], + "compiled": {}, + "manifest": null +} diff --git a/tests/goldens/compiler/failure/audit-variable-in-doubled-backticks.json b/tests/goldens/compiler/failure/audit-variable-in-doubled-backticks.json new file mode 100644 index 0000000000..3525ad0f6b --- /dev/null +++ b/tests/goldens/compiler/failure/audit-variable-in-doubled-backticks.json @@ -0,0 +1,18 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "P001", + "message": "unknown project variable '@@floor_amount' in '/models/staging/_sqlbuild/_audits/generic/floor.sql'. Available vars: region" + } + ] + } + ], + "compiled": {}, + "manifest": null +} diff --git a/tests/goldens/compiler/failure/built-in-audit-shadow.json b/tests/goldens/compiler/failure/built-in-audit-shadow.json new file mode 100644 index 0000000000..eda200b8ff --- /dev/null +++ b/tests/goldens/compiler/failure/built-in-audit-shadow.json @@ -0,0 +1,279 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "warning", + "code": "P003", + "message": "project audit 'not_null' overrides built-in audit 'not_null'", + "path": "models/staging/_sqlbuild/_audits/generic/not_null.sql", + "help": "rename the project audit if this override is unintended" + }, + { + "phase": "compile", + "severity": "error", + "code": "S010", + "message": "Unused local declaration 'audit:not_null' at models/staging/_sqlbuild/_audits/generic/not_null.sql; remove it or add a genuine runtime use", + "resource_type": "audit", + "resource_name": "not_null", + "path": "models/staging/_sqlbuild/_audits/generic/not_null.sql", + "line": 1, + "column": 1, + "location": { + "path": "models/staging/_sqlbuild/_audits/generic/not_null.sql", + "line": 1, + "column": 1 + } + } + ] + } + ], + "compiled": { + "models/marts/customer_totals.sql": [ + "SELECT customer_id, SUM(amount) AS total_amount", + "FROM __ref(\"stg_orders\")", + "GROUP BY customer_id", + "" + ], + "models/staging/stg_orders.sql": [ + "SELECT order_id, customer_id, amount, status", + "FROM __source(\"raw_orders\")", + "" + ] + }, + "manifest": { + "nodes": { + "model.failure_corpus.customer_totals": { + "database": null, + "schema": "main", + "name": "customer_totals", + "resource_type": "model", + "package_name": "failure_corpus", + "path": "models/marts/customer_totals.sql", + "original_file_path": "models/marts/customer_totals.sql", + "unique_id": "model.failure_corpus.customer_totals", + "fqn": [ + "failure_corpus", + "models", + "marts", + "customer_totals" + ], + "alias": "customer_totals", + "config": { + "enabled": true, + "alias": null, + "schema": null, + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Order totals per customer", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.customer_totals", + "depends_on": { + "macros": [], + "nodes": [ + "model.failure_corpus.stg_orders" + ] + } + }, + "model.failure_corpus.stg_orders": { + "database": null, + "schema": "main", + "name": "stg_orders", + "resource_type": "model", + "package_name": "failure_corpus", + "path": "models/staging/stg_orders.sql", + "original_file_path": "models/staging/stg_orders.sql", + "unique_id": "model.failure_corpus.stg_orders", + "fqn": [ + "failure_corpus", + "models", + "staging", + "stg_orders" + ], + "alias": "stg_orders", + "config": { + "enabled": true, + "alias": null, + "schema": null, + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Staged orders", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.stg_orders", + "depends_on": { + "macros": [], + "nodes": [ + "source.failure_corpus.raw_orders" + ] + } + } + }, + "sources": { + "source.failure_corpus.raw_orders": { + "database": null, + "schema": "", + "name": "raw_orders", + "resource_type": "source", + "package_name": "failure_corpus", + "path": "sources/raw.yml", + "original_file_path": "sources/raw.yml", + "unique_id": "source.failure_corpus.raw_orders", + "fqn": [ + "failure_corpus", + "raw_orders" + ], + "source_name": "raw_orders", + "source_description": "", + "loader": "", + "identifier": "raw_orders", + "quoting": {}, + "loaded_at_field": null, + "freshness": null, + "external": null, + "description": "Orders feed.", + "columns": { + "order_id": { + "name": "order_id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "customer_id": { + "name": "customer_id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "amount": { + "name": "amount", + "description": "", + "meta": {}, + "data_type": "DOUBLE", + "constraints": [], + "quote": null, + "tags": [] + }, + "status": { + "name": "status", + "description": "", + "meta": {}, + "data_type": "VARCHAR", + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": {}, + "source_meta": {}, + "tags": [], + "config": {}, + "patch_path": null, + "unrendered_config": {}, + "relation_name": null + } + }, + "macros": {}, + "exposures": {}, + "metrics": {}, + "groups": {}, + "selectors": {}, + "disabled": {}, + "parent_map": { + "model.failure_corpus.customer_totals": [ + "model.failure_corpus.stg_orders" + ], + "model.failure_corpus.stg_orders": [ + "source.failure_corpus.raw_orders" + ], + "source.failure_corpus.raw_orders": [] + }, + "group_map": {}, + "docs": {}, + "saved_queries": {}, + "semantic_models": {}, + "unit_tests": {} + } +} diff --git a/tests/goldens/compiler/failure/check-depends-on-check.json b/tests/goldens/compiler/failure/check-depends-on-check.json new file mode 100644 index 0000000000..79039c3ed2 --- /dev/null +++ b/tests/goldens/compiler/failure/check-depends-on-check.json @@ -0,0 +1,18 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "D007", + "message": "Check 'orders_checked' depends on another check; checks may depend on loaders, tasks, assets, models, sources, and seeds only" + } + ] + } + ], + "compiled": {}, + "manifest": null +} diff --git a/tests/goldens/compiler/failure/column-nullable-not-null.json b/tests/goldens/compiler/failure/column-nullable-not-null.json new file mode 100644 index 0000000000..967e247e56 --- /dev/null +++ b/tests/goldens/compiler/failure/column-nullable-not-null.json @@ -0,0 +1,19 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "P002", + "message": "models/staging/stg_orders.sql column 'order_id' cannot set nullable = true and audit not_null", + "help": "remove the not_null audit or set nullable = false" + } + ] + } + ], + "compiled": {}, + "manifest": null +} diff --git a/tests/goldens/compiler/failure/command-output-sink-unknown-provider.json b/tests/goldens/compiler/failure/command-output-sink-unknown-provider.json new file mode 100644 index 0000000000..9fb21e36bd --- /dev/null +++ b/tests/goldens/compiler/failure/command-output-sink-unknown-provider.json @@ -0,0 +1,18 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "D015", + "message": "Command-output sink 'publish_output' in sinks/output.py requires unknown provider 'destination_client'" + } + ] + } + ], + "compiled": {}, + "manifest": null +} diff --git a/tests/goldens/compiler/failure/commented-ref-argument.json b/tests/goldens/compiler/failure/commented-ref-argument.json new file mode 100644 index 0000000000..c4d7ff89d0 --- /dev/null +++ b/tests/goldens/compiler/failure/commented-ref-argument.json @@ -0,0 +1,270 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "P012", + "message": "__ref( /* upstream */ 'stg_orders') is not a valid __ref() call", + "resource_type": "model", + "resource_name": "customer_totals", + "path": "models/marts/customer_totals.sql", + "line": 6, + "column": 6, + "location": { + "path": "models/marts/customer_totals.sql", + "line": 6, + "column": 6, + "end_line": 6, + "end_column": 41 + }, + "help": "__ref() takes exactly one double-quoted name, with no comments or extra spaces inside the parentheses: __ref(\"stg_orders\")" + } + ] + } + ], + "compiled": { + "models/marts/customer_totals.sql": [ + "SELECT customer_id, SUM(amount) AS total_amount", + "FROM __ref( /* upstream */ 'stg_orders')", + "GROUP BY customer_id", + "" + ], + "models/staging/stg_orders.sql": [ + "SELECT order_id, customer_id, amount, status", + "FROM __source(\"raw_orders\")", + "" + ] + }, + "manifest": { + "nodes": { + "model.failure_corpus.customer_totals": { + "database": null, + "schema": "main", + "name": "customer_totals", + "resource_type": "model", + "package_name": "failure_corpus", + "path": "models/marts/customer_totals.sql", + "original_file_path": "models/marts/customer_totals.sql", + "unique_id": "model.failure_corpus.customer_totals", + "fqn": [ + "failure_corpus", + "models", + "marts", + "customer_totals" + ], + "alias": "customer_totals", + "config": { + "enabled": true, + "alias": null, + "schema": null, + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Order totals per customer", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.customer_totals", + "depends_on": { + "macros": [], + "nodes": [] + } + }, + "model.failure_corpus.stg_orders": { + "database": null, + "schema": "main", + "name": "stg_orders", + "resource_type": "model", + "package_name": "failure_corpus", + "path": "models/staging/stg_orders.sql", + "original_file_path": "models/staging/stg_orders.sql", + "unique_id": "model.failure_corpus.stg_orders", + "fqn": [ + "failure_corpus", + "models", + "staging", + "stg_orders" + ], + "alias": "stg_orders", + "config": { + "enabled": true, + "alias": null, + "schema": null, + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Staged orders", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.stg_orders", + "depends_on": { + "macros": [], + "nodes": [ + "source.failure_corpus.raw_orders" + ] + } + } + }, + "sources": { + "source.failure_corpus.raw_orders": { + "database": null, + "schema": "", + "name": "raw_orders", + "resource_type": "source", + "package_name": "failure_corpus", + "path": "sources/raw.yml", + "original_file_path": "sources/raw.yml", + "unique_id": "source.failure_corpus.raw_orders", + "fqn": [ + "failure_corpus", + "raw_orders" + ], + "source_name": "raw_orders", + "source_description": "", + "loader": "", + "identifier": "raw_orders", + "quoting": {}, + "loaded_at_field": null, + "freshness": null, + "external": null, + "description": "Orders feed.", + "columns": { + "order_id": { + "name": "order_id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "customer_id": { + "name": "customer_id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "amount": { + "name": "amount", + "description": "", + "meta": {}, + "data_type": "DOUBLE", + "constraints": [], + "quote": null, + "tags": [] + }, + "status": { + "name": "status", + "description": "", + "meta": {}, + "data_type": "VARCHAR", + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": {}, + "source_meta": {}, + "tags": [], + "config": {}, + "patch_path": null, + "unrendered_config": {}, + "relation_name": null + } + }, + "macros": {}, + "exposures": {}, + "metrics": {}, + "groups": {}, + "selectors": {}, + "disabled": {}, + "parent_map": { + "model.failure_corpus.customer_totals": [], + "model.failure_corpus.stg_orders": [ + "source.failure_corpus.raw_orders" + ], + "source.failure_corpus.raw_orders": [] + }, + "group_map": {}, + "docs": {}, + "saved_queries": {}, + "semantic_models": {}, + "unit_tests": {} + } +} diff --git a/tests/goldens/compiler/failure/comparison-type.json b/tests/goldens/compiler/failure/comparison-type.json new file mode 100644 index 0000000000..51fbf222ca --- /dev/null +++ b/tests/goldens/compiler/failure/comparison-type.json @@ -0,0 +1,278 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "B217", + "message": "Incompatible comparison between NUMERIC and DATE", + "resource_type": "model", + "resource_name": "customer_totals", + "path": "models/marts/customer_totals.sql", + "line": 7, + "column": 7, + "location": { + "path": "models/marts/customer_totals.sql", + "line": 7, + "column": 7, + "end_line": 7, + "end_column": 33 + }, + "help": "compare with a date, for example DATE '2026-04-01'", + "notes": [ + "amount is DOUBLE, DATE '2026-01-01' is DATE" + ] + } + ] + } + ], + "compiled": { + "models/marts/customer_totals.sql": [ + "SELECT customer_id, SUM(amount) AS total_amount", + "FROM __ref(\"stg_orders\")", + "WHERE amount = DATE '2026-01-01'", + "GROUP BY customer_id", + "" + ], + "models/staging/stg_orders.sql": [ + "SELECT order_id, customer_id, amount, status", + "FROM __source(\"raw_orders\")", + "" + ] + }, + "manifest": { + "nodes": { + "model.failure_corpus.customer_totals": { + "database": null, + "schema": "main", + "name": "customer_totals", + "resource_type": "model", + "package_name": "failure_corpus", + "path": "models/marts/customer_totals.sql", + "original_file_path": "models/marts/customer_totals.sql", + "unique_id": "model.failure_corpus.customer_totals", + "fqn": [ + "failure_corpus", + "models", + "marts", + "customer_totals" + ], + "alias": "customer_totals", + "config": { + "enabled": true, + "alias": null, + "schema": null, + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Order totals per customer", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.customer_totals", + "depends_on": { + "macros": [], + "nodes": [ + "model.failure_corpus.stg_orders" + ] + } + }, + "model.failure_corpus.stg_orders": { + "database": null, + "schema": "main", + "name": "stg_orders", + "resource_type": "model", + "package_name": "failure_corpus", + "path": "models/staging/stg_orders.sql", + "original_file_path": "models/staging/stg_orders.sql", + "unique_id": "model.failure_corpus.stg_orders", + "fqn": [ + "failure_corpus", + "models", + "staging", + "stg_orders" + ], + "alias": "stg_orders", + "config": { + "enabled": true, + "alias": null, + "schema": null, + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Staged orders", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.stg_orders", + "depends_on": { + "macros": [], + "nodes": [ + "source.failure_corpus.raw_orders" + ] + } + } + }, + "sources": { + "source.failure_corpus.raw_orders": { + "database": null, + "schema": "", + "name": "raw_orders", + "resource_type": "source", + "package_name": "failure_corpus", + "path": "sources/raw.yml", + "original_file_path": "sources/raw.yml", + "unique_id": "source.failure_corpus.raw_orders", + "fqn": [ + "failure_corpus", + "raw_orders" + ], + "source_name": "raw_orders", + "source_description": "", + "loader": "", + "identifier": "raw_orders", + "quoting": {}, + "loaded_at_field": null, + "freshness": null, + "external": null, + "description": "Orders feed.", + "columns": { + "order_id": { + "name": "order_id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "customer_id": { + "name": "customer_id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "amount": { + "name": "amount", + "description": "", + "meta": {}, + "data_type": "DOUBLE", + "constraints": [], + "quote": null, + "tags": [] + }, + "status": { + "name": "status", + "description": "", + "meta": {}, + "data_type": "VARCHAR", + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": {}, + "source_meta": {}, + "tags": [], + "config": {}, + "patch_path": null, + "unrendered_config": {}, + "relation_name": null + } + }, + "macros": {}, + "exposures": {}, + "metrics": {}, + "groups": {}, + "selectors": {}, + "disabled": {}, + "parent_map": { + "model.failure_corpus.customer_totals": [ + "model.failure_corpus.stg_orders" + ], + "model.failure_corpus.stg_orders": [ + "source.failure_corpus.raw_orders" + ], + "source.failure_corpus.raw_orders": [] + }, + "group_map": {}, + "docs": {}, + "saved_queries": {}, + "semantic_models": {}, + "unit_tests": {} + } +} diff --git a/tests/goldens/compiler/failure/config-bom.json b/tests/goldens/compiler/failure/config-bom.json new file mode 100644 index 0000000000..e2af44d5b4 --- /dev/null +++ b/tests/goldens/compiler/failure/config-bom.json @@ -0,0 +1,18 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "D001", + "message": "/sqlbuild_project.toml contains invalid TOML: Invalid statement (at line 1, column 1)" + } + ] + } + ], + "compiled": {}, + "manifest": null +} diff --git a/tests/goldens/compiler/failure/config-concurrency-without-capability.json b/tests/goldens/compiler/failure/config-concurrency-without-capability.json new file mode 100644 index 0000000000..72b28b51ed --- /dev/null +++ b/tests/goldens/compiler/failure/config-concurrency-without-capability.json @@ -0,0 +1,18 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "P001", + "message": "model 'stg_orders': batch_concurrency > 1 requires incremental_mode=microbatch" + } + ] + } + ], + "compiled": {}, + "manifest": null +} diff --git a/tests/goldens/compiler/failure/config-cursor-bound-outside-utc-years.json b/tests/goldens/compiler/failure/config-cursor-bound-outside-utc-years.json new file mode 100644 index 0000000000..b933cf121d --- /dev/null +++ b/tests/goldens/compiler/failure/config-cursor-bound-outside-utc-years.json @@ -0,0 +1,19 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "P001", + "message": "model 'stg_orders': cursor_start value '0001-01-01T00:00:00+01:00' falls outside years 1-9999 once converted to UTC", + "help": "keep cursor_start within years 1-9999 in UTC, add this to the MODEL header:\n MODEL (\n cursor_start '0001-01-01T00:00:00+00:00',\n ...\n );" + } + ] + } + ], + "compiled": {}, + "manifest": null +} diff --git a/tests/goldens/compiler/failure/config-cursor-bounds-reversed.json b/tests/goldens/compiler/failure/config-cursor-bounds-reversed.json new file mode 100644 index 0000000000..219673ce50 --- /dev/null +++ b/tests/goldens/compiler/failure/config-cursor-bounds-reversed.json @@ -0,0 +1,18 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "P001", + "message": "model 'stg_orders': cursor_start must be before exclusive cursor_end" + } + ] + } + ], + "compiled": {}, + "manifest": null +} diff --git a/tests/goldens/compiler/failure/config-equally-specific-path-defaults.json b/tests/goldens/compiler/failure/config-equally-specific-path-defaults.json new file mode 100644 index 0000000000..3055d01eee --- /dev/null +++ b/tests/goldens/compiler/failure/config-equally-specific-path-defaults.json @@ -0,0 +1,19 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "D007", + "message": "Model path 'staging/stg_orders.sql' matches equally specific path_defaults keys: '*/stg_orders.sql', 'staging/*'.", + "help": "Make one pattern narrower by adding literal or '*' path segments, or remove the overlap. Path-default selection never depends on declaration order." + } + ] + } + ], + "compiled": {}, + "manifest": null +} diff --git a/tests/goldens/compiler/failure/config-header-tags-not-list.json b/tests/goldens/compiler/failure/config-header-tags-not-list.json new file mode 100644 index 0000000000..e5bf494b08 --- /dev/null +++ b/tests/goldens/compiler/failure/config-header-tags-not-list.json @@ -0,0 +1,18 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "P001", + "message": "model 'stg_orders' tags must be a list" + } + ] + } + ], + "compiled": {}, + "manifest": null +} diff --git a/tests/goldens/compiler/failure/config-incremental-key-on-table.json b/tests/goldens/compiler/failure/config-incremental-key-on-table.json new file mode 100644 index 0000000000..27e5f2e87a --- /dev/null +++ b/tests/goldens/compiler/failure/config-incremental-key-on-table.json @@ -0,0 +1,18 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "P001", + "message": "model 'stg_orders': on_schema_change is only valid for incremental models" + } + ] + } + ], + "compiled": {}, + "manifest": null +} diff --git a/tests/goldens/compiler/failure/config-incremental-without-strategy.json b/tests/goldens/compiler/failure/config-incremental-without-strategy.json new file mode 100644 index 0000000000..4e834f3909 --- /dev/null +++ b/tests/goldens/compiler/failure/config-incremental-without-strategy.json @@ -0,0 +1,18 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "P001", + "message": "model 'stg_orders': incremental materialization requires incremental_strategy" + } + ] + } + ], + "compiled": {}, + "manifest": null +} diff --git a/tests/goldens/compiler/failure/config-macro-outside-hooks.json b/tests/goldens/compiler/failure/config-macro-outside-hooks.json new file mode 100644 index 0000000000..469e811060 --- /dev/null +++ b/tests/goldens/compiler/failure/config-macro-outside-hooks.json @@ -0,0 +1,18 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "P001", + "message": "model config field 'alias' does not allow macros" + } + ] + } + ], + "compiled": {}, + "manifest": null +} diff --git a/tests/goldens/compiler/failure/config-merge-exclusion-overlaps-key.json b/tests/goldens/compiler/failure/config-merge-exclusion-overlaps-key.json new file mode 100644 index 0000000000..0ce60aa3f5 --- /dev/null +++ b/tests/goldens/compiler/failure/config-merge-exclusion-overlaps-key.json @@ -0,0 +1,18 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "P001", + "message": "model 'stg_orders': merge_exclude_columns cannot include unique_key column(s): ORDER_ID" + } + ] + } + ], + "compiled": {}, + "manifest": null +} diff --git a/tests/goldens/compiler/failure/config-microbatch-without-strategy.json b/tests/goldens/compiler/failure/config-microbatch-without-strategy.json new file mode 100644 index 0000000000..bad4f2e413 --- /dev/null +++ b/tests/goldens/compiler/failure/config-microbatch-without-strategy.json @@ -0,0 +1,18 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "P001", + "message": "model 'stg_orders': incremental_mode=microbatch requires explicit microbatch_strategy (rolling_window or watermark)" + } + ] + } + ], + "compiled": {}, + "manifest": null +} diff --git a/tests/goldens/compiler/failure/config-migrate-force-on-table.json b/tests/goldens/compiler/failure/config-migrate-force-on-table.json new file mode 100644 index 0000000000..f8d07ab2cc --- /dev/null +++ b/tests/goldens/compiler/failure/config-migrate-force-on-table.json @@ -0,0 +1,19 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "P001", + "message": "model 'stg_orders': migrate_force is only valid for incremental and snapshot models; nothing is replaced when a 'table' model migrates, because tables and views are rebuilt under their new name", + "help": "remove migrate_force from the model header" + } + ] + } + ], + "compiled": {}, + "manifest": null +} diff --git a/tests/goldens/compiler/failure/config-old-name-view-without-duration.json b/tests/goldens/compiler/failure/config-old-name-view-without-duration.json new file mode 100644 index 0000000000..53627ed2b4 --- /dev/null +++ b/tests/goldens/compiler/failure/config-old-name-view-without-duration.json @@ -0,0 +1,19 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "P001", + "message": "model 'stg_orders': old_name_view must be a positive duration such as 7d, or false; got 'soon'", + "help": "write for example old_name_view 7d, or old_name_view false" + } + ] + } + ], + "compiled": {}, + "manifest": null +} diff --git a/tests/goldens/compiler/failure/config-placeholder-on-built-in-materialization.json b/tests/goldens/compiler/failure/config-placeholder-on-built-in-materialization.json new file mode 100644 index 0000000000..8158924744 --- /dev/null +++ b/tests/goldens/compiler/failure/config-placeholder-on-built-in-materialization.json @@ -0,0 +1,18 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "P001", + "message": "model 'stg_orders': @@@placeholders are only allowed on custom materializations" + } + ] + } + ], + "compiled": {}, + "manifest": null +} diff --git a/tests/goldens/compiler/failure/config-retention-not-whole-days.json b/tests/goldens/compiler/failure/config-retention-not-whole-days.json new file mode 100644 index 0000000000..4440debbaf --- /dev/null +++ b/tests/goldens/compiler/failure/config-retention-not-whole-days.json @@ -0,0 +1,18 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "P001", + "message": "model 'stg_orders': time_travel_retention must be a whole-day string like '7d', 'inherit', or 'disabled'" + } + ] + } + ], + "compiled": {}, + "manifest": null +} diff --git a/tests/goldens/compiler/failure/config-retention-on-view.json b/tests/goldens/compiler/failure/config-retention-on-view.json new file mode 100644 index 0000000000..b9daf2ab2a --- /dev/null +++ b/tests/goldens/compiler/failure/config-retention-on-view.json @@ -0,0 +1,18 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "P001", + "message": "model 'stg_orders': managed time_travel_retention is not valid for views; set time_travel_retention disabled" + } + ] + } + ], + "compiled": {}, + "manifest": null +} diff --git a/tests/goldens/compiler/failure/config-several-faults-reports-the-first.json b/tests/goldens/compiler/failure/config-several-faults-reports-the-first.json new file mode 100644 index 0000000000..477aa05f9c --- /dev/null +++ b/tests/goldens/compiler/failure/config-several-faults-reports-the-first.json @@ -0,0 +1,18 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "P001", + "message": "model 'stg_orders': table_type must be permanent, transient, or inherit" + } + ] + } + ], + "compiled": {}, + "manifest": null +} diff --git a/tests/goldens/compiler/failure/config-snapshot-without-unique-key.json b/tests/goldens/compiler/failure/config-snapshot-without-unique-key.json new file mode 100644 index 0000000000..431a0ffca7 --- /dev/null +++ b/tests/goldens/compiler/failure/config-snapshot-without-unique-key.json @@ -0,0 +1,18 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "P001", + "message": "model 'stg_orders': snapshot materialization requires unique_key" + } + ] + } + ], + "compiled": {}, + "manifest": null +} diff --git a/tests/goldens/compiler/failure/config-template-missing-environment-variable.json b/tests/goldens/compiler/failure/config-template-missing-environment-variable.json new file mode 100644 index 0000000000..12032faac2 --- /dev/null +++ b/tests/goldens/compiler/failure/config-template-missing-environment-variable.json @@ -0,0 +1,18 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "P001", + "message": "model config references missing ENV variable 'SQB_CORPUS_UNSET_SCHEMA'" + } + ] + } + ], + "compiled": {}, + "manifest": null +} diff --git a/tests/goldens/compiler/failure/config-template-unknown-namespace.json b/tests/goldens/compiler/failure/config-template-unknown-namespace.json new file mode 100644 index 0000000000..2700f29bcd --- /dev/null +++ b/tests/goldens/compiler/failure/config-template-unknown-namespace.json @@ -0,0 +1,18 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "P001", + "message": "model config references unsupported template namespace 'VAULT'" + } + ] + } + ], + "compiled": {}, + "manifest": null +} diff --git a/tests/goldens/compiler/failure/config-template-unknown-variable.json b/tests/goldens/compiler/failure/config-template-unknown-variable.json new file mode 100644 index 0000000000..72753ad8c6 --- /dev/null +++ b/tests/goldens/compiler/failure/config-template-unknown-variable.json @@ -0,0 +1,18 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "P001", + "message": "model config references unknown variable 'missing_region'" + } + ] + } + ], + "compiled": {}, + "manifest": null +} diff --git a/tests/goldens/compiler/failure/config-template-unsupported-function.json b/tests/goldens/compiler/failure/config-template-unsupported-function.json new file mode 100644 index 0000000000..598de35743 --- /dev/null +++ b/tests/goldens/compiler/failure/config-template-unsupported-function.json @@ -0,0 +1,18 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "P001", + "message": "model config references unsupported template function 'upper'" + } + ] + } + ], + "compiled": {}, + "manifest": null +} diff --git a/tests/goldens/compiler/failure/config-template-unterminated-string.json b/tests/goldens/compiler/failure/config-template-unterminated-string.json new file mode 100644 index 0000000000..928b630a46 --- /dev/null +++ b/tests/goldens/compiler/failure/config-template-unterminated-string.json @@ -0,0 +1,18 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "P001", + "message": "template expression has unterminated single-quoted string at position 9" + } + ] + } + ], + "compiled": {}, + "manifest": null +} diff --git a/tests/goldens/compiler/failure/config-template-wrong-argument-count.json b/tests/goldens/compiler/failure/config-template-wrong-argument-count.json new file mode 100644 index 0000000000..7987f0aa98 --- /dev/null +++ b/tests/goldens/compiler/failure/config-template-wrong-argument-count.json @@ -0,0 +1,18 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "P001", + "message": "model config if(...) expects 3 arguments" + } + ] + } + ], + "compiled": {}, + "manifest": null +} diff --git a/tests/goldens/compiler/failure/config-toml-syntax.json b/tests/goldens/compiler/failure/config-toml-syntax.json new file mode 100644 index 0000000000..3b8eaedf30 --- /dev/null +++ b/tests/goldens/compiler/failure/config-toml-syntax.json @@ -0,0 +1,18 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "D001", + "message": "/sqlbuild_project.toml contains invalid TOML: Expected ']' at the end of a table declaration (at line 6, column 10)" + } + ] + } + ], + "compiled": {}, + "manifest": null +} diff --git a/tests/goldens/compiler/failure/config-unknown-adapter.json b/tests/goldens/compiler/failure/config-unknown-adapter.json new file mode 100644 index 0000000000..cb53fda78f --- /dev/null +++ b/tests/goldens/compiler/failure/config-unknown-adapter.json @@ -0,0 +1,18 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "C601", + "message": "unknown adapter 'warehouse9000'. Available built-in adapters: bigquery, databricks, duckdb, motherduck, postgres, snowflake, sqlserver. Project-local adapters are discovered from adapters/**/*.py." + } + ] + } + ], + "compiled": {}, + "manifest": null +} diff --git a/tests/goldens/compiler/failure/config-unknown-contract.json b/tests/goldens/compiler/failure/config-unknown-contract.json new file mode 100644 index 0000000000..c92b833472 --- /dev/null +++ b/tests/goldens/compiler/failure/config-unknown-contract.json @@ -0,0 +1,18 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "P001", + "message": "model 'stg_orders': unknown contract 'strict'; valid values: enforced, none" + } + ] + } + ], + "compiled": {}, + "manifest": null +} diff --git a/tests/goldens/compiler/failure/config-unknown-default-key.json b/tests/goldens/compiler/failure/config-unknown-default-key.json new file mode 100644 index 0000000000..171f6252b1 --- /dev/null +++ b/tests/goldens/compiler/failure/config-unknown-default-key.json @@ -0,0 +1,19 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "D001", + "message": "/sqlbuild_project.toml [defaults] contains unknown key(s): description. Allowed keys: append_cursor_inclusive, batch_concurrency, batch_size, contract, cursor_end, cursor_future_action, cursor_future_max_distance, cursor_start, cursor_start_max_action, cursor_start_max_ahead, cursor_watermark_mode, database, full_refresh, function_database, function_schema, incremental_mode, incremental_strategy, lookback, materialized, max_microbatches, merge_exclude_columns, microbatch_strategy, replay_on_change, row_diff_exclude_columns, row_diff_sample_rows, row_diff_sample_seed, row_diff_tolerances, schema, seed_database, seed_schema, tags, unaccounted_partition_policy", + "help": "remove the key; see the reference for supported keys" + } + ] + } + ], + "compiled": {}, + "manifest": null +} diff --git a/tests/goldens/compiler/failure/config-unknown-materialization.json b/tests/goldens/compiler/failure/config-unknown-materialization.json new file mode 100644 index 0000000000..bb367cc1c7 --- /dev/null +++ b/tests/goldens/compiler/failure/config-unknown-materialization.json @@ -0,0 +1,18 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "P001", + "message": "model 'stg_orders': unknown materialization 'archive'; not a built-in type and no custom materialization with that name was discovered" + } + ] + } + ], + "compiled": {}, + "manifest": null +} diff --git a/tests/goldens/compiler/failure/config-unknown-model-reference.json b/tests/goldens/compiler/failure/config-unknown-model-reference.json new file mode 100644 index 0000000000..e30c3b4ce0 --- /dev/null +++ b/tests/goldens/compiler/failure/config-unknown-model-reference.json @@ -0,0 +1,18 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "P001", + "message": "Model file models/marts/customer_totals.sql references unknown model 'stg_refunds'" + } + ] + } + ], + "compiled": {}, + "manifest": null +} diff --git a/tests/goldens/compiler/failure/config-unknown-table-type.json b/tests/goldens/compiler/failure/config-unknown-table-type.json new file mode 100644 index 0000000000..477aa05f9c --- /dev/null +++ b/tests/goldens/compiler/failure/config-unknown-table-type.json @@ -0,0 +1,18 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "P001", + "message": "model 'stg_orders': table_type must be permanent, transient, or inherit" + } + ] + } + ], + "compiled": {}, + "manifest": null +} diff --git a/tests/goldens/compiler/failure/config-untyped-hook-entry.json b/tests/goldens/compiler/failure/config-untyped-hook-entry.json new file mode 100644 index 0000000000..9a008b6060 --- /dev/null +++ b/tests/goldens/compiler/failure/config-untyped-hook-entry.json @@ -0,0 +1,18 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "P001", + "message": "model 'stg_orders' pre_hooks entries must use typed inline_sql(...), sql(...), or python(...) hook syntax" + } + ] + } + ], + "compiled": {}, + "manifest": null +} diff --git a/tests/goldens/compiler/failure/config-watermark-limit-below-lookback.json b/tests/goldens/compiler/failure/config-watermark-limit-below-lookback.json new file mode 100644 index 0000000000..3964c7d569 --- /dev/null +++ b/tests/goldens/compiler/failure/config-watermark-limit-below-lookback.json @@ -0,0 +1,18 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "P001", + "message": "model 'stg_orders': max_microbatches 2 is below the ordinary lookback requirement of 4 batches" + } + ] + } + ], + "compiled": {}, + "manifest": null +} diff --git a/tests/goldens/compiler/failure/constant-header-nested-too-deep.json b/tests/goldens/compiler/failure/constant-header-nested-too-deep.json new file mode 100644 index 0000000000..766c4e1b73 --- /dev/null +++ b/tests/goldens/compiler/failure/constant-header-nested-too-deep.json @@ -0,0 +1,19 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "D013", + "message": "CONSTANT(...) in '/models/staging/_sqlbuild/_constants/statuses.sql:1' contains invalid SQLBuild header syntax: values nest deeper than 256 levels", + "help": "flatten the value so it nests at most 256 levels deep" + } + ] + } + ], + "compiled": {}, + "manifest": null +} diff --git a/tests/goldens/compiler/failure/context-variable-in-model-sql.json b/tests/goldens/compiler/failure/context-variable-in-model-sql.json new file mode 100644 index 0000000000..7f1b08f4cf --- /dev/null +++ b/tests/goldens/compiler/failure/context-variable-in-model-sql.json @@ -0,0 +1,18 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "P001", + "message": "SQL text in '/models/staging/stg_orders.sql' does not allow @@CTX templates" + } + ] + } + ], + "compiled": {}, + "manifest": null +} diff --git a/tests/goldens/compiler/failure/contract-dynamic-columns-unproven.json b/tests/goldens/compiler/failure/contract-dynamic-columns-unproven.json new file mode 100644 index 0000000000..fc9632ced7 --- /dev/null +++ b/tests/goldens/compiler/failure/contract-dynamic-columns-unproven.json @@ -0,0 +1,289 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "contract", + "severity": "error", + "code": "K011", + "message": "model 'customer_totals' dynamic column contract is not proven: dynamic pivot input does not have an authoritative, explicit schema", + "resource_type": "model", + "resource_name": "customer_totals", + "path": "models/marts/customer_totals.sql", + "help": "project one supported runtime-dynamic pivot through a wildcard output boundary and declare every generated family" + } + ] + } + ], + "compiled": { + "models/marts/customer_totals.sql": [ + "PIVOT __ref(\"stg_orders\")", + "ON status", + "USING MAX(amount)", + "GROUP BY customer_id", + "" + ], + "models/staging/stg_orders.sql": [ + "SELECT order_id, customer_id, amount, status", + "FROM __source(\"raw_orders\")", + "" + ] + }, + "manifest": { + "nodes": { + "model.failure_corpus.customer_totals": { + "database": null, + "schema": "main", + "name": "customer_totals", + "resource_type": "model", + "package_name": "failure_corpus", + "path": "models/marts/customer_totals.sql", + "original_file_path": "models/marts/customer_totals.sql", + "unique_id": "model.failure_corpus.customer_totals", + "fqn": [ + "failure_corpus", + "models", + "marts", + "customer_totals" + ], + "alias": "customer_totals", + "config": { + "enabled": true, + "alias": null, + "schema": null, + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "table", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Order totals per customer", + "columns": { + "customer_id": { + "name": "customer_id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": { + "sqlbuild": { + "dynamic_columns": [ + { + "name": "status_amounts", + "pivot_column": "status", + "value_column": "amount", + "aggregate": "MAX", + "type": "DOUBLE", + "name_pattern": null + } + ] + } + }, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.customer_totals", + "depends_on": { + "macros": [], + "nodes": [ + "model.failure_corpus.stg_orders" + ] + } + }, + "model.failure_corpus.stg_orders": { + "database": null, + "schema": "main", + "name": "stg_orders", + "resource_type": "model", + "package_name": "failure_corpus", + "path": "models/staging/stg_orders.sql", + "original_file_path": "models/staging/stg_orders.sql", + "unique_id": "model.failure_corpus.stg_orders", + "fqn": [ + "failure_corpus", + "models", + "staging", + "stg_orders" + ], + "alias": "stg_orders", + "config": { + "enabled": true, + "alias": null, + "schema": null, + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Staged orders", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.stg_orders", + "depends_on": { + "macros": [], + "nodes": [ + "source.failure_corpus.raw_orders" + ] + } + } + }, + "sources": { + "source.failure_corpus.raw_orders": { + "database": null, + "schema": "", + "name": "raw_orders", + "resource_type": "source", + "package_name": "failure_corpus", + "path": "sources/raw.yml", + "original_file_path": "sources/raw.yml", + "unique_id": "source.failure_corpus.raw_orders", + "fqn": [ + "failure_corpus", + "raw_orders" + ], + "source_name": "raw_orders", + "source_description": "", + "loader": "", + "identifier": "raw_orders", + "quoting": {}, + "loaded_at_field": null, + "freshness": null, + "external": null, + "description": "Orders feed.", + "columns": { + "order_id": { + "name": "order_id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "customer_id": { + "name": "customer_id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "amount": { + "name": "amount", + "description": "", + "meta": {}, + "data_type": "DOUBLE", + "constraints": [], + "quote": null, + "tags": [] + }, + "status": { + "name": "status", + "description": "", + "meta": {}, + "data_type": "VARCHAR", + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": {}, + "source_meta": {}, + "tags": [], + "config": {}, + "patch_path": null, + "unrendered_config": {}, + "relation_name": null + } + }, + "macros": {}, + "exposures": {}, + "metrics": {}, + "groups": {}, + "selectors": {}, + "disabled": {}, + "parent_map": { + "model.failure_corpus.customer_totals": [ + "model.failure_corpus.stg_orders" + ], + "model.failure_corpus.stg_orders": [ + "source.failure_corpus.raw_orders" + ], + "source.failure_corpus.raw_orders": [] + }, + "group_map": {}, + "docs": {}, + "saved_queries": {}, + "semantic_models": {}, + "unit_tests": {} + } +} diff --git a/tests/goldens/compiler/failure/contract-dynamic-columns-unsupported-dialect.json b/tests/goldens/compiler/failure/contract-dynamic-columns-unsupported-dialect.json new file mode 100644 index 0000000000..2d8a604087 --- /dev/null +++ b/tests/goldens/compiler/failure/contract-dynamic-columns-unsupported-dialect.json @@ -0,0 +1,288 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "contract", + "severity": "error", + "code": "K011", + "message": "model 'customer_totals' dynamic column contract is not proven: adapter dialect 'postgresql' does not support compiler-proven dynamic pivots", + "resource_type": "model", + "resource_name": "customer_totals", + "path": "models/marts/customer_totals.sql", + "help": "project one supported runtime-dynamic pivot through a wildcard output boundary and declare every generated family" + } + ] + } + ], + "compiled": { + "models/marts/customer_totals.sql": [ + "SELECT customer_id, MAX(amount) AS placed", + "FROM __ref(\"stg_orders\")", + "GROUP BY customer_id", + "" + ], + "models/staging/stg_orders.sql": [ + "SELECT order_id, customer_id, amount, status", + "FROM __source(\"raw_orders\")", + "" + ] + }, + "manifest": { + "nodes": { + "model.failure_corpus.customer_totals": { + "database": null, + "schema": "public", + "name": "customer_totals", + "resource_type": "model", + "package_name": "failure_corpus", + "path": "models/marts/customer_totals.sql", + "original_file_path": "models/marts/customer_totals.sql", + "unique_id": "model.failure_corpus.customer_totals", + "fqn": [ + "failure_corpus", + "models", + "marts", + "customer_totals" + ], + "alias": "customer_totals", + "config": { + "enabled": true, + "alias": null, + "schema": null, + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "table", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Order totals per customer", + "columns": { + "customer_id": { + "name": "customer_id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": { + "sqlbuild": { + "dynamic_columns": [ + { + "name": "status_amounts", + "pivot_column": "status", + "value_column": "amount", + "aggregate": "MAX", + "type": "DOUBLE", + "name_pattern": null + } + ] + } + }, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "public.customer_totals", + "depends_on": { + "macros": [], + "nodes": [ + "model.failure_corpus.stg_orders" + ] + } + }, + "model.failure_corpus.stg_orders": { + "database": null, + "schema": "public", + "name": "stg_orders", + "resource_type": "model", + "package_name": "failure_corpus", + "path": "models/staging/stg_orders.sql", + "original_file_path": "models/staging/stg_orders.sql", + "unique_id": "model.failure_corpus.stg_orders", + "fqn": [ + "failure_corpus", + "models", + "staging", + "stg_orders" + ], + "alias": "stg_orders", + "config": { + "enabled": true, + "alias": null, + "schema": null, + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Staged orders", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "public.stg_orders", + "depends_on": { + "macros": [], + "nodes": [ + "source.failure_corpus.raw_orders" + ] + } + } + }, + "sources": { + "source.failure_corpus.raw_orders": { + "database": null, + "schema": "", + "name": "raw_orders", + "resource_type": "source", + "package_name": "failure_corpus", + "path": "sources/raw.yml", + "original_file_path": "sources/raw.yml", + "unique_id": "source.failure_corpus.raw_orders", + "fqn": [ + "failure_corpus", + "raw_orders" + ], + "source_name": "raw_orders", + "source_description": "", + "loader": "", + "identifier": "raw_orders", + "quoting": {}, + "loaded_at_field": null, + "freshness": null, + "external": null, + "description": "Orders feed.", + "columns": { + "order_id": { + "name": "order_id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "customer_id": { + "name": "customer_id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "amount": { + "name": "amount", + "description": "", + "meta": {}, + "data_type": "DOUBLE", + "constraints": [], + "quote": null, + "tags": [] + }, + "status": { + "name": "status", + "description": "", + "meta": {}, + "data_type": "VARCHAR", + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": {}, + "source_meta": {}, + "tags": [], + "config": {}, + "patch_path": null, + "unrendered_config": {}, + "relation_name": null + } + }, + "macros": {}, + "exposures": {}, + "metrics": {}, + "groups": {}, + "selectors": {}, + "disabled": {}, + "parent_map": { + "model.failure_corpus.customer_totals": [ + "model.failure_corpus.stg_orders" + ], + "model.failure_corpus.stg_orders": [ + "source.failure_corpus.raw_orders" + ], + "source.failure_corpus.raw_orders": [] + }, + "group_map": {}, + "docs": {}, + "saved_queries": {}, + "semantic_models": {}, + "unit_tests": {} + } +} diff --git a/tests/goldens/compiler/failure/contract-extra-column.json b/tests/goldens/compiler/failure/contract-extra-column.json new file mode 100644 index 0000000000..b67d98b5f4 --- /dev/null +++ b/tests/goldens/compiler/failure/contract-extra-column.json @@ -0,0 +1,303 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "contract", + "severity": "error", + "code": "K005", + "message": "column 'status' is not declared in enforced contract for model 'stg_orders'", + "resource_type": "model", + "resource_name": "stg_orders", + "column_name": "status", + "path": "models/staging/stg_orders.sql", + "line": 11, + "column": 39, + "location": { + "path": "models/staging/stg_orders.sql", + "line": 11, + "column": 39, + "end_line": 11, + "end_column": 45 + }, + "help": "add the column to MODEL(columns) or remove it from the SELECT list" + } + ] + } + ], + "compiled": { + "models/marts/customer_totals.sql": [ + "SELECT customer_id, SUM(amount) AS total_amount", + "FROM __ref(\"stg_orders\")", + "GROUP BY customer_id", + "" + ], + "models/staging/stg_orders.sql": [ + "SELECT order_id, customer_id, amount, status", + "FROM __source(\"raw_orders\")", + "" + ] + }, + "manifest": { + "nodes": { + "model.failure_corpus.customer_totals": { + "database": null, + "schema": "main", + "name": "customer_totals", + "resource_type": "model", + "package_name": "failure_corpus", + "path": "models/marts/customer_totals.sql", + "original_file_path": "models/marts/customer_totals.sql", + "unique_id": "model.failure_corpus.customer_totals", + "fqn": [ + "failure_corpus", + "models", + "marts", + "customer_totals" + ], + "alias": "customer_totals", + "config": { + "enabled": true, + "alias": null, + "schema": null, + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Order totals per customer", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.customer_totals", + "depends_on": { + "macros": [], + "nodes": [ + "model.failure_corpus.stg_orders" + ] + } + }, + "model.failure_corpus.stg_orders": { + "database": null, + "schema": "main", + "name": "stg_orders", + "resource_type": "model", + "package_name": "failure_corpus", + "path": "models/staging/stg_orders.sql", + "original_file_path": "models/staging/stg_orders.sql", + "unique_id": "model.failure_corpus.stg_orders", + "fqn": [ + "failure_corpus", + "models", + "staging", + "stg_orders" + ], + "alias": "stg_orders", + "config": { + "enabled": true, + "alias": null, + "schema": null, + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Staged orders", + "columns": { + "order_id": { + "name": "order_id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "customer_id": { + "name": "customer_id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "amount": { + "name": "amount", + "description": "", + "meta": {}, + "data_type": "DOUBLE", + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.stg_orders", + "depends_on": { + "macros": [], + "nodes": [ + "source.failure_corpus.raw_orders" + ] + } + } + }, + "sources": { + "source.failure_corpus.raw_orders": { + "database": null, + "schema": "", + "name": "raw_orders", + "resource_type": "source", + "package_name": "failure_corpus", + "path": "sources/raw.yml", + "original_file_path": "sources/raw.yml", + "unique_id": "source.failure_corpus.raw_orders", + "fqn": [ + "failure_corpus", + "raw_orders" + ], + "source_name": "raw_orders", + "source_description": "", + "loader": "", + "identifier": "raw_orders", + "quoting": {}, + "loaded_at_field": null, + "freshness": null, + "external": null, + "description": "Orders feed.", + "columns": { + "order_id": { + "name": "order_id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "customer_id": { + "name": "customer_id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "amount": { + "name": "amount", + "description": "", + "meta": {}, + "data_type": "DOUBLE", + "constraints": [], + "quote": null, + "tags": [] + }, + "status": { + "name": "status", + "description": "", + "meta": {}, + "data_type": "VARCHAR", + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": {}, + "source_meta": {}, + "tags": [], + "config": {}, + "patch_path": null, + "unrendered_config": {}, + "relation_name": null + } + }, + "macros": {}, + "exposures": {}, + "metrics": {}, + "groups": {}, + "selectors": {}, + "disabled": {}, + "parent_map": { + "model.failure_corpus.customer_totals": [ + "model.failure_corpus.stg_orders" + ], + "model.failure_corpus.stg_orders": [ + "source.failure_corpus.raw_orders" + ], + "source.failure_corpus.raw_orders": [] + }, + "group_map": {}, + "docs": {}, + "saved_queries": {}, + "semantic_models": {}, + "unit_tests": {} + } +} diff --git a/tests/goldens/compiler/failure/contract-immediate-promotion.json b/tests/goldens/compiler/failure/contract-immediate-promotion.json new file mode 100644 index 0000000000..e6761a433d --- /dev/null +++ b/tests/goldens/compiler/failure/contract-immediate-promotion.json @@ -0,0 +1,302 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "contract", + "severity": "error", + "code": "K011", + "message": "model 'stg_orders': contract enforced requires staged table promotion; immediate table promotion cannot validate runtime output before target mutation", + "resource_type": "model", + "resource_name": "stg_orders", + "path": "models/staging/stg_orders.sql", + "help": "sqlbuild_project.toml sets [settings] table_promotion_mode = \"immediate\"; enforced contracts are validated in a staging table before promotion\n = help: remove that line to use the default staged promotion, or set this in sqlbuild_project.toml:\n [settings]\n table_promotion_mode = \"staged\"\n = help: or remove the model's enforced contract: `contract enforced` in its MODEL header, or `contract = \"enforced\"` in the [defaults] or [path_defaults] entry that applies to it" + } + ] + } + ], + "compiled": { + "models/marts/customer_totals.sql": [ + "SELECT customer_id, SUM(amount) AS total_amount", + "FROM __ref(\"stg_orders\")", + "GROUP BY customer_id", + "" + ], + "models/staging/stg_orders.sql": [ + "SELECT order_id, customer_id, amount, status", + "FROM __source(\"raw_orders\")", + "" + ] + }, + "manifest": { + "nodes": { + "model.failure_corpus.customer_totals": { + "database": null, + "schema": "main", + "name": "customer_totals", + "resource_type": "model", + "package_name": "failure_corpus", + "path": "models/marts/customer_totals.sql", + "original_file_path": "models/marts/customer_totals.sql", + "unique_id": "model.failure_corpus.customer_totals", + "fqn": [ + "failure_corpus", + "models", + "marts", + "customer_totals" + ], + "alias": "customer_totals", + "config": { + "enabled": true, + "alias": null, + "schema": null, + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Order totals per customer", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.customer_totals", + "depends_on": { + "macros": [], + "nodes": [ + "model.failure_corpus.stg_orders" + ] + } + }, + "model.failure_corpus.stg_orders": { + "database": null, + "schema": "main", + "name": "stg_orders", + "resource_type": "model", + "package_name": "failure_corpus", + "path": "models/staging/stg_orders.sql", + "original_file_path": "models/staging/stg_orders.sql", + "unique_id": "model.failure_corpus.stg_orders", + "fqn": [ + "failure_corpus", + "models", + "staging", + "stg_orders" + ], + "alias": "stg_orders", + "config": { + "enabled": true, + "alias": null, + "schema": null, + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "table", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Staged orders", + "columns": { + "order_id": { + "name": "order_id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "customer_id": { + "name": "customer_id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "amount": { + "name": "amount", + "description": "", + "meta": {}, + "data_type": "DOUBLE", + "constraints": [], + "quote": null, + "tags": [] + }, + "status": { + "name": "status", + "description": "", + "meta": {}, + "data_type": "VARCHAR", + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.stg_orders", + "depends_on": { + "macros": [], + "nodes": [ + "source.failure_corpus.raw_orders" + ] + } + } + }, + "sources": { + "source.failure_corpus.raw_orders": { + "database": null, + "schema": "", + "name": "raw_orders", + "resource_type": "source", + "package_name": "failure_corpus", + "path": "sources/raw.yml", + "original_file_path": "sources/raw.yml", + "unique_id": "source.failure_corpus.raw_orders", + "fqn": [ + "failure_corpus", + "raw_orders" + ], + "source_name": "raw_orders", + "source_description": "", + "loader": "", + "identifier": "raw_orders", + "quoting": {}, + "loaded_at_field": null, + "freshness": null, + "external": null, + "description": "Orders feed.", + "columns": { + "order_id": { + "name": "order_id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "customer_id": { + "name": "customer_id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "amount": { + "name": "amount", + "description": "", + "meta": {}, + "data_type": "DOUBLE", + "constraints": [], + "quote": null, + "tags": [] + }, + "status": { + "name": "status", + "description": "", + "meta": {}, + "data_type": "VARCHAR", + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": {}, + "source_meta": {}, + "tags": [], + "config": {}, + "patch_path": null, + "unrendered_config": {}, + "relation_name": null + } + }, + "macros": {}, + "exposures": {}, + "metrics": {}, + "groups": {}, + "selectors": {}, + "disabled": {}, + "parent_map": { + "model.failure_corpus.customer_totals": [ + "model.failure_corpus.stg_orders" + ], + "model.failure_corpus.stg_orders": [ + "source.failure_corpus.raw_orders" + ], + "source.failure_corpus.raw_orders": [] + }, + "group_map": {}, + "docs": {}, + "saved_queries": {}, + "semantic_models": {}, + "unit_tests": {} + } +} diff --git a/tests/goldens/compiler/failure/contract-missing-column.json b/tests/goldens/compiler/failure/contract-missing-column.json new file mode 100644 index 0000000000..4569eb6cb6 --- /dev/null +++ b/tests/goldens/compiler/failure/contract-missing-column.json @@ -0,0 +1,321 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "contract", + "severity": "error", + "code": "K001", + "message": "enforced contract column 'discount' was not found in statically inferred output for model 'stg_orders'", + "resource_type": "model", + "resource_name": "stg_orders", + "column_name": "discount", + "path": "models/staging/stg_orders.sql", + "line": 9, + "column": 5, + "location": { + "path": "models/staging/stg_orders.sql", + "line": 9, + "column": 5, + "end_line": 9, + "end_column": 13 + }, + "help": "add discount to the SELECT list or correct MODEL(columns (...)); validation is active because this model declares contract enforced" + } + ] + } + ], + "compiled": { + "models/marts/customer_totals.sql": [ + "SELECT customer_id, SUM(amount) AS total_amount", + "FROM __ref(\"stg_orders\")", + "GROUP BY customer_id", + "" + ], + "models/staging/stg_orders.sql": [ + "SELECT order_id, customer_id, amount, status", + "FROM __source(\"raw_orders\")", + "" + ] + }, + "manifest": { + "nodes": { + "model.failure_corpus.customer_totals": { + "database": null, + "schema": "main", + "name": "customer_totals", + "resource_type": "model", + "package_name": "failure_corpus", + "path": "models/marts/customer_totals.sql", + "original_file_path": "models/marts/customer_totals.sql", + "unique_id": "model.failure_corpus.customer_totals", + "fqn": [ + "failure_corpus", + "models", + "marts", + "customer_totals" + ], + "alias": "customer_totals", + "config": { + "enabled": true, + "alias": null, + "schema": null, + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Order totals per customer", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.customer_totals", + "depends_on": { + "macros": [], + "nodes": [ + "model.failure_corpus.stg_orders" + ] + } + }, + "model.failure_corpus.stg_orders": { + "database": null, + "schema": "main", + "name": "stg_orders", + "resource_type": "model", + "package_name": "failure_corpus", + "path": "models/staging/stg_orders.sql", + "original_file_path": "models/staging/stg_orders.sql", + "unique_id": "model.failure_corpus.stg_orders", + "fqn": [ + "failure_corpus", + "models", + "staging", + "stg_orders" + ], + "alias": "stg_orders", + "config": { + "enabled": true, + "alias": null, + "schema": null, + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Staged orders", + "columns": { + "order_id": { + "name": "order_id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "customer_id": { + "name": "customer_id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "amount": { + "name": "amount", + "description": "", + "meta": {}, + "data_type": "DOUBLE", + "constraints": [], + "quote": null, + "tags": [] + }, + "status": { + "name": "status", + "description": "", + "meta": {}, + "data_type": "VARCHAR", + "constraints": [], + "quote": null, + "tags": [] + }, + "discount": { + "name": "discount", + "description": "", + "meta": {}, + "data_type": "DOUBLE", + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.stg_orders", + "depends_on": { + "macros": [], + "nodes": [ + "source.failure_corpus.raw_orders" + ] + } + } + }, + "sources": { + "source.failure_corpus.raw_orders": { + "database": null, + "schema": "", + "name": "raw_orders", + "resource_type": "source", + "package_name": "failure_corpus", + "path": "sources/raw.yml", + "original_file_path": "sources/raw.yml", + "unique_id": "source.failure_corpus.raw_orders", + "fqn": [ + "failure_corpus", + "raw_orders" + ], + "source_name": "raw_orders", + "source_description": "", + "loader": "", + "identifier": "raw_orders", + "quoting": {}, + "loaded_at_field": null, + "freshness": null, + "external": null, + "description": "Orders feed.", + "columns": { + "order_id": { + "name": "order_id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "customer_id": { + "name": "customer_id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "amount": { + "name": "amount", + "description": "", + "meta": {}, + "data_type": "DOUBLE", + "constraints": [], + "quote": null, + "tags": [] + }, + "status": { + "name": "status", + "description": "", + "meta": {}, + "data_type": "VARCHAR", + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": {}, + "source_meta": {}, + "tags": [], + "config": {}, + "patch_path": null, + "unrendered_config": {}, + "relation_name": null + } + }, + "macros": {}, + "exposures": {}, + "metrics": {}, + "groups": {}, + "selectors": {}, + "disabled": {}, + "parent_map": { + "model.failure_corpus.customer_totals": [ + "model.failure_corpus.stg_orders" + ], + "model.failure_corpus.stg_orders": [ + "source.failure_corpus.raw_orders" + ], + "source.failure_corpus.raw_orders": [] + }, + "group_map": {}, + "docs": {}, + "saved_queries": {}, + "semantic_models": {}, + "unit_tests": {} + } +} diff --git a/tests/goldens/compiler/failure/contract-nullability.json b/tests/goldens/compiler/failure/contract-nullability.json new file mode 100644 index 0000000000..b76fd7bb4e --- /dev/null +++ b/tests/goldens/compiler/failure/contract-nullability.json @@ -0,0 +1,325 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "contract", + "severity": "error", + "code": "K004", + "message": "column 'customer_id' is declared non-null but may be nullable", + "resource_type": "model", + "resource_name": "stg_orders", + "column_name": "customer_id", + "path": "models/staging/stg_orders.sql", + "line": 6, + "column": 5, + "location": { + "path": "models/staging/stg_orders.sql", + "line": 6, + "column": 5, + "end_line": 6, + "end_column": 16 + }, + "related_locations": [ + { + "label": "output", + "location": { + "path": "models/staging/stg_orders.sql", + "line": 12, + "column": 18, + "end_line": 12, + "end_column": 54 + }, + "message": "output expression proven nullable" + } + ], + "help": "use COALESCE, filter nulls explicitly, or remove the non-null contract" + } + ] + } + ], + "compiled": { + "models/marts/customer_totals.sql": [ + "SELECT customer_id, SUM(amount) AS total_amount", + "FROM __ref(\"stg_orders\")", + "GROUP BY customer_id", + "" + ], + "models/staging/stg_orders.sql": [ + "SELECT order_id, CAST(NULL AS INTEGER) AS customer_id, amount, status", + "FROM __source(\"raw_orders\")", + "" + ] + }, + "manifest": { + "nodes": { + "model.failure_corpus.customer_totals": { + "database": null, + "schema": "main", + "name": "customer_totals", + "resource_type": "model", + "package_name": "failure_corpus", + "path": "models/marts/customer_totals.sql", + "original_file_path": "models/marts/customer_totals.sql", + "unique_id": "model.failure_corpus.customer_totals", + "fqn": [ + "failure_corpus", + "models", + "marts", + "customer_totals" + ], + "alias": "customer_totals", + "config": { + "enabled": true, + "alias": null, + "schema": null, + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Order totals per customer", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.customer_totals", + "depends_on": { + "macros": [], + "nodes": [ + "model.failure_corpus.stg_orders" + ] + } + }, + "model.failure_corpus.stg_orders": { + "database": null, + "schema": "main", + "name": "stg_orders", + "resource_type": "model", + "package_name": "failure_corpus", + "path": "models/staging/stg_orders.sql", + "original_file_path": "models/staging/stg_orders.sql", + "unique_id": "model.failure_corpus.stg_orders", + "fqn": [ + "failure_corpus", + "models", + "staging", + "stg_orders" + ], + "alias": "stg_orders", + "config": { + "enabled": true, + "alias": null, + "schema": null, + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Staged orders", + "columns": { + "order_id": { + "name": "order_id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "customer_id": { + "name": "customer_id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "amount": { + "name": "amount", + "description": "", + "meta": {}, + "data_type": "DOUBLE", + "constraints": [], + "quote": null, + "tags": [] + }, + "status": { + "name": "status", + "description": "", + "meta": {}, + "data_type": "VARCHAR", + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.stg_orders", + "depends_on": { + "macros": [], + "nodes": [ + "source.failure_corpus.raw_orders" + ] + } + } + }, + "sources": { + "source.failure_corpus.raw_orders": { + "database": null, + "schema": "", + "name": "raw_orders", + "resource_type": "source", + "package_name": "failure_corpus", + "path": "sources/raw.yml", + "original_file_path": "sources/raw.yml", + "unique_id": "source.failure_corpus.raw_orders", + "fqn": [ + "failure_corpus", + "raw_orders" + ], + "source_name": "raw_orders", + "source_description": "", + "loader": "", + "identifier": "raw_orders", + "quoting": {}, + "loaded_at_field": null, + "freshness": null, + "external": null, + "description": "Orders feed.", + "columns": { + "order_id": { + "name": "order_id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "customer_id": { + "name": "customer_id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "amount": { + "name": "amount", + "description": "", + "meta": {}, + "data_type": "DOUBLE", + "constraints": [], + "quote": null, + "tags": [] + }, + "status": { + "name": "status", + "description": "", + "meta": {}, + "data_type": "VARCHAR", + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": {}, + "source_meta": {}, + "tags": [], + "config": {}, + "patch_path": null, + "unrendered_config": {}, + "relation_name": null + } + }, + "macros": {}, + "exposures": {}, + "metrics": {}, + "groups": {}, + "selectors": {}, + "disabled": {}, + "parent_map": { + "model.failure_corpus.customer_totals": [ + "model.failure_corpus.stg_orders" + ], + "model.failure_corpus.stg_orders": [ + "source.failure_corpus.raw_orders" + ], + "source.failure_corpus.raw_orders": [] + }, + "group_map": {}, + "docs": {}, + "saved_queries": {}, + "semantic_models": {}, + "unit_tests": {} + } +} diff --git a/tests/goldens/compiler/failure/contract-type-mismatch.json b/tests/goldens/compiler/failure/contract-type-mismatch.json new file mode 100644 index 0000000000..b02987306e --- /dev/null +++ b/tests/goldens/compiler/failure/contract-type-mismatch.json @@ -0,0 +1,325 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "contract", + "severity": "error", + "code": "K002", + "message": "column 'order_id' inferred as INTEGER but declared type is VARCHAR", + "resource_type": "model", + "resource_name": "stg_orders", + "column_name": "order_id", + "path": "models/staging/stg_orders.sql", + "line": 5, + "column": 5, + "location": { + "path": "models/staging/stg_orders.sql", + "line": 5, + "column": 5, + "end_line": 5, + "end_column": 13 + }, + "related_locations": [ + { + "label": "output", + "location": { + "path": "models/staging/stg_orders.sql", + "line": 12, + "column": 8, + "end_line": 12, + "end_column": 16 + }, + "message": "inferred INTEGER" + } + ], + "help": "change the declared type or cast the expression explicitly" + } + ] + } + ], + "compiled": { + "models/marts/customer_totals.sql": [ + "SELECT customer_id, SUM(amount) AS total_amount", + "FROM __ref(\"stg_orders\")", + "GROUP BY customer_id", + "" + ], + "models/staging/stg_orders.sql": [ + "SELECT order_id, customer_id, amount, status", + "FROM __source(\"raw_orders\")", + "" + ] + }, + "manifest": { + "nodes": { + "model.failure_corpus.customer_totals": { + "database": null, + "schema": "main", + "name": "customer_totals", + "resource_type": "model", + "package_name": "failure_corpus", + "path": "models/marts/customer_totals.sql", + "original_file_path": "models/marts/customer_totals.sql", + "unique_id": "model.failure_corpus.customer_totals", + "fqn": [ + "failure_corpus", + "models", + "marts", + "customer_totals" + ], + "alias": "customer_totals", + "config": { + "enabled": true, + "alias": null, + "schema": null, + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Order totals per customer", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.customer_totals", + "depends_on": { + "macros": [], + "nodes": [ + "model.failure_corpus.stg_orders" + ] + } + }, + "model.failure_corpus.stg_orders": { + "database": null, + "schema": "main", + "name": "stg_orders", + "resource_type": "model", + "package_name": "failure_corpus", + "path": "models/staging/stg_orders.sql", + "original_file_path": "models/staging/stg_orders.sql", + "unique_id": "model.failure_corpus.stg_orders", + "fqn": [ + "failure_corpus", + "models", + "staging", + "stg_orders" + ], + "alias": "stg_orders", + "config": { + "enabled": true, + "alias": null, + "schema": null, + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Staged orders", + "columns": { + "order_id": { + "name": "order_id", + "description": "", + "meta": {}, + "data_type": "VARCHAR", + "constraints": [], + "quote": null, + "tags": [] + }, + "customer_id": { + "name": "customer_id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "amount": { + "name": "amount", + "description": "", + "meta": {}, + "data_type": "DOUBLE", + "constraints": [], + "quote": null, + "tags": [] + }, + "status": { + "name": "status", + "description": "", + "meta": {}, + "data_type": "VARCHAR", + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.stg_orders", + "depends_on": { + "macros": [], + "nodes": [ + "source.failure_corpus.raw_orders" + ] + } + } + }, + "sources": { + "source.failure_corpus.raw_orders": { + "database": null, + "schema": "", + "name": "raw_orders", + "resource_type": "source", + "package_name": "failure_corpus", + "path": "sources/raw.yml", + "original_file_path": "sources/raw.yml", + "unique_id": "source.failure_corpus.raw_orders", + "fqn": [ + "failure_corpus", + "raw_orders" + ], + "source_name": "raw_orders", + "source_description": "", + "loader": "", + "identifier": "raw_orders", + "quoting": {}, + "loaded_at_field": null, + "freshness": null, + "external": null, + "description": "Orders feed.", + "columns": { + "order_id": { + "name": "order_id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "customer_id": { + "name": "customer_id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "amount": { + "name": "amount", + "description": "", + "meta": {}, + "data_type": "DOUBLE", + "constraints": [], + "quote": null, + "tags": [] + }, + "status": { + "name": "status", + "description": "", + "meta": {}, + "data_type": "VARCHAR", + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": {}, + "source_meta": {}, + "tags": [], + "config": {}, + "patch_path": null, + "unrendered_config": {}, + "relation_name": null + } + }, + "macros": {}, + "exposures": {}, + "metrics": {}, + "groups": {}, + "selectors": {}, + "disabled": {}, + "parent_map": { + "model.failure_corpus.customer_totals": [ + "model.failure_corpus.stg_orders" + ], + "model.failure_corpus.stg_orders": [ + "source.failure_corpus.raw_orders" + ], + "source.failure_corpus.raw_orders": [] + }, + "group_map": {}, + "docs": {}, + "saved_queries": {}, + "semantic_models": {}, + "unit_tests": {} + } +} diff --git a/tests/goldens/compiler/failure/contract-unknown-type.json b/tests/goldens/compiler/failure/contract-unknown-type.json new file mode 100644 index 0000000000..c0d4f7a6f1 --- /dev/null +++ b/tests/goldens/compiler/failure/contract-unknown-type.json @@ -0,0 +1,298 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "contract", + "severity": "error", + "code": "K002", + "message": "column 'order_id' inferred as INTEGER but declared type is MYSTERY_TYPE", + "resource_type": "model", + "resource_name": "stg_orders", + "column_name": "order_id", + "path": "models/staging/stg_orders.sql", + "line": 4, + "column": 5, + "location": { + "path": "models/staging/stg_orders.sql", + "line": 4, + "column": 5, + "end_line": 4, + "end_column": 13 + }, + "related_locations": [ + { + "label": "output", + "location": { + "path": "models/staging/stg_orders.sql", + "line": 8, + "column": 8, + "end_line": 8, + "end_column": 16 + }, + "message": "inferred INTEGER" + } + ], + "help": "change the declared type or cast the expression explicitly" + } + ] + } + ], + "compiled": { + "models/marts/customer_totals.sql": [ + "SELECT customer_id, SUM(amount) AS total_amount", + "FROM __ref(\"stg_orders\")", + "GROUP BY customer_id", + "" + ], + "models/staging/stg_orders.sql": [ + "SELECT order_id, customer_id, amount, status", + "FROM __source(\"raw_orders\")", + "" + ] + }, + "manifest": { + "nodes": { + "model.failure_corpus.customer_totals": { + "database": null, + "schema": "main", + "name": "customer_totals", + "resource_type": "model", + "package_name": "failure_corpus", + "path": "models/marts/customer_totals.sql", + "original_file_path": "models/marts/customer_totals.sql", + "unique_id": "model.failure_corpus.customer_totals", + "fqn": [ + "failure_corpus", + "models", + "marts", + "customer_totals" + ], + "alias": "customer_totals", + "config": { + "enabled": true, + "alias": null, + "schema": null, + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Order totals per customer", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.customer_totals", + "depends_on": { + "macros": [], + "nodes": [ + "model.failure_corpus.stg_orders" + ] + } + }, + "model.failure_corpus.stg_orders": { + "database": null, + "schema": "main", + "name": "stg_orders", + "resource_type": "model", + "package_name": "failure_corpus", + "path": "models/staging/stg_orders.sql", + "original_file_path": "models/staging/stg_orders.sql", + "unique_id": "model.failure_corpus.stg_orders", + "fqn": [ + "failure_corpus", + "models", + "staging", + "stg_orders" + ], + "alias": "stg_orders", + "config": { + "enabled": true, + "alias": null, + "schema": null, + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Staged orders", + "columns": { + "order_id": { + "name": "order_id", + "description": "", + "meta": {}, + "data_type": "MYSTERY_TYPE", + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.stg_orders", + "depends_on": { + "macros": [], + "nodes": [ + "source.failure_corpus.raw_orders" + ] + } + } + }, + "sources": { + "source.failure_corpus.raw_orders": { + "database": null, + "schema": "", + "name": "raw_orders", + "resource_type": "source", + "package_name": "failure_corpus", + "path": "sources/raw.yml", + "original_file_path": "sources/raw.yml", + "unique_id": "source.failure_corpus.raw_orders", + "fqn": [ + "failure_corpus", + "raw_orders" + ], + "source_name": "raw_orders", + "source_description": "", + "loader": "", + "identifier": "raw_orders", + "quoting": {}, + "loaded_at_field": null, + "freshness": null, + "external": null, + "description": "Orders feed.", + "columns": { + "order_id": { + "name": "order_id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "customer_id": { + "name": "customer_id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "amount": { + "name": "amount", + "description": "", + "meta": {}, + "data_type": "DOUBLE", + "constraints": [], + "quote": null, + "tags": [] + }, + "status": { + "name": "status", + "description": "", + "meta": {}, + "data_type": "VARCHAR", + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": {}, + "source_meta": {}, + "tags": [], + "config": {}, + "patch_path": null, + "unrendered_config": {}, + "relation_name": null + } + }, + "macros": {}, + "exposures": {}, + "metrics": {}, + "groups": {}, + "selectors": {}, + "disabled": {}, + "parent_map": { + "model.failure_corpus.customer_totals": [ + "model.failure_corpus.stg_orders" + ], + "model.failure_corpus.stg_orders": [ + "source.failure_corpus.raw_orders" + ], + "source.failure_corpus.raw_orders": [] + }, + "group_map": {}, + "docs": {}, + "saved_queries": {}, + "semantic_models": {}, + "unit_tests": {} + } +} diff --git a/tests/goldens/compiler/failure/contract-without-columns.json b/tests/goldens/compiler/failure/contract-without-columns.json new file mode 100644 index 0000000000..480749a518 --- /dev/null +++ b/tests/goldens/compiler/failure/contract-without-columns.json @@ -0,0 +1,265 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "contract", + "severity": "error", + "code": "K006", + "message": "model 'stg_orders' has contract enforced but declares no columns", + "resource_type": "model", + "resource_name": "stg_orders", + "path": "models/staging/stg_orders.sql", + "help": "add MODEL(columns (...)), declare a proven MODEL(dynamic_columns (...)) family, or set contract none for this model" + } + ] + } + ], + "compiled": { + "models/marts/customer_totals.sql": [ + "SELECT customer_id, SUM(amount) AS total_amount", + "FROM __ref(\"stg_orders\")", + "GROUP BY customer_id", + "" + ], + "models/staging/stg_orders.sql": [ + "SELECT order_id, customer_id, amount, status", + "FROM __source(\"raw_orders\")", + "" + ] + }, + "manifest": { + "nodes": { + "model.failure_corpus.customer_totals": { + "database": null, + "schema": "main", + "name": "customer_totals", + "resource_type": "model", + "package_name": "failure_corpus", + "path": "models/marts/customer_totals.sql", + "original_file_path": "models/marts/customer_totals.sql", + "unique_id": "model.failure_corpus.customer_totals", + "fqn": [ + "failure_corpus", + "models", + "marts", + "customer_totals" + ], + "alias": "customer_totals", + "config": { + "enabled": true, + "alias": null, + "schema": null, + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Order totals per customer", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.customer_totals", + "depends_on": { + "macros": [], + "nodes": [ + "model.failure_corpus.stg_orders" + ] + } + }, + "model.failure_corpus.stg_orders": { + "database": null, + "schema": "main", + "name": "stg_orders", + "resource_type": "model", + "package_name": "failure_corpus", + "path": "models/staging/stg_orders.sql", + "original_file_path": "models/staging/stg_orders.sql", + "unique_id": "model.failure_corpus.stg_orders", + "fqn": [ + "failure_corpus", + "models", + "staging", + "stg_orders" + ], + "alias": "stg_orders", + "config": { + "enabled": true, + "alias": null, + "schema": null, + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Staged orders", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.stg_orders", + "depends_on": { + "macros": [], + "nodes": [ + "source.failure_corpus.raw_orders" + ] + } + } + }, + "sources": { + "source.failure_corpus.raw_orders": { + "database": null, + "schema": "", + "name": "raw_orders", + "resource_type": "source", + "package_name": "failure_corpus", + "path": "sources/raw.yml", + "original_file_path": "sources/raw.yml", + "unique_id": "source.failure_corpus.raw_orders", + "fqn": [ + "failure_corpus", + "raw_orders" + ], + "source_name": "raw_orders", + "source_description": "", + "loader": "", + "identifier": "raw_orders", + "quoting": {}, + "loaded_at_field": null, + "freshness": null, + "external": null, + "description": "Orders feed.", + "columns": { + "order_id": { + "name": "order_id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "customer_id": { + "name": "customer_id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "amount": { + "name": "amount", + "description": "", + "meta": {}, + "data_type": "DOUBLE", + "constraints": [], + "quote": null, + "tags": [] + }, + "status": { + "name": "status", + "description": "", + "meta": {}, + "data_type": "VARCHAR", + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": {}, + "source_meta": {}, + "tags": [], + "config": {}, + "patch_path": null, + "unrendered_config": {}, + "relation_name": null + } + }, + "macros": {}, + "exposures": {}, + "metrics": {}, + "groups": {}, + "selectors": {}, + "disabled": {}, + "parent_map": { + "model.failure_corpus.customer_totals": [ + "model.failure_corpus.stg_orders" + ], + "model.failure_corpus.stg_orders": [ + "source.failure_corpus.raw_orders" + ], + "source.failure_corpus.raw_orders": [] + }, + "group_map": {}, + "docs": {}, + "saved_queries": {}, + "semantic_models": {}, + "unit_tests": {} + } +} diff --git a/tests/goldens/compiler/failure/cursor-intrinsic-outside-incremental.json b/tests/goldens/compiler/failure/cursor-intrinsic-outside-incremental.json new file mode 100644 index 0000000000..38b924499f --- /dev/null +++ b/tests/goldens/compiler/failure/cursor-intrinsic-outside-incremental.json @@ -0,0 +1,18 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "P001", + "message": "Model 'stg_orders' uses cursor intrinsics but is not a built-in incremental model" + } + ] + } + ], + "compiled": {}, + "manifest": null +} diff --git a/tests/goldens/compiler/failure/cursor-model-without-inputs.json b/tests/goldens/compiler/failure/cursor-model-without-inputs.json new file mode 100644 index 0000000000..8fed1f1c08 --- /dev/null +++ b/tests/goldens/compiler/failure/cursor-model-without-inputs.json @@ -0,0 +1,268 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "P011", + "message": "incremental model 'customer_totals' reads no inputs, so builds after the first cannot work out their cursor window", + "resource_type": "model", + "resource_name": "customer_totals", + "path": "models/marts/customer_totals.sql", + "line": 5, + "column": 3, + "location": { + "path": "models/marts/customer_totals.sql", + "line": 5, + "column": 3, + "end_line": 5, + "end_column": 19 + }, + "help": "read the data through __ref(), __source() or __seed() so its cursor can bound the window, or use materialized table if the model has no upstream data" + } + ] + } + ], + "compiled": { + "models/marts/customer_totals.sql": [ + "SELECT TIMESTAMP '2026-01-01 00:00:00' AS placed_at, 1 AS customer_id", + "" + ], + "models/staging/stg_orders.sql": [ + "SELECT order_id, customer_id, amount, status", + "FROM __source(\"raw_orders\")", + "" + ] + }, + "manifest": { + "nodes": { + "model.failure_corpus.customer_totals": { + "database": null, + "schema": "main", + "name": "customer_totals", + "resource_type": "model", + "package_name": "failure_corpus", + "path": "models/marts/customer_totals.sql", + "original_file_path": "models/marts/customer_totals.sql", + "unique_id": "model.failure_corpus.customer_totals", + "fqn": [ + "failure_corpus", + "models", + "marts", + "customer_totals" + ], + "alias": "customer_totals", + "config": { + "enabled": true, + "alias": null, + "schema": null, + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "incremental", + "incremental_strategy": "delete_insert", + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Order totals per customer", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.customer_totals", + "depends_on": { + "macros": [], + "nodes": [] + } + }, + "model.failure_corpus.stg_orders": { + "database": null, + "schema": "main", + "name": "stg_orders", + "resource_type": "model", + "package_name": "failure_corpus", + "path": "models/staging/stg_orders.sql", + "original_file_path": "models/staging/stg_orders.sql", + "unique_id": "model.failure_corpus.stg_orders", + "fqn": [ + "failure_corpus", + "models", + "staging", + "stg_orders" + ], + "alias": "stg_orders", + "config": { + "enabled": true, + "alias": null, + "schema": null, + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Staged orders", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.stg_orders", + "depends_on": { + "macros": [], + "nodes": [ + "source.failure_corpus.raw_orders" + ] + } + } + }, + "sources": { + "source.failure_corpus.raw_orders": { + "database": null, + "schema": "", + "name": "raw_orders", + "resource_type": "source", + "package_name": "failure_corpus", + "path": "sources/raw.yml", + "original_file_path": "sources/raw.yml", + "unique_id": "source.failure_corpus.raw_orders", + "fqn": [ + "failure_corpus", + "raw_orders" + ], + "source_name": "raw_orders", + "source_description": "", + "loader": "", + "identifier": "raw_orders", + "quoting": {}, + "loaded_at_field": null, + "freshness": null, + "external": null, + "description": "Orders feed.", + "columns": { + "order_id": { + "name": "order_id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "customer_id": { + "name": "customer_id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "amount": { + "name": "amount", + "description": "", + "meta": {}, + "data_type": "DOUBLE", + "constraints": [], + "quote": null, + "tags": [] + }, + "status": { + "name": "status", + "description": "", + "meta": {}, + "data_type": "VARCHAR", + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": {}, + "source_meta": {}, + "tags": [], + "config": {}, + "patch_path": null, + "unrendered_config": {}, + "relation_name": null + } + }, + "macros": {}, + "exposures": {}, + "metrics": {}, + "groups": {}, + "selectors": {}, + "disabled": {}, + "parent_map": { + "model.failure_corpus.customer_totals": [], + "model.failure_corpus.stg_orders": [ + "source.failure_corpus.raw_orders" + ], + "source.failure_corpus.raw_orders": [] + }, + "group_map": {}, + "docs": {}, + "saved_queries": {}, + "semantic_models": {}, + "unit_tests": {} + } +} diff --git a/tests/goldens/compiler/failure/cursor-unknown-column.json b/tests/goldens/compiler/failure/cursor-unknown-column.json new file mode 100644 index 0000000000..f614aa5ce4 --- /dev/null +++ b/tests/goldens/compiler/failure/cursor-unknown-column.json @@ -0,0 +1,272 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "B300", + "message": "cursor references unknown column 'placed_at'", + "resource_type": "model", + "resource_name": "customer_totals", + "path": "models/marts/customer_totals.sql", + "line": 5, + "column": 10, + "location": { + "path": "models/marts/customer_totals.sql", + "line": 5, + "column": 10 + }, + "help": "change the metadata column name to an existing output column" + } + ] + } + ], + "compiled": { + "models/marts/customer_totals.sql": [ + "SELECT customer_id, SUM(amount) AS total_amount", + "FROM __ref(\"stg_orders\")", + "GROUP BY customer_id", + "" + ], + "models/staging/stg_orders.sql": [ + "SELECT order_id, customer_id, amount, status", + "FROM __source(\"raw_orders\")", + "" + ] + }, + "manifest": { + "nodes": { + "model.failure_corpus.customer_totals": { + "database": null, + "schema": "main", + "name": "customer_totals", + "resource_type": "model", + "package_name": "failure_corpus", + "path": "models/marts/customer_totals.sql", + "original_file_path": "models/marts/customer_totals.sql", + "unique_id": "model.failure_corpus.customer_totals", + "fqn": [ + "failure_corpus", + "models", + "marts", + "customer_totals" + ], + "alias": "customer_totals", + "config": { + "enabled": true, + "alias": null, + "schema": null, + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "incremental", + "incremental_strategy": "delete_insert", + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Order totals per customer", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.customer_totals", + "depends_on": { + "macros": [], + "nodes": [ + "model.failure_corpus.stg_orders" + ] + } + }, + "model.failure_corpus.stg_orders": { + "database": null, + "schema": "main", + "name": "stg_orders", + "resource_type": "model", + "package_name": "failure_corpus", + "path": "models/staging/stg_orders.sql", + "original_file_path": "models/staging/stg_orders.sql", + "unique_id": "model.failure_corpus.stg_orders", + "fqn": [ + "failure_corpus", + "models", + "staging", + "stg_orders" + ], + "alias": "stg_orders", + "config": { + "enabled": true, + "alias": null, + "schema": null, + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Staged orders", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.stg_orders", + "depends_on": { + "macros": [], + "nodes": [ + "source.failure_corpus.raw_orders" + ] + } + } + }, + "sources": { + "source.failure_corpus.raw_orders": { + "database": null, + "schema": "", + "name": "raw_orders", + "resource_type": "source", + "package_name": "failure_corpus", + "path": "sources/raw.yml", + "original_file_path": "sources/raw.yml", + "unique_id": "source.failure_corpus.raw_orders", + "fqn": [ + "failure_corpus", + "raw_orders" + ], + "source_name": "raw_orders", + "source_description": "", + "loader": "", + "identifier": "raw_orders", + "quoting": {}, + "loaded_at_field": null, + "freshness": null, + "external": null, + "description": "Orders feed.", + "columns": { + "order_id": { + "name": "order_id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "customer_id": { + "name": "customer_id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "amount": { + "name": "amount", + "description": "", + "meta": {}, + "data_type": "DOUBLE", + "constraints": [], + "quote": null, + "tags": [] + }, + "status": { + "name": "status", + "description": "", + "meta": {}, + "data_type": "VARCHAR", + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": {}, + "source_meta": {}, + "tags": [], + "config": {}, + "patch_path": null, + "unrendered_config": {}, + "relation_name": null + } + }, + "macros": {}, + "exposures": {}, + "metrics": {}, + "groups": {}, + "selectors": {}, + "disabled": {}, + "parent_map": { + "model.failure_corpus.customer_totals": [ + "model.failure_corpus.stg_orders" + ], + "model.failure_corpus.stg_orders": [ + "source.failure_corpus.raw_orders" + ], + "source.failure_corpus.raw_orders": [] + }, + "group_map": {}, + "docs": {}, + "saved_queries": {}, + "semantic_models": {}, + "unit_tests": {} + } +} diff --git a/tests/goldens/compiler/failure/dbt-reference-without-manifest.json b/tests/goldens/compiler/failure/dbt-reference-without-manifest.json new file mode 100644 index 0000000000..6f3b715373 --- /dev/null +++ b/tests/goldens/compiler/failure/dbt-reference-without-manifest.json @@ -0,0 +1,19 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "C214", + "message": "Model file models/marts/customer_totals.sql uses __dbt_ref(\"upstream_orders\") but no dbt manifest was found", + "help": "Run dbt compile or configure dbt target_path so SQLBuild can read manifest.json." + } + ] + } + ], + "compiled": {}, + "manifest": null +} diff --git a/tests/goldens/compiler/failure/dbt-unknown-key.json b/tests/goldens/compiler/failure/dbt-unknown-key.json new file mode 100644 index 0000000000..349baa71af --- /dev/null +++ b/tests/goldens/compiler/failure/dbt-unknown-key.json @@ -0,0 +1,19 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "D001", + "message": "/sqlbuild_project.toml dbt contains unknown key(s): manifest. Allowed keys: profiles_dir, project_dir, target, target_path, vars", + "help": "supported keys: profiles_dir, project_dir, target, target_path, vars" + } + ] + } + ], + "compiled": {}, + "manifest": null +} diff --git a/tests/goldens/compiler/failure/declaration-first-of-two-reference-errors.json b/tests/goldens/compiler/failure/declaration-first-of-two-reference-errors.json new file mode 100644 index 0000000000..a2c4b904e1 --- /dev/null +++ b/tests/goldens/compiler/failure/declaration-first-of-two-reference-errors.json @@ -0,0 +1,18 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "P001", + "message": "Unknown member 'HELD' for enum 'staged_status' in '/models/staging/stg_orders.sql'. Available members: PLACED" + } + ] + } + ], + "compiled": {}, + "manifest": null +} diff --git a/tests/goldens/compiler/failure/declaration-group-below-root.json b/tests/goldens/compiler/failure/declaration-group-below-root.json new file mode 100644 index 0000000000..ebbb8b534f --- /dev/null +++ b/tests/goldens/compiler/failure/declaration-group-below-root.json @@ -0,0 +1,18 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "D013", + "message": "Grouped declaration root models/_sqlbuild/ must be below a concrete owner directory; use the project-wide macros/, enums/, constants/, audits/, schemas/, or hooks/ root instead" + } + ] + } + ], + "compiled": {}, + "manifest": null +} diff --git a/tests/goldens/compiler/failure/declaration-group-unsupported-entry.json b/tests/goldens/compiler/failure/declaration-group-unsupported-entry.json new file mode 100644 index 0000000000..71f4684d71 --- /dev/null +++ b/tests/goldens/compiler/failure/declaration-group-unsupported-entry.json @@ -0,0 +1,18 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "D013", + "message": "Declaration group models/marts/_sqlbuild/ contains unsupported entries: models/marts/_sqlbuild/notes.txt" + } + ] + } + ], + "compiled": {}, + "manifest": null +} diff --git a/tests/goldens/compiler/failure/declaration-inaccessible-local-constant.json b/tests/goldens/compiler/failure/declaration-inaccessible-local-constant.json new file mode 100644 index 0000000000..32d7405555 --- /dev/null +++ b/tests/goldens/compiler/failure/declaration-inaccessible-local-constant.json @@ -0,0 +1,18 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "P001", + "message": "Constant 'order_cap' is known but inaccessible in '/models/staging/stg_orders.sql'. It is defined at models/marts/_constants/limits.sql:1:1 with local scope owned by 'models/marts'; consumer path: '/models/staging/stg_orders.sql'" + } + ] + } + ], + "compiled": {}, + "manifest": null +} diff --git a/tests/goldens/compiler/failure/declaration-invalid-constant-reference.json b/tests/goldens/compiler/failure/declaration-invalid-constant-reference.json new file mode 100644 index 0000000000..407aef0a1a --- /dev/null +++ b/tests/goldens/compiler/failure/declaration-invalid-constant-reference.json @@ -0,0 +1,18 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "P001", + "message": "Invalid constant reference in '/models/staging/stg_orders.sql'" + } + ] + } + ], + "compiled": {}, + "manifest": null +} diff --git a/tests/goldens/compiler/failure/declaration-root-nested.json b/tests/goldens/compiler/failure/declaration-root-nested.json new file mode 100644 index 0000000000..1a803d6726 --- /dev/null +++ b/tests/goldens/compiler/failure/declaration-root-nested.json @@ -0,0 +1,18 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "D013", + "message": "Declaration root models/marts/_enums/_constants/ is nested inside another declaration tree" + } + ] + } + ], + "compiled": {}, + "manifest": null +} diff --git a/tests/goldens/compiler/failure/declaration-unclosed-quote-before-reference.json b/tests/goldens/compiler/failure/declaration-unclosed-quote-before-reference.json new file mode 100644 index 0000000000..df2f8f4d34 --- /dev/null +++ b/tests/goldens/compiler/failure/declaration-unclosed-quote-before-reference.json @@ -0,0 +1,18 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "P001", + "message": "Enum and constant expansion contains an unclosed quoted string" + } + ] + } + ], + "compiled": {}, + "manifest": null +} diff --git a/tests/goldens/compiler/failure/declaration-unknown-enum-after-macro.json b/tests/goldens/compiler/failure/declaration-unknown-enum-after-macro.json new file mode 100644 index 0000000000..3e9904a035 --- /dev/null +++ b/tests/goldens/compiler/failure/declaration-unknown-enum-after-macro.json @@ -0,0 +1,18 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "P001", + "message": "Unknown enum 'missing_status' in '/models/staging/stg_orders.sql'. Visible enums: none" + } + ] + } + ], + "compiled": {}, + "manifest": null +} diff --git a/tests/goldens/compiler/failure/duplicate-declaration.json b/tests/goldens/compiler/failure/duplicate-declaration.json new file mode 100644 index 0000000000..bbf984bf10 --- /dev/null +++ b/tests/goldens/compiler/failure/duplicate-declaration.json @@ -0,0 +1,18 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "P001", + "message": "Invalid declaration scope index:\n[S003] Duplicate declaration 'constant:minimum_amount' at models/staging/_sqlbuild/_constants/limits.sql:1:1, models/staging/_sqlbuild/_constants/limits.sql:1:1\n[S003] Duplicate declaration 'constant:minimum_amount' at models/staging/_sqlbuild/_constants/limits.sql:1:1, models/staging/_sqlbuild/_constants/limits.sql:1:1" + } + ] + } + ], + "compiled": {}, + "manifest": null +} diff --git a/tests/goldens/compiler/failure/duplicate-hook.json b/tests/goldens/compiler/failure/duplicate-hook.json new file mode 100644 index 0000000000..2a11772cb4 --- /dev/null +++ b/tests/goldens/compiler/failure/duplicate-hook.json @@ -0,0 +1,18 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "D011", + "message": "Duplicate hook name 'note_refresh' found in hooks/python/first.py and hooks/python/second.py" + } + ] + } + ], + "compiled": {}, + "manifest": null +} diff --git a/tests/goldens/compiler/failure/duplicate-model.json b/tests/goldens/compiler/failure/duplicate-model.json new file mode 100644 index 0000000000..b9b49b0472 --- /dev/null +++ b/tests/goldens/compiler/failure/duplicate-model.json @@ -0,0 +1,18 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "D007", + "message": "Duplicate model file name found for 'stg_orders' in models/marts/stg_orders.sql and models/staging/stg_orders.sql" + } + ] + } + ], + "compiled": {}, + "manifest": null +} diff --git a/tests/goldens/compiler/failure/duplicate-provider.json b/tests/goldens/compiler/failure/duplicate-provider.json new file mode 100644 index 0000000000..2c25af46db --- /dev/null +++ b/tests/goldens/compiler/failure/duplicate-provider.json @@ -0,0 +1,18 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "D012", + "message": "Duplicate provider name 'export_settings' found in providers/first.py and providers/second.py" + } + ] + } + ], + "compiled": {}, + "manifest": null +} diff --git a/tests/goldens/compiler/failure/duplicate-python-node.json b/tests/goldens/compiler/failure/duplicate-python-node.json new file mode 100644 index 0000000000..7b8d648d20 --- /dev/null +++ b/tests/goldens/compiler/failure/duplicate-python-node.json @@ -0,0 +1,18 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "D007", + "message": "Duplicate Python node found for 'export_orders' in python/tasks/first.py and python/tasks/second.py; loader, task, asset, and check names must be globally unique" + } + ] + } + ], + "compiled": {}, + "manifest": null +} diff --git a/tests/goldens/compiler/failure/duplicate-scenario.json b/tests/goldens/compiler/failure/duplicate-scenario.json new file mode 100644 index 0000000000..1436b1b6b4 --- /dev/null +++ b/tests/goldens/compiler/failure/duplicate-scenario.json @@ -0,0 +1,18 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "D007", + "message": "Duplicate scenario file name found for 'orders' in tests/scenarios/north/orders.sql and tests/scenarios/south/orders.sql" + } + ] + } + ], + "compiled": {}, + "manifest": null +} diff --git a/tests/goldens/compiler/failure/duplicate-source.json b/tests/goldens/compiler/failure/duplicate-source.json new file mode 100644 index 0000000000..1b824c951e --- /dev/null +++ b/tests/goldens/compiler/failure/duplicate-source.json @@ -0,0 +1,18 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "D007", + "message": "Duplicate source declaration found for 'raw_orders' in sources/more.yml and sources/raw.yml" + } + ] + } + ], + "compiled": {}, + "manifest": null +} diff --git a/tests/goldens/compiler/failure/engine-error-case-folded-duplicate-column.json b/tests/goldens/compiler/failure/engine-error-case-folded-duplicate-column.json new file mode 100644 index 0000000000..8595e5f723 --- /dev/null +++ b/tests/goldens/compiler/failure/engine-error-case-folded-duplicate-column.json @@ -0,0 +1,18 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "P001", + "message": "models/marts/customer_totals.sql model has duplicate column 'größe' (column names are case-insensitive)" + } + ] + } + ], + "compiled": {}, + "manifest": null +} diff --git a/tests/goldens/compiler/failure/engine-error-unset-environment-variable.json b/tests/goldens/compiler/failure/engine-error-unset-environment-variable.json new file mode 100644 index 0000000000..d34e094776 --- /dev/null +++ b/tests/goldens/compiler/failure/engine-error-unset-environment-variable.json @@ -0,0 +1,18 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "P001", + "message": "unknown environment variable '@@ENV:SQB_ENGINE_ERROR_UNSET_REGION' in '/models/marts/customer_totals.sql'" + } + ] + } + ], + "compiled": {}, + "manifest": null +} diff --git a/tests/goldens/compiler/failure/engine-error-week-date-cursor-start.json b/tests/goldens/compiler/failure/engine-error-week-date-cursor-start.json new file mode 100644 index 0000000000..c835739003 --- /dev/null +++ b/tests/goldens/compiler/failure/engine-error-week-date-cursor-start.json @@ -0,0 +1,18 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "P001", + "message": "model 'customer_totals': cursor_start value '2024-W01-1T25:00' is not a valid ISO timestamp: hour must be in 0..23" + } + ] + } + ], + "compiled": {}, + "manifest": null +} diff --git a/tests/goldens/compiler/failure/enum-duplicate-member.json b/tests/goldens/compiler/failure/enum-duplicate-member.json new file mode 100644 index 0000000000..aea3a7ecad --- /dev/null +++ b/tests/goldens/compiler/failure/enum-duplicate-member.json @@ -0,0 +1,18 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "D013", + "message": "/models/staging/_sqlbuild/_enums/order_status.sql enum 'order_status' has duplicate member 'PLACED'" + } + ] + } + ], + "compiled": {}, + "manifest": null +} diff --git a/tests/goldens/compiler/failure/enum-parse.json b/tests/goldens/compiler/failure/enum-parse.json new file mode 100644 index 0000000000..57406a204d --- /dev/null +++ b/tests/goldens/compiler/failure/enum-parse.json @@ -0,0 +1,18 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "D013", + "message": "ENUM(...) in '/enums/order_status.sql' contains invalid SQLBuild header syntax: expected value at position 35" + } + ] + } + ], + "compiled": {}, + "manifest": null +} diff --git a/tests/goldens/compiler/failure/failing-macro.json b/tests/goldens/compiler/failure/failing-macro.json new file mode 100644 index 0000000000..f8ffc4fae3 --- /dev/null +++ b/tests/goldens/compiler/failure/failing-macro.json @@ -0,0 +1,18 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "P001", + "message": "Macro '@cents' in '/models/staging/stg_orders.sql' failed: cannot convert amount" + } + ] + } + ], + "compiled": {}, + "manifest": null +} diff --git a/tests/goldens/compiler/failure/generic-audit-cursor-intrinsic.json b/tests/goldens/compiler/failure/generic-audit-cursor-intrinsic.json new file mode 100644 index 0000000000..139a76c54e --- /dev/null +++ b/tests/goldens/compiler/failure/generic-audit-cursor-intrinsic.json @@ -0,0 +1,18 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "P001", + "message": "Audit 'fresh' uses cursor intrinsics, which are only supported in cursor-based incremental model query SQL" + } + ] + } + ], + "compiled": {}, + "manifest": null +} diff --git a/tests/goldens/compiler/failure/generic-audit-header.json b/tests/goldens/compiler/failure/generic-audit-header.json new file mode 100644 index 0000000000..3bbb5d34fa --- /dev/null +++ b/tests/goldens/compiler/failure/generic-audit-header.json @@ -0,0 +1,19 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "D004", + "message": "AUDIT() in '/models/marts/_sqlbuild/_audits/generic/at_least.sql:1' has unsupported keys: unknown_option", + "help": "supported keys: always_run, evaluation, minimum_samples, name, run_scope, sample_count, sample_unit, severity, sql_analysis, thresholds, value" + } + ] + } + ], + "compiled": {}, + "manifest": null +} diff --git a/tests/goldens/compiler/failure/generic-audit-missing-argument.json b/tests/goldens/compiler/failure/generic-audit-missing-argument.json new file mode 100644 index 0000000000..5e02dc2c5e --- /dev/null +++ b/tests/goldens/compiler/failure/generic-audit-missing-argument.json @@ -0,0 +1,18 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "P001", + "message": "models/staging/stg_orders.sql is missing argument 'minimum' for generic audit 'floor'" + } + ] + } + ], + "compiled": {}, + "manifest": null +} diff --git a/tests/goldens/compiler/failure/generic-audit-overrides-implicit-model.json b/tests/goldens/compiler/failure/generic-audit-overrides-implicit-model.json new file mode 100644 index 0000000000..31faebe172 --- /dev/null +++ b/tests/goldens/compiler/failure/generic-audit-overrides-implicit-model.json @@ -0,0 +1,18 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "P001", + "message": "models/staging/stg_orders.sql audit 'floor' must not override implicit model from attached context" + } + ] + } + ], + "compiled": {}, + "manifest": null +} diff --git a/tests/goldens/compiler/failure/generic-audit-unknown-run-scope.json b/tests/goldens/compiler/failure/generic-audit-unknown-run-scope.json new file mode 100644 index 0000000000..f138d91f5f --- /dev/null +++ b/tests/goldens/compiler/failure/generic-audit-unknown-run-scope.json @@ -0,0 +1,18 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "P001", + "message": "unknown audit run_scope 'always'; valid values: delta_and_final, final" + } + ] + } + ], + "compiled": {}, + "manifest": null +} diff --git a/tests/goldens/compiler/failure/hard-coded-relation-in-python.json b/tests/goldens/compiler/failure/hard-coded-relation-in-python.json new file mode 100644 index 0000000000..6002c32dfd --- /dev/null +++ b/tests/goldens/compiler/failure/hard-coded-relation-in-python.json @@ -0,0 +1,270 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "P008", + "message": "task:export_orders names model:stg_orders as 'stg_orders' in SQL passed to ctx.query()", + "path": "python/tasks/exports.py", + "line": 7, + "column": 1, + "location": { + "path": "python/tasks/exports.py", + "line": 7, + "column": 1 + }, + "help": "declare it with depends_on=model(\"stg_orders\") and use ctx.relation(model(\"stg_orders\")) instead of the relation name\n = help: if 'stg_orders' is an external table that shares the name, qualify it with its schema\n = help: the current value is [references] enforce_explicit = true (from sqlbuild_project.toml or its default); while migrating a project, to allow hard-coded relation names, set this in sqlbuild_project.toml:\n [references]\n enforce_explicit = false" + } + ] + } + ], + "compiled": { + "models/marts/customer_totals.sql": [ + "SELECT customer_id, SUM(amount) AS total_amount", + "FROM __ref(\"stg_orders\")", + "GROUP BY customer_id", + "" + ], + "models/staging/stg_orders.sql": [ + "SELECT order_id, customer_id, amount, status", + "FROM __source(\"raw_orders\")", + "" + ] + }, + "manifest": { + "nodes": { + "model.failure_corpus.customer_totals": { + "database": null, + "schema": "main", + "name": "customer_totals", + "resource_type": "model", + "package_name": "failure_corpus", + "path": "models/marts/customer_totals.sql", + "original_file_path": "models/marts/customer_totals.sql", + "unique_id": "model.failure_corpus.customer_totals", + "fqn": [ + "failure_corpus", + "models", + "marts", + "customer_totals" + ], + "alias": "customer_totals", + "config": { + "enabled": true, + "alias": null, + "schema": null, + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Order totals per customer", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.customer_totals", + "depends_on": { + "macros": [], + "nodes": [ + "model.failure_corpus.stg_orders" + ] + } + }, + "model.failure_corpus.stg_orders": { + "database": null, + "schema": "main", + "name": "stg_orders", + "resource_type": "model", + "package_name": "failure_corpus", + "path": "models/staging/stg_orders.sql", + "original_file_path": "models/staging/stg_orders.sql", + "unique_id": "model.failure_corpus.stg_orders", + "fqn": [ + "failure_corpus", + "models", + "staging", + "stg_orders" + ], + "alias": "stg_orders", + "config": { + "enabled": true, + "alias": null, + "schema": null, + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Staged orders", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.stg_orders", + "depends_on": { + "macros": [], + "nodes": [ + "source.failure_corpus.raw_orders" + ] + } + } + }, + "sources": { + "source.failure_corpus.raw_orders": { + "database": null, + "schema": "", + "name": "raw_orders", + "resource_type": "source", + "package_name": "failure_corpus", + "path": "sources/raw.yml", + "original_file_path": "sources/raw.yml", + "unique_id": "source.failure_corpus.raw_orders", + "fqn": [ + "failure_corpus", + "raw_orders" + ], + "source_name": "raw_orders", + "source_description": "", + "loader": "", + "identifier": "raw_orders", + "quoting": {}, + "loaded_at_field": null, + "freshness": null, + "external": null, + "description": "Orders feed.", + "columns": { + "order_id": { + "name": "order_id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "customer_id": { + "name": "customer_id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "amount": { + "name": "amount", + "description": "", + "meta": {}, + "data_type": "DOUBLE", + "constraints": [], + "quote": null, + "tags": [] + }, + "status": { + "name": "status", + "description": "", + "meta": {}, + "data_type": "VARCHAR", + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": {}, + "source_meta": {}, + "tags": [], + "config": {}, + "patch_path": null, + "unrendered_config": {}, + "relation_name": null + } + }, + "macros": {}, + "exposures": {}, + "metrics": {}, + "groups": {}, + "selectors": {}, + "disabled": {}, + "parent_map": { + "model.failure_corpus.customer_totals": [ + "model.failure_corpus.stg_orders" + ], + "model.failure_corpus.stg_orders": [ + "source.failure_corpus.raw_orders" + ], + "source.failure_corpus.raw_orders": [] + }, + "group_map": {}, + "docs": {}, + "saved_queries": {}, + "semantic_models": {}, + "unit_tests": {} + } +} diff --git a/tests/goldens/compiler/failure/header-audit-identity.json b/tests/goldens/compiler/failure/header-audit-identity.json new file mode 100644 index 0000000000..90bd441865 --- /dev/null +++ b/tests/goldens/compiler/failure/header-audit-identity.json @@ -0,0 +1,19 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "D016", + "message": "Invalid model audit definition identity 'NotNull' in models/staging/stg_orders.sql; use snake_case 'not_null'", + "help": "Rename the authored identity and update its references, selectors, and integration keys. SQLBuild does not silently normalize resource identities. Double underscores remain valid." + } + ] + } + ], + "compiled": {}, + "manifest": null +} diff --git a/tests/goldens/compiler/failure/header-audit-severity.json b/tests/goldens/compiler/failure/header-audit-severity.json new file mode 100644 index 0000000000..c4c39c8630 --- /dev/null +++ b/tests/goldens/compiler/failure/header-audit-severity.json @@ -0,0 +1,18 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "P001", + "message": "models/staging/stg_orders.sql model audit 'unique_combination' 'severity' must be one of: warn, error" + } + ] + } + ], + "compiled": {}, + "manifest": null +} diff --git a/tests/goldens/compiler/failure/header-column-audits-not-a-list.json b/tests/goldens/compiler/failure/header-column-audits-not-a-list.json new file mode 100644 index 0000000000..411e67da84 --- /dev/null +++ b/tests/goldens/compiler/failure/header-column-audits-not-a-list.json @@ -0,0 +1,18 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "P001", + "message": "models/staging/stg_orders.sql model column 'order_id' audits must be a list" + } + ] + } + ], + "compiled": {}, + "manifest": null +} diff --git a/tests/goldens/compiler/failure/header-column-type-not-text.json b/tests/goldens/compiler/failure/header-column-type-not-text.json new file mode 100644 index 0000000000..f3b20117b1 --- /dev/null +++ b/tests/goldens/compiler/failure/header-column-type-not-text.json @@ -0,0 +1,18 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "P001", + "message": "models/staging/stg_orders.sql model column 'order_id' 'type' must be a non-empty string" + } + ] + } + ], + "compiled": {}, + "manifest": null +} diff --git a/tests/goldens/compiler/failure/header-column-unknown-metadata-key.json b/tests/goldens/compiler/failure/header-column-unknown-metadata-key.json new file mode 100644 index 0000000000..26fd68c93b --- /dev/null +++ b/tests/goldens/compiler/failure/header-column-unknown-metadata-key.json @@ -0,0 +1,18 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "P001", + "message": "models/staging/stg_orders.sql model column 'order_id' has unknown metadata keys: format" + } + ] + } + ], + "compiled": {}, + "manifest": null +} diff --git a/tests/goldens/compiler/failure/header-duplicate-column.json b/tests/goldens/compiler/failure/header-duplicate-column.json new file mode 100644 index 0000000000..33959188fb --- /dev/null +++ b/tests/goldens/compiler/failure/header-duplicate-column.json @@ -0,0 +1,18 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "P001", + "message": "models/staging/stg_orders.sql model has duplicate column 'ORDER_ID' (column names are case-insensitive)" + } + ] + } + ], + "compiled": {}, + "manifest": null +} diff --git a/tests/goldens/compiler/failure/hook-reads-dependant.json b/tests/goldens/compiler/failure/hook-reads-dependant.json new file mode 100644 index 0000000000..58c2f1bc67 --- /dev/null +++ b/tests/goldens/compiler/failure/hook-reads-dependant.json @@ -0,0 +1,280 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "P007", + "message": "Python hook 'note_refresh' on model 'stg_orders' reads model 'customer_totals', which depends on 'stg_orders'; a hook runs as part of its model, so it cannot wait for a resource built from that model", + "resource_type": "model", + "resource_name": "stg_orders", + "path": "models/staging/_sqlbuild/_hooks/python/notes.py", + "help": "attach the hook to a model built after 'customer_totals', or drop the read and use ctx.destination for 'stg_orders' itself" + } + ] + } + ], + "compiled": { + "models/marts/customer_totals.sql": [ + "SELECT customer_id, SUM(amount) AS total_amount", + "FROM __ref(\"stg_orders\")", + "GROUP BY customer_id", + "" + ], + "models/staging/stg_orders.sql": [ + "SELECT order_id, customer_id, amount, status", + "FROM __source(\"raw_orders\")", + "" + ] + }, + "manifest": { + "nodes": { + "model.failure_corpus.customer_totals": { + "database": null, + "schema": "main", + "name": "customer_totals", + "resource_type": "model", + "package_name": "failure_corpus", + "path": "models/marts/customer_totals.sql", + "original_file_path": "models/marts/customer_totals.sql", + "unique_id": "model.failure_corpus.customer_totals", + "fqn": [ + "failure_corpus", + "models", + "marts", + "customer_totals" + ], + "alias": "customer_totals", + "config": { + "enabled": true, + "alias": null, + "schema": null, + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Order totals per customer", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.customer_totals", + "depends_on": { + "macros": [], + "nodes": [ + "model.failure_corpus.stg_orders" + ] + } + }, + "model.failure_corpus.stg_orders": { + "database": null, + "schema": "main", + "name": "stg_orders", + "resource_type": "model", + "package_name": "failure_corpus", + "path": "models/staging/stg_orders.sql", + "original_file_path": "models/staging/stg_orders.sql", + "unique_id": "model.failure_corpus.stg_orders", + "fqn": [ + "failure_corpus", + "models", + "staging", + "stg_orders" + ], + "alias": "stg_orders", + "config": { + "enabled": true, + "alias": null, + "schema": null, + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Staged orders", + "columns": {}, + "meta": { + "sqlbuild": { + "lifecycle_hooks": { + "post_hooks": [ + { + "type": "python", + "name": "note_refresh", + "kwargs": {}, + "relative_path": "models/staging/_sqlbuild/_hooks/python/notes.py", + "definition_hash": "5d9bbe39d4017828edf4fac4743dc8253cd0f9b3d65779e5f9cff6100bee89dd", + "version_hash": "38cb2db363acaa4f13ad2ac96e920b672895acf529c55e86c75d1ab2c73137a9" + } + ] + } + } + }, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.stg_orders", + "depends_on": { + "macros": [], + "nodes": [ + "source.failure_corpus.raw_orders" + ] + } + } + }, + "sources": { + "source.failure_corpus.raw_orders": { + "database": null, + "schema": "", + "name": "raw_orders", + "resource_type": "source", + "package_name": "failure_corpus", + "path": "sources/raw.yml", + "original_file_path": "sources/raw.yml", + "unique_id": "source.failure_corpus.raw_orders", + "fqn": [ + "failure_corpus", + "raw_orders" + ], + "source_name": "raw_orders", + "source_description": "", + "loader": "", + "identifier": "raw_orders", + "quoting": {}, + "loaded_at_field": null, + "freshness": null, + "external": null, + "description": "Orders feed.", + "columns": { + "order_id": { + "name": "order_id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "customer_id": { + "name": "customer_id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "amount": { + "name": "amount", + "description": "", + "meta": {}, + "data_type": "DOUBLE", + "constraints": [], + "quote": null, + "tags": [] + }, + "status": { + "name": "status", + "description": "", + "meta": {}, + "data_type": "VARCHAR", + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": {}, + "source_meta": {}, + "tags": [], + "config": {}, + "patch_path": null, + "unrendered_config": {}, + "relation_name": null + } + }, + "macros": {}, + "exposures": {}, + "metrics": {}, + "groups": {}, + "selectors": {}, + "disabled": {}, + "parent_map": { + "model.failure_corpus.customer_totals": [ + "model.failure_corpus.stg_orders" + ], + "model.failure_corpus.stg_orders": [ + "source.failure_corpus.raw_orders" + ], + "source.failure_corpus.raw_orders": [] + }, + "group_map": {}, + "docs": {}, + "saved_queries": {}, + "semantic_models": {}, + "unit_tests": {} + } +} diff --git a/tests/goldens/compiler/failure/hook-role-unsupported-entry.json b/tests/goldens/compiler/failure/hook-role-unsupported-entry.json new file mode 100644 index 0000000000..c9109a0c9a --- /dev/null +++ b/tests/goldens/compiler/failure/hook-role-unsupported-entry.json @@ -0,0 +1,18 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "D013", + "message": "Unsupported entries in models/marts/_sqlbuild/hooks/: models/marts/_sqlbuild/hooks/notes.sql; hooks/ accepts only python/, sql/" + } + ] + } + ], + "compiled": {}, + "manifest": null +} diff --git a/tests/goldens/compiler/failure/hook-unknown-argument.json b/tests/goldens/compiler/failure/hook-unknown-argument.json new file mode 100644 index 0000000000..accbb62629 --- /dev/null +++ b/tests/goldens/compiler/failure/hook-unknown-argument.json @@ -0,0 +1,18 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "P001", + "message": "/models/marts/customer_totals.sql post_hooks[0] sql(\"record_refresh\") passes unknown argument(s) to SQL hook 'record_refresh' in hooks/sql/record_refresh.sql: tag" + } + ] + } + ], + "compiled": {}, + "manifest": null +} diff --git a/tests/goldens/compiler/failure/hook-without-header.json b/tests/goldens/compiler/failure/hook-without-header.json new file mode 100644 index 0000000000..0ab3158d7b --- /dev/null +++ b/tests/goldens/compiler/failure/hook-without-header.json @@ -0,0 +1,18 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "D014", + "message": "SQL hook '/hooks/sql/record_refresh.sql' must start with a HOOK() header as the first non-whitespace content" + } + ] + } + ], + "compiled": {}, + "manifest": null +} diff --git a/tests/goldens/compiler/failure/inaccessible-declaration.json b/tests/goldens/compiler/failure/inaccessible-declaration.json new file mode 100644 index 0000000000..905dddc45a --- /dev/null +++ b/tests/goldens/compiler/failure/inaccessible-declaration.json @@ -0,0 +1,18 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "P001", + "message": "Constant 'minimum_amount' is known but inaccessible in '/models/staging/stg_orders.sql'. It is defined at models/marts/_sqlbuild/_constants/limits.sql:1:1 with local scope owned by 'models/marts'; consumer path: '/models/staging/stg_orders.sql'" + } + ] + } + ], + "compiled": {}, + "manifest": null +} diff --git a/tests/goldens/compiler/failure/inherited-declaration-used-in-one-folder.json b/tests/goldens/compiler/failure/inherited-declaration-used-in-one-folder.json new file mode 100644 index 0000000000..45086430ab --- /dev/null +++ b/tests/goldens/compiler/failure/inherited-declaration-used-in-one-folder.json @@ -0,0 +1,270 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "S008", + "message": "Declaration 'constant:minimum_amount' is currently descendant-public at 'models/staging' (models/staging/_sqlbuild/constants/limits.sql); required exact-owner-private at 'models/staging'. Consumers: model:stg_orders. Move it to 'models/staging/_sqlbuild/_constants/'", + "path": "models/staging/_sqlbuild/constants/limits.sql", + "line": 1, + "column": 1, + "location": { + "path": "models/staging/_sqlbuild/constants/limits.sql", + "line": 1, + "column": 1 + } + } + ] + } + ], + "compiled": { + "models/marts/customer_totals.sql": [ + "SELECT customer_id, SUM(amount) AS total_amount", + "FROM __ref(\"stg_orders\")", + "GROUP BY customer_id", + "" + ], + "models/staging/stg_orders.sql": [ + "SELECT order_id, customer_id, amount, status", + "FROM __source(\"raw_orders\")", + "WHERE amount > 1", + "" + ] + }, + "manifest": { + "nodes": { + "model.failure_corpus.customer_totals": { + "database": null, + "schema": "main", + "name": "customer_totals", + "resource_type": "model", + "package_name": "failure_corpus", + "path": "models/marts/customer_totals.sql", + "original_file_path": "models/marts/customer_totals.sql", + "unique_id": "model.failure_corpus.customer_totals", + "fqn": [ + "failure_corpus", + "models", + "marts", + "customer_totals" + ], + "alias": "customer_totals", + "config": { + "enabled": true, + "alias": null, + "schema": null, + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Order totals per customer", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.customer_totals", + "depends_on": { + "macros": [], + "nodes": [ + "model.failure_corpus.stg_orders" + ] + } + }, + "model.failure_corpus.stg_orders": { + "database": null, + "schema": "main", + "name": "stg_orders", + "resource_type": "model", + "package_name": "failure_corpus", + "path": "models/staging/stg_orders.sql", + "original_file_path": "models/staging/stg_orders.sql", + "unique_id": "model.failure_corpus.stg_orders", + "fqn": [ + "failure_corpus", + "models", + "staging", + "stg_orders" + ], + "alias": "stg_orders", + "config": { + "enabled": true, + "alias": null, + "schema": null, + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Staged orders", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.stg_orders", + "depends_on": { + "macros": [], + "nodes": [ + "source.failure_corpus.raw_orders" + ] + } + } + }, + "sources": { + "source.failure_corpus.raw_orders": { + "database": null, + "schema": "", + "name": "raw_orders", + "resource_type": "source", + "package_name": "failure_corpus", + "path": "sources/raw.yml", + "original_file_path": "sources/raw.yml", + "unique_id": "source.failure_corpus.raw_orders", + "fqn": [ + "failure_corpus", + "raw_orders" + ], + "source_name": "raw_orders", + "source_description": "", + "loader": "", + "identifier": "raw_orders", + "quoting": {}, + "loaded_at_field": null, + "freshness": null, + "external": null, + "description": "Orders feed.", + "columns": { + "order_id": { + "name": "order_id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "customer_id": { + "name": "customer_id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "amount": { + "name": "amount", + "description": "", + "meta": {}, + "data_type": "DOUBLE", + "constraints": [], + "quote": null, + "tags": [] + }, + "status": { + "name": "status", + "description": "", + "meta": {}, + "data_type": "VARCHAR", + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": {}, + "source_meta": {}, + "tags": [], + "config": {}, + "patch_path": null, + "unrendered_config": {}, + "relation_name": null + } + }, + "macros": {}, + "exposures": {}, + "metrics": {}, + "groups": {}, + "selectors": {}, + "disabled": {}, + "parent_map": { + "model.failure_corpus.customer_totals": [ + "model.failure_corpus.stg_orders" + ], + "model.failure_corpus.stg_orders": [ + "source.failure_corpus.raw_orders" + ], + "source.failure_corpus.raw_orders": [] + }, + "group_map": {}, + "docs": {}, + "saved_queries": {}, + "semantic_models": {}, + "unit_tests": {} + } +} diff --git a/tests/goldens/compiler/failure/invalid-decimal-constant.json b/tests/goldens/compiler/failure/invalid-decimal-constant.json new file mode 100644 index 0000000000..a1488abd9f --- /dev/null +++ b/tests/goldens/compiler/failure/invalid-decimal-constant.json @@ -0,0 +1,18 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "D013", + "message": "/models/staging/_sqlbuild/_constants/limits.sql constant 'minimum_amount' has invalid decimal value 'ten'" + } + ] + } + ], + "compiled": {}, + "manifest": null +} diff --git a/tests/goldens/compiler/failure/invalid-declaration-name.json b/tests/goldens/compiler/failure/invalid-declaration-name.json new file mode 100644 index 0000000000..74a1835434 --- /dev/null +++ b/tests/goldens/compiler/failure/invalid-declaration-name.json @@ -0,0 +1,19 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "D016", + "message": "Invalid public constant identity 'MinimumAmount' in /models/staging/_sqlbuild/_constants/limits.sql; use snake_case 'minimum_amount'", + "help": "Rename the authored identity and update its references, selectors, and integration keys. SQLBuild does not silently normalize resource identities. Double underscores remain valid." + } + ] + } + ], + "compiled": {}, + "manifest": null +} diff --git a/tests/goldens/compiler/failure/invalid-reference-calls-in-two-files.json b/tests/goldens/compiler/failure/invalid-reference-calls-in-two-files.json new file mode 100644 index 0000000000..021ff54895 --- /dev/null +++ b/tests/goldens/compiler/failure/invalid-reference-calls-in-two-files.json @@ -0,0 +1,285 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "P012", + "message": "__ref(stg_orders) is not a valid __ref() call", + "resource_type": "model", + "resource_name": "customer_totals", + "path": "models/marts/customer_totals.sql", + "line": 6, + "column": 6, + "location": { + "path": "models/marts/customer_totals.sql", + "line": 6, + "column": 6, + "end_line": 6, + "end_column": 23 + }, + "help": "__ref() takes exactly one double-quoted name, with no comments or extra spaces inside the parentheses: __ref(\"stg_orders\")" + }, + { + "phase": "compile", + "severity": "error", + "code": "P012", + "message": "__source('raw_orders') is not a valid __source() call", + "resource_type": "model", + "resource_name": "stg_orders", + "path": "models/staging/stg_orders.sql", + "line": 6, + "column": 6, + "location": { + "path": "models/staging/stg_orders.sql", + "line": 6, + "column": 6, + "end_line": 6, + "end_column": 28 + }, + "help": "__source() takes exactly one double-quoted name, with no comments or extra spaces inside the parentheses: __source(\"raw_orders\")" + } + ] + } + ], + "compiled": { + "models/marts/customer_totals.sql": [ + "SELECT customer_id, SUM(amount) AS total_amount", + "FROM __ref(stg_orders)", + "GROUP BY customer_id", + "" + ], + "models/staging/stg_orders.sql": [ + "SELECT order_id, customer_id, amount, status", + "FROM __source('raw_orders')", + "" + ] + }, + "manifest": { + "nodes": { + "model.failure_corpus.customer_totals": { + "database": null, + "schema": "main", + "name": "customer_totals", + "resource_type": "model", + "package_name": "failure_corpus", + "path": "models/marts/customer_totals.sql", + "original_file_path": "models/marts/customer_totals.sql", + "unique_id": "model.failure_corpus.customer_totals", + "fqn": [ + "failure_corpus", + "models", + "marts", + "customer_totals" + ], + "alias": "customer_totals", + "config": { + "enabled": true, + "alias": null, + "schema": null, + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Order totals per customer", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.customer_totals", + "depends_on": { + "macros": [], + "nodes": [] + } + }, + "model.failure_corpus.stg_orders": { + "database": null, + "schema": "main", + "name": "stg_orders", + "resource_type": "model", + "package_name": "failure_corpus", + "path": "models/staging/stg_orders.sql", + "original_file_path": "models/staging/stg_orders.sql", + "unique_id": "model.failure_corpus.stg_orders", + "fqn": [ + "failure_corpus", + "models", + "staging", + "stg_orders" + ], + "alias": "stg_orders", + "config": { + "enabled": true, + "alias": null, + "schema": null, + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Staged orders", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.stg_orders", + "depends_on": { + "macros": [], + "nodes": [] + } + } + }, + "sources": { + "source.failure_corpus.raw_orders": { + "database": null, + "schema": "", + "name": "raw_orders", + "resource_type": "source", + "package_name": "failure_corpus", + "path": "sources/raw.yml", + "original_file_path": "sources/raw.yml", + "unique_id": "source.failure_corpus.raw_orders", + "fqn": [ + "failure_corpus", + "raw_orders" + ], + "source_name": "raw_orders", + "source_description": "", + "loader": "", + "identifier": "raw_orders", + "quoting": {}, + "loaded_at_field": null, + "freshness": null, + "external": null, + "description": "Orders feed.", + "columns": { + "order_id": { + "name": "order_id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "customer_id": { + "name": "customer_id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "amount": { + "name": "amount", + "description": "", + "meta": {}, + "data_type": "DOUBLE", + "constraints": [], + "quote": null, + "tags": [] + }, + "status": { + "name": "status", + "description": "", + "meta": {}, + "data_type": "VARCHAR", + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": {}, + "source_meta": {}, + "tags": [], + "config": {}, + "patch_path": null, + "unrendered_config": {}, + "relation_name": null + } + }, + "macros": {}, + "exposures": {}, + "metrics": {}, + "groups": {}, + "selectors": {}, + "disabled": {}, + "parent_map": { + "model.failure_corpus.customer_totals": [], + "model.failure_corpus.stg_orders": [], + "source.failure_corpus.raw_orders": [] + }, + "group_map": {}, + "docs": {}, + "saved_queries": {}, + "semantic_models": {}, + "unit_tests": {} + } +} diff --git a/tests/goldens/compiler/failure/invalid-resource-name.json b/tests/goldens/compiler/failure/invalid-resource-name.json new file mode 100644 index 0000000000..33c6c184a5 --- /dev/null +++ b/tests/goldens/compiler/failure/invalid-resource-name.json @@ -0,0 +1,19 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "D016", + "message": "Invalid model identity 'order-totals' in models/marts/order-totals.sql; use snake_case 'order_totals'", + "help": "Rename the authored identity and update its references, selectors, and integration keys. SQLBuild does not silently normalize resource identities. Double underscores remain valid." + } + ] + } + ], + "compiled": {}, + "manifest": null +} diff --git a/tests/goldens/compiler/failure/legacy-event-exporters-directory.json b/tests/goldens/compiler/failure/legacy-event-exporters-directory.json new file mode 100644 index 0000000000..2c0a87d690 --- /dev/null +++ b/tests/goldens/compiler/failure/legacy-event-exporters-directory.json @@ -0,0 +1,18 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "D015", + "message": "event_exporters/ was replaced by sinks/ and @lifecycle_event_sink" + } + ] + } + ], + "compiled": {}, + "manifest": null +} diff --git a/tests/goldens/compiler/failure/lifecycle-sink-default-parameter.json b/tests/goldens/compiler/failure/lifecycle-sink-default-parameter.json new file mode 100644 index 0000000000..04787f8f41 --- /dev/null +++ b/tests/goldens/compiler/failure/lifecycle-sink-default-parameter.json @@ -0,0 +1,18 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "D015", + "message": "Event exporter 'ignore_events' in sinks/lifecycle.py parameters must not have defaults" + } + ] + } + ], + "compiled": {}, + "manifest": null +} diff --git a/tests/goldens/compiler/failure/lifecycle-sink-unknown-provider.json b/tests/goldens/compiler/failure/lifecycle-sink-unknown-provider.json new file mode 100644 index 0000000000..e1b706e978 --- /dev/null +++ b/tests/goldens/compiler/failure/lifecycle-sink-unknown-provider.json @@ -0,0 +1,18 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "D015", + "message": "Event exporter 'publish_events' in sinks/lifecycle.py requires unknown provider 'destination_client'" + } + ] + } + ], + "compiled": {}, + "manifest": null +} diff --git a/tests/goldens/compiler/failure/lineage-opted-out-except-and-ctes.json b/tests/goldens/compiler/failure/lineage-opted-out-except-and-ctes.json new file mode 100644 index 0000000000..0e07d34bb1 --- /dev/null +++ b/tests/goldens/compiler/failure/lineage-opted-out-except-and-ctes.json @@ -0,0 +1,353 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "B004", + "message": "Unknown table or alias 'x' referenced by column 'order_id'", + "resource_type": "model", + "resource_name": "broken_orders", + "path": "models/marts/broken_orders.sql", + "line": 5, + "column": 8, + "location": { + "path": "models/marts/broken_orders.sql", + "line": 5, + "column": 8, + "end_line": 5, + "end_column": 9 + }, + "help": "give each referenced relation a visible alias in this scope" + } + ] + } + ], + "compiled": { + "models/marts/broken_orders.sql": [ + "SELECT x.order_id", + "FROM __ref(\"stg_orders\") o", + "" + ], + "models/marts/customer_totals.sql": [ + "WITH kept AS (SELECT customer_id FROM __ref(\"stg_orders\")),", + "again AS (SELECT * FROM kept)", + "SELECT customer_id, COUNT(*) AS total_amount FROM again GROUP BY 1", + "EXCEPT", + "SELECT customer_id, amount FROM __ref(\"stg_orders\")", + "" + ], + "models/staging/stg_orders.sql": [ + "SELECT order_id, customer_id, amount, status", + "FROM __source(\"raw_orders\")", + "" + ] + }, + "manifest": { + "nodes": { + "model.failure_corpus.broken_orders": { + "database": null, + "schema": "main", + "name": "broken_orders", + "resource_type": "model", + "package_name": "failure_corpus", + "path": "models/marts/broken_orders.sql", + "original_file_path": "models/marts/broken_orders.sql", + "unique_id": "model.failure_corpus.broken_orders", + "fqn": [ + "failure_corpus", + "models", + "marts", + "broken_orders" + ], + "alias": "broken_orders", + "config": { + "enabled": true, + "alias": null, + "schema": null, + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Orders with an unknown qualifier", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.broken_orders", + "depends_on": { + "macros": [], + "nodes": [ + "model.failure_corpus.stg_orders" + ] + } + }, + "model.failure_corpus.customer_totals": { + "database": null, + "schema": "main", + "name": "customer_totals", + "resource_type": "model", + "package_name": "failure_corpus", + "path": "models/marts/customer_totals.sql", + "original_file_path": "models/marts/customer_totals.sql", + "unique_id": "model.failure_corpus.customer_totals", + "fqn": [ + "failure_corpus", + "models", + "marts", + "customer_totals" + ], + "alias": "customer_totals", + "config": { + "enabled": true, + "alias": null, + "schema": null, + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Order totals per customer", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.customer_totals", + "depends_on": { + "macros": [], + "nodes": [ + "model.failure_corpus.stg_orders" + ] + } + }, + "model.failure_corpus.stg_orders": { + "database": null, + "schema": "main", + "name": "stg_orders", + "resource_type": "model", + "package_name": "failure_corpus", + "path": "models/staging/stg_orders.sql", + "original_file_path": "models/staging/stg_orders.sql", + "unique_id": "model.failure_corpus.stg_orders", + "fqn": [ + "failure_corpus", + "models", + "staging", + "stg_orders" + ], + "alias": "stg_orders", + "config": { + "enabled": true, + "alias": null, + "schema": null, + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Staged orders", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.stg_orders", + "depends_on": { + "macros": [], + "nodes": [ + "source.failure_corpus.raw_orders" + ] + } + } + }, + "sources": { + "source.failure_corpus.raw_orders": { + "database": null, + "schema": "", + "name": "raw_orders", + "resource_type": "source", + "package_name": "failure_corpus", + "path": "sources/raw.yml", + "original_file_path": "sources/raw.yml", + "unique_id": "source.failure_corpus.raw_orders", + "fqn": [ + "failure_corpus", + "raw_orders" + ], + "source_name": "raw_orders", + "source_description": "", + "loader": "", + "identifier": "raw_orders", + "quoting": {}, + "loaded_at_field": null, + "freshness": null, + "external": null, + "description": "Orders feed.", + "columns": { + "order_id": { + "name": "order_id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "customer_id": { + "name": "customer_id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "amount": { + "name": "amount", + "description": "", + "meta": {}, + "data_type": "DOUBLE", + "constraints": [], + "quote": null, + "tags": [] + }, + "status": { + "name": "status", + "description": "", + "meta": {}, + "data_type": "VARCHAR", + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": {}, + "source_meta": {}, + "tags": [], + "config": {}, + "patch_path": null, + "unrendered_config": {}, + "relation_name": null + } + }, + "macros": {}, + "exposures": {}, + "metrics": {}, + "groups": {}, + "selectors": {}, + "disabled": {}, + "parent_map": { + "model.failure_corpus.broken_orders": [ + "model.failure_corpus.stg_orders" + ], + "model.failure_corpus.customer_totals": [ + "model.failure_corpus.stg_orders" + ], + "model.failure_corpus.stg_orders": [ + "source.failure_corpus.raw_orders" + ], + "source.failure_corpus.raw_orders": [] + }, + "group_map": {}, + "docs": {}, + "saved_queries": {}, + "semantic_models": {}, + "unit_tests": {} + } +} diff --git a/tests/goldens/compiler/failure/lineage-opted-out-quoted-columns.json b/tests/goldens/compiler/failure/lineage-opted-out-quoted-columns.json new file mode 100644 index 0000000000..708c091054 --- /dev/null +++ b/tests/goldens/compiler/failure/lineage-opted-out-quoted-columns.json @@ -0,0 +1,352 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "B004", + "message": "Unknown table or alias 'x' referenced by column 'order_id'", + "resource_type": "model", + "resource_name": "broken_orders", + "path": "models/marts/broken_orders.sql", + "line": 5, + "column": 8, + "location": { + "path": "models/marts/broken_orders.sql", + "line": 5, + "column": 8, + "end_line": 5, + "end_column": 9 + }, + "help": "give each referenced relation a visible alias in this scope" + } + ] + } + ], + "compiled": { + "models/marts/broken_orders.sql": [ + "SELECT x.order_id", + "FROM __ref(\"stg_orders\") o", + "" + ], + "models/marts/customer_totals.sql": [ + "SELECT \"Customer_ID\", SUM(O.Amount) AS \"Total_Amount\",", + " CAST(MAX(o.status) AS VARCHAR) AS last_status, 'fixed' AS label", + "FROM __ref(\"stg_orders\") AS O", + "GROUP BY 1", + "" + ], + "models/staging/stg_orders.sql": [ + "SELECT order_id, customer_id, amount, status", + "FROM __source(\"raw_orders\")", + "" + ] + }, + "manifest": { + "nodes": { + "model.failure_corpus.broken_orders": { + "database": null, + "schema": "main", + "name": "broken_orders", + "resource_type": "model", + "package_name": "failure_corpus", + "path": "models/marts/broken_orders.sql", + "original_file_path": "models/marts/broken_orders.sql", + "unique_id": "model.failure_corpus.broken_orders", + "fqn": [ + "failure_corpus", + "models", + "marts", + "broken_orders" + ], + "alias": "broken_orders", + "config": { + "enabled": true, + "alias": null, + "schema": null, + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Orders with an unknown qualifier", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.broken_orders", + "depends_on": { + "macros": [], + "nodes": [ + "model.failure_corpus.stg_orders" + ] + } + }, + "model.failure_corpus.customer_totals": { + "database": null, + "schema": "main", + "name": "customer_totals", + "resource_type": "model", + "package_name": "failure_corpus", + "path": "models/marts/customer_totals.sql", + "original_file_path": "models/marts/customer_totals.sql", + "unique_id": "model.failure_corpus.customer_totals", + "fqn": [ + "failure_corpus", + "models", + "marts", + "customer_totals" + ], + "alias": "customer_totals", + "config": { + "enabled": true, + "alias": null, + "schema": null, + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Order totals per customer", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.customer_totals", + "depends_on": { + "macros": [], + "nodes": [ + "model.failure_corpus.stg_orders" + ] + } + }, + "model.failure_corpus.stg_orders": { + "database": null, + "schema": "main", + "name": "stg_orders", + "resource_type": "model", + "package_name": "failure_corpus", + "path": "models/staging/stg_orders.sql", + "original_file_path": "models/staging/stg_orders.sql", + "unique_id": "model.failure_corpus.stg_orders", + "fqn": [ + "failure_corpus", + "models", + "staging", + "stg_orders" + ], + "alias": "stg_orders", + "config": { + "enabled": true, + "alias": null, + "schema": null, + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Staged orders", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.stg_orders", + "depends_on": { + "macros": [], + "nodes": [ + "source.failure_corpus.raw_orders" + ] + } + } + }, + "sources": { + "source.failure_corpus.raw_orders": { + "database": null, + "schema": "", + "name": "raw_orders", + "resource_type": "source", + "package_name": "failure_corpus", + "path": "sources/raw.yml", + "original_file_path": "sources/raw.yml", + "unique_id": "source.failure_corpus.raw_orders", + "fqn": [ + "failure_corpus", + "raw_orders" + ], + "source_name": "raw_orders", + "source_description": "", + "loader": "", + "identifier": "raw_orders", + "quoting": {}, + "loaded_at_field": null, + "freshness": null, + "external": null, + "description": "Orders feed.", + "columns": { + "order_id": { + "name": "order_id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "customer_id": { + "name": "customer_id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "amount": { + "name": "amount", + "description": "", + "meta": {}, + "data_type": "DOUBLE", + "constraints": [], + "quote": null, + "tags": [] + }, + "status": { + "name": "status", + "description": "", + "meta": {}, + "data_type": "VARCHAR", + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": {}, + "source_meta": {}, + "tags": [], + "config": {}, + "patch_path": null, + "unrendered_config": {}, + "relation_name": null + } + }, + "macros": {}, + "exposures": {}, + "metrics": {}, + "groups": {}, + "selectors": {}, + "disabled": {}, + "parent_map": { + "model.failure_corpus.broken_orders": [ + "model.failure_corpus.stg_orders" + ], + "model.failure_corpus.customer_totals": [ + "model.failure_corpus.stg_orders" + ], + "model.failure_corpus.stg_orders": [ + "source.failure_corpus.raw_orders" + ], + "source.failure_corpus.raw_orders": [] + }, + "group_map": {}, + "docs": {}, + "saved_queries": {}, + "semantic_models": {}, + "unit_tests": {} + } +} diff --git a/tests/goldens/compiler/failure/lineage-opted-out-star.json b/tests/goldens/compiler/failure/lineage-opted-out-star.json new file mode 100644 index 0000000000..0d8db9c495 --- /dev/null +++ b/tests/goldens/compiler/failure/lineage-opted-out-star.json @@ -0,0 +1,350 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "B004", + "message": "Unknown table or alias 'x' referenced by column 'order_id'", + "resource_type": "model", + "resource_name": "broken_orders", + "path": "models/marts/broken_orders.sql", + "line": 5, + "column": 8, + "location": { + "path": "models/marts/broken_orders.sql", + "line": 5, + "column": 8, + "end_line": 5, + "end_column": 9 + }, + "help": "give each referenced relation a visible alias in this scope" + } + ] + } + ], + "compiled": { + "models/marts/broken_orders.sql": [ + "SELECT x.order_id", + "FROM __ref(\"stg_orders\") o", + "" + ], + "models/marts/customer_totals.sql": [ + "SELECT o.order_id AS first_order, o.*", + "FROM __ref(\"stg_orders\") o", + "" + ], + "models/staging/stg_orders.sql": [ + "SELECT order_id, customer_id, amount, status", + "FROM __source(\"raw_orders\")", + "" + ] + }, + "manifest": { + "nodes": { + "model.failure_corpus.broken_orders": { + "database": null, + "schema": "main", + "name": "broken_orders", + "resource_type": "model", + "package_name": "failure_corpus", + "path": "models/marts/broken_orders.sql", + "original_file_path": "models/marts/broken_orders.sql", + "unique_id": "model.failure_corpus.broken_orders", + "fqn": [ + "failure_corpus", + "models", + "marts", + "broken_orders" + ], + "alias": "broken_orders", + "config": { + "enabled": true, + "alias": null, + "schema": null, + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Orders with an unknown qualifier", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.broken_orders", + "depends_on": { + "macros": [], + "nodes": [ + "model.failure_corpus.stg_orders" + ] + } + }, + "model.failure_corpus.customer_totals": { + "database": null, + "schema": "main", + "name": "customer_totals", + "resource_type": "model", + "package_name": "failure_corpus", + "path": "models/marts/customer_totals.sql", + "original_file_path": "models/marts/customer_totals.sql", + "unique_id": "model.failure_corpus.customer_totals", + "fqn": [ + "failure_corpus", + "models", + "marts", + "customer_totals" + ], + "alias": "customer_totals", + "config": { + "enabled": true, + "alias": null, + "schema": null, + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Order totals per customer", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.customer_totals", + "depends_on": { + "macros": [], + "nodes": [ + "model.failure_corpus.stg_orders" + ] + } + }, + "model.failure_corpus.stg_orders": { + "database": null, + "schema": "main", + "name": "stg_orders", + "resource_type": "model", + "package_name": "failure_corpus", + "path": "models/staging/stg_orders.sql", + "original_file_path": "models/staging/stg_orders.sql", + "unique_id": "model.failure_corpus.stg_orders", + "fqn": [ + "failure_corpus", + "models", + "staging", + "stg_orders" + ], + "alias": "stg_orders", + "config": { + "enabled": true, + "alias": null, + "schema": null, + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Staged orders", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.stg_orders", + "depends_on": { + "macros": [], + "nodes": [ + "source.failure_corpus.raw_orders" + ] + } + } + }, + "sources": { + "source.failure_corpus.raw_orders": { + "database": null, + "schema": "", + "name": "raw_orders", + "resource_type": "source", + "package_name": "failure_corpus", + "path": "sources/raw.yml", + "original_file_path": "sources/raw.yml", + "unique_id": "source.failure_corpus.raw_orders", + "fqn": [ + "failure_corpus", + "raw_orders" + ], + "source_name": "raw_orders", + "source_description": "", + "loader": "", + "identifier": "raw_orders", + "quoting": {}, + "loaded_at_field": null, + "freshness": null, + "external": null, + "description": "Orders feed.", + "columns": { + "order_id": { + "name": "order_id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "customer_id": { + "name": "customer_id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "amount": { + "name": "amount", + "description": "", + "meta": {}, + "data_type": "DOUBLE", + "constraints": [], + "quote": null, + "tags": [] + }, + "status": { + "name": "status", + "description": "", + "meta": {}, + "data_type": "VARCHAR", + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": {}, + "source_meta": {}, + "tags": [], + "config": {}, + "patch_path": null, + "unrendered_config": {}, + "relation_name": null + } + }, + "macros": {}, + "exposures": {}, + "metrics": {}, + "groups": {}, + "selectors": {}, + "disabled": {}, + "parent_map": { + "model.failure_corpus.broken_orders": [ + "model.failure_corpus.stg_orders" + ], + "model.failure_corpus.customer_totals": [ + "model.failure_corpus.stg_orders" + ], + "model.failure_corpus.stg_orders": [ + "source.failure_corpus.raw_orders" + ], + "source.failure_corpus.raw_orders": [] + }, + "group_map": {}, + "docs": {}, + "saved_queries": {}, + "semantic_models": {}, + "unit_tests": {} + } +} diff --git a/tests/goldens/compiler/failure/lineage-opted-out-union-of-stars.json b/tests/goldens/compiler/failure/lineage-opted-out-union-of-stars.json new file mode 100644 index 0000000000..854fbc877f --- /dev/null +++ b/tests/goldens/compiler/failure/lineage-opted-out-union-of-stars.json @@ -0,0 +1,354 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "B004", + "message": "Unknown table or alias 'x' referenced by column 'order_id'", + "resource_type": "model", + "resource_name": "broken_orders", + "path": "models/marts/broken_orders.sql", + "line": 5, + "column": 8, + "location": { + "path": "models/marts/broken_orders.sql", + "line": 5, + "column": 8, + "end_line": 5, + "end_column": 9 + }, + "help": "give each referenced relation a visible alias in this scope" + } + ] + } + ], + "compiled": { + "models/marts/broken_orders.sql": [ + "SELECT x.order_id", + "FROM __ref(\"stg_orders\") o", + "" + ], + "models/marts/customer_totals.sql": [ + "SELECT *, 1 AS copy_number", + "FROM __ref(\"stg_orders\")", + "UNION ALL", + "SELECT \"R\".*, 2 FROM __source(\"raw_orders\") AS \"R\" JOIN __ref(\"stg_orders\") s ON TRUE", + "" + ], + "models/staging/stg_orders.sql": [ + "SELECT order_id, customer_id, amount, status", + "FROM __source(\"raw_orders\")", + "" + ] + }, + "manifest": { + "nodes": { + "model.failure_corpus.broken_orders": { + "database": null, + "schema": "main", + "name": "broken_orders", + "resource_type": "model", + "package_name": "failure_corpus", + "path": "models/marts/broken_orders.sql", + "original_file_path": "models/marts/broken_orders.sql", + "unique_id": "model.failure_corpus.broken_orders", + "fqn": [ + "failure_corpus", + "models", + "marts", + "broken_orders" + ], + "alias": "broken_orders", + "config": { + "enabled": true, + "alias": null, + "schema": null, + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Orders with an unknown qualifier", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.broken_orders", + "depends_on": { + "macros": [], + "nodes": [ + "model.failure_corpus.stg_orders" + ] + } + }, + "model.failure_corpus.customer_totals": { + "database": null, + "schema": "main", + "name": "customer_totals", + "resource_type": "model", + "package_name": "failure_corpus", + "path": "models/marts/customer_totals.sql", + "original_file_path": "models/marts/customer_totals.sql", + "unique_id": "model.failure_corpus.customer_totals", + "fqn": [ + "failure_corpus", + "models", + "marts", + "customer_totals" + ], + "alias": "customer_totals", + "config": { + "enabled": true, + "alias": null, + "schema": null, + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Order totals per customer", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.customer_totals", + "depends_on": { + "macros": [], + "nodes": [ + "model.failure_corpus.stg_orders", + "source.failure_corpus.raw_orders" + ] + } + }, + "model.failure_corpus.stg_orders": { + "database": null, + "schema": "main", + "name": "stg_orders", + "resource_type": "model", + "package_name": "failure_corpus", + "path": "models/staging/stg_orders.sql", + "original_file_path": "models/staging/stg_orders.sql", + "unique_id": "model.failure_corpus.stg_orders", + "fqn": [ + "failure_corpus", + "models", + "staging", + "stg_orders" + ], + "alias": "stg_orders", + "config": { + "enabled": true, + "alias": null, + "schema": null, + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Staged orders", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.stg_orders", + "depends_on": { + "macros": [], + "nodes": [ + "source.failure_corpus.raw_orders" + ] + } + } + }, + "sources": { + "source.failure_corpus.raw_orders": { + "database": null, + "schema": "", + "name": "raw_orders", + "resource_type": "source", + "package_name": "failure_corpus", + "path": "sources/raw.yml", + "original_file_path": "sources/raw.yml", + "unique_id": "source.failure_corpus.raw_orders", + "fqn": [ + "failure_corpus", + "raw_orders" + ], + "source_name": "raw_orders", + "source_description": "", + "loader": "", + "identifier": "raw_orders", + "quoting": {}, + "loaded_at_field": null, + "freshness": null, + "external": null, + "description": "Orders feed.", + "columns": { + "order_id": { + "name": "order_id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "customer_id": { + "name": "customer_id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "amount": { + "name": "amount", + "description": "", + "meta": {}, + "data_type": "DOUBLE", + "constraints": [], + "quote": null, + "tags": [] + }, + "status": { + "name": "status", + "description": "", + "meta": {}, + "data_type": "VARCHAR", + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": {}, + "source_meta": {}, + "tags": [], + "config": {}, + "patch_path": null, + "unrendered_config": {}, + "relation_name": null + } + }, + "macros": {}, + "exposures": {}, + "metrics": {}, + "groups": {}, + "selectors": {}, + "disabled": {}, + "parent_map": { + "model.failure_corpus.broken_orders": [ + "model.failure_corpus.stg_orders" + ], + "model.failure_corpus.customer_totals": [ + "model.failure_corpus.stg_orders", + "source.failure_corpus.raw_orders" + ], + "model.failure_corpus.stg_orders": [ + "source.failure_corpus.raw_orders" + ], + "source.failure_corpus.raw_orders": [] + }, + "group_map": {}, + "docs": {}, + "saved_queries": {}, + "semantic_models": {}, + "unit_tests": {} + } +} diff --git a/tests/goldens/compiler/failure/list-constant-mixed-types.json b/tests/goldens/compiler/failure/list-constant-mixed-types.json new file mode 100644 index 0000000000..e1ef440e76 --- /dev/null +++ b/tests/goldens/compiler/failure/list-constant-mixed-types.json @@ -0,0 +1,18 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "D013", + "message": "/models/staging/_sqlbuild/_constants/statuses.sql constant 'kept_statuses'[1] has type integer; expected string" + } + ] + } + ], + "compiled": {}, + "manifest": null +} diff --git a/tests/goldens/compiler/failure/local-config-unknown-key.json b/tests/goldens/compiler/failure/local-config-unknown-key.json new file mode 100644 index 0000000000..82ca337180 --- /dev/null +++ b/tests/goldens/compiler/failure/local-config-unknown-key.json @@ -0,0 +1,19 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "D001", + "message": "/sqlbuild_local.toml settings contains unknown key(s): sql_analysiss. Allowed keys: auto_load_sources, column_contract_mode, concurrency, default_audit_run_scope, default_audit_severity, max_concurrency, microbatch_concurrency, microbatch_unaccounted_partition_policy, query_change_tracking, require_sql_analysis, sql_analysis, sql_validation, table_promotion_mode", + "help": "did you mean 'sql_analysis'?" + } + ] + } + ], + "compiled": {}, + "manifest": null +} diff --git a/tests/goldens/compiler/failure/local-macro-needed-below-owner.json b/tests/goldens/compiler/failure/local-macro-needed-below-owner.json new file mode 100644 index 0000000000..5f0025f710 --- /dev/null +++ b/tests/goldens/compiler/failure/local-macro-needed-below-owner.json @@ -0,0 +1,412 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "S007", + "message": "Declaration 'macro:cents' is currently exact-owner-private at 'models/staging' (models/staging/_sqlbuild/_macros/orders.py); required exact-owner-private at 'models/staging/regional'. Consumers: macro:wrapped. Move it to 'models/staging/regional/_sqlbuild/_macros/'", + "path": "models/staging/_sqlbuild/_macros/orders.py", + "line": 1, + "column": 1, + "location": { + "path": "models/staging/_sqlbuild/_macros/orders.py", + "line": 1, + "column": 1 + } + }, + { + "phase": "compile", + "severity": "error", + "code": "S008", + "message": "Declaration 'macro:wrapped' is currently descendant-public at 'models/staging' (models/staging/_sqlbuild/macros/wrapping.py); required exact-owner-private at 'models/staging/regional'. Consumers: model:stg_regional_orders. Move it to 'models/staging/regional/_sqlbuild/_macros/'", + "path": "models/staging/_sqlbuild/macros/wrapping.py", + "line": 4, + "column": 1, + "location": { + "path": "models/staging/_sqlbuild/macros/wrapping.py", + "line": 4, + "column": 1 + } + } + ] + } + ], + "compiled": { + "models/marts/customer_totals.sql": [ + "SELECT customer_id, SUM(amount) AS total_amount", + "FROM __ref(\"stg_orders\")", + "GROUP BY customer_id", + "" + ], + "models/staging/regional/stg_regional_orders.sql": [ + "SELECT order_id, (amount * 100) AS amount_cents", + "FROM __ref(\"stg_orders\")", + "" + ], + "models/staging/stg_orders.sql": [ + "SELECT order_id, customer_id, amount, status", + "FROM __source(\"raw_orders\")", + "" + ] + }, + "manifest": { + "nodes": { + "model.failure_corpus.customer_totals": { + "database": null, + "schema": "main", + "name": "customer_totals", + "resource_type": "model", + "package_name": "failure_corpus", + "path": "models/marts/customer_totals.sql", + "original_file_path": "models/marts/customer_totals.sql", + "unique_id": "model.failure_corpus.customer_totals", + "fqn": [ + "failure_corpus", + "models", + "marts", + "customer_totals" + ], + "alias": "customer_totals", + "config": { + "enabled": true, + "alias": null, + "schema": null, + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Order totals per customer", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.customer_totals", + "depends_on": { + "macros": [], + "nodes": [ + "model.failure_corpus.stg_orders" + ] + } + }, + "model.failure_corpus.stg_regional_orders": { + "database": null, + "schema": "main", + "name": "stg_regional_orders", + "resource_type": "model", + "package_name": "failure_corpus", + "path": "models/staging/regional/stg_regional_orders.sql", + "original_file_path": "models/staging/regional/stg_regional_orders.sql", + "unique_id": "model.failure_corpus.stg_regional_orders", + "fqn": [ + "failure_corpus", + "models", + "staging", + "regional", + "stg_regional_orders" + ], + "alias": "stg_regional_orders", + "config": { + "enabled": true, + "alias": null, + "schema": null, + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Regional orders", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.stg_regional_orders", + "depends_on": { + "macros": [], + "nodes": [ + "model.failure_corpus.stg_orders" + ] + } + }, + "model.failure_corpus.stg_orders": { + "database": null, + "schema": "main", + "name": "stg_orders", + "resource_type": "model", + "package_name": "failure_corpus", + "path": "models/staging/stg_orders.sql", + "original_file_path": "models/staging/stg_orders.sql", + "unique_id": "model.failure_corpus.stg_orders", + "fqn": [ + "failure_corpus", + "models", + "staging", + "stg_orders" + ], + "alias": "stg_orders", + "config": { + "enabled": true, + "alias": null, + "schema": null, + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Staged orders", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.stg_orders", + "depends_on": { + "macros": [], + "nodes": [ + "source.failure_corpus.raw_orders" + ] + } + } + }, + "sources": { + "source.failure_corpus.raw_orders": { + "database": null, + "schema": "", + "name": "raw_orders", + "resource_type": "source", + "package_name": "failure_corpus", + "path": "sources/raw.yml", + "original_file_path": "sources/raw.yml", + "unique_id": "source.failure_corpus.raw_orders", + "fqn": [ + "failure_corpus", + "raw_orders" + ], + "source_name": "raw_orders", + "source_description": "", + "loader": "", + "identifier": "raw_orders", + "quoting": {}, + "loaded_at_field": null, + "freshness": null, + "external": null, + "description": "Orders feed.", + "columns": { + "order_id": { + "name": "order_id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "customer_id": { + "name": "customer_id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "amount": { + "name": "amount", + "description": "", + "meta": {}, + "data_type": "DOUBLE", + "constraints": [], + "quote": null, + "tags": [] + }, + "status": { + "name": "status", + "description": "", + "meta": {}, + "data_type": "VARCHAR", + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": {}, + "source_meta": {}, + "tags": [], + "config": {}, + "patch_path": null, + "unrendered_config": {}, + "relation_name": null + } + }, + "macros": { + "macro.failure_corpus.cents": { + "name": "cents", + "resource_type": "macro", + "package_name": "failure_corpus", + "path": "models/staging/_sqlbuild/_macros/orders.py", + "original_file_path": "models/staging/_sqlbuild/_macros/orders.py", + "unique_id": "macro.failure_corpus.cents", + "macro_sql": "def cents(expression: str) -> str:\n \"\"\"Convert to cents.\"\"\"\n return f\"({expression} * 100)\"\n", + "depends_on": { + "macros": [] + }, + "description": "", + "meta": { + "sqlbuild_visibility": "local", + "sqlbuild_scope_path": "models/staging" + }, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "arguments": [], + "supported_languages": null + }, + "macro.failure_corpus.wrapped": { + "name": "wrapped", + "resource_type": "macro", + "package_name": "failure_corpus", + "path": "models/staging/_sqlbuild/macros/wrapping.py", + "original_file_path": "models/staging/_sqlbuild/macros/wrapping.py", + "unique_id": "macro.failure_corpus.wrapped", + "macro_sql": "from models.staging._sqlbuild._macros.orders import cents\n\n\ndef wrapped(expression: str) -> str:\n \"\"\"Wrap cents.\"\"\"\n return cents(expression)\n", + "depends_on": { + "macros": [ + "macro.failure_corpus.cents" + ] + }, + "description": "", + "meta": { + "sqlbuild_visibility": "inherited", + "sqlbuild_scope_path": "models/staging" + }, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "arguments": [], + "supported_languages": null + } + }, + "exposures": {}, + "metrics": {}, + "groups": {}, + "selectors": {}, + "disabled": {}, + "parent_map": { + "model.failure_corpus.customer_totals": [ + "model.failure_corpus.stg_orders" + ], + "model.failure_corpus.stg_regional_orders": [ + "model.failure_corpus.stg_orders" + ], + "model.failure_corpus.stg_orders": [ + "source.failure_corpus.raw_orders" + ], + "source.failure_corpus.raw_orders": [] + }, + "group_map": {}, + "docs": {}, + "saved_queries": {}, + "semantic_models": {}, + "unit_tests": {} + } +} diff --git a/tests/goldens/compiler/failure/local-singular-audit.json b/tests/goldens/compiler/failure/local-singular-audit.json new file mode 100644 index 0000000000..5e954f12ed --- /dev/null +++ b/tests/goldens/compiler/failure/local-singular-audit.json @@ -0,0 +1,18 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "D013", + "message": "models/marts/_sqlbuild/_audits/singular/ is invalid: singular audits are never used by name, so folder-only visibility has no meaning; use _sqlbuild/audits/singular/" + } + ] + } + ], + "compiled": {}, + "manifest": null +} diff --git a/tests/goldens/compiler/failure/macro-arguments-do-not-parse.json b/tests/goldens/compiler/failure/macro-arguments-do-not-parse.json new file mode 100644 index 0000000000..76bd9e282b --- /dev/null +++ b/tests/goldens/compiler/failure/macro-arguments-do-not-parse.json @@ -0,0 +1,18 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "P001", + "message": "Macro arguments in '/models/staging/stg_orders.sql' could not be parsed: invalid syntax (, line 1)" + } + ] + } + ], + "compiled": {}, + "manifest": null +} diff --git a/tests/goldens/compiler/failure/macro-first-of-two-failing-models.json b/tests/goldens/compiler/failure/macro-first-of-two-failing-models.json new file mode 100644 index 0000000000..9a92d52682 --- /dev/null +++ b/tests/goldens/compiler/failure/macro-first-of-two-failing-models.json @@ -0,0 +1,18 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "P001", + "message": "Macro '@blow_up' in '/models/marts/customer_totals.sql' failed: cannot render total" + } + ] + } + ], + "compiled": {}, + "manifest": null +} diff --git a/tests/goldens/compiler/failure/macro-generated-reference.json b/tests/goldens/compiler/failure/macro-generated-reference.json new file mode 100644 index 0000000000..f778bac568 --- /dev/null +++ b/tests/goldens/compiler/failure/macro-generated-reference.json @@ -0,0 +1,290 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "P006", + "message": "model:customer_totals depends on model:stg_orders through macro staged_orders()", + "resource_type": "model", + "resource_name": "customer_totals", + "path": "models/marts/_sqlbuild/_macros/orders.py", + "help": "write the reference in the model, or pass it in: @staged_orders(__ref(\"stg_orders\"))\n = help: the current value is [references] enforce_explicit = true (from sqlbuild_project.toml or its default); while migrating a project, to allow macro-generated references, set this in sqlbuild_project.toml:\n [references]\n enforce_explicit = false" + } + ] + } + ], + "compiled": { + "models/marts/customer_totals.sql": [ + "SELECT customer_id, SUM(amount) AS total_amount", + "FROM __ref(\"stg_orders\")", + "GROUP BY customer_id", + "" + ], + "models/staging/stg_orders.sql": [ + "SELECT order_id, customer_id, amount, status", + "FROM __source(\"raw_orders\")", + "" + ] + }, + "manifest": { + "nodes": { + "model.failure_corpus.customer_totals": { + "database": null, + "schema": "main", + "name": "customer_totals", + "resource_type": "model", + "package_name": "failure_corpus", + "path": "models/marts/customer_totals.sql", + "original_file_path": "models/marts/customer_totals.sql", + "unique_id": "model.failure_corpus.customer_totals", + "fqn": [ + "failure_corpus", + "models", + "marts", + "customer_totals" + ], + "alias": "customer_totals", + "config": { + "enabled": true, + "alias": null, + "schema": null, + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Order totals per customer", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.customer_totals", + "depends_on": { + "macros": [], + "nodes": [ + "model.failure_corpus.stg_orders" + ] + } + }, + "model.failure_corpus.stg_orders": { + "database": null, + "schema": "main", + "name": "stg_orders", + "resource_type": "model", + "package_name": "failure_corpus", + "path": "models/staging/stg_orders.sql", + "original_file_path": "models/staging/stg_orders.sql", + "unique_id": "model.failure_corpus.stg_orders", + "fqn": [ + "failure_corpus", + "models", + "staging", + "stg_orders" + ], + "alias": "stg_orders", + "config": { + "enabled": true, + "alias": null, + "schema": null, + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Staged orders", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.stg_orders", + "depends_on": { + "macros": [], + "nodes": [ + "source.failure_corpus.raw_orders" + ] + } + } + }, + "sources": { + "source.failure_corpus.raw_orders": { + "database": null, + "schema": "", + "name": "raw_orders", + "resource_type": "source", + "package_name": "failure_corpus", + "path": "sources/raw.yml", + "original_file_path": "sources/raw.yml", + "unique_id": "source.failure_corpus.raw_orders", + "fqn": [ + "failure_corpus", + "raw_orders" + ], + "source_name": "raw_orders", + "source_description": "", + "loader": "", + "identifier": "raw_orders", + "quoting": {}, + "loaded_at_field": null, + "freshness": null, + "external": null, + "description": "Orders feed.", + "columns": { + "order_id": { + "name": "order_id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "customer_id": { + "name": "customer_id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "amount": { + "name": "amount", + "description": "", + "meta": {}, + "data_type": "DOUBLE", + "constraints": [], + "quote": null, + "tags": [] + }, + "status": { + "name": "status", + "description": "", + "meta": {}, + "data_type": "VARCHAR", + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": {}, + "source_meta": {}, + "tags": [], + "config": {}, + "patch_path": null, + "unrendered_config": {}, + "relation_name": null + } + }, + "macros": { + "macro.failure_corpus.staged_orders": { + "name": "staged_orders", + "resource_type": "macro", + "package_name": "failure_corpus", + "path": "models/marts/_sqlbuild/_macros/orders.py", + "original_file_path": "models/marts/_sqlbuild/_macros/orders.py", + "unique_id": "macro.failure_corpus.staged_orders", + "macro_sql": "def staged_orders() -> str:\n \"\"\"Return the staged orders relation.\"\"\"\n return '__ref(\"stg_orders\")'\n", + "depends_on": { + "macros": [] + }, + "description": "", + "meta": { + "sqlbuild_visibility": "local", + "sqlbuild_scope_path": "models/marts" + }, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "arguments": [], + "supported_languages": null + } + }, + "exposures": {}, + "metrics": {}, + "groups": {}, + "selectors": {}, + "disabled": {}, + "parent_map": { + "model.failure_corpus.customer_totals": [ + "model.failure_corpus.stg_orders" + ], + "model.failure_corpus.stg_orders": [ + "source.failure_corpus.raw_orders" + ], + "source.failure_corpus.raw_orders": [] + }, + "group_map": {}, + "docs": {}, + "saved_queries": {}, + "semantic_models": {}, + "unit_tests": {} + } +} diff --git a/tests/goldens/compiler/failure/macro-import-cycle.json b/tests/goldens/compiler/failure/macro-import-cycle.json new file mode 100644 index 0000000000..9eebdc8491 --- /dev/null +++ b/tests/goldens/compiler/failure/macro-import-cycle.json @@ -0,0 +1,18 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "P001", + "message": "Macro import cycle in 'macros/first.py': macros.first -> macros.second -> macros.first" + } + ] + } + ], + "compiled": {}, + "manifest": null +} diff --git a/tests/goldens/compiler/failure/macro-in-sibling-scope.json b/tests/goldens/compiler/failure/macro-in-sibling-scope.json new file mode 100644 index 0000000000..0865f3526a --- /dev/null +++ b/tests/goldens/compiler/failure/macro-in-sibling-scope.json @@ -0,0 +1,18 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "P001", + "message": "Macro '@cents' in '/models/staging/stg_orders.sql' is inaccessible. Defined at 'models/marts/_sqlbuild/_macros/orders.py:1:1' with scope owner 'models/marts'; consumer/definition path is '/models/staging/stg_orders.sql'. Visible macros: none" + } + ] + } + ], + "compiled": {}, + "manifest": null +} diff --git a/tests/goldens/compiler/failure/macro-non-literal-argument.json b/tests/goldens/compiler/failure/macro-non-literal-argument.json new file mode 100644 index 0000000000..c2bf929aa5 --- /dev/null +++ b/tests/goldens/compiler/failure/macro-non-literal-argument.json @@ -0,0 +1,18 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "P001", + "message": "Macro arguments in '/models/staging/stg_orders.sql' must use only Python literals, nested macro calls, and __ref(), __source(), or __seed() references" + } + ] + } + ], + "compiled": {}, + "manifest": null +} diff --git a/tests/goldens/compiler/failure/macro-output-calls-macro.json b/tests/goldens/compiler/failure/macro-output-calls-macro.json new file mode 100644 index 0000000000..d12c7c95d7 --- /dev/null +++ b/tests/goldens/compiler/failure/macro-output-calls-macro.json @@ -0,0 +1,18 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "P001", + "message": "Macro '@wrapped' in '/models/staging/stg_orders.sql' returned SQLBuild call(s) @cents(). Macro output must be final SQL. Compose macros with ordinary Python imports and function calls instead; nested @macro() calls are supported only when explicitly authored in SQL" + } + ] + } + ], + "compiled": {}, + "manifest": null +} diff --git a/tests/goldens/compiler/failure/macro-private-helper.json b/tests/goldens/compiler/failure/macro-private-helper.json new file mode 100644 index 0000000000..e8581dc32d --- /dev/null +++ b/tests/goldens/compiler/failure/macro-private-helper.json @@ -0,0 +1,18 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "P001", + "message": "Unknown macro '@_scaled' in '/models/staging/stg_orders.sql'. Available macros: cents" + } + ] + } + ], + "compiled": {}, + "manifest": null +} diff --git a/tests/goldens/compiler/failure/macro-raises-after-a-call.json b/tests/goldens/compiler/failure/macro-raises-after-a-call.json new file mode 100644 index 0000000000..acab2b6871 --- /dev/null +++ b/tests/goldens/compiler/failure/macro-raises-after-a-call.json @@ -0,0 +1,18 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "P001", + "message": "Macro '@explode' in '/models/staging/stg_orders.sql' failed: cannot render status" + } + ] + } + ], + "compiled": {}, + "manifest": null +} diff --git a/tests/goldens/compiler/failure/macro-returned-invalid-reference-call.json b/tests/goldens/compiler/failure/macro-returned-invalid-reference-call.json new file mode 100644 index 0000000000..fd55833526 --- /dev/null +++ b/tests/goldens/compiler/failure/macro-returned-invalid-reference-call.json @@ -0,0 +1,295 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "P012", + "message": "__ref(stg_orders) is not a valid __ref() call, returned by macro staged_orders()", + "resource_type": "model", + "resource_name": "customer_totals", + "path": "models/marts/_sqlbuild/_macros/orders.py", + "line": 3, + "column": 13, + "location": { + "path": "models/marts/_sqlbuild/_macros/orders.py", + "line": 3, + "column": 13, + "end_line": 3, + "end_column": 30 + }, + "help": "write __ref(\"stg_orders\") in the model and pass it in: @staged_orders(__ref(\"stg_orders\"))\n = help: __ref() takes exactly one double-quoted name, with no comments or extra spaces inside the parentheses: __ref(\"stg_orders\")" + } + ] + } + ], + "compiled": { + "models/marts/customer_totals.sql": [ + "SELECT customer_id, SUM(amount) AS total_amount", + "FROM __ref(stg_orders)", + "GROUP BY customer_id", + "" + ], + "models/staging/stg_orders.sql": [ + "SELECT order_id, customer_id, amount, status", + "FROM __source(\"raw_orders\")", + "" + ] + }, + "manifest": { + "nodes": { + "model.failure_corpus.customer_totals": { + "database": null, + "schema": "main", + "name": "customer_totals", + "resource_type": "model", + "package_name": "failure_corpus", + "path": "models/marts/customer_totals.sql", + "original_file_path": "models/marts/customer_totals.sql", + "unique_id": "model.failure_corpus.customer_totals", + "fqn": [ + "failure_corpus", + "models", + "marts", + "customer_totals" + ], + "alias": "customer_totals", + "config": { + "enabled": true, + "alias": null, + "schema": null, + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Order totals per customer", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.customer_totals", + "depends_on": { + "macros": [], + "nodes": [] + } + }, + "model.failure_corpus.stg_orders": { + "database": null, + "schema": "main", + "name": "stg_orders", + "resource_type": "model", + "package_name": "failure_corpus", + "path": "models/staging/stg_orders.sql", + "original_file_path": "models/staging/stg_orders.sql", + "unique_id": "model.failure_corpus.stg_orders", + "fqn": [ + "failure_corpus", + "models", + "staging", + "stg_orders" + ], + "alias": "stg_orders", + "config": { + "enabled": true, + "alias": null, + "schema": null, + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Staged orders", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.stg_orders", + "depends_on": { + "macros": [], + "nodes": [ + "source.failure_corpus.raw_orders" + ] + } + } + }, + "sources": { + "source.failure_corpus.raw_orders": { + "database": null, + "schema": "", + "name": "raw_orders", + "resource_type": "source", + "package_name": "failure_corpus", + "path": "sources/raw.yml", + "original_file_path": "sources/raw.yml", + "unique_id": "source.failure_corpus.raw_orders", + "fqn": [ + "failure_corpus", + "raw_orders" + ], + "source_name": "raw_orders", + "source_description": "", + "loader": "", + "identifier": "raw_orders", + "quoting": {}, + "loaded_at_field": null, + "freshness": null, + "external": null, + "description": "Orders feed.", + "columns": { + "order_id": { + "name": "order_id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "customer_id": { + "name": "customer_id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "amount": { + "name": "amount", + "description": "", + "meta": {}, + "data_type": "DOUBLE", + "constraints": [], + "quote": null, + "tags": [] + }, + "status": { + "name": "status", + "description": "", + "meta": {}, + "data_type": "VARCHAR", + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": {}, + "source_meta": {}, + "tags": [], + "config": {}, + "patch_path": null, + "unrendered_config": {}, + "relation_name": null + } + }, + "macros": { + "macro.failure_corpus.staged_orders": { + "name": "staged_orders", + "resource_type": "macro", + "package_name": "failure_corpus", + "path": "models/marts/_sqlbuild/_macros/orders.py", + "original_file_path": "models/marts/_sqlbuild/_macros/orders.py", + "unique_id": "macro.failure_corpus.staged_orders", + "macro_sql": "def staged_orders() -> str:\n \"\"\"Return the staged orders relation.\"\"\"\n return '__ref(stg_orders)'\n", + "depends_on": { + "macros": [] + }, + "description": "", + "meta": { + "sqlbuild_visibility": "local", + "sqlbuild_scope_path": "models/marts" + }, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "arguments": [], + "supported_languages": null + } + }, + "exposures": {}, + "metrics": {}, + "groups": {}, + "selectors": {}, + "disabled": {}, + "parent_map": { + "model.failure_corpus.customer_totals": [], + "model.failure_corpus.stg_orders": [ + "source.failure_corpus.raw_orders" + ], + "source.failure_corpus.raw_orders": [] + }, + "group_map": {}, + "docs": {}, + "saved_queries": {}, + "semantic_models": {}, + "unit_tests": {} + } +} diff --git a/tests/goldens/compiler/failure/macro-returns-non-string.json b/tests/goldens/compiler/failure/macro-returns-non-string.json new file mode 100644 index 0000000000..4a74fb3132 --- /dev/null +++ b/tests/goldens/compiler/failure/macro-returns-non-string.json @@ -0,0 +1,18 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "P001", + "message": "Macro '@answer' in '/models/staging/stg_orders.sql' must return a SQL string when used directly in SQL" + } + ] + } + ], + "compiled": {}, + "manifest": null +} diff --git a/tests/goldens/compiler/failure/macro-test-helper-calls-udf.json b/tests/goldens/compiler/failure/macro-test-helper-calls-udf.json new file mode 100644 index 0000000000..3468b46e88 --- /dev/null +++ b/tests/goldens/compiler/failure/macro-test-helper-calls-udf.json @@ -0,0 +1,18 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "P001", + "message": "SQL test 'tests/unit/test_cents.sql' mode 'macro' helper CTE 'labels' must not call udf; call reusable logic only in __macro_actual__" + } + ] + } + ], + "compiled": {}, + "manifest": null +} diff --git a/tests/goldens/compiler/failure/macro-test-helper-malformed-reference.json b/tests/goldens/compiler/failure/macro-test-helper-malformed-reference.json new file mode 100644 index 0000000000..07233323af --- /dev/null +++ b/tests/goldens/compiler/failure/macro-test-helper-malformed-reference.json @@ -0,0 +1,305 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "P012", + "message": "__source(\"raw\", \"orders\") is not a valid __source() call", + "path": "tests/unit/test_cents.sql", + "line": 6, + "column": 33, + "location": { + "path": "tests/unit/test_cents.sql", + "line": 6, + "column": 33, + "end_line": 6, + "end_column": 58 + }, + "help": "__source() takes exactly one double-quoted name, with no comments or extra spaces inside the parentheses: __source(\"source_name\")" + } + ] + } + ], + "compiled": { + "models/marts/customer_totals.sql": [ + "SELECT customer_id, SUM(amount) AS total_amount", + "FROM __ref(\"stg_orders\")", + "GROUP BY customer_id", + "" + ], + "models/staging/stg_orders.sql": [ + "SELECT order_id, customer_id, amount, status", + "FROM __source(\"raw_orders\")", + "" + ], + "tests/macro test_cents/test_cents.sql": [ + "WITH input_values AS (SELECT 5 AS amount),", + "sample_orders AS (SELECT * FROM __source(\"raw\", \"orders\")),", + "__actual__macro_test_cents AS (SELECT (amount * 100) AS cents FROM input_values),", + "__expected__macro_test_cents AS (SELECT 500 AS cents)", + "SELECT 0 AS step_index, 'macro test_cents' AS model_name, (SELECT COUNT(*) FROM __actual__macro_test_cents) AS actual_count, (SELECT COUNT(*) FROM __expected__macro_test_cents) AS expected_count, (SELECT COUNT(*) FROM (SELECT * FROM __actual__macro_test_cents EXCEPT SELECT * FROM __expected__macro_test_cents) AS __sqlbuild_mismatch) AS unexpected_count, (SELECT COUNT(*) FROM (SELECT * FROM __expected__macro_test_cents EXCEPT SELECT * FROM __actual__macro_test_cents) AS __sqlbuild_missing) AS missing_count", + "" + ] + }, + "manifest": { + "nodes": { + "model.failure_corpus.customer_totals": { + "database": null, + "schema": "main", + "name": "customer_totals", + "resource_type": "model", + "package_name": "failure_corpus", + "path": "models/marts/customer_totals.sql", + "original_file_path": "models/marts/customer_totals.sql", + "unique_id": "model.failure_corpus.customer_totals", + "fqn": [ + "failure_corpus", + "models", + "marts", + "customer_totals" + ], + "alias": "customer_totals", + "config": { + "enabled": true, + "alias": null, + "schema": null, + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Order totals per customer", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.customer_totals", + "depends_on": { + "macros": [], + "nodes": [ + "model.failure_corpus.stg_orders" + ] + } + }, + "model.failure_corpus.stg_orders": { + "database": null, + "schema": "main", + "name": "stg_orders", + "resource_type": "model", + "package_name": "failure_corpus", + "path": "models/staging/stg_orders.sql", + "original_file_path": "models/staging/stg_orders.sql", + "unique_id": "model.failure_corpus.stg_orders", + "fqn": [ + "failure_corpus", + "models", + "staging", + "stg_orders" + ], + "alias": "stg_orders", + "config": { + "enabled": true, + "alias": null, + "schema": null, + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Staged orders", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.stg_orders", + "depends_on": { + "macros": [], + "nodes": [ + "source.failure_corpus.raw_orders" + ] + } + } + }, + "sources": { + "source.failure_corpus.raw_orders": { + "database": null, + "schema": "", + "name": "raw_orders", + "resource_type": "source", + "package_name": "failure_corpus", + "path": "sources/raw.yml", + "original_file_path": "sources/raw.yml", + "unique_id": "source.failure_corpus.raw_orders", + "fqn": [ + "failure_corpus", + "raw_orders" + ], + "source_name": "raw_orders", + "source_description": "", + "loader": "", + "identifier": "raw_orders", + "quoting": {}, + "loaded_at_field": null, + "freshness": null, + "external": null, + "description": "Orders feed.", + "columns": { + "order_id": { + "name": "order_id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "customer_id": { + "name": "customer_id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "amount": { + "name": "amount", + "description": "", + "meta": {}, + "data_type": "DOUBLE", + "constraints": [], + "quote": null, + "tags": [] + }, + "status": { + "name": "status", + "description": "", + "meta": {}, + "data_type": "VARCHAR", + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": {}, + "source_meta": {}, + "tags": [], + "config": {}, + "patch_path": null, + "unrendered_config": {}, + "relation_name": null + } + }, + "macros": { + "macro.failure_corpus.cents": { + "name": "cents", + "resource_type": "macro", + "package_name": "failure_corpus", + "path": "tests/unit/_macros/orders.py", + "original_file_path": "tests/unit/_macros/orders.py", + "unique_id": "macro.failure_corpus.cents", + "macro_sql": "def cents(expression: str) -> str:\n \"\"\"Convert to cents.\"\"\"\n return f\"({expression} * 100)\"\n", + "depends_on": { + "macros": [] + }, + "description": "", + "meta": { + "sqlbuild_visibility": "local", + "sqlbuild_scope_path": "tests/unit" + }, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "arguments": [], + "supported_languages": null + } + }, + "exposures": {}, + "metrics": {}, + "groups": {}, + "selectors": {}, + "disabled": {}, + "parent_map": { + "model.failure_corpus.customer_totals": [ + "model.failure_corpus.stg_orders" + ], + "model.failure_corpus.stg_orders": [ + "source.failure_corpus.raw_orders" + ], + "source.failure_corpus.raw_orders": [] + }, + "group_map": {}, + "docs": {}, + "saved_queries": {}, + "semantic_models": {}, + "unit_tests": {} + } +} diff --git a/tests/goldens/compiler/failure/macro-test-quoted-cte-name.json b/tests/goldens/compiler/failure/macro-test-quoted-cte-name.json new file mode 100644 index 0000000000..c120d3ab4e --- /dev/null +++ b/tests/goldens/compiler/failure/macro-test-quoted-cte-name.json @@ -0,0 +1,19 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "P001", + "message": "SQL test 'tests/unit/test_stg_orders.sql' CTE name \"__macro_actual__\" must be an unquoted identifier of ASCII letters, digits and underscores\n --> tests/unit/test_stg_orders.sql:4:1", + "help": "rename the CTE, for example __macro_actual__; quoted CTE names and names with $ or non-ASCII characters are not supported" + } + ] + } + ], + "compiled": {}, + "manifest": null +} diff --git a/tests/goldens/compiler/failure/managed-source-without-loader.json b/tests/goldens/compiler/failure/managed-source-without-loader.json new file mode 100644 index 0000000000..470980d011 --- /dev/null +++ b/tests/goldens/compiler/failure/managed-source-without-loader.json @@ -0,0 +1,18 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "D007", + "message": "Managed source 'loaded_products' in sources/raw.yml requires loader 'loaded_products'" + } + ] + } + ], + "compiled": {}, + "manifest": null +} diff --git a/tests/goldens/compiler/failure/materialization-import-error.json b/tests/goldens/compiler/failure/materialization-import-error.json new file mode 100644 index 0000000000..c135e75ea8 --- /dev/null +++ b/tests/goldens/compiler/failure/materialization-import-error.json @@ -0,0 +1,18 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "D011", + "message": "Failed to import materialization file materializations/copy_table.py: No module named 'missing_warehouse_client'" + } + ] + } + ], + "compiled": {}, + "manifest": null +} diff --git a/tests/goldens/compiler/failure/missing-description.json b/tests/goldens/compiler/failure/missing-description.json new file mode 100644 index 0000000000..996e78aa3c --- /dev/null +++ b/tests/goldens/compiler/failure/missing-description.json @@ -0,0 +1,272 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "P010", + "message": "model 'stg_orders' has no description", + "resource_type": "model", + "resource_name": "stg_orders", + "path": "models/staging/stg_orders.sql", + "line": 1, + "column": 1, + "location": { + "path": "models/staging/stg_orders.sql", + "line": 1, + "column": 1 + }, + "help": "to describe model 'stg_orders', add this to the MODEL header in models/staging/stg_orders.sql:\n MODEL (\n description \"What one row of stg_orders represents\",\n ...\n );" + } + ] + } + ], + "compiled": { + "models/marts/customer_totals.sql": [ + "SELECT customer_id, SUM(amount) AS total_amount", + "FROM __ref(\"stg_orders\")", + "GROUP BY customer_id", + "" + ], + "models/staging/stg_orders.sql": [ + "SELECT order_id, customer_id, amount, status", + "FROM __source(\"raw_orders\")", + "" + ] + }, + "manifest": { + "nodes": { + "model.failure_corpus.customer_totals": { + "database": null, + "schema": "main", + "name": "customer_totals", + "resource_type": "model", + "package_name": "failure_corpus", + "path": "models/marts/customer_totals.sql", + "original_file_path": "models/marts/customer_totals.sql", + "unique_id": "model.failure_corpus.customer_totals", + "fqn": [ + "failure_corpus", + "models", + "marts", + "customer_totals" + ], + "alias": "customer_totals", + "config": { + "enabled": true, + "alias": null, + "schema": null, + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Order totals per customer", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.customer_totals", + "depends_on": { + "macros": [], + "nodes": [ + "model.failure_corpus.stg_orders" + ] + } + }, + "model.failure_corpus.stg_orders": { + "database": null, + "schema": "main", + "name": "stg_orders", + "resource_type": "model", + "package_name": "failure_corpus", + "path": "models/staging/stg_orders.sql", + "original_file_path": "models/staging/stg_orders.sql", + "unique_id": "model.failure_corpus.stg_orders", + "fqn": [ + "failure_corpus", + "models", + "staging", + "stg_orders" + ], + "alias": "stg_orders", + "config": { + "enabled": true, + "alias": null, + "schema": null, + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.stg_orders", + "depends_on": { + "macros": [], + "nodes": [ + "source.failure_corpus.raw_orders" + ] + } + } + }, + "sources": { + "source.failure_corpus.raw_orders": { + "database": null, + "schema": "", + "name": "raw_orders", + "resource_type": "source", + "package_name": "failure_corpus", + "path": "sources/raw.yml", + "original_file_path": "sources/raw.yml", + "unique_id": "source.failure_corpus.raw_orders", + "fqn": [ + "failure_corpus", + "raw_orders" + ], + "source_name": "raw_orders", + "source_description": "", + "loader": "", + "identifier": "raw_orders", + "quoting": {}, + "loaded_at_field": null, + "freshness": null, + "external": null, + "description": "Orders feed.", + "columns": { + "order_id": { + "name": "order_id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "customer_id": { + "name": "customer_id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "amount": { + "name": "amount", + "description": "", + "meta": {}, + "data_type": "DOUBLE", + "constraints": [], + "quote": null, + "tags": [] + }, + "status": { + "name": "status", + "description": "", + "meta": {}, + "data_type": "VARCHAR", + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": {}, + "source_meta": {}, + "tags": [], + "config": {}, + "patch_path": null, + "unrendered_config": {}, + "relation_name": null + } + }, + "macros": {}, + "exposures": {}, + "metrics": {}, + "groups": {}, + "selectors": {}, + "disabled": {}, + "parent_map": { + "model.failure_corpus.customer_totals": [ + "model.failure_corpus.stg_orders" + ], + "model.failure_corpus.stg_orders": [ + "source.failure_corpus.raw_orders" + ], + "source.failure_corpus.raw_orders": [] + }, + "group_map": {}, + "docs": {}, + "saved_queries": {}, + "semantic_models": {}, + "unit_tests": {} + } +} diff --git a/tests/goldens/compiler/failure/model-blank-column-type.json b/tests/goldens/compiler/failure/model-blank-column-type.json new file mode 100644 index 0000000000..bfb4d464a3 --- /dev/null +++ b/tests/goldens/compiler/failure/model-blank-column-type.json @@ -0,0 +1,18 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "P001", + "message": "models/staging/stg_orders.sql model column 'amount' 'type' must be a non-empty string" + } + ] + } + ], + "compiled": {}, + "manifest": null +} diff --git a/tests/goldens/compiler/failure/model-bom.json b/tests/goldens/compiler/failure/model-bom.json new file mode 100644 index 0000000000..46c3a414d8 --- /dev/null +++ b/tests/goldens/compiler/failure/model-bom.json @@ -0,0 +1,18 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "D002", + "message": "SQL model '/models/staging/stg_orders.sql' must start with a MODEL(...) header as the first non-whitespace content" + } + ] + } + ], + "compiled": {}, + "manifest": null +} diff --git a/tests/goldens/compiler/failure/model-column-audit-unknown-severity.json b/tests/goldens/compiler/failure/model-column-audit-unknown-severity.json new file mode 100644 index 0000000000..12389687b5 --- /dev/null +++ b/tests/goldens/compiler/failure/model-column-audit-unknown-severity.json @@ -0,0 +1,18 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "P001", + "message": "models/staging/stg_orders.sql model column 'amount' audit 'not_null' 'severity' must be one of: warn, error" + } + ] + } + ], + "compiled": {}, + "manifest": null +} diff --git a/tests/goldens/compiler/failure/model-duplicate-column.json b/tests/goldens/compiler/failure/model-duplicate-column.json new file mode 100644 index 0000000000..db3b188fff --- /dev/null +++ b/tests/goldens/compiler/failure/model-duplicate-column.json @@ -0,0 +1,18 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "P001", + "message": "models/staging/stg_orders.sql model has duplicate column 'AMOUNT' (column names are case-insensitive)" + } + ] + } + ], + "compiled": {}, + "manifest": null +} diff --git a/tests/goldens/compiler/failure/model-header-nested-too-deep.json b/tests/goldens/compiler/failure/model-header-nested-too-deep.json new file mode 100644 index 0000000000..f0eb3e117e --- /dev/null +++ b/tests/goldens/compiler/failure/model-header-nested-too-deep.json @@ -0,0 +1,19 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "D002", + "message": "MODEL(...) in '/models/staging/stg_orders.sql:3' contains invalid SQLBuild header syntax: values nest deeper than 256 levels", + "help": "flatten the value so it nests at most 256 levels deep" + } + ] + } + ], + "compiled": {}, + "manifest": null +} diff --git a/tests/goldens/compiler/failure/model-header-syntax.json b/tests/goldens/compiler/failure/model-header-syntax.json new file mode 100644 index 0000000000..5d3b1f5aca --- /dev/null +++ b/tests/goldens/compiler/failure/model-header-syntax.json @@ -0,0 +1,18 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "D002", + "message": "MODEL(...) in '/models/staging/stg_orders.sql' contains invalid SQLBuild header syntax: expected key at position 50" + } + ] + } + ], + "compiled": {}, + "manifest": null +} diff --git a/tests/goldens/compiler/failure/model-missing-header.json b/tests/goldens/compiler/failure/model-missing-header.json new file mode 100644 index 0000000000..46c3a414d8 --- /dev/null +++ b/tests/goldens/compiler/failure/model-missing-header.json @@ -0,0 +1,18 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "D002", + "message": "SQL model '/models/staging/stg_orders.sql' must start with a MODEL(...) header as the first non-whitespace content" + } + ] + } + ], + "compiled": {}, + "manifest": null +} diff --git a/tests/goldens/compiler/failure/model-nullable-column-with-not-null.json b/tests/goldens/compiler/failure/model-nullable-column-with-not-null.json new file mode 100644 index 0000000000..0c8e55f2d1 --- /dev/null +++ b/tests/goldens/compiler/failure/model-nullable-column-with-not-null.json @@ -0,0 +1,19 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "P002", + "message": "models/staging/stg_orders.sql column 'amount' cannot set nullable = true and audit not_null", + "help": "remove the not_null audit or set nullable = false" + } + ] + } + ], + "compiled": {}, + "manifest": null +} diff --git a/tests/goldens/compiler/failure/model-path-not-utf8.json b/tests/goldens/compiler/failure/model-path-not-utf8.json new file mode 100644 index 0000000000..a04a655ed8 --- /dev/null +++ b/tests/goldens/compiler/failure/model-path-not-utf8.json @@ -0,0 +1,18 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "D016", + "message": "Project path /models/staging/caf\\xe9.sql is not valid UTF-8; rename it so SQLBuild can read it" + } + ] + } + ], + "compiled": {}, + "manifest": null +} diff --git a/tests/goldens/compiler/failure/model-private-enum-misuse.json b/tests/goldens/compiler/failure/model-private-enum-misuse.json new file mode 100644 index 0000000000..e1cf0a0865 --- /dev/null +++ b/tests/goldens/compiler/failure/model-private-enum-misuse.json @@ -0,0 +1,18 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "P001", + "message": "Unknown member 'PENDING' for enum '_state' in '/models/staging/stg_orders.sql'. Available members: OPEN, CLOSED" + } + ] + } + ], + "compiled": {}, + "manifest": null +} diff --git a/tests/goldens/compiler/failure/model-reserved-name.json b/tests/goldens/compiler/failure/model-reserved-name.json new file mode 100644 index 0000000000..d6fc87b3d8 --- /dev/null +++ b/tests/goldens/compiler/failure/model-reserved-name.json @@ -0,0 +1,19 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "D016", + "message": "Invalid model identity '_chain_' in models/marts/_chain_.sql; use snake_case 'chain'", + "help": "Rename the authored identity and update its references, selectors, and integration keys. SQLBuild does not silently normalize resource identities. Double underscores remain valid." + } + ] + } + ], + "compiled": {}, + "manifest": null +} diff --git a/tests/goldens/compiler/failure/model-sql-syntax.json b/tests/goldens/compiler/failure/model-sql-syntax.json new file mode 100644 index 0000000000..d0fd5e25fc --- /dev/null +++ b/tests/goldens/compiler/failure/model-sql-syntax.json @@ -0,0 +1,19 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "P001", + "message": "SQL syntax error in model 'stg_orders' (/models/staging/stg_orders.sql): Unexpected token: Comma", + "help": "if this SQL is valid for your warehouse, skip SQL analysis for this model, add this to the MODEL header:\n MODEL (\n sql_analysis false,\n ...\n );\n and please report the SQL so the parser can support it\n = help: SQL analysis is on for this project; to turn it off for every model, set this in sqlbuild_project.toml:\n [settings]\n sql_analysis = false\n = help: to skip SQL analysis for one run, use `--no-sql-analysis`" + } + ] + } + ], + "compiled": {}, + "manifest": null +} diff --git a/tests/goldens/compiler/failure/model-template-missing-environment.json b/tests/goldens/compiler/failure/model-template-missing-environment.json new file mode 100644 index 0000000000..73fb28ed55 --- /dev/null +++ b/tests/goldens/compiler/failure/model-template-missing-environment.json @@ -0,0 +1,18 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "P001", + "message": "model config references missing ENV variable 'SQB_DIFFERENTIAL_UNSET'" + } + ] + } + ], + "compiled": {}, + "manifest": null +} diff --git a/tests/goldens/compiler/failure/model-template-unknown-function.json b/tests/goldens/compiler/failure/model-template-unknown-function.json new file mode 100644 index 0000000000..598de35743 --- /dev/null +++ b/tests/goldens/compiler/failure/model-template-unknown-function.json @@ -0,0 +1,18 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "P001", + "message": "model config references unsupported template function 'upper'" + } + ] + } + ], + "compiled": {}, + "manifest": null +} diff --git a/tests/goldens/compiler/failure/model-template-unterminated-string.json b/tests/goldens/compiler/failure/model-template-unterminated-string.json new file mode 100644 index 0000000000..928b630a46 --- /dev/null +++ b/tests/goldens/compiler/failure/model-template-unterminated-string.json @@ -0,0 +1,18 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "P001", + "message": "template expression has unterminated single-quoted string at position 9" + } + ] + } + ], + "compiled": {}, + "manifest": null +} diff --git a/tests/goldens/compiler/failure/model-unknown-header-key.json b/tests/goldens/compiler/failure/model-unknown-header-key.json new file mode 100644 index 0000000000..a7f7d46ce2 --- /dev/null +++ b/tests/goldens/compiler/failure/model-unknown-header-key.json @@ -0,0 +1,19 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "D002", + "message": "MODEL() in '/models/staging/stg_orders.sql:3' has unsupported keys: materialised", + "help": "did you mean 'materialized'?" + } + ] + } + ], + "compiled": {}, + "manifest": null +} diff --git a/tests/goldens/compiler/failure/multiple-errors-order.json b/tests/goldens/compiler/failure/multiple-errors-order.json new file mode 100644 index 0000000000..a2e8ee113f --- /dev/null +++ b/tests/goldens/compiler/failure/multiple-errors-order.json @@ -0,0 +1,384 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "P010", + "message": "model 'customer_totals' has no description", + "resource_type": "model", + "resource_name": "customer_totals", + "path": "models/marts/customer_totals.sql", + "line": 1, + "column": 1, + "location": { + "path": "models/marts/customer_totals.sql", + "line": 1, + "column": 1 + }, + "help": "to describe model 'customer_totals', add this to the MODEL header in models/marts/customer_totals.sql:\n MODEL (\n description \"What one row of customer_totals represents\",\n ...\n );" + }, + { + "phase": "compile", + "severity": "error", + "code": "P010", + "message": "model 'order_counts' has no description", + "resource_type": "model", + "resource_name": "order_counts", + "path": "models/marts/order_counts.sql", + "line": 1, + "column": 1, + "location": { + "path": "models/marts/order_counts.sql", + "line": 1, + "column": 1 + }, + "help": "to describe model 'order_counts', add this to the MODEL header in models/marts/order_counts.sql:\n MODEL (\n description \"What one row of order_counts represents\",\n ...\n );" + }, + { + "phase": "compile", + "severity": "error", + "code": "P010", + "message": "model 'stg_orders' has no description", + "resource_type": "model", + "resource_name": "stg_orders", + "path": "models/staging/stg_orders.sql", + "line": 1, + "column": 1, + "location": { + "path": "models/staging/stg_orders.sql", + "line": 1, + "column": 1 + }, + "help": "to describe model 'stg_orders', add this to the MODEL header in models/staging/stg_orders.sql:\n MODEL (\n description \"What one row of stg_orders represents\",\n ...\n );" + } + ] + } + ], + "compiled": { + "models/marts/customer_totals.sql": [ + "SELECT customer_id, SUM(amount) AS total_amount", + "FROM __ref(\"stg_orders\")", + "GROUP BY customer_id", + "" + ], + "models/marts/order_counts.sql": [ + "SELECT customer_id, COUNT(*) AS order_count", + "FROM __ref(\"stg_orders\")", + "GROUP BY customer_id", + "" + ], + "models/staging/stg_orders.sql": [ + "SELECT order_id, customer_id, amount, status", + "FROM __source(\"raw_orders\")", + "" + ] + }, + "manifest": { + "nodes": { + "model.failure_corpus.customer_totals": { + "database": null, + "schema": "main", + "name": "customer_totals", + "resource_type": "model", + "package_name": "failure_corpus", + "path": "models/marts/customer_totals.sql", + "original_file_path": "models/marts/customer_totals.sql", + "unique_id": "model.failure_corpus.customer_totals", + "fqn": [ + "failure_corpus", + "models", + "marts", + "customer_totals" + ], + "alias": "customer_totals", + "config": { + "enabled": true, + "alias": null, + "schema": null, + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.customer_totals", + "depends_on": { + "macros": [], + "nodes": [ + "model.failure_corpus.stg_orders" + ] + } + }, + "model.failure_corpus.order_counts": { + "database": null, + "schema": "main", + "name": "order_counts", + "resource_type": "model", + "package_name": "failure_corpus", + "path": "models/marts/order_counts.sql", + "original_file_path": "models/marts/order_counts.sql", + "unique_id": "model.failure_corpus.order_counts", + "fqn": [ + "failure_corpus", + "models", + "marts", + "order_counts" + ], + "alias": "order_counts", + "config": { + "enabled": true, + "alias": null, + "schema": null, + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.order_counts", + "depends_on": { + "macros": [], + "nodes": [ + "model.failure_corpus.stg_orders" + ] + } + }, + "model.failure_corpus.stg_orders": { + "database": null, + "schema": "main", + "name": "stg_orders", + "resource_type": "model", + "package_name": "failure_corpus", + "path": "models/staging/stg_orders.sql", + "original_file_path": "models/staging/stg_orders.sql", + "unique_id": "model.failure_corpus.stg_orders", + "fqn": [ + "failure_corpus", + "models", + "staging", + "stg_orders" + ], + "alias": "stg_orders", + "config": { + "enabled": true, + "alias": null, + "schema": null, + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.stg_orders", + "depends_on": { + "macros": [], + "nodes": [ + "source.failure_corpus.raw_orders" + ] + } + } + }, + "sources": { + "source.failure_corpus.raw_orders": { + "database": null, + "schema": "", + "name": "raw_orders", + "resource_type": "source", + "package_name": "failure_corpus", + "path": "sources/raw.yml", + "original_file_path": "sources/raw.yml", + "unique_id": "source.failure_corpus.raw_orders", + "fqn": [ + "failure_corpus", + "raw_orders" + ], + "source_name": "raw_orders", + "source_description": "", + "loader": "", + "identifier": "raw_orders", + "quoting": {}, + "loaded_at_field": null, + "freshness": null, + "external": null, + "description": "Orders feed.", + "columns": { + "order_id": { + "name": "order_id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "customer_id": { + "name": "customer_id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "amount": { + "name": "amount", + "description": "", + "meta": {}, + "data_type": "DOUBLE", + "constraints": [], + "quote": null, + "tags": [] + }, + "status": { + "name": "status", + "description": "", + "meta": {}, + "data_type": "VARCHAR", + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": {}, + "source_meta": {}, + "tags": [], + "config": {}, + "patch_path": null, + "unrendered_config": {}, + "relation_name": null + } + }, + "macros": {}, + "exposures": {}, + "metrics": {}, + "groups": {}, + "selectors": {}, + "disabled": {}, + "parent_map": { + "model.failure_corpus.customer_totals": [ + "model.failure_corpus.stg_orders" + ], + "model.failure_corpus.order_counts": [ + "model.failure_corpus.stg_orders" + ], + "model.failure_corpus.stg_orders": [ + "source.failure_corpus.raw_orders" + ], + "source.failure_corpus.raw_orders": [] + }, + "group_map": {}, + "docs": {}, + "saved_queries": {}, + "semantic_models": {}, + "unit_tests": {} + } +} diff --git a/tests/goldens/compiler/failure/named-hook-missing-argument.json b/tests/goldens/compiler/failure/named-hook-missing-argument.json new file mode 100644 index 0000000000..8d741b20ca --- /dev/null +++ b/tests/goldens/compiler/failure/named-hook-missing-argument.json @@ -0,0 +1,18 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "P001", + "message": "/models/marts/customer_totals.sql post_hooks[0] sql(\"record_refresh\") is missing argument 'label' for SQL hook 'record_refresh' in hooks/sql/record_refresh.sql" + } + ] + } + ], + "compiled": {}, + "manifest": null +} diff --git a/tests/goldens/compiler/failure/named-role-nested-declaration.json b/tests/goldens/compiler/failure/named-role-nested-declaration.json new file mode 100644 index 0000000000..6f8b5789cc --- /dev/null +++ b/tests/goldens/compiler/failure/named-role-nested-declaration.json @@ -0,0 +1,18 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "D013", + "message": "Declaration directory schemas/enums/ is not allowed inside the project-wide schemas/ role; scoped declarations belong under /_sqlbuild/ below a resource tree" + } + ] + } + ], + "compiled": {}, + "manifest": null +} diff --git a/tests/goldens/compiler/failure/order-binding-metadata-contract.json b/tests/goldens/compiler/failure/order-binding-metadata-contract.json new file mode 100644 index 0000000000..c515f0891b --- /dev/null +++ b/tests/goldens/compiler/failure/order-binding-metadata-contract.json @@ -0,0 +1,361 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "B002", + "message": "Unknown column 'refund' in stg_orders", + "resource_type": "model", + "resource_name": "customer_totals", + "path": "models/marts/customer_totals.sql", + "line": 7, + "column": 25, + "location": { + "path": "models/marts/customer_totals.sql", + "line": 7, + "column": 25, + "end_line": 7, + "end_column": 31 + }, + "notes": [ + "stg_orders has: order_id, amount, discount, customer_id, status" + ] + }, + { + "phase": "compile", + "severity": "error", + "code": "B300", + "message": "unique_key references unknown column 'order_key'", + "resource_type": "model", + "resource_name": "customer_totals", + "path": "models/marts/customer_totals.sql", + "line": 4, + "column": 15, + "location": { + "path": "models/marts/customer_totals.sql", + "line": 4, + "column": 15 + }, + "help": "change the metadata column name to an existing output column" + }, + { + "phase": "contract", + "severity": "error", + "code": "K001", + "message": "enforced contract column 'discount' was not found in statically inferred output for model 'stg_orders'", + "resource_type": "model", + "resource_name": "stg_orders", + "column_name": "discount", + "path": "models/staging/stg_orders.sql", + "line": 9, + "column": 5, + "location": { + "path": "models/staging/stg_orders.sql", + "line": 9, + "column": 5, + "end_line": 9, + "end_column": 13 + }, + "help": "add discount to the SELECT list or correct MODEL(columns (...)); validation is active because this model declares contract enforced" + } + ] + } + ], + "compiled": { + "models/marts/customer_totals.sql": [ + "SELECT customer_id, SUM(refund) AS total_amount", + "FROM __ref(\"stg_orders\")", + "GROUP BY customer_id", + "" + ], + "models/staging/stg_orders.sql": [ + "SELECT order_id, customer_id, amount, status", + "FROM __source(\"raw_orders\")", + "" + ] + }, + "manifest": { + "nodes": { + "model.failure_corpus.customer_totals": { + "database": null, + "schema": "main", + "name": "customer_totals", + "resource_type": "model", + "package_name": "failure_corpus", + "path": "models/marts/customer_totals.sql", + "original_file_path": "models/marts/customer_totals.sql", + "unique_id": "model.failure_corpus.customer_totals", + "fqn": [ + "failure_corpus", + "models", + "marts", + "customer_totals" + ], + "alias": "customer_totals", + "config": { + "enabled": true, + "alias": null, + "schema": null, + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "table", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": [ + "order_key" + ], + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Order totals per customer", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.customer_totals", + "depends_on": { + "macros": [], + "nodes": [ + "model.failure_corpus.stg_orders" + ] + } + }, + "model.failure_corpus.stg_orders": { + "database": null, + "schema": "main", + "name": "stg_orders", + "resource_type": "model", + "package_name": "failure_corpus", + "path": "models/staging/stg_orders.sql", + "original_file_path": "models/staging/stg_orders.sql", + "unique_id": "model.failure_corpus.stg_orders", + "fqn": [ + "failure_corpus", + "models", + "staging", + "stg_orders" + ], + "alias": "stg_orders", + "config": { + "enabled": true, + "alias": null, + "schema": null, + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Staged orders", + "columns": { + "order_id": { + "name": "order_id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "customer_id": { + "name": "customer_id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "amount": { + "name": "amount", + "description": "", + "meta": {}, + "data_type": "DOUBLE", + "constraints": [], + "quote": null, + "tags": [] + }, + "status": { + "name": "status", + "description": "", + "meta": {}, + "data_type": "VARCHAR", + "constraints": [], + "quote": null, + "tags": [] + }, + "discount": { + "name": "discount", + "description": "", + "meta": {}, + "data_type": "DOUBLE", + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.stg_orders", + "depends_on": { + "macros": [], + "nodes": [ + "source.failure_corpus.raw_orders" + ] + } + } + }, + "sources": { + "source.failure_corpus.raw_orders": { + "database": null, + "schema": "", + "name": "raw_orders", + "resource_type": "source", + "package_name": "failure_corpus", + "path": "sources/raw.yml", + "original_file_path": "sources/raw.yml", + "unique_id": "source.failure_corpus.raw_orders", + "fqn": [ + "failure_corpus", + "raw_orders" + ], + "source_name": "raw_orders", + "source_description": "", + "loader": "", + "identifier": "raw_orders", + "quoting": {}, + "loaded_at_field": null, + "freshness": null, + "external": null, + "description": "Orders feed.", + "columns": { + "order_id": { + "name": "order_id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "customer_id": { + "name": "customer_id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "amount": { + "name": "amount", + "description": "", + "meta": {}, + "data_type": "DOUBLE", + "constraints": [], + "quote": null, + "tags": [] + }, + "status": { + "name": "status", + "description": "", + "meta": {}, + "data_type": "VARCHAR", + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": {}, + "source_meta": {}, + "tags": [], + "config": {}, + "patch_path": null, + "unrendered_config": {}, + "relation_name": null + } + }, + "macros": {}, + "exposures": {}, + "metrics": {}, + "groups": {}, + "selectors": {}, + "disabled": {}, + "parent_map": { + "model.failure_corpus.customer_totals": [ + "model.failure_corpus.stg_orders" + ], + "model.failure_corpus.stg_orders": [ + "source.failure_corpus.raw_orders" + ], + "source.failure_corpus.raw_orders": [] + }, + "group_map": {}, + "docs": {}, + "saved_queries": {}, + "semantic_models": {}, + "unit_tests": {} + } +} diff --git a/tests/goldens/compiler/failure/order-closest-column-help.json b/tests/goldens/compiler/failure/order-closest-column-help.json new file mode 100644 index 0000000000..e50e568592 --- /dev/null +++ b/tests/goldens/compiler/failure/order-closest-column-help.json @@ -0,0 +1,277 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "B002", + "message": "Unknown column 'amonut' in stg_orders", + "resource_type": "model", + "resource_name": "customer_totals", + "path": "models/marts/customer_totals.sql", + "line": 5, + "column": 25, + "location": { + "path": "models/marts/customer_totals.sql", + "line": 5, + "column": 25, + "end_line": 5, + "end_column": 31 + }, + "help": "did you mean 'amount'?", + "notes": [ + "stg_orders has: amount, status, customer_id, order_id" + ] + } + ] + } + ], + "compiled": { + "models/marts/customer_totals.sql": [ + "SELECT customer_id, SUM(amonut) AS total_amount", + "FROM __ref(\"stg_orders\")", + "GROUP BY customer_id", + "" + ], + "models/staging/stg_orders.sql": [ + "SELECT order_id, customer_id, amount, status", + "FROM __source(\"raw_orders\")", + "" + ] + }, + "manifest": { + "nodes": { + "model.failure_corpus.customer_totals": { + "database": null, + "schema": "main", + "name": "customer_totals", + "resource_type": "model", + "package_name": "failure_corpus", + "path": "models/marts/customer_totals.sql", + "original_file_path": "models/marts/customer_totals.sql", + "unique_id": "model.failure_corpus.customer_totals", + "fqn": [ + "failure_corpus", + "models", + "marts", + "customer_totals" + ], + "alias": "customer_totals", + "config": { + "enabled": true, + "alias": null, + "schema": null, + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Order totals per customer", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.customer_totals", + "depends_on": { + "macros": [], + "nodes": [ + "model.failure_corpus.stg_orders" + ] + } + }, + "model.failure_corpus.stg_orders": { + "database": null, + "schema": "main", + "name": "stg_orders", + "resource_type": "model", + "package_name": "failure_corpus", + "path": "models/staging/stg_orders.sql", + "original_file_path": "models/staging/stg_orders.sql", + "unique_id": "model.failure_corpus.stg_orders", + "fqn": [ + "failure_corpus", + "models", + "staging", + "stg_orders" + ], + "alias": "stg_orders", + "config": { + "enabled": true, + "alias": null, + "schema": null, + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Staged orders", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.stg_orders", + "depends_on": { + "macros": [], + "nodes": [ + "source.failure_corpus.raw_orders" + ] + } + } + }, + "sources": { + "source.failure_corpus.raw_orders": { + "database": null, + "schema": "", + "name": "raw_orders", + "resource_type": "source", + "package_name": "failure_corpus", + "path": "sources/raw.yml", + "original_file_path": "sources/raw.yml", + "unique_id": "source.failure_corpus.raw_orders", + "fqn": [ + "failure_corpus", + "raw_orders" + ], + "source_name": "raw_orders", + "source_description": "", + "loader": "", + "identifier": "raw_orders", + "quoting": {}, + "loaded_at_field": null, + "freshness": null, + "external": null, + "description": "Orders feed.", + "columns": { + "order_id": { + "name": "order_id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "customer_id": { + "name": "customer_id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "amount": { + "name": "amount", + "description": "", + "meta": {}, + "data_type": "DOUBLE", + "constraints": [], + "quote": null, + "tags": [] + }, + "status": { + "name": "status", + "description": "", + "meta": {}, + "data_type": "VARCHAR", + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": {}, + "source_meta": {}, + "tags": [], + "config": {}, + "patch_path": null, + "unrendered_config": {}, + "relation_name": null + } + }, + "macros": {}, + "exposures": {}, + "metrics": {}, + "groups": {}, + "selectors": {}, + "disabled": {}, + "parent_map": { + "model.failure_corpus.customer_totals": [ + "model.failure_corpus.stg_orders" + ], + "model.failure_corpus.stg_orders": [ + "source.failure_corpus.raw_orders" + ], + "source.failure_corpus.raw_orders": [] + }, + "group_map": {}, + "docs": {}, + "saved_queries": {}, + "semantic_models": {}, + "unit_tests": {} + } +} diff --git a/tests/goldens/compiler/failure/order-contract-error-then-warning.json b/tests/goldens/compiler/failure/order-contract-error-then-warning.json new file mode 100644 index 0000000000..742801ce10 --- /dev/null +++ b/tests/goldens/compiler/failure/order-contract-error-then-warning.json @@ -0,0 +1,345 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "contract", + "severity": "error", + "code": "K005", + "message": "column 'status' is not declared in enforced contract for model 'stg_orders'", + "resource_type": "model", + "resource_name": "stg_orders", + "column_name": "status", + "path": "models/staging/stg_orders.sql", + "line": 12, + "column": 53, + "location": { + "path": "models/staging/stg_orders.sql", + "line": 12, + "column": 53, + "end_line": 12, + "end_column": 59 + }, + "help": "add the column to MODEL(columns) or remove it from the SELECT list" + }, + { + "phase": "contract", + "severity": "warning", + "code": "K003", + "message": "column 'note' type could not be proven against declared VARCHAR", + "resource_type": "model", + "resource_name": "stg_orders", + "column_name": "note", + "path": "models/staging/stg_orders.sql", + "line": 6, + "column": 5, + "location": { + "path": "models/staging/stg_orders.sql", + "line": 6, + "column": 5, + "end_line": 6, + "end_column": 9 + }, + "related_locations": [ + { + "label": "output", + "location": { + "path": "models/staging/stg_orders.sql", + "line": 12, + "column": 18, + "end_line": 12, + "end_column": 30 + }, + "message": "output expression with unproven type" + } + ], + "help": "add an explicit CAST if this declared type should be checked statically" + } + ] + } + ], + "compiled": { + "models/marts/customer_totals.sql": [ + "SELECT customer_id, SUM(amount) AS total_amount", + "FROM __ref(\"stg_orders\")", + "GROUP BY customer_id", + "" + ], + "models/staging/stg_orders.sql": [ + "SELECT order_id, NULL AS note, customer_id, amount, status", + "FROM __source(\"raw_orders\")", + "" + ] + }, + "manifest": { + "nodes": { + "model.failure_corpus.customer_totals": { + "database": null, + "schema": "main", + "name": "customer_totals", + "resource_type": "model", + "package_name": "failure_corpus", + "path": "models/marts/customer_totals.sql", + "original_file_path": "models/marts/customer_totals.sql", + "unique_id": "model.failure_corpus.customer_totals", + "fqn": [ + "failure_corpus", + "models", + "marts", + "customer_totals" + ], + "alias": "customer_totals", + "config": { + "enabled": true, + "alias": null, + "schema": null, + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Order totals per customer", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.customer_totals", + "depends_on": { + "macros": [], + "nodes": [ + "model.failure_corpus.stg_orders" + ] + } + }, + "model.failure_corpus.stg_orders": { + "database": null, + "schema": "main", + "name": "stg_orders", + "resource_type": "model", + "package_name": "failure_corpus", + "path": "models/staging/stg_orders.sql", + "original_file_path": "models/staging/stg_orders.sql", + "unique_id": "model.failure_corpus.stg_orders", + "fqn": [ + "failure_corpus", + "models", + "staging", + "stg_orders" + ], + "alias": "stg_orders", + "config": { + "enabled": true, + "alias": null, + "schema": null, + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Staged orders", + "columns": { + "order_id": { + "name": "order_id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "note": { + "name": "note", + "description": "", + "meta": {}, + "data_type": "VARCHAR", + "constraints": [], + "quote": null, + "tags": [] + }, + "customer_id": { + "name": "customer_id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "amount": { + "name": "amount", + "description": "", + "meta": {}, + "data_type": "DOUBLE", + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.stg_orders", + "depends_on": { + "macros": [], + "nodes": [ + "source.failure_corpus.raw_orders" + ] + } + } + }, + "sources": { + "source.failure_corpus.raw_orders": { + "database": null, + "schema": "", + "name": "raw_orders", + "resource_type": "source", + "package_name": "failure_corpus", + "path": "sources/raw.yml", + "original_file_path": "sources/raw.yml", + "unique_id": "source.failure_corpus.raw_orders", + "fqn": [ + "failure_corpus", + "raw_orders" + ], + "source_name": "raw_orders", + "source_description": "", + "loader": "", + "identifier": "raw_orders", + "quoting": {}, + "loaded_at_field": null, + "freshness": null, + "external": null, + "description": "Orders feed.", + "columns": { + "order_id": { + "name": "order_id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "customer_id": { + "name": "customer_id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "amount": { + "name": "amount", + "description": "", + "meta": {}, + "data_type": "DOUBLE", + "constraints": [], + "quote": null, + "tags": [] + }, + "status": { + "name": "status", + "description": "", + "meta": {}, + "data_type": "VARCHAR", + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": {}, + "source_meta": {}, + "tags": [], + "config": {}, + "patch_path": null, + "unrendered_config": {}, + "relation_name": null + } + }, + "macros": {}, + "exposures": {}, + "metrics": {}, + "groups": {}, + "selectors": {}, + "disabled": {}, + "parent_map": { + "model.failure_corpus.customer_totals": [ + "model.failure_corpus.stg_orders" + ], + "model.failure_corpus.stg_orders": [ + "source.failure_corpus.raw_orders" + ], + "source.failure_corpus.raw_orders": [] + }, + "group_map": {}, + "docs": {}, + "saved_queries": {}, + "semantic_models": {}, + "unit_tests": {} + } +} diff --git a/tests/goldens/compiler/failure/order-poisoned-downstream-uses.json b/tests/goldens/compiler/failure/order-poisoned-downstream-uses.json new file mode 100644 index 0000000000..631ed47dc9 --- /dev/null +++ b/tests/goldens/compiler/failure/order-poisoned-downstream-uses.json @@ -0,0 +1,277 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "B002", + "message": "Unknown column 'refund' in raw_orders", + "resource_type": "model", + "resource_name": "stg_orders", + "path": "models/staging/stg_orders.sql", + "line": 5, + "column": 39, + "location": { + "path": "models/staging/stg_orders.sql", + "line": 5, + "column": 39, + "end_line": 5, + "end_column": 45 + }, + "notes": [ + "raw_orders has: order_id, amount, customer_id, status", + "1 downstream output uses were not type-checked because of this error" + ] + } + ] + } + ], + "compiled": { + "models/marts/customer_totals.sql": [ + "SELECT customer_id, SUM(refund) AS total_refund", + "FROM __ref(\"stg_orders\")", + "GROUP BY customer_id", + "" + ], + "models/staging/stg_orders.sql": [ + "SELECT order_id, customer_id, amount, refund, status", + "FROM __source(\"raw_orders\")", + "" + ] + }, + "manifest": { + "nodes": { + "model.failure_corpus.customer_totals": { + "database": null, + "schema": "main", + "name": "customer_totals", + "resource_type": "model", + "package_name": "failure_corpus", + "path": "models/marts/customer_totals.sql", + "original_file_path": "models/marts/customer_totals.sql", + "unique_id": "model.failure_corpus.customer_totals", + "fqn": [ + "failure_corpus", + "models", + "marts", + "customer_totals" + ], + "alias": "customer_totals", + "config": { + "enabled": true, + "alias": null, + "schema": null, + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Order totals per customer", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.customer_totals", + "depends_on": { + "macros": [], + "nodes": [ + "model.failure_corpus.stg_orders" + ] + } + }, + "model.failure_corpus.stg_orders": { + "database": null, + "schema": "main", + "name": "stg_orders", + "resource_type": "model", + "package_name": "failure_corpus", + "path": "models/staging/stg_orders.sql", + "original_file_path": "models/staging/stg_orders.sql", + "unique_id": "model.failure_corpus.stg_orders", + "fqn": [ + "failure_corpus", + "models", + "staging", + "stg_orders" + ], + "alias": "stg_orders", + "config": { + "enabled": true, + "alias": null, + "schema": null, + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Staged orders", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.stg_orders", + "depends_on": { + "macros": [], + "nodes": [ + "source.failure_corpus.raw_orders" + ] + } + } + }, + "sources": { + "source.failure_corpus.raw_orders": { + "database": null, + "schema": "", + "name": "raw_orders", + "resource_type": "source", + "package_name": "failure_corpus", + "path": "sources/raw.yml", + "original_file_path": "sources/raw.yml", + "unique_id": "source.failure_corpus.raw_orders", + "fqn": [ + "failure_corpus", + "raw_orders" + ], + "source_name": "raw_orders", + "source_description": "", + "loader": "", + "identifier": "raw_orders", + "quoting": {}, + "loaded_at_field": null, + "freshness": null, + "external": null, + "description": "Orders feed.", + "columns": { + "order_id": { + "name": "order_id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "customer_id": { + "name": "customer_id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "amount": { + "name": "amount", + "description": "", + "meta": {}, + "data_type": "DOUBLE", + "constraints": [], + "quote": null, + "tags": [] + }, + "status": { + "name": "status", + "description": "", + "meta": {}, + "data_type": "VARCHAR", + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": {}, + "source_meta": {}, + "tags": [], + "config": {}, + "patch_path": null, + "unrendered_config": {}, + "relation_name": null + } + }, + "macros": {}, + "exposures": {}, + "metrics": {}, + "groups": {}, + "selectors": {}, + "disabled": {}, + "parent_map": { + "model.failure_corpus.customer_totals": [ + "model.failure_corpus.stg_orders" + ], + "model.failure_corpus.stg_orders": [ + "source.failure_corpus.raw_orders" + ], + "source.failure_corpus.raw_orders": [] + }, + "group_map": {}, + "docs": {}, + "saved_queries": {}, + "semantic_models": {}, + "unit_tests": {} + } +} diff --git a/tests/goldens/compiler/failure/order-two-failing-models.json b/tests/goldens/compiler/failure/order-two-failing-models.json new file mode 100644 index 0000000000..c3bfef6368 --- /dev/null +++ b/tests/goldens/compiler/failure/order-two-failing-models.json @@ -0,0 +1,375 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "B002", + "message": "Unknown column 'missing_column' in stg_orders", + "resource_type": "model", + "resource_name": "order_counts", + "path": "models/marts/order_counts.sql", + "line": 5, + "column": 27, + "location": { + "path": "models/marts/order_counts.sql", + "line": 5, + "column": 27, + "end_line": 5, + "end_column": 41 + }, + "notes": [ + "stg_orders has: discount, amount, status, customer_id, order_id" + ] + }, + { + "phase": "compile", + "severity": "error", + "code": "B002", + "message": "Unknown column 'discount' in raw_orders", + "resource_type": "model", + "resource_name": "stg_orders", + "path": "models/staging/stg_orders.sql", + "line": 5, + "column": 39, + "location": { + "path": "models/staging/stg_orders.sql", + "line": 5, + "column": 39, + "end_line": 5, + "end_column": 47 + }, + "notes": [ + "raw_orders has: amount, customer_id, status, order_id" + ] + } + ] + } + ], + "compiled": { + "models/marts/customer_totals.sql": [ + "SELECT customer_id, SUM(amount) AS total_amount", + "FROM __ref(\"stg_orders\")", + "GROUP BY customer_id", + "" + ], + "models/marts/order_counts.sql": [ + "SELECT customer_id, COUNT(missing_column) AS order_count", + "FROM __ref(\"stg_orders\")", + "GROUP BY customer_id", + "" + ], + "models/staging/stg_orders.sql": [ + "SELECT order_id, customer_id, amount, discount, status", + "FROM __source(\"raw_orders\")", + "" + ] + }, + "manifest": { + "nodes": { + "model.failure_corpus.customer_totals": { + "database": null, + "schema": "main", + "name": "customer_totals", + "resource_type": "model", + "package_name": "failure_corpus", + "path": "models/marts/customer_totals.sql", + "original_file_path": "models/marts/customer_totals.sql", + "unique_id": "model.failure_corpus.customer_totals", + "fqn": [ + "failure_corpus", + "models", + "marts", + "customer_totals" + ], + "alias": "customer_totals", + "config": { + "enabled": true, + "alias": null, + "schema": null, + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Order totals per customer", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.customer_totals", + "depends_on": { + "macros": [], + "nodes": [ + "model.failure_corpus.stg_orders" + ] + } + }, + "model.failure_corpus.order_counts": { + "database": null, + "schema": "main", + "name": "order_counts", + "resource_type": "model", + "package_name": "failure_corpus", + "path": "models/marts/order_counts.sql", + "original_file_path": "models/marts/order_counts.sql", + "unique_id": "model.failure_corpus.order_counts", + "fqn": [ + "failure_corpus", + "models", + "marts", + "order_counts" + ], + "alias": "order_counts", + "config": { + "enabled": true, + "alias": null, + "schema": null, + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Order counts per customer", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.order_counts", + "depends_on": { + "macros": [], + "nodes": [ + "model.failure_corpus.stg_orders" + ] + } + }, + "model.failure_corpus.stg_orders": { + "database": null, + "schema": "main", + "name": "stg_orders", + "resource_type": "model", + "package_name": "failure_corpus", + "path": "models/staging/stg_orders.sql", + "original_file_path": "models/staging/stg_orders.sql", + "unique_id": "model.failure_corpus.stg_orders", + "fqn": [ + "failure_corpus", + "models", + "staging", + "stg_orders" + ], + "alias": "stg_orders", + "config": { + "enabled": true, + "alias": null, + "schema": null, + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Staged orders", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.stg_orders", + "depends_on": { + "macros": [], + "nodes": [ + "source.failure_corpus.raw_orders" + ] + } + } + }, + "sources": { + "source.failure_corpus.raw_orders": { + "database": null, + "schema": "", + "name": "raw_orders", + "resource_type": "source", + "package_name": "failure_corpus", + "path": "sources/raw.yml", + "original_file_path": "sources/raw.yml", + "unique_id": "source.failure_corpus.raw_orders", + "fqn": [ + "failure_corpus", + "raw_orders" + ], + "source_name": "raw_orders", + "source_description": "", + "loader": "", + "identifier": "raw_orders", + "quoting": {}, + "loaded_at_field": null, + "freshness": null, + "external": null, + "description": "Orders feed.", + "columns": { + "order_id": { + "name": "order_id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "customer_id": { + "name": "customer_id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "amount": { + "name": "amount", + "description": "", + "meta": {}, + "data_type": "DOUBLE", + "constraints": [], + "quote": null, + "tags": [] + }, + "status": { + "name": "status", + "description": "", + "meta": {}, + "data_type": "VARCHAR", + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": {}, + "source_meta": {}, + "tags": [], + "config": {}, + "patch_path": null, + "unrendered_config": {}, + "relation_name": null + } + }, + "macros": {}, + "exposures": {}, + "metrics": {}, + "groups": {}, + "selectors": {}, + "disabled": {}, + "parent_map": { + "model.failure_corpus.customer_totals": [ + "model.failure_corpus.stg_orders" + ], + "model.failure_corpus.order_counts": [ + "model.failure_corpus.stg_orders" + ], + "model.failure_corpus.stg_orders": [ + "source.failure_corpus.raw_orders" + ], + "source.failure_corpus.raw_orders": [] + }, + "group_map": {}, + "docs": {}, + "saved_queries": {}, + "semantic_models": {}, + "unit_tests": {} + } +} diff --git a/tests/goldens/compiler/failure/orphan-audit-factory.json b/tests/goldens/compiler/failure/orphan-audit-factory.json new file mode 100644 index 0000000000..99894f52bb --- /dev/null +++ b/tests/goldens/compiler/failure/orphan-audit-factory.json @@ -0,0 +1,286 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "warning", + "code": "C216", + "message": "Audit factory 'order_quality' is not referenced by any model and will not produce audits", + "resource_type": "audit", + "resource_name": "order_quality", + "path": "python/audits/quality.py", + "line": 4, + "column": 1, + "location": { + "path": "python/audits/quality.py", + "line": 4, + "column": 1 + } + }, + { + "phase": "compile", + "severity": "error", + "code": "S024", + "message": "Declaration 'constant:minimum_amount' is currently project at 'constants' (constants/limits.sql); required exact-owner-private at 'models/staging'. Consumers: model:stg_orders. Move it to 'models/staging/_sqlbuild/_constants/'", + "path": "constants/limits.sql", + "line": 1, + "column": 1, + "location": { + "path": "constants/limits.sql", + "line": 1, + "column": 1 + } + } + ] + } + ], + "compiled": { + "models/marts/customer_totals.sql": [ + "SELECT customer_id, SUM(amount) AS total_amount", + "FROM __ref(\"stg_orders\")", + "GROUP BY customer_id", + "" + ], + "models/staging/stg_orders.sql": [ + "SELECT order_id, customer_id, amount, status", + "FROM __source(\"raw_orders\")", + "WHERE amount > 1", + "" + ] + }, + "manifest": { + "nodes": { + "model.failure_corpus.customer_totals": { + "database": null, + "schema": "main", + "name": "customer_totals", + "resource_type": "model", + "package_name": "failure_corpus", + "path": "models/marts/customer_totals.sql", + "original_file_path": "models/marts/customer_totals.sql", + "unique_id": "model.failure_corpus.customer_totals", + "fqn": [ + "failure_corpus", + "models", + "marts", + "customer_totals" + ], + "alias": "customer_totals", + "config": { + "enabled": true, + "alias": null, + "schema": null, + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Order totals per customer", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.customer_totals", + "depends_on": { + "macros": [], + "nodes": [ + "model.failure_corpus.stg_orders" + ] + } + }, + "model.failure_corpus.stg_orders": { + "database": null, + "schema": "main", + "name": "stg_orders", + "resource_type": "model", + "package_name": "failure_corpus", + "path": "models/staging/stg_orders.sql", + "original_file_path": "models/staging/stg_orders.sql", + "unique_id": "model.failure_corpus.stg_orders", + "fqn": [ + "failure_corpus", + "models", + "staging", + "stg_orders" + ], + "alias": "stg_orders", + "config": { + "enabled": true, + "alias": null, + "schema": null, + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Staged orders", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.stg_orders", + "depends_on": { + "macros": [], + "nodes": [ + "source.failure_corpus.raw_orders" + ] + } + } + }, + "sources": { + "source.failure_corpus.raw_orders": { + "database": null, + "schema": "", + "name": "raw_orders", + "resource_type": "source", + "package_name": "failure_corpus", + "path": "sources/raw.yml", + "original_file_path": "sources/raw.yml", + "unique_id": "source.failure_corpus.raw_orders", + "fqn": [ + "failure_corpus", + "raw_orders" + ], + "source_name": "raw_orders", + "source_description": "", + "loader": "", + "identifier": "raw_orders", + "quoting": {}, + "loaded_at_field": null, + "freshness": null, + "external": null, + "description": "Orders feed.", + "columns": { + "order_id": { + "name": "order_id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "customer_id": { + "name": "customer_id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "amount": { + "name": "amount", + "description": "", + "meta": {}, + "data_type": "DOUBLE", + "constraints": [], + "quote": null, + "tags": [] + }, + "status": { + "name": "status", + "description": "", + "meta": {}, + "data_type": "VARCHAR", + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": {}, + "source_meta": {}, + "tags": [], + "config": {}, + "patch_path": null, + "unrendered_config": {}, + "relation_name": null + } + }, + "macros": {}, + "exposures": {}, + "metrics": {}, + "groups": {}, + "selectors": {}, + "disabled": {}, + "parent_map": { + "model.failure_corpus.customer_totals": [ + "model.failure_corpus.stg_orders" + ], + "model.failure_corpus.stg_orders": [ + "source.failure_corpus.raw_orders" + ], + "source.failure_corpus.raw_orders": [] + }, + "group_map": {}, + "docs": {}, + "saved_queries": {}, + "semantic_models": {}, + "unit_tests": {} + } +} diff --git a/tests/goldens/compiler/failure/over-broad-global.json b/tests/goldens/compiler/failure/over-broad-global.json new file mode 100644 index 0000000000..4489bdef1f --- /dev/null +++ b/tests/goldens/compiler/failure/over-broad-global.json @@ -0,0 +1,270 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "S024", + "message": "Declaration 'constant:minimum_amount' is currently project at 'constants' (constants/limits.sql); required exact-owner-private at 'models/staging'. Consumers: model:stg_orders. Move it to 'models/staging/_sqlbuild/_constants/'", + "path": "constants/limits.sql", + "line": 1, + "column": 1, + "location": { + "path": "constants/limits.sql", + "line": 1, + "column": 1 + } + } + ] + } + ], + "compiled": { + "models/marts/customer_totals.sql": [ + "SELECT customer_id, SUM(amount) AS total_amount", + "FROM __ref(\"stg_orders\")", + "GROUP BY customer_id", + "" + ], + "models/staging/stg_orders.sql": [ + "SELECT order_id, customer_id, amount, status", + "FROM __source(\"raw_orders\")", + "WHERE amount > 1", + "" + ] + }, + "manifest": { + "nodes": { + "model.failure_corpus.customer_totals": { + "database": null, + "schema": "main", + "name": "customer_totals", + "resource_type": "model", + "package_name": "failure_corpus", + "path": "models/marts/customer_totals.sql", + "original_file_path": "models/marts/customer_totals.sql", + "unique_id": "model.failure_corpus.customer_totals", + "fqn": [ + "failure_corpus", + "models", + "marts", + "customer_totals" + ], + "alias": "customer_totals", + "config": { + "enabled": true, + "alias": null, + "schema": null, + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Order totals per customer", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.customer_totals", + "depends_on": { + "macros": [], + "nodes": [ + "model.failure_corpus.stg_orders" + ] + } + }, + "model.failure_corpus.stg_orders": { + "database": null, + "schema": "main", + "name": "stg_orders", + "resource_type": "model", + "package_name": "failure_corpus", + "path": "models/staging/stg_orders.sql", + "original_file_path": "models/staging/stg_orders.sql", + "unique_id": "model.failure_corpus.stg_orders", + "fqn": [ + "failure_corpus", + "models", + "staging", + "stg_orders" + ], + "alias": "stg_orders", + "config": { + "enabled": true, + "alias": null, + "schema": null, + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Staged orders", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.stg_orders", + "depends_on": { + "macros": [], + "nodes": [ + "source.failure_corpus.raw_orders" + ] + } + } + }, + "sources": { + "source.failure_corpus.raw_orders": { + "database": null, + "schema": "", + "name": "raw_orders", + "resource_type": "source", + "package_name": "failure_corpus", + "path": "sources/raw.yml", + "original_file_path": "sources/raw.yml", + "unique_id": "source.failure_corpus.raw_orders", + "fqn": [ + "failure_corpus", + "raw_orders" + ], + "source_name": "raw_orders", + "source_description": "", + "loader": "", + "identifier": "raw_orders", + "quoting": {}, + "loaded_at_field": null, + "freshness": null, + "external": null, + "description": "Orders feed.", + "columns": { + "order_id": { + "name": "order_id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "customer_id": { + "name": "customer_id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "amount": { + "name": "amount", + "description": "", + "meta": {}, + "data_type": "DOUBLE", + "constraints": [], + "quote": null, + "tags": [] + }, + "status": { + "name": "status", + "description": "", + "meta": {}, + "data_type": "VARCHAR", + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": {}, + "source_meta": {}, + "tags": [], + "config": {}, + "patch_path": null, + "unrendered_config": {}, + "relation_name": null + } + }, + "macros": {}, + "exposures": {}, + "metrics": {}, + "groups": {}, + "selectors": {}, + "disabled": {}, + "parent_map": { + "model.failure_corpus.customer_totals": [ + "model.failure_corpus.stg_orders" + ], + "model.failure_corpus.stg_orders": [ + "source.failure_corpus.raw_orders" + ], + "source.failure_corpus.raw_orders": [] + }, + "group_map": {}, + "docs": {}, + "saved_queries": {}, + "semantic_models": {}, + "unit_tests": {} + } +} diff --git a/tests/goldens/compiler/failure/path-defaults-unmatched.json b/tests/goldens/compiler/failure/path-defaults-unmatched.json new file mode 100644 index 0000000000..cbd48e3136 --- /dev/null +++ b/tests/goldens/compiler/failure/path-defaults-unmatched.json @@ -0,0 +1,18 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "D007", + "message": "path_defaults['reporting/finance'] does not match any model paths. Known model folders include: marts, staging" + } + ] + } + ], + "compiled": {}, + "manifest": null +} diff --git a/tests/goldens/compiler/failure/provider-invalid-settings.json b/tests/goldens/compiler/failure/provider-invalid-settings.json new file mode 100644 index 0000000000..7c7295b81e --- /dev/null +++ b/tests/goldens/compiler/failure/provider-invalid-settings.json @@ -0,0 +1,18 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "D012", + "message": "Provider 'export_settings' in providers/export_settings.py has invalid settings:\napi_token: Field required [missing]" + } + ] + } + ], + "compiled": {}, + "manifest": null +} diff --git a/tests/goldens/compiler/failure/python-node-import-error.json b/tests/goldens/compiler/failure/python-node-import-error.json new file mode 100644 index 0000000000..1e4fa38507 --- /dev/null +++ b/tests/goldens/compiler/failure/python-node-import-error.json @@ -0,0 +1,18 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "D011", + "message": "Failed to import Python node file python/tasks/exports.py: No module named 'missing_orders_client'" + } + ] + } + ], + "compiled": {}, + "manifest": null +} diff --git a/tests/goldens/compiler/failure/python-node-missing-description.json b/tests/goldens/compiler/failure/python-node-missing-description.json new file mode 100644 index 0000000000..139033f6af --- /dev/null +++ b/tests/goldens/compiler/failure/python-node-missing-description.json @@ -0,0 +1,271 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "P010", + "message": "task 'export_orders' has no description", + "resource_name": "export_orders", + "path": "python/tasks/exports.py", + "line": 4, + "column": 1, + "location": { + "path": "python/tasks/exports.py", + "line": 4, + "column": 1 + }, + "help": "to describe task 'export_orders', give its function in python/tasks/exports.py a docstring or pass a description to @task:\n @task(description=\"What export_orders does\", ...)" + } + ] + } + ], + "compiled": { + "models/marts/customer_totals.sql": [ + "SELECT customer_id, SUM(amount) AS total_amount", + "FROM __ref(\"stg_orders\")", + "GROUP BY customer_id", + "" + ], + "models/staging/stg_orders.sql": [ + "SELECT order_id, customer_id, amount, status", + "FROM __source(\"raw_orders\")", + "" + ] + }, + "manifest": { + "nodes": { + "model.failure_corpus.customer_totals": { + "database": null, + "schema": "main", + "name": "customer_totals", + "resource_type": "model", + "package_name": "failure_corpus", + "path": "models/marts/customer_totals.sql", + "original_file_path": "models/marts/customer_totals.sql", + "unique_id": "model.failure_corpus.customer_totals", + "fqn": [ + "failure_corpus", + "models", + "marts", + "customer_totals" + ], + "alias": "customer_totals", + "config": { + "enabled": true, + "alias": null, + "schema": null, + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Order totals per customer", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.customer_totals", + "depends_on": { + "macros": [], + "nodes": [ + "model.failure_corpus.stg_orders" + ] + } + }, + "model.failure_corpus.stg_orders": { + "database": null, + "schema": "main", + "name": "stg_orders", + "resource_type": "model", + "package_name": "failure_corpus", + "path": "models/staging/stg_orders.sql", + "original_file_path": "models/staging/stg_orders.sql", + "unique_id": "model.failure_corpus.stg_orders", + "fqn": [ + "failure_corpus", + "models", + "staging", + "stg_orders" + ], + "alias": "stg_orders", + "config": { + "enabled": true, + "alias": null, + "schema": null, + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Staged orders", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.stg_orders", + "depends_on": { + "macros": [], + "nodes": [ + "source.failure_corpus.raw_orders" + ] + } + } + }, + "sources": { + "source.failure_corpus.raw_orders": { + "database": null, + "schema": "", + "name": "raw_orders", + "resource_type": "source", + "package_name": "failure_corpus", + "path": "sources/raw.yml", + "original_file_path": "sources/raw.yml", + "unique_id": "source.failure_corpus.raw_orders", + "fqn": [ + "failure_corpus", + "raw_orders" + ], + "source_name": "raw_orders", + "source_description": "", + "loader": "", + "identifier": "raw_orders", + "quoting": {}, + "loaded_at_field": null, + "freshness": null, + "external": null, + "description": "Orders feed.", + "columns": { + "order_id": { + "name": "order_id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "customer_id": { + "name": "customer_id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "amount": { + "name": "amount", + "description": "", + "meta": {}, + "data_type": "DOUBLE", + "constraints": [], + "quote": null, + "tags": [] + }, + "status": { + "name": "status", + "description": "", + "meta": {}, + "data_type": "VARCHAR", + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": {}, + "source_meta": {}, + "tags": [], + "config": {}, + "patch_path": null, + "unrendered_config": {}, + "relation_name": null + } + }, + "macros": {}, + "exposures": {}, + "metrics": {}, + "groups": {}, + "selectors": {}, + "disabled": {}, + "parent_map": { + "model.failure_corpus.customer_totals": [ + "model.failure_corpus.stg_orders" + ], + "model.failure_corpus.stg_orders": [ + "source.failure_corpus.raw_orders" + ], + "source.failure_corpus.raw_orders": [] + }, + "group_map": {}, + "docs": {}, + "saved_queries": {}, + "semantic_models": {}, + "unit_tests": {} + } +} diff --git a/tests/goldens/compiler/failure/python-node-syntax.json b/tests/goldens/compiler/failure/python-node-syntax.json new file mode 100644 index 0000000000..961ba2eae4 --- /dev/null +++ b/tests/goldens/compiler/failure/python-node-syntax.json @@ -0,0 +1,18 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "D011", + "message": "Failed to import Python node file python/tasks/exports.py: invalid syntax (exports.py, line 1)" + } + ] + } + ], + "compiled": {}, + "manifest": null +} diff --git a/tests/goldens/compiler/failure/python-outside-extension-root.json b/tests/goldens/compiler/failure/python-outside-extension-root.json new file mode 100644 index 0000000000..30a354b381 --- /dev/null +++ b/tests/goldens/compiler/failure/python-outside-extension-root.json @@ -0,0 +1,19 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "D016", + "message": "Unsupported project Python path(s): helpers/formatting.py", + "help": "Move pipeline Python nodes and the helper modules they import under python/, or move SQLBuild extensions under their documented extension path." + } + ] + } + ], + "compiled": {}, + "manifest": null +} diff --git a/tests/goldens/compiler/failure/reference-dbt-three-arguments.json b/tests/goldens/compiler/failure/reference-dbt-three-arguments.json new file mode 100644 index 0000000000..b21a746e88 --- /dev/null +++ b/tests/goldens/compiler/failure/reference-dbt-three-arguments.json @@ -0,0 +1,269 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "P012", + "message": "__dbt_ref(\"shop\", \"orders\", \"extra\") is not a valid __dbt_ref() call", + "resource_type": "model", + "resource_name": "customer_totals", + "path": "models/marts/customer_totals.sql", + "line": 6, + "column": 6, + "location": { + "path": "models/marts/customer_totals.sql", + "line": 6, + "column": 6, + "end_line": 6, + "end_column": 42 + }, + "help": "__dbt_ref() takes one double-quoted model name, or a double-quoted package name and model name separated by a comma, with no comments or extra spaces inside the parentheses: __dbt_ref(\"package_name\", \"model_name\")" + } + ] + } + ], + "compiled": { + "models/marts/customer_totals.sql": [ + "SELECT order_id AS customer_id, 1 AS total_amount", + "FROM __dbt_ref(\"shop\", \"orders\", \"extra\")", + "" + ], + "models/staging/stg_orders.sql": [ + "SELECT order_id, customer_id, amount, status", + "FROM __source(\"raw_orders\")", + "" + ] + }, + "manifest": { + "nodes": { + "model.failure_corpus.customer_totals": { + "database": null, + "schema": "main", + "name": "customer_totals", + "resource_type": "model", + "package_name": "failure_corpus", + "path": "models/marts/customer_totals.sql", + "original_file_path": "models/marts/customer_totals.sql", + "unique_id": "model.failure_corpus.customer_totals", + "fqn": [ + "failure_corpus", + "models", + "marts", + "customer_totals" + ], + "alias": "customer_totals", + "config": { + "enabled": true, + "alias": null, + "schema": null, + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Order totals per customer", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.customer_totals", + "depends_on": { + "macros": [], + "nodes": [] + } + }, + "model.failure_corpus.stg_orders": { + "database": null, + "schema": "main", + "name": "stg_orders", + "resource_type": "model", + "package_name": "failure_corpus", + "path": "models/staging/stg_orders.sql", + "original_file_path": "models/staging/stg_orders.sql", + "unique_id": "model.failure_corpus.stg_orders", + "fqn": [ + "failure_corpus", + "models", + "staging", + "stg_orders" + ], + "alias": "stg_orders", + "config": { + "enabled": true, + "alias": null, + "schema": null, + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Staged orders", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.stg_orders", + "depends_on": { + "macros": [], + "nodes": [ + "source.failure_corpus.raw_orders" + ] + } + } + }, + "sources": { + "source.failure_corpus.raw_orders": { + "database": null, + "schema": "", + "name": "raw_orders", + "resource_type": "source", + "package_name": "failure_corpus", + "path": "sources/raw.yml", + "original_file_path": "sources/raw.yml", + "unique_id": "source.failure_corpus.raw_orders", + "fqn": [ + "failure_corpus", + "raw_orders" + ], + "source_name": "raw_orders", + "source_description": "", + "loader": "", + "identifier": "raw_orders", + "quoting": {}, + "loaded_at_field": null, + "freshness": null, + "external": null, + "description": "Orders feed.", + "columns": { + "order_id": { + "name": "order_id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "customer_id": { + "name": "customer_id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "amount": { + "name": "amount", + "description": "", + "meta": {}, + "data_type": "DOUBLE", + "constraints": [], + "quote": null, + "tags": [] + }, + "status": { + "name": "status", + "description": "", + "meta": {}, + "data_type": "VARCHAR", + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": {}, + "source_meta": {}, + "tags": [], + "config": {}, + "patch_path": null, + "unrendered_config": {}, + "relation_name": null + } + }, + "macros": {}, + "exposures": {}, + "metrics": {}, + "groups": {}, + "selectors": {}, + "disabled": {}, + "parent_map": { + "model.failure_corpus.customer_totals": [], + "model.failure_corpus.stg_orders": [ + "source.failure_corpus.raw_orders" + ], + "source.failure_corpus.raw_orders": [] + }, + "group_map": {}, + "docs": {}, + "saved_queries": {}, + "semantic_models": {}, + "unit_tests": {} + } +} diff --git a/tests/goldens/compiler/failure/reference-empty-argument.json b/tests/goldens/compiler/failure/reference-empty-argument.json new file mode 100644 index 0000000000..3021f71bd6 --- /dev/null +++ b/tests/goldens/compiler/failure/reference-empty-argument.json @@ -0,0 +1,269 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "P012", + "message": "__ref(\"stg_orders\", ) is not a valid __ref() call", + "resource_type": "model", + "resource_name": "customer_totals", + "path": "models/marts/customer_totals.sql", + "line": 6, + "column": 6, + "location": { + "path": "models/marts/customer_totals.sql", + "line": 6, + "column": 6, + "end_line": 6, + "end_column": 27 + }, + "help": "__ref() takes exactly one double-quoted name, with no comments or extra spaces inside the parentheses: __ref(\"model_name\")" + } + ] + } + ], + "compiled": { + "models/marts/customer_totals.sql": [ + "SELECT order_id AS customer_id, 1 AS total_amount", + "FROM __ref(\"stg_orders\", )", + "" + ], + "models/staging/stg_orders.sql": [ + "SELECT order_id, customer_id, amount, status", + "FROM __source(\"raw_orders\")", + "" + ] + }, + "manifest": { + "nodes": { + "model.failure_corpus.customer_totals": { + "database": null, + "schema": "main", + "name": "customer_totals", + "resource_type": "model", + "package_name": "failure_corpus", + "path": "models/marts/customer_totals.sql", + "original_file_path": "models/marts/customer_totals.sql", + "unique_id": "model.failure_corpus.customer_totals", + "fqn": [ + "failure_corpus", + "models", + "marts", + "customer_totals" + ], + "alias": "customer_totals", + "config": { + "enabled": true, + "alias": null, + "schema": null, + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Order totals per customer", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.customer_totals", + "depends_on": { + "macros": [], + "nodes": [] + } + }, + "model.failure_corpus.stg_orders": { + "database": null, + "schema": "main", + "name": "stg_orders", + "resource_type": "model", + "package_name": "failure_corpus", + "path": "models/staging/stg_orders.sql", + "original_file_path": "models/staging/stg_orders.sql", + "unique_id": "model.failure_corpus.stg_orders", + "fqn": [ + "failure_corpus", + "models", + "staging", + "stg_orders" + ], + "alias": "stg_orders", + "config": { + "enabled": true, + "alias": null, + "schema": null, + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Staged orders", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.stg_orders", + "depends_on": { + "macros": [], + "nodes": [ + "source.failure_corpus.raw_orders" + ] + } + } + }, + "sources": { + "source.failure_corpus.raw_orders": { + "database": null, + "schema": "", + "name": "raw_orders", + "resource_type": "source", + "package_name": "failure_corpus", + "path": "sources/raw.yml", + "original_file_path": "sources/raw.yml", + "unique_id": "source.failure_corpus.raw_orders", + "fqn": [ + "failure_corpus", + "raw_orders" + ], + "source_name": "raw_orders", + "source_description": "", + "loader": "", + "identifier": "raw_orders", + "quoting": {}, + "loaded_at_field": null, + "freshness": null, + "external": null, + "description": "Orders feed.", + "columns": { + "order_id": { + "name": "order_id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "customer_id": { + "name": "customer_id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "amount": { + "name": "amount", + "description": "", + "meta": {}, + "data_type": "DOUBLE", + "constraints": [], + "quote": null, + "tags": [] + }, + "status": { + "name": "status", + "description": "", + "meta": {}, + "data_type": "VARCHAR", + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": {}, + "source_meta": {}, + "tags": [], + "config": {}, + "patch_path": null, + "unrendered_config": {}, + "relation_name": null + } + }, + "macros": {}, + "exposures": {}, + "metrics": {}, + "groups": {}, + "selectors": {}, + "disabled": {}, + "parent_map": { + "model.failure_corpus.customer_totals": [], + "model.failure_corpus.stg_orders": [ + "source.failure_corpus.raw_orders" + ], + "source.failure_corpus.raw_orders": [] + }, + "group_map": {}, + "docs": {}, + "saved_queries": {}, + "semantic_models": {}, + "unit_tests": {} + } +} diff --git a/tests/goldens/compiler/failure/reference-error-after-rejected-calls.json b/tests/goldens/compiler/failure/reference-error-after-rejected-calls.json new file mode 100644 index 0000000000..cd15879862 --- /dev/null +++ b/tests/goldens/compiler/failure/reference-error-after-rejected-calls.json @@ -0,0 +1,18 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "P001", + "message": "models/marts/customer_totals.sql:7:39: SQL reference contains an unclosed quoted string" + } + ] + } + ], + "compiled": {}, + "manifest": null +} diff --git a/tests/goldens/compiler/failure/reference-expression-name.json b/tests/goldens/compiler/failure/reference-expression-name.json new file mode 100644 index 0000000000..de4eaf4ffc --- /dev/null +++ b/tests/goldens/compiler/failure/reference-expression-name.json @@ -0,0 +1,269 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "P012", + "message": "__ref(concat('stg_', 'orders')) is not a valid __ref() call", + "resource_type": "model", + "resource_name": "customer_totals", + "path": "models/marts/customer_totals.sql", + "line": 6, + "column": 6, + "location": { + "path": "models/marts/customer_totals.sql", + "line": 6, + "column": 6, + "end_line": 6, + "end_column": 37 + }, + "help": "__ref() takes exactly one double-quoted name, with no comments or extra spaces inside the parentheses: __ref(\"model_name\")" + } + ] + } + ], + "compiled": { + "models/marts/customer_totals.sql": [ + "SELECT order_id AS customer_id, 1 AS total_amount", + "FROM __ref(concat('stg_', 'orders'))", + "" + ], + "models/staging/stg_orders.sql": [ + "SELECT order_id, customer_id, amount, status", + "FROM __source(\"raw_orders\")", + "" + ] + }, + "manifest": { + "nodes": { + "model.failure_corpus.customer_totals": { + "database": null, + "schema": "main", + "name": "customer_totals", + "resource_type": "model", + "package_name": "failure_corpus", + "path": "models/marts/customer_totals.sql", + "original_file_path": "models/marts/customer_totals.sql", + "unique_id": "model.failure_corpus.customer_totals", + "fqn": [ + "failure_corpus", + "models", + "marts", + "customer_totals" + ], + "alias": "customer_totals", + "config": { + "enabled": true, + "alias": null, + "schema": null, + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Order totals per customer", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.customer_totals", + "depends_on": { + "macros": [], + "nodes": [] + } + }, + "model.failure_corpus.stg_orders": { + "database": null, + "schema": "main", + "name": "stg_orders", + "resource_type": "model", + "package_name": "failure_corpus", + "path": "models/staging/stg_orders.sql", + "original_file_path": "models/staging/stg_orders.sql", + "unique_id": "model.failure_corpus.stg_orders", + "fqn": [ + "failure_corpus", + "models", + "staging", + "stg_orders" + ], + "alias": "stg_orders", + "config": { + "enabled": true, + "alias": null, + "schema": null, + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Staged orders", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.stg_orders", + "depends_on": { + "macros": [], + "nodes": [ + "source.failure_corpus.raw_orders" + ] + } + } + }, + "sources": { + "source.failure_corpus.raw_orders": { + "database": null, + "schema": "", + "name": "raw_orders", + "resource_type": "source", + "package_name": "failure_corpus", + "path": "sources/raw.yml", + "original_file_path": "sources/raw.yml", + "unique_id": "source.failure_corpus.raw_orders", + "fqn": [ + "failure_corpus", + "raw_orders" + ], + "source_name": "raw_orders", + "source_description": "", + "loader": "", + "identifier": "raw_orders", + "quoting": {}, + "loaded_at_field": null, + "freshness": null, + "external": null, + "description": "Orders feed.", + "columns": { + "order_id": { + "name": "order_id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "customer_id": { + "name": "customer_id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "amount": { + "name": "amount", + "description": "", + "meta": {}, + "data_type": "DOUBLE", + "constraints": [], + "quote": null, + "tags": [] + }, + "status": { + "name": "status", + "description": "", + "meta": {}, + "data_type": "VARCHAR", + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": {}, + "source_meta": {}, + "tags": [], + "config": {}, + "patch_path": null, + "unrendered_config": {}, + "relation_name": null + } + }, + "macros": {}, + "exposures": {}, + "metrics": {}, + "groups": {}, + "selectors": {}, + "disabled": {}, + "parent_map": { + "model.failure_corpus.customer_totals": [], + "model.failure_corpus.stg_orders": [ + "source.failure_corpus.raw_orders" + ], + "source.failure_corpus.raw_orders": [] + }, + "group_map": {}, + "docs": {}, + "saved_queries": {}, + "semantic_models": {}, + "unit_tests": {} + } +} diff --git a/tests/goldens/compiler/failure/reference-rejected-calls-across-files.json b/tests/goldens/compiler/failure/reference-rejected-calls-across-files.json new file mode 100644 index 0000000000..bc8c93801e --- /dev/null +++ b/tests/goldens/compiler/failure/reference-rejected-calls-across-files.json @@ -0,0 +1,284 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "P012", + "message": "__dbt_ref(shop, orders, extra) is not a valid __dbt_ref() call", + "resource_type": "model", + "resource_name": "customer_totals", + "path": "models/marts/customer_totals.sql", + "line": 6, + "column": 6, + "location": { + "path": "models/marts/customer_totals.sql", + "line": 6, + "column": 6, + "end_line": 6, + "end_column": 36 + }, + "help": "__dbt_ref() takes one double-quoted model name, or a double-quoted package name and model name separated by a comma, with no comments or extra spaces inside the parentheses: __dbt_ref(\"package_name\", \"model_name\")" + }, + { + "phase": "compile", + "severity": "error", + "code": "P012", + "message": "__table_fn must be followed by an argument list", + "resource_type": "model", + "resource_name": "stg_orders", + "path": "models/staging/stg_orders.sql", + "line": 6, + "column": 6, + "location": { + "path": "models/staging/stg_orders.sql", + "line": 6, + "column": 6, + "end_line": 6, + "end_column": 40 + }, + "help": "pass the function arguments in a second set of parentheses, using () for no arguments: __table_fn(\"table_fn__orders_for\")()" + } + ] + } + ], + "compiled": { + "models/marts/customer_totals.sql": [ + "SELECT order_id AS customer_id, 1 AS total_amount", + "FROM __dbt_ref(shop, orders, extra)", + "" + ], + "models/staging/stg_orders.sql": [ + "SELECT order_id, customer_id, amount, status", + "FROM __table_fn(\"table_fn__orders_for\") -- no call", + "" + ] + }, + "manifest": { + "nodes": { + "model.failure_corpus.customer_totals": { + "database": null, + "schema": "main", + "name": "customer_totals", + "resource_type": "model", + "package_name": "failure_corpus", + "path": "models/marts/customer_totals.sql", + "original_file_path": "models/marts/customer_totals.sql", + "unique_id": "model.failure_corpus.customer_totals", + "fqn": [ + "failure_corpus", + "models", + "marts", + "customer_totals" + ], + "alias": "customer_totals", + "config": { + "enabled": true, + "alias": null, + "schema": null, + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Order totals per customer", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.customer_totals", + "depends_on": { + "macros": [], + "nodes": [] + } + }, + "model.failure_corpus.stg_orders": { + "database": null, + "schema": "main", + "name": "stg_orders", + "resource_type": "model", + "package_name": "failure_corpus", + "path": "models/staging/stg_orders.sql", + "original_file_path": "models/staging/stg_orders.sql", + "unique_id": "model.failure_corpus.stg_orders", + "fqn": [ + "failure_corpus", + "models", + "staging", + "stg_orders" + ], + "alias": "stg_orders", + "config": { + "enabled": true, + "alias": null, + "schema": null, + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Staged orders", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.stg_orders", + "depends_on": { + "macros": [], + "nodes": [] + } + } + }, + "sources": { + "source.failure_corpus.raw_orders": { + "database": null, + "schema": "", + "name": "raw_orders", + "resource_type": "source", + "package_name": "failure_corpus", + "path": "sources/raw.yml", + "original_file_path": "sources/raw.yml", + "unique_id": "source.failure_corpus.raw_orders", + "fqn": [ + "failure_corpus", + "raw_orders" + ], + "source_name": "raw_orders", + "source_description": "", + "loader": "", + "identifier": "raw_orders", + "quoting": {}, + "loaded_at_field": null, + "freshness": null, + "external": null, + "description": "Orders feed.", + "columns": { + "order_id": { + "name": "order_id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "customer_id": { + "name": "customer_id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "amount": { + "name": "amount", + "description": "", + "meta": {}, + "data_type": "DOUBLE", + "constraints": [], + "quote": null, + "tags": [] + }, + "status": { + "name": "status", + "description": "", + "meta": {}, + "data_type": "VARCHAR", + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": {}, + "source_meta": {}, + "tags": [], + "config": {}, + "patch_path": null, + "unrendered_config": {}, + "relation_name": null + } + }, + "macros": {}, + "exposures": {}, + "metrics": {}, + "groups": {}, + "selectors": {}, + "disabled": {}, + "parent_map": { + "model.failure_corpus.customer_totals": [], + "model.failure_corpus.stg_orders": [], + "source.failure_corpus.raw_orders": [] + }, + "group_map": {}, + "docs": {}, + "saved_queries": {}, + "semantic_models": {}, + "unit_tests": {} + } +} diff --git a/tests/goldens/compiler/failure/reference-several-rejected-calls-in-one-file.json b/tests/goldens/compiler/failure/reference-several-rejected-calls-in-one-file.json new file mode 100644 index 0000000000..13f1d3968b --- /dev/null +++ b/tests/goldens/compiler/failure/reference-several-rejected-calls-in-one-file.json @@ -0,0 +1,311 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "P012", + "message": "__ref(stg_orders) is not a valid __ref() call", + "resource_type": "model", + "resource_name": "customer_totals", + "path": "models/marts/customer_totals.sql", + "line": 8, + "column": 6, + "location": { + "path": "models/marts/customer_totals.sql", + "line": 8, + "column": 6, + "end_line": 8, + "end_column": 23 + }, + "help": "__ref() takes exactly one double-quoted name, with no comments or extra spaces inside the parentheses: __ref(\"stg_orders\")" + }, + { + "phase": "compile", + "severity": "error", + "code": "P012", + "message": "__source( 'raw_orders' ) is not a valid __source() call", + "resource_type": "model", + "resource_name": "customer_totals", + "path": "models/marts/customer_totals.sql", + "line": 9, + "column": 6, + "location": { + "path": "models/marts/customer_totals.sql", + "line": 9, + "column": 6, + "end_line": 9, + "end_column": 30 + }, + "help": "__source() takes exactly one double-quoted name, with no comments or extra spaces inside the parentheses: __source(\"raw_orders\")" + }, + { + "phase": "compile", + "severity": "error", + "code": "P012", + "message": "__seed(\"a\", \"b\") is not a valid __seed() call", + "resource_type": "model", + "resource_name": "customer_totals", + "path": "models/marts/customer_totals.sql", + "line": 10, + "column": 47, + "location": { + "path": "models/marts/customer_totals.sql", + "line": 10, + "column": 47, + "end_line": 10, + "end_column": 63 + }, + "help": "__seed() takes exactly one double-quoted name, with no comments or extra spaces inside the parentheses: __seed(\"seed_name\")" + } + ] + } + ], + "compiled": { + "models/marts/customer_totals.sql": [ + "-- __ref(hidden)", + "SELECT order_id AS customer_id, 'é __ref(hidden)' AS note, $$__ref(hidden)$$ AS body,", + " 1 AS total_amount /* __ref(hidden) */", + "FROM __ref(stg_orders)", + "JOIN __source( 'raw_orders' ) USING (customer_id)", + "WHERE customer_id IN (SELECT customer_id FROM __seed(\"a\", \"b\"))", + "" + ], + "models/staging/stg_orders.sql": [ + "SELECT order_id, customer_id, amount, status", + "FROM __source(\"raw_orders\")", + "" + ] + }, + "manifest": { + "nodes": { + "model.failure_corpus.customer_totals": { + "database": null, + "schema": "main", + "name": "customer_totals", + "resource_type": "model", + "package_name": "failure_corpus", + "path": "models/marts/customer_totals.sql", + "original_file_path": "models/marts/customer_totals.sql", + "unique_id": "model.failure_corpus.customer_totals", + "fqn": [ + "failure_corpus", + "models", + "marts", + "customer_totals" + ], + "alias": "customer_totals", + "config": { + "enabled": true, + "alias": null, + "schema": null, + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Order totals per customer", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.customer_totals", + "depends_on": { + "macros": [], + "nodes": [] + } + }, + "model.failure_corpus.stg_orders": { + "database": null, + "schema": "main", + "name": "stg_orders", + "resource_type": "model", + "package_name": "failure_corpus", + "path": "models/staging/stg_orders.sql", + "original_file_path": "models/staging/stg_orders.sql", + "unique_id": "model.failure_corpus.stg_orders", + "fqn": [ + "failure_corpus", + "models", + "staging", + "stg_orders" + ], + "alias": "stg_orders", + "config": { + "enabled": true, + "alias": null, + "schema": null, + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Staged orders", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.stg_orders", + "depends_on": { + "macros": [], + "nodes": [ + "source.failure_corpus.raw_orders" + ] + } + } + }, + "sources": { + "source.failure_corpus.raw_orders": { + "database": null, + "schema": "", + "name": "raw_orders", + "resource_type": "source", + "package_name": "failure_corpus", + "path": "sources/raw.yml", + "original_file_path": "sources/raw.yml", + "unique_id": "source.failure_corpus.raw_orders", + "fqn": [ + "failure_corpus", + "raw_orders" + ], + "source_name": "raw_orders", + "source_description": "", + "loader": "", + "identifier": "raw_orders", + "quoting": {}, + "loaded_at_field": null, + "freshness": null, + "external": null, + "description": "Orders feed.", + "columns": { + "order_id": { + "name": "order_id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "customer_id": { + "name": "customer_id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "amount": { + "name": "amount", + "description": "", + "meta": {}, + "data_type": "DOUBLE", + "constraints": [], + "quote": null, + "tags": [] + }, + "status": { + "name": "status", + "description": "", + "meta": {}, + "data_type": "VARCHAR", + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": {}, + "source_meta": {}, + "tags": [], + "config": {}, + "patch_path": null, + "unrendered_config": {}, + "relation_name": null + } + }, + "macros": {}, + "exposures": {}, + "metrics": {}, + "groups": {}, + "selectors": {}, + "disabled": {}, + "parent_map": { + "model.failure_corpus.customer_totals": [], + "model.failure_corpus.stg_orders": [ + "source.failure_corpus.raw_orders" + ], + "source.failure_corpus.raw_orders": [] + }, + "group_map": {}, + "docs": {}, + "saved_queries": {}, + "semantic_models": {}, + "unit_tests": {} + } +} diff --git a/tests/goldens/compiler/failure/reference-two-name-arguments.json b/tests/goldens/compiler/failure/reference-two-name-arguments.json new file mode 100644 index 0000000000..a419ba1b44 --- /dev/null +++ b/tests/goldens/compiler/failure/reference-two-name-arguments.json @@ -0,0 +1,269 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "P012", + "message": "__ref(\"stg_orders\", \"orders\") is not a valid __ref() call", + "resource_type": "model", + "resource_name": "customer_totals", + "path": "models/marts/customer_totals.sql", + "line": 6, + "column": 6, + "location": { + "path": "models/marts/customer_totals.sql", + "line": 6, + "column": 6, + "end_line": 6, + "end_column": 35 + }, + "help": "__ref() takes exactly one double-quoted name, with no comments or extra spaces inside the parentheses: __ref(\"model_name\")" + } + ] + } + ], + "compiled": { + "models/marts/customer_totals.sql": [ + "SELECT order_id AS customer_id, 1 AS total_amount", + "FROM __ref(\"stg_orders\", \"orders\")", + "" + ], + "models/staging/stg_orders.sql": [ + "SELECT order_id, customer_id, amount, status", + "FROM __source(\"raw_orders\")", + "" + ] + }, + "manifest": { + "nodes": { + "model.failure_corpus.customer_totals": { + "database": null, + "schema": "main", + "name": "customer_totals", + "resource_type": "model", + "package_name": "failure_corpus", + "path": "models/marts/customer_totals.sql", + "original_file_path": "models/marts/customer_totals.sql", + "unique_id": "model.failure_corpus.customer_totals", + "fqn": [ + "failure_corpus", + "models", + "marts", + "customer_totals" + ], + "alias": "customer_totals", + "config": { + "enabled": true, + "alias": null, + "schema": null, + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Order totals per customer", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.customer_totals", + "depends_on": { + "macros": [], + "nodes": [] + } + }, + "model.failure_corpus.stg_orders": { + "database": null, + "schema": "main", + "name": "stg_orders", + "resource_type": "model", + "package_name": "failure_corpus", + "path": "models/staging/stg_orders.sql", + "original_file_path": "models/staging/stg_orders.sql", + "unique_id": "model.failure_corpus.stg_orders", + "fqn": [ + "failure_corpus", + "models", + "staging", + "stg_orders" + ], + "alias": "stg_orders", + "config": { + "enabled": true, + "alias": null, + "schema": null, + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Staged orders", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.stg_orders", + "depends_on": { + "macros": [], + "nodes": [ + "source.failure_corpus.raw_orders" + ] + } + } + }, + "sources": { + "source.failure_corpus.raw_orders": { + "database": null, + "schema": "", + "name": "raw_orders", + "resource_type": "source", + "package_name": "failure_corpus", + "path": "sources/raw.yml", + "original_file_path": "sources/raw.yml", + "unique_id": "source.failure_corpus.raw_orders", + "fqn": [ + "failure_corpus", + "raw_orders" + ], + "source_name": "raw_orders", + "source_description": "", + "loader": "", + "identifier": "raw_orders", + "quoting": {}, + "loaded_at_field": null, + "freshness": null, + "external": null, + "description": "Orders feed.", + "columns": { + "order_id": { + "name": "order_id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "customer_id": { + "name": "customer_id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "amount": { + "name": "amount", + "description": "", + "meta": {}, + "data_type": "DOUBLE", + "constraints": [], + "quote": null, + "tags": [] + }, + "status": { + "name": "status", + "description": "", + "meta": {}, + "data_type": "VARCHAR", + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": {}, + "source_meta": {}, + "tags": [], + "config": {}, + "patch_path": null, + "unrendered_config": {}, + "relation_name": null + } + }, + "macros": {}, + "exposures": {}, + "metrics": {}, + "groups": {}, + "selectors": {}, + "disabled": {}, + "parent_map": { + "model.failure_corpus.customer_totals": [], + "model.failure_corpus.stg_orders": [ + "source.failure_corpus.raw_orders" + ], + "source.failure_corpus.raw_orders": [] + }, + "group_map": {}, + "docs": {}, + "saved_queries": {}, + "semantic_models": {}, + "unit_tests": {} + } +} diff --git a/tests/goldens/compiler/failure/reference-unclosed-call.json b/tests/goldens/compiler/failure/reference-unclosed-call.json new file mode 100644 index 0000000000..ff28d64647 --- /dev/null +++ b/tests/goldens/compiler/failure/reference-unclosed-call.json @@ -0,0 +1,18 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "P001", + "message": "models/marts/customer_totals.sql:6:6: SQL reference contains an unclosed parenthesis" + } + ] + } + ], + "compiled": {}, + "manifest": null +} diff --git a/tests/goldens/compiler/failure/resource-name-collision.json b/tests/goldens/compiler/failure/resource-name-collision.json new file mode 100644 index 0000000000..65d112fa88 --- /dev/null +++ b/tests/goldens/compiler/failure/resource-name-collision.json @@ -0,0 +1,18 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "D007", + "message": "Logical relation name 'stg_orders' is declared as both model in models/staging/stg_orders.sql and seed in seeds/stg_orders.yml" + } + ] + } + ], + "compiled": {}, + "manifest": null +} diff --git a/tests/goldens/compiler/failure/rule-custom-finding.json b/tests/goldens/compiler/failure/rule-custom-finding.json new file mode 100644 index 0000000000..fae7d107f4 --- /dev/null +++ b/tests/goldens/compiler/failure/rule-custom-finding.json @@ -0,0 +1,27 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "rule", + "severity": "error", + "code": "XSQBRDIFF001", + "message": "Models avoid the stg_ prefix", + "path": "models/staging/stg_orders.sql", + "line": 1, + "column": 1, + "location": { + "path": "models/staging/stg_orders.sql", + "line": 1, + "column": 1 + }, + "help": "Rename the model." + } + ] + } + ], + "compiled": {}, + "manifest": null +} diff --git a/tests/goldens/compiler/failure/rule-unused-cte.json b/tests/goldens/compiler/failure/rule-unused-cte.json new file mode 100644 index 0000000000..7bf21cd0f3 --- /dev/null +++ b/tests/goldens/compiler/failure/rule-unused-cte.json @@ -0,0 +1,30 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "rule", + "severity": "error", + "code": "SQBRSQL005", + "message": "CTE is unreachable from the final query", + "path": "models/marts/customer_totals.sql", + "line": 5, + "column": 6, + "location": { + "path": "models/marts/customer_totals.sql", + "line": 5, + "column": 6 + }, + "help": "Reference the CTE from the final query or another reachable CTE, or remove it.", + "notes": [ + "Fix available: `sqb format --fix` applies it after compiler verification" + ] + } + ] + } + ], + "compiled": {}, + "manifest": null +} diff --git a/tests/goldens/compiler/failure/runtime-placeholder-without-default.json b/tests/goldens/compiler/failure/runtime-placeholder-without-default.json new file mode 100644 index 0000000000..c41ac30c26 --- /dev/null +++ b/tests/goldens/compiler/failure/runtime-placeholder-without-default.json @@ -0,0 +1,18 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "P001", + "message": "model 'stg_orders': @@@placeholders without default values in placeholders config: window_start" + } + ] + } + ], + "compiled": {}, + "manifest": null +} diff --git a/tests/goldens/compiler/failure/scenario-check-reads-source.json b/tests/goldens/compiler/failure/scenario-check-reads-source.json new file mode 100644 index 0000000000..f262a45f84 --- /dev/null +++ b/tests/goldens/compiler/failure/scenario-check-reads-source.json @@ -0,0 +1,18 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "P001", + "message": "SQL scenario file tests/scenarios/orders.sql CTE '__expected__stg_orders' must not reference project source 'raw_orders' with __source(); source-backed scenario data is only allowed in helper and fixture CTEs" + } + ] + } + ], + "compiled": {}, + "manifest": null +} diff --git a/tests/goldens/compiler/failure/scenario-dependent-checks.json b/tests/goldens/compiler/failure/scenario-dependent-checks.json new file mode 100644 index 0000000000..e2564dc3e4 --- /dev/null +++ b/tests/goldens/compiler/failure/scenario-dependent-checks.json @@ -0,0 +1,18 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "P001", + "message": "SQL scenario 'tests/scenarios/orders.sql' check CTE '__expected__stg_orders' must not depend on '__assert__no_rows'; expected results and assertions must be independent" + } + ] + } + ], + "compiled": {}, + "manifest": null +} diff --git a/tests/goldens/compiler/failure/scenario-first-of-two-errors.json b/tests/goldens/compiler/failure/scenario-first-of-two-errors.json new file mode 100644 index 0000000000..10b8363fc0 --- /dev/null +++ b/tests/goldens/compiler/failure/scenario-first-of-two-errors.json @@ -0,0 +1,18 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "P001", + "message": "SQL scenario 'tests/scenarios/a_orders.sql' must use __seed__ to identify a target" + } + ] + } + ], + "compiled": {}, + "manifest": null +} diff --git a/tests/goldens/compiler/failure/scenario-fixture-without-target.json b/tests/goldens/compiler/failure/scenario-fixture-without-target.json new file mode 100644 index 0000000000..26fa2c8e66 --- /dev/null +++ b/tests/goldens/compiler/failure/scenario-fixture-without-target.json @@ -0,0 +1,18 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "P001", + "message": "SQL scenario 'tests/scenarios/orders.sql' must use __seed__ to identify a target" + } + ] + } + ], + "compiled": {}, + "manifest": null +} diff --git a/tests/goldens/compiler/failure/scenario-macro-mock.json b/tests/goldens/compiler/failure/scenario-macro-mock.json new file mode 100644 index 0000000000..0282822409 --- /dev/null +++ b/tests/goldens/compiler/failure/scenario-macro-mock.json @@ -0,0 +1,18 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "P001", + "message": "SQL scenario 'tests/scenarios/orders.sql' does not support macro mock CTE '__macro__tidy'. Scenarios run real project macros; use SQL unit tests for macro mocks." + } + ] + } + ], + "compiled": {}, + "manifest": null +} diff --git a/tests/goldens/compiler/failure/scenario-materialized-cte.json b/tests/goldens/compiler/failure/scenario-materialized-cte.json new file mode 100644 index 0000000000..0844e70d10 --- /dev/null +++ b/tests/goldens/compiler/failure/scenario-materialized-cte.json @@ -0,0 +1,19 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "P001", + "message": "SQL scenario 'tests/scenarios/orders.sql' CTE '__source__raw_orders' must not use AS MATERIALIZED; materialization hints are not supported in SQL scenario CTEs\n --> tests/scenarios/orders.sql:6:25", + "help": "remove MATERIALIZED and write __source__raw_orders AS (...)" + } + ] + } + ], + "compiled": {}, + "manifest": null +} diff --git a/tests/goldens/compiler/failure/scenario-parse.json b/tests/goldens/compiler/failure/scenario-parse.json new file mode 100644 index 0000000000..f9a2ec10b1 --- /dev/null +++ b/tests/goldens/compiler/failure/scenario-parse.json @@ -0,0 +1,18 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "D009", + "message": "SQL scenario '/tests/scenarios/orders.sql' must start with a SCENARIO() header as the first non-whitespace content" + } + ] + } + ], + "compiled": {}, + "manifest": null +} diff --git a/tests/goldens/compiler/failure/scenario-quoted-cte-name.json b/tests/goldens/compiler/failure/scenario-quoted-cte-name.json new file mode 100644 index 0000000000..909d3d072d --- /dev/null +++ b/tests/goldens/compiler/failure/scenario-quoted-cte-name.json @@ -0,0 +1,19 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "P001", + "message": "SQL scenario 'tests/scenarios/orders.sql' CTE name \"__source__raw_orders\" must be an unquoted identifier of ASCII letters, digits and underscores\n --> tests/scenarios/orders.sql:6:1", + "help": "rename the CTE, for example __source__raw_orders; quoted CTE names and names with $ or non-ASCII characters are not supported" + } + ] + } + ], + "compiled": {}, + "manifest": null +} diff --git a/tests/goldens/compiler/failure/scenario-statement-after-ctes.json b/tests/goldens/compiler/failure/scenario-statement-after-ctes.json new file mode 100644 index 0000000000..0a1890b46b --- /dev/null +++ b/tests/goldens/compiler/failure/scenario-statement-after-ctes.json @@ -0,0 +1,18 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "P001", + "message": "SQL scenario 'tests/scenarios/orders.sql' must end after its CTEs; only an optional ceremonial top-level `SELECT 1` may follow them" + } + ] + } + ], + "compiled": {}, + "manifest": null +} diff --git a/tests/goldens/compiler/failure/scenario-without-checks.json b/tests/goldens/compiler/failure/scenario-without-checks.json new file mode 100644 index 0000000000..b8f7ce9fe9 --- /dev/null +++ b/tests/goldens/compiler/failure/scenario-without-checks.json @@ -0,0 +1,18 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "P001", + "message": "SQL scenario 'tests/scenarios/orders.sql' must define at least one __expected__ or __assert__ CTE" + } + ] + } + ], + "compiled": {}, + "manifest": null +} diff --git a/tests/goldens/compiler/failure/schema-from-sibling-folder.json b/tests/goldens/compiler/failure/schema-from-sibling-folder.json new file mode 100644 index 0000000000..fb5dc4d694 --- /dev/null +++ b/tests/goldens/compiler/failure/schema-from-sibling-folder.json @@ -0,0 +1,289 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "S006", + "message": "Schema 'order_shape' is not visible from 'models/staging/stg_orders.sql'. It is defined at models/marts/_sqlbuild/_schemas/order_shape.sql with local scope owned by 'models/marts'", + "resource_type": "model", + "resource_name": "stg_orders", + "path": "models/staging/stg_orders.sql", + "help": "move 'models/marts/_sqlbuild/_schemas/order_shape.sql' to a schemas/ role owned by 'models/staging' or one of its parent folders, or to the project-wide schemas/ when consumers span resource trees" + }, + { + "phase": "compile", + "severity": "error", + "code": "S007", + "message": "Declaration 'schema:order_shape' is currently exact-owner-private at 'models/marts' (models/marts/_sqlbuild/_schemas/order_shape.sql); required exact-owner-private at 'models/staging'. Consumers: model:stg_orders. Move it to 'models/staging/_sqlbuild/_schemas/'", + "path": "models/marts/_sqlbuild/_schemas/order_shape.sql", + "line": 1, + "column": 1, + "location": { + "path": "models/marts/_sqlbuild/_schemas/order_shape.sql", + "line": 1, + "column": 1 + } + } + ] + } + ], + "compiled": { + "models/marts/customer_totals.sql": [ + "SELECT customer_id, SUM(amount) AS total_amount", + "FROM __ref(\"stg_orders\")", + "GROUP BY customer_id", + "" + ], + "models/staging/stg_orders.sql": [ + "SELECT order_id, customer_id, amount, status", + "FROM __source(\"raw_orders\")", + "" + ] + }, + "manifest": { + "nodes": { + "model.failure_corpus.customer_totals": { + "database": null, + "schema": "main", + "name": "customer_totals", + "resource_type": "model", + "package_name": "failure_corpus", + "path": "models/marts/customer_totals.sql", + "original_file_path": "models/marts/customer_totals.sql", + "unique_id": "model.failure_corpus.customer_totals", + "fqn": [ + "failure_corpus", + "models", + "marts", + "customer_totals" + ], + "alias": "customer_totals", + "config": { + "enabled": true, + "alias": null, + "schema": null, + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Order totals per customer", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.customer_totals", + "depends_on": { + "macros": [], + "nodes": [ + "model.failure_corpus.stg_orders" + ] + } + }, + "model.failure_corpus.stg_orders": { + "database": null, + "schema": "main", + "name": "stg_orders", + "resource_type": "model", + "package_name": "failure_corpus", + "path": "models/staging/stg_orders.sql", + "original_file_path": "models/staging/stg_orders.sql", + "unique_id": "model.failure_corpus.stg_orders", + "fqn": [ + "failure_corpus", + "models", + "staging", + "stg_orders" + ], + "alias": "stg_orders", + "config": { + "enabled": true, + "alias": null, + "schema": null, + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Staged orders", + "columns": { + "order_id": { + "name": "order_id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.stg_orders", + "depends_on": { + "macros": [], + "nodes": [ + "source.failure_corpus.raw_orders" + ] + } + } + }, + "sources": { + "source.failure_corpus.raw_orders": { + "database": null, + "schema": "", + "name": "raw_orders", + "resource_type": "source", + "package_name": "failure_corpus", + "path": "sources/raw.yml", + "original_file_path": "sources/raw.yml", + "unique_id": "source.failure_corpus.raw_orders", + "fqn": [ + "failure_corpus", + "raw_orders" + ], + "source_name": "raw_orders", + "source_description": "", + "loader": "", + "identifier": "raw_orders", + "quoting": {}, + "loaded_at_field": null, + "freshness": null, + "external": null, + "description": "Orders feed.", + "columns": { + "order_id": { + "name": "order_id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "customer_id": { + "name": "customer_id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "amount": { + "name": "amount", + "description": "", + "meta": {}, + "data_type": "DOUBLE", + "constraints": [], + "quote": null, + "tags": [] + }, + "status": { + "name": "status", + "description": "", + "meta": {}, + "data_type": "VARCHAR", + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": {}, + "source_meta": {}, + "tags": [], + "config": {}, + "patch_path": null, + "unrendered_config": {}, + "relation_name": null + } + }, + "macros": {}, + "exposures": {}, + "metrics": {}, + "groups": {}, + "selectors": {}, + "disabled": {}, + "parent_map": { + "model.failure_corpus.customer_totals": [ + "model.failure_corpus.stg_orders" + ], + "model.failure_corpus.stg_orders": [ + "source.failure_corpus.raw_orders" + ], + "source.failure_corpus.raw_orders": [] + }, + "group_map": {}, + "docs": {}, + "saved_queries": {}, + "semantic_models": {}, + "unit_tests": {} + } +} diff --git a/tests/goldens/compiler/failure/schema-unterminated-declaration.json b/tests/goldens/compiler/failure/schema-unterminated-declaration.json new file mode 100644 index 0000000000..82860340ab --- /dev/null +++ b/tests/goldens/compiler/failure/schema-unterminated-declaration.json @@ -0,0 +1,18 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "D013", + "message": "/models/staging/_sqlbuild/_schemas/orders.sql has an unterminated declaration header" + } + ] + } + ], + "compiled": {}, + "manifest": null +} diff --git a/tests/goldens/compiler/failure/schema-yml-invalid-yaml.json b/tests/goldens/compiler/failure/schema-yml-invalid-yaml.json new file mode 100644 index 0000000000..ae3dc287c4 --- /dev/null +++ b/tests/goldens/compiler/failure/schema-yml-invalid-yaml.json @@ -0,0 +1,18 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "D005", + "message": "/models/marts/schema.yml contains invalid YAML at line 2, column 1: while parsing a node, did not find expected node content" + } + ] + } + ], + "compiled": {}, + "manifest": null +} diff --git a/tests/goldens/compiler/failure/schema-yml-model-metadata.json b/tests/goldens/compiler/failure/schema-yml-model-metadata.json new file mode 100644 index 0000000000..0d428b077f --- /dev/null +++ b/tests/goldens/compiler/failure/schema-yml-model-metadata.json @@ -0,0 +1,18 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "D005", + "message": "/models/marts/schema.yml declares model metadata in 'models', but model metadata must live in the model file MODEL(...). Move description, columns, and audits into the model header." + } + ] + } + ], + "compiled": {}, + "manifest": null +} diff --git a/tests/goldens/compiler/failure/scope-duplicate-scoped-enum.json b/tests/goldens/compiler/failure/scope-duplicate-scoped-enum.json new file mode 100644 index 0000000000..f0290603a8 --- /dev/null +++ b/tests/goldens/compiler/failure/scope-duplicate-scoped-enum.json @@ -0,0 +1,18 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "P001", + "message": "Invalid declaration scope index:\n[S003] Duplicate declaration 'enum:staged_status' at enums/status.sql:1:1, models/staging/_enums/status.sql:1:1\n[S003] Duplicate declaration 'enum:staged_status' at enums/status.sql:1:1, models/staging/_enums/status.sql:1:1" + } + ] + } + ], + "compiled": {}, + "manifest": null +} diff --git a/tests/goldens/compiler/failure/scope-first-of-two-visibility-errors.json b/tests/goldens/compiler/failure/scope-first-of-two-visibility-errors.json new file mode 100644 index 0000000000..e2a6fe7353 --- /dev/null +++ b/tests/goldens/compiler/failure/scope-first-of-two-visibility-errors.json @@ -0,0 +1,18 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "P001", + "message": "Enum 'staged_status' is known but inaccessible in '/models/marts/customer_totals.sql'. It is defined at models/staging/_enums/status.sql:1:1 with local scope owned by 'models/staging'; consumer path: '/models/marts/customer_totals.sql'" + } + ] + } + ], + "compiled": {}, + "manifest": null +} diff --git a/tests/goldens/compiler/failure/scope-inaccessible-local-macro.json b/tests/goldens/compiler/failure/scope-inaccessible-local-macro.json new file mode 100644 index 0000000000..c6c2cda986 --- /dev/null +++ b/tests/goldens/compiler/failure/scope-inaccessible-local-macro.json @@ -0,0 +1,18 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "P001", + "message": "Macro '@staged_amount' in '/models/marts/customer_totals.sql' is inaccessible. Defined at 'models/staging/_macros/amounts.py:1:1' with scope owner 'models/staging'; consumer/definition path is '/models/marts/customer_totals.sql'. Visible macros: none" + } + ] + } + ], + "compiled": {}, + "manifest": null +} diff --git a/tests/goldens/compiler/failure/scope-macro-test-cte-without-body.json b/tests/goldens/compiler/failure/scope-macro-test-cte-without-body.json new file mode 100644 index 0000000000..0f1d66c006 --- /dev/null +++ b/tests/goldens/compiler/failure/scope-macro-test-cte-without-body.json @@ -0,0 +1,18 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "P001", + "message": "SQL test 'tests/unit/test_customer_totals.sql' CTE '__macro_actual__' must use AS (...)" + } + ] + } + ], + "compiled": {}, + "manifest": null +} diff --git a/tests/goldens/compiler/failure/scope-malformed-expected-relationship.json b/tests/goldens/compiler/failure/scope-malformed-expected-relationship.json new file mode 100644 index 0000000000..3bf3e6f32e --- /dev/null +++ b/tests/goldens/compiler/failure/scope-malformed-expected-relationship.json @@ -0,0 +1,18 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "P001", + "message": "SQL test 'tests/unit/test_customer_totals.sql' must use __expected__ to identify a target" + } + ] + } + ], + "compiled": {}, + "manifest": null +} diff --git a/tests/goldens/compiler/failure/scope-scenario-expected-without-target.json b/tests/goldens/compiler/failure/scope-scenario-expected-without-target.json new file mode 100644 index 0000000000..a423eb3db6 --- /dev/null +++ b/tests/goldens/compiler/failure/scope-scenario-expected-without-target.json @@ -0,0 +1,18 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "P001", + "message": "SQL scenario 'tests/scenarios/orders.sql' must use __expected__ to identify a target" + } + ] + } + ], + "compiled": {}, + "manifest": null +} diff --git a/tests/goldens/compiler/failure/scope-test-outside-relationship-grant.json b/tests/goldens/compiler/failure/scope-test-outside-relationship-grant.json new file mode 100644 index 0000000000..c0fdc3f0d4 --- /dev/null +++ b/tests/goldens/compiler/failure/scope-test-outside-relationship-grant.json @@ -0,0 +1,18 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "P001", + "message": "Enum 'staged_status' is known but inaccessible in '/tests/unit/test_customer_totals.sql'. It is defined at models/staging/_enums/status.sql:1:1 with local scope owned by 'models/staging'; consumer path: '/tests/unit/test_customer_totals.sql'" + } + ] + } + ], + "compiled": {}, + "manifest": null +} diff --git a/tests/goldens/compiler/failure/scoped-root-at-project-root.json b/tests/goldens/compiler/failure/scoped-root-at-project-root.json new file mode 100644 index 0000000000..a2b71957ba --- /dev/null +++ b/tests/goldens/compiler/failure/scoped-root-at-project-root.json @@ -0,0 +1,18 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "D013", + "message": "Scoped declaration root _enums/ must be below a canonical authored root" + } + ] + } + ], + "compiled": {}, + "manifest": null +} diff --git a/tests/goldens/compiler/failure/seed-declaration-without-csv.json b/tests/goldens/compiler/failure/seed-declaration-without-csv.json new file mode 100644 index 0000000000..8eb1c9b5d2 --- /dev/null +++ b/tests/goldens/compiler/failure/seed-declaration-without-csv.json @@ -0,0 +1,18 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "D008", + "message": "Seed declaration 'order_channels' in seeds/order_channels.yml has no matching CSV file under seeds/" + } + ] + } + ], + "compiled": {}, + "manifest": null +} diff --git a/tests/goldens/compiler/failure/seed-header-mismatch.json b/tests/goldens/compiler/failure/seed-header-mismatch.json new file mode 100644 index 0000000000..d333066a74 --- /dev/null +++ b/tests/goldens/compiler/failure/seed-header-mismatch.json @@ -0,0 +1,18 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "D008", + "message": "seeds/order_channels.csv header ('id', 'name') does not match declared seed columns ('id', 'label') for 'order_channels'" + } + ] + } + ], + "compiled": {}, + "manifest": null +} diff --git a/tests/goldens/compiler/failure/seed-without-header.json b/tests/goldens/compiler/failure/seed-without-header.json new file mode 100644 index 0000000000..323aeb2ead --- /dev/null +++ b/tests/goldens/compiler/failure/seed-without-header.json @@ -0,0 +1,18 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "D008", + "message": "Seed CSV seeds/lookups.csv has no matching declaration for seed 'lookups' under seeds/**/*.yml" + } + ] + } + ], + "compiled": {}, + "manifest": null +} diff --git a/tests/goldens/compiler/failure/seed-yaml-extension.json b/tests/goldens/compiler/failure/seed-yaml-extension.json new file mode 100644 index 0000000000..57cedec1d4 --- /dev/null +++ b/tests/goldens/compiler/failure/seed-yaml-extension.json @@ -0,0 +1,18 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "D005", + "message": "Seed declaration file seeds/order_channels.yaml must use .yml; .yaml is not supported" + } + ] + } + ], + "compiled": {}, + "manifest": null +} diff --git a/tests/goldens/compiler/failure/set-operation-width.json b/tests/goldens/compiler/failure/set-operation-width.json new file mode 100644 index 0000000000..216edb4671 --- /dev/null +++ b/tests/goldens/compiler/failure/set-operation-width.json @@ -0,0 +1,272 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "B216", + "message": "UNION operands return different column counts: left 2, right 1", + "resource_type": "model", + "resource_name": "customer_totals", + "path": "models/marts/customer_totals.sql", + "line": 5, + "column": 1, + "location": { + "path": "models/marts/customer_totals.sql", + "line": 5, + "column": 1 + }, + "help": "return the same number of columns on both sides of this operation" + } + ] + } + ], + "compiled": { + "models/marts/customer_totals.sql": [ + "SELECT customer_id, amount FROM __ref(\"stg_orders\")", + "UNION ALL", + "SELECT customer_id FROM __ref(\"stg_orders\")", + "" + ], + "models/staging/stg_orders.sql": [ + "SELECT order_id, customer_id, amount, status", + "FROM __source(\"raw_orders\")", + "" + ] + }, + "manifest": { + "nodes": { + "model.failure_corpus.customer_totals": { + "database": null, + "schema": "main", + "name": "customer_totals", + "resource_type": "model", + "package_name": "failure_corpus", + "path": "models/marts/customer_totals.sql", + "original_file_path": "models/marts/customer_totals.sql", + "unique_id": "model.failure_corpus.customer_totals", + "fqn": [ + "failure_corpus", + "models", + "marts", + "customer_totals" + ], + "alias": "customer_totals", + "config": { + "enabled": true, + "alias": null, + "schema": null, + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Order totals per customer", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.customer_totals", + "depends_on": { + "macros": [], + "nodes": [ + "model.failure_corpus.stg_orders" + ] + } + }, + "model.failure_corpus.stg_orders": { + "database": null, + "schema": "main", + "name": "stg_orders", + "resource_type": "model", + "package_name": "failure_corpus", + "path": "models/staging/stg_orders.sql", + "original_file_path": "models/staging/stg_orders.sql", + "unique_id": "model.failure_corpus.stg_orders", + "fqn": [ + "failure_corpus", + "models", + "staging", + "stg_orders" + ], + "alias": "stg_orders", + "config": { + "enabled": true, + "alias": null, + "schema": null, + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Staged orders", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.stg_orders", + "depends_on": { + "macros": [], + "nodes": [ + "source.failure_corpus.raw_orders" + ] + } + } + }, + "sources": { + "source.failure_corpus.raw_orders": { + "database": null, + "schema": "", + "name": "raw_orders", + "resource_type": "source", + "package_name": "failure_corpus", + "path": "sources/raw.yml", + "original_file_path": "sources/raw.yml", + "unique_id": "source.failure_corpus.raw_orders", + "fqn": [ + "failure_corpus", + "raw_orders" + ], + "source_name": "raw_orders", + "source_description": "", + "loader": "", + "identifier": "raw_orders", + "quoting": {}, + "loaded_at_field": null, + "freshness": null, + "external": null, + "description": "Orders feed.", + "columns": { + "order_id": { + "name": "order_id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "customer_id": { + "name": "customer_id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "amount": { + "name": "amount", + "description": "", + "meta": {}, + "data_type": "DOUBLE", + "constraints": [], + "quote": null, + "tags": [] + }, + "status": { + "name": "status", + "description": "", + "meta": {}, + "data_type": "VARCHAR", + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": {}, + "source_meta": {}, + "tags": [], + "config": {}, + "patch_path": null, + "unrendered_config": {}, + "relation_name": null + } + }, + "macros": {}, + "exposures": {}, + "metrics": {}, + "groups": {}, + "selectors": {}, + "disabled": {}, + "parent_map": { + "model.failure_corpus.customer_totals": [ + "model.failure_corpus.stg_orders" + ], + "model.failure_corpus.stg_orders": [ + "source.failure_corpus.raw_orders" + ], + "source.failure_corpus.raw_orders": [] + }, + "group_map": {}, + "docs": {}, + "saved_queries": {}, + "semantic_models": {}, + "unit_tests": {} + } +} diff --git a/tests/goldens/compiler/failure/single-quoted-source-name.json b/tests/goldens/compiler/failure/single-quoted-source-name.json new file mode 100644 index 0000000000..85819900ef --- /dev/null +++ b/tests/goldens/compiler/failure/single-quoted-source-name.json @@ -0,0 +1,270 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "P012", + "message": "__source('raw_orders') is not a valid __source() call", + "resource_type": "model", + "resource_name": "stg_orders", + "path": "models/staging/stg_orders.sql", + "line": 6, + "column": 6, + "location": { + "path": "models/staging/stg_orders.sql", + "line": 6, + "column": 6, + "end_line": 6, + "end_column": 28 + }, + "help": "__source() takes exactly one double-quoted name, with no comments or extra spaces inside the parentheses: __source(\"raw_orders\")" + } + ] + } + ], + "compiled": { + "models/marts/customer_totals.sql": [ + "SELECT customer_id, SUM(amount) AS total_amount", + "FROM __ref(\"stg_orders\")", + "GROUP BY customer_id", + "" + ], + "models/staging/stg_orders.sql": [ + "SELECT order_id, customer_id, amount, status", + "FROM __source('raw_orders')", + "" + ] + }, + "manifest": { + "nodes": { + "model.failure_corpus.customer_totals": { + "database": null, + "schema": "main", + "name": "customer_totals", + "resource_type": "model", + "package_name": "failure_corpus", + "path": "models/marts/customer_totals.sql", + "original_file_path": "models/marts/customer_totals.sql", + "unique_id": "model.failure_corpus.customer_totals", + "fqn": [ + "failure_corpus", + "models", + "marts", + "customer_totals" + ], + "alias": "customer_totals", + "config": { + "enabled": true, + "alias": null, + "schema": null, + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Order totals per customer", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.customer_totals", + "depends_on": { + "macros": [], + "nodes": [ + "model.failure_corpus.stg_orders" + ] + } + }, + "model.failure_corpus.stg_orders": { + "database": null, + "schema": "main", + "name": "stg_orders", + "resource_type": "model", + "package_name": "failure_corpus", + "path": "models/staging/stg_orders.sql", + "original_file_path": "models/staging/stg_orders.sql", + "unique_id": "model.failure_corpus.stg_orders", + "fqn": [ + "failure_corpus", + "models", + "staging", + "stg_orders" + ], + "alias": "stg_orders", + "config": { + "enabled": true, + "alias": null, + "schema": null, + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Staged orders", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.stg_orders", + "depends_on": { + "macros": [], + "nodes": [] + } + } + }, + "sources": { + "source.failure_corpus.raw_orders": { + "database": null, + "schema": "", + "name": "raw_orders", + "resource_type": "source", + "package_name": "failure_corpus", + "path": "sources/raw.yml", + "original_file_path": "sources/raw.yml", + "unique_id": "source.failure_corpus.raw_orders", + "fqn": [ + "failure_corpus", + "raw_orders" + ], + "source_name": "raw_orders", + "source_description": "", + "loader": "", + "identifier": "raw_orders", + "quoting": {}, + "loaded_at_field": null, + "freshness": null, + "external": null, + "description": "Orders feed.", + "columns": { + "order_id": { + "name": "order_id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "customer_id": { + "name": "customer_id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "amount": { + "name": "amount", + "description": "", + "meta": {}, + "data_type": "DOUBLE", + "constraints": [], + "quote": null, + "tags": [] + }, + "status": { + "name": "status", + "description": "", + "meta": {}, + "data_type": "VARCHAR", + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": {}, + "source_meta": {}, + "tags": [], + "config": {}, + "patch_path": null, + "unrendered_config": {}, + "relation_name": null + } + }, + "macros": {}, + "exposures": {}, + "metrics": {}, + "groups": {}, + "selectors": {}, + "disabled": {}, + "parent_map": { + "model.failure_corpus.customer_totals": [ + "model.failure_corpus.stg_orders" + ], + "model.failure_corpus.stg_orders": [], + "source.failure_corpus.raw_orders": [] + }, + "group_map": {}, + "docs": {}, + "saved_queries": {}, + "semantic_models": {}, + "unit_tests": {} + } +} diff --git a/tests/goldens/compiler/failure/singular-audit-across-trees.json b/tests/goldens/compiler/failure/singular-audit-across-trees.json new file mode 100644 index 0000000000..0609e7b219 --- /dev/null +++ b/tests/goldens/compiler/failure/singular-audit-across-trees.json @@ -0,0 +1,278 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "S009", + "message": "Declaration 'singular_audit:totals_have_orders' is currently descendant-public at 'models/marts' (models/marts/_sqlbuild/audits/singular/totals_have_orders.sql); required project at 'top-level root'. References: model:customer_totals, source:raw_orders. Move it to 'top-level audits/singular/'", + "resource_type": "audit", + "resource_name": "totals_have_orders", + "path": "models/marts/_sqlbuild/audits/singular/totals_have_orders.sql", + "line": 1, + "column": 1, + "location": { + "path": "models/marts/_sqlbuild/audits/singular/totals_have_orders.sql", + "line": 1, + "column": 1 + } + } + ] + } + ], + "compiled": { + "audits/singular/totals_have_orders.sql": [ + "SELECT t.customer_id", + "FROM __ref(\"customer_totals\") t", + "LEFT JOIN __source(\"raw_orders\") o ON t.customer_id = o.customer_id", + "WHERE o.customer_id IS NULL", + "" + ], + "models/marts/customer_totals.sql": [ + "SELECT customer_id, SUM(amount) AS total_amount", + "FROM __ref(\"stg_orders\")", + "GROUP BY customer_id", + "" + ], + "models/staging/stg_orders.sql": [ + "SELECT order_id, customer_id, amount, status", + "FROM __source(\"raw_orders\")", + "" + ] + }, + "manifest": { + "nodes": { + "model.failure_corpus.customer_totals": { + "database": null, + "schema": "main", + "name": "customer_totals", + "resource_type": "model", + "package_name": "failure_corpus", + "path": "models/marts/customer_totals.sql", + "original_file_path": "models/marts/customer_totals.sql", + "unique_id": "model.failure_corpus.customer_totals", + "fqn": [ + "failure_corpus", + "models", + "marts", + "customer_totals" + ], + "alias": "customer_totals", + "config": { + "enabled": true, + "alias": null, + "schema": null, + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Order totals per customer", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.customer_totals", + "depends_on": { + "macros": [], + "nodes": [ + "model.failure_corpus.stg_orders" + ] + } + }, + "model.failure_corpus.stg_orders": { + "database": null, + "schema": "main", + "name": "stg_orders", + "resource_type": "model", + "package_name": "failure_corpus", + "path": "models/staging/stg_orders.sql", + "original_file_path": "models/staging/stg_orders.sql", + "unique_id": "model.failure_corpus.stg_orders", + "fqn": [ + "failure_corpus", + "models", + "staging", + "stg_orders" + ], + "alias": "stg_orders", + "config": { + "enabled": true, + "alias": null, + "schema": null, + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Staged orders", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.stg_orders", + "depends_on": { + "macros": [], + "nodes": [ + "source.failure_corpus.raw_orders" + ] + } + } + }, + "sources": { + "source.failure_corpus.raw_orders": { + "database": null, + "schema": "", + "name": "raw_orders", + "resource_type": "source", + "package_name": "failure_corpus", + "path": "sources/raw.yml", + "original_file_path": "sources/raw.yml", + "unique_id": "source.failure_corpus.raw_orders", + "fqn": [ + "failure_corpus", + "raw_orders" + ], + "source_name": "raw_orders", + "source_description": "", + "loader": "", + "identifier": "raw_orders", + "quoting": {}, + "loaded_at_field": null, + "freshness": null, + "external": null, + "description": "Orders feed.", + "columns": { + "order_id": { + "name": "order_id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "customer_id": { + "name": "customer_id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "amount": { + "name": "amount", + "description": "", + "meta": {}, + "data_type": "DOUBLE", + "constraints": [], + "quote": null, + "tags": [] + }, + "status": { + "name": "status", + "description": "", + "meta": {}, + "data_type": "VARCHAR", + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": {}, + "source_meta": {}, + "tags": [], + "config": {}, + "patch_path": null, + "unrendered_config": {}, + "relation_name": null + } + }, + "macros": {}, + "exposures": {}, + "metrics": {}, + "groups": {}, + "selectors": {}, + "disabled": {}, + "parent_map": { + "model.failure_corpus.customer_totals": [ + "model.failure_corpus.stg_orders" + ], + "model.failure_corpus.stg_orders": [ + "source.failure_corpus.raw_orders" + ], + "source.failure_corpus.raw_orders": [] + }, + "group_map": {}, + "docs": {}, + "saved_queries": {}, + "semantic_models": {}, + "unit_tests": {} + } +} diff --git a/tests/goldens/compiler/failure/singular-audit-one-resource.json b/tests/goldens/compiler/failure/singular-audit-one-resource.json new file mode 100644 index 0000000000..0985d3bfad --- /dev/null +++ b/tests/goldens/compiler/failure/singular-audit-one-resource.json @@ -0,0 +1,269 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "P004", + "message": "Singular audit 'positive_totals' in models/marts/_sqlbuild/audits/singular/positive_totals.sql checks only model 'customer_totals'; singular audits must be cross-resource", + "resource_type": "audit", + "resource_name": "positive_totals", + "path": "models/marts/_sqlbuild/audits/singular/positive_totals.sql", + "help": "attach a generic audit to 'customer_totals' instead: write it in an audits/generic/ role and select FROM @relation, joining @relation to itself for self-join checks" + } + ] + } + ], + "compiled": { + "audits/singular/positive_totals.sql": [ + "SELECT customer_id FROM __ref(\"customer_totals\") WHERE total_amount < 0", + "" + ], + "models/marts/customer_totals.sql": [ + "SELECT customer_id, SUM(amount) AS total_amount", + "FROM __ref(\"stg_orders\")", + "GROUP BY customer_id", + "" + ], + "models/staging/stg_orders.sql": [ + "SELECT order_id, customer_id, amount, status", + "FROM __source(\"raw_orders\")", + "" + ] + }, + "manifest": { + "nodes": { + "model.failure_corpus.customer_totals": { + "database": null, + "schema": "main", + "name": "customer_totals", + "resource_type": "model", + "package_name": "failure_corpus", + "path": "models/marts/customer_totals.sql", + "original_file_path": "models/marts/customer_totals.sql", + "unique_id": "model.failure_corpus.customer_totals", + "fqn": [ + "failure_corpus", + "models", + "marts", + "customer_totals" + ], + "alias": "customer_totals", + "config": { + "enabled": true, + "alias": null, + "schema": null, + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Order totals per customer", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.customer_totals", + "depends_on": { + "macros": [], + "nodes": [ + "model.failure_corpus.stg_orders" + ] + } + }, + "model.failure_corpus.stg_orders": { + "database": null, + "schema": "main", + "name": "stg_orders", + "resource_type": "model", + "package_name": "failure_corpus", + "path": "models/staging/stg_orders.sql", + "original_file_path": "models/staging/stg_orders.sql", + "unique_id": "model.failure_corpus.stg_orders", + "fqn": [ + "failure_corpus", + "models", + "staging", + "stg_orders" + ], + "alias": "stg_orders", + "config": { + "enabled": true, + "alias": null, + "schema": null, + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Staged orders", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.stg_orders", + "depends_on": { + "macros": [], + "nodes": [ + "source.failure_corpus.raw_orders" + ] + } + } + }, + "sources": { + "source.failure_corpus.raw_orders": { + "database": null, + "schema": "", + "name": "raw_orders", + "resource_type": "source", + "package_name": "failure_corpus", + "path": "sources/raw.yml", + "original_file_path": "sources/raw.yml", + "unique_id": "source.failure_corpus.raw_orders", + "fqn": [ + "failure_corpus", + "raw_orders" + ], + "source_name": "raw_orders", + "source_description": "", + "loader": "", + "identifier": "raw_orders", + "quoting": {}, + "loaded_at_field": null, + "freshness": null, + "external": null, + "description": "Orders feed.", + "columns": { + "order_id": { + "name": "order_id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "customer_id": { + "name": "customer_id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "amount": { + "name": "amount", + "description": "", + "meta": {}, + "data_type": "DOUBLE", + "constraints": [], + "quote": null, + "tags": [] + }, + "status": { + "name": "status", + "description": "", + "meta": {}, + "data_type": "VARCHAR", + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": {}, + "source_meta": {}, + "tags": [], + "config": {}, + "patch_path": null, + "unrendered_config": {}, + "relation_name": null + } + }, + "macros": {}, + "exposures": {}, + "metrics": {}, + "groups": {}, + "selectors": {}, + "disabled": {}, + "parent_map": { + "model.failure_corpus.customer_totals": [ + "model.failure_corpus.stg_orders" + ], + "model.failure_corpus.stg_orders": [ + "source.failure_corpus.raw_orders" + ], + "source.failure_corpus.raw_orders": [] + }, + "group_map": {}, + "docs": {}, + "saved_queries": {}, + "semantic_models": {}, + "unit_tests": {} + } +} diff --git a/tests/goldens/compiler/failure/snapshot-schema-change-with-contract.json b/tests/goldens/compiler/failure/snapshot-schema-change-with-contract.json new file mode 100644 index 0000000000..c602ead729 --- /dev/null +++ b/tests/goldens/compiler/failure/snapshot-schema-change-with-contract.json @@ -0,0 +1,18 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "K012", + "message": "model 'stg_orders': snapshot_schema_change=append_new_columns is not valid with contract enforced; add new columns to the contract or set contract none" + } + ] + } + ], + "compiled": {}, + "manifest": null +} diff --git a/tests/goldens/compiler/failure/source-description-unknown-variable.json b/tests/goldens/compiler/failure/source-description-unknown-variable.json new file mode 100644 index 0000000000..ee3bfbff1d --- /dev/null +++ b/tests/goldens/compiler/failure/source-description-unknown-variable.json @@ -0,0 +1,18 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "P001", + "message": "source raw_orders description references unknown variable 'channel'" + } + ] + } + ], + "compiled": {}, + "manifest": null +} diff --git a/tests/goldens/compiler/failure/source-parse.json b/tests/goldens/compiler/failure/source-parse.json new file mode 100644 index 0000000000..905f62d103 --- /dev/null +++ b/tests/goldens/compiler/failure/source-parse.json @@ -0,0 +1,18 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "D006", + "message": "/sources/raw.yml contains invalid YAML at line 4, column 1: while parsing a node, did not find expected node content" + } + ] + } + ], + "compiled": {}, + "manifest": null +} diff --git a/tests/goldens/compiler/failure/source-unknown-key.json b/tests/goldens/compiler/failure/source-unknown-key.json new file mode 100644 index 0000000000..b458236a1f --- /dev/null +++ b/tests/goldens/compiler/failure/source-unknown-key.json @@ -0,0 +1,19 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "D006", + "message": "/sources/raw.yml source has unknown keys: freshnes; allowed keys: audits, columns, contract, cursor_column, database, description, expression, freshness, ingestr, load_batch_size, managed, meta, name, schema, table, type_enforcement, unique_key, write_strategy", + "help": "did you mean 'freshness'?" + } + ] + } + ], + "compiled": {}, + "manifest": null +} diff --git a/tests/goldens/compiler/failure/source-unsupported-yaml.json b/tests/goldens/compiler/failure/source-unsupported-yaml.json new file mode 100644 index 0000000000..76820a2e86 --- /dev/null +++ b/tests/goldens/compiler/failure/source-unsupported-yaml.json @@ -0,0 +1,19 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "D006", + "message": "/sources/raw.yml uses YAML that SQLBuild does not support at line 17, column 22: values tagged !!binary", + "help": "SQLBuild reads plain YAML: block and flow mappings and sequences, plain and quoted scalars, literal and folded block scalars, anchors, aliases and merge keys. Rewrite this part in one of those forms." + } + ] + } + ], + "compiled": {}, + "manifest": null +} diff --git a/tests/goldens/compiler/failure/sql-function-header.json b/tests/goldens/compiler/failure/sql-function-header.json new file mode 100644 index 0000000000..4b0cd22780 --- /dev/null +++ b/tests/goldens/compiler/failure/sql-function-header.json @@ -0,0 +1,18 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "D002", + "message": "FUNCTION(...) in '/functions/sql/is_placed.sql' contains invalid SQLBuild header syntax: expected value at position 42" + } + ] + } + ], + "compiled": {}, + "manifest": null +} diff --git a/tests/goldens/compiler/failure/sql-function-missing-returns.json b/tests/goldens/compiler/failure/sql-function-missing-returns.json new file mode 100644 index 0000000000..56078e96cd --- /dev/null +++ b/tests/goldens/compiler/failure/sql-function-missing-returns.json @@ -0,0 +1,18 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "P001", + "message": "SQL function file functions/sql/order_label.sql must declare returns" + } + ] + } + ], + "compiled": {}, + "manifest": null +} diff --git a/tests/goldens/compiler/failure/sql-function-template-namespace.json b/tests/goldens/compiler/failure/sql-function-template-namespace.json new file mode 100644 index 0000000000..a245f00d16 --- /dev/null +++ b/tests/goldens/compiler/failure/sql-function-template-namespace.json @@ -0,0 +1,18 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "P001", + "message": "SQL function functions/sql/order_label.sql returns references unsupported template namespace 'VAR'" + } + ] + } + ], + "compiled": {}, + "manifest": null +} diff --git a/tests/goldens/compiler/failure/sql-hook-unknown-key.json b/tests/goldens/compiler/failure/sql-hook-unknown-key.json new file mode 100644 index 0000000000..2911434d9e --- /dev/null +++ b/tests/goldens/compiler/failure/sql-hook-unknown-key.json @@ -0,0 +1,19 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "D014", + "message": "HOOK() in '/hooks/sql/record_refresh.sql:3' has unsupported keys: phase", + "help": "supported keys: description" + } + ] + } + ], + "compiled": {}, + "manifest": null +} diff --git a/tests/goldens/compiler/failure/syntax-function.json b/tests/goldens/compiler/failure/syntax-function.json new file mode 100644 index 0000000000..b6ddf7cd24 --- /dev/null +++ b/tests/goldens/compiler/failure/syntax-function.json @@ -0,0 +1,19 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "P001", + "message": "SQL syntax error in function 'scaled_amount' (/functions/sql/scaled_amount.sql): Parse error at line 1, column 21: Expected RParen, got end of input ('') near [* (p_factor]", + "help": "if this SQL is valid for your warehouse, please report it so the parser can support it\n = help: SQL analysis is on for this project; to turn it off for every model, set this in sqlbuild_project.toml:\n [settings]\n sql_analysis = false\n = help: to skip SQL analysis for one run, use `--no-sql-analysis`" + } + ] + } + ], + "compiled": {}, + "manifest": null +} diff --git a/tests/goldens/compiler/failure/syntax-hook.json b/tests/goldens/compiler/failure/syntax-hook.json new file mode 100644 index 0000000000..d8ab5bfc26 --- /dev/null +++ b/tests/goldens/compiler/failure/syntax-hook.json @@ -0,0 +1,19 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "P001", + "message": "Polyglot could not parse model 'customer_totals' post_hooks[0] sql(\"record_refresh\") (hooks/sql/record_refresh.sql): Expected table name or subquery, got Where", + "help": "if this SQL is valid for your warehouse, skip SQL analysis for this model, add this to the MODEL header:\n MODEL (\n sql_analysis false,\n ...\n );\n and please report the SQL so the parser can support it\n = help: SQL analysis is on for this project; to turn it off for every model, set this in sqlbuild_project.toml:\n [settings]\n sql_analysis = false\n = help: to skip SQL analysis for one run, use `--no-sql-analysis`" + } + ] + } + ], + "compiled": {}, + "manifest": null +} diff --git a/tests/goldens/compiler/failure/syntax-inline-hook.json b/tests/goldens/compiler/failure/syntax-inline-hook.json new file mode 100644 index 0000000000..1a52b36985 --- /dev/null +++ b/tests/goldens/compiler/failure/syntax-inline-hook.json @@ -0,0 +1,19 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "P001", + "message": "Polyglot could not parse model 'customer_totals' post_hooks[0] inline_sql(\"...\") (/models/marts/customer_totals.sql): Expected table name or subquery, got Where", + "help": "if this SQL is valid for your warehouse, skip SQL analysis for this model, add this to the MODEL header:\n MODEL (\n sql_analysis false,\n ...\n );\n and please report the SQL so the parser can support it\n = help: SQL analysis is on for this project; to turn it off for every model, set this in sqlbuild_project.toml:\n [settings]\n sql_analysis = false\n = help: to skip SQL analysis for one run, use `--no-sql-analysis`" + } + ] + } + ], + "compiled": {}, + "manifest": null +} diff --git a/tests/goldens/compiler/failure/syntax-source-expression.json b/tests/goldens/compiler/failure/syntax-source-expression.json new file mode 100644 index 0000000000..e336422be3 --- /dev/null +++ b/tests/goldens/compiler/failure/syntax-source-expression.json @@ -0,0 +1,19 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "P001", + "message": "SQL syntax error in source expression 'raw_orders' (/sources/raw.yml): Parse error at line 1, column 38: Unexpected token: Comma", + "help": "if this SQL is valid for your warehouse, please report it so the parser can support it\n = help: SQL analysis is on for this project; to turn it off for every model, set this in sqlbuild_project.toml:\n [settings]\n sql_analysis = false\n = help: to skip SQL analysis for one run, use `--no-sql-analysis`" + } + ] + } + ], + "compiled": {}, + "manifest": null +} diff --git a/tests/goldens/compiler/failure/table-function-arity.json b/tests/goldens/compiler/failure/table-function-arity.json new file mode 100644 index 0000000000..22621bb4fa --- /dev/null +++ b/tests/goldens/compiler/failure/table-function-arity.json @@ -0,0 +1,18 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "P001", + "message": "Model file models/marts/customer_totals.sql table function 'table_fn__orders_for' expects 1 argument but received 2" + } + ] + } + ], + "compiled": {}, + "manifest": null +} diff --git a/tests/goldens/compiler/failure/table-function-empty-call-argument.json b/tests/goldens/compiler/failure/table-function-empty-call-argument.json new file mode 100644 index 0000000000..946df88075 --- /dev/null +++ b/tests/goldens/compiler/failure/table-function-empty-call-argument.json @@ -0,0 +1,18 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "P001", + "message": "models/marts/customer_totals.sql:6:6: SQL reference contains an empty argument" + } + ] + } + ], + "compiled": {}, + "manifest": null +} diff --git a/tests/goldens/compiler/failure/table-function-single-quoted-name.json b/tests/goldens/compiler/failure/table-function-single-quoted-name.json new file mode 100644 index 0000000000..9b0b2aaa4e --- /dev/null +++ b/tests/goldens/compiler/failure/table-function-single-quoted-name.json @@ -0,0 +1,269 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "P012", + "message": "__table_fn('table_fn__orders_for') is not a valid __table_fn() call", + "resource_type": "model", + "resource_name": "customer_totals", + "path": "models/marts/customer_totals.sql", + "line": 6, + "column": 6, + "location": { + "path": "models/marts/customer_totals.sql", + "line": 6, + "column": 6, + "end_line": 6, + "end_column": 40 + }, + "help": "__table_fn() takes exactly one double-quoted name, with no comments or extra spaces inside the parentheses: __table_fn(\"table_fn__orders_for\")(...)" + } + ] + } + ], + "compiled": { + "models/marts/customer_totals.sql": [ + "SELECT order_id AS customer_id, 1 AS total_amount", + "FROM __table_fn('table_fn__orders_for')(1)", + "" + ], + "models/staging/stg_orders.sql": [ + "SELECT order_id, customer_id, amount, status", + "FROM __source(\"raw_orders\")", + "" + ] + }, + "manifest": { + "nodes": { + "model.failure_corpus.customer_totals": { + "database": null, + "schema": "main", + "name": "customer_totals", + "resource_type": "model", + "package_name": "failure_corpus", + "path": "models/marts/customer_totals.sql", + "original_file_path": "models/marts/customer_totals.sql", + "unique_id": "model.failure_corpus.customer_totals", + "fqn": [ + "failure_corpus", + "models", + "marts", + "customer_totals" + ], + "alias": "customer_totals", + "config": { + "enabled": true, + "alias": null, + "schema": null, + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Order totals per customer", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.customer_totals", + "depends_on": { + "macros": [], + "nodes": [] + } + }, + "model.failure_corpus.stg_orders": { + "database": null, + "schema": "main", + "name": "stg_orders", + "resource_type": "model", + "package_name": "failure_corpus", + "path": "models/staging/stg_orders.sql", + "original_file_path": "models/staging/stg_orders.sql", + "unique_id": "model.failure_corpus.stg_orders", + "fqn": [ + "failure_corpus", + "models", + "staging", + "stg_orders" + ], + "alias": "stg_orders", + "config": { + "enabled": true, + "alias": null, + "schema": null, + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Staged orders", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.stg_orders", + "depends_on": { + "macros": [], + "nodes": [ + "source.failure_corpus.raw_orders" + ] + } + } + }, + "sources": { + "source.failure_corpus.raw_orders": { + "database": null, + "schema": "", + "name": "raw_orders", + "resource_type": "source", + "package_name": "failure_corpus", + "path": "sources/raw.yml", + "original_file_path": "sources/raw.yml", + "unique_id": "source.failure_corpus.raw_orders", + "fqn": [ + "failure_corpus", + "raw_orders" + ], + "source_name": "raw_orders", + "source_description": "", + "loader": "", + "identifier": "raw_orders", + "quoting": {}, + "loaded_at_field": null, + "freshness": null, + "external": null, + "description": "Orders feed.", + "columns": { + "order_id": { + "name": "order_id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "customer_id": { + "name": "customer_id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "amount": { + "name": "amount", + "description": "", + "meta": {}, + "data_type": "DOUBLE", + "constraints": [], + "quote": null, + "tags": [] + }, + "status": { + "name": "status", + "description": "", + "meta": {}, + "data_type": "VARCHAR", + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": {}, + "source_meta": {}, + "tags": [], + "config": {}, + "patch_path": null, + "unrendered_config": {}, + "relation_name": null + } + }, + "macros": {}, + "exposures": {}, + "metrics": {}, + "groups": {}, + "selectors": {}, + "disabled": {}, + "parent_map": { + "model.failure_corpus.customer_totals": [], + "model.failure_corpus.stg_orders": [ + "source.failure_corpus.raw_orders" + ], + "source.failure_corpus.raw_orders": [] + }, + "group_map": {}, + "docs": {}, + "saved_queries": {}, + "semantic_models": {}, + "unit_tests": {} + } +} diff --git a/tests/goldens/compiler/failure/table-function-unclosed-call.json b/tests/goldens/compiler/failure/table-function-unclosed-call.json new file mode 100644 index 0000000000..233c2dadd1 --- /dev/null +++ b/tests/goldens/compiler/failure/table-function-unclosed-call.json @@ -0,0 +1,18 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "P001", + "message": "models/marts/customer_totals.sql:6:6: SQL table function call contains an unclosed parenthesis" + } + ] + } + ], + "compiled": {}, + "manifest": null +} diff --git a/tests/goldens/compiler/failure/table-function-without-arguments.json b/tests/goldens/compiler/failure/table-function-without-arguments.json new file mode 100644 index 0000000000..8ce7a7e6a3 --- /dev/null +++ b/tests/goldens/compiler/failure/table-function-without-arguments.json @@ -0,0 +1,269 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "P012", + "message": "__table_fn must be followed by an argument list", + "resource_type": "model", + "resource_name": "customer_totals", + "path": "models/marts/customer_totals.sql", + "line": 6, + "column": 6, + "location": { + "path": "models/marts/customer_totals.sql", + "line": 6, + "column": 6, + "end_line": 6, + "end_column": 40 + }, + "help": "pass the function arguments in a second set of parentheses, using () for no arguments: __table_fn(\"table_fn__orders_for\")()" + } + ] + } + ], + "compiled": { + "models/marts/customer_totals.sql": [ + "SELECT order_id AS customer_id, 1 AS total_amount", + "FROM __table_fn(\"table_fn__orders_for\") /* no call */", + "" + ], + "models/staging/stg_orders.sql": [ + "SELECT order_id, customer_id, amount, status", + "FROM __source(\"raw_orders\")", + "" + ] + }, + "manifest": { + "nodes": { + "model.failure_corpus.customer_totals": { + "database": null, + "schema": "main", + "name": "customer_totals", + "resource_type": "model", + "package_name": "failure_corpus", + "path": "models/marts/customer_totals.sql", + "original_file_path": "models/marts/customer_totals.sql", + "unique_id": "model.failure_corpus.customer_totals", + "fqn": [ + "failure_corpus", + "models", + "marts", + "customer_totals" + ], + "alias": "customer_totals", + "config": { + "enabled": true, + "alias": null, + "schema": null, + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Order totals per customer", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.customer_totals", + "depends_on": { + "macros": [], + "nodes": [] + } + }, + "model.failure_corpus.stg_orders": { + "database": null, + "schema": "main", + "name": "stg_orders", + "resource_type": "model", + "package_name": "failure_corpus", + "path": "models/staging/stg_orders.sql", + "original_file_path": "models/staging/stg_orders.sql", + "unique_id": "model.failure_corpus.stg_orders", + "fqn": [ + "failure_corpus", + "models", + "staging", + "stg_orders" + ], + "alias": "stg_orders", + "config": { + "enabled": true, + "alias": null, + "schema": null, + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Staged orders", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.stg_orders", + "depends_on": { + "macros": [], + "nodes": [ + "source.failure_corpus.raw_orders" + ] + } + } + }, + "sources": { + "source.failure_corpus.raw_orders": { + "database": null, + "schema": "", + "name": "raw_orders", + "resource_type": "source", + "package_name": "failure_corpus", + "path": "sources/raw.yml", + "original_file_path": "sources/raw.yml", + "unique_id": "source.failure_corpus.raw_orders", + "fqn": [ + "failure_corpus", + "raw_orders" + ], + "source_name": "raw_orders", + "source_description": "", + "loader": "", + "identifier": "raw_orders", + "quoting": {}, + "loaded_at_field": null, + "freshness": null, + "external": null, + "description": "Orders feed.", + "columns": { + "order_id": { + "name": "order_id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "customer_id": { + "name": "customer_id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "amount": { + "name": "amount", + "description": "", + "meta": {}, + "data_type": "DOUBLE", + "constraints": [], + "quote": null, + "tags": [] + }, + "status": { + "name": "status", + "description": "", + "meta": {}, + "data_type": "VARCHAR", + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": {}, + "source_meta": {}, + "tags": [], + "config": {}, + "patch_path": null, + "unrendered_config": {}, + "relation_name": null + } + }, + "macros": {}, + "exposures": {}, + "metrics": {}, + "groups": {}, + "selectors": {}, + "disabled": {}, + "parent_map": { + "model.failure_corpus.customer_totals": [], + "model.failure_corpus.stg_orders": [ + "source.failure_corpus.raw_orders" + ], + "source.failure_corpus.raw_orders": [] + }, + "group_map": {}, + "docs": {}, + "saved_queries": {}, + "semantic_models": {}, + "unit_tests": {} + } +} diff --git a/tests/goldens/compiler/failure/target-managed-loader-collision.json b/tests/goldens/compiler/failure/target-managed-loader-collision.json new file mode 100644 index 0000000000..0d0215bd07 --- /dev/null +++ b/tests/goldens/compiler/failure/target-managed-loader-collision.json @@ -0,0 +1,18 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "S102", + "message": "Managed loader write collision: targets 'dev' and 'prod' both resolve to database='failure.duckdb' schema='raw'. Set distinct targets.dev.loader_schema and targets.prod.loader_schema values." + } + ] + } + ], + "compiled": {}, + "manifest": null +} diff --git a/tests/goldens/compiler/failure/target-missing-database.json b/tests/goldens/compiler/failure/target-missing-database.json new file mode 100644 index 0000000000..3fce7f186c --- /dev/null +++ b/tests/goldens/compiler/failure/target-missing-database.json @@ -0,0 +1,19 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "S101", + "message": "snowflake execution requires explicit target database. model 'customer_totals' resolved to database=None schema='ANALYTICS'. Set them in the model config, the environment config, or the project defaults.", + "help": "to set project-wide defaults, set this in sqlbuild_project.toml:\n [defaults]\n database = \"my_database\"" + } + ] + } + ], + "compiled": {}, + "manifest": null +} diff --git a/tests/goldens/compiler/failure/target-missing-write-schema.json b/tests/goldens/compiler/failure/target-missing-write-schema.json new file mode 100644 index 0000000000..dc9dd0833a --- /dev/null +++ b/tests/goldens/compiler/failure/target-missing-write-schema.json @@ -0,0 +1,18 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "S103", + "message": "Model 'customer_totals' has no explicitly resolved physical write schema. Set schema on the resource or its defaults, selected target, loader_schema, or connection." + } + ] + } + ], + "compiled": {}, + "manifest": null +} diff --git a/tests/goldens/compiler/failure/target-named-without-schema.json b/tests/goldens/compiler/failure/target-named-without-schema.json new file mode 100644 index 0000000000..f256f489be --- /dev/null +++ b/tests/goldens/compiler/failure/target-named-without-schema.json @@ -0,0 +1,18 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "S104", + "message": "Named target 'dev' must explicitly set schema to a nonblank literal or 'preserve'. Resource schemas, defaults, and connection schemas do not select a named target schema strategy." + } + ] + } + ], + "compiled": {}, + "manifest": null +} diff --git a/tests/goldens/compiler/failure/target-unknown-key.json b/tests/goldens/compiler/failure/target-unknown-key.json new file mode 100644 index 0000000000..238270d0ba --- /dev/null +++ b/tests/goldens/compiler/failure/target-unknown-key.json @@ -0,0 +1,19 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "D001", + "message": "/sqlbuild_project.toml targets.ci contains unknown key(s): warehouse_size. Allowed keys: clone, compile_cache, connection, database, default_table_type, defer_clone_from, defer_sources_to, diff, execution_limits, loader_schema, missing_migration_origin, owns_time_travel_retention_namespace, schema, table_type_downgrade, time_travel_retention, time_travel_retention_decrease, vars, warehouses", + "help": "did you mean 'warehouses'?" + } + ] + } + ], + "compiled": {}, + "manifest": null +} diff --git a/tests/goldens/compiler/failure/test-assertion-reads-unmocked-source.json b/tests/goldens/compiler/failure/test-assertion-reads-unmocked-source.json new file mode 100644 index 0000000000..0d989d6ad8 --- /dev/null +++ b/tests/goldens/compiler/failure/test-assertion-reads-unmocked-source.json @@ -0,0 +1,274 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "P013", + "message": "SQL test assertion CTE '__assert__no_negative_orders' calls __source(\"raw_orders\"), which the test does not mock, so the test query cannot resolve it", + "resource_type": "sql_test", + "resource_name": "test_customer_totals", + "path": "tests/unit/test_customer_totals.sql", + "line": 11, + "column": 24, + "location": { + "path": "tests/unit/test_customer_totals.sql", + "line": 11, + "column": 24, + "end_line": 11, + "end_column": 46 + }, + "help": "Mock it in the test, for example __source__raw_orders AS (SELECT ...), or remove the call from assertion CTE '__assert__no_negative_orders'." + } + ] + } + ], + "compiled": { + "models/marts/customer_totals.sql": [ + "SELECT customer_id, SUM(amount) AS total_amount", + "FROM __ref(\"stg_orders\")", + "GROUP BY customer_id", + "" + ], + "models/staging/stg_orders.sql": [ + "SELECT order_id, customer_id, amount, status", + "FROM __source(\"raw_orders\")", + "" + ] + }, + "manifest": { + "nodes": { + "model.failure_corpus.customer_totals": { + "database": null, + "schema": "main", + "name": "customer_totals", + "resource_type": "model", + "package_name": "failure_corpus", + "path": "models/marts/customer_totals.sql", + "original_file_path": "models/marts/customer_totals.sql", + "unique_id": "model.failure_corpus.customer_totals", + "fqn": [ + "failure_corpus", + "models", + "marts", + "customer_totals" + ], + "alias": "customer_totals", + "config": { + "enabled": true, + "alias": null, + "schema": null, + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Order totals per customer", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.customer_totals", + "depends_on": { + "macros": [], + "nodes": [ + "model.failure_corpus.stg_orders" + ] + } + }, + "model.failure_corpus.stg_orders": { + "database": null, + "schema": "main", + "name": "stg_orders", + "resource_type": "model", + "package_name": "failure_corpus", + "path": "models/staging/stg_orders.sql", + "original_file_path": "models/staging/stg_orders.sql", + "unique_id": "model.failure_corpus.stg_orders", + "fqn": [ + "failure_corpus", + "models", + "staging", + "stg_orders" + ], + "alias": "stg_orders", + "config": { + "enabled": true, + "alias": null, + "schema": null, + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Staged orders", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.stg_orders", + "depends_on": { + "macros": [], + "nodes": [ + "source.failure_corpus.raw_orders" + ] + } + } + }, + "sources": { + "source.failure_corpus.raw_orders": { + "database": null, + "schema": "", + "name": "raw_orders", + "resource_type": "source", + "package_name": "failure_corpus", + "path": "sources/raw.yml", + "original_file_path": "sources/raw.yml", + "unique_id": "source.failure_corpus.raw_orders", + "fqn": [ + "failure_corpus", + "raw_orders" + ], + "source_name": "raw_orders", + "source_description": "", + "loader": "", + "identifier": "raw_orders", + "quoting": {}, + "loaded_at_field": null, + "freshness": null, + "external": null, + "description": "Orders feed.", + "columns": { + "order_id": { + "name": "order_id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "customer_id": { + "name": "customer_id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "amount": { + "name": "amount", + "description": "", + "meta": {}, + "data_type": "DOUBLE", + "constraints": [], + "quote": null, + "tags": [] + }, + "status": { + "name": "status", + "description": "", + "meta": {}, + "data_type": "VARCHAR", + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": {}, + "source_meta": {}, + "tags": [], + "config": {}, + "patch_path": null, + "unrendered_config": {}, + "relation_name": null + } + }, + "macros": {}, + "exposures": {}, + "metrics": {}, + "groups": {}, + "selectors": {}, + "disabled": {}, + "parent_map": { + "model.failure_corpus.customer_totals": [ + "model.failure_corpus.stg_orders" + ], + "model.failure_corpus.stg_orders": [ + "source.failure_corpus.raw_orders" + ], + "source.failure_corpus.raw_orders": [] + }, + "group_map": {}, + "docs": {}, + "saved_queries": {}, + "semantic_models": {}, + "unit_tests": {} + } +} diff --git a/tests/goldens/compiler/failure/test-case-missing-parameter.json b/tests/goldens/compiler/failure/test-case-missing-parameter.json new file mode 100644 index 0000000000..36f1373446 --- /dev/null +++ b/tests/goldens/compiler/failure/test-case-missing-parameter.json @@ -0,0 +1,18 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "D003", + "message": "TEST() case 'placed' in '/tests/unit/test_order_cases.sql' is missing parameters: amount_value" + } + ] + } + ], + "compiled": {}, + "manifest": null +} diff --git a/tests/goldens/compiler/failure/test-expected-implicit-alias.json b/tests/goldens/compiler/failure/test-expected-implicit-alias.json new file mode 100644 index 0000000000..29a4c61e16 --- /dev/null +++ b/tests/goldens/compiler/failure/test-expected-implicit-alias.json @@ -0,0 +1,19 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "P001", + "message": "SQL test 'tests/unit/test_stg_orders.sql' must alias every non-trivial __expected__stg_orders projection", + "help": "name the column with an alias, for example amount IS NULL AS ; only a bare or qualified column reference may go unaliased" + } + ] + } + ], + "compiled": {}, + "manifest": null +} diff --git a/tests/goldens/compiler/failure/test-expected-parenthesised-branch.json b/tests/goldens/compiler/failure/test-expected-parenthesised-branch.json new file mode 100644 index 0000000000..a777cb689c --- /dev/null +++ b/tests/goldens/compiler/failure/test-expected-parenthesised-branch.json @@ -0,0 +1,18 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "P001", + "message": "SQL test 'tests/unit/test_stg_orders.sql' must write each __expected__stg_orders set-operation branch as a plain SELECT without enclosing parentheses" + } + ] + } + ], + "compiled": {}, + "manifest": null +} diff --git a/tests/goldens/compiler/failure/test-expected-reads-unknown-model.json b/tests/goldens/compiler/failure/test-expected-reads-unknown-model.json new file mode 100644 index 0000000000..fabb28be9c --- /dev/null +++ b/tests/goldens/compiler/failure/test-expected-reads-unknown-model.json @@ -0,0 +1,274 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "P013", + "message": "SQL test expected CTE '__expected__stg_orders' references unknown model 'archived_orders'", + "resource_type": "sql_test", + "resource_name": "test_stg_orders", + "path": "tests/unit/test_stg_orders.sql", + "line": 8, + "column": 24, + "location": { + "path": "tests/unit/test_stg_orders.sql", + "line": 8, + "column": 24, + "end_line": 8, + "end_column": 48 + }, + "help": "Read a model defined in the project, for example __ref(\"\"), or remove the reference from expected CTE '__expected__stg_orders'." + } + ] + } + ], + "compiled": { + "models/marts/customer_totals.sql": [ + "SELECT customer_id, SUM(amount) AS total_amount", + "FROM __ref(\"stg_orders\")", + "GROUP BY customer_id", + "" + ], + "models/staging/stg_orders.sql": [ + "SELECT order_id, customer_id, amount, status", + "FROM __source(\"raw_orders\")", + "" + ] + }, + "manifest": { + "nodes": { + "model.failure_corpus.customer_totals": { + "database": null, + "schema": "main", + "name": "customer_totals", + "resource_type": "model", + "package_name": "failure_corpus", + "path": "models/marts/customer_totals.sql", + "original_file_path": "models/marts/customer_totals.sql", + "unique_id": "model.failure_corpus.customer_totals", + "fqn": [ + "failure_corpus", + "models", + "marts", + "customer_totals" + ], + "alias": "customer_totals", + "config": { + "enabled": true, + "alias": null, + "schema": null, + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Order totals per customer", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.customer_totals", + "depends_on": { + "macros": [], + "nodes": [ + "model.failure_corpus.stg_orders" + ] + } + }, + "model.failure_corpus.stg_orders": { + "database": null, + "schema": "main", + "name": "stg_orders", + "resource_type": "model", + "package_name": "failure_corpus", + "path": "models/staging/stg_orders.sql", + "original_file_path": "models/staging/stg_orders.sql", + "unique_id": "model.failure_corpus.stg_orders", + "fqn": [ + "failure_corpus", + "models", + "staging", + "stg_orders" + ], + "alias": "stg_orders", + "config": { + "enabled": true, + "alias": null, + "schema": null, + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Staged orders", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.stg_orders", + "depends_on": { + "macros": [], + "nodes": [ + "source.failure_corpus.raw_orders" + ] + } + } + }, + "sources": { + "source.failure_corpus.raw_orders": { + "database": null, + "schema": "", + "name": "raw_orders", + "resource_type": "source", + "package_name": "failure_corpus", + "path": "sources/raw.yml", + "original_file_path": "sources/raw.yml", + "unique_id": "source.failure_corpus.raw_orders", + "fqn": [ + "failure_corpus", + "raw_orders" + ], + "source_name": "raw_orders", + "source_description": "", + "loader": "", + "identifier": "raw_orders", + "quoting": {}, + "loaded_at_field": null, + "freshness": null, + "external": null, + "description": "Orders feed.", + "columns": { + "order_id": { + "name": "order_id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "customer_id": { + "name": "customer_id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "amount": { + "name": "amount", + "description": "", + "meta": {}, + "data_type": "DOUBLE", + "constraints": [], + "quote": null, + "tags": [] + }, + "status": { + "name": "status", + "description": "", + "meta": {}, + "data_type": "VARCHAR", + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": {}, + "source_meta": {}, + "tags": [], + "config": {}, + "patch_path": null, + "unrendered_config": {}, + "relation_name": null + } + }, + "macros": {}, + "exposures": {}, + "metrics": {}, + "groups": {}, + "selectors": {}, + "disabled": {}, + "parent_map": { + "model.failure_corpus.customer_totals": [ + "model.failure_corpus.stg_orders" + ], + "model.failure_corpus.stg_orders": [ + "source.failure_corpus.raw_orders" + ], + "source.failure_corpus.raw_orders": [] + }, + "group_map": {}, + "docs": {}, + "saved_queries": {}, + "semantic_models": {}, + "unit_tests": {} + } +} diff --git a/tests/goldens/compiler/failure/test-expected-unknown-column.json b/tests/goldens/compiler/failure/test-expected-unknown-column.json new file mode 100644 index 0000000000..c1e0924657 --- /dev/null +++ b/tests/goldens/compiler/failure/test-expected-unknown-column.json @@ -0,0 +1,283 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "B302", + "message": "SQL test '__expected__stg_orders' names unknown column 'discount'", + "resource_type": "sql_test", + "resource_name": "test_stg_orders", + "path": "tests/unit/test_stg_orders.sql", + "line": 8, + "column": 30, + "location": { + "path": "tests/unit/test_stg_orders.sql", + "line": 8, + "column": 30, + "end_line": 8, + "end_column": 38 + }, + "help": "align the SQL-test fixture or expected columns with the tested resource's output" + } + ] + } + ], + "compiled": { + "models/marts/customer_totals.sql": [ + "SELECT customer_id, SUM(amount) AS total_amount", + "FROM __ref(\"stg_orders\")", + "GROUP BY customer_id", + "" + ], + "models/staging/stg_orders.sql": [ + "SELECT order_id, customer_id, amount, status", + "FROM __source(\"raw_orders\")", + "" + ], + "tests/stg_orders/test_stg_orders.sql": [ + "WITH __source__raw_orders AS (SELECT 1 AS order_id, 10 AS customer_id, CAST(5 AS DOUBLE) AS amount, 'placed' AS status),", + "__ref__stg_orders AS (SELECT order_id, customer_id, amount, status", + "FROM __source__raw_orders),", + "__actual__stg_orders AS (SELECT * FROM __ref__stg_orders),", + "__expected__stg_orders AS (SELECT 1 AS order_id, 2 AS discount)", + "SELECT 0 AS step_index, 'stg_orders' AS model_name, (SELECT COUNT(*) FROM __actual__stg_orders) AS actual_count, (SELECT COUNT(*) FROM __expected__stg_orders) AS expected_count, (SELECT COUNT(*) FROM (SELECT order_id, discount FROM __actual__stg_orders EXCEPT SELECT order_id, discount FROM __expected__stg_orders) AS __sqlbuild_mismatch) AS unexpected_count, (SELECT COUNT(*) FROM (SELECT order_id, discount FROM __expected__stg_orders EXCEPT SELECT order_id, discount FROM __actual__stg_orders) AS __sqlbuild_missing) AS missing_count", + "" + ] + }, + "manifest": { + "nodes": { + "model.failure_corpus.customer_totals": { + "database": null, + "schema": "main", + "name": "customer_totals", + "resource_type": "model", + "package_name": "failure_corpus", + "path": "models/marts/customer_totals.sql", + "original_file_path": "models/marts/customer_totals.sql", + "unique_id": "model.failure_corpus.customer_totals", + "fqn": [ + "failure_corpus", + "models", + "marts", + "customer_totals" + ], + "alias": "customer_totals", + "config": { + "enabled": true, + "alias": null, + "schema": null, + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Order totals per customer", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.customer_totals", + "depends_on": { + "macros": [], + "nodes": [ + "model.failure_corpus.stg_orders" + ] + } + }, + "model.failure_corpus.stg_orders": { + "database": null, + "schema": "main", + "name": "stg_orders", + "resource_type": "model", + "package_name": "failure_corpus", + "path": "models/staging/stg_orders.sql", + "original_file_path": "models/staging/stg_orders.sql", + "unique_id": "model.failure_corpus.stg_orders", + "fqn": [ + "failure_corpus", + "models", + "staging", + "stg_orders" + ], + "alias": "stg_orders", + "config": { + "enabled": true, + "alias": null, + "schema": null, + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Staged orders", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.stg_orders", + "depends_on": { + "macros": [], + "nodes": [ + "source.failure_corpus.raw_orders" + ] + } + } + }, + "sources": { + "source.failure_corpus.raw_orders": { + "database": null, + "schema": "", + "name": "raw_orders", + "resource_type": "source", + "package_name": "failure_corpus", + "path": "sources/raw.yml", + "original_file_path": "sources/raw.yml", + "unique_id": "source.failure_corpus.raw_orders", + "fqn": [ + "failure_corpus", + "raw_orders" + ], + "source_name": "raw_orders", + "source_description": "", + "loader": "", + "identifier": "raw_orders", + "quoting": {}, + "loaded_at_field": null, + "freshness": null, + "external": null, + "description": "Orders feed.", + "columns": { + "order_id": { + "name": "order_id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "customer_id": { + "name": "customer_id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "amount": { + "name": "amount", + "description": "", + "meta": {}, + "data_type": "DOUBLE", + "constraints": [], + "quote": null, + "tags": [] + }, + "status": { + "name": "status", + "description": "", + "meta": {}, + "data_type": "VARCHAR", + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": {}, + "source_meta": {}, + "tags": [], + "config": {}, + "patch_path": null, + "unrendered_config": {}, + "relation_name": null + } + }, + "macros": {}, + "exposures": {}, + "metrics": {}, + "groups": {}, + "selectors": {}, + "disabled": {}, + "parent_map": { + "model.failure_corpus.customer_totals": [ + "model.failure_corpus.stg_orders" + ], + "model.failure_corpus.stg_orders": [ + "source.failure_corpus.raw_orders" + ], + "source.failure_corpus.raw_orders": [] + }, + "group_map": {}, + "docs": {}, + "saved_queries": {}, + "semantic_models": {}, + "unit_tests": {} + } +} diff --git a/tests/goldens/compiler/failure/test-expects-unknown-model.json b/tests/goldens/compiler/failure/test-expects-unknown-model.json new file mode 100644 index 0000000000..b39f388d2b --- /dev/null +++ b/tests/goldens/compiler/failure/test-expects-unknown-model.json @@ -0,0 +1,18 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "P001", + "message": "SQL test file tests/unit/test_stg_payments.sql expects unknown model 'stg_payments'" + } + ] + } + ], + "compiled": {}, + "manifest": null +} diff --git a/tests/goldens/compiler/failure/test-helper-reads-unknown-model.json b/tests/goldens/compiler/failure/test-helper-reads-unknown-model.json new file mode 100644 index 0000000000..be0c3d62df --- /dev/null +++ b/tests/goldens/compiler/failure/test-helper-reads-unknown-model.json @@ -0,0 +1,274 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "P013", + "message": "SQL test helper CTE 'archived_orders' references unknown model 'archived_orders'", + "resource_type": "sql_test", + "resource_name": "test_stg_orders", + "path": "tests/unit/test_stg_orders.sql", + "line": 8, + "column": 24, + "location": { + "path": "tests/unit/test_stg_orders.sql", + "line": 8, + "column": 24, + "end_line": 8, + "end_column": 48 + }, + "help": "Read a model defined in the project, for example __ref(\"\"), or remove the reference from helper CTE 'archived_orders'." + } + ] + } + ], + "compiled": { + "models/marts/customer_totals.sql": [ + "SELECT customer_id, SUM(amount) AS total_amount", + "FROM __ref(\"stg_orders\")", + "GROUP BY customer_id", + "" + ], + "models/staging/stg_orders.sql": [ + "SELECT order_id, customer_id, amount, status", + "FROM __source(\"raw_orders\")", + "" + ] + }, + "manifest": { + "nodes": { + "model.failure_corpus.customer_totals": { + "database": null, + "schema": "main", + "name": "customer_totals", + "resource_type": "model", + "package_name": "failure_corpus", + "path": "models/marts/customer_totals.sql", + "original_file_path": "models/marts/customer_totals.sql", + "unique_id": "model.failure_corpus.customer_totals", + "fqn": [ + "failure_corpus", + "models", + "marts", + "customer_totals" + ], + "alias": "customer_totals", + "config": { + "enabled": true, + "alias": null, + "schema": null, + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Order totals per customer", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.customer_totals", + "depends_on": { + "macros": [], + "nodes": [ + "model.failure_corpus.stg_orders" + ] + } + }, + "model.failure_corpus.stg_orders": { + "database": null, + "schema": "main", + "name": "stg_orders", + "resource_type": "model", + "package_name": "failure_corpus", + "path": "models/staging/stg_orders.sql", + "original_file_path": "models/staging/stg_orders.sql", + "unique_id": "model.failure_corpus.stg_orders", + "fqn": [ + "failure_corpus", + "models", + "staging", + "stg_orders" + ], + "alias": "stg_orders", + "config": { + "enabled": true, + "alias": null, + "schema": null, + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Staged orders", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.stg_orders", + "depends_on": { + "macros": [], + "nodes": [ + "source.failure_corpus.raw_orders" + ] + } + } + }, + "sources": { + "source.failure_corpus.raw_orders": { + "database": null, + "schema": "", + "name": "raw_orders", + "resource_type": "source", + "package_name": "failure_corpus", + "path": "sources/raw.yml", + "original_file_path": "sources/raw.yml", + "unique_id": "source.failure_corpus.raw_orders", + "fqn": [ + "failure_corpus", + "raw_orders" + ], + "source_name": "raw_orders", + "source_description": "", + "loader": "", + "identifier": "raw_orders", + "quoting": {}, + "loaded_at_field": null, + "freshness": null, + "external": null, + "description": "Orders feed.", + "columns": { + "order_id": { + "name": "order_id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "customer_id": { + "name": "customer_id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "amount": { + "name": "amount", + "description": "", + "meta": {}, + "data_type": "DOUBLE", + "constraints": [], + "quote": null, + "tags": [] + }, + "status": { + "name": "status", + "description": "", + "meta": {}, + "data_type": "VARCHAR", + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": {}, + "source_meta": {}, + "tags": [], + "config": {}, + "patch_path": null, + "unrendered_config": {}, + "relation_name": null + } + }, + "macros": {}, + "exposures": {}, + "metrics": {}, + "groups": {}, + "selectors": {}, + "disabled": {}, + "parent_map": { + "model.failure_corpus.customer_totals": [ + "model.failure_corpus.stg_orders" + ], + "model.failure_corpus.stg_orders": [ + "source.failure_corpus.raw_orders" + ], + "source.failure_corpus.raw_orders": [] + }, + "group_map": {}, + "docs": {}, + "saved_queries": {}, + "semantic_models": {}, + "unit_tests": {} + } +} diff --git a/tests/goldens/compiler/failure/test-mocks-unknown-source.json b/tests/goldens/compiler/failure/test-mocks-unknown-source.json new file mode 100644 index 0000000000..fb0c37c930 --- /dev/null +++ b/tests/goldens/compiler/failure/test-mocks-unknown-source.json @@ -0,0 +1,18 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "P001", + "message": "SQL test file tests/unit/test_stg_orders.sql mocks unknown source 'raw_payments'" + } + ] + } + ], + "compiled": {}, + "manifest": null +} diff --git a/tests/goldens/compiler/failure/test-parse.json b/tests/goldens/compiler/failure/test-parse.json new file mode 100644 index 0000000000..868397b0a6 --- /dev/null +++ b/tests/goldens/compiler/failure/test-parse.json @@ -0,0 +1,18 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "D003", + "message": "SQL test '/tests/unit/test_stg_orders.sql' must start with a TEST() header as the first non-whitespace content" + } + ] + } + ], + "compiled": {}, + "manifest": null +} diff --git a/tests/goldens/compiler/failure/test-quoted-cte-name.json b/tests/goldens/compiler/failure/test-quoted-cte-name.json new file mode 100644 index 0000000000..0fa006a626 --- /dev/null +++ b/tests/goldens/compiler/failure/test-quoted-cte-name.json @@ -0,0 +1,19 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "P001", + "message": "SQL test 'tests/unit/test_stg_orders.sql' CTE name \"recent orders\" must be an unquoted identifier of ASCII letters, digits and underscores\n --> tests/unit/test_stg_orders.sql:7:1", + "help": "rename the CTE, for example recent_orders; quoted CTE names and names with $ or non-ASCII characters are not supported" + } + ] + } + ], + "compiled": {}, + "manifest": null +} diff --git a/tests/goldens/compiler/failure/test-reads-only-mocks.json b/tests/goldens/compiler/failure/test-reads-only-mocks.json new file mode 100644 index 0000000000..d91fc257f0 --- /dev/null +++ b/tests/goldens/compiler/failure/test-reads-only-mocks.json @@ -0,0 +1,274 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "P013", + "message": "SQL test 'test_stg_orders' mocks the model it tests (__ref__stg_orders), so the test has no model to run against", + "resource_type": "sql_test", + "resource_name": "test_stg_orders", + "path": "tests/unit/test_stg_orders.sql", + "line": 8, + "column": 24, + "location": { + "path": "tests/unit/test_stg_orders.sql", + "line": 8, + "column": 24, + "end_line": 8, + "end_column": 43 + }, + "help": "Mock the model's inputs instead (for example __source__ or __ref__) and keep __ref(\"stg_orders\") in the __assert__ or __expected__ CTE, or remove the test." + } + ] + } + ], + "compiled": { + "models/marts/customer_totals.sql": [ + "SELECT customer_id, SUM(amount) AS total_amount", + "FROM __ref(\"stg_orders\")", + "GROUP BY customer_id", + "" + ], + "models/staging/stg_orders.sql": [ + "SELECT order_id, customer_id, amount, status", + "FROM __source(\"raw_orders\")", + "" + ] + }, + "manifest": { + "nodes": { + "model.failure_corpus.customer_totals": { + "database": null, + "schema": "main", + "name": "customer_totals", + "resource_type": "model", + "package_name": "failure_corpus", + "path": "models/marts/customer_totals.sql", + "original_file_path": "models/marts/customer_totals.sql", + "unique_id": "model.failure_corpus.customer_totals", + "fqn": [ + "failure_corpus", + "models", + "marts", + "customer_totals" + ], + "alias": "customer_totals", + "config": { + "enabled": true, + "alias": null, + "schema": null, + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Order totals per customer", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.customer_totals", + "depends_on": { + "macros": [], + "nodes": [ + "model.failure_corpus.stg_orders" + ] + } + }, + "model.failure_corpus.stg_orders": { + "database": null, + "schema": "main", + "name": "stg_orders", + "resource_type": "model", + "package_name": "failure_corpus", + "path": "models/staging/stg_orders.sql", + "original_file_path": "models/staging/stg_orders.sql", + "unique_id": "model.failure_corpus.stg_orders", + "fqn": [ + "failure_corpus", + "models", + "staging", + "stg_orders" + ], + "alias": "stg_orders", + "config": { + "enabled": true, + "alias": null, + "schema": null, + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Staged orders", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.stg_orders", + "depends_on": { + "macros": [], + "nodes": [ + "source.failure_corpus.raw_orders" + ] + } + } + }, + "sources": { + "source.failure_corpus.raw_orders": { + "database": null, + "schema": "", + "name": "raw_orders", + "resource_type": "source", + "package_name": "failure_corpus", + "path": "sources/raw.yml", + "original_file_path": "sources/raw.yml", + "unique_id": "source.failure_corpus.raw_orders", + "fqn": [ + "failure_corpus", + "raw_orders" + ], + "source_name": "raw_orders", + "source_description": "", + "loader": "", + "identifier": "raw_orders", + "quoting": {}, + "loaded_at_field": null, + "freshness": null, + "external": null, + "description": "Orders feed.", + "columns": { + "order_id": { + "name": "order_id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "customer_id": { + "name": "customer_id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "amount": { + "name": "amount", + "description": "", + "meta": {}, + "data_type": "DOUBLE", + "constraints": [], + "quote": null, + "tags": [] + }, + "status": { + "name": "status", + "description": "", + "meta": {}, + "data_type": "VARCHAR", + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": {}, + "source_meta": {}, + "tags": [], + "config": {}, + "patch_path": null, + "unrendered_config": {}, + "relation_name": null + } + }, + "macros": {}, + "exposures": {}, + "metrics": {}, + "groups": {}, + "selectors": {}, + "disabled": {}, + "parent_map": { + "model.failure_corpus.customer_totals": [ + "model.failure_corpus.stg_orders" + ], + "model.failure_corpus.stg_orders": [ + "source.failure_corpus.raw_orders" + ], + "source.failure_corpus.raw_orders": [] + }, + "group_map": {}, + "docs": {}, + "saved_queries": {}, + "semantic_models": {}, + "unit_tests": {} + } +} diff --git a/tests/goldens/compiler/failure/unclosed-dollar-quote-with-project-variable.json b/tests/goldens/compiler/failure/unclosed-dollar-quote-with-project-variable.json new file mode 100644 index 0000000000..a9e5ec93cd --- /dev/null +++ b/tests/goldens/compiler/failure/unclosed-dollar-quote-with-project-variable.json @@ -0,0 +1,18 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "P001", + "message": "SQL interpolation contains an unclosed quoted string" + } + ] + } + ], + "compiled": {}, + "manifest": null +} diff --git a/tests/goldens/compiler/failure/ungrouped-column.json b/tests/goldens/compiler/failure/ungrouped-column.json new file mode 100644 index 0000000000..7c623a89c5 --- /dev/null +++ b/tests/goldens/compiler/failure/ungrouped-column.json @@ -0,0 +1,274 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "B230", + "message": "Column must be grouped or used in an aggregate function", + "resource_type": "model", + "resource_name": "customer_totals", + "path": "models/marts/customer_totals.sql", + "line": 5, + "column": 21, + "location": { + "path": "models/marts/customer_totals.sql", + "line": 5, + "column": 21, + "end_line": 5, + "end_column": 27 + }, + "help": "add this expression to GROUP BY or use it inside an aggregate" + } + ] + } + ], + "compiled": { + "models/marts/customer_totals.sql": [ + "SELECT customer_id, status, SUM(amount) AS total_amount", + "FROM __ref(\"stg_orders\")", + "GROUP BY customer_id", + "" + ], + "models/staging/stg_orders.sql": [ + "SELECT order_id, customer_id, amount, status", + "FROM __source(\"raw_orders\")", + "" + ] + }, + "manifest": { + "nodes": { + "model.failure_corpus.customer_totals": { + "database": null, + "schema": "main", + "name": "customer_totals", + "resource_type": "model", + "package_name": "failure_corpus", + "path": "models/marts/customer_totals.sql", + "original_file_path": "models/marts/customer_totals.sql", + "unique_id": "model.failure_corpus.customer_totals", + "fqn": [ + "failure_corpus", + "models", + "marts", + "customer_totals" + ], + "alias": "customer_totals", + "config": { + "enabled": true, + "alias": null, + "schema": null, + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Order totals per customer", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.customer_totals", + "depends_on": { + "macros": [], + "nodes": [ + "model.failure_corpus.stg_orders" + ] + } + }, + "model.failure_corpus.stg_orders": { + "database": null, + "schema": "main", + "name": "stg_orders", + "resource_type": "model", + "package_name": "failure_corpus", + "path": "models/staging/stg_orders.sql", + "original_file_path": "models/staging/stg_orders.sql", + "unique_id": "model.failure_corpus.stg_orders", + "fqn": [ + "failure_corpus", + "models", + "staging", + "stg_orders" + ], + "alias": "stg_orders", + "config": { + "enabled": true, + "alias": null, + "schema": null, + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Staged orders", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.stg_orders", + "depends_on": { + "macros": [], + "nodes": [ + "source.failure_corpus.raw_orders" + ] + } + } + }, + "sources": { + "source.failure_corpus.raw_orders": { + "database": null, + "schema": "", + "name": "raw_orders", + "resource_type": "source", + "package_name": "failure_corpus", + "path": "sources/raw.yml", + "original_file_path": "sources/raw.yml", + "unique_id": "source.failure_corpus.raw_orders", + "fqn": [ + "failure_corpus", + "raw_orders" + ], + "source_name": "raw_orders", + "source_description": "", + "loader": "", + "identifier": "raw_orders", + "quoting": {}, + "loaded_at_field": null, + "freshness": null, + "external": null, + "description": "Orders feed.", + "columns": { + "order_id": { + "name": "order_id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "customer_id": { + "name": "customer_id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "amount": { + "name": "amount", + "description": "", + "meta": {}, + "data_type": "DOUBLE", + "constraints": [], + "quote": null, + "tags": [] + }, + "status": { + "name": "status", + "description": "", + "meta": {}, + "data_type": "VARCHAR", + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": {}, + "source_meta": {}, + "tags": [], + "config": {}, + "patch_path": null, + "unrendered_config": {}, + "relation_name": null + } + }, + "macros": {}, + "exposures": {}, + "metrics": {}, + "groups": {}, + "selectors": {}, + "disabled": {}, + "parent_map": { + "model.failure_corpus.customer_totals": [ + "model.failure_corpus.stg_orders" + ], + "model.failure_corpus.stg_orders": [ + "source.failure_corpus.raw_orders" + ], + "source.failure_corpus.raw_orders": [] + }, + "group_map": {}, + "docs": {}, + "saved_queries": {}, + "semantic_models": {}, + "unit_tests": {} + } +} diff --git a/tests/goldens/compiler/failure/unknown-column.json b/tests/goldens/compiler/failure/unknown-column.json new file mode 100644 index 0000000000..3a592e9c09 --- /dev/null +++ b/tests/goldens/compiler/failure/unknown-column.json @@ -0,0 +1,276 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "B002", + "message": "Unknown column 'discount' in stg_orders", + "resource_type": "model", + "resource_name": "customer_totals", + "path": "models/marts/customer_totals.sql", + "line": 5, + "column": 25, + "location": { + "path": "models/marts/customer_totals.sql", + "line": 5, + "column": 25, + "end_line": 5, + "end_column": 33 + }, + "notes": [ + "stg_orders has: amount, customer_id, status, order_id" + ] + } + ] + } + ], + "compiled": { + "models/marts/customer_totals.sql": [ + "SELECT customer_id, SUM(discount) AS total_amount", + "FROM __ref(\"stg_orders\")", + "GROUP BY customer_id", + "" + ], + "models/staging/stg_orders.sql": [ + "SELECT order_id, customer_id, amount, status", + "FROM __source(\"raw_orders\")", + "" + ] + }, + "manifest": { + "nodes": { + "model.failure_corpus.customer_totals": { + "database": null, + "schema": "main", + "name": "customer_totals", + "resource_type": "model", + "package_name": "failure_corpus", + "path": "models/marts/customer_totals.sql", + "original_file_path": "models/marts/customer_totals.sql", + "unique_id": "model.failure_corpus.customer_totals", + "fqn": [ + "failure_corpus", + "models", + "marts", + "customer_totals" + ], + "alias": "customer_totals", + "config": { + "enabled": true, + "alias": null, + "schema": null, + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Order totals per customer", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.customer_totals", + "depends_on": { + "macros": [], + "nodes": [ + "model.failure_corpus.stg_orders" + ] + } + }, + "model.failure_corpus.stg_orders": { + "database": null, + "schema": "main", + "name": "stg_orders", + "resource_type": "model", + "package_name": "failure_corpus", + "path": "models/staging/stg_orders.sql", + "original_file_path": "models/staging/stg_orders.sql", + "unique_id": "model.failure_corpus.stg_orders", + "fqn": [ + "failure_corpus", + "models", + "staging", + "stg_orders" + ], + "alias": "stg_orders", + "config": { + "enabled": true, + "alias": null, + "schema": null, + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Staged orders", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.stg_orders", + "depends_on": { + "macros": [], + "nodes": [ + "source.failure_corpus.raw_orders" + ] + } + } + }, + "sources": { + "source.failure_corpus.raw_orders": { + "database": null, + "schema": "", + "name": "raw_orders", + "resource_type": "source", + "package_name": "failure_corpus", + "path": "sources/raw.yml", + "original_file_path": "sources/raw.yml", + "unique_id": "source.failure_corpus.raw_orders", + "fqn": [ + "failure_corpus", + "raw_orders" + ], + "source_name": "raw_orders", + "source_description": "", + "loader": "", + "identifier": "raw_orders", + "quoting": {}, + "loaded_at_field": null, + "freshness": null, + "external": null, + "description": "Orders feed.", + "columns": { + "order_id": { + "name": "order_id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "customer_id": { + "name": "customer_id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "amount": { + "name": "amount", + "description": "", + "meta": {}, + "data_type": "DOUBLE", + "constraints": [], + "quote": null, + "tags": [] + }, + "status": { + "name": "status", + "description": "", + "meta": {}, + "data_type": "VARCHAR", + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": {}, + "source_meta": {}, + "tags": [], + "config": {}, + "patch_path": null, + "unrendered_config": {}, + "relation_name": null + } + }, + "macros": {}, + "exposures": {}, + "metrics": {}, + "groups": {}, + "selectors": {}, + "disabled": {}, + "parent_map": { + "model.failure_corpus.customer_totals": [ + "model.failure_corpus.stg_orders" + ], + "model.failure_corpus.stg_orders": [ + "source.failure_corpus.raw_orders" + ], + "source.failure_corpus.raw_orders": [] + }, + "group_map": {}, + "docs": {}, + "saved_queries": {}, + "semantic_models": {}, + "unit_tests": {} + } +} diff --git a/tests/goldens/compiler/failure/unknown-constant.json b/tests/goldens/compiler/failure/unknown-constant.json new file mode 100644 index 0000000000..5c030968f4 --- /dev/null +++ b/tests/goldens/compiler/failure/unknown-constant.json @@ -0,0 +1,18 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "P001", + "message": "Unknown constant 'minimum_amount' in '/models/staging/stg_orders.sql'. Visible constants: none" + } + ] + } + ], + "compiled": {}, + "manifest": null +} diff --git a/tests/goldens/compiler/failure/unknown-enum-member.json b/tests/goldens/compiler/failure/unknown-enum-member.json new file mode 100644 index 0000000000..893223e5ee --- /dev/null +++ b/tests/goldens/compiler/failure/unknown-enum-member.json @@ -0,0 +1,18 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "P001", + "message": "Unknown member 'RETURNED' for enum 'order_status' in '/models/staging/stg_orders.sql'. Available members: PLACED, SHIPPED" + } + ] + } + ], + "compiled": {}, + "manifest": null +} diff --git a/tests/goldens/compiler/failure/unknown-macro.json b/tests/goldens/compiler/failure/unknown-macro.json new file mode 100644 index 0000000000..adbe60f253 --- /dev/null +++ b/tests/goldens/compiler/failure/unknown-macro.json @@ -0,0 +1,18 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "P001", + "message": "Unknown macro '@cents' in '/models/staging/stg_orders.sql'. Available macros: none" + } + ] + } + ], + "compiled": {}, + "manifest": null +} diff --git a/tests/goldens/compiler/failure/unknown-ref.json b/tests/goldens/compiler/failure/unknown-ref.json new file mode 100644 index 0000000000..7ae331d589 --- /dev/null +++ b/tests/goldens/compiler/failure/unknown-ref.json @@ -0,0 +1,18 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "P001", + "message": "Model file models/marts/customer_totals.sql references unknown model 'stg_payments'" + } + ] + } + ], + "compiled": {}, + "manifest": null +} diff --git a/tests/goldens/compiler/failure/unknown-scalar-function.json b/tests/goldens/compiler/failure/unknown-scalar-function.json new file mode 100644 index 0000000000..1a6bb7ca94 --- /dev/null +++ b/tests/goldens/compiler/failure/unknown-scalar-function.json @@ -0,0 +1,18 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "P001", + "message": "Model file models/staging/stg_orders.sql references unknown SQL function 'is_large'" + } + ] + } + ], + "compiled": {}, + "manifest": null +} diff --git a/tests/goldens/compiler/failure/unknown-source.json b/tests/goldens/compiler/failure/unknown-source.json new file mode 100644 index 0000000000..b62cf8f45d --- /dev/null +++ b/tests/goldens/compiler/failure/unknown-source.json @@ -0,0 +1,18 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "P001", + "message": "Model file models/staging/stg_orders.sql references unknown source 'raw_payments'" + } + ] + } + ], + "compiled": {}, + "manifest": null +} diff --git a/tests/goldens/compiler/failure/unknown-table-function.json b/tests/goldens/compiler/failure/unknown-table-function.json new file mode 100644 index 0000000000..73f39c7925 --- /dev/null +++ b/tests/goldens/compiler/failure/unknown-table-function.json @@ -0,0 +1,18 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "P001", + "message": "Model file models/marts/customer_totals.sql references unknown table function 'table_fn__missing'" + } + ] + } + ], + "compiled": {}, + "manifest": null +} diff --git a/tests/goldens/compiler/failure/unneeded-sql-analysis-opt-out.json b/tests/goldens/compiler/failure/unneeded-sql-analysis-opt-out.json new file mode 100644 index 0000000000..623f4193a5 --- /dev/null +++ b/tests/goldens/compiler/failure/unneeded-sql-analysis-opt-out.json @@ -0,0 +1,275 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "P009", + "message": "`sql_analysis false` is not needed for model 'stg_orders'", + "resource_type": "model", + "resource_name": "stg_orders", + "path": "models/staging/stg_orders.sql", + "line": 3, + "column": 3, + "location": { + "path": "models/staging/stg_orders.sql", + "line": 3, + "column": 3 + }, + "help": "remove `sql_analysis false`; it is not hiding any findings\n = help: to allow `sql_analysis false` on any model, SQL test or audit, set this in sqlbuild_project.toml:\n [settings]\n require_sql_analysis = false", + "notes": [ + "sqlbuild_project.toml sets [settings] require_sql_analysis = true, which only allows `sql_analysis false` on SQL that SQLBuild cannot parse; this model parses successfully." + ] + } + ] + } + ], + "compiled": { + "models/marts/customer_totals.sql": [ + "SELECT customer_id, SUM(amount) AS total_amount", + "FROM __ref(\"stg_orders\")", + "GROUP BY customer_id", + "" + ], + "models/staging/stg_orders.sql": [ + "SELECT order_id, customer_id, amount, status", + "FROM __source(\"raw_orders\")", + "" + ] + }, + "manifest": { + "nodes": { + "model.failure_corpus.customer_totals": { + "database": null, + "schema": "main", + "name": "customer_totals", + "resource_type": "model", + "package_name": "failure_corpus", + "path": "models/marts/customer_totals.sql", + "original_file_path": "models/marts/customer_totals.sql", + "unique_id": "model.failure_corpus.customer_totals", + "fqn": [ + "failure_corpus", + "models", + "marts", + "customer_totals" + ], + "alias": "customer_totals", + "config": { + "enabled": true, + "alias": null, + "schema": null, + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Order totals per customer", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.customer_totals", + "depends_on": { + "macros": [], + "nodes": [ + "model.failure_corpus.stg_orders" + ] + } + }, + "model.failure_corpus.stg_orders": { + "database": null, + "schema": "main", + "name": "stg_orders", + "resource_type": "model", + "package_name": "failure_corpus", + "path": "models/staging/stg_orders.sql", + "original_file_path": "models/staging/stg_orders.sql", + "unique_id": "model.failure_corpus.stg_orders", + "fqn": [ + "failure_corpus", + "models", + "staging", + "stg_orders" + ], + "alias": "stg_orders", + "config": { + "enabled": true, + "alias": null, + "schema": null, + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Staged orders", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.stg_orders", + "depends_on": { + "macros": [], + "nodes": [ + "source.failure_corpus.raw_orders" + ] + } + } + }, + "sources": { + "source.failure_corpus.raw_orders": { + "database": null, + "schema": "", + "name": "raw_orders", + "resource_type": "source", + "package_name": "failure_corpus", + "path": "sources/raw.yml", + "original_file_path": "sources/raw.yml", + "unique_id": "source.failure_corpus.raw_orders", + "fqn": [ + "failure_corpus", + "raw_orders" + ], + "source_name": "raw_orders", + "source_description": "", + "loader": "", + "identifier": "raw_orders", + "quoting": {}, + "loaded_at_field": null, + "freshness": null, + "external": null, + "description": "Orders feed.", + "columns": { + "order_id": { + "name": "order_id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "customer_id": { + "name": "customer_id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "amount": { + "name": "amount", + "description": "", + "meta": {}, + "data_type": "DOUBLE", + "constraints": [], + "quote": null, + "tags": [] + }, + "status": { + "name": "status", + "description": "", + "meta": {}, + "data_type": "VARCHAR", + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": {}, + "source_meta": {}, + "tags": [], + "config": {}, + "patch_path": null, + "unrendered_config": {}, + "relation_name": null + } + }, + "macros": {}, + "exposures": {}, + "metrics": {}, + "groups": {}, + "selectors": {}, + "disabled": {}, + "parent_map": { + "model.failure_corpus.customer_totals": [ + "model.failure_corpus.stg_orders" + ], + "model.failure_corpus.stg_orders": [ + "source.failure_corpus.raw_orders" + ], + "source.failure_corpus.raw_orders": [] + }, + "group_map": {}, + "docs": {}, + "saved_queries": {}, + "semantic_models": {}, + "unit_tests": {} + } +} diff --git a/tests/goldens/compiler/failure/unquoted-ref-name.json b/tests/goldens/compiler/failure/unquoted-ref-name.json new file mode 100644 index 0000000000..11c1f3b0c7 --- /dev/null +++ b/tests/goldens/compiler/failure/unquoted-ref-name.json @@ -0,0 +1,270 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "P012", + "message": "__ref(stg_orders) is not a valid __ref() call", + "resource_type": "model", + "resource_name": "customer_totals", + "path": "models/marts/customer_totals.sql", + "line": 6, + "column": 6, + "location": { + "path": "models/marts/customer_totals.sql", + "line": 6, + "column": 6, + "end_line": 6, + "end_column": 23 + }, + "help": "__ref() takes exactly one double-quoted name, with no comments or extra spaces inside the parentheses: __ref(\"stg_orders\")" + } + ] + } + ], + "compiled": { + "models/marts/customer_totals.sql": [ + "SELECT customer_id, SUM(amount) AS total_amount", + "FROM __ref(stg_orders)", + "GROUP BY customer_id", + "" + ], + "models/staging/stg_orders.sql": [ + "SELECT order_id, customer_id, amount, status", + "FROM __source(\"raw_orders\")", + "" + ] + }, + "manifest": { + "nodes": { + "model.failure_corpus.customer_totals": { + "database": null, + "schema": "main", + "name": "customer_totals", + "resource_type": "model", + "package_name": "failure_corpus", + "path": "models/marts/customer_totals.sql", + "original_file_path": "models/marts/customer_totals.sql", + "unique_id": "model.failure_corpus.customer_totals", + "fqn": [ + "failure_corpus", + "models", + "marts", + "customer_totals" + ], + "alias": "customer_totals", + "config": { + "enabled": true, + "alias": null, + "schema": null, + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Order totals per customer", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.customer_totals", + "depends_on": { + "macros": [], + "nodes": [] + } + }, + "model.failure_corpus.stg_orders": { + "database": null, + "schema": "main", + "name": "stg_orders", + "resource_type": "model", + "package_name": "failure_corpus", + "path": "models/staging/stg_orders.sql", + "original_file_path": "models/staging/stg_orders.sql", + "unique_id": "model.failure_corpus.stg_orders", + "fqn": [ + "failure_corpus", + "models", + "staging", + "stg_orders" + ], + "alias": "stg_orders", + "config": { + "enabled": true, + "alias": null, + "schema": null, + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Staged orders", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.stg_orders", + "depends_on": { + "macros": [], + "nodes": [ + "source.failure_corpus.raw_orders" + ] + } + } + }, + "sources": { + "source.failure_corpus.raw_orders": { + "database": null, + "schema": "", + "name": "raw_orders", + "resource_type": "source", + "package_name": "failure_corpus", + "path": "sources/raw.yml", + "original_file_path": "sources/raw.yml", + "unique_id": "source.failure_corpus.raw_orders", + "fqn": [ + "failure_corpus", + "raw_orders" + ], + "source_name": "raw_orders", + "source_description": "", + "loader": "", + "identifier": "raw_orders", + "quoting": {}, + "loaded_at_field": null, + "freshness": null, + "external": null, + "description": "Orders feed.", + "columns": { + "order_id": { + "name": "order_id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "customer_id": { + "name": "customer_id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "amount": { + "name": "amount", + "description": "", + "meta": {}, + "data_type": "DOUBLE", + "constraints": [], + "quote": null, + "tags": [] + }, + "status": { + "name": "status", + "description": "", + "meta": {}, + "data_type": "VARCHAR", + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": {}, + "source_meta": {}, + "tags": [], + "config": {}, + "patch_path": null, + "unrendered_config": {}, + "relation_name": null + } + }, + "macros": {}, + "exposures": {}, + "metrics": {}, + "groups": {}, + "selectors": {}, + "disabled": {}, + "parent_map": { + "model.failure_corpus.customer_totals": [], + "model.failure_corpus.stg_orders": [ + "source.failure_corpus.raw_orders" + ], + "source.failure_corpus.raw_orders": [] + }, + "group_map": {}, + "docs": {}, + "saved_queries": {}, + "semantic_models": {}, + "unit_tests": {} + } +} diff --git a/tests/goldens/compiler/failure/unset-environment-variable.json b/tests/goldens/compiler/failure/unset-environment-variable.json new file mode 100644 index 0000000000..330dd64b66 --- /dev/null +++ b/tests/goldens/compiler/failure/unset-environment-variable.json @@ -0,0 +1,18 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "P001", + "message": "unknown environment variable '@@ENV:SQB_DIFFERENTIAL_UNSET' in '/models/staging/stg_orders.sql'" + } + ] + } + ], + "compiled": {}, + "manifest": null +} diff --git a/tests/goldens/compiler/failure/unused-declaration.json b/tests/goldens/compiler/failure/unused-declaration.json new file mode 100644 index 0000000000..8daa69c5ed --- /dev/null +++ b/tests/goldens/compiler/failure/unused-declaration.json @@ -0,0 +1,269 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "S010", + "message": "Unused local declaration 'constant:spare' at models/staging/_sqlbuild/_constants/limits.sql; remove it or add a genuine runtime use", + "path": "models/staging/_sqlbuild/_constants/limits.sql", + "line": 1, + "column": 1, + "location": { + "path": "models/staging/_sqlbuild/_constants/limits.sql", + "line": 1, + "column": 1 + } + } + ] + } + ], + "compiled": { + "models/marts/customer_totals.sql": [ + "SELECT customer_id, SUM(amount) AS total_amount", + "FROM __ref(\"stg_orders\")", + "GROUP BY customer_id", + "" + ], + "models/staging/stg_orders.sql": [ + "SELECT order_id, customer_id, amount, status", + "FROM __source(\"raw_orders\")", + "" + ] + }, + "manifest": { + "nodes": { + "model.failure_corpus.customer_totals": { + "database": null, + "schema": "main", + "name": "customer_totals", + "resource_type": "model", + "package_name": "failure_corpus", + "path": "models/marts/customer_totals.sql", + "original_file_path": "models/marts/customer_totals.sql", + "unique_id": "model.failure_corpus.customer_totals", + "fqn": [ + "failure_corpus", + "models", + "marts", + "customer_totals" + ], + "alias": "customer_totals", + "config": { + "enabled": true, + "alias": null, + "schema": null, + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Order totals per customer", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.customer_totals", + "depends_on": { + "macros": [], + "nodes": [ + "model.failure_corpus.stg_orders" + ] + } + }, + "model.failure_corpus.stg_orders": { + "database": null, + "schema": "main", + "name": "stg_orders", + "resource_type": "model", + "package_name": "failure_corpus", + "path": "models/staging/stg_orders.sql", + "original_file_path": "models/staging/stg_orders.sql", + "unique_id": "model.failure_corpus.stg_orders", + "fqn": [ + "failure_corpus", + "models", + "staging", + "stg_orders" + ], + "alias": "stg_orders", + "config": { + "enabled": true, + "alias": null, + "schema": null, + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Staged orders", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.stg_orders", + "depends_on": { + "macros": [], + "nodes": [ + "source.failure_corpus.raw_orders" + ] + } + } + }, + "sources": { + "source.failure_corpus.raw_orders": { + "database": null, + "schema": "", + "name": "raw_orders", + "resource_type": "source", + "package_name": "failure_corpus", + "path": "sources/raw.yml", + "original_file_path": "sources/raw.yml", + "unique_id": "source.failure_corpus.raw_orders", + "fqn": [ + "failure_corpus", + "raw_orders" + ], + "source_name": "raw_orders", + "source_description": "", + "loader": "", + "identifier": "raw_orders", + "quoting": {}, + "loaded_at_field": null, + "freshness": null, + "external": null, + "description": "Orders feed.", + "columns": { + "order_id": { + "name": "order_id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "customer_id": { + "name": "customer_id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "amount": { + "name": "amount", + "description": "", + "meta": {}, + "data_type": "DOUBLE", + "constraints": [], + "quote": null, + "tags": [] + }, + "status": { + "name": "status", + "description": "", + "meta": {}, + "data_type": "VARCHAR", + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": {}, + "source_meta": {}, + "tags": [], + "config": {}, + "patch_path": null, + "unrendered_config": {}, + "relation_name": null + } + }, + "macros": {}, + "exposures": {}, + "metrics": {}, + "groups": {}, + "selectors": {}, + "disabled": {}, + "parent_map": { + "model.failure_corpus.customer_totals": [ + "model.failure_corpus.stg_orders" + ], + "model.failure_corpus.stg_orders": [ + "source.failure_corpus.raw_orders" + ], + "source.failure_corpus.raw_orders": [] + }, + "group_map": {}, + "docs": {}, + "saved_queries": {}, + "semantic_models": {}, + "unit_tests": {} + } +} diff --git a/tests/goldens/compiler/failure/variable-not-defined.json b/tests/goldens/compiler/failure/variable-not-defined.json new file mode 100644 index 0000000000..4054304f5e --- /dev/null +++ b/tests/goldens/compiler/failure/variable-not-defined.json @@ -0,0 +1,18 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "P001", + "message": "unknown project variable '@@region' in '/models/staging/stg_orders.sql'. Available vars: none" + } + ] + } + ], + "compiled": {}, + "manifest": null +} diff --git a/tests/goldens/compiler/fixture/dbt_interop.json b/tests/goldens/compiler/fixture/dbt_interop.json new file mode 100644 index 0000000000..0bd687b95e --- /dev/null +++ b/tests/goldens/compiler/fixture/dbt_interop.json @@ -0,0 +1,66 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "C214", + "message": "Model file models/marts/downstream_orders.sql uses __dbt_ref(\"fact_orders\") but no dbt manifest was found", + "help": "Run dbt compile or configure dbt target_path so SQLBuild can read manifest.json." + } + ] + }, + { + "label": "compile-warm", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "C214", + "message": "Model file models/marts/downstream_orders.sql uses __dbt_ref(\"fact_orders\") but no dbt manifest was found", + "help": "Run dbt compile or configure dbt target_path so SQLBuild can read manifest.json." + } + ] + }, + { + "label": "compile-store-warm", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "C214", + "message": "Model file models/marts/downstream_orders.sql uses __dbt_ref(\"fact_orders\") but no dbt manifest was found", + "help": "Run dbt compile or configure dbt target_path so SQLBuild can read manifest.json." + } + ] + }, + { + "label": "plan", + "exit_code": 1, + "diagnostics": [ + "Project discovery START", + "Project assembly START", + "Declaration parsing START", + "Declaration parsing OK ()", + "Python discovery START", + "Python discovery OK ()", + "Project assembly OK ()", + "Project discovery OK ()", + "External manifest discovery START", + "External manifest discovery OK ()", + "Compiling project...", + "error[C214]: Model file models/marts/downstream_orders.sql uses __dbt_ref(\"fact_orders\") but no dbt manifest was found", + " = help: Run dbt compile or configure dbt target_path so SQLBuild can read manifest.json.", + "", + "" + ] + } + ], + "compiled": {}, + "manifest": null +} diff --git a/tests/goldens/compiler/fixture/source_loader_strategies.json b/tests/goldens/compiler/fixture/source_loader_strategies.json new file mode 100644 index 0000000000..122ad0c76d --- /dev/null +++ b/tests/goldens/compiler/fixture/source_loader_strategies.json @@ -0,0 +1,368 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 0, + "diagnostics": [] + }, + { + "label": "compile-warm", + "exit_code": 0, + "diagnostics": [] + }, + { + "label": "compile-store-warm", + "exit_code": 0, + "diagnostics": [] + }, + { + "label": "plan", + "exit_code": 0, + "diagnostics": [] + } + ], + "compiled": {}, + "manifest": { + "nodes": {}, + "sources": { + "source.source_loader_strategies.raw_countries": { + "database": null, + "schema": "", + "name": "raw_countries", + "resource_type": "source", + "package_name": "source_loader_strategies", + "path": "sources/raw.yml", + "original_file_path": "sources/raw.yml", + "unique_id": "source.source_loader_strategies.raw_countries", + "fqn": [ + "source_loader_strategies", + "raw_countries" + ], + "source_name": "raw_countries", + "source_description": "", + "loader": "", + "identifier": "raw_countries", + "quoting": {}, + "loaded_at_field": null, + "freshness": null, + "external": null, + "description": "Test source raw_countries.", + "columns": { + "country_id": { + "name": "country_id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "country_code": { + "name": "country_code", + "description": "", + "meta": {}, + "data_type": "VARCHAR", + "constraints": [], + "quote": null, + "tags": [] + }, + "country_name": { + "name": "country_name", + "description": "", + "meta": {}, + "data_type": "VARCHAR", + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": { + "sqlbuild": { + "loader": "raw_countries", + "auto_load": false, + "write_strategy": "table" + } + }, + "source_meta": {}, + "tags": [], + "config": {}, + "patch_path": null, + "unrendered_config": {}, + "relation_name": null + }, + "source.source_loader_strategies.raw_webhook_events": { + "database": null, + "schema": "", + "name": "raw_webhook_events", + "resource_type": "source", + "package_name": "source_loader_strategies", + "path": "sources/raw.yml", + "original_file_path": "sources/raw.yml", + "unique_id": "source.source_loader_strategies.raw_webhook_events", + "fqn": [ + "source_loader_strategies", + "raw_webhook_events" + ], + "source_name": "raw_webhook_events", + "source_description": "", + "loader": "", + "identifier": "raw_webhook_events", + "quoting": {}, + "loaded_at_field": null, + "freshness": null, + "external": null, + "description": "Test source raw_webhook_events.", + "columns": { + "event_id": { + "name": "event_id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "event_name": { + "name": "event_name", + "description": "", + "meta": {}, + "data_type": "VARCHAR", + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": { + "sqlbuild": { + "loader": "raw_webhook_events", + "auto_load": false, + "write_strategy": "append" + } + }, + "source_meta": {}, + "tags": [], + "config": {}, + "patch_path": null, + "unrendered_config": {}, + "relation_name": null + }, + "source.source_loader_strategies.raw_order_events": { + "database": null, + "schema": "", + "name": "raw_order_events", + "resource_type": "source", + "package_name": "source_loader_strategies", + "path": "sources/raw.yml", + "original_file_path": "sources/raw.yml", + "unique_id": "source.source_loader_strategies.raw_order_events", + "fqn": [ + "source_loader_strategies", + "raw_order_events" + ], + "source_name": "raw_order_events", + "source_description": "", + "loader": "", + "identifier": "raw_order_events", + "quoting": {}, + "loaded_at_field": null, + "freshness": null, + "external": null, + "description": "Test source raw_order_events.", + "columns": { + "event_id": { + "name": "event_id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "event_at": { + "name": "event_at", + "description": "", + "meta": {}, + "data_type": "TIMESTAMP", + "constraints": [], + "quote": null, + "tags": [] + }, + "amount_cents": { + "name": "amount_cents", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": { + "sqlbuild": { + "loader": "raw_order_events", + "auto_load": false, + "write_strategy": "delete_insert", + "cursor_column": "event_at" + } + }, + "source_meta": {}, + "tags": [], + "config": {}, + "patch_path": null, + "unrendered_config": {}, + "relation_name": null + }, + "source.source_loader_strategies.raw_customers": { + "database": null, + "schema": "", + "name": "raw_customers", + "resource_type": "source", + "package_name": "source_loader_strategies", + "path": "sources/raw.yml", + "original_file_path": "sources/raw.yml", + "unique_id": "source.source_loader_strategies.raw_customers", + "fqn": [ + "source_loader_strategies", + "raw_customers" + ], + "source_name": "raw_customers", + "source_description": "", + "loader": "", + "identifier": "raw_customers", + "quoting": {}, + "loaded_at_field": null, + "freshness": null, + "external": null, + "description": "Test source raw_customers.", + "columns": { + "customer_id": { + "name": "customer_id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "plan_name": { + "name": "plan_name", + "description": "", + "meta": {}, + "data_type": "VARCHAR", + "constraints": [], + "quote": null, + "tags": [] + }, + "updated_at": { + "name": "updated_at", + "description": "", + "meta": {}, + "data_type": "TIMESTAMP", + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": { + "sqlbuild": { + "loader": "raw_customers", + "auto_load": false, + "write_strategy": "merge", + "cursor_column": "updated_at", + "unique_key": [ + "customer_id" + ] + } + }, + "source_meta": {}, + "tags": [], + "config": {}, + "patch_path": null, + "unrendered_config": {}, + "relation_name": null + }, + "source.source_loader_strategies.raw_loader_status": { + "database": null, + "schema": "", + "name": "raw_loader_status", + "resource_type": "source", + "package_name": "source_loader_strategies", + "path": "sources/raw.yml", + "original_file_path": "sources/raw.yml", + "unique_id": "source.source_loader_strategies.raw_loader_status", + "fqn": [ + "source_loader_strategies", + "raw_loader_status" + ], + "source_name": "raw_loader_status", + "source_description": "", + "loader": "", + "identifier": "raw_loader_status", + "quoting": {}, + "loaded_at_field": null, + "freshness": null, + "external": null, + "description": "Test source raw_loader_status.", + "columns": { + "status_id": { + "name": "status_id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "status_name": { + "name": "status_name", + "description": "", + "meta": {}, + "data_type": "VARCHAR", + "constraints": [], + "quote": null, + "tags": [] + }, + "loaded_by": { + "name": "loaded_by", + "description": "", + "meta": {}, + "data_type": "VARCHAR", + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": { + "sqlbuild": { + "loader": "raw_loader_status", + "auto_load": false + } + }, + "source_meta": {}, + "tags": [], + "config": {}, + "patch_path": null, + "unrendered_config": {}, + "relation_name": null + } + }, + "macros": {}, + "exposures": {}, + "metrics": {}, + "groups": {}, + "selectors": {}, + "disabled": {}, + "parent_map": { + "source.source_loader_strategies.raw_countries": [], + "source.source_loader_strategies.raw_webhook_events": [], + "source.source_loader_strategies.raw_order_events": [], + "source.source_loader_strategies.raw_customers": [], + "source.source_loader_strategies.raw_loader_status": [] + }, + "group_map": {}, + "docs": {}, + "saved_queries": {}, + "semantic_models": {}, + "unit_tests": {} + } +} diff --git a/tests/goldens/compiler/fixture/waffle_shop.json b/tests/goldens/compiler/fixture/waffle_shop.json new file mode 100644 index 0000000000..01bd0de7a2 --- /dev/null +++ b/tests/goldens/compiler/fixture/waffle_shop.json @@ -0,0 +1,2110 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 0, + "diagnostics": [] + }, + { + "label": "compile-warm", + "exit_code": 0, + "diagnostics": [] + }, + { + "label": "compile-store-warm", + "exit_code": 0, + "diagnostics": [] + }, + { + "label": "plan", + "exit_code": 0, + "diagnostics": [] + } + ], + "compiled": { + "audits/generic/customer_status_snapshot/not_null__customer_id.sql": [ + "SELECT customer_id", + "FROM __ref(\"customer_status_snapshot\")", + "WHERE customer_id IS NULL", + "" + ], + "audits/generic/customer_status_snapshot/unique__customer_id.sql": [ + "SELECT customer_id, COUNT(*) AS duplicate_count", + "FROM __ref(\"customer_status_snapshot\")", + "WHERE customer_id IS NOT NULL", + "GROUP BY customer_id", + "HAVING COUNT(*) > 1", + "" + ], + "audits/generic/daily_activity_rollup/daily_orders_placed_is_non_negative.sql": [ + "SELECT *", + "FROM __ref(\"daily_activity_rollup\")", + "WHERE NOT (orders_placed >= 0)", + "" + ], + "audits/generic/daily_activity_rollup/not_null__activity_day.sql": [ + "SELECT activity_day", + "FROM __ref(\"daily_activity_rollup\")", + "WHERE activity_day IS NULL", + "" + ], + "audits/generic/daily_order_partitioned/not_null__order_date.sql": [ + "SELECT order_date", + "FROM __ref(\"daily_order_partitioned\")", + "WHERE order_date IS NULL", + "" + ], + "audits/generic/daily_order_partitioned/waffles_ordered_is_positive.sql": [ + "SELECT *", + "FROM __ref(\"daily_order_partitioned\")", + "WHERE NOT (waffles_ordered > 0)", + "" + ], + "audits/generic/daily_revenue/revenue_is_non_negative.sql": [ + "SELECT *", + "FROM __ref(\"daily_revenue\")", + "WHERE NOT (total_revenue_cents >= 0)", + "" + ], + "audits/generic/dim_customers/not_null__customer_id.sql": [ + "SELECT customer_id", + "FROM __ref(\"dim_customers\")", + "WHERE customer_id IS NULL", + "" + ], + "audits/generic/dim_customers/unique__customer_id.sql": [ + "SELECT customer_id, COUNT(*) AS duplicate_count", + "FROM __ref(\"dim_customers\")", + "WHERE customer_id IS NOT NULL", + "GROUP BY customer_id", + "HAVING COUNT(*) > 1", + "" + ], + "audits/generic/fact_orders/not_null__order_id.sql": [ + "SELECT order_id", + "FROM __ref(\"fact_orders\")", + "WHERE order_id IS NULL", + "" + ], + "audits/generic/hourly_activity_with_daily_context/day_orders_cover_hourly_orders.sql": [ + "SELECT *", + "FROM __ref(\"hourly_activity_with_daily_context\")", + "WHERE NOT (day_orders_placed >= orders_placed)", + "" + ], + "audits/generic/hourly_activity_with_daily_context/not_null__activity_hour.sql": [ + "SELECT activity_hour", + "FROM __ref(\"hourly_activity_with_daily_context\")", + "WHERE activity_hour IS NULL", + "" + ], + "audits/generic/hourly_order_activity/not_null__activity_hour.sql": [ + "SELECT activity_hour", + "FROM __ref(\"hourly_order_activity\")", + "WHERE activity_hour IS NULL", + "" + ], + "audits/generic/hourly_order_activity/orders_placed_is_non_negative.sql": [ + "SELECT *", + "FROM __ref(\"hourly_order_activity\")", + "WHERE NOT (orders_placed >= 0)", + "" + ], + "audits/generic/order_status_index/not_null__order_id.sql": [ + "SELECT order_id", + "FROM __ref(\"order_status_index\")", + "WHERE order_id IS NULL", + "" + ], + "audits/generic/order_status_index/unique__order_id.sql": [ + "SELECT order_id, COUNT(*) AS duplicate_count", + "FROM __ref(\"order_status_index\")", + "WHERE order_id IS NOT NULL", + "GROUP BY order_id", + "HAVING COUNT(*) > 1", + "" + ], + "audits/generic/stg_customers/not_null__customer_id.sql": [ + "SELECT customer_id", + "FROM __ref(\"stg_customers\")", + "WHERE customer_id IS NULL", + "" + ], + "audits/generic/stg_customers/not_null__email.sql": [ + "SELECT email", + "FROM __ref(\"stg_customers\")", + "WHERE email IS NULL", + "" + ], + "audits/generic/stg_customers/unique__customer_id.sql": [ + "SELECT customer_id, COUNT(*) AS duplicate_count", + "FROM __ref(\"stg_customers\")", + "WHERE customer_id IS NOT NULL", + "GROUP BY customer_id", + "HAVING COUNT(*) > 1", + "" + ], + "audits/generic/stg_orders/accepted_values__status.sql": [ + "SELECT status", + "FROM __ref(\"stg_orders\")", + "WHERE status IS NOT NULL", + " AND status NOT IN ('placed', 'preparing', 'ready', 'completed', 'cancelled')", + "" + ], + "audits/generic/stg_orders/not_null__customer_id.sql": [ + "SELECT customer_id", + "FROM __ref(\"stg_orders\")", + "WHERE customer_id IS NULL", + "" + ], + "audits/generic/stg_orders/not_null__order_id.sql": [ + "SELECT order_id", + "FROM __ref(\"stg_orders\")", + "WHERE order_id IS NULL", + "" + ], + "audits/generic/stg_orders/relationships__waffle_type_id.sql": [ + "SELECT waffle_type_id", + "FROM __ref(\"stg_orders\")", + "WHERE waffle_type_id IS NOT NULL", + " AND waffle_type_id NOT IN (", + " SELECT waffle_type_id", + " FROM __seed(\"waffle_types\")", + " WHERE waffle_type_id IS NOT NULL", + " )", + "" + ], + "audits/generic/stg_orders/unique__order_id.sql": [ + "SELECT order_id, COUNT(*) AS duplicate_count", + "FROM __ref(\"stg_orders\")", + "WHERE order_id IS NOT NULL", + "GROUP BY order_id", + "HAVING COUNT(*) > 1", + "" + ], + "audits/generic/stg_payments/not_null__order_id.sql": [ + "SELECT order_id", + "FROM __ref(\"stg_payments\")", + "WHERE order_id IS NULL", + "" + ], + "audits/generic/stg_payments/not_null__payment_id.sql": [ + "SELECT payment_id", + "FROM __ref(\"stg_payments\")", + "WHERE payment_id IS NULL", + "" + ], + "audits/generic/stg_payments/relationships__order_id.sql": [ + "SELECT order_id", + "FROM __ref(\"stg_payments\")", + "WHERE order_id IS NOT NULL", + " AND order_id NOT IN (", + " SELECT id", + " FROM __source(\"raw_orders\")", + " WHERE id IS NOT NULL", + " )", + "" + ], + "audits/generic/stg_payments/unique__payment_id.sql": [ + "SELECT payment_id, COUNT(*) AS duplicate_count", + "FROM __ref(\"stg_payments\")", + "WHERE payment_id IS NOT NULL", + "GROUP BY payment_id", + "HAVING COUNT(*) > 1", + "" + ], + "functions/python/is_completed_order_py.sql": [ + "REGISTER PYTHON FUNCTION is_completed_order_py(STRING) RETURNS BOOLEAN", + "" + ], + "functions/sql/customer_orders.sql": [ + "CREATE OR REPLACE MACRO main.customer_orders(p_customer_id) AS TABLE", + "SELECT", + " order_id,", + " ordered_at,", + " waffle_name,", + " line_total_cents,", + " order_status,", + " is_completed_order", + "FROM __ref(\"fact_orders\")", + "WHERE customer_id = p_customer_id", + "" + ], + "functions/sql/is_completed_order.sql": [ + "CREATE OR REPLACE MACRO main.is_completed_order(order_status) AS (", + "order_status = 'completed'", + ")", + "" + ], + "models/intermediate/customer_status_snapshot.sql": [ + "SELECT", + " customer_id,", + " MAX(ordered_at) AS last_ordered_at,", + " MAX(order_status) AS latest_order_status,", + " COUNT(*) AS total_orders,", + " SUM(line_total_cents) AS total_revenue_cents", + "FROM __ref(\"fact_orders\")", + "GROUP BY customer_id", + "" + ], + "models/intermediate/order_status_index.sql": [ + "SELECT", + " order_id,", + " customer_id,", + " order_status,", + " ordered_at,", + " line_total_cents", + "FROM __ref(\"fact_orders\")", + "" + ], + "models/marts/daily_activity_rollup.sql": [ + "SELECT", + " DATE_TRUNC('day', activity_hour) AS activity_day,", + " SUM(orders_placed) AS orders_placed,", + " SUM(waffles_ordered) AS waffles_ordered,", + " SUM(revenue_cents) AS revenue_cents", + "FROM __ref(\"hourly_order_activity\")", + "GROUP BY DATE_TRUNC('day', activity_hour)", + "" + ], + "models/marts/daily_order_partitioned.sql": [ + "SELECT", + " CAST(o.ordered_at AS DATE) AS order_date,", + " COUNT(DISTINCT o.order_id) AS order_count,", + " SUM(o.quantity) AS waffles_ordered,", + " COUNT(DISTINCT o.customer_id) AS unique_customers", + "FROM __ref(\"stg_orders\") o", + "WHERE CAST(o.ordered_at AS DATE) >= CAST(@@@partition_start AS DATE)", + " AND CAST(o.ordered_at AS DATE) < CAST(@@@partition_end AS DATE)", + "GROUP BY CAST(o.ordered_at AS DATE)", + "" + ], + "models/marts/daily_revenue.sql": [ + "SELECT", + " CAST(o.ordered_at AS DATE) AS revenue_date,", + " COUNT(DISTINCT o.order_id) AS order_count,", + " SUM(o.quantity) AS waffles_sold,", + " SUM(p.amount_cents) AS total_revenue_cents,", + " SUM(p.amount_cents) / COUNT(DISTINCT o.order_id) AS avg_order_value_cents", + "FROM __ref(\"stg_orders\") o", + "INNER JOIN __ref(\"stg_payments\") p ON o.order_id = p.order_id AND p.payment_status = 'success'", + "GROUP BY CAST(o.ordered_at AS DATE)", + "" + ], + "models/marts/dim_customers.sql": [ + "SELECT", + " c.customer_id,", + " c.first_name,", + " c.last_name,", + " c.email,", + " c.created_at AS customer_since,", + " COUNT(DISTINCT o.order_id) AS lifetime_orders,", + " COALESCE(SUM(p.amount_cents), 0) AS lifetime_spend_cents", + "FROM __ref(\"stg_customers\") c", + "LEFT JOIN __ref(\"stg_orders\") o ON c.customer_id = o.customer_id", + "LEFT JOIN __ref(\"stg_payments\") p ON o.order_id = p.order_id AND p.payment_status = 'success'", + "GROUP BY c.customer_id, c.first_name, c.last_name, c.email, c.created_at", + "" + ], + "models/marts/fact_orders.sql": [ + "SELECT", + " o.order_id,", + " o.customer_id,", + " o.waffle_type_id,", + " w.waffle_name,", + " w.category AS waffle_category,", + " o.quantity,", + " w.price_cents * o.quantity AS line_total_cents,", + " o.ordered_at,", + " o.status AS order_status,", + " __udf(\"is_completed_order\")(o.status) AS is_completed_order,", + " __udf(\"is_completed_order_py\")(o.status) AS is_completed_order_py,", + " p.payment_method,", + " p.payment_status,", + " p.amount_cents AS payment_amount_cents", + "FROM __ref(\"stg_orders\") o", + "LEFT JOIN __seed(\"waffle_types\") w ON o.waffle_type_id = w.waffle_type_id", + "LEFT JOIN __ref(\"stg_payments\") p ON o.order_id = p.order_id", + "" + ], + "models/marts/hourly_activity_with_daily_context.sql": [ + "SELECT", + " h.activity_hour,", + " h.orders_placed,", + " d.orders_placed AS day_orders_placed,", + " h.waffles_ordered,", + " h.revenue_cents", + "FROM __ref(\"hourly_order_activity\") h", + "INNER JOIN __ref(\"daily_activity_rollup\") d", + " ON DATE_TRUNC('day', h.activity_hour) = d.activity_day", + "" + ], + "models/marts/hourly_order_activity.sql": [ + "SELECT", + " DATE_TRUNC('hour', o.ordered_at) AS activity_hour,", + " COUNT(*) AS orders_placed,", + " SUM(o.quantity) AS waffles_ordered,", + " SUM(o.line_total_cents) AS revenue_cents", + "FROM __ref(\"fact_orders\") o", + "GROUP BY DATE_TRUNC('hour', o.ordered_at)", + "" + ], + "models/marts/scenario_order_prices.sql": [ + "SELECT", + " o.order_id,", + " o.quantity,", + " w.waffle_name,", + " w.price_cents,", + " w.price_cents * o.quantity AS line_total_cents,", + " p.amount_cents AS payment_amount_cents", + "FROM __ref(\"stg_orders\") o", + "INNER JOIN __seed(\"waffle_types\") w ON o.waffle_type_id = w.waffle_type_id", + "LEFT JOIN __ref(\"stg_payments\") p ON o.order_id = p.order_id", + "" + ], + "models/staging/stg_customers.sql": [ + "SELECT", + " id AS customer_id,", + " first_name,", + " last_name,", + " email,", + " created_at", + "FROM __source(\"raw_customers\")", + "" + ], + "models/staging/stg_orders.sql": [ + "SELECT", + " id AS order_id,", + " customer_id,", + " waffle_type_id,", + " quantity,", + " ordered_at,", + " status", + "FROM __source(\"raw_orders\")", + "" + ], + "models/staging/stg_payments.sql": [ + "SELECT", + " id AS payment_id,", + " order_id,", + " amount_cents,", + " payment_method,", + " paid_at,", + " status AS payment_status", + "FROM __source(\"raw_payments\")", + "" + ], + "tests/fact_orders/test_fact_orders.sql": [ + "WITH __ref__stg_orders AS (SELECT", + " 1 AS order_id,", + " 100 AS customer_id,", + " 2 AS waffle_type_id,", + " 3 AS quantity,", + " CAST('2026-04-01 10:00:00' AS TIMESTAMP) AS ordered_at,", + " 'completed' AS status),", + "__ref__stg_payments AS (SELECT", + " 10 AS payment_id,", + " 1 AS order_id,", + " 2850 AS amount_cents,", + " 'card' AS payment_method,", + " CAST('2026-04-01 10:05:00' AS TIMESTAMP) AS paid_at,", + " 'success' AS payment_status),", + "__seed__waffle_types AS (SELECT", + " 2 AS waffle_type_id,", + " 'Liege' AS waffle_name,", + " 'sweet' AS category,", + " 950 AS price_cents),", + "__ref__fact_orders AS (SELECT", + " o.order_id,", + " o.customer_id,", + " o.waffle_type_id,", + " w.waffle_name,", + " w.category AS waffle_category,", + " o.quantity,", + " w.price_cents * o.quantity AS line_total_cents,", + " o.ordered_at,", + " o.status AS order_status,", + " main.is_completed_order(o.status) AS is_completed_order,", + " is_completed_order_py(o.status) AS is_completed_order_py,", + " p.payment_method,", + " p.payment_status,", + " p.amount_cents AS payment_amount_cents", + "FROM __ref__stg_orders o", + "LEFT JOIN __seed__waffle_types w ON o.waffle_type_id = w.waffle_type_id", + "LEFT JOIN __ref__stg_payments p ON o.order_id = p.order_id),", + "__actual__fact_orders AS (SELECT * FROM __ref__fact_orders),", + "__expected__fact_orders AS (SELECT", + " 1 AS order_id,", + " 100 AS customer_id,", + " 2 AS waffle_type_id,", + " 'Liege' AS waffle_name,", + " 'sweet' AS waffle_category,", + " 3 AS quantity,", + " 2850 AS line_total_cents,", + " CAST('2026-04-01 10:00:00' AS TIMESTAMP) AS ordered_at,", + " 'completed' AS order_status,", + " TRUE AS is_completed_order,", + " TRUE AS is_completed_order_py,", + " 'card' AS payment_method,", + " 'success' AS payment_status,", + " 2850 AS payment_amount_cents),", + "__assert__line_totals_are_non_negative AS (SELECT *", + " FROM __ref__fact_orders", + " WHERE line_total_cents < 0)", + "SELECT 0 AS step_index, 'fact_orders' AS model_name, (SELECT COUNT(*) FROM __actual__fact_orders) AS actual_count, (SELECT COUNT(*) FROM __expected__fact_orders) AS expected_count, (SELECT COUNT(*) FROM (SELECT order_id, customer_id, waffle_type_id, waffle_name, waffle_category, quantity, line_total_cents, ordered_at, order_status, is_completed_order, is_completed_order_py, payment_method, payment_status, payment_amount_cents FROM __actual__fact_orders EXCEPT SELECT order_id, customer_id, waffle_type_id, waffle_name, waffle_category, quantity, line_total_cents, ordered_at, order_status, is_completed_order, is_completed_order_py, payment_method, payment_status, payment_amount_cents FROM __expected__fact_orders) AS __sqlbuild_mismatch) AS unexpected_count, (SELECT COUNT(*) FROM (SELECT order_id, customer_id, waffle_type_id, waffle_name, waffle_category, quantity, line_total_cents, ordered_at, order_status, is_completed_order, is_completed_order_py, payment_method, payment_status, payment_amount_cents FROM __expected__fact_orders EXCEPT SELECT order_id, customer_id, waffle_type_id, waffle_name, waffle_category, quantity, line_total_cents, ordered_at, order_status, is_completed_order, is_completed_order_py, payment_method, payment_status, payment_amount_cents FROM __actual__fact_orders) AS __sqlbuild_missing) AS missing_count", + "UNION ALL", + "SELECT 1 AS step_index, 'assertion line_totals_are_non_negative' AS model_name, (SELECT COUNT(*) FROM __assert__line_totals_are_non_negative) AS actual_count, 0 AS expected_count, (SELECT COUNT(*) FROM __assert__line_totals_are_non_negative) AS unexpected_count, 0 AS missing_count", + "" + ], + "tests/macro calculates_line_total_cents/calculates_line_total_cents.sql": [ + "WITH input_values AS (SELECT 950 AS price_cents, 3 AS quantity),", + "__actual__macro_calculates_line_total_cents AS (SELECT price_cents * quantity AS line_total_cents", + " FROM input_values),", + "__expected__macro_calculates_line_total_cents AS (SELECT 2850 AS line_total_cents)", + "SELECT 0 AS step_index, 'macro calculates_line_total_cents' AS model_name, (SELECT COUNT(*) FROM __actual__macro_calculates_line_total_cents) AS actual_count, (SELECT COUNT(*) FROM __expected__macro_calculates_line_total_cents) AS expected_count, (SELECT COUNT(*) FROM (SELECT * FROM __actual__macro_calculates_line_total_cents EXCEPT SELECT * FROM __expected__macro_calculates_line_total_cents) AS __sqlbuild_mismatch) AS unexpected_count, (SELECT COUNT(*) FROM (SELECT * FROM __expected__macro_calculates_line_total_cents EXCEPT SELECT * FROM __actual__macro_calculates_line_total_cents) AS __sqlbuild_missing) AS missing_count", + "" + ], + "tests/stg_orders/test_stg_orders.sql": [ + "WITH __source__raw_orders AS (SELECT", + " 1 AS id,", + " 100 AS customer_id,", + " 2 AS waffle_type_id,", + " 3 AS quantity,", + " '2026-04-01 10:00:00' AS ordered_at,", + " 'completed' AS status),", + "__ref__stg_orders AS (SELECT", + " id AS order_id,", + " customer_id,", + " waffle_type_id,", + " quantity,", + " ordered_at,", + " status", + "FROM __source__raw_orders),", + "__actual__stg_orders AS (SELECT * FROM __ref__stg_orders),", + "__expected__stg_orders AS (SELECT", + " 1 AS order_id,", + " 100 AS customer_id,", + " 2 AS waffle_type_id,", + " 3 AS quantity,", + " '2026-04-01 10:00:00' AS ordered_at,", + " 'completed' AS status),", + "__assert__order_ids_are_not_null AS (SELECT *", + " FROM __ref__stg_orders", + " WHERE order_id IS NULL)", + "SELECT 0 AS step_index, 'stg_orders' AS model_name, (SELECT COUNT(*) FROM __actual__stg_orders) AS actual_count, (SELECT COUNT(*) FROM __expected__stg_orders) AS expected_count, (SELECT COUNT(*) FROM (SELECT order_id, customer_id, waffle_type_id, quantity, ordered_at, status FROM __actual__stg_orders EXCEPT SELECT order_id, customer_id, waffle_type_id, quantity, ordered_at, status FROM __expected__stg_orders) AS __sqlbuild_mismatch) AS unexpected_count, (SELECT COUNT(*) FROM (SELECT order_id, customer_id, waffle_type_id, quantity, ordered_at, status FROM __expected__stg_orders EXCEPT SELECT order_id, customer_id, waffle_type_id, quantity, ordered_at, status FROM __actual__stg_orders) AS __sqlbuild_missing) AS missing_count", + "UNION ALL", + "SELECT 1 AS step_index, 'assertion order_ids_are_not_null' AS model_name, (SELECT COUNT(*) FROM __assert__order_ids_are_not_null) AS actual_count, 0 AS expected_count, (SELECT COUNT(*) FROM __assert__order_ids_are_not_null) AS unexpected_count, 0 AS missing_count", + "" + ], + "tests/table_fn returns_customer_orders/returns_customer_orders.sql": [ + "WITH __actual__table_fn_returns_customer_orders AS (SELECT", + " order_id,", + " order_status,", + " is_completed_order", + " FROM main.customer_orders(1)),", + "__expected__table_fn_returns_customer_orders AS (SELECT 1 AS order_id, 'completed' AS order_status, TRUE AS is_completed_order", + " UNION ALL", + " SELECT 2 AS order_id, 'completed' AS order_status, TRUE AS is_completed_order", + " UNION ALL", + " SELECT 8 AS order_id, 'completed' AS order_status, TRUE AS is_completed_order)", + "SELECT 0 AS step_index, 'table_fn returns_customer_orders' AS model_name, (SELECT COUNT(*) FROM __actual__table_fn_returns_customer_orders) AS actual_count, (SELECT COUNT(*) FROM __expected__table_fn_returns_customer_orders) AS expected_count, (SELECT COUNT(*) FROM (SELECT * FROM __actual__table_fn_returns_customer_orders EXCEPT SELECT * FROM __expected__table_fn_returns_customer_orders) AS __sqlbuild_mismatch) AS unexpected_count, (SELECT COUNT(*) FROM (SELECT * FROM __expected__table_fn_returns_customer_orders EXCEPT SELECT * FROM __actual__table_fn_returns_customer_orders) AS __sqlbuild_missing) AS missing_count", + "" + ], + "tests/udf detects_completed_orders/detects_completed_orders.sql": [ + "WITH input_values AS (SELECT 'completed' AS order_status", + " UNION ALL", + " SELECT 'pending' AS order_status),", + "__actual__udf_detects_completed_orders AS (SELECT", + " order_status,", + " main.is_completed_order(order_status) AS is_completed_order", + " FROM input_values),", + "__expected__udf_detects_completed_orders AS (SELECT 'completed' AS order_status, TRUE AS is_completed_order", + " UNION ALL", + " SELECT 'pending' AS order_status, FALSE AS is_completed_order)", + "SELECT 0 AS step_index, 'udf detects_completed_orders' AS model_name, (SELECT COUNT(*) FROM __actual__udf_detects_completed_orders) AS actual_count, (SELECT COUNT(*) FROM __expected__udf_detects_completed_orders) AS expected_count, (SELECT COUNT(*) FROM (SELECT * FROM __actual__udf_detects_completed_orders EXCEPT SELECT * FROM __expected__udf_detects_completed_orders) AS __sqlbuild_mismatch) AS unexpected_count, (SELECT COUNT(*) FROM (SELECT * FROM __expected__udf_detects_completed_orders EXCEPT SELECT * FROM __actual__udf_detects_completed_orders) AS __sqlbuild_missing) AS missing_count", + "" + ] + }, + "manifest": { + "nodes": { + "model.waffle_shop.customer_status_snapshot": { + "database": null, + "schema": "main", + "name": "customer_status_snapshot", + "resource_type": "model", + "package_name": "waffle_shop", + "path": "models/intermediate/customer_status_snapshot.sql", + "original_file_path": "models/intermediate/customer_status_snapshot.sql", + "unique_id": "model.waffle_shop.customer_status_snapshot", + "fqn": [ + "waffle_shop", + "models", + "intermediate", + "customer_status_snapshot" + ], + "alias": "customer_status_snapshot", + "config": { + "enabled": true, + "alias": null, + "schema": null, + "database": null, + "tags": [ + "intermediate", + "acceptance" + ], + "meta": {}, + "group": null, + "materialized": "incremental", + "incremental_strategy": "merge", + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": [ + "customer_id" + ], + "on_schema_change": "sync_all_columns", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [ + "intermediate", + "acceptance" + ], + "description": "Merge-based customer snapshot with timestamp cursor and explicit backfill policies.", + "columns": { + "customer_id": { + "name": "customer_id", + "description": "", + "meta": {}, + "data_type": null, + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.customer_status_snapshot", + "depends_on": { + "macros": [], + "nodes": [ + "model.waffle_shop.fact_orders" + ] + } + }, + "model.waffle_shop.order_status_index": { + "database": null, + "schema": "main", + "name": "order_status_index", + "resource_type": "model", + "package_name": "waffle_shop", + "path": "models/intermediate/order_status_index.sql", + "original_file_path": "models/intermediate/order_status_index.sql", + "unique_id": "model.waffle_shop.order_status_index", + "fqn": [ + "waffle_shop", + "models", + "intermediate", + "order_status_index" + ], + "alias": "order_status_index", + "config": { + "enabled": true, + "alias": null, + "schema": null, + "database": null, + "tags": [ + "intermediate", + "acceptance" + ], + "meta": {}, + "group": null, + "materialized": "incremental", + "incremental_strategy": "delete_insert", + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "append_new_columns", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [ + "intermediate", + "acceptance" + ], + "description": "Integer-cursor incremental projection over fact orders.", + "columns": { + "order_id": { + "name": "order_id", + "description": "", + "meta": {}, + "data_type": null, + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.order_status_index", + "depends_on": { + "macros": [], + "nodes": [ + "model.waffle_shop.fact_orders" + ] + } + }, + "model.waffle_shop.daily_activity_rollup": { + "database": null, + "schema": "main", + "name": "daily_activity_rollup", + "resource_type": "model", + "package_name": "waffle_shop", + "path": "models/marts/daily_activity_rollup.sql", + "original_file_path": "models/marts/daily_activity_rollup.sql", + "unique_id": "model.waffle_shop.daily_activity_rollup", + "fqn": [ + "waffle_shop", + "models", + "marts", + "daily_activity_rollup" + ], + "alias": "daily_activity_rollup", + "config": { + "enabled": true, + "alias": null, + "schema": null, + "database": null, + "tags": [ + "marts", + "acceptance" + ], + "meta": {}, + "group": null, + "materialized": "incremental", + "incremental_strategy": "delete_insert", + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [ + "marts", + "acceptance" + ], + "description": "Downstream daily microbatch rollup with a wider batch size than its hourly upstream.", + "columns": { + "activity_day": { + "name": "activity_day", + "description": "", + "meta": {}, + "data_type": null, + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.daily_activity_rollup", + "depends_on": { + "macros": [], + "nodes": [ + "model.waffle_shop.hourly_order_activity" + ] + } + }, + "model.waffle_shop.daily_order_partitioned": { + "database": null, + "schema": "main", + "name": "daily_order_partitioned", + "resource_type": "model", + "package_name": "waffle_shop", + "path": "models/marts/daily_order_partitioned.sql", + "original_file_path": "models/marts/daily_order_partitioned.sql", + "unique_id": "model.waffle_shop.daily_order_partitioned", + "fqn": [ + "waffle_shop", + "models", + "marts", + "daily_order_partitioned" + ], + "alias": "daily_order_partitioned", + "config": { + "enabled": true, + "alias": null, + "schema": null, + "database": null, + "tags": [ + "marts" + ], + "meta": {}, + "group": null, + "materialized": "partition_tracked", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [ + "marts" + ], + "description": "Partition-tracked daily order summary using custom materialization.", + "columns": { + "order_date": { + "name": "order_date", + "description": "", + "meta": {}, + "data_type": null, + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.daily_order_partitioned", + "depends_on": { + "macros": [], + "nodes": [ + "model.waffle_shop.stg_orders" + ] + } + }, + "model.waffle_shop.daily_revenue": { + "database": null, + "schema": "main", + "name": "daily_revenue", + "resource_type": "model", + "package_name": "waffle_shop", + "path": "models/marts/daily_revenue.sql", + "original_file_path": "models/marts/daily_revenue.sql", + "unique_id": "model.waffle_shop.daily_revenue", + "fqn": [ + "waffle_shop", + "models", + "marts", + "daily_revenue" + ], + "alias": "daily_revenue", + "config": { + "enabled": true, + "alias": null, + "schema": null, + "database": null, + "tags": [ + "marts" + ], + "meta": {}, + "group": null, + "materialized": "table", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [ + "marts" + ], + "description": "Daily revenue aggregation.", + "columns": { + "revenue_date": { + "name": "revenue_date", + "description": "", + "meta": {}, + "data_type": null, + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.daily_revenue", + "depends_on": { + "macros": [], + "nodes": [ + "model.waffle_shop.stg_orders", + "model.waffle_shop.stg_payments" + ] + } + }, + "model.waffle_shop.dim_customers": { + "database": null, + "schema": "main", + "name": "dim_customers", + "resource_type": "model", + "package_name": "waffle_shop", + "path": "models/marts/dim_customers.sql", + "original_file_path": "models/marts/dim_customers.sql", + "unique_id": "model.waffle_shop.dim_customers", + "fqn": [ + "waffle_shop", + "models", + "marts", + "dim_customers" + ], + "alias": "dim_customers", + "config": { + "enabled": true, + "alias": null, + "schema": null, + "database": null, + "tags": [ + "marts" + ], + "meta": {}, + "group": null, + "materialized": "table", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [ + "marts" + ], + "description": "Customer dimension with lifetime metrics.", + "columns": { + "customer_id": { + "name": "customer_id", + "description": "", + "meta": {}, + "data_type": null, + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.dim_customers", + "depends_on": { + "macros": [], + "nodes": [ + "model.waffle_shop.stg_customers", + "model.waffle_shop.stg_orders", + "model.waffle_shop.stg_payments" + ] + } + }, + "model.waffle_shop.fact_orders": { + "database": null, + "schema": "main", + "name": "fact_orders", + "resource_type": "model", + "package_name": "waffle_shop", + "path": "models/marts/fact_orders.sql", + "original_file_path": "models/marts/fact_orders.sql", + "unique_id": "model.waffle_shop.fact_orders", + "fqn": [ + "waffle_shop", + "models", + "marts", + "fact_orders" + ], + "alias": "fact_orders", + "config": { + "enabled": true, + "alias": null, + "schema": null, + "database": null, + "tags": [ + "marts" + ], + "meta": {}, + "group": null, + "materialized": "table", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [ + "marts" + ], + "description": "Order fact table with waffle and payment details.", + "columns": { + "order_id": { + "name": "order_id", + "description": "", + "meta": {}, + "data_type": null, + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.fact_orders", + "depends_on": { + "macros": [], + "nodes": [ + "model.waffle_shop.is_completed_order", + "model.waffle_shop.is_completed_order_py", + "model.waffle_shop.stg_orders", + "seed.waffle_shop.waffle_types", + "model.waffle_shop.stg_payments" + ] + } + }, + "model.waffle_shop.hourly_activity_with_daily_context": { + "database": null, + "schema": "main", + "name": "hourly_activity_with_daily_context", + "resource_type": "model", + "package_name": "waffle_shop", + "path": "models/marts/hourly_activity_with_daily_context.sql", + "original_file_path": "models/marts/hourly_activity_with_daily_context.sql", + "unique_id": "model.waffle_shop.hourly_activity_with_daily_context", + "fqn": [ + "waffle_shop", + "models", + "marts", + "hourly_activity_with_daily_context" + ], + "alias": "hourly_activity_with_daily_context", + "config": { + "enabled": true, + "alias": null, + "schema": null, + "database": null, + "tags": [ + "marts", + "acceptance" + ], + "meta": {}, + "group": null, + "materialized": "incremental", + "incremental_strategy": "delete_insert", + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [ + "marts", + "acceptance" + ], + "description": "Hourly downstream microbatch model that depends on a coarser-grain daily upstream.", + "columns": { + "activity_hour": { + "name": "activity_hour", + "description": "", + "meta": {}, + "data_type": null, + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.hourly_activity_with_daily_context", + "depends_on": { + "macros": [], + "nodes": [ + "model.waffle_shop.hourly_order_activity", + "model.waffle_shop.daily_activity_rollup" + ] + } + }, + "model.waffle_shop.hourly_order_activity": { + "database": null, + "schema": "main", + "name": "hourly_order_activity", + "resource_type": "model", + "package_name": "waffle_shop", + "path": "models/marts/hourly_order_activity.sql", + "original_file_path": "models/marts/hourly_order_activity.sql", + "unique_id": "model.waffle_shop.hourly_order_activity", + "fqn": [ + "waffle_shop", + "models", + "marts", + "hourly_order_activity" + ], + "alias": "hourly_order_activity", + "config": { + "enabled": true, + "alias": null, + "schema": null, + "database": null, + "tags": [ + "marts" + ], + "meta": {}, + "group": null, + "materialized": "incremental", + "incremental_strategy": "delete_insert", + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [ + "marts" + ], + "description": "Hourly order activity aggregated via microbatch incremental.", + "columns": { + "activity_hour": { + "name": "activity_hour", + "description": "", + "meta": {}, + "data_type": null, + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.hourly_order_activity", + "depends_on": { + "macros": [], + "nodes": [ + "model.waffle_shop.fact_orders" + ] + } + }, + "model.waffle_shop.scenario_order_prices": { + "database": null, + "schema": "main", + "name": "scenario_order_prices", + "resource_type": "model", + "package_name": "waffle_shop", + "path": "models/marts/scenario_order_prices.sql", + "original_file_path": "models/marts/scenario_order_prices.sql", + "unique_id": "model.waffle_shop.scenario_order_prices", + "fqn": [ + "waffle_shop", + "models", + "marts", + "scenario_order_prices" + ], + "alias": "scenario_order_prices", + "config": { + "enabled": true, + "alias": null, + "schema": null, + "database": null, + "tags": [ + "marts", + "scenario" + ], + "meta": {}, + "group": null, + "materialized": "table", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [ + "marts", + "scenario" + ], + "description": "Small model used by scenario CLI e2e retain coverage.", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.scenario_order_prices", + "depends_on": { + "macros": [], + "nodes": [ + "model.waffle_shop.stg_orders", + "seed.waffle_shop.waffle_types", + "model.waffle_shop.stg_payments" + ] + } + }, + "model.waffle_shop.stg_customers": { + "database": null, + "schema": "main", + "name": "stg_customers", + "resource_type": "model", + "package_name": "waffle_shop", + "path": "models/staging/stg_customers.sql", + "original_file_path": "models/staging/stg_customers.sql", + "unique_id": "model.waffle_shop.stg_customers", + "fqn": [ + "waffle_shop", + "models", + "staging", + "stg_customers" + ], + "alias": "stg_customers", + "config": { + "enabled": true, + "alias": null, + "schema": null, + "database": null, + "tags": [ + "staging" + ], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [ + "staging" + ], + "description": "Cleaned customer records.", + "columns": { + "customer_id": { + "name": "customer_id", + "description": "", + "meta": {}, + "data_type": null, + "constraints": [], + "quote": null, + "tags": [] + }, + "email": { + "name": "email", + "description": "", + "meta": {}, + "data_type": null, + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.stg_customers", + "depends_on": { + "macros": [], + "nodes": [ + "source.waffle_shop.raw_customers" + ] + } + }, + "model.waffle_shop.stg_orders": { + "database": null, + "schema": "main", + "name": "stg_orders", + "resource_type": "model", + "package_name": "waffle_shop", + "path": "models/staging/stg_orders.sql", + "original_file_path": "models/staging/stg_orders.sql", + "unique_id": "model.waffle_shop.stg_orders", + "fqn": [ + "waffle_shop", + "models", + "staging", + "stg_orders" + ], + "alias": "stg_orders", + "config": { + "enabled": true, + "alias": null, + "schema": null, + "database": null, + "tags": [ + "staging" + ], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [ + "staging" + ], + "description": "Cleaned order records.", + "columns": { + "order_id": { + "name": "order_id", + "description": "", + "meta": {}, + "data_type": null, + "constraints": [], + "quote": null, + "tags": [] + }, + "customer_id": { + "name": "customer_id", + "description": "", + "meta": {}, + "data_type": null, + "constraints": [], + "quote": null, + "tags": [] + }, + "waffle_type_id": { + "name": "waffle_type_id", + "description": "", + "meta": {}, + "data_type": null, + "constraints": [], + "quote": null, + "tags": [] + }, + "status": { + "name": "status", + "description": "", + "meta": {}, + "data_type": null, + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.stg_orders", + "depends_on": { + "macros": [], + "nodes": [ + "source.waffle_shop.raw_orders" + ] + } + }, + "model.waffle_shop.stg_payments": { + "database": null, + "schema": "main", + "name": "stg_payments", + "resource_type": "model", + "package_name": "waffle_shop", + "path": "models/staging/stg_payments.sql", + "original_file_path": "models/staging/stg_payments.sql", + "unique_id": "model.waffle_shop.stg_payments", + "fqn": [ + "waffle_shop", + "models", + "staging", + "stg_payments" + ], + "alias": "stg_payments", + "config": { + "enabled": true, + "alias": null, + "schema": null, + "database": null, + "tags": [ + "staging" + ], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [ + "staging" + ], + "description": "Cleaned payment records.", + "columns": { + "payment_id": { + "name": "payment_id", + "description": "", + "meta": {}, + "data_type": null, + "constraints": [], + "quote": null, + "tags": [] + }, + "order_id": { + "name": "order_id", + "description": "", + "meta": {}, + "data_type": null, + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.stg_payments", + "depends_on": { + "macros": [], + "nodes": [ + "source.waffle_shop.raw_payments" + ] + } + }, + "seed.waffle_shop.waffle_types": { + "database": null, + "schema": "main", + "name": "waffle_types", + "resource_type": "seed", + "package_name": "waffle_shop", + "path": "seeds/waffle_types.csv", + "original_file_path": "seeds/waffle_types.csv", + "unique_id": "seed.waffle_shop.waffle_types", + "fqn": [ + "waffle_shop", + "seeds", + "waffle_types" + ], + "alias": "waffle_types", + "config": { + "enabled": true, + "alias": null, + "schema": "main", + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "seed", + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + }, + "delimiter": ",", + "quote_columns": null + }, + "tags": [], + "description": "Waffle menu items with pricing.", + "columns": { + "waffle_type_id": { + "name": "waffle_type_id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "waffle_name": { + "name": "waffle_name", + "description": "", + "meta": {}, + "data_type": "VARCHAR", + "constraints": [], + "quote": null, + "tags": [] + }, + "category": { + "name": "category", + "description": "", + "meta": {}, + "data_type": "VARCHAR", + "constraints": [], + "quote": null, + "tags": [] + }, + "price_cents": { + "name": "price_cents", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.waffle_types", + "root_path": null, + "depends_on": { + "macros": [] + } + } + }, + "sources": { + "source.waffle_shop.raw_customers": { + "database": null, + "schema": "", + "name": "raw_customers", + "resource_type": "source", + "package_name": "waffle_shop", + "path": "sources/raw.yml", + "original_file_path": "sources/raw.yml", + "unique_id": "source.waffle_shop.raw_customers", + "fqn": [ + "waffle_shop", + "raw_customers" + ], + "source_name": "raw_customers", + "source_description": "", + "loader": "", + "identifier": "raw_customers", + "quoting": {}, + "loaded_at_field": null, + "freshness": null, + "external": null, + "description": "Test source raw_customers.", + "columns": { + "id": { + "name": "id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "first_name": { + "name": "first_name", + "description": "", + "meta": {}, + "data_type": "VARCHAR", + "constraints": [], + "quote": null, + "tags": [] + }, + "last_name": { + "name": "last_name", + "description": "", + "meta": {}, + "data_type": "VARCHAR", + "constraints": [], + "quote": null, + "tags": [] + }, + "email": { + "name": "email", + "description": "", + "meta": {}, + "data_type": "VARCHAR", + "constraints": [], + "quote": null, + "tags": [] + }, + "created_at": { + "name": "created_at", + "description": "", + "meta": {}, + "data_type": "TIMESTAMP", + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": { + "sqlbuild": { + "loader": "raw_customers", + "auto_load": false, + "write_strategy": "table" + } + }, + "source_meta": {}, + "tags": [], + "config": {}, + "patch_path": null, + "unrendered_config": {}, + "relation_name": null + }, + "source.waffle_shop.raw_orders": { + "database": null, + "schema": "", + "name": "raw_orders", + "resource_type": "source", + "package_name": "waffle_shop", + "path": "sources/raw.yml", + "original_file_path": "sources/raw.yml", + "unique_id": "source.waffle_shop.raw_orders", + "fqn": [ + "waffle_shop", + "raw_orders" + ], + "source_name": "raw_orders", + "source_description": "", + "loader": "", + "identifier": "raw_orders", + "quoting": {}, + "loaded_at_field": null, + "freshness": null, + "external": null, + "description": "Test source raw_orders.", + "columns": { + "id": { + "name": "id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "customer_id": { + "name": "customer_id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "waffle_type_id": { + "name": "waffle_type_id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "quantity": { + "name": "quantity", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "ordered_at": { + "name": "ordered_at", + "description": "", + "meta": {}, + "data_type": "TIMESTAMP", + "constraints": [], + "quote": null, + "tags": [] + }, + "status": { + "name": "status", + "description": "", + "meta": {}, + "data_type": "VARCHAR", + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": { + "sqlbuild": { + "loader": "raw_orders", + "auto_load": false, + "write_strategy": "delete_insert", + "cursor_column": "ordered_at" + } + }, + "source_meta": {}, + "tags": [], + "config": {}, + "patch_path": null, + "unrendered_config": {}, + "relation_name": null + }, + "source.waffle_shop.raw_payments": { + "database": null, + "schema": "", + "name": "raw_payments", + "resource_type": "source", + "package_name": "waffle_shop", + "path": "sources/raw.yml", + "original_file_path": "sources/raw.yml", + "unique_id": "source.waffle_shop.raw_payments", + "fqn": [ + "waffle_shop", + "raw_payments" + ], + "source_name": "raw_payments", + "source_description": "", + "loader": "", + "identifier": "raw_payments", + "quoting": {}, + "loaded_at_field": null, + "freshness": null, + "external": null, + "description": "Test source raw_payments.", + "columns": { + "amount_cents": { + "name": "amount_cents", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": {}, + "source_meta": {}, + "tags": [], + "config": {}, + "patch_path": null, + "unrendered_config": {}, + "relation_name": null + } + }, + "macros": { + "macro.waffle_shop.line_total_cents": { + "name": "line_total_cents", + "resource_type": "macro", + "package_name": "waffle_shop", + "path": "macros/currency.py", + "original_file_path": "macros/currency.py", + "unique_id": "macro.waffle_shop.line_total_cents", + "macro_sql": "\"\"\"Currency formatting macros.\"\"\"\n\n\ndef line_total_cents(price_cents: str, quantity: str) -> str:\n \"\"\"Calculate line total cents from a unit price and quantity.\"\"\"\n return f\"{price_cents} * {quantity}\"\n", + "depends_on": { + "macros": [] + }, + "description": "", + "meta": { + "sqlbuild_visibility": "global", + "sqlbuild_scope_path": null + }, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "arguments": [], + "supported_languages": null + }, + "macro.waffle_shop.timestamp_trunc": { + "name": "timestamp_trunc", + "resource_type": "macro", + "package_name": "waffle_shop", + "path": "models/marts/_macros/time.py", + "original_file_path": "models/marts/_macros/time.py", + "unique_id": "macro.waffle_shop.timestamp_trunc", + "macro_sql": "\"\"\"Time expression macros.\"\"\"\n\nfrom sqlbuild.compiler.compile.models import MacroContext\n\n\ndef timestamp_trunc(ctx: MacroContext, grain: str, expression: str) -> str:\n \"\"\"Render timestamp truncation for the active adapter.\"\"\"\n\n if ctx.adapter_name == \"bigquery\":\n return f\"TIMESTAMP_TRUNC({expression}, {grain.upper()})\"\n return f\"DATE_TRUNC('{grain}', {expression})\"\n", + "depends_on": { + "macros": [] + }, + "description": "", + "meta": { + "sqlbuild_visibility": "local", + "sqlbuild_scope_path": "models/marts" + }, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "arguments": [], + "supported_languages": null + } + }, + "exposures": {}, + "metrics": {}, + "groups": {}, + "selectors": {}, + "disabled": {}, + "parent_map": { + "model.waffle_shop.customer_status_snapshot": [ + "model.waffle_shop.fact_orders" + ], + "model.waffle_shop.order_status_index": [ + "model.waffle_shop.fact_orders" + ], + "model.waffle_shop.daily_activity_rollup": [ + "model.waffle_shop.hourly_order_activity" + ], + "model.waffle_shop.daily_order_partitioned": [ + "model.waffle_shop.stg_orders" + ], + "model.waffle_shop.daily_revenue": [ + "model.waffle_shop.stg_orders", + "model.waffle_shop.stg_payments" + ], + "model.waffle_shop.dim_customers": [ + "model.waffle_shop.stg_customers", + "model.waffle_shop.stg_orders", + "model.waffle_shop.stg_payments" + ], + "model.waffle_shop.fact_orders": [ + "model.waffle_shop.is_completed_order", + "model.waffle_shop.is_completed_order_py", + "model.waffle_shop.stg_orders", + "seed.waffle_shop.waffle_types", + "model.waffle_shop.stg_payments" + ], + "model.waffle_shop.hourly_activity_with_daily_context": [ + "model.waffle_shop.hourly_order_activity", + "model.waffle_shop.daily_activity_rollup" + ], + "model.waffle_shop.hourly_order_activity": [ + "model.waffle_shop.fact_orders" + ], + "model.waffle_shop.scenario_order_prices": [ + "model.waffle_shop.stg_orders", + "seed.waffle_shop.waffle_types", + "model.waffle_shop.stg_payments" + ], + "model.waffle_shop.stg_customers": [ + "source.waffle_shop.raw_customers" + ], + "model.waffle_shop.stg_orders": [ + "source.waffle_shop.raw_orders" + ], + "model.waffle_shop.stg_payments": [ + "source.waffle_shop.raw_payments" + ], + "source.waffle_shop.raw_customers": [], + "source.waffle_shop.raw_orders": [], + "source.waffle_shop.raw_payments": [], + "seed.waffle_shop.waffle_types": [], + "model.waffle_shop.customer_orders": [ + "model.waffle_shop.fact_orders" + ], + "model.waffle_shop.is_completed_order": [], + "model.waffle_shop.is_completed_order_py": [] + }, + "group_map": {}, + "docs": {}, + "saved_queries": {}, + "semantic_models": {}, + "unit_tests": {} + } +} diff --git a/tests/goldens/compiler/seed/0-postgres.json b/tests/goldens/compiler/seed/0-postgres.json new file mode 100644 index 0000000000..13f05d651d --- /dev/null +++ b/tests/goldens/compiler/seed/0-postgres.json @@ -0,0 +1,2906 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 0, + "diagnostics": [] + } + ], + "compiled": { + "audits/generic/checked_stg_fulfillment_1/accepted_values__status.sql": [ + "SELECT status", + "FROM __ref(\"checked_stg_fulfillment_1\")", + "WHERE status IS NOT NULL", + " AND status NOT IN ('placed', 'expédié', '取消', 'returned ✓')", + "" + ], + "audits/generic/checked_stg_fulfillment_1/expression_is_true.sql": [ + "SELECT *", + "FROM __ref(\"checked_stg_fulfillment_1\")", + "WHERE NOT (amount >= -1000)", + "" + ], + "audits/generic/checked_stg_fulfillment_1/relationships__id.sql": [ + "SELECT id", + "FROM __ref(\"checked_stg_fulfillment_1\")", + "WHERE id IS NOT NULL", + " AND id NOT IN (", + " SELECT id", + " FROM __ref(\"stg_fulfillment_1\")", + " WHERE id IS NOT NULL", + " )", + "" + ], + "audits/generic/fulfillment_summary_1/not_null__id.sql": [ + "SELECT id", + "FROM __ref(\"fulfillment_summary_1\")", + "WHERE id IS NULL", + "" + ], + "audits/generic/sales_summary_0/not_null__id.sql": [ + "SELECT id", + "FROM __ref(\"sales_summary_0\")", + "WHERE id IS NULL", + "" + ], + "audits/generic/sales_summary_2/not_null__id.sql": [ + "SELECT id", + "FROM __ref(\"sales_summary_2\")", + "WHERE id IS NULL", + "" + ], + "audits/generic/stg_fulfillment_0/not_null__id.sql": [ + "SELECT id", + "FROM __ref(\"stg_fulfillment_0\")", + "WHERE id IS NULL", + "" + ], + "audits/generic/stg_sales_0/not_null__id.sql": [ + "SELECT id", + "FROM __ref(\"stg_sales_0\")", + "WHERE id IS NULL", + "" + ], + "audits/generic/stg_sales_1/not_null__id.sql": [ + "SELECT id", + "FROM __ref(\"stg_sales_1\")", + "WHERE id IS NULL", + "" + ], + "functions/sql/scaled_amount.sql": [ + "CREATE OR REPLACE FUNCTION analytics.scaled_amount(p_amount DOUBLE, p_factor INTEGER)", + "RETURNS DOUBLE", + "LANGUAGE SQL AS $$", + "p_amount * p_factor", + "$$", + "" + ], + "functions/sql/table_fn__amount_bands.sql": [ + "CREATE OR REPLACE FUNCTION analytics.table_fn__amount_bands(p_floor DOUBLE)", + "RETURNS TABLE (id INTEGER, band VARCHAR)", + "LANGUAGE SQL AS $$", + "SELECT CAST(id AS INTEGER) AS id,", + " CASE WHEN amount > p_floor THEN 'high' ELSE 'low' END AS band", + "FROM __ref(\"stg_fulfillment_0\")", + "$$", + "" + ], + "models/analysis/banded_amounts.sql": [ + "SELECT bands.id, bands.band", + "FROM __table_fn(\"table_fn__amount_bands\")(2.5) AS bands", + "" + ], + "models/analysis/chain_1_stg_sales_0.sql": [ + "SELECT id, label, amount", + "FROM __ref(\"contracted_stg_sales_0\")", + "" + ], + "models/analysis/chain_2_stg_sales_0.sql": [ + "SELECT id, UPPER(label) AS label, amount", + "FROM __ref(\"chain_1_stg_sales_0\")", + "" + ], + "models/analysis/chain_3_stg_sales_0.sql": [ + "SELECT id, label, amount", + "FROM __ref(\"chain_2_stg_sales_0\")", + "" + ], + "models/analysis/checked_stg_fulfillment_1.sql": [ + "SELECT id, amount, status", + "FROM __ref(\"stg_fulfillment_1\")", + "" + ], + "models/analysis/combined_stg_fulfillment_0.sql": [ + "WITH recent AS (", + " SELECT id, created_at FROM __ref(\"stg_fulfillment_0\") WHERE amount > 0", + "),", + "older AS (", + " SELECT id, created_at FROM __ref(\"stg_sales_1\")", + ")", + "SELECT id, created_at FROM recent", + "UNION ALL", + "SELECT id, created_at FROM older", + "" + ], + "models/analysis/contracted_stg_sales_0.sql": [ + "SELECT CAST(id AS INTEGER) AS id, CAST(status AS VARCHAR(19)) AS label,", + " amount", + "FROM __source(\"raw_events_0\")", + "" + ], + "models/analysis/every_open_event.sql": [ + "SELECT *", + "FROM __source(\"open_events\")", + "" + ], + "models/analysis/every_raw_events_0.sql": [ + "SELECT *", + "FROM __source(\"raw_events_0\")", + "" + ], + "models/analysis/every_stg_fulfillment_0.sql": [ + "SELECT *", + "FROM __ref(\"stg_fulfillment_0\")", + "" + ], + "models/analysis/passed_stg_fulfillment_0.sql": [ + "WITH passthrough AS (", + " SELECT id, created_at FROM __ref(\"stg_fulfillment_0\")", + ")", + "SELECT id, created_at", + "FROM passthrough", + "" + ], + "models/analysis/regional_stg_fulfillment_1.sql": [ + "SELECT id, amount, 'east' AS region", + "FROM __ref(\"stg_fulfillment_1\")", + "" + ], + "models/analysis/scaled_stg_fulfillment_0.sql": [ + "SELECT id, __udf(\"scaled_amount\")(amount, 2) AS scaled_amount", + "FROM __ref(\"stg_fulfillment_0\")", + "" + ], + "models/analysis/typed_stg_fulfillment_0.sql": [ + "SELECT id, amount * 2 AS doubled_amount, CAST(status AS VARCHAR) AS status_text,", + " created_at, NULL AS missing_note", + "FROM __ref(\"stg_fulfillment_0\")", + "" + ], + "models/analysis/unanalysed_stg_fulfillment_0.sql": [ + "SELECT id, amount", + "FROM __ref(\"stg_fulfillment_0\")", + "WHERE id = ANY [1, 2]", + "" + ], + "models/fulfillment/marts/fulfillment_summary_0.sql": [ + "SELECT", + " t.id,", + " t.amount,", + " t.status,", + " t.created_at,", + " (t.amount * (1 - 1e-07)) AS adjust_2_amount,", + " j2.label AS lookup_label_2", + "FROM __ref(\"stg_fulfillment_0\") t", + "LEFT JOIN __ref(\"stg_sales_0\") j1 ON t.id = j1.id", + "LEFT JOIN __seed(\"lookup_1\") j2 ON t.id = j2.id", + "" + ], + "models/fulfillment/marts/fulfillment_summary_1.sql": [ + "SELECT", + " t.id,", + " t.amount,", + " t.status,", + " t.created_at", + "FROM __ref(\"stg_fulfillment_0\") t", + "" + ], + "models/fulfillment/marts/fulfillment_summary_2.sql": [ + "SELECT", + " t.id,", + " t.amount,", + " t.status,", + " t.created_at,", + " (t.amount * (1 - 1e-07)) AS adjust_2_amount,", + " j1.label AS lookup_label_1,", + " 'Zürich – depot' AS site_label", + "FROM __ref(\"stg_fulfillment_1\") t", + "LEFT JOIN __seed(\"lookup_0\") j1 ON t.id = j1.id", + "" + ], + "models/fulfillment/staging/stg_fulfillment_0.sql": [ + "SELECT", + " t.id,", + " t.amount,", + " t.status,", + " t.created_at,", + " (t.amount + LENGTH('east')) AS adjust_1_amount,", + " 'Zürich – depot' AS site_label", + "FROM __source(\"raw_events_1\") t", + "" + ], + "models/fulfillment/staging/stg_fulfillment_1.sql": [ + "SELECT", + " t.id,", + " t.amount,", + " t.status,", + " t.created_at,", + " 'Ελληνικά orders' AS threshold_0_label,", + " ROUND(ROUND((t.amount * 2), 2), 2) AS adjust_0_amount", + "FROM __source(\"raw_events_0\") t", + "WHERE t.status <> 'expédié'", + "" + ], + "models/sales/marts/sales_summary_0.sql": [ + "SELECT", + " t.id,", + " t.amount,", + " t.status,", + " t.created_at,", + " 'Ελληνικά orders' AS threshold_0_label,", + " j1.label AS lookup_label_1", + "FROM __ref(\"stg_sales_0\") t", + "LEFT JOIN __seed(\"lookup_1\") j1 ON t.id = j1.id", + "" + ], + "models/sales/marts/sales_summary_1.sql": [ + "SELECT", + " t.id,", + " t.amount,", + " t.status,", + " t.created_at,", + " (t.amount * (1 - 1e-07)) AS adjust_2_amount,", + " j1.label AS lookup_label_1", + "FROM __ref(\"stg_sales_0\") t", + "LEFT JOIN __seed(\"lookup_0\") j1 ON t.id = j1.id", + "WHERE t.status <> '取消'", + "" + ], + "models/sales/marts/sales_summary_2.sql": [ + "SELECT", + " t.id,", + " t.amount,", + " t.status,", + " t.created_at,", + " 'naïve résumé' AS threshold_1_label,", + " ROUND(ROUND((t.amount * 2), 2), 2) AS adjust_0_amount,", + " j2.label AS lookup_label_2", + "FROM __ref(\"stg_sales_1\") t", + "LEFT JOIN __ref(\"stg_fulfillment_0\") j1 ON t.id = j1.id", + "LEFT JOIN __seed(\"lookup_1\") j2 ON t.id = j2.id", + "" + ], + "models/sales/staging/stg_sales_0.sql": [ + "SELECT", + " t.id,", + " t.amount,", + " t.status,", + " t.created_at,", + " t.amount * '1e-07' AS threshold_2_amount,", + " 'Zürich – depot' AS site_label", + "FROM __source(\"raw_events_0\") t", + "" + ], + "models/sales/staging/stg_sales_1.sql": [ + "SELECT", + " t.id,", + " t.amount,", + " t.status,", + " t.created_at,", + " 'naïve résumé' AS threshold_1_label", + "FROM __source(\"raw_events_1\") t", + "" + ], + "tests/regional_stg_fulfillment_1/test_regional_stg_fulfillment_1.sql": [ + "WITH __ref__stg_fulfillment_1 AS (SELECT 1 AS id, CAST(4 AS DOUBLE) AS amount, 'placed' AS status,", + " TIMESTAMP '2026-01-01 00:00:00' AS created_at),", + "__ref__regional_stg_fulfillment_1 AS (SELECT id, amount, 'west' AS region", + "FROM __ref__stg_fulfillment_1),", + "__actual__regional_stg_fulfillment_1 AS (SELECT * FROM __ref__regional_stg_fulfillment_1),", + "__expected__regional_stg_fulfillment_1 AS (SELECT 1 AS id, CAST(4 AS DOUBLE) AS amount, 'west' AS region)", + "SELECT 0 AS step_index, 'regional_stg_fulfillment_1' AS model_name, (SELECT COUNT(*) FROM __actual__regional_stg_fulfillment_1) AS actual_count, (SELECT COUNT(*) FROM __expected__regional_stg_fulfillment_1) AS expected_count, (SELECT COUNT(*) FROM (SELECT id, amount, region FROM __actual__regional_stg_fulfillment_1 EXCEPT SELECT id, amount, region FROM __expected__regional_stg_fulfillment_1) AS __sqlbuild_mismatch) AS unexpected_count, (SELECT COUNT(*) FROM (SELECT id, amount, region FROM __expected__regional_stg_fulfillment_1 EXCEPT SELECT id, amount, region FROM __actual__regional_stg_fulfillment_1) AS __sqlbuild_missing) AS missing_count", + "" + ], + "tests/stg_fulfillment_0/test_stg_fulfillment_0.sql": [ + "WITH __source__raw_events_1 AS (SELECT 1 AS id, CAST(10 AS DOUBLE) AS amount, 'placed' AS status,", + " TIMESTAMP '2026-01-01 00:00:00' AS created_at),", + "__ref__stg_fulfillment_0 AS (SELECT", + " t.id,", + " t.amount,", + " t.status,", + " t.created_at,", + " (t.amount + LENGTH('east')) AS adjust_1_amount,", + " 'Zürich – depot' AS site_label", + "FROM __source__raw_events_1 t),", + "__actual__stg_fulfillment_0 AS (SELECT * FROM __ref__stg_fulfillment_0),", + "__expected__stg_fulfillment_0 AS (SELECT 1 AS id, 'placed' AS status)", + "SELECT 0 AS step_index, 'stg_fulfillment_0' AS model_name, (SELECT COUNT(*) FROM __actual__stg_fulfillment_0) AS actual_count, (SELECT COUNT(*) FROM __expected__stg_fulfillment_0) AS expected_count, (SELECT COUNT(*) FROM (SELECT id, status FROM __actual__stg_fulfillment_0 EXCEPT SELECT id, status FROM __expected__stg_fulfillment_0) AS __sqlbuild_mismatch) AS unexpected_count, (SELECT COUNT(*) FROM (SELECT id, status FROM __expected__stg_fulfillment_0 EXCEPT SELECT id, status FROM __actual__stg_fulfillment_0) AS __sqlbuild_missing) AS missing_count", + "" + ] + }, + "manifest": { + "nodes": { + "model.generated_0.banded_amounts": { + "database": null, + "schema": "analytics", + "name": "banded_amounts", + "resource_type": "model", + "package_name": "generated_0", + "path": "models/analysis/banded_amounts.sql", + "original_file_path": "models/analysis/banded_amounts.sql", + "unique_id": "model.generated_0.banded_amounts", + "fqn": [ + "generated_0", + "models", + "analysis", + "banded_amounts" + ], + "alias": "banded_amounts", + "config": { + "enabled": true, + "alias": null, + "schema": "analytics", + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "table", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Amount bands from a table function", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "analytics.banded_amounts", + "depends_on": { + "macros": [], + "nodes": [ + "model.generated_0.table_fn__amount_bands" + ] + } + }, + "model.generated_0.chain_1_stg_sales_0": { + "database": null, + "schema": "analytics", + "name": "chain_1_stg_sales_0", + "resource_type": "model", + "package_name": "generated_0", + "path": "models/analysis/chain_1_stg_sales_0.sql", + "original_file_path": "models/analysis/chain_1_stg_sales_0.sql", + "unique_id": "model.generated_0.chain_1_stg_sales_0", + "fqn": [ + "generated_0", + "models", + "analysis", + "chain_1_stg_sales_0" + ], + "alias": "chain_1_stg_sales_0", + "config": { + "enabled": true, + "alias": null, + "schema": "analytics", + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "table", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Level 1 below the contracted copy of stg_sales_0", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "analytics.chain_1_stg_sales_0", + "depends_on": { + "macros": [], + "nodes": [ + "model.generated_0.contracted_stg_sales_0" + ] + } + }, + "model.generated_0.chain_2_stg_sales_0": { + "database": null, + "schema": "analytics", + "name": "chain_2_stg_sales_0", + "resource_type": "model", + "package_name": "generated_0", + "path": "models/analysis/chain_2_stg_sales_0.sql", + "original_file_path": "models/analysis/chain_2_stg_sales_0.sql", + "unique_id": "model.generated_0.chain_2_stg_sales_0", + "fqn": [ + "generated_0", + "models", + "analysis", + "chain_2_stg_sales_0" + ], + "alias": "chain_2_stg_sales_0", + "config": { + "enabled": true, + "alias": null, + "schema": "analytics", + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "table", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Level 2 below the contracted copy of stg_sales_0", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "analytics.chain_2_stg_sales_0", + "depends_on": { + "macros": [], + "nodes": [ + "model.generated_0.chain_1_stg_sales_0" + ] + } + }, + "model.generated_0.chain_3_stg_sales_0": { + "database": null, + "schema": "analytics", + "name": "chain_3_stg_sales_0", + "resource_type": "model", + "package_name": "generated_0", + "path": "models/analysis/chain_3_stg_sales_0.sql", + "original_file_path": "models/analysis/chain_3_stg_sales_0.sql", + "unique_id": "model.generated_0.chain_3_stg_sales_0", + "fqn": [ + "generated_0", + "models", + "analysis", + "chain_3_stg_sales_0" + ], + "alias": "chain_3_stg_sales_0", + "config": { + "enabled": true, + "alias": null, + "schema": "analytics", + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "table", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Level 3 below the contracted copy of stg_sales_0", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "analytics.chain_3_stg_sales_0", + "depends_on": { + "macros": [], + "nodes": [ + "model.generated_0.chain_2_stg_sales_0" + ] + } + }, + "model.generated_0.checked_stg_fulfillment_1": { + "database": null, + "schema": "analytics", + "name": "checked_stg_fulfillment_1", + "resource_type": "model", + "package_name": "generated_0", + "path": "models/analysis/checked_stg_fulfillment_1.sql", + "original_file_path": "models/analysis/checked_stg_fulfillment_1.sql", + "unique_id": "model.generated_0.checked_stg_fulfillment_1", + "fqn": [ + "generated_0", + "models", + "analysis", + "checked_stg_fulfillment_1" + ], + "alias": "checked_stg_fulfillment_1", + "config": { + "enabled": true, + "alias": null, + "schema": "analytics", + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "table", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": [ + "id" + ], + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Metadata-checked copy of stg_fulfillment_1", + "columns": { + "id": { + "name": "id", + "description": "", + "meta": {}, + "data_type": null, + "constraints": [], + "quote": null, + "tags": [] + }, + "status": { + "name": "status", + "description": "", + "meta": {}, + "data_type": null, + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "analytics.checked_stg_fulfillment_1", + "depends_on": { + "macros": [], + "nodes": [ + "model.generated_0.stg_fulfillment_1" + ] + } + }, + "model.generated_0.combined_stg_fulfillment_0": { + "database": null, + "schema": "analytics", + "name": "combined_stg_fulfillment_0", + "resource_type": "model", + "package_name": "generated_0", + "path": "models/analysis/combined_stg_fulfillment_0.sql", + "original_file_path": "models/analysis/combined_stg_fulfillment_0.sql", + "unique_id": "model.generated_0.combined_stg_fulfillment_0", + "fqn": [ + "generated_0", + "models", + "analysis", + "combined_stg_fulfillment_0" + ], + "alias": "combined_stg_fulfillment_0", + "config": { + "enabled": true, + "alias": null, + "schema": "analytics", + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "table", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Recent and older rows of stg_fulfillment_0 and stg_sales_1", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "analytics.combined_stg_fulfillment_0", + "depends_on": { + "macros": [], + "nodes": [ + "model.generated_0.stg_fulfillment_0", + "model.generated_0.stg_sales_1" + ] + } + }, + "model.generated_0.contracted_stg_sales_0": { + "database": null, + "schema": "analytics", + "name": "contracted_stg_sales_0", + "resource_type": "model", + "package_name": "generated_0", + "path": "models/analysis/contracted_stg_sales_0.sql", + "original_file_path": "models/analysis/contracted_stg_sales_0.sql", + "unique_id": "model.generated_0.contracted_stg_sales_0", + "fqn": [ + "generated_0", + "models", + "analysis", + "contracted_stg_sales_0" + ], + "alias": "contracted_stg_sales_0", + "config": { + "enabled": true, + "alias": null, + "schema": "analytics", + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "table", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Contracted copy of raw_events_0", + "columns": { + "id": { + "name": "id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "label": { + "name": "label", + "description": "", + "meta": {}, + "data_type": "VARCHAR(19)", + "constraints": [], + "quote": null, + "tags": [] + }, + "amount": { + "name": "amount", + "description": "", + "meta": {}, + "data_type": "DOUBLE", + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "analytics.contracted_stg_sales_0", + "depends_on": { + "macros": [], + "nodes": [ + "source.generated_0.raw_events_0" + ] + } + }, + "model.generated_0.every_open_event": { + "database": null, + "schema": "analytics", + "name": "every_open_event", + "resource_type": "model", + "package_name": "generated_0", + "path": "models/analysis/every_open_event.sql", + "original_file_path": "models/analysis/every_open_event.sql", + "unique_id": "model.generated_0.every_open_event", + "fqn": [ + "generated_0", + "models", + "analysis", + "every_open_event" + ], + "alias": "every_open_event", + "config": { + "enabled": true, + "alias": null, + "schema": "analytics", + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "table", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Every column of an open source", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "analytics.every_open_event", + "depends_on": { + "macros": [], + "nodes": [ + "source.generated_0.open_events" + ] + } + }, + "model.generated_0.every_raw_events_0": { + "database": null, + "schema": "analytics", + "name": "every_raw_events_0", + "resource_type": "model", + "package_name": "generated_0", + "path": "models/analysis/every_raw_events_0.sql", + "original_file_path": "models/analysis/every_raw_events_0.sql", + "unique_id": "model.generated_0.every_raw_events_0", + "fqn": [ + "generated_0", + "models", + "analysis", + "every_raw_events_0" + ], + "alias": "every_raw_events_0", + "config": { + "enabled": true, + "alias": null, + "schema": "analytics", + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "table", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Every column of raw_events_0", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "analytics.every_raw_events_0", + "depends_on": { + "macros": [], + "nodes": [ + "source.generated_0.raw_events_0" + ] + } + }, + "model.generated_0.every_stg_fulfillment_0": { + "database": null, + "schema": "analytics", + "name": "every_stg_fulfillment_0", + "resource_type": "model", + "package_name": "generated_0", + "path": "models/analysis/every_stg_fulfillment_0.sql", + "original_file_path": "models/analysis/every_stg_fulfillment_0.sql", + "unique_id": "model.generated_0.every_stg_fulfillment_0", + "fqn": [ + "generated_0", + "models", + "analysis", + "every_stg_fulfillment_0" + ], + "alias": "every_stg_fulfillment_0", + "config": { + "enabled": true, + "alias": null, + "schema": "analytics", + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "table", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Every column of stg_fulfillment_0", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "analytics.every_stg_fulfillment_0", + "depends_on": { + "macros": [], + "nodes": [ + "model.generated_0.stg_fulfillment_0" + ] + } + }, + "model.generated_0.passed_stg_fulfillment_0": { + "database": null, + "schema": "analytics", + "name": "passed_stg_fulfillment_0", + "resource_type": "model", + "package_name": "generated_0", + "path": "models/analysis/passed_stg_fulfillment_0.sql", + "original_file_path": "models/analysis/passed_stg_fulfillment_0.sql", + "unique_id": "model.generated_0.passed_stg_fulfillment_0", + "fqn": [ + "generated_0", + "models", + "analysis", + "passed_stg_fulfillment_0" + ], + "alias": "passed_stg_fulfillment_0", + "config": { + "enabled": true, + "alias": null, + "schema": "analytics", + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "table", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Columns of stg_fulfillment_0 passed through a CTE", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "analytics.passed_stg_fulfillment_0", + "depends_on": { + "macros": [], + "nodes": [ + "model.generated_0.stg_fulfillment_0" + ] + } + }, + "model.generated_0.regional_stg_fulfillment_1": { + "database": null, + "schema": "analytics", + "name": "regional_stg_fulfillment_1", + "resource_type": "model", + "package_name": "generated_0", + "path": "models/analysis/regional_stg_fulfillment_1.sql", + "original_file_path": "models/analysis/regional_stg_fulfillment_1.sql", + "unique_id": "model.generated_0.regional_stg_fulfillment_1", + "fqn": [ + "generated_0", + "models", + "analysis", + "regional_stg_fulfillment_1" + ], + "alias": "regional_stg_fulfillment_1", + "config": { + "enabled": true, + "alias": null, + "schema": "analytics", + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "table", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Regional copy of stg_fulfillment_1", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "analytics.regional_stg_fulfillment_1", + "depends_on": { + "macros": [], + "nodes": [ + "model.generated_0.stg_fulfillment_1" + ] + } + }, + "model.generated_0.scaled_stg_fulfillment_0": { + "database": null, + "schema": "analytics", + "name": "scaled_stg_fulfillment_0", + "resource_type": "model", + "package_name": "generated_0", + "path": "models/analysis/scaled_stg_fulfillment_0.sql", + "original_file_path": "models/analysis/scaled_stg_fulfillment_0.sql", + "unique_id": "model.generated_0.scaled_stg_fulfillment_0", + "fqn": [ + "generated_0", + "models", + "analysis", + "scaled_stg_fulfillment_0" + ], + "alias": "scaled_stg_fulfillment_0", + "config": { + "enabled": true, + "alias": null, + "schema": "analytics", + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "table", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Scaled amounts of stg_fulfillment_0", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "analytics.scaled_stg_fulfillment_0", + "depends_on": { + "macros": [], + "nodes": [ + "model.generated_0.scaled_amount", + "model.generated_0.stg_fulfillment_0" + ] + } + }, + "model.generated_0.typed_stg_fulfillment_0": { + "database": null, + "schema": "analytics", + "name": "typed_stg_fulfillment_0", + "resource_type": "model", + "package_name": "generated_0", + "path": "models/analysis/typed_stg_fulfillment_0.sql", + "original_file_path": "models/analysis/typed_stg_fulfillment_0.sql", + "unique_id": "model.generated_0.typed_stg_fulfillment_0", + "fqn": [ + "generated_0", + "models", + "analysis", + "typed_stg_fulfillment_0" + ], + "alias": "typed_stg_fulfillment_0", + "config": { + "enabled": true, + "alias": null, + "schema": "analytics", + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "table", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Typed and untyped outputs of stg_fulfillment_0", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "analytics.typed_stg_fulfillment_0", + "depends_on": { + "macros": [], + "nodes": [ + "model.generated_0.stg_fulfillment_0" + ] + } + }, + "model.generated_0.unanalysed_stg_fulfillment_0": { + "database": null, + "schema": "analytics", + "name": "unanalysed_stg_fulfillment_0", + "resource_type": "model", + "package_name": "generated_0", + "path": "models/analysis/unanalysed_stg_fulfillment_0.sql", + "original_file_path": "models/analysis/unanalysed_stg_fulfillment_0.sql", + "unique_id": "model.generated_0.unanalysed_stg_fulfillment_0", + "fqn": [ + "generated_0", + "models", + "analysis", + "unanalysed_stg_fulfillment_0" + ], + "alias": "unanalysed_stg_fulfillment_0", + "config": { + "enabled": true, + "alias": null, + "schema": "analytics", + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "table", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Copy of stg_fulfillment_0 that SQL analysis cannot parse", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "analytics.unanalysed_stg_fulfillment_0", + "depends_on": { + "macros": [], + "nodes": [ + "model.generated_0.stg_fulfillment_0" + ] + } + }, + "model.generated_0.fulfillment_summary_0": { + "database": null, + "schema": "analytics", + "name": "fulfillment_summary_0", + "resource_type": "model", + "package_name": "generated_0", + "path": "models/fulfillment/marts/fulfillment_summary_0.sql", + "original_file_path": "models/fulfillment/marts/fulfillment_summary_0.sql", + "unique_id": "model.generated_0.fulfillment_summary_0", + "fqn": [ + "generated_0", + "models", + "fulfillment", + "marts", + "fulfillment_summary_0" + ], + "alias": "fulfillment_summary_0", + "config": { + "enabled": true, + "alias": null, + "schema": "analytics", + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "table", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Summary of fulfillment activity 0", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "analytics.fulfillment_summary_0", + "depends_on": { + "macros": [], + "nodes": [ + "model.generated_0.stg_fulfillment_0", + "model.generated_0.stg_sales_0", + "seed.generated_0.lookup_1" + ] + } + }, + "model.generated_0.fulfillment_summary_1": { + "database": null, + "schema": "analytics", + "name": "fulfillment_summary_1", + "resource_type": "model", + "package_name": "generated_0", + "path": "models/fulfillment/marts/fulfillment_summary_1.sql", + "original_file_path": "models/fulfillment/marts/fulfillment_summary_1.sql", + "unique_id": "model.generated_0.fulfillment_summary_1", + "fqn": [ + "generated_0", + "models", + "fulfillment", + "marts", + "fulfillment_summary_1" + ], + "alias": "fulfillment_summary_1", + "config": { + "enabled": true, + "alias": null, + "schema": "analytics", + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "table", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [ + { + "sql": "SELECT 'analytics.fulfillment_summary_1' AS refreshed_relation, 'Ελληνικά orders' AS refresh_label", + "transaction": true, + "index": 0 + }, + { + "sql": "SELECT 'analytics.fulfillment_summary_1' AS refreshed", + "transaction": true, + "index": 1 + } + ], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Summary of fulfillment activity 1", + "columns": { + "id": { + "name": "id", + "description": "", + "meta": {}, + "data_type": null, + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": { + "sqlbuild": { + "lifecycle_hooks": { + "post_hooks": [ + { + "type": "sql", + "statement": "SELECT 'analytics.fulfillment_summary_1' AS refreshed_relation, 'Ελληνικά orders' AS refresh_label", + "name": "record_refresh", + "relative_path": "hooks/sql/record_refresh.sql", + "definition_sql": "SELECT @'relation' AS refreshed_relation, @'label' AS refresh_label", + "kwargs": { + "relation": "@@CTX:destination.qualified", + "label": "Ελληνικά orders" + }, + "description": "Record a refresh for one relation" + }, + { + "type": "sql", + "statement": "SELECT 'analytics.fulfillment_summary_1' AS refreshed" + } + ] + } + } + }, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "analytics.fulfillment_summary_1", + "depends_on": { + "macros": [], + "nodes": [ + "model.generated_0.stg_fulfillment_0" + ] + } + }, + "model.generated_0.fulfillment_summary_2": { + "database": null, + "schema": "analytics", + "name": "fulfillment_summary_2", + "resource_type": "model", + "package_name": "generated_0", + "path": "models/fulfillment/marts/fulfillment_summary_2.sql", + "original_file_path": "models/fulfillment/marts/fulfillment_summary_2.sql", + "unique_id": "model.generated_0.fulfillment_summary_2", + "fqn": [ + "generated_0", + "models", + "fulfillment", + "marts", + "fulfillment_summary_2" + ], + "alias": "fulfillment_summary_2", + "config": { + "enabled": true, + "alias": null, + "schema": "analytics", + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "table", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [ + { + "sql": "SELECT 'analytics.fulfillment_summary_2' AS refreshed_relation, 'café ☕ counter' AS refresh_label", + "transaction": true, + "index": 0 + }, + { + "sql": "SELECT 'analytics.fulfillment_summary_2' AS refreshed", + "transaction": true, + "index": 1 + } + ], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Summary of fulfillment activity 2 (naïve résumé)", + "columns": {}, + "meta": { + "sqlbuild": { + "lifecycle_hooks": { + "post_hooks": [ + { + "type": "sql", + "statement": "SELECT 'analytics.fulfillment_summary_2' AS refreshed_relation, 'café ☕ counter' AS refresh_label", + "name": "record_refresh", + "relative_path": "hooks/sql/record_refresh.sql", + "definition_sql": "SELECT @'relation' AS refreshed_relation, @'label' AS refresh_label", + "kwargs": { + "relation": "@@CTX:destination.qualified", + "label": "café ☕ counter" + }, + "description": "Record a refresh for one relation" + }, + { + "type": "sql", + "statement": "SELECT 'analytics.fulfillment_summary_2' AS refreshed" + } + ] + } + } + }, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "analytics.fulfillment_summary_2", + "depends_on": { + "macros": [], + "nodes": [ + "model.generated_0.stg_fulfillment_1", + "seed.generated_0.lookup_0" + ] + } + }, + "model.generated_0.stg_fulfillment_0": { + "database": null, + "schema": "analytics", + "name": "stg_fulfillment_0", + "resource_type": "model", + "package_name": "generated_0", + "path": "models/fulfillment/staging/stg_fulfillment_0.sql", + "original_file_path": "models/fulfillment/staging/stg_fulfillment_0.sql", + "unique_id": "model.generated_0.stg_fulfillment_0", + "fqn": [ + "generated_0", + "models", + "fulfillment", + "staging", + "stg_fulfillment_0" + ], + "alias": "stg_fulfillment_0", + "config": { + "enabled": true, + "alias": null, + "schema": "analytics", + "database": null, + "tags": [ + "staging" + ], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [ + "staging" + ], + "description": "Staged fulfillment events 0", + "columns": { + "id": { + "name": "id", + "description": "", + "meta": {}, + "data_type": null, + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "analytics.stg_fulfillment_0", + "depends_on": { + "macros": [], + "nodes": [ + "source.generated_0.raw_events_1" + ] + } + }, + "model.generated_0.stg_fulfillment_1": { + "database": null, + "schema": "analytics", + "name": "stg_fulfillment_1", + "resource_type": "model", + "package_name": "generated_0", + "path": "models/fulfillment/staging/stg_fulfillment_1.sql", + "original_file_path": "models/fulfillment/staging/stg_fulfillment_1.sql", + "unique_id": "model.generated_0.stg_fulfillment_1", + "fqn": [ + "generated_0", + "models", + "fulfillment", + "staging", + "stg_fulfillment_1" + ], + "alias": "stg_fulfillment_1", + "config": { + "enabled": true, + "alias": null, + "schema": "analytics", + "database": null, + "tags": [ + "staging" + ], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [ + "staging" + ], + "description": "Staged fulfillment events 1", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "analytics.stg_fulfillment_1", + "depends_on": { + "macros": [], + "nodes": [ + "source.generated_0.raw_events_0" + ] + } + }, + "model.generated_0.sales_summary_0": { + "database": null, + "schema": "analytics", + "name": "sales_summary_0", + "resource_type": "model", + "package_name": "generated_0", + "path": "models/sales/marts/sales_summary_0.sql", + "original_file_path": "models/sales/marts/sales_summary_0.sql", + "unique_id": "model.generated_0.sales_summary_0", + "fqn": [ + "generated_0", + "models", + "sales", + "marts", + "sales_summary_0" + ], + "alias": "sales_summary_0", + "config": { + "enabled": true, + "alias": null, + "schema": "analytics", + "database": null, + "tags": [ + "sales", + "marts" + ], + "meta": {}, + "group": null, + "materialized": "table", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [ + "sales", + "marts" + ], + "description": "Summary of sales activity 0", + "columns": { + "id": { + "name": "id", + "description": "", + "meta": {}, + "data_type": null, + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "analytics.sales_summary_0", + "depends_on": { + "macros": [], + "nodes": [ + "model.generated_0.stg_sales_0", + "seed.generated_0.lookup_1" + ] + } + }, + "model.generated_0.sales_summary_1": { + "database": null, + "schema": "analytics", + "name": "sales_summary_1", + "resource_type": "model", + "package_name": "generated_0", + "path": "models/sales/marts/sales_summary_1.sql", + "original_file_path": "models/sales/marts/sales_summary_1.sql", + "unique_id": "model.generated_0.sales_summary_1", + "fqn": [ + "generated_0", + "models", + "sales", + "marts", + "sales_summary_1" + ], + "alias": "sales_summary_1", + "config": { + "enabled": true, + "alias": null, + "schema": "analytics", + "database": null, + "tags": [ + "sales", + "marts" + ], + "meta": {}, + "group": null, + "materialized": "table", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [ + { + "sql": "SELECT 'analytics.sales_summary_1' AS refreshed_relation, '東京 warehouse' AS refresh_label", + "transaction": true, + "index": 0 + }, + { + "sql": "SELECT 'analytics.sales_summary_1' AS refreshed", + "transaction": true, + "index": 1 + } + ], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [ + "sales", + "marts" + ], + "description": "Summary of sales activity 1", + "columns": {}, + "meta": { + "sqlbuild": { + "lifecycle_hooks": { + "post_hooks": [ + { + "type": "sql", + "statement": "SELECT 'analytics.sales_summary_1' AS refreshed_relation, '東京 warehouse' AS refresh_label", + "name": "record_refresh", + "relative_path": "hooks/sql/record_refresh.sql", + "definition_sql": "SELECT @'relation' AS refreshed_relation, @'label' AS refresh_label", + "kwargs": { + "relation": "@@CTX:destination.qualified", + "label": "東京 warehouse" + }, + "description": "Record a refresh for one relation" + }, + { + "type": "sql", + "statement": "SELECT 'analytics.sales_summary_1' AS refreshed" + } + ] + } + } + }, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "analytics.sales_summary_1", + "depends_on": { + "macros": [], + "nodes": [ + "model.generated_0.stg_sales_0", + "seed.generated_0.lookup_0" + ] + } + }, + "model.generated_0.sales_summary_2": { + "database": null, + "schema": "analytics", + "name": "sales_summary_2", + "resource_type": "model", + "package_name": "generated_0", + "path": "models/sales/marts/sales_summary_2.sql", + "original_file_path": "models/sales/marts/sales_summary_2.sql", + "unique_id": "model.generated_0.sales_summary_2", + "fqn": [ + "generated_0", + "models", + "sales", + "marts", + "sales_summary_2" + ], + "alias": "sales_summary_2", + "config": { + "enabled": true, + "alias": null, + "schema": "analytics", + "database": null, + "tags": [ + "sales", + "marts" + ], + "meta": {}, + "group": null, + "materialized": "table", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [ + "sales", + "marts" + ], + "description": "Summary of sales activity 2 (Ελληνικά orders)", + "columns": { + "id": { + "name": "id", + "description": "", + "meta": {}, + "data_type": null, + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "analytics.sales_summary_2", + "depends_on": { + "macros": [], + "nodes": [ + "model.generated_0.stg_sales_1", + "model.generated_0.stg_fulfillment_0", + "seed.generated_0.lookup_1" + ] + } + }, + "model.generated_0.stg_sales_0": { + "database": null, + "schema": "analytics", + "name": "stg_sales_0", + "resource_type": "model", + "package_name": "generated_0", + "path": "models/sales/staging/stg_sales_0.sql", + "original_file_path": "models/sales/staging/stg_sales_0.sql", + "unique_id": "model.generated_0.stg_sales_0", + "fqn": [ + "generated_0", + "models", + "sales", + "staging", + "stg_sales_0" + ], + "alias": "stg_sales_0", + "config": { + "enabled": true, + "alias": null, + "schema": "analytics", + "database": null, + "tags": [ + "staging" + ], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [ + "staging" + ], + "description": "Staged sales events 0", + "columns": { + "id": { + "name": "id", + "description": "", + "meta": {}, + "data_type": null, + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "analytics.stg_sales_0", + "depends_on": { + "macros": [], + "nodes": [ + "source.generated_0.raw_events_0" + ] + } + }, + "model.generated_0.stg_sales_1": { + "database": null, + "schema": "analytics", + "name": "stg_sales_1", + "resource_type": "model", + "package_name": "generated_0", + "path": "models/sales/staging/stg_sales_1.sql", + "original_file_path": "models/sales/staging/stg_sales_1.sql", + "unique_id": "model.generated_0.stg_sales_1", + "fqn": [ + "generated_0", + "models", + "sales", + "staging", + "stg_sales_1" + ], + "alias": "stg_sales_1", + "config": { + "enabled": true, + "alias": null, + "schema": "analytics", + "database": null, + "tags": [ + "staging" + ], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [ + "staging" + ], + "description": "Staged sales events 1", + "columns": { + "id": { + "name": "id", + "description": "", + "meta": {}, + "data_type": null, + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "analytics.stg_sales_1", + "depends_on": { + "macros": [], + "nodes": [ + "source.generated_0.raw_events_1" + ] + } + }, + "seed.generated_0.lookup_0": { + "database": null, + "schema": "analytics", + "name": "lookup_0", + "resource_type": "seed", + "package_name": "generated_0", + "path": "seeds/lookup_0.csv", + "original_file_path": "seeds/lookup_0.csv", + "unique_id": "seed.generated_0.lookup_0", + "fqn": [ + "generated_0", + "seeds", + "lookup_0" + ], + "alias": "lookup_0", + "config": { + "enabled": true, + "alias": null, + "schema": "analytics", + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "seed", + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + }, + "delimiter": ",", + "quote_columns": null + }, + "tags": [], + "description": "Generated lookup 0.", + "columns": { + "id": { + "name": "id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "label": { + "name": "label", + "description": "", + "meta": {}, + "data_type": "VARCHAR", + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "analytics.lookup_0", + "root_path": null, + "depends_on": { + "macros": [] + } + }, + "seed.generated_0.lookup_1": { + "database": null, + "schema": "analytics", + "name": "lookup_1", + "resource_type": "seed", + "package_name": "generated_0", + "path": "seeds/lookup_1.csv", + "original_file_path": "seeds/lookup_1.csv", + "unique_id": "seed.generated_0.lookup_1", + "fqn": [ + "generated_0", + "seeds", + "lookup_1" + ], + "alias": "lookup_1", + "config": { + "enabled": true, + "alias": null, + "schema": "analytics", + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "seed", + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + }, + "delimiter": ",", + "quote_columns": null + }, + "tags": [], + "description": "Generated lookup 1.", + "columns": { + "id": { + "name": "id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "label": { + "name": "label", + "description": "", + "meta": {}, + "data_type": "VARCHAR", + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "analytics.lookup_1", + "root_path": null, + "depends_on": { + "macros": [] + } + } + }, + "sources": { + "source.generated_0.open_events": { + "database": null, + "schema": "", + "name": "open_events", + "resource_type": "source", + "package_name": "generated_0", + "path": "sources/open.yml", + "original_file_path": "sources/open.yml", + "unique_id": "source.generated_0.open_events", + "fqn": [ + "generated_0", + "open_events" + ], + "source_name": "open_events", + "source_description": "", + "loader": "", + "identifier": "open_events", + "quoting": {}, + "loaded_at_field": null, + "freshness": null, + "external": null, + "description": "Events whose columns are only known to the warehouse.", + "columns": {}, + "meta": {}, + "source_meta": {}, + "tags": [], + "config": {}, + "patch_path": null, + "unrendered_config": {}, + "relation_name": null + }, + "source.generated_0.raw_events_0": { + "database": null, + "schema": "", + "name": "raw_events_0", + "resource_type": "source", + "package_name": "generated_0", + "path": "sources/raw.yml", + "original_file_path": "sources/raw.yml", + "unique_id": "source.generated_0.raw_events_0", + "fqn": [ + "generated_0", + "raw_events_0" + ], + "source_name": "raw_events_0", + "source_description": "", + "loader": "", + "identifier": "raw_events_0", + "quoting": {}, + "loaded_at_field": null, + "freshness": null, + "external": null, + "description": "Generated event feed 0.", + "columns": { + "id": { + "name": "id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "amount": { + "name": "amount", + "description": "", + "meta": {}, + "data_type": "DOUBLE", + "constraints": [], + "quote": null, + "tags": [] + }, + "status": { + "name": "status", + "description": "", + "meta": {}, + "data_type": "VARCHAR", + "constraints": [], + "quote": null, + "tags": [] + }, + "created_at": { + "name": "created_at", + "description": "", + "meta": {}, + "data_type": "TIMESTAMP", + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": {}, + "source_meta": {}, + "tags": [], + "config": {}, + "patch_path": null, + "unrendered_config": {}, + "relation_name": null + }, + "source.generated_0.raw_events_1": { + "database": null, + "schema": "", + "name": "raw_events_1", + "resource_type": "source", + "package_name": "generated_0", + "path": "sources/raw.yml", + "original_file_path": "sources/raw.yml", + "unique_id": "source.generated_0.raw_events_1", + "fqn": [ + "generated_0", + "raw_events_1" + ], + "source_name": "raw_events_1", + "source_description": "", + "loader": "", + "identifier": "raw_events_1", + "quoting": {}, + "loaded_at_field": null, + "freshness": null, + "external": null, + "description": "Generated event feed 1.", + "columns": { + "id": { + "name": "id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "amount": { + "name": "amount", + "description": "", + "meta": {}, + "data_type": "DOUBLE", + "constraints": [], + "quote": null, + "tags": [] + }, + "status": { + "name": "status", + "description": "", + "meta": {}, + "data_type": "VARCHAR", + "constraints": [], + "quote": null, + "tags": [] + }, + "created_at": { + "name": "created_at", + "description": "", + "meta": {}, + "data_type": "TIMESTAMP", + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": {}, + "source_meta": {}, + "tags": [], + "config": {}, + "patch_path": null, + "unrendered_config": {}, + "relation_name": null + } + }, + "macros": { + "macro.generated_0.adjust_0": { + "name": "adjust_0", + "resource_type": "macro", + "package_name": "generated_0", + "path": "macros/adjust_0.py", + "original_file_path": "macros/adjust_0.py", + "unique_id": "macro.generated_0.adjust_0", + "macro_sql": "def adjust_0_round(expression: str) -> str:\n \"\"\"Round an amount to two places.\"\"\"\n return f\"ROUND({expression}, 2)\"\n\n\ndef adjust_0(expression: str) -> str:\n \"\"\"Double and round an amount.\"\"\"\n return adjust_0_round(f\"({expression} * 2)\")\n", + "depends_on": { + "macros": [ + "macro.generated_0.adjust_0_round" + ] + }, + "description": "", + "meta": { + "sqlbuild_visibility": "global", + "sqlbuild_scope_path": null + }, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "arguments": [], + "supported_languages": null + }, + "macro.generated_0.adjust_0_round": { + "name": "adjust_0_round", + "resource_type": "macro", + "package_name": "generated_0", + "path": "macros/adjust_0.py", + "original_file_path": "macros/adjust_0.py", + "unique_id": "macro.generated_0.adjust_0_round", + "macro_sql": "def adjust_0_round(expression: str) -> str:\n \"\"\"Round an amount to two places.\"\"\"\n return f\"ROUND({expression}, 2)\"\n\n\ndef adjust_0(expression: str) -> str:\n \"\"\"Double and round an amount.\"\"\"\n return adjust_0_round(f\"({expression} * 2)\")\n", + "depends_on": { + "macros": [] + }, + "description": "", + "meta": { + "sqlbuild_visibility": "global", + "sqlbuild_scope_path": null + }, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "arguments": [], + "supported_languages": null + }, + "macro.generated_0.adjust_2": { + "name": "adjust_2", + "resource_type": "macro", + "package_name": "generated_0", + "path": "macros/adjust_2.py", + "original_file_path": "macros/adjust_2.py", + "unique_id": "macro.generated_0.adjust_2", + "macro_sql": "def adjust_2(ctx, expression: str) -> str:\n \"\"\"Scale an amount by the configured discount.\"\"\"\n rate = ctx.vars[\"discount_rate\"]\n return f\"({expression} * (1 - {rate}))\"\n", + "depends_on": { + "macros": [] + }, + "description": "", + "meta": { + "sqlbuild_visibility": "global", + "sqlbuild_scope_path": null + }, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "arguments": [], + "supported_languages": null + }, + "macro.generated_0.analysis_region": { + "name": "analysis_region", + "resource_type": "macro", + "package_name": "generated_0", + "path": "models/analysis/_sqlbuild/_macros/regions.py", + "original_file_path": "models/analysis/_sqlbuild/_macros/regions.py", + "unique_id": "macro.generated_0.analysis_region", + "macro_sql": "def analysis_region() -> str:\n \"\"\"Return the region rows are assigned to.\"\"\"\n return \"'east'\"\n", + "depends_on": { + "macros": [] + }, + "description": "", + "meta": { + "sqlbuild_visibility": "local", + "sqlbuild_scope_path": "models/analysis" + }, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "arguments": [], + "supported_languages": null + }, + "macro.generated_0.adjust_1": { + "name": "adjust_1", + "resource_type": "macro", + "package_name": "generated_0", + "path": "models/fulfillment/staging/_sqlbuild/_macros/adjust_1.py", + "original_file_path": "models/fulfillment/staging/_sqlbuild/_macros/adjust_1.py", + "unique_id": "macro.generated_0.adjust_1", + "macro_sql": "import os\n\n\ndef adjust_1(expression: str) -> str:\n \"\"\"Tag an amount with the deployment region.\"\"\"\n region = os.environ.get(\"SQB_DIFFERENTIAL_REGION\", \"east\")\n return f\"({expression} + LENGTH('{region}'))\"\n", + "depends_on": { + "macros": [] + }, + "description": "", + "meta": { + "sqlbuild_visibility": "local", + "sqlbuild_scope_path": "models/fulfillment/staging" + }, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "arguments": [], + "supported_languages": null + }, + "macro.generated_0__sqlbuild_hooks.record_refresh": { + "name": "record_refresh", + "resource_type": "macro", + "package_name": "generated_0__sqlbuild_hooks", + "path": "hooks/sql/record_refresh.sql", + "original_file_path": "hooks/sql/record_refresh.sql", + "unique_id": "macro.generated_0__sqlbuild_hooks.record_refresh", + "macro_sql": "SELECT @'relation' AS refreshed_relation, @'label' AS refresh_label", + "depends_on": { + "macros": [] + }, + "description": "Record a refresh for one relation", + "meta": { + "sqlbuild_resource_type": "sql_hook", + "sqlbuild_hook_name": "record_refresh" + }, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "arguments": [], + "supported_languages": null + } + }, + "exposures": {}, + "metrics": {}, + "groups": {}, + "selectors": {}, + "disabled": {}, + "parent_map": { + "model.generated_0.banded_amounts": [ + "model.generated_0.table_fn__amount_bands" + ], + "model.generated_0.chain_1_stg_sales_0": [ + "model.generated_0.contracted_stg_sales_0" + ], + "model.generated_0.chain_2_stg_sales_0": [ + "model.generated_0.chain_1_stg_sales_0" + ], + "model.generated_0.chain_3_stg_sales_0": [ + "model.generated_0.chain_2_stg_sales_0" + ], + "model.generated_0.checked_stg_fulfillment_1": [ + "model.generated_0.stg_fulfillment_1" + ], + "model.generated_0.combined_stg_fulfillment_0": [ + "model.generated_0.stg_fulfillment_0", + "model.generated_0.stg_sales_1" + ], + "model.generated_0.contracted_stg_sales_0": [ + "source.generated_0.raw_events_0" + ], + "model.generated_0.every_open_event": [ + "source.generated_0.open_events" + ], + "model.generated_0.every_raw_events_0": [ + "source.generated_0.raw_events_0" + ], + "model.generated_0.every_stg_fulfillment_0": [ + "model.generated_0.stg_fulfillment_0" + ], + "model.generated_0.passed_stg_fulfillment_0": [ + "model.generated_0.stg_fulfillment_0" + ], + "model.generated_0.regional_stg_fulfillment_1": [ + "model.generated_0.stg_fulfillment_1" + ], + "model.generated_0.scaled_stg_fulfillment_0": [ + "model.generated_0.scaled_amount", + "model.generated_0.stg_fulfillment_0" + ], + "model.generated_0.typed_stg_fulfillment_0": [ + "model.generated_0.stg_fulfillment_0" + ], + "model.generated_0.unanalysed_stg_fulfillment_0": [ + "model.generated_0.stg_fulfillment_0" + ], + "model.generated_0.fulfillment_summary_0": [ + "model.generated_0.stg_fulfillment_0", + "model.generated_0.stg_sales_0", + "seed.generated_0.lookup_1" + ], + "model.generated_0.fulfillment_summary_1": [ + "model.generated_0.stg_fulfillment_0" + ], + "model.generated_0.fulfillment_summary_2": [ + "model.generated_0.stg_fulfillment_1", + "seed.generated_0.lookup_0" + ], + "model.generated_0.stg_fulfillment_0": [ + "source.generated_0.raw_events_1" + ], + "model.generated_0.stg_fulfillment_1": [ + "source.generated_0.raw_events_0" + ], + "model.generated_0.sales_summary_0": [ + "model.generated_0.stg_sales_0", + "seed.generated_0.lookup_1" + ], + "model.generated_0.sales_summary_1": [ + "model.generated_0.stg_sales_0", + "seed.generated_0.lookup_0" + ], + "model.generated_0.sales_summary_2": [ + "model.generated_0.stg_sales_1", + "model.generated_0.stg_fulfillment_0", + "seed.generated_0.lookup_1" + ], + "model.generated_0.stg_sales_0": [ + "source.generated_0.raw_events_0" + ], + "model.generated_0.stg_sales_1": [ + "source.generated_0.raw_events_1" + ], + "source.generated_0.open_events": [], + "source.generated_0.raw_events_0": [], + "source.generated_0.raw_events_1": [], + "seed.generated_0.lookup_0": [], + "seed.generated_0.lookup_1": [], + "model.generated_0.scaled_amount": [], + "model.generated_0.table_fn__amount_bands": [ + "model.generated_0.stg_fulfillment_0" + ] + }, + "group_map": {}, + "docs": {}, + "saved_queries": {}, + "semantic_models": {}, + "unit_tests": {} + } +} diff --git a/tests/goldens/compiler/seed/0-snowflake.json b/tests/goldens/compiler/seed/0-snowflake.json new file mode 100644 index 0000000000..86d39efb09 --- /dev/null +++ b/tests/goldens/compiler/seed/0-snowflake.json @@ -0,0 +1,2907 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 0, + "diagnostics": [] + } + ], + "compiled": { + "audits/generic/checked_stg_fulfillment_1/accepted_values__status.sql": [ + "SELECT status", + "FROM __ref(\"checked_stg_fulfillment_1\")", + "WHERE status IS NOT NULL", + " AND status NOT IN ('placed', 'expédié', '取消', 'returned ✓')", + "" + ], + "audits/generic/checked_stg_fulfillment_1/expression_is_true.sql": [ + "SELECT *", + "FROM __ref(\"checked_stg_fulfillment_1\")", + "WHERE NOT (amount >= -1000)", + "" + ], + "audits/generic/checked_stg_fulfillment_1/relationships__id.sql": [ + "SELECT id", + "FROM __ref(\"checked_stg_fulfillment_1\")", + "WHERE id IS NOT NULL", + " AND id NOT IN (", + " SELECT id", + " FROM __ref(\"stg_fulfillment_1\")", + " WHERE id IS NOT NULL", + " )", + "" + ], + "audits/generic/fulfillment_summary_1/not_null__id.sql": [ + "SELECT id", + "FROM __ref(\"fulfillment_summary_1\")", + "WHERE id IS NULL", + "" + ], + "audits/generic/sales_summary_0/not_null__id.sql": [ + "SELECT id", + "FROM __ref(\"sales_summary_0\")", + "WHERE id IS NULL", + "" + ], + "audits/generic/sales_summary_2/not_null__id.sql": [ + "SELECT id", + "FROM __ref(\"sales_summary_2\")", + "WHERE id IS NULL", + "" + ], + "audits/generic/stg_fulfillment_0/not_null__id.sql": [ + "SELECT id", + "FROM __ref(\"stg_fulfillment_0\")", + "WHERE id IS NULL", + "" + ], + "audits/generic/stg_sales_0/not_null__id.sql": [ + "SELECT id", + "FROM __ref(\"stg_sales_0\")", + "WHERE id IS NULL", + "" + ], + "audits/generic/stg_sales_1/not_null__id.sql": [ + "SELECT id", + "FROM __ref(\"stg_sales_1\")", + "WHERE id IS NULL", + "" + ], + "functions/sql/scaled_amount.sql": [ + "CREATE OR REPLACE FUNCTION GENERATED.analytics.scaled_amount(p_amount DOUBLE, p_factor INTEGER)", + "RETURNS DOUBLE", + "LANGUAGE SQL", + "AS $$", + "p_amount * p_factor", + "$$", + "" + ], + "functions/sql/table_fn__amount_bands.sql": [ + "CREATE OR REPLACE FUNCTION GENERATED.analytics.table_fn__amount_bands(p_floor DOUBLE)", + "RETURNS TABLE (id INTEGER, band VARCHAR)", + "AS $$", + "SELECT CAST(id AS INTEGER) AS id,", + " CASE WHEN amount > p_floor THEN 'high' ELSE 'low' END AS band", + "FROM __ref(\"stg_fulfillment_0\")", + "$$", + "" + ], + "models/analysis/banded_amounts.sql": [ + "SELECT bands.id, bands.band", + "FROM __table_fn(\"table_fn__amount_bands\")(2.5) AS bands", + "" + ], + "models/analysis/chain_1_stg_sales_0.sql": [ + "SELECT id, label, amount", + "FROM __ref(\"contracted_stg_sales_0\")", + "" + ], + "models/analysis/chain_2_stg_sales_0.sql": [ + "SELECT id, UPPER(label) AS label, amount", + "FROM __ref(\"chain_1_stg_sales_0\")", + "" + ], + "models/analysis/chain_3_stg_sales_0.sql": [ + "SELECT id, label, amount", + "FROM __ref(\"chain_2_stg_sales_0\")", + "" + ], + "models/analysis/checked_stg_fulfillment_1.sql": [ + "SELECT id, amount, status", + "FROM __ref(\"stg_fulfillment_1\")", + "" + ], + "models/analysis/combined_stg_fulfillment_0.sql": [ + "WITH recent AS (", + " SELECT id, created_at FROM __ref(\"stg_fulfillment_0\") WHERE amount > 0", + "),", + "older AS (", + " SELECT id, created_at FROM __ref(\"stg_sales_1\")", + ")", + "SELECT id, created_at FROM recent", + "UNION ALL", + "SELECT id, created_at FROM older", + "" + ], + "models/analysis/contracted_stg_sales_0.sql": [ + "SELECT CAST(id AS INTEGER) AS id, CAST(status AS VARCHAR(19)) AS label,", + " amount", + "FROM __source(\"raw_events_0\")", + "" + ], + "models/analysis/every_open_event.sql": [ + "SELECT *", + "FROM __source(\"open_events\")", + "" + ], + "models/analysis/every_raw_events_0.sql": [ + "SELECT *", + "FROM __source(\"raw_events_0\")", + "" + ], + "models/analysis/every_stg_fulfillment_0.sql": [ + "SELECT *", + "FROM __ref(\"stg_fulfillment_0\")", + "" + ], + "models/analysis/passed_stg_fulfillment_0.sql": [ + "WITH passthrough AS (", + " SELECT id, created_at FROM __ref(\"stg_fulfillment_0\")", + ")", + "SELECT id, created_at", + "FROM passthrough", + "" + ], + "models/analysis/regional_stg_fulfillment_1.sql": [ + "SELECT id, amount, 'east' AS region", + "FROM __ref(\"stg_fulfillment_1\")", + "" + ], + "models/analysis/scaled_stg_fulfillment_0.sql": [ + "SELECT id, __udf(\"scaled_amount\")(amount, 2) AS scaled_amount", + "FROM __ref(\"stg_fulfillment_0\")", + "" + ], + "models/analysis/typed_stg_fulfillment_0.sql": [ + "SELECT id, amount * 2 AS doubled_amount, CAST(status AS VARCHAR) AS status_text,", + " created_at, NULL AS missing_note", + "FROM __ref(\"stg_fulfillment_0\")", + "" + ], + "models/analysis/unanalysed_stg_fulfillment_0.sql": [ + "SELECT id, amount", + "FROM __ref(\"stg_fulfillment_0\")", + "WHERE id = ANY [1, 2]", + "" + ], + "models/fulfillment/marts/fulfillment_summary_0.sql": [ + "SELECT", + " t.id,", + " t.amount,", + " t.status,", + " t.created_at,", + " (t.amount * (1 - 1e-07)) AS adjust_2_amount,", + " j2.label AS lookup_label_2", + "FROM __ref(\"stg_fulfillment_0\") t", + "LEFT JOIN __ref(\"stg_sales_0\") j1 ON t.id = j1.id", + "LEFT JOIN __seed(\"lookup_1\") j2 ON t.id = j2.id", + "" + ], + "models/fulfillment/marts/fulfillment_summary_1.sql": [ + "SELECT", + " t.id,", + " t.amount,", + " t.status,", + " t.created_at", + "FROM __ref(\"stg_fulfillment_0\") t", + "" + ], + "models/fulfillment/marts/fulfillment_summary_2.sql": [ + "SELECT", + " t.id,", + " t.amount,", + " t.status,", + " t.created_at,", + " (t.amount * (1 - 1e-07)) AS adjust_2_amount,", + " j1.label AS lookup_label_1,", + " 'Zürich – depot' AS site_label", + "FROM __ref(\"stg_fulfillment_1\") t", + "LEFT JOIN __seed(\"lookup_0\") j1 ON t.id = j1.id", + "" + ], + "models/fulfillment/staging/stg_fulfillment_0.sql": [ + "SELECT", + " t.id,", + " t.amount,", + " t.status,", + " t.created_at,", + " (t.amount + LENGTH('east')) AS adjust_1_amount,", + " 'Zürich – depot' AS site_label", + "FROM __source(\"raw_events_1\") t", + "" + ], + "models/fulfillment/staging/stg_fulfillment_1.sql": [ + "SELECT", + " t.id,", + " t.amount,", + " t.status,", + " t.created_at,", + " 'Ελληνικά orders' AS threshold_0_label,", + " ROUND(ROUND((t.amount * 2), 2), 2) AS adjust_0_amount", + "FROM __source(\"raw_events_0\") t", + "WHERE t.status <> 'expédié'", + "" + ], + "models/sales/marts/sales_summary_0.sql": [ + "SELECT", + " t.id,", + " t.amount,", + " t.status,", + " t.created_at,", + " 'Ελληνικά orders' AS threshold_0_label,", + " j1.label AS lookup_label_1", + "FROM __ref(\"stg_sales_0\") t", + "LEFT JOIN __seed(\"lookup_1\") j1 ON t.id = j1.id", + "" + ], + "models/sales/marts/sales_summary_1.sql": [ + "SELECT", + " t.id,", + " t.amount,", + " t.status,", + " t.created_at,", + " (t.amount * (1 - 1e-07)) AS adjust_2_amount,", + " j1.label AS lookup_label_1", + "FROM __ref(\"stg_sales_0\") t", + "LEFT JOIN __seed(\"lookup_0\") j1 ON t.id = j1.id", + "WHERE t.status <> '取消'", + "" + ], + "models/sales/marts/sales_summary_2.sql": [ + "SELECT", + " t.id,", + " t.amount,", + " t.status,", + " t.created_at,", + " 'naïve résumé' AS threshold_1_label,", + " ROUND(ROUND((t.amount * 2), 2), 2) AS adjust_0_amount,", + " j2.label AS lookup_label_2", + "FROM __ref(\"stg_sales_1\") t", + "LEFT JOIN __ref(\"stg_fulfillment_0\") j1 ON t.id = j1.id", + "LEFT JOIN __seed(\"lookup_1\") j2 ON t.id = j2.id", + "" + ], + "models/sales/staging/stg_sales_0.sql": [ + "SELECT", + " t.id,", + " t.amount,", + " t.status,", + " t.created_at,", + " t.amount * '1e-07' AS threshold_2_amount,", + " 'Zürich – depot' AS site_label", + "FROM __source(\"raw_events_0\") t", + "" + ], + "models/sales/staging/stg_sales_1.sql": [ + "SELECT", + " t.id,", + " t.amount,", + " t.status,", + " t.created_at,", + " 'naïve résumé' AS threshold_1_label", + "FROM __source(\"raw_events_1\") t", + "" + ], + "tests/regional_stg_fulfillment_1/test_regional_stg_fulfillment_1.sql": [ + "WITH __ref__stg_fulfillment_1 AS (SELECT 1 AS id, CAST(4 AS DOUBLE) AS amount, 'placed' AS status,", + " TIMESTAMP '2026-01-01 00:00:00' AS created_at),", + "__ref__regional_stg_fulfillment_1 AS (SELECT id, amount, 'west' AS region", + "FROM __ref__stg_fulfillment_1),", + "__actual__regional_stg_fulfillment_1 AS (SELECT * FROM __ref__regional_stg_fulfillment_1),", + "__expected__regional_stg_fulfillment_1 AS (SELECT 1 AS id, CAST(4 AS DOUBLE) AS amount, 'west' AS region)", + "SELECT 0 AS step_index, 'regional_stg_fulfillment_1' AS model_name, (SELECT COUNT(*) FROM __actual__regional_stg_fulfillment_1) AS actual_count, (SELECT COUNT(*) FROM __expected__regional_stg_fulfillment_1) AS expected_count, (SELECT COUNT(*) FROM (SELECT id, amount, region FROM __actual__regional_stg_fulfillment_1 EXCEPT SELECT id, amount, region FROM __expected__regional_stg_fulfillment_1) AS __sqlbuild_mismatch) AS unexpected_count, (SELECT COUNT(*) FROM (SELECT id, amount, region FROM __expected__regional_stg_fulfillment_1 EXCEPT SELECT id, amount, region FROM __actual__regional_stg_fulfillment_1) AS __sqlbuild_missing) AS missing_count", + "" + ], + "tests/stg_fulfillment_0/test_stg_fulfillment_0.sql": [ + "WITH __source__raw_events_1 AS (SELECT 1 AS id, CAST(10 AS DOUBLE) AS amount, 'placed' AS status,", + " TIMESTAMP '2026-01-01 00:00:00' AS created_at),", + "__ref__stg_fulfillment_0 AS (SELECT", + " t.id,", + " t.amount,", + " t.status,", + " t.created_at,", + " (t.amount + LENGTH('east')) AS adjust_1_amount,", + " 'Zürich – depot' AS site_label", + "FROM __source__raw_events_1 t),", + "__actual__stg_fulfillment_0 AS (SELECT * FROM __ref__stg_fulfillment_0),", + "__expected__stg_fulfillment_0 AS (SELECT 1 AS id, 'placed' AS status)", + "SELECT 0 AS step_index, 'stg_fulfillment_0' AS model_name, (SELECT COUNT(*) FROM __actual__stg_fulfillment_0) AS actual_count, (SELECT COUNT(*) FROM __expected__stg_fulfillment_0) AS expected_count, (SELECT COUNT(*) FROM (SELECT id, status FROM __actual__stg_fulfillment_0 EXCEPT SELECT id, status FROM __expected__stg_fulfillment_0) AS __sqlbuild_mismatch) AS unexpected_count, (SELECT COUNT(*) FROM (SELECT id, status FROM __expected__stg_fulfillment_0 EXCEPT SELECT id, status FROM __actual__stg_fulfillment_0) AS __sqlbuild_missing) AS missing_count", + "" + ] + }, + "manifest": { + "nodes": { + "model.generated_0.banded_amounts": { + "database": "GENERATED", + "schema": "analytics", + "name": "banded_amounts", + "resource_type": "model", + "package_name": "generated_0", + "path": "models/analysis/banded_amounts.sql", + "original_file_path": "models/analysis/banded_amounts.sql", + "unique_id": "model.generated_0.banded_amounts", + "fqn": [ + "generated_0", + "models", + "analysis", + "banded_amounts" + ], + "alias": "banded_amounts", + "config": { + "enabled": true, + "alias": null, + "schema": "analytics", + "database": "GENERATED", + "tags": [], + "meta": {}, + "group": null, + "materialized": "table", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Amount bands from a table function", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "GENERATED.analytics.banded_amounts", + "depends_on": { + "macros": [], + "nodes": [ + "model.generated_0.table_fn__amount_bands" + ] + } + }, + "model.generated_0.chain_1_stg_sales_0": { + "database": "GENERATED", + "schema": "analytics", + "name": "chain_1_stg_sales_0", + "resource_type": "model", + "package_name": "generated_0", + "path": "models/analysis/chain_1_stg_sales_0.sql", + "original_file_path": "models/analysis/chain_1_stg_sales_0.sql", + "unique_id": "model.generated_0.chain_1_stg_sales_0", + "fqn": [ + "generated_0", + "models", + "analysis", + "chain_1_stg_sales_0" + ], + "alias": "chain_1_stg_sales_0", + "config": { + "enabled": true, + "alias": null, + "schema": "analytics", + "database": "GENERATED", + "tags": [], + "meta": {}, + "group": null, + "materialized": "table", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Level 1 below the contracted copy of stg_sales_0", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "GENERATED.analytics.chain_1_stg_sales_0", + "depends_on": { + "macros": [], + "nodes": [ + "model.generated_0.contracted_stg_sales_0" + ] + } + }, + "model.generated_0.chain_2_stg_sales_0": { + "database": "GENERATED", + "schema": "analytics", + "name": "chain_2_stg_sales_0", + "resource_type": "model", + "package_name": "generated_0", + "path": "models/analysis/chain_2_stg_sales_0.sql", + "original_file_path": "models/analysis/chain_2_stg_sales_0.sql", + "unique_id": "model.generated_0.chain_2_stg_sales_0", + "fqn": [ + "generated_0", + "models", + "analysis", + "chain_2_stg_sales_0" + ], + "alias": "chain_2_stg_sales_0", + "config": { + "enabled": true, + "alias": null, + "schema": "analytics", + "database": "GENERATED", + "tags": [], + "meta": {}, + "group": null, + "materialized": "table", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Level 2 below the contracted copy of stg_sales_0", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "GENERATED.analytics.chain_2_stg_sales_0", + "depends_on": { + "macros": [], + "nodes": [ + "model.generated_0.chain_1_stg_sales_0" + ] + } + }, + "model.generated_0.chain_3_stg_sales_0": { + "database": "GENERATED", + "schema": "analytics", + "name": "chain_3_stg_sales_0", + "resource_type": "model", + "package_name": "generated_0", + "path": "models/analysis/chain_3_stg_sales_0.sql", + "original_file_path": "models/analysis/chain_3_stg_sales_0.sql", + "unique_id": "model.generated_0.chain_3_stg_sales_0", + "fqn": [ + "generated_0", + "models", + "analysis", + "chain_3_stg_sales_0" + ], + "alias": "chain_3_stg_sales_0", + "config": { + "enabled": true, + "alias": null, + "schema": "analytics", + "database": "GENERATED", + "tags": [], + "meta": {}, + "group": null, + "materialized": "table", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Level 3 below the contracted copy of stg_sales_0", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "GENERATED.analytics.chain_3_stg_sales_0", + "depends_on": { + "macros": [], + "nodes": [ + "model.generated_0.chain_2_stg_sales_0" + ] + } + }, + "model.generated_0.checked_stg_fulfillment_1": { + "database": "GENERATED", + "schema": "analytics", + "name": "checked_stg_fulfillment_1", + "resource_type": "model", + "package_name": "generated_0", + "path": "models/analysis/checked_stg_fulfillment_1.sql", + "original_file_path": "models/analysis/checked_stg_fulfillment_1.sql", + "unique_id": "model.generated_0.checked_stg_fulfillment_1", + "fqn": [ + "generated_0", + "models", + "analysis", + "checked_stg_fulfillment_1" + ], + "alias": "checked_stg_fulfillment_1", + "config": { + "enabled": true, + "alias": null, + "schema": "analytics", + "database": "GENERATED", + "tags": [], + "meta": {}, + "group": null, + "materialized": "table", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": [ + "id" + ], + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Metadata-checked copy of stg_fulfillment_1", + "columns": { + "id": { + "name": "id", + "description": "", + "meta": {}, + "data_type": null, + "constraints": [], + "quote": null, + "tags": [] + }, + "status": { + "name": "status", + "description": "", + "meta": {}, + "data_type": null, + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "GENERATED.analytics.checked_stg_fulfillment_1", + "depends_on": { + "macros": [], + "nodes": [ + "model.generated_0.stg_fulfillment_1" + ] + } + }, + "model.generated_0.combined_stg_fulfillment_0": { + "database": "GENERATED", + "schema": "analytics", + "name": "combined_stg_fulfillment_0", + "resource_type": "model", + "package_name": "generated_0", + "path": "models/analysis/combined_stg_fulfillment_0.sql", + "original_file_path": "models/analysis/combined_stg_fulfillment_0.sql", + "unique_id": "model.generated_0.combined_stg_fulfillment_0", + "fqn": [ + "generated_0", + "models", + "analysis", + "combined_stg_fulfillment_0" + ], + "alias": "combined_stg_fulfillment_0", + "config": { + "enabled": true, + "alias": null, + "schema": "analytics", + "database": "GENERATED", + "tags": [], + "meta": {}, + "group": null, + "materialized": "table", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Recent and older rows of stg_fulfillment_0 and stg_sales_1", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "GENERATED.analytics.combined_stg_fulfillment_0", + "depends_on": { + "macros": [], + "nodes": [ + "model.generated_0.stg_fulfillment_0", + "model.generated_0.stg_sales_1" + ] + } + }, + "model.generated_0.contracted_stg_sales_0": { + "database": "GENERATED", + "schema": "analytics", + "name": "contracted_stg_sales_0", + "resource_type": "model", + "package_name": "generated_0", + "path": "models/analysis/contracted_stg_sales_0.sql", + "original_file_path": "models/analysis/contracted_stg_sales_0.sql", + "unique_id": "model.generated_0.contracted_stg_sales_0", + "fqn": [ + "generated_0", + "models", + "analysis", + "contracted_stg_sales_0" + ], + "alias": "contracted_stg_sales_0", + "config": { + "enabled": true, + "alias": null, + "schema": "analytics", + "database": "GENERATED", + "tags": [], + "meta": {}, + "group": null, + "materialized": "table", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Contracted copy of raw_events_0", + "columns": { + "id": { + "name": "id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "label": { + "name": "label", + "description": "", + "meta": {}, + "data_type": "VARCHAR(19)", + "constraints": [], + "quote": null, + "tags": [] + }, + "amount": { + "name": "amount", + "description": "", + "meta": {}, + "data_type": "DOUBLE", + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "GENERATED.analytics.contracted_stg_sales_0", + "depends_on": { + "macros": [], + "nodes": [ + "source.generated_0.raw_events_0" + ] + } + }, + "model.generated_0.every_open_event": { + "database": "GENERATED", + "schema": "analytics", + "name": "every_open_event", + "resource_type": "model", + "package_name": "generated_0", + "path": "models/analysis/every_open_event.sql", + "original_file_path": "models/analysis/every_open_event.sql", + "unique_id": "model.generated_0.every_open_event", + "fqn": [ + "generated_0", + "models", + "analysis", + "every_open_event" + ], + "alias": "every_open_event", + "config": { + "enabled": true, + "alias": null, + "schema": "analytics", + "database": "GENERATED", + "tags": [], + "meta": {}, + "group": null, + "materialized": "table", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Every column of an open source", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "GENERATED.analytics.every_open_event", + "depends_on": { + "macros": [], + "nodes": [ + "source.generated_0.open_events" + ] + } + }, + "model.generated_0.every_raw_events_0": { + "database": "GENERATED", + "schema": "analytics", + "name": "every_raw_events_0", + "resource_type": "model", + "package_name": "generated_0", + "path": "models/analysis/every_raw_events_0.sql", + "original_file_path": "models/analysis/every_raw_events_0.sql", + "unique_id": "model.generated_0.every_raw_events_0", + "fqn": [ + "generated_0", + "models", + "analysis", + "every_raw_events_0" + ], + "alias": "every_raw_events_0", + "config": { + "enabled": true, + "alias": null, + "schema": "analytics", + "database": "GENERATED", + "tags": [], + "meta": {}, + "group": null, + "materialized": "table", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Every column of raw_events_0", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "GENERATED.analytics.every_raw_events_0", + "depends_on": { + "macros": [], + "nodes": [ + "source.generated_0.raw_events_0" + ] + } + }, + "model.generated_0.every_stg_fulfillment_0": { + "database": "GENERATED", + "schema": "analytics", + "name": "every_stg_fulfillment_0", + "resource_type": "model", + "package_name": "generated_0", + "path": "models/analysis/every_stg_fulfillment_0.sql", + "original_file_path": "models/analysis/every_stg_fulfillment_0.sql", + "unique_id": "model.generated_0.every_stg_fulfillment_0", + "fqn": [ + "generated_0", + "models", + "analysis", + "every_stg_fulfillment_0" + ], + "alias": "every_stg_fulfillment_0", + "config": { + "enabled": true, + "alias": null, + "schema": "analytics", + "database": "GENERATED", + "tags": [], + "meta": {}, + "group": null, + "materialized": "table", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Every column of stg_fulfillment_0", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "GENERATED.analytics.every_stg_fulfillment_0", + "depends_on": { + "macros": [], + "nodes": [ + "model.generated_0.stg_fulfillment_0" + ] + } + }, + "model.generated_0.passed_stg_fulfillment_0": { + "database": "GENERATED", + "schema": "analytics", + "name": "passed_stg_fulfillment_0", + "resource_type": "model", + "package_name": "generated_0", + "path": "models/analysis/passed_stg_fulfillment_0.sql", + "original_file_path": "models/analysis/passed_stg_fulfillment_0.sql", + "unique_id": "model.generated_0.passed_stg_fulfillment_0", + "fqn": [ + "generated_0", + "models", + "analysis", + "passed_stg_fulfillment_0" + ], + "alias": "passed_stg_fulfillment_0", + "config": { + "enabled": true, + "alias": null, + "schema": "analytics", + "database": "GENERATED", + "tags": [], + "meta": {}, + "group": null, + "materialized": "table", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Columns of stg_fulfillment_0 passed through a CTE", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "GENERATED.analytics.passed_stg_fulfillment_0", + "depends_on": { + "macros": [], + "nodes": [ + "model.generated_0.stg_fulfillment_0" + ] + } + }, + "model.generated_0.regional_stg_fulfillment_1": { + "database": "GENERATED", + "schema": "analytics", + "name": "regional_stg_fulfillment_1", + "resource_type": "model", + "package_name": "generated_0", + "path": "models/analysis/regional_stg_fulfillment_1.sql", + "original_file_path": "models/analysis/regional_stg_fulfillment_1.sql", + "unique_id": "model.generated_0.regional_stg_fulfillment_1", + "fqn": [ + "generated_0", + "models", + "analysis", + "regional_stg_fulfillment_1" + ], + "alias": "regional_stg_fulfillment_1", + "config": { + "enabled": true, + "alias": null, + "schema": "analytics", + "database": "GENERATED", + "tags": [], + "meta": {}, + "group": null, + "materialized": "table", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Regional copy of stg_fulfillment_1", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "GENERATED.analytics.regional_stg_fulfillment_1", + "depends_on": { + "macros": [], + "nodes": [ + "model.generated_0.stg_fulfillment_1" + ] + } + }, + "model.generated_0.scaled_stg_fulfillment_0": { + "database": "GENERATED", + "schema": "analytics", + "name": "scaled_stg_fulfillment_0", + "resource_type": "model", + "package_name": "generated_0", + "path": "models/analysis/scaled_stg_fulfillment_0.sql", + "original_file_path": "models/analysis/scaled_stg_fulfillment_0.sql", + "unique_id": "model.generated_0.scaled_stg_fulfillment_0", + "fqn": [ + "generated_0", + "models", + "analysis", + "scaled_stg_fulfillment_0" + ], + "alias": "scaled_stg_fulfillment_0", + "config": { + "enabled": true, + "alias": null, + "schema": "analytics", + "database": "GENERATED", + "tags": [], + "meta": {}, + "group": null, + "materialized": "table", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Scaled amounts of stg_fulfillment_0", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "GENERATED.analytics.scaled_stg_fulfillment_0", + "depends_on": { + "macros": [], + "nodes": [ + "model.generated_0.scaled_amount", + "model.generated_0.stg_fulfillment_0" + ] + } + }, + "model.generated_0.typed_stg_fulfillment_0": { + "database": "GENERATED", + "schema": "analytics", + "name": "typed_stg_fulfillment_0", + "resource_type": "model", + "package_name": "generated_0", + "path": "models/analysis/typed_stg_fulfillment_0.sql", + "original_file_path": "models/analysis/typed_stg_fulfillment_0.sql", + "unique_id": "model.generated_0.typed_stg_fulfillment_0", + "fqn": [ + "generated_0", + "models", + "analysis", + "typed_stg_fulfillment_0" + ], + "alias": "typed_stg_fulfillment_0", + "config": { + "enabled": true, + "alias": null, + "schema": "analytics", + "database": "GENERATED", + "tags": [], + "meta": {}, + "group": null, + "materialized": "table", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Typed and untyped outputs of stg_fulfillment_0", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "GENERATED.analytics.typed_stg_fulfillment_0", + "depends_on": { + "macros": [], + "nodes": [ + "model.generated_0.stg_fulfillment_0" + ] + } + }, + "model.generated_0.unanalysed_stg_fulfillment_0": { + "database": "GENERATED", + "schema": "analytics", + "name": "unanalysed_stg_fulfillment_0", + "resource_type": "model", + "package_name": "generated_0", + "path": "models/analysis/unanalysed_stg_fulfillment_0.sql", + "original_file_path": "models/analysis/unanalysed_stg_fulfillment_0.sql", + "unique_id": "model.generated_0.unanalysed_stg_fulfillment_0", + "fqn": [ + "generated_0", + "models", + "analysis", + "unanalysed_stg_fulfillment_0" + ], + "alias": "unanalysed_stg_fulfillment_0", + "config": { + "enabled": true, + "alias": null, + "schema": "analytics", + "database": "GENERATED", + "tags": [], + "meta": {}, + "group": null, + "materialized": "table", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Copy of stg_fulfillment_0 that SQL analysis cannot parse", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "GENERATED.analytics.unanalysed_stg_fulfillment_0", + "depends_on": { + "macros": [], + "nodes": [ + "model.generated_0.stg_fulfillment_0" + ] + } + }, + "model.generated_0.fulfillment_summary_0": { + "database": "GENERATED", + "schema": "analytics", + "name": "fulfillment_summary_0", + "resource_type": "model", + "package_name": "generated_0", + "path": "models/fulfillment/marts/fulfillment_summary_0.sql", + "original_file_path": "models/fulfillment/marts/fulfillment_summary_0.sql", + "unique_id": "model.generated_0.fulfillment_summary_0", + "fqn": [ + "generated_0", + "models", + "fulfillment", + "marts", + "fulfillment_summary_0" + ], + "alias": "fulfillment_summary_0", + "config": { + "enabled": true, + "alias": null, + "schema": "analytics", + "database": "GENERATED", + "tags": [], + "meta": {}, + "group": null, + "materialized": "table", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Summary of fulfillment activity 0", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "GENERATED.analytics.fulfillment_summary_0", + "depends_on": { + "macros": [], + "nodes": [ + "model.generated_0.stg_fulfillment_0", + "model.generated_0.stg_sales_0", + "seed.generated_0.lookup_1" + ] + } + }, + "model.generated_0.fulfillment_summary_1": { + "database": "GENERATED", + "schema": "analytics", + "name": "fulfillment_summary_1", + "resource_type": "model", + "package_name": "generated_0", + "path": "models/fulfillment/marts/fulfillment_summary_1.sql", + "original_file_path": "models/fulfillment/marts/fulfillment_summary_1.sql", + "unique_id": "model.generated_0.fulfillment_summary_1", + "fqn": [ + "generated_0", + "models", + "fulfillment", + "marts", + "fulfillment_summary_1" + ], + "alias": "fulfillment_summary_1", + "config": { + "enabled": true, + "alias": null, + "schema": "analytics", + "database": "GENERATED", + "tags": [], + "meta": {}, + "group": null, + "materialized": "table", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [ + { + "sql": "SELECT 'GENERATED.analytics.fulfillment_summary_1' AS refreshed_relation, 'Ελληνικά orders' AS refresh_label", + "transaction": true, + "index": 0 + }, + { + "sql": "SELECT 'GENERATED.analytics.fulfillment_summary_1' AS refreshed", + "transaction": true, + "index": 1 + } + ], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Summary of fulfillment activity 1", + "columns": { + "id": { + "name": "id", + "description": "", + "meta": {}, + "data_type": null, + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": { + "sqlbuild": { + "lifecycle_hooks": { + "post_hooks": [ + { + "type": "sql", + "statement": "SELECT 'GENERATED.analytics.fulfillment_summary_1' AS refreshed_relation, 'Ελληνικά orders' AS refresh_label", + "name": "record_refresh", + "relative_path": "hooks/sql/record_refresh.sql", + "definition_sql": "SELECT @'relation' AS refreshed_relation, @'label' AS refresh_label", + "kwargs": { + "relation": "@@CTX:destination.qualified", + "label": "Ελληνικά orders" + }, + "description": "Record a refresh for one relation" + }, + { + "type": "sql", + "statement": "SELECT 'GENERATED.analytics.fulfillment_summary_1' AS refreshed" + } + ] + } + } + }, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "GENERATED.analytics.fulfillment_summary_1", + "depends_on": { + "macros": [], + "nodes": [ + "model.generated_0.stg_fulfillment_0" + ] + } + }, + "model.generated_0.fulfillment_summary_2": { + "database": "GENERATED", + "schema": "analytics", + "name": "fulfillment_summary_2", + "resource_type": "model", + "package_name": "generated_0", + "path": "models/fulfillment/marts/fulfillment_summary_2.sql", + "original_file_path": "models/fulfillment/marts/fulfillment_summary_2.sql", + "unique_id": "model.generated_0.fulfillment_summary_2", + "fqn": [ + "generated_0", + "models", + "fulfillment", + "marts", + "fulfillment_summary_2" + ], + "alias": "fulfillment_summary_2", + "config": { + "enabled": true, + "alias": null, + "schema": "analytics", + "database": "GENERATED", + "tags": [], + "meta": {}, + "group": null, + "materialized": "table", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [ + { + "sql": "SELECT 'GENERATED.analytics.fulfillment_summary_2' AS refreshed_relation, 'café ☕ counter' AS refresh_label", + "transaction": true, + "index": 0 + }, + { + "sql": "SELECT 'GENERATED.analytics.fulfillment_summary_2' AS refreshed", + "transaction": true, + "index": 1 + } + ], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Summary of fulfillment activity 2 (naïve résumé)", + "columns": {}, + "meta": { + "sqlbuild": { + "lifecycle_hooks": { + "post_hooks": [ + { + "type": "sql", + "statement": "SELECT 'GENERATED.analytics.fulfillment_summary_2' AS refreshed_relation, 'café ☕ counter' AS refresh_label", + "name": "record_refresh", + "relative_path": "hooks/sql/record_refresh.sql", + "definition_sql": "SELECT @'relation' AS refreshed_relation, @'label' AS refresh_label", + "kwargs": { + "relation": "@@CTX:destination.qualified", + "label": "café ☕ counter" + }, + "description": "Record a refresh for one relation" + }, + { + "type": "sql", + "statement": "SELECT 'GENERATED.analytics.fulfillment_summary_2' AS refreshed" + } + ] + } + } + }, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "GENERATED.analytics.fulfillment_summary_2", + "depends_on": { + "macros": [], + "nodes": [ + "model.generated_0.stg_fulfillment_1", + "seed.generated_0.lookup_0" + ] + } + }, + "model.generated_0.stg_fulfillment_0": { + "database": "GENERATED", + "schema": "analytics", + "name": "stg_fulfillment_0", + "resource_type": "model", + "package_name": "generated_0", + "path": "models/fulfillment/staging/stg_fulfillment_0.sql", + "original_file_path": "models/fulfillment/staging/stg_fulfillment_0.sql", + "unique_id": "model.generated_0.stg_fulfillment_0", + "fqn": [ + "generated_0", + "models", + "fulfillment", + "staging", + "stg_fulfillment_0" + ], + "alias": "stg_fulfillment_0", + "config": { + "enabled": true, + "alias": null, + "schema": "analytics", + "database": "GENERATED", + "tags": [ + "staging" + ], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [ + "staging" + ], + "description": "Staged fulfillment events 0", + "columns": { + "id": { + "name": "id", + "description": "", + "meta": {}, + "data_type": null, + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "GENERATED.analytics.stg_fulfillment_0", + "depends_on": { + "macros": [], + "nodes": [ + "source.generated_0.raw_events_1" + ] + } + }, + "model.generated_0.stg_fulfillment_1": { + "database": "GENERATED", + "schema": "analytics", + "name": "stg_fulfillment_1", + "resource_type": "model", + "package_name": "generated_0", + "path": "models/fulfillment/staging/stg_fulfillment_1.sql", + "original_file_path": "models/fulfillment/staging/stg_fulfillment_1.sql", + "unique_id": "model.generated_0.stg_fulfillment_1", + "fqn": [ + "generated_0", + "models", + "fulfillment", + "staging", + "stg_fulfillment_1" + ], + "alias": "stg_fulfillment_1", + "config": { + "enabled": true, + "alias": null, + "schema": "analytics", + "database": "GENERATED", + "tags": [ + "staging" + ], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [ + "staging" + ], + "description": "Staged fulfillment events 1", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "GENERATED.analytics.stg_fulfillment_1", + "depends_on": { + "macros": [], + "nodes": [ + "source.generated_0.raw_events_0" + ] + } + }, + "model.generated_0.sales_summary_0": { + "database": "GENERATED", + "schema": "analytics", + "name": "sales_summary_0", + "resource_type": "model", + "package_name": "generated_0", + "path": "models/sales/marts/sales_summary_0.sql", + "original_file_path": "models/sales/marts/sales_summary_0.sql", + "unique_id": "model.generated_0.sales_summary_0", + "fqn": [ + "generated_0", + "models", + "sales", + "marts", + "sales_summary_0" + ], + "alias": "sales_summary_0", + "config": { + "enabled": true, + "alias": null, + "schema": "analytics", + "database": "GENERATED", + "tags": [ + "sales", + "marts" + ], + "meta": {}, + "group": null, + "materialized": "table", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [ + "sales", + "marts" + ], + "description": "Summary of sales activity 0", + "columns": { + "id": { + "name": "id", + "description": "", + "meta": {}, + "data_type": null, + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "GENERATED.analytics.sales_summary_0", + "depends_on": { + "macros": [], + "nodes": [ + "model.generated_0.stg_sales_0", + "seed.generated_0.lookup_1" + ] + } + }, + "model.generated_0.sales_summary_1": { + "database": "GENERATED", + "schema": "analytics", + "name": "sales_summary_1", + "resource_type": "model", + "package_name": "generated_0", + "path": "models/sales/marts/sales_summary_1.sql", + "original_file_path": "models/sales/marts/sales_summary_1.sql", + "unique_id": "model.generated_0.sales_summary_1", + "fqn": [ + "generated_0", + "models", + "sales", + "marts", + "sales_summary_1" + ], + "alias": "sales_summary_1", + "config": { + "enabled": true, + "alias": null, + "schema": "analytics", + "database": "GENERATED", + "tags": [ + "sales", + "marts" + ], + "meta": {}, + "group": null, + "materialized": "table", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [ + { + "sql": "SELECT 'GENERATED.analytics.sales_summary_1' AS refreshed_relation, '東京 warehouse' AS refresh_label", + "transaction": true, + "index": 0 + }, + { + "sql": "SELECT 'GENERATED.analytics.sales_summary_1' AS refreshed", + "transaction": true, + "index": 1 + } + ], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [ + "sales", + "marts" + ], + "description": "Summary of sales activity 1", + "columns": {}, + "meta": { + "sqlbuild": { + "lifecycle_hooks": { + "post_hooks": [ + { + "type": "sql", + "statement": "SELECT 'GENERATED.analytics.sales_summary_1' AS refreshed_relation, '東京 warehouse' AS refresh_label", + "name": "record_refresh", + "relative_path": "hooks/sql/record_refresh.sql", + "definition_sql": "SELECT @'relation' AS refreshed_relation, @'label' AS refresh_label", + "kwargs": { + "relation": "@@CTX:destination.qualified", + "label": "東京 warehouse" + }, + "description": "Record a refresh for one relation" + }, + { + "type": "sql", + "statement": "SELECT 'GENERATED.analytics.sales_summary_1' AS refreshed" + } + ] + } + } + }, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "GENERATED.analytics.sales_summary_1", + "depends_on": { + "macros": [], + "nodes": [ + "model.generated_0.stg_sales_0", + "seed.generated_0.lookup_0" + ] + } + }, + "model.generated_0.sales_summary_2": { + "database": "GENERATED", + "schema": "analytics", + "name": "sales_summary_2", + "resource_type": "model", + "package_name": "generated_0", + "path": "models/sales/marts/sales_summary_2.sql", + "original_file_path": "models/sales/marts/sales_summary_2.sql", + "unique_id": "model.generated_0.sales_summary_2", + "fqn": [ + "generated_0", + "models", + "sales", + "marts", + "sales_summary_2" + ], + "alias": "sales_summary_2", + "config": { + "enabled": true, + "alias": null, + "schema": "analytics", + "database": "GENERATED", + "tags": [ + "sales", + "marts" + ], + "meta": {}, + "group": null, + "materialized": "table", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [ + "sales", + "marts" + ], + "description": "Summary of sales activity 2 (Ελληνικά orders)", + "columns": { + "id": { + "name": "id", + "description": "", + "meta": {}, + "data_type": null, + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "GENERATED.analytics.sales_summary_2", + "depends_on": { + "macros": [], + "nodes": [ + "model.generated_0.stg_sales_1", + "model.generated_0.stg_fulfillment_0", + "seed.generated_0.lookup_1" + ] + } + }, + "model.generated_0.stg_sales_0": { + "database": "GENERATED", + "schema": "analytics", + "name": "stg_sales_0", + "resource_type": "model", + "package_name": "generated_0", + "path": "models/sales/staging/stg_sales_0.sql", + "original_file_path": "models/sales/staging/stg_sales_0.sql", + "unique_id": "model.generated_0.stg_sales_0", + "fqn": [ + "generated_0", + "models", + "sales", + "staging", + "stg_sales_0" + ], + "alias": "stg_sales_0", + "config": { + "enabled": true, + "alias": null, + "schema": "analytics", + "database": "GENERATED", + "tags": [ + "staging" + ], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [ + "staging" + ], + "description": "Staged sales events 0", + "columns": { + "id": { + "name": "id", + "description": "", + "meta": {}, + "data_type": null, + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "GENERATED.analytics.stg_sales_0", + "depends_on": { + "macros": [], + "nodes": [ + "source.generated_0.raw_events_0" + ] + } + }, + "model.generated_0.stg_sales_1": { + "database": "GENERATED", + "schema": "analytics", + "name": "stg_sales_1", + "resource_type": "model", + "package_name": "generated_0", + "path": "models/sales/staging/stg_sales_1.sql", + "original_file_path": "models/sales/staging/stg_sales_1.sql", + "unique_id": "model.generated_0.stg_sales_1", + "fqn": [ + "generated_0", + "models", + "sales", + "staging", + "stg_sales_1" + ], + "alias": "stg_sales_1", + "config": { + "enabled": true, + "alias": null, + "schema": "analytics", + "database": "GENERATED", + "tags": [ + "staging" + ], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [ + "staging" + ], + "description": "Staged sales events 1", + "columns": { + "id": { + "name": "id", + "description": "", + "meta": {}, + "data_type": null, + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "GENERATED.analytics.stg_sales_1", + "depends_on": { + "macros": [], + "nodes": [ + "source.generated_0.raw_events_1" + ] + } + }, + "seed.generated_0.lookup_0": { + "database": "GENERATED", + "schema": "analytics", + "name": "lookup_0", + "resource_type": "seed", + "package_name": "generated_0", + "path": "seeds/lookup_0.csv", + "original_file_path": "seeds/lookup_0.csv", + "unique_id": "seed.generated_0.lookup_0", + "fqn": [ + "generated_0", + "seeds", + "lookup_0" + ], + "alias": "lookup_0", + "config": { + "enabled": true, + "alias": null, + "schema": "analytics", + "database": "GENERATED", + "tags": [], + "meta": {}, + "group": null, + "materialized": "seed", + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + }, + "delimiter": ",", + "quote_columns": null + }, + "tags": [], + "description": "Generated lookup 0.", + "columns": { + "id": { + "name": "id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "label": { + "name": "label", + "description": "", + "meta": {}, + "data_type": "VARCHAR", + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "GENERATED.analytics.lookup_0", + "root_path": null, + "depends_on": { + "macros": [] + } + }, + "seed.generated_0.lookup_1": { + "database": "GENERATED", + "schema": "analytics", + "name": "lookup_1", + "resource_type": "seed", + "package_name": "generated_0", + "path": "seeds/lookup_1.csv", + "original_file_path": "seeds/lookup_1.csv", + "unique_id": "seed.generated_0.lookup_1", + "fqn": [ + "generated_0", + "seeds", + "lookup_1" + ], + "alias": "lookup_1", + "config": { + "enabled": true, + "alias": null, + "schema": "analytics", + "database": "GENERATED", + "tags": [], + "meta": {}, + "group": null, + "materialized": "seed", + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + }, + "delimiter": ",", + "quote_columns": null + }, + "tags": [], + "description": "Generated lookup 1.", + "columns": { + "id": { + "name": "id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "label": { + "name": "label", + "description": "", + "meta": {}, + "data_type": "VARCHAR", + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "GENERATED.analytics.lookup_1", + "root_path": null, + "depends_on": { + "macros": [] + } + } + }, + "sources": { + "source.generated_0.open_events": { + "database": null, + "schema": "", + "name": "open_events", + "resource_type": "source", + "package_name": "generated_0", + "path": "sources/open.yml", + "original_file_path": "sources/open.yml", + "unique_id": "source.generated_0.open_events", + "fqn": [ + "generated_0", + "open_events" + ], + "source_name": "open_events", + "source_description": "", + "loader": "", + "identifier": "open_events", + "quoting": {}, + "loaded_at_field": null, + "freshness": null, + "external": null, + "description": "Events whose columns are only known to the warehouse.", + "columns": {}, + "meta": {}, + "source_meta": {}, + "tags": [], + "config": {}, + "patch_path": null, + "unrendered_config": {}, + "relation_name": null + }, + "source.generated_0.raw_events_0": { + "database": null, + "schema": "", + "name": "raw_events_0", + "resource_type": "source", + "package_name": "generated_0", + "path": "sources/raw.yml", + "original_file_path": "sources/raw.yml", + "unique_id": "source.generated_0.raw_events_0", + "fqn": [ + "generated_0", + "raw_events_0" + ], + "source_name": "raw_events_0", + "source_description": "", + "loader": "", + "identifier": "raw_events_0", + "quoting": {}, + "loaded_at_field": null, + "freshness": null, + "external": null, + "description": "Generated event feed 0.", + "columns": { + "id": { + "name": "id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "amount": { + "name": "amount", + "description": "", + "meta": {}, + "data_type": "DOUBLE", + "constraints": [], + "quote": null, + "tags": [] + }, + "status": { + "name": "status", + "description": "", + "meta": {}, + "data_type": "VARCHAR", + "constraints": [], + "quote": null, + "tags": [] + }, + "created_at": { + "name": "created_at", + "description": "", + "meta": {}, + "data_type": "TIMESTAMP", + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": {}, + "source_meta": {}, + "tags": [], + "config": {}, + "patch_path": null, + "unrendered_config": {}, + "relation_name": null + }, + "source.generated_0.raw_events_1": { + "database": null, + "schema": "", + "name": "raw_events_1", + "resource_type": "source", + "package_name": "generated_0", + "path": "sources/raw.yml", + "original_file_path": "sources/raw.yml", + "unique_id": "source.generated_0.raw_events_1", + "fqn": [ + "generated_0", + "raw_events_1" + ], + "source_name": "raw_events_1", + "source_description": "", + "loader": "", + "identifier": "raw_events_1", + "quoting": {}, + "loaded_at_field": null, + "freshness": null, + "external": null, + "description": "Generated event feed 1.", + "columns": { + "id": { + "name": "id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "amount": { + "name": "amount", + "description": "", + "meta": {}, + "data_type": "DOUBLE", + "constraints": [], + "quote": null, + "tags": [] + }, + "status": { + "name": "status", + "description": "", + "meta": {}, + "data_type": "VARCHAR", + "constraints": [], + "quote": null, + "tags": [] + }, + "created_at": { + "name": "created_at", + "description": "", + "meta": {}, + "data_type": "TIMESTAMP", + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": {}, + "source_meta": {}, + "tags": [], + "config": {}, + "patch_path": null, + "unrendered_config": {}, + "relation_name": null + } + }, + "macros": { + "macro.generated_0.adjust_0": { + "name": "adjust_0", + "resource_type": "macro", + "package_name": "generated_0", + "path": "macros/adjust_0.py", + "original_file_path": "macros/adjust_0.py", + "unique_id": "macro.generated_0.adjust_0", + "macro_sql": "def adjust_0_round(expression: str) -> str:\n \"\"\"Round an amount to two places.\"\"\"\n return f\"ROUND({expression}, 2)\"\n\n\ndef adjust_0(expression: str) -> str:\n \"\"\"Double and round an amount.\"\"\"\n return adjust_0_round(f\"({expression} * 2)\")\n", + "depends_on": { + "macros": [ + "macro.generated_0.adjust_0_round" + ] + }, + "description": "", + "meta": { + "sqlbuild_visibility": "global", + "sqlbuild_scope_path": null + }, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "arguments": [], + "supported_languages": null + }, + "macro.generated_0.adjust_0_round": { + "name": "adjust_0_round", + "resource_type": "macro", + "package_name": "generated_0", + "path": "macros/adjust_0.py", + "original_file_path": "macros/adjust_0.py", + "unique_id": "macro.generated_0.adjust_0_round", + "macro_sql": "def adjust_0_round(expression: str) -> str:\n \"\"\"Round an amount to two places.\"\"\"\n return f\"ROUND({expression}, 2)\"\n\n\ndef adjust_0(expression: str) -> str:\n \"\"\"Double and round an amount.\"\"\"\n return adjust_0_round(f\"({expression} * 2)\")\n", + "depends_on": { + "macros": [] + }, + "description": "", + "meta": { + "sqlbuild_visibility": "global", + "sqlbuild_scope_path": null + }, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "arguments": [], + "supported_languages": null + }, + "macro.generated_0.adjust_2": { + "name": "adjust_2", + "resource_type": "macro", + "package_name": "generated_0", + "path": "macros/adjust_2.py", + "original_file_path": "macros/adjust_2.py", + "unique_id": "macro.generated_0.adjust_2", + "macro_sql": "def adjust_2(ctx, expression: str) -> str:\n \"\"\"Scale an amount by the configured discount.\"\"\"\n rate = ctx.vars[\"discount_rate\"]\n return f\"({expression} * (1 - {rate}))\"\n", + "depends_on": { + "macros": [] + }, + "description": "", + "meta": { + "sqlbuild_visibility": "global", + "sqlbuild_scope_path": null + }, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "arguments": [], + "supported_languages": null + }, + "macro.generated_0.analysis_region": { + "name": "analysis_region", + "resource_type": "macro", + "package_name": "generated_0", + "path": "models/analysis/_sqlbuild/_macros/regions.py", + "original_file_path": "models/analysis/_sqlbuild/_macros/regions.py", + "unique_id": "macro.generated_0.analysis_region", + "macro_sql": "def analysis_region() -> str:\n \"\"\"Return the region rows are assigned to.\"\"\"\n return \"'east'\"\n", + "depends_on": { + "macros": [] + }, + "description": "", + "meta": { + "sqlbuild_visibility": "local", + "sqlbuild_scope_path": "models/analysis" + }, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "arguments": [], + "supported_languages": null + }, + "macro.generated_0.adjust_1": { + "name": "adjust_1", + "resource_type": "macro", + "package_name": "generated_0", + "path": "models/fulfillment/staging/_sqlbuild/_macros/adjust_1.py", + "original_file_path": "models/fulfillment/staging/_sqlbuild/_macros/adjust_1.py", + "unique_id": "macro.generated_0.adjust_1", + "macro_sql": "import os\n\n\ndef adjust_1(expression: str) -> str:\n \"\"\"Tag an amount with the deployment region.\"\"\"\n region = os.environ.get(\"SQB_DIFFERENTIAL_REGION\", \"east\")\n return f\"({expression} + LENGTH('{region}'))\"\n", + "depends_on": { + "macros": [] + }, + "description": "", + "meta": { + "sqlbuild_visibility": "local", + "sqlbuild_scope_path": "models/fulfillment/staging" + }, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "arguments": [], + "supported_languages": null + }, + "macro.generated_0__sqlbuild_hooks.record_refresh": { + "name": "record_refresh", + "resource_type": "macro", + "package_name": "generated_0__sqlbuild_hooks", + "path": "hooks/sql/record_refresh.sql", + "original_file_path": "hooks/sql/record_refresh.sql", + "unique_id": "macro.generated_0__sqlbuild_hooks.record_refresh", + "macro_sql": "SELECT @'relation' AS refreshed_relation, @'label' AS refresh_label", + "depends_on": { + "macros": [] + }, + "description": "Record a refresh for one relation", + "meta": { + "sqlbuild_resource_type": "sql_hook", + "sqlbuild_hook_name": "record_refresh" + }, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "arguments": [], + "supported_languages": null + } + }, + "exposures": {}, + "metrics": {}, + "groups": {}, + "selectors": {}, + "disabled": {}, + "parent_map": { + "model.generated_0.banded_amounts": [ + "model.generated_0.table_fn__amount_bands" + ], + "model.generated_0.chain_1_stg_sales_0": [ + "model.generated_0.contracted_stg_sales_0" + ], + "model.generated_0.chain_2_stg_sales_0": [ + "model.generated_0.chain_1_stg_sales_0" + ], + "model.generated_0.chain_3_stg_sales_0": [ + "model.generated_0.chain_2_stg_sales_0" + ], + "model.generated_0.checked_stg_fulfillment_1": [ + "model.generated_0.stg_fulfillment_1" + ], + "model.generated_0.combined_stg_fulfillment_0": [ + "model.generated_0.stg_fulfillment_0", + "model.generated_0.stg_sales_1" + ], + "model.generated_0.contracted_stg_sales_0": [ + "source.generated_0.raw_events_0" + ], + "model.generated_0.every_open_event": [ + "source.generated_0.open_events" + ], + "model.generated_0.every_raw_events_0": [ + "source.generated_0.raw_events_0" + ], + "model.generated_0.every_stg_fulfillment_0": [ + "model.generated_0.stg_fulfillment_0" + ], + "model.generated_0.passed_stg_fulfillment_0": [ + "model.generated_0.stg_fulfillment_0" + ], + "model.generated_0.regional_stg_fulfillment_1": [ + "model.generated_0.stg_fulfillment_1" + ], + "model.generated_0.scaled_stg_fulfillment_0": [ + "model.generated_0.scaled_amount", + "model.generated_0.stg_fulfillment_0" + ], + "model.generated_0.typed_stg_fulfillment_0": [ + "model.generated_0.stg_fulfillment_0" + ], + "model.generated_0.unanalysed_stg_fulfillment_0": [ + "model.generated_0.stg_fulfillment_0" + ], + "model.generated_0.fulfillment_summary_0": [ + "model.generated_0.stg_fulfillment_0", + "model.generated_0.stg_sales_0", + "seed.generated_0.lookup_1" + ], + "model.generated_0.fulfillment_summary_1": [ + "model.generated_0.stg_fulfillment_0" + ], + "model.generated_0.fulfillment_summary_2": [ + "model.generated_0.stg_fulfillment_1", + "seed.generated_0.lookup_0" + ], + "model.generated_0.stg_fulfillment_0": [ + "source.generated_0.raw_events_1" + ], + "model.generated_0.stg_fulfillment_1": [ + "source.generated_0.raw_events_0" + ], + "model.generated_0.sales_summary_0": [ + "model.generated_0.stg_sales_0", + "seed.generated_0.lookup_1" + ], + "model.generated_0.sales_summary_1": [ + "model.generated_0.stg_sales_0", + "seed.generated_0.lookup_0" + ], + "model.generated_0.sales_summary_2": [ + "model.generated_0.stg_sales_1", + "model.generated_0.stg_fulfillment_0", + "seed.generated_0.lookup_1" + ], + "model.generated_0.stg_sales_0": [ + "source.generated_0.raw_events_0" + ], + "model.generated_0.stg_sales_1": [ + "source.generated_0.raw_events_1" + ], + "source.generated_0.open_events": [], + "source.generated_0.raw_events_0": [], + "source.generated_0.raw_events_1": [], + "seed.generated_0.lookup_0": [], + "seed.generated_0.lookup_1": [], + "model.generated_0.scaled_amount": [], + "model.generated_0.table_fn__amount_bands": [ + "model.generated_0.stg_fulfillment_0" + ] + }, + "group_map": {}, + "docs": {}, + "saved_queries": {}, + "semantic_models": {}, + "unit_tests": {} + } +} diff --git a/tests/goldens/compiler/seed/0.json b/tests/goldens/compiler/seed/0.json new file mode 100644 index 0000000000..28b8246e6b --- /dev/null +++ b/tests/goldens/compiler/seed/0.json @@ -0,0 +1,3479 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 0, + "diagnostics": [] + }, + { + "label": "compile-store-warm", + "exit_code": 0, + "diagnostics": [] + }, + { + "label": "plan", + "exit_code": 0, + "diagnostics": [] + }, + { + "label": "compile-select", + "exit_code": 0, + "diagnostics": [] + }, + { + "label": "compile-no-analysis", + "exit_code": 0, + "diagnostics": [] + }, + { + "label": "compile-rich-lineage", + "exit_code": 0, + "diagnostics": [] + } + ], + "compiled": { + "audits/generic/configured_events/accepted_values__id.sql": [ + "SELECT id", + "FROM __source(\"configured_events\")", + "WHERE id IS NOT NULL", + " AND id NOT IN ('1')", + "" + ], + "audits/generic/configured_stg_sales_1/accepted_values__status.sql": [ + "SELECT status", + "FROM __ref(\"configured_stg_sales_1\")", + "WHERE status IS NOT NULL", + " AND status NOT IN ('placed', 'shipped')", + "" + ], + "audits/generic/configured_stg_sales_1/configured_amount_floor.sql": [ + "SELECT *", + "FROM __ref(\"configured_stg_sales_1\")", + "WHERE amount < -35", + "" + ], + "audits/generic/configured_stg_sales_1/configured_id_present__id.sql": [ + "SELECT id", + "FROM __ref(\"configured_stg_sales_1\")", + "WHERE id IS NULL", + "" + ], + "audits/generic/configured_stg_sales_1/unique__id.sql": [ + "SELECT id, COUNT(*) AS duplicate_count", + "FROM __ref(\"configured_stg_sales_1\")", + "WHERE id IS NOT NULL", + "GROUP BY id", + "HAVING COUNT(*) > 1", + "" + ], + "audits/generic/fulfillment_summary_0/not_null__id.sql": [ + "SELECT id", + "FROM __ref(\"fulfillment_summary_0\")", + "WHERE id IS NULL", + "" + ], + "audits/generic/labelled_stg_fulfillment_0/tidy_status__status.sql": [ + "SELECT *", + "FROM __ref(\"labelled_stg_fulfillment_0\")", + "WHERE status <> LOWER(TRIM(status))", + "" + ], + "audits/generic/sales_summary_0/not_null__id.sql": [ + "SELECT id", + "FROM __ref(\"sales_summary_0\")", + "WHERE id IS NULL", + "" + ], + "audits/generic/sales_summary_2/not_null__id.sql": [ + "SELECT id", + "FROM __ref(\"sales_summary_2\")", + "WHERE id IS NULL", + "" + ], + "audits/generic/shaped_stg_sales_0/not_null__id.sql": [ + "SELECT id", + "FROM __ref(\"shaped_stg_sales_0\")", + "WHERE id IS NULL", + "" + ], + "audits/generic/stg_fulfillment_0/not_null__id.sql": [ + "SELECT id", + "FROM __ref(\"stg_fulfillment_0\")", + "WHERE id IS NULL", + "" + ], + "audits/singular/fulfillment_summary_2_matches.sql": [ + "SELECT a.id", + "FROM __ref(\"fulfillment_summary_2\") a", + "LEFT JOIN __ref(\"fulfillment_summary_0\") b ON a.id = b.id", + "WHERE b.id IS NULL", + "" + ], + "audits/singular/labelled_channels_match.sql": [ + "SELECT l.id", + "FROM __ref(\"labelled_stg_fulfillment_0\") l", + "JOIN __ref(\"channel_labels\") c ON l.id = c.id", + "WHERE l.status = c.channel", + "" + ], + "functions/python/is_large.sql": [ + "REGISTER PYTHON FUNCTION is_large(DOUBLE) RETURNS BOOLEAN", + "" + ], + "functions/sql/configured_label.sql": [ + "CREATE OR REPLACE MACRO ci_checks.configured_label(raw_status) AS (", + "UPPER(raw_status)", + ")", + "" + ], + "functions/sql/is_placed.sql": [ + "CREATE OR REPLACE MACRO ci_checks.is_placed(event_status) AS (", + "event_status = 'placed'", + ")", + "" + ], + "functions/sql/table_fn__events_above.sql": [ + "CREATE OR REPLACE MACRO ci_checks.table_fn__events_above(p_minimum) AS TABLE", + "SELECT id, amount", + "FROM __ref(\"stg_sales_1\")", + "WHERE amount > p_minimum", + "" + ], + "functions/sql/tidy_status.sql": [ + "CREATE OR REPLACE MACRO ci_checks.tidy_status(raw_status) AS (", + "LOWER(TRIM(raw_status))", + ")", + "" + ], + "models/analysis/pivot_input_stg_sales_0.sql": [ + "SELECT CAST(id AS INTEGER) AS id, CAST(status AS VARCHAR) AS status, amount", + "FROM __ref(\"stg_sales_0\")", + "" + ], + "models/analysis/pivoted_stg_sales_0.sql": [ + "PIVOT __ref(\"pivot_input_stg_sales_0\")", + "ON status", + "USING MAX(amount)", + "GROUP BY id", + "" + ], + "models/analysis/regional_stg_fulfillment_0.sql": [ + "SELECT id, amount, 'east' AS region", + "FROM __ref(\"stg_fulfillment_0\")", + "" + ], + "models/analysis/unanalysed_stg_sales_0.sql": [ + "SELECT id, amount", + "FROM __ref(\"stg_sales_0\")", + "WHERE id = ANY [1, 2]", + "" + ], + "models/features/append_stg_fulfillment_1.sql": [ + "SELECT id, amount, status, created_at", + "FROM __ref(\"stg_fulfillment_1\")", + "" + ], + "models/features/batched_rolling_window_stg_sales_0.sql": [ + "SELECT CAST(created_at AS DATE) AS created_at, SUM(amount) AS amount", + "FROM __ref(\"stg_sales_0\")", + "GROUP BY CAST(created_at AS DATE)", + "" + ], + "models/features/flagged_stg_fulfillment_0.sql": [ + "SELECT id, __udf(\"is_placed\")(status) AS is_placed,", + " __udf(\"is_large\")(amount) AS is_large", + "FROM __ref(\"stg_fulfillment_0\")", + "" + ], + "models/features/product_labels.sql": [ + "SELECT id, label", + "FROM __source(\"loaded_products\")", + "" + ], + "models/features/shaped_stg_sales_0.sql": [ + "SELECT CAST(id AS INTEGER) AS id, status", + "FROM __ref(\"stg_sales_0\")", + "" + ], + "models/features/taxed_stg_fulfillment_0.sql": [ + "SELECT id, ROUND(amount * 1.2, 2) AS taxed, ROUND(amount, 1) AS rounded", + "FROM __ref(\"stg_fulfillment_0\")", + "" + ], + "models/fulfillment/marts/fulfillment_summary_0.sql": [ + "SELECT", + " t.id,", + " t.amount,", + " t.status,", + " t.created_at,", + " (t.amount * (1 - 0.1)) AS adjust_2_amount,", + " j2.label AS lookup_label_2,", + " 'naïve résumé' AS site_label", + "FROM __ref(\"stg_fulfillment_0\") t", + "LEFT JOIN __ref(\"stg_sales_0\") j1 ON t.id = j1.id", + "LEFT JOIN __seed(\"lookup_1\") j2 ON t.id = j2.id", + "" + ], + "models/fulfillment/marts/fulfillment_summary_1.sql": [ + "SELECT", + " t.id,", + " t.amount,", + " t.status,", + " t.created_at", + "FROM __ref(\"stg_fulfillment_0\") t", + "" + ], + "models/fulfillment/marts/fulfillment_summary_2.sql": [ + "SELECT", + " t.id,", + " t.amount,", + " t.status,", + " t.created_at,", + " (t.amount * (1 - 0.1)) AS adjust_2_amount,", + " j1.label AS lookup_label_1", + "FROM __ref(\"stg_fulfillment_1\") t", + "LEFT JOIN __seed(\"lookup_0\") j1 ON t.id = j1.id", + "" + ], + "models/fulfillment/staging/stg_fulfillment_0.sql": [ + "SELECT", + " t.id,", + " t.amount,", + " t.status,", + " t.created_at,", + " (t.amount + LENGTH('east')) AS adjust_1_amount,", + " 'naïve résumé' AS site_label", + "FROM __source(\"raw_events_1\") t", + "" + ], + "models/fulfillment/staging/stg_fulfillment_1.sql": [ + "SELECT", + " t.id,", + " t.amount,", + " t.status,", + " t.created_at,", + " 'Ελληνικά orders' AS threshold_0_label,", + " ROUND(ROUND((t.amount * 2), 2), 2) AS adjust_0_amount,", + " 'naïve résumé' AS site_label", + "FROM __source(\"raw_events_0\") t", + "WHERE t.status <> 'expédié'", + "" + ], + "models/rendering/bounded_stg_fulfillment_1.sql": [ + "SELECT id, amount, created_at", + "FROM __ref(\"stg_fulfillment_1\")", + "WHERE created_at >= __cursor_start()", + " AND created_at < __cursor_end()", + "" + ], + "models/rendering/channel_labels.sql": [ + "SELECT id, channel", + "FROM __source(\"labelled_channels\")", + "" + ], + "models/rendering/configured_stg_sales_1.sql": [ + "SELECT id, amount, status", + "FROM __ref(\"stg_sales_1\")", + "" + ], + "models/rendering/labelled_stg_fulfillment_0.sql": [ + "SELECT id, LOWER(TRIM(status)) AS status,", + " __udf(\"tidy_status\")(status) AS tidied", + "FROM __ref(\"stg_fulfillment_0\")", + "" + ], + "models/rendering/large_events.sql": [ + "SELECT id, amount", + "FROM __table_fn(\"table_fn__events_above\")(4)", + "" + ], + "models/rendering/latest_raw_events_1.sql": [ + "SELECT id, amount", + "FROM (SELECT id, amount FROM __source(\"raw_events_1\") ORDER BY id DESC LIMIT 2) AS latest", + "" + ], + "models/rendering/unioned_event_ids.sql": [ + "SELECT id", + "FROM (SELECT id FROM __ref(\"stg_sales_0\") UNION ALL SELECT id FROM __ref(\"stg_sales_1\")) AS unioned", + "" + ], + "models/sales/marts/sales_summary_0.sql": [ + "SELECT", + " t.id,", + " t.amount,", + " t.status,", + " t.created_at,", + " 'Ελληνικά orders' AS threshold_0_label,", + " j1.label AS lookup_label_1", + "FROM __ref(\"stg_sales_0\") t", + "LEFT JOIN __seed(\"lookup_1\") j1 ON t.id = j1.id", + "" + ], + "models/sales/marts/sales_summary_1.sql": [ + "SELECT", + " t.id,", + " t.amount,", + " t.status,", + " t.created_at,", + " (t.amount * (1 - 0.1)) AS adjust_2_amount,", + " j1.label AS lookup_label_1", + "FROM __ref(\"stg_sales_0\") t", + "LEFT JOIN __seed(\"lookup_0\") j1 ON t.id = j1.id", + "WHERE t.status <> '取消'", + "" + ], + "models/sales/marts/sales_summary_2.sql": [ + "SELECT", + " t.id,", + " t.amount,", + " t.status,", + " t.created_at,", + " 'naïve résumé' AS threshold_1_label,", + " ROUND(ROUND((t.amount * 2), 2), 2) AS adjust_0_amount,", + " j2.label AS lookup_label_2", + "FROM __ref(\"stg_sales_1\") t", + "LEFT JOIN __ref(\"stg_fulfillment_0\") j1 ON t.id = j1.id", + "LEFT JOIN __seed(\"lookup_1\") j2 ON t.id = j2.id", + "" + ], + "models/sales/staging/stg_sales_0.sql": [ + "SELECT", + " t.id,", + " t.amount,", + " t.status,", + " t.created_at,", + " t.amount * '1e-07' AS threshold_2_amount,", + " 'naïve résumé' AS site_label", + "FROM __source(\"raw_events_0\") t", + "" + ], + "models/sales/staging/stg_sales_1.sql": [ + "SELECT", + " t.id,", + " t.amount,", + " t.status,", + " t.created_at,", + " 'naïve résumé' AS threshold_1_label", + "FROM __source(\"raw_events_1\") t", + "" + ], + "tests/_chain_/labelled_stg_fulfillment_0__stg_fulfillment_0/test_labelled_stg_fulfillment_0.sql": [ + "WITH __source__raw_events_1 AS (SELECT 1 AS id, CAST(1 AS DOUBLE) AS amount, 'placed' AS status, TIMESTAMP '2026-01-01 00:00:00' AS created_at UNION ALL SELECT 2 AS id, CAST(2 AS DOUBLE) AS amount, 'placed' AS status, TIMESTAMP '2026-01-02 00:00:00' AS created_at),", + "__ref__stg_fulfillment_0 AS (SELECT", + " t.id,", + " t.amount,", + " t.status,", + " t.created_at,", + " (t.amount + LENGTH('east')) AS adjust_1_amount,", + " 'naïve résumé' AS site_label", + "FROM __source__raw_events_1 t),", + "__ref__labelled_stg_fulfillment_0 AS (SELECT id, LOWER(TRIM(status)) AS status,", + " ci_checks.tidy_status(status) AS tidied", + "FROM __ref__stg_fulfillment_0),", + "__actual__labelled_stg_fulfillment_0 AS (SELECT * FROM __ref__labelled_stg_fulfillment_0),", + "__expected__labelled_stg_fulfillment_0 AS (SELECT 1 AS id, LOWER(TRIM('placed')) AS status", + " UNION ALL", + " SELECT 2 AS id, 'placed' AS status)", + "SELECT 1 AS step_index, 'labelled_stg_fulfillment_0' AS model_name, (SELECT COUNT(*) FROM __actual__labelled_stg_fulfillment_0) AS actual_count, (SELECT COUNT(*) FROM __expected__labelled_stg_fulfillment_0) AS expected_count, (SELECT COUNT(*) FROM (SELECT id, status FROM __actual__labelled_stg_fulfillment_0 EXCEPT SELECT id, status FROM __expected__labelled_stg_fulfillment_0) AS __sqlbuild_mismatch) AS unexpected_count, (SELECT COUNT(*) FROM (SELECT id, status FROM __expected__labelled_stg_fulfillment_0 EXCEPT SELECT id, status FROM __actual__labelled_stg_fulfillment_0) AS __sqlbuild_missing) AS missing_count", + "" + ], + "tests/macro taxes_amounts/taxes_amounts.sql": [ + "WITH input_values AS (SELECT 10 AS amount),", + "__actual__macro_taxes_amounts AS (SELECT ROUND(amount * 1.2, 2) AS taxed", + " FROM input_values),", + "__expected__macro_taxes_amounts AS (SELECT 12.0 AS taxed)", + "SELECT 0 AS step_index, 'macro taxes_amounts' AS model_name, (SELECT COUNT(*) FROM __actual__macro_taxes_amounts) AS actual_count, (SELECT COUNT(*) FROM __expected__macro_taxes_amounts) AS expected_count, (SELECT COUNT(*) FROM (SELECT * FROM __actual__macro_taxes_amounts EXCEPT SELECT * FROM __expected__macro_taxes_amounts) AS __sqlbuild_mismatch) AS unexpected_count, (SELECT COUNT(*) FROM (SELECT * FROM __expected__macro_taxes_amounts EXCEPT SELECT * FROM __actual__macro_taxes_amounts) AS __sqlbuild_missing) AS missing_count", + "" + ], + "tests/regional_stg_fulfillment_0/test_regional_stg_fulfillment_0.sql": [ + "WITH __ref__stg_fulfillment_0 AS (SELECT 1 AS id, CAST(4 AS DOUBLE) AS amount, 'placed' AS status,", + " TIMESTAMP '2026-01-01 00:00:00' AS created_at),", + "__ref__regional_stg_fulfillment_0 AS (SELECT id, amount, 'west' AS region", + "FROM __ref__stg_fulfillment_0),", + "__actual__regional_stg_fulfillment_0 AS (SELECT * FROM __ref__regional_stg_fulfillment_0),", + "__expected__regional_stg_fulfillment_0 AS (SELECT 1 AS id, CAST(4 AS DOUBLE) AS amount, 'west' AS region)", + "SELECT 0 AS step_index, 'regional_stg_fulfillment_0' AS model_name, (SELECT COUNT(*) FROM __actual__regional_stg_fulfillment_0) AS actual_count, (SELECT COUNT(*) FROM __expected__regional_stg_fulfillment_0) AS expected_count, (SELECT COUNT(*) FROM (SELECT id, amount, region FROM __actual__regional_stg_fulfillment_0 EXCEPT SELECT id, amount, region FROM __expected__regional_stg_fulfillment_0) AS __sqlbuild_mismatch) AS unexpected_count, (SELECT COUNT(*) FROM (SELECT id, amount, region FROM __expected__regional_stg_fulfillment_0 EXCEPT SELECT id, amount, region FROM __actual__regional_stg_fulfillment_0) AS __sqlbuild_missing) AS missing_count", + "" + ], + "tests/stg_fulfillment_1/test_stg_fulfillment_1.sql": [ + "WITH __source__raw_events_0 AS (SELECT 1 AS id, CAST(10 AS DOUBLE) AS amount, 'placed' AS status,", + " TIMESTAMP '2026-01-01 00:00:00' AS created_at),", + "__ref__stg_fulfillment_1 AS (SELECT", + " t.id,", + " t.amount,", + " t.status,", + " t.created_at,", + " 'Ελληνικά orders' AS threshold_0_label,", + " ROUND(ROUND((t.amount * 2), 2), 2) AS adjust_0_amount,", + " 'naïve résumé' AS site_label", + "FROM __source__raw_events_0 t", + "WHERE t.status <> 'expédié'),", + "__actual__stg_fulfillment_1 AS (SELECT * FROM __ref__stg_fulfillment_1),", + "__expected__stg_fulfillment_1 AS (SELECT 1 AS id, 'placed' AS status)", + "SELECT 0 AS step_index, 'stg_fulfillment_1' AS model_name, (SELECT COUNT(*) FROM __actual__stg_fulfillment_1) AS actual_count, (SELECT COUNT(*) FROM __expected__stg_fulfillment_1) AS expected_count, (SELECT COUNT(*) FROM (SELECT id, status FROM __actual__stg_fulfillment_1 EXCEPT SELECT id, status FROM __expected__stg_fulfillment_1) AS __sqlbuild_mismatch) AS unexpected_count, (SELECT COUNT(*) FROM (SELECT id, status FROM __expected__stg_fulfillment_1 EXCEPT SELECT id, status FROM __actual__stg_fulfillment_1) AS __sqlbuild_missing) AS missing_count", + "" + ], + "tests/tests/unit/test_stg_sales_1_cases/block_1__labelled.sql": [ + "WITH __source__raw_events_1 AS (SELECT 1 AS id, CAST(2.25 AS DOUBLE) AS amount,", + " '東京 warehouse' AS status,", + " TIMESTAMP '2026-01-01 00:00:00' AS created_at),", + "__ref__stg_sales_1 AS (SELECT", + " t.id,", + " t.amount,", + " t.status,", + " t.created_at,", + " 'naïve résumé' AS threshold_1_label", + "FROM __source__raw_events_1 t),", + "__actual__stg_sales_1 AS (SELECT * FROM __ref__stg_sales_1),", + "__expected__stg_sales_1 AS (SELECT 1 AS id)", + "SELECT 0 AS step_index, 'stg_sales_1' AS model_name, (SELECT COUNT(*) FROM __actual__stg_sales_1) AS actual_count, (SELECT COUNT(*) FROM __expected__stg_sales_1) AS expected_count, (SELECT COUNT(*) FROM (SELECT id FROM __actual__stg_sales_1 EXCEPT SELECT id FROM __expected__stg_sales_1) AS __sqlbuild_mismatch) AS unexpected_count, (SELECT COUNT(*) FROM (SELECT id FROM __expected__stg_sales_1 EXCEPT SELECT id FROM __actual__stg_sales_1) AS __sqlbuild_missing) AS missing_count", + "" + ], + "tests/tests/unit/test_stg_sales_1_cases/block_1__placed.sql": [ + "WITH __source__raw_events_1 AS (SELECT 1 AS id, CAST(1.5 AS DOUBLE) AS amount,", + " 'placed' AS status,", + " TIMESTAMP '2026-01-01 00:00:00' AS created_at),", + "__ref__stg_sales_1 AS (SELECT", + " t.id,", + " t.amount,", + " t.status,", + " t.created_at,", + " 'naïve résumé' AS threshold_1_label", + "FROM __source__raw_events_1 t),", + "__actual__stg_sales_1 AS (SELECT * FROM __ref__stg_sales_1),", + "__expected__stg_sales_1 AS (SELECT 1 AS id)", + "SELECT 0 AS step_index, 'stg_sales_1' AS model_name, (SELECT COUNT(*) FROM __actual__stg_sales_1) AS actual_count, (SELECT COUNT(*) FROM __expected__stg_sales_1) AS expected_count, (SELECT COUNT(*) FROM (SELECT id FROM __actual__stg_sales_1 EXCEPT SELECT id FROM __expected__stg_sales_1) AS __sqlbuild_mismatch) AS unexpected_count, (SELECT COUNT(*) FROM (SELECT id FROM __expected__stg_sales_1 EXCEPT SELECT id FROM __actual__stg_sales_1) AS __sqlbuild_missing) AS missing_count", + "" + ], + "tests/udf detects_placed_events/detects_placed_events.sql": [ + "WITH input_values AS (SELECT 'placed' AS event_status),", + "__actual__udf_detects_placed_events AS (SELECT event_status, ci_checks.is_placed(event_status) AS is_placed", + " FROM input_values),", + "__expected__udf_detects_placed_events AS (SELECT 'placed' AS event_status, TRUE AS is_placed)", + "SELECT 0 AS step_index, 'udf detects_placed_events' AS model_name, (SELECT COUNT(*) FROM __actual__udf_detects_placed_events) AS actual_count, (SELECT COUNT(*) FROM __expected__udf_detects_placed_events) AS expected_count, (SELECT COUNT(*) FROM (SELECT * FROM __actual__udf_detects_placed_events EXCEPT SELECT * FROM __expected__udf_detects_placed_events) AS __sqlbuild_mismatch) AS unexpected_count, (SELECT COUNT(*) FROM (SELECT * FROM __expected__udf_detects_placed_events EXCEPT SELECT * FROM __actual__udf_detects_placed_events) AS __sqlbuild_missing) AS missing_count", + "" + ] + }, + "manifest": { + "nodes": { + "model.generated_0.pivot_input_stg_sales_0": { + "database": null, + "schema": "ci_checks", + "name": "pivot_input_stg_sales_0", + "resource_type": "model", + "package_name": "generated_0", + "path": "models/analysis/pivot_input_stg_sales_0.sql", + "original_file_path": "models/analysis/pivot_input_stg_sales_0.sql", + "unique_id": "model.generated_0.pivot_input_stg_sales_0", + "fqn": [ + "generated_0", + "models", + "analysis", + "pivot_input_stg_sales_0" + ], + "alias": "pivot_input_stg_sales_0", + "config": { + "enabled": true, + "alias": null, + "schema": "ci_checks", + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "table", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Contracted pivot input from stg_sales_0", + "columns": { + "id": { + "name": "id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "status": { + "name": "status", + "description": "", + "meta": {}, + "data_type": "VARCHAR", + "constraints": [], + "quote": null, + "tags": [] + }, + "amount": { + "name": "amount", + "description": "", + "meta": {}, + "data_type": "DOUBLE", + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "ci_checks.pivot_input_stg_sales_0", + "depends_on": { + "macros": [], + "nodes": [ + "model.generated_0.stg_sales_0" + ] + } + }, + "model.generated_0.pivoted_stg_sales_0": { + "database": null, + "schema": "ci_checks", + "name": "pivoted_stg_sales_0", + "resource_type": "model", + "package_name": "generated_0", + "path": "models/analysis/pivoted_stg_sales_0.sql", + "original_file_path": "models/analysis/pivoted_stg_sales_0.sql", + "unique_id": "model.generated_0.pivoted_stg_sales_0", + "fqn": [ + "generated_0", + "models", + "analysis", + "pivoted_stg_sales_0" + ], + "alias": "pivoted_stg_sales_0", + "config": { + "enabled": true, + "alias": null, + "schema": "ci_checks", + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "table", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Amounts of stg_sales_0 pivoted by status", + "columns": { + "id": { + "name": "id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": { + "sqlbuild": { + "dynamic_columns": [ + { + "name": "status_amounts", + "pivot_column": "status", + "value_column": "amount", + "aggregate": "MAX", + "type": "DOUBLE", + "name_pattern": null + } + ] + } + }, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "ci_checks.pivoted_stg_sales_0", + "depends_on": { + "macros": [], + "nodes": [ + "model.generated_0.pivot_input_stg_sales_0" + ] + } + }, + "model.generated_0.regional_stg_fulfillment_0": { + "database": null, + "schema": "ci_checks", + "name": "regional_stg_fulfillment_0", + "resource_type": "model", + "package_name": "generated_0", + "path": "models/analysis/regional_stg_fulfillment_0.sql", + "original_file_path": "models/analysis/regional_stg_fulfillment_0.sql", + "unique_id": "model.generated_0.regional_stg_fulfillment_0", + "fqn": [ + "generated_0", + "models", + "analysis", + "regional_stg_fulfillment_0" + ], + "alias": "regional_stg_fulfillment_0", + "config": { + "enabled": true, + "alias": null, + "schema": "ci_checks", + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "table", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Regional copy of stg_fulfillment_0", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "ci_checks.regional_stg_fulfillment_0", + "depends_on": { + "macros": [], + "nodes": [ + "model.generated_0.stg_fulfillment_0" + ] + } + }, + "model.generated_0.unanalysed_stg_sales_0": { + "database": null, + "schema": "ci_checks", + "name": "unanalysed_stg_sales_0", + "resource_type": "model", + "package_name": "generated_0", + "path": "models/analysis/unanalysed_stg_sales_0.sql", + "original_file_path": "models/analysis/unanalysed_stg_sales_0.sql", + "unique_id": "model.generated_0.unanalysed_stg_sales_0", + "fqn": [ + "generated_0", + "models", + "analysis", + "unanalysed_stg_sales_0" + ], + "alias": "unanalysed_stg_sales_0", + "config": { + "enabled": true, + "alias": null, + "schema": "ci_checks", + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "table", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Copy of stg_sales_0 that SQL analysis cannot parse", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "ci_checks.unanalysed_stg_sales_0", + "depends_on": { + "macros": [], + "nodes": [ + "model.generated_0.stg_sales_0" + ] + } + }, + "model.generated_0.append_stg_fulfillment_1": { + "database": null, + "schema": "ci_checks", + "name": "append_stg_fulfillment_1", + "resource_type": "model", + "package_name": "generated_0", + "path": "models/features/append_stg_fulfillment_1.sql", + "original_file_path": "models/features/append_stg_fulfillment_1.sql", + "unique_id": "model.generated_0.append_stg_fulfillment_1", + "fqn": [ + "generated_0", + "models", + "features", + "append_stg_fulfillment_1" + ], + "alias": "append_stg_fulfillment_1", + "config": { + "enabled": true, + "alias": null, + "schema": "ci_checks", + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "incremental", + "incremental_strategy": "append", + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Incremental append copy of stg_fulfillment_1", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "ci_checks.append_stg_fulfillment_1", + "depends_on": { + "macros": [], + "nodes": [ + "model.generated_0.stg_fulfillment_1" + ] + } + }, + "model.generated_0.batched_rolling_window_stg_sales_0": { + "database": null, + "schema": "ci_checks", + "name": "batched_rolling_window_stg_sales_0", + "resource_type": "model", + "package_name": "generated_0", + "path": "models/features/batched_rolling_window_stg_sales_0.sql", + "original_file_path": "models/features/batched_rolling_window_stg_sales_0.sql", + "unique_id": "model.generated_0.batched_rolling_window_stg_sales_0", + "fqn": [ + "generated_0", + "models", + "features", + "batched_rolling_window_stg_sales_0" + ], + "alias": "batched_rolling_window_stg_sales_0", + "config": { + "enabled": true, + "alias": null, + "schema": "ci_checks", + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "incremental", + "incremental_strategy": "delete_insert", + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Microbatched rolling_window totals of stg_sales_0", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "ci_checks.batched_rolling_window_stg_sales_0", + "depends_on": { + "macros": [], + "nodes": [ + "model.generated_0.stg_sales_0" + ] + } + }, + "model.generated_0.flagged_stg_fulfillment_0": { + "database": null, + "schema": "ci_checks", + "name": "flagged_stg_fulfillment_0", + "resource_type": "model", + "package_name": "generated_0", + "path": "models/features/flagged_stg_fulfillment_0.sql", + "original_file_path": "models/features/flagged_stg_fulfillment_0.sql", + "unique_id": "model.generated_0.flagged_stg_fulfillment_0", + "fqn": [ + "generated_0", + "models", + "features", + "flagged_stg_fulfillment_0" + ], + "alias": "flagged_stg_fulfillment_0", + "config": { + "enabled": true, + "alias": null, + "schema": "ci_checks", + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "table", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Function-flagged copy of stg_fulfillment_0", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "ci_checks.flagged_stg_fulfillment_0", + "depends_on": { + "macros": [], + "nodes": [ + "model.generated_0.is_placed", + "model.generated_0.is_large", + "model.generated_0.stg_fulfillment_0" + ] + } + }, + "model.generated_0.product_labels": { + "database": null, + "schema": "ci_checks", + "name": "product_labels", + "resource_type": "model", + "package_name": "generated_0", + "path": "models/features/product_labels.sql", + "original_file_path": "models/features/product_labels.sql", + "unique_id": "model.generated_0.product_labels", + "fqn": [ + "generated_0", + "models", + "features", + "product_labels" + ], + "alias": "product_labels", + "config": { + "enabled": true, + "alias": null, + "schema": "ci_checks", + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Product labels from the loader", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "ci_checks.product_labels", + "depends_on": { + "macros": [], + "nodes": [ + "source.generated_0.loaded_products" + ] + } + }, + "model.generated_0.shaped_stg_sales_0": { + "database": null, + "schema": "ci_checks", + "name": "shaped_stg_sales_0", + "resource_type": "model", + "package_name": "generated_0", + "path": "models/features/shaped_stg_sales_0.sql", + "original_file_path": "models/features/shaped_stg_sales_0.sql", + "unique_id": "model.generated_0.shaped_stg_sales_0", + "fqn": [ + "generated_0", + "models", + "features", + "shaped_stg_sales_0" + ], + "alias": "shaped_stg_sales_0", + "config": { + "enabled": true, + "alias": null, + "schema": "ci_checks", + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "table", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Schema-shaped copy of stg_sales_0", + "columns": { + "id": { + "name": "id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "status": { + "name": "status", + "description": "", + "meta": {}, + "data_type": "VARCHAR", + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "ci_checks.shaped_stg_sales_0", + "depends_on": { + "macros": [], + "nodes": [ + "model.generated_0.stg_sales_0" + ] + } + }, + "model.generated_0.taxed_stg_fulfillment_0": { + "database": null, + "schema": "ci_checks", + "name": "taxed_stg_fulfillment_0", + "resource_type": "model", + "package_name": "generated_0", + "path": "models/features/taxed_stg_fulfillment_0.sql", + "original_file_path": "models/features/taxed_stg_fulfillment_0.sql", + "unique_id": "model.generated_0.taxed_stg_fulfillment_0", + "fqn": [ + "generated_0", + "models", + "features", + "taxed_stg_fulfillment_0" + ], + "alias": "taxed_stg_fulfillment_0", + "config": { + "enabled": true, + "alias": null, + "schema": "ci_checks", + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "table", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Taxed copy of stg_fulfillment_0", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "ci_checks.taxed_stg_fulfillment_0", + "depends_on": { + "macros": [], + "nodes": [ + "model.generated_0.stg_fulfillment_0" + ] + } + }, + "model.generated_0.fulfillment_summary_0": { + "database": null, + "schema": "ci_checks", + "name": "fulfillment_summary_0", + "resource_type": "model", + "package_name": "generated_0", + "path": "models/fulfillment/marts/fulfillment_summary_0.sql", + "original_file_path": "models/fulfillment/marts/fulfillment_summary_0.sql", + "unique_id": "model.generated_0.fulfillment_summary_0", + "fqn": [ + "generated_0", + "models", + "fulfillment", + "marts", + "fulfillment_summary_0" + ], + "alias": "fulfillment_summary_0", + "config": { + "enabled": true, + "alias": null, + "schema": "ci_checks", + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "table", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Summary of fulfillment activity 0", + "columns": { + "id": { + "name": "id", + "description": "", + "meta": {}, + "data_type": null, + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "ci_checks.fulfillment_summary_0", + "depends_on": { + "macros": [], + "nodes": [ + "model.generated_0.stg_fulfillment_0", + "model.generated_0.stg_sales_0", + "seed.generated_0.lookup_1" + ] + } + }, + "model.generated_0.fulfillment_summary_1": { + "database": null, + "schema": "ci_checks", + "name": "fulfillment_summary_1", + "resource_type": "model", + "package_name": "generated_0", + "path": "models/fulfillment/marts/fulfillment_summary_1.sql", + "original_file_path": "models/fulfillment/marts/fulfillment_summary_1.sql", + "unique_id": "model.generated_0.fulfillment_summary_1", + "fqn": [ + "generated_0", + "models", + "fulfillment", + "marts", + "fulfillment_summary_1" + ], + "alias": "fulfillment_summary_1", + "config": { + "enabled": true, + "alias": null, + "schema": "ci_checks", + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "table", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Summary of fulfillment activity 1", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "ci_checks.fulfillment_summary_1", + "depends_on": { + "macros": [], + "nodes": [ + "model.generated_0.stg_fulfillment_0" + ] + } + }, + "model.generated_0.fulfillment_summary_2": { + "database": null, + "schema": "ci_checks", + "name": "fulfillment_summary_2", + "resource_type": "model", + "package_name": "generated_0", + "path": "models/fulfillment/marts/fulfillment_summary_2.sql", + "original_file_path": "models/fulfillment/marts/fulfillment_summary_2.sql", + "unique_id": "model.generated_0.fulfillment_summary_2", + "fqn": [ + "generated_0", + "models", + "fulfillment", + "marts", + "fulfillment_summary_2" + ], + "alias": "fulfillment_summary_2", + "config": { + "enabled": true, + "alias": null, + "schema": "ci_checks", + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "table", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [ + { + "sql": "SELECT 'ci_checks.fulfillment_summary_2' AS refreshed_relation, 'Zürich – depot' AS refresh_label", + "transaction": true, + "index": 0 + }, + { + "sql": "SELECT 'ci_checks.fulfillment_summary_2' AS refreshed", + "transaction": true, + "index": 1 + } + ], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Summary of fulfillment activity 2 (naïve résumé)", + "columns": {}, + "meta": { + "sqlbuild": { + "lifecycle_hooks": { + "post_hooks": [ + { + "type": "sql", + "statement": "SELECT 'ci_checks.fulfillment_summary_2' AS refreshed_relation, 'Zürich – depot' AS refresh_label", + "name": "record_refresh", + "relative_path": "hooks/sql/record_refresh.sql", + "definition_sql": "SELECT @'relation' AS refreshed_relation, @'label' AS refresh_label", + "kwargs": { + "relation": "@@CTX:destination.qualified", + "label": "Zürich – depot" + }, + "description": "Record a refresh for one relation" + }, + { + "type": "sql", + "statement": "SELECT 'ci_checks.fulfillment_summary_2' AS refreshed" + } + ] + } + } + }, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "ci_checks.fulfillment_summary_2", + "depends_on": { + "macros": [], + "nodes": [ + "model.generated_0.stg_fulfillment_1", + "seed.generated_0.lookup_0" + ] + } + }, + "model.generated_0.stg_fulfillment_0": { + "database": null, + "schema": "ci_checks", + "name": "stg_fulfillment_0", + "resource_type": "model", + "package_name": "generated_0", + "path": "models/fulfillment/staging/stg_fulfillment_0.sql", + "original_file_path": "models/fulfillment/staging/stg_fulfillment_0.sql", + "unique_id": "model.generated_0.stg_fulfillment_0", + "fqn": [ + "generated_0", + "models", + "fulfillment", + "staging", + "stg_fulfillment_0" + ], + "alias": "stg_fulfillment_0", + "config": { + "enabled": true, + "alias": null, + "schema": "ci_checks", + "database": null, + "tags": [ + "staging" + ], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [ + "staging" + ], + "description": "Staged fulfillment events 0", + "columns": { + "id": { + "name": "id", + "description": "", + "meta": {}, + "data_type": null, + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "ci_checks.stg_fulfillment_0", + "depends_on": { + "macros": [], + "nodes": [ + "source.generated_0.raw_events_1" + ] + } + }, + "model.generated_0.stg_fulfillment_1": { + "database": null, + "schema": "ci_checks", + "name": "stg_fulfillment_1", + "resource_type": "model", + "package_name": "generated_0", + "path": "models/fulfillment/staging/stg_fulfillment_1.sql", + "original_file_path": "models/fulfillment/staging/stg_fulfillment_1.sql", + "unique_id": "model.generated_0.stg_fulfillment_1", + "fqn": [ + "generated_0", + "models", + "fulfillment", + "staging", + "stg_fulfillment_1" + ], + "alias": "stg_fulfillment_1", + "config": { + "enabled": true, + "alias": null, + "schema": "ci_checks", + "database": null, + "tags": [ + "staging" + ], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [ + "staging" + ], + "description": "Staged fulfillment events 1", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "ci_checks.stg_fulfillment_1", + "depends_on": { + "macros": [], + "nodes": [ + "source.generated_0.raw_events_0" + ] + } + }, + "model.generated_0.bounded_stg_fulfillment_1": { + "database": null, + "schema": "ci_checks", + "name": "bounded_stg_fulfillment_1", + "resource_type": "model", + "package_name": "generated_0", + "path": "models/rendering/bounded_stg_fulfillment_1.sql", + "original_file_path": "models/rendering/bounded_stg_fulfillment_1.sql", + "unique_id": "model.generated_0.bounded_stg_fulfillment_1", + "fqn": [ + "generated_0", + "models", + "rendering", + "bounded_stg_fulfillment_1" + ], + "alias": "bounded_stg_fulfillment_1", + "config": { + "enabled": true, + "alias": null, + "schema": "ci_checks", + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "incremental", + "incremental_strategy": "delete_insert", + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": [ + "id" + ], + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Cursor-bounded copy of stg_fulfillment_1", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "ci_checks.bounded_stg_fulfillment_1", + "depends_on": { + "macros": [], + "nodes": [ + "model.generated_0.stg_fulfillment_1" + ] + } + }, + "model.generated_0.channel_labels": { + "database": null, + "schema": "ci_checks", + "name": "channel_labels", + "resource_type": "model", + "package_name": "generated_0", + "path": "models/rendering/channel_labels.sql", + "original_file_path": "models/rendering/channel_labels.sql", + "unique_id": "model.generated_0.channel_labels", + "fqn": [ + "generated_0", + "models", + "rendering", + "channel_labels" + ], + "alias": "channel_labels", + "config": { + "enabled": true, + "alias": null, + "schema": "ci_checks", + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "table", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Normalised channel labels", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "ci_checks.channel_labels", + "depends_on": { + "macros": [], + "nodes": [ + "source.generated_0.labelled_channels" + ] + } + }, + "model.generated_0.configured_stg_sales_1": { + "database": null, + "schema": "ci_checks", + "name": "configured_stg_sales_1", + "resource_type": "model", + "package_name": "generated_0", + "path": "models/rendering/configured_stg_sales_1.sql", + "original_file_path": "models/rendering/configured_stg_sales_1.sql", + "unique_id": "model.generated_0.configured_stg_sales_1", + "fqn": [ + "generated_0", + "models", + "rendering", + "configured_stg_sales_1" + ], + "alias": "configured_stg_sales_1", + "config": { + "enabled": true, + "alias": null, + "schema": "ci_checks", + "database": null, + "tags": [ + "release" + ], + "meta": {}, + "group": null, + "materialized": "table", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [ + "release" + ], + "description": "Configured copy of stg_sales_1 (réglé)", + "columns": { + "id": { + "name": "id", + "description": "Row key", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "amount": { + "name": "amount", + "description": "Montant réglé", + "meta": {}, + "data_type": "DOUBLE", + "constraints": [], + "quote": null, + "tags": [] + }, + "status": { + "name": "status", + "description": "", + "meta": {}, + "data_type": null, + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "ci_checks.configured_stg_sales_1", + "depends_on": { + "macros": [], + "nodes": [ + "model.generated_0.stg_sales_1" + ] + } + }, + "model.generated_0.labelled_stg_fulfillment_0": { + "database": null, + "schema": "ci_checks", + "name": "labelled_stg_fulfillment_0", + "resource_type": "model", + "package_name": "generated_0", + "path": "models/rendering/labelled_stg_fulfillment_0.sql", + "original_file_path": "models/rendering/labelled_stg_fulfillment_0.sql", + "unique_id": "model.generated_0.labelled_stg_fulfillment_0", + "fqn": [ + "generated_0", + "models", + "rendering", + "labelled_stg_fulfillment_0" + ], + "alias": "labelled_stg_fulfillment_0", + "config": { + "enabled": true, + "alias": null, + "schema": "ci_checks", + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "table", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Labelled copy of stg_fulfillment_0", + "columns": { + "status": { + "name": "status", + "description": "", + "meta": {}, + "data_type": null, + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "ci_checks.labelled_stg_fulfillment_0", + "depends_on": { + "macros": [], + "nodes": [ + "model.generated_0.tidy_status", + "model.generated_0.stg_fulfillment_0" + ] + } + }, + "model.generated_0.large_events": { + "database": null, + "schema": "ci_checks", + "name": "large_events", + "resource_type": "model", + "package_name": "generated_0", + "path": "models/rendering/large_events.sql", + "original_file_path": "models/rendering/large_events.sql", + "unique_id": "model.generated_0.large_events", + "fqn": [ + "generated_0", + "models", + "rendering", + "large_events" + ], + "alias": "large_events", + "config": { + "enabled": true, + "alias": null, + "schema": "ci_checks", + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "table", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Events above a threshold from a table function", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "ci_checks.large_events", + "depends_on": { + "macros": [], + "nodes": [ + "model.generated_0.table_fn__events_above" + ] + } + }, + "model.generated_0.latest_raw_events_1": { + "database": null, + "schema": "ci_checks", + "name": "latest_raw_events_1", + "resource_type": "model", + "package_name": "generated_0", + "path": "models/rendering/latest_raw_events_1.sql", + "original_file_path": "models/rendering/latest_raw_events_1.sql", + "unique_id": "model.generated_0.latest_raw_events_1", + "fqn": [ + "generated_0", + "models", + "rendering", + "latest_raw_events_1" + ], + "alias": "latest_raw_events_1", + "config": { + "enabled": true, + "alias": null, + "schema": "ci_checks", + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "table", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Newest rows of raw_events_1", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "ci_checks.latest_raw_events_1", + "depends_on": { + "macros": [], + "nodes": [ + "source.generated_0.raw_events_1" + ] + } + }, + "model.generated_0.unioned_event_ids": { + "database": null, + "schema": "ci_checks", + "name": "unioned_event_ids", + "resource_type": "model", + "package_name": "generated_0", + "path": "models/rendering/unioned_event_ids.sql", + "original_file_path": "models/rendering/unioned_event_ids.sql", + "unique_id": "model.generated_0.unioned_event_ids", + "fqn": [ + "generated_0", + "models", + "rendering", + "unioned_event_ids" + ], + "alias": "unioned_event_ids", + "config": { + "enabled": true, + "alias": null, + "schema": "ci_checks", + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "table", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Ids from two staged relations", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "ci_checks.unioned_event_ids", + "depends_on": { + "macros": [], + "nodes": [ + "model.generated_0.stg_sales_0", + "model.generated_0.stg_sales_1" + ] + } + }, + "model.generated_0.sales_summary_0": { + "database": null, + "schema": "ci_checks", + "name": "sales_summary_0", + "resource_type": "model", + "package_name": "generated_0", + "path": "models/sales/marts/sales_summary_0.sql", + "original_file_path": "models/sales/marts/sales_summary_0.sql", + "unique_id": "model.generated_0.sales_summary_0", + "fqn": [ + "generated_0", + "models", + "sales", + "marts", + "sales_summary_0" + ], + "alias": "sales_summary_0", + "config": { + "enabled": true, + "alias": null, + "schema": "ci_checks", + "database": null, + "tags": [ + "sales", + "marts" + ], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [ + { + "sql": "SELECT 'ci_checks.sales_summary_0' AS refreshed_relation, 'Ελληνικά orders' AS refresh_label", + "transaction": true, + "index": 0 + }, + { + "sql": "SELECT 'ci_checks.sales_summary_0' AS refreshed", + "transaction": true, + "index": 1 + } + ], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [ + "sales", + "marts" + ], + "description": "Summary of sales activity 0", + "columns": { + "id": { + "name": "id", + "description": "", + "meta": {}, + "data_type": null, + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": { + "sqlbuild": { + "lifecycle_hooks": { + "post_hooks": [ + { + "type": "sql", + "statement": "SELECT 'ci_checks.sales_summary_0' AS refreshed_relation, 'Ελληνικά orders' AS refresh_label", + "name": "record_refresh", + "relative_path": "hooks/sql/record_refresh.sql", + "definition_sql": "SELECT @'relation' AS refreshed_relation, @'label' AS refresh_label", + "kwargs": { + "relation": "@@CTX:destination.qualified", + "label": "Ελληνικά orders" + }, + "description": "Record a refresh for one relation" + }, + { + "type": "sql", + "statement": "SELECT 'ci_checks.sales_summary_0' AS refreshed" + } + ] + } + } + }, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "ci_checks.sales_summary_0", + "depends_on": { + "macros": [], + "nodes": [ + "model.generated_0.stg_sales_0", + "seed.generated_0.lookup_1" + ] + } + }, + "model.generated_0.sales_summary_1": { + "database": null, + "schema": "ci_checks", + "name": "sales_summary_1", + "resource_type": "model", + "package_name": "generated_0", + "path": "models/sales/marts/sales_summary_1.sql", + "original_file_path": "models/sales/marts/sales_summary_1.sql", + "unique_id": "model.generated_0.sales_summary_1", + "fqn": [ + "generated_0", + "models", + "sales", + "marts", + "sales_summary_1" + ], + "alias": "sales_summary_1", + "config": { + "enabled": true, + "alias": null, + "schema": "ci_checks", + "database": null, + "tags": [ + "sales", + "marts" + ], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [ + "sales", + "marts" + ], + "description": "Summary of sales activity 1", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "ci_checks.sales_summary_1", + "depends_on": { + "macros": [], + "nodes": [ + "model.generated_0.stg_sales_0", + "seed.generated_0.lookup_0" + ] + } + }, + "model.generated_0.sales_summary_2": { + "database": null, + "schema": "ci_checks", + "name": "sales_summary_2", + "resource_type": "model", + "package_name": "generated_0", + "path": "models/sales/marts/sales_summary_2.sql", + "original_file_path": "models/sales/marts/sales_summary_2.sql", + "unique_id": "model.generated_0.sales_summary_2", + "fqn": [ + "generated_0", + "models", + "sales", + "marts", + "sales_summary_2" + ], + "alias": "sales_summary_2", + "config": { + "enabled": true, + "alias": null, + "schema": "ci_checks", + "database": null, + "tags": [ + "sales", + "marts" + ], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [ + "sales", + "marts" + ], + "description": "Summary of sales activity 2 (Ελληνικά orders)", + "columns": { + "id": { + "name": "id", + "description": "", + "meta": {}, + "data_type": null, + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "ci_checks.sales_summary_2", + "depends_on": { + "macros": [], + "nodes": [ + "model.generated_0.stg_sales_1", + "model.generated_0.stg_fulfillment_0", + "seed.generated_0.lookup_1" + ] + } + }, + "model.generated_0.stg_sales_0": { + "database": null, + "schema": "ci_checks", + "name": "stg_sales_0", + "resource_type": "model", + "package_name": "generated_0", + "path": "models/sales/staging/stg_sales_0.sql", + "original_file_path": "models/sales/staging/stg_sales_0.sql", + "unique_id": "model.generated_0.stg_sales_0", + "fqn": [ + "generated_0", + "models", + "sales", + "staging", + "stg_sales_0" + ], + "alias": "stg_sales_0", + "config": { + "enabled": true, + "alias": null, + "schema": "ci_checks", + "database": null, + "tags": [ + "staging" + ], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [ + "staging" + ], + "description": "Staged sales events 0", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "ci_checks.stg_sales_0", + "depends_on": { + "macros": [], + "nodes": [ + "source.generated_0.raw_events_0" + ] + } + }, + "model.generated_0.stg_sales_1": { + "database": null, + "schema": "ci_checks", + "name": "stg_sales_1", + "resource_type": "model", + "package_name": "generated_0", + "path": "models/sales/staging/stg_sales_1.sql", + "original_file_path": "models/sales/staging/stg_sales_1.sql", + "unique_id": "model.generated_0.stg_sales_1", + "fqn": [ + "generated_0", + "models", + "sales", + "staging", + "stg_sales_1" + ], + "alias": "stg_sales_1", + "config": { + "enabled": true, + "alias": null, + "schema": "ci_checks", + "database": null, + "tags": [ + "staging" + ], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [ + "staging" + ], + "description": "Staged sales events 1", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "ci_checks.stg_sales_1", + "depends_on": { + "macros": [], + "nodes": [ + "source.generated_0.raw_events_1" + ] + } + }, + "seed.generated_0.lookup_0": { + "database": null, + "schema": "ci_checks", + "name": "lookup_0", + "resource_type": "seed", + "package_name": "generated_0", + "path": "seeds/lookup_0.csv", + "original_file_path": "seeds/lookup_0.csv", + "unique_id": "seed.generated_0.lookup_0", + "fqn": [ + "generated_0", + "seeds", + "lookup_0" + ], + "alias": "lookup_0", + "config": { + "enabled": true, + "alias": null, + "schema": "ci_checks", + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "seed", + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + }, + "delimiter": ",", + "quote_columns": null + }, + "tags": [], + "description": "Generated lookup 0.", + "columns": { + "id": { + "name": "id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "label": { + "name": "label", + "description": "", + "meta": {}, + "data_type": "VARCHAR", + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "ci_checks.lookup_0", + "root_path": null, + "depends_on": { + "macros": [] + } + }, + "seed.generated_0.lookup_1": { + "database": null, + "schema": "ci_checks", + "name": "lookup_1", + "resource_type": "seed", + "package_name": "generated_0", + "path": "seeds/lookup_1.csv", + "original_file_path": "seeds/lookup_1.csv", + "unique_id": "seed.generated_0.lookup_1", + "fqn": [ + "generated_0", + "seeds", + "lookup_1" + ], + "alias": "lookup_1", + "config": { + "enabled": true, + "alias": null, + "schema": "ci_checks", + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "seed", + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + }, + "delimiter": ",", + "quote_columns": null + }, + "tags": [], + "description": "Generated lookup 1.", + "columns": { + "id": { + "name": "id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "label": { + "name": "label", + "description": "", + "meta": {}, + "data_type": "VARCHAR", + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "ci_checks.lookup_1", + "root_path": null, + "depends_on": { + "macros": [] + } + } + }, + "sources": { + "source.generated_0.configured_events": { + "database": null, + "schema": "", + "name": "configured_events", + "resource_type": "source", + "package_name": "generated_0", + "path": "sources/configured.yml", + "original_file_path": "sources/configured.yml", + "unique_id": "source.generated_0.configured_events", + "fqn": [ + "generated_0", + "configured_events" + ], + "source_name": "configured_events", + "source_description": "", + "loader": "", + "identifier": "configured_events", + "quoting": {}, + "loaded_at_field": null, + "freshness": null, + "external": null, + "description": "Configured feed events", + "columns": { + "id": { + "name": "id", + "description": "Key id", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "amount": { + "name": "amount", + "description": "", + "meta": {}, + "data_type": "DOUBLE", + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": { + "owner": "orders team" + }, + "source_meta": {}, + "tags": [], + "config": {}, + "patch_path": null, + "unrendered_config": {}, + "relation_name": null + }, + "source.generated_0.labelled_channels": { + "database": null, + "schema": "", + "name": "labelled_channels", + "resource_type": "source", + "package_name": "generated_0", + "path": "sources/labelled.yml", + "original_file_path": "sources/labelled.yml", + "unique_id": "source.generated_0.labelled_channels", + "fqn": [ + "generated_0", + "labelled_channels" + ], + "source_name": "labelled_channels", + "source_description": "", + "loader": "", + "identifier": "labelled_channels", + "quoting": {}, + "loaded_at_field": null, + "freshness": null, + "external": null, + "description": "Channel labels normalised in the source expression.", + "columns": { + "id": { + "name": "id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "channel": { + "name": "channel", + "description": "", + "meta": {}, + "data_type": "VARCHAR", + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": {}, + "source_meta": {}, + "tags": [], + "config": {}, + "patch_path": null, + "unrendered_config": {}, + "relation_name": null + }, + "source.generated_0.loaded_products": { + "database": null, + "schema": "ci_checks", + "name": "loaded_products", + "resource_type": "source", + "package_name": "generated_0", + "path": "sources/managed.yml", + "original_file_path": "sources/managed.yml", + "unique_id": "source.generated_0.loaded_products", + "fqn": [ + "generated_0", + "loaded_products" + ], + "source_name": "loaded_products", + "source_description": "", + "loader": "", + "identifier": "loaded_products", + "quoting": {}, + "loaded_at_field": null, + "freshness": null, + "external": null, + "description": "Products loaded by a project loader.", + "columns": { + "id": { + "name": "id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "label": { + "name": "label", + "description": "", + "meta": {}, + "data_type": "VARCHAR", + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": { + "sqlbuild": { + "loader": "loaded_products", + "auto_load": false, + "write_strategy": "table" + } + }, + "source_meta": {}, + "tags": [], + "config": {}, + "patch_path": null, + "unrendered_config": {}, + "relation_name": null + }, + "source.generated_0.raw_events_0": { + "database": null, + "schema": "", + "name": "raw_events_0", + "resource_type": "source", + "package_name": "generated_0", + "path": "sources/raw.yml", + "original_file_path": "sources/raw.yml", + "unique_id": "source.generated_0.raw_events_0", + "fqn": [ + "generated_0", + "raw_events_0" + ], + "source_name": "raw_events_0", + "source_description": "", + "loader": "", + "identifier": "raw_events_0", + "quoting": {}, + "loaded_at_field": null, + "freshness": null, + "external": null, + "description": "Generated event feed 0.", + "columns": { + "id": { + "name": "id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "amount": { + "name": "amount", + "description": "", + "meta": {}, + "data_type": "DOUBLE", + "constraints": [], + "quote": null, + "tags": [] + }, + "status": { + "name": "status", + "description": "", + "meta": {}, + "data_type": "VARCHAR", + "constraints": [], + "quote": null, + "tags": [] + }, + "created_at": { + "name": "created_at", + "description": "", + "meta": {}, + "data_type": "TIMESTAMP", + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": {}, + "source_meta": {}, + "tags": [], + "config": {}, + "patch_path": null, + "unrendered_config": {}, + "relation_name": null + }, + "source.generated_0.raw_events_1": { + "database": null, + "schema": "", + "name": "raw_events_1", + "resource_type": "source", + "package_name": "generated_0", + "path": "sources/raw.yml", + "original_file_path": "sources/raw.yml", + "unique_id": "source.generated_0.raw_events_1", + "fqn": [ + "generated_0", + "raw_events_1" + ], + "source_name": "raw_events_1", + "source_description": "", + "loader": "", + "identifier": "raw_events_1", + "quoting": {}, + "loaded_at_field": null, + "freshness": null, + "external": null, + "description": "Generated event feed 1.", + "columns": { + "id": { + "name": "id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "amount": { + "name": "amount", + "description": "", + "meta": {}, + "data_type": "DOUBLE", + "constraints": [], + "quote": null, + "tags": [] + }, + "status": { + "name": "status", + "description": "", + "meta": {}, + "data_type": "VARCHAR", + "constraints": [], + "quote": null, + "tags": [] + }, + "created_at": { + "name": "created_at", + "description": "", + "meta": {}, + "data_type": "TIMESTAMP", + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": {}, + "source_meta": {}, + "tags": [], + "config": {}, + "patch_path": null, + "unrendered_config": {}, + "relation_name": null + } + }, + "macros": { + "macro.generated_0.adjust_0": { + "name": "adjust_0", + "resource_type": "macro", + "package_name": "generated_0", + "path": "macros/adjust_0.py", + "original_file_path": "macros/adjust_0.py", + "unique_id": "macro.generated_0.adjust_0", + "macro_sql": "def adjust_0_round(expression: str) -> str:\n \"\"\"Round an amount to two places.\"\"\"\n return f\"ROUND({expression}, 2)\"\n\n\ndef adjust_0(expression: str) -> str:\n \"\"\"Double and round an amount.\"\"\"\n return adjust_0_round(f\"({expression} * 2)\")\n", + "depends_on": { + "macros": [ + "macro.generated_0.adjust_0_round" + ] + }, + "description": "", + "meta": { + "sqlbuild_visibility": "global", + "sqlbuild_scope_path": null + }, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "arguments": [], + "supported_languages": null + }, + "macro.generated_0.adjust_0_round": { + "name": "adjust_0_round", + "resource_type": "macro", + "package_name": "generated_0", + "path": "macros/adjust_0.py", + "original_file_path": "macros/adjust_0.py", + "unique_id": "macro.generated_0.adjust_0_round", + "macro_sql": "def adjust_0_round(expression: str) -> str:\n \"\"\"Round an amount to two places.\"\"\"\n return f\"ROUND({expression}, 2)\"\n\n\ndef adjust_0(expression: str) -> str:\n \"\"\"Double and round an amount.\"\"\"\n return adjust_0_round(f\"({expression} * 2)\")\n", + "depends_on": { + "macros": [] + }, + "description": "", + "meta": { + "sqlbuild_visibility": "global", + "sqlbuild_scope_path": null + }, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "arguments": [], + "supported_languages": null + }, + "macro.generated_0.adjust_2": { + "name": "adjust_2", + "resource_type": "macro", + "package_name": "generated_0", + "path": "macros/adjust_2.py", + "original_file_path": "macros/adjust_2.py", + "unique_id": "macro.generated_0.adjust_2", + "macro_sql": "def adjust_2(ctx, expression: str) -> str:\n \"\"\"Scale an amount by the configured discount.\"\"\"\n rate = ctx.vars[\"discount_rate\"]\n return f\"({expression} * (1 - {rate}))\"\n", + "depends_on": { + "macros": [] + }, + "description": "", + "meta": { + "sqlbuild_visibility": "global", + "sqlbuild_scope_path": null + }, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "arguments": [], + "supported_languages": null + }, + "macro.generated_0.tidy_label": { + "name": "tidy_label", + "resource_type": "macro", + "package_name": "generated_0", + "path": "macros/labels.py", + "original_file_path": "macros/labels.py", + "unique_id": "macro.generated_0.tidy_label", + "macro_sql": "def tidy_label(expression: str) -> str:\n \"\"\"Trim and lower-case a label.\"\"\"\n return f\"LOWER(TRIM({expression}))\"\n", + "depends_on": { + "macros": [] + }, + "description": "", + "meta": { + "sqlbuild_visibility": "global", + "sqlbuild_scope_path": null + }, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "arguments": [], + "supported_languages": null + }, + "macro.generated_0.round_amount": { + "name": "round_amount", + "resource_type": "macro", + "package_name": "generated_0", + "path": "macros/rounding.py", + "original_file_path": "macros/rounding.py", + "unique_id": "macro.generated_0.round_amount", + "macro_sql": "def round_amount(expression: str, places: int = 2) -> str:\n \"\"\"Round an amount.\"\"\"\n return f\"ROUND({expression}, {places})\"\n", + "depends_on": { + "macros": [] + }, + "description": "", + "meta": { + "sqlbuild_visibility": "global", + "sqlbuild_scope_path": null + }, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "arguments": [], + "supported_languages": null + }, + "macro.generated_0.taxed": { + "name": "taxed", + "resource_type": "macro", + "package_name": "generated_0", + "path": "macros/tax.py", + "original_file_path": "macros/tax.py", + "unique_id": "macro.generated_0.taxed", + "macro_sql": "from macros.rounding import round_amount\n\n\ndef taxed(expression: str) -> str:\n \"\"\"Add tax to an amount, rounded.\"\"\"\n return round_amount(f\"{expression} * 1.2\")\n", + "depends_on": { + "macros": [ + "macro.generated_0.round_amount" + ] + }, + "description": "", + "meta": { + "sqlbuild_visibility": "global", + "sqlbuild_scope_path": null + }, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "arguments": [], + "supported_languages": null + }, + "macro.generated_0.analysis_region": { + "name": "analysis_region", + "resource_type": "macro", + "package_name": "generated_0", + "path": "models/analysis/_sqlbuild/_macros/regions.py", + "original_file_path": "models/analysis/_sqlbuild/_macros/regions.py", + "unique_id": "macro.generated_0.analysis_region", + "macro_sql": "def analysis_region() -> str:\n \"\"\"Return the region rows are assigned to.\"\"\"\n return \"'east'\"\n", + "depends_on": { + "macros": [] + }, + "description": "", + "meta": { + "sqlbuild_visibility": "local", + "sqlbuild_scope_path": "models/analysis" + }, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "arguments": [], + "supported_languages": null + }, + "macro.generated_0.adjust_1": { + "name": "adjust_1", + "resource_type": "macro", + "package_name": "generated_0", + "path": "models/fulfillment/staging/_sqlbuild/_macros/adjust_1.py", + "original_file_path": "models/fulfillment/staging/_sqlbuild/_macros/adjust_1.py", + "unique_id": "macro.generated_0.adjust_1", + "macro_sql": "import os\n\n\ndef adjust_1(expression: str) -> str:\n \"\"\"Tag an amount with the deployment region.\"\"\"\n region = os.environ.get(\"SQB_DIFFERENTIAL_REGION\", \"east\")\n return f\"({expression} + LENGTH('{region}'))\"\n", + "depends_on": { + "macros": [] + }, + "description": "", + "meta": { + "sqlbuild_visibility": "local", + "sqlbuild_scope_path": "models/fulfillment/staging" + }, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "arguments": [], + "supported_languages": null + }, + "macro.generated_0.latest_rows": { + "name": "latest_rows", + "resource_type": "macro", + "package_name": "generated_0", + "path": "models/rendering/_sqlbuild/_macros/relations.py", + "original_file_path": "models/rendering/_sqlbuild/_macros/relations.py", + "unique_id": "macro.generated_0.latest_rows", + "macro_sql": "from sqlbuild.refs import SqlResourceRef\n\n\ndef union_ids(relations: list[SqlResourceRef]) -> str:\n \"\"\"Union the ids of several relations.\"\"\"\n selects = [f\"SELECT id FROM {relation}\" for relation in relations]\n return \" UNION ALL \".join(selects)\n\n\ndef latest_rows(relation: SqlResourceRef, limit: int = 2) -> str:\n \"\"\"Return the newest rows of one relation.\"\"\"\n return f\"(SELECT id, amount FROM {relation} ORDER BY id DESC LIMIT {limit})\"\n", + "depends_on": { + "macros": [] + }, + "description": "", + "meta": { + "sqlbuild_visibility": "local", + "sqlbuild_scope_path": "models/rendering" + }, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "arguments": [], + "supported_languages": null + }, + "macro.generated_0.union_ids": { + "name": "union_ids", + "resource_type": "macro", + "package_name": "generated_0", + "path": "models/rendering/_sqlbuild/_macros/relations.py", + "original_file_path": "models/rendering/_sqlbuild/_macros/relations.py", + "unique_id": "macro.generated_0.union_ids", + "macro_sql": "from sqlbuild.refs import SqlResourceRef\n\n\ndef union_ids(relations: list[SqlResourceRef]) -> str:\n \"\"\"Union the ids of several relations.\"\"\"\n selects = [f\"SELECT id FROM {relation}\" for relation in relations]\n return \" UNION ALL \".join(selects)\n\n\ndef latest_rows(relation: SqlResourceRef, limit: int = 2) -> str:\n \"\"\"Return the newest rows of one relation.\"\"\"\n return f\"(SELECT id, amount FROM {relation} ORDER BY id DESC LIMIT {limit})\"\n", + "depends_on": { + "macros": [] + }, + "description": "", + "meta": { + "sqlbuild_visibility": "local", + "sqlbuild_scope_path": "models/rendering" + }, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "arguments": [], + "supported_languages": null + }, + "macro.generated_0.event_rows": { + "name": "event_rows", + "resource_type": "macro", + "package_name": "generated_0", + "path": "tests/unit/rendering/_sqlbuild/_macros/fixtures.py", + "original_file_path": "tests/unit/rendering/_sqlbuild/_macros/fixtures.py", + "unique_id": "macro.generated_0.event_rows", + "macro_sql": "def event_rows(count: int = 2) -> str:\n \"\"\"Return fixture event rows.\"\"\"\n return \" UNION ALL \".join(\n f\"SELECT {index} AS id, CAST({index} AS DOUBLE) AS amount, \"\n f\"'placed' AS status, \"\n f\"TIMESTAMP '2026-01-0{index} 00:00:00' AS created_at\"\n for index in range(1, count + 1)\n )\n", + "depends_on": { + "macros": [] + }, + "description": "", + "meta": { + "sqlbuild_visibility": "local", + "sqlbuild_scope_path": "tests/unit/rendering" + }, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "arguments": [], + "supported_languages": null + }, + "macro.generated_0__sqlbuild_hooks.record_refresh": { + "name": "record_refresh", + "resource_type": "macro", + "package_name": "generated_0__sqlbuild_hooks", + "path": "hooks/sql/record_refresh.sql", + "original_file_path": "hooks/sql/record_refresh.sql", + "unique_id": "macro.generated_0__sqlbuild_hooks.record_refresh", + "macro_sql": "SELECT @'relation' AS refreshed_relation, @'label' AS refresh_label", + "depends_on": { + "macros": [] + }, + "description": "Record a refresh for one relation", + "meta": { + "sqlbuild_resource_type": "sql_hook", + "sqlbuild_hook_name": "record_refresh" + }, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "arguments": [], + "supported_languages": null + } + }, + "exposures": {}, + "metrics": {}, + "groups": {}, + "selectors": {}, + "disabled": {}, + "parent_map": { + "model.generated_0.pivot_input_stg_sales_0": [ + "model.generated_0.stg_sales_0" + ], + "model.generated_0.pivoted_stg_sales_0": [ + "model.generated_0.pivot_input_stg_sales_0" + ], + "model.generated_0.regional_stg_fulfillment_0": [ + "model.generated_0.stg_fulfillment_0" + ], + "model.generated_0.unanalysed_stg_sales_0": [ + "model.generated_0.stg_sales_0" + ], + "model.generated_0.append_stg_fulfillment_1": [ + "model.generated_0.stg_fulfillment_1" + ], + "model.generated_0.batched_rolling_window_stg_sales_0": [ + "model.generated_0.stg_sales_0" + ], + "model.generated_0.flagged_stg_fulfillment_0": [ + "model.generated_0.is_placed", + "model.generated_0.is_large", + "model.generated_0.stg_fulfillment_0" + ], + "model.generated_0.product_labels": [ + "source.generated_0.loaded_products" + ], + "model.generated_0.shaped_stg_sales_0": [ + "model.generated_0.stg_sales_0" + ], + "model.generated_0.taxed_stg_fulfillment_0": [ + "model.generated_0.stg_fulfillment_0" + ], + "model.generated_0.fulfillment_summary_0": [ + "model.generated_0.stg_fulfillment_0", + "model.generated_0.stg_sales_0", + "seed.generated_0.lookup_1" + ], + "model.generated_0.fulfillment_summary_1": [ + "model.generated_0.stg_fulfillment_0" + ], + "model.generated_0.fulfillment_summary_2": [ + "model.generated_0.stg_fulfillment_1", + "seed.generated_0.lookup_0" + ], + "model.generated_0.stg_fulfillment_0": [ + "source.generated_0.raw_events_1" + ], + "model.generated_0.stg_fulfillment_1": [ + "source.generated_0.raw_events_0" + ], + "model.generated_0.bounded_stg_fulfillment_1": [ + "model.generated_0.stg_fulfillment_1" + ], + "model.generated_0.channel_labels": [ + "source.generated_0.labelled_channels" + ], + "model.generated_0.configured_stg_sales_1": [ + "model.generated_0.stg_sales_1" + ], + "model.generated_0.labelled_stg_fulfillment_0": [ + "model.generated_0.tidy_status", + "model.generated_0.stg_fulfillment_0" + ], + "model.generated_0.large_events": [ + "model.generated_0.table_fn__events_above" + ], + "model.generated_0.latest_raw_events_1": [ + "source.generated_0.raw_events_1" + ], + "model.generated_0.unioned_event_ids": [ + "model.generated_0.stg_sales_0", + "model.generated_0.stg_sales_1" + ], + "model.generated_0.sales_summary_0": [ + "model.generated_0.stg_sales_0", + "seed.generated_0.lookup_1" + ], + "model.generated_0.sales_summary_1": [ + "model.generated_0.stg_sales_0", + "seed.generated_0.lookup_0" + ], + "model.generated_0.sales_summary_2": [ + "model.generated_0.stg_sales_1", + "model.generated_0.stg_fulfillment_0", + "seed.generated_0.lookup_1" + ], + "model.generated_0.stg_sales_0": [ + "source.generated_0.raw_events_0" + ], + "model.generated_0.stg_sales_1": [ + "source.generated_0.raw_events_1" + ], + "source.generated_0.configured_events": [], + "source.generated_0.labelled_channels": [], + "source.generated_0.loaded_products": [], + "source.generated_0.raw_events_0": [], + "source.generated_0.raw_events_1": [], + "seed.generated_0.lookup_0": [], + "seed.generated_0.lookup_1": [], + "model.generated_0.configured_label": [], + "model.generated_0.is_placed": [], + "model.generated_0.table_fn__events_above": [ + "model.generated_0.stg_sales_1" + ], + "model.generated_0.tidy_status": [], + "model.generated_0.is_large": [] + }, + "group_map": {}, + "docs": {}, + "saved_queries": {}, + "semantic_models": {}, + "unit_tests": {} + } +} diff --git a/tests/goldens/compiler/seed/1.json b/tests/goldens/compiler/seed/1.json new file mode 100644 index 0000000000..49b3078359 --- /dev/null +++ b/tests/goldens/compiler/seed/1.json @@ -0,0 +1,37 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "C214", + "message": "Model file models/features/dbt_order_counts.sql uses __dbt_ref(\"upstream_orders\") but no dbt manifest was found", + "help": "Run dbt compile or configure dbt target_path so SQLBuild can read manifest.json." + } + ] + }, + { + "label": "compile-store-warm", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "C214", + "message": "Model file models/features/dbt_order_counts.sql uses __dbt_ref(\"upstream_orders\") but no dbt manifest was found", + "help": "Run dbt compile or configure dbt target_path so SQLBuild can read manifest.json." + } + ] + }, + { + "label": "plan", + "exit_code": 0, + "diagnostics": [] + } + ], + "compiled": {}, + "manifest": null +} diff --git a/tests/goldens/compiler/seed/10.json b/tests/goldens/compiler/seed/10.json new file mode 100644 index 0000000000..e5d7177d6c --- /dev/null +++ b/tests/goldens/compiler/seed/10.json @@ -0,0 +1,3302 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 0, + "diagnostics": [] + }, + { + "label": "compile-store-warm", + "exit_code": 0, + "diagnostics": [] + }, + { + "label": "plan", + "exit_code": 0, + "diagnostics": [] + }, + { + "label": "compile-select", + "exit_code": 0, + "diagnostics": [] + }, + { + "label": "compile-no-analysis", + "exit_code": 0, + "diagnostics": [] + }, + { + "label": "compile-rich-lineage", + "exit_code": 0, + "diagnostics": [] + } + ], + "compiled": { + "audits/generic/audited_stg_fulfillment_1/id_present__id.sql": [ + "SELECT id", + "FROM __ref(\"audited_stg_fulfillment_1\")", + "WHERE id IS NULL", + "" + ], + "audits/generic/checked_stg_fulfillment_0/accepted_values__status.sql": [ + "SELECT status", + "FROM __ref(\"checked_stg_fulfillment_0\")", + "WHERE status IS NOT NULL", + " AND status NOT IN ('placed', 'expédié', '取消', 'returned ✓')", + "" + ], + "audits/generic/checked_stg_fulfillment_0/expression_is_true.sql": [ + "SELECT *", + "FROM __ref(\"checked_stg_fulfillment_0\")", + "WHERE NOT (amount >= -1000)", + "" + ], + "audits/generic/checked_stg_fulfillment_0/relationships__id.sql": [ + "SELECT id", + "FROM __ref(\"checked_stg_fulfillment_0\")", + "WHERE id IS NOT NULL", + " AND id NOT IN (", + " SELECT id", + " FROM __ref(\"stg_fulfillment_0\")", + " WHERE id IS NOT NULL", + " )", + "" + ], + "audits/generic/fulfillment_summary_1/not_null__id.sql": [ + "SELECT id", + "FROM __ref(\"fulfillment_summary_1\")", + "WHERE id IS NULL", + "" + ], + "audits/generic/guarded_stg_inventory_1/amount_at_least__amount.sql": [ + "SELECT *", + "FROM __ref(\"guarded_stg_inventory_1\")", + "WHERE amount < 0", + "" + ], + "audits/generic/stated_stg_fulfillment_1/accepted_values__status.sql": [ + "SELECT status", + "FROM __ref(\"stated_stg_fulfillment_1\")", + "WHERE status IS NOT NULL", + " AND status NOT IN ('placed', 'expédié', '取消', 'returned ✓')", + "" + ], + "audits/generic/stg_fulfillment_1/not_null__id.sql": [ + "SELECT id", + "FROM __ref(\"stg_fulfillment_1\")", + "WHERE id IS NULL", + "" + ], + "audits/generic/stg_inventory_1/not_null__id.sql": [ + "SELECT id", + "FROM __ref(\"stg_inventory_1\")", + "WHERE id IS NULL", + "" + ], + "audits/generic/stg_sales_0/not_null__id.sql": [ + "SELECT id", + "FROM __ref(\"stg_sales_0\")", + "WHERE id IS NULL", + "" + ], + "audits/singular/fulfillment_summary_1_matches.sql": [ + "SELECT a.id", + "FROM __ref(\"fulfillment_summary_1\") a", + "LEFT JOIN __ref(\"fulfillment_summary_0\") b ON a.id = b.id", + "WHERE b.id IS NULL", + "" + ], + "functions/python/is_large.sql": [ + "REGISTER PYTHON FUNCTION is_large(DOUBLE) RETURNS BOOLEAN", + "" + ], + "functions/sql/is_placed.sql": [ + "CREATE OR REPLACE MACRO main.is_placed(event_status) AS (", + "event_status = 'placed'", + ")", + "" + ], + "functions/sql/table_fn__events_above.sql": [ + "CREATE OR REPLACE MACRO main.table_fn__events_above(p_minimum) AS TABLE", + "SELECT id, amount", + "FROM __ref(\"stg_inventory_0\")", + "WHERE amount > p_minimum", + "" + ], + "models/analysis/chain_1_stg_sales_0.sql": [ + "SELECT id, label, amount", + "FROM __ref(\"contracted_stg_sales_0\")", + "" + ], + "models/analysis/chain_2_stg_sales_0.sql": [ + "SELECT id, UPPER(label) AS label, amount", + "FROM __ref(\"chain_1_stg_sales_0\")", + "" + ], + "models/analysis/chain_3_stg_sales_0.sql": [ + "SELECT id, label, amount", + "FROM __ref(\"chain_2_stg_sales_0\")", + "" + ], + "models/analysis/checked_stg_fulfillment_0.sql": [ + "SELECT id, amount, status", + "FROM __ref(\"stg_fulfillment_0\")", + "" + ], + "models/analysis/contracted_stg_sales_0.sql": [ + "SELECT CAST(id AS INTEGER) AS id, CAST(status AS VARCHAR(34)) AS label,", + " amount", + "FROM __source(\"raw_events_1\")", + "" + ], + "models/analysis/regional_stg_inventory_0.sql": [ + "SELECT id, amount, 'north' AS region", + "FROM __ref(\"stg_inventory_0\")", + "" + ], + "models/features/audited_stg_fulfillment_1.sql": [ + "SELECT id, amount", + "FROM __ref(\"stg_fulfillment_1\")", + "" + ], + "models/features/check_history_stg_fulfillment_1.sql": [ + "SELECT id, status, amount", + "FROM __ref(\"stg_fulfillment_1\")", + "" + ], + "models/features/copied_stg_sales_0.sql": [ + "SELECT id, status", + "FROM __ref(\"stg_sales_0\")", + "" + ], + "models/features/flagged_stg_inventory_1.sql": [ + "SELECT id, __udf(\"is_placed\")(status) AS is_placed,", + " __udf(\"is_large\")(amount) AS is_large", + "FROM __ref(\"stg_inventory_1\")", + "" + ], + "models/features/guarded_stg_inventory_1.sql": [ + "SELECT id, amount", + "FROM __ref(\"stg_inventory_1\")", + "" + ], + "models/features/merge_stg_sales_0.sql": [ + "SELECT id, amount, status, created_at", + "FROM __ref(\"stg_sales_0\")", + "" + ], + "models/features/product_labels.sql": [ + "SELECT id, label", + "FROM __source(\"loaded_products\")", + "" + ], + "models/features/spaced_stg_sales_0.sql": [ + "-- Généré pour 東京 warehouse", + "SELECT", + "\tid,", + "\t'naïve résumé' AS label, -- étiquette ✓", + "\tamount", + "FROM __ref(\"stg_sales_0\")", + "" + ], + "models/features/taxed_stg_sales_0.sql": [ + "SELECT id, ROUND(amount * 1.2, 2) AS taxed, ROUND(amount, 1) AS rounded", + "FROM __ref(\"stg_sales_0\")", + "" + ], + "models/fulfillment/marts/fulfillment_summary_0.sql": [ + "SELECT", + " t.id,", + " t.amount,", + " t.status,", + " t.created_at,", + " (t.amount * (1 - 12345.678)) AS adjust_0_amount", + "FROM __ref(\"stg_fulfillment_1\") t", + "" + ], + "models/fulfillment/marts/fulfillment_summary_1.sql": [ + "SELECT", + " t.id,", + " t.amount,", + " t.status,", + " t.created_at", + "FROM __ref(\"stg_fulfillment_0\") t", + "" + ], + "models/fulfillment/staging/stg_fulfillment_0.sql": [ + "SELECT", + " t.id,", + " t.amount,", + " t.status,", + " t.created_at,", + " (t.amount * (1 - 12345.678)) AS adjust_0_amount,", + " ROUND(ROUND((t.amount * 2), 2), 2) AS adjust_1_amount", + "FROM __source(\"raw_events_2\") t", + "" + ], + "models/fulfillment/staging/stg_fulfillment_1.sql": [ + "SELECT", + " t.id,", + " t.amount,", + " t.status,", + " t.created_at,", + " 3 AS priority,", + " (t.amount * (1 - 12345.678)) AS adjust_0_amount,", + " ROUND(ROUND((t.amount * 2), 2), 2) AS adjust_1_amount", + "FROM __source(\"raw_events_0\") t", + "" + ], + "models/inventory/marts/inventory_summary_0.sql": [ + "SELECT", + " t.id,", + " t.amount,", + " t.status,", + " t.created_at,", + " 3 AS priority,", + " ROUND(ROUND((t.amount * 2), 2), 2) AS adjust_1_amount", + "FROM __ref(\"stg_inventory_1\") t", + "" + ], + "models/inventory/marts/inventory_summary_1.sql": [ + "SELECT", + " t.id,", + " t.amount,", + " t.status,", + " t.created_at", + "FROM __ref(\"stg_inventory_1\") t", + "" + ], + "models/inventory/staging/stg_inventory_0.sql": [ + "SELECT", + " t.id,", + " t.amount,", + " t.status,", + " t.created_at,", + " (t.amount + 2 + 1) AS adjust_2_amount", + "FROM __source(\"raw_events_0\") t", + "" + ], + "models/inventory/staging/stg_inventory_1.sql": [ + "SELECT", + " t.id,", + " t.amount,", + " t.status,", + " t.created_at,", + " 'naïve résumé' AS site_label", + "FROM __source(\"raw_events_0\") t", + "" + ], + "models/rendering/large_events.sql": [ + "SELECT id, amount", + "FROM __table_fn(\"table_fn__events_above\")(1.5)", + "" + ], + "models/rendering/latest_raw_events_2.sql": [ + "SELECT id, amount", + "FROM (SELECT id, amount FROM __source(\"raw_events_2\") ORDER BY id DESC LIMIT 2) AS latest", + "" + ], + "models/rendering/stated_stg_fulfillment_1.sql": [ + "SELECT CAST(id AS INTEGER) AS id, status,", + " CASE WHEN amount >= 3 THEN 'HIGH'", + " ELSE 'LOW' END AS tier", + "FROM __ref(\"stg_fulfillment_1\")", + "" + ], + "models/rendering/unioned_event_ids.sql": [ + "SELECT id", + "FROM (SELECT id FROM __ref(\"stg_fulfillment_0\") UNION ALL SELECT id FROM __ref(\"stg_fulfillment_1\")) AS unioned", + "" + ], + "models/sales/marts/sales_summary_0.sql": [ + "SELECT", + " t.id,", + " t.amount,", + " t.status,", + " t.created_at,", + " 3 AS priority,", + " 'naïve résumé' AS site_label", + "FROM __ref(\"stg_sales_0\") t", + "WHERE t.status <> 'returned ✓'", + "" + ], + "models/sales/staging/stg_sales_0.sql": [ + "SELECT", + " t.id,", + " t.amount,", + " t.status,", + " t.created_at,", + " t.amount + 117 AS threshold_0_amount", + "FROM __source(\"raw_events_1\") t", + "" + ], + "tests/macro taxes_amounts/taxes_amounts.sql": [ + "WITH input_values AS (SELECT 10 AS amount),", + "__actual__macro_taxes_amounts AS (SELECT ROUND(amount * 1.2, 2) AS taxed", + " FROM input_values),", + "__expected__macro_taxes_amounts AS (SELECT 12.0 AS taxed)", + "SELECT 0 AS step_index, 'macro taxes_amounts' AS model_name, (SELECT COUNT(*) FROM __actual__macro_taxes_amounts) AS actual_count, (SELECT COUNT(*) FROM __expected__macro_taxes_amounts) AS expected_count, (SELECT COUNT(*) FROM (SELECT * FROM __actual__macro_taxes_amounts EXCEPT SELECT * FROM __expected__macro_taxes_amounts) AS __sqlbuild_mismatch) AS unexpected_count, (SELECT COUNT(*) FROM (SELECT * FROM __expected__macro_taxes_amounts EXCEPT SELECT * FROM __actual__macro_taxes_amounts) AS __sqlbuild_missing) AS missing_count", + "" + ], + "tests/regional_stg_inventory_0/test_regional_stg_inventory_0.sql": [ + "WITH __ref__stg_inventory_0 AS (SELECT 1 AS id, CAST(4 AS DOUBLE) AS amount, 'placed' AS status,", + " TIMESTAMP '2026-01-01 00:00:00' AS created_at),", + "__ref__regional_stg_inventory_0 AS (SELECT id, amount, 'west' AS region", + "FROM __ref__stg_inventory_0),", + "__actual__regional_stg_inventory_0 AS (SELECT * FROM __ref__regional_stg_inventory_0),", + "__expected__regional_stg_inventory_0 AS (SELECT 1 AS id, CAST(4 AS DOUBLE) AS amount, 'west' AS region)", + "SELECT 0 AS step_index, 'regional_stg_inventory_0' AS model_name, (SELECT COUNT(*) FROM __actual__regional_stg_inventory_0) AS actual_count, (SELECT COUNT(*) FROM __expected__regional_stg_inventory_0) AS expected_count, (SELECT COUNT(*) FROM (SELECT id, amount, region FROM __actual__regional_stg_inventory_0 EXCEPT SELECT id, amount, region FROM __expected__regional_stg_inventory_0) AS __sqlbuild_mismatch) AS unexpected_count, (SELECT COUNT(*) FROM (SELECT id, amount, region FROM __expected__regional_stg_inventory_0 EXCEPT SELECT id, amount, region FROM __actual__regional_stg_inventory_0) AS __sqlbuild_missing) AS missing_count", + "" + ], + "tests/stg_fulfillment_0/test_stg_fulfillment_0.sql": [ + "WITH __source__raw_events_2 AS (SELECT 1 AS id, CAST(10 AS DOUBLE) AS amount, 'placed' AS status,", + " TIMESTAMP '2026-01-01 00:00:00' AS created_at),", + "__ref__stg_fulfillment_0 AS (SELECT", + " t.id,", + " t.amount,", + " t.status,", + " t.created_at,", + " (t.amount * (1 - 12345.678)) AS adjust_0_amount,", + " ROUND(ROUND((t.amount * 2), 2), 2) AS adjust_1_amount", + "FROM __source__raw_events_2 t),", + "__actual__stg_fulfillment_0 AS (SELECT * FROM __ref__stg_fulfillment_0),", + "__expected__stg_fulfillment_0 AS (SELECT 1 AS id, 'placed' AS status)", + "SELECT 0 AS step_index, 'stg_fulfillment_0' AS model_name, (SELECT COUNT(*) FROM __actual__stg_fulfillment_0) AS actual_count, (SELECT COUNT(*) FROM __expected__stg_fulfillment_0) AS expected_count, (SELECT COUNT(*) FROM (SELECT id, status FROM __actual__stg_fulfillment_0 EXCEPT SELECT id, status FROM __expected__stg_fulfillment_0) AS __sqlbuild_mismatch) AS unexpected_count, (SELECT COUNT(*) FROM (SELECT id, status FROM __expected__stg_fulfillment_0 EXCEPT SELECT id, status FROM __actual__stg_fulfillment_0) AS __sqlbuild_missing) AS missing_count", + "" + ], + "tests/udf detects_placed_events/detects_placed_events.sql": [ + "WITH input_values AS (SELECT 'placed' AS event_status),", + "__actual__udf_detects_placed_events AS (SELECT event_status, main.is_placed(event_status) AS is_placed", + " FROM input_values),", + "__expected__udf_detects_placed_events AS (SELECT 'placed' AS event_status, TRUE AS is_placed)", + "SELECT 0 AS step_index, 'udf detects_placed_events' AS model_name, (SELECT COUNT(*) FROM __actual__udf_detects_placed_events) AS actual_count, (SELECT COUNT(*) FROM __expected__udf_detects_placed_events) AS expected_count, (SELECT COUNT(*) FROM (SELECT * FROM __actual__udf_detects_placed_events EXCEPT SELECT * FROM __expected__udf_detects_placed_events) AS __sqlbuild_mismatch) AS unexpected_count, (SELECT COUNT(*) FROM (SELECT * FROM __expected__udf_detects_placed_events EXCEPT SELECT * FROM __actual__udf_detects_placed_events) AS __sqlbuild_missing) AS missing_count", + "" + ] + }, + "manifest": { + "nodes": { + "model.generated_10.chain_1_stg_sales_0": { + "database": null, + "schema": "main", + "name": "chain_1_stg_sales_0", + "resource_type": "model", + "package_name": "generated_10", + "path": "models/analysis/chain_1_stg_sales_0.sql", + "original_file_path": "models/analysis/chain_1_stg_sales_0.sql", + "unique_id": "model.generated_10.chain_1_stg_sales_0", + "fqn": [ + "generated_10", + "models", + "analysis", + "chain_1_stg_sales_0" + ], + "alias": "chain_1_stg_sales_0", + "config": { + "enabled": true, + "alias": null, + "schema": "main", + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "table", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Level 1 below the contracted copy of stg_sales_0", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.chain_1_stg_sales_0", + "depends_on": { + "macros": [], + "nodes": [ + "model.generated_10.contracted_stg_sales_0" + ] + } + }, + "model.generated_10.chain_2_stg_sales_0": { + "database": null, + "schema": "main", + "name": "chain_2_stg_sales_0", + "resource_type": "model", + "package_name": "generated_10", + "path": "models/analysis/chain_2_stg_sales_0.sql", + "original_file_path": "models/analysis/chain_2_stg_sales_0.sql", + "unique_id": "model.generated_10.chain_2_stg_sales_0", + "fqn": [ + "generated_10", + "models", + "analysis", + "chain_2_stg_sales_0" + ], + "alias": "chain_2_stg_sales_0", + "config": { + "enabled": true, + "alias": null, + "schema": "main", + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "table", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Level 2 below the contracted copy of stg_sales_0", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.chain_2_stg_sales_0", + "depends_on": { + "macros": [], + "nodes": [ + "model.generated_10.chain_1_stg_sales_0" + ] + } + }, + "model.generated_10.chain_3_stg_sales_0": { + "database": null, + "schema": "main", + "name": "chain_3_stg_sales_0", + "resource_type": "model", + "package_name": "generated_10", + "path": "models/analysis/chain_3_stg_sales_0.sql", + "original_file_path": "models/analysis/chain_3_stg_sales_0.sql", + "unique_id": "model.generated_10.chain_3_stg_sales_0", + "fqn": [ + "generated_10", + "models", + "analysis", + "chain_3_stg_sales_0" + ], + "alias": "chain_3_stg_sales_0", + "config": { + "enabled": true, + "alias": null, + "schema": "main", + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "table", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Level 3 below the contracted copy of stg_sales_0", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.chain_3_stg_sales_0", + "depends_on": { + "macros": [], + "nodes": [ + "model.generated_10.chain_2_stg_sales_0" + ] + } + }, + "model.generated_10.checked_stg_fulfillment_0": { + "database": null, + "schema": "main", + "name": "checked_stg_fulfillment_0", + "resource_type": "model", + "package_name": "generated_10", + "path": "models/analysis/checked_stg_fulfillment_0.sql", + "original_file_path": "models/analysis/checked_stg_fulfillment_0.sql", + "unique_id": "model.generated_10.checked_stg_fulfillment_0", + "fqn": [ + "generated_10", + "models", + "analysis", + "checked_stg_fulfillment_0" + ], + "alias": "checked_stg_fulfillment_0", + "config": { + "enabled": true, + "alias": null, + "schema": "main", + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "table", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": [ + "id" + ], + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Metadata-checked copy of stg_fulfillment_0", + "columns": { + "id": { + "name": "id", + "description": "", + "meta": {}, + "data_type": null, + "constraints": [], + "quote": null, + "tags": [] + }, + "status": { + "name": "status", + "description": "", + "meta": {}, + "data_type": null, + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.checked_stg_fulfillment_0", + "depends_on": { + "macros": [], + "nodes": [ + "model.generated_10.stg_fulfillment_0" + ] + } + }, + "model.generated_10.contracted_stg_sales_0": { + "database": null, + "schema": "main", + "name": "contracted_stg_sales_0", + "resource_type": "model", + "package_name": "generated_10", + "path": "models/analysis/contracted_stg_sales_0.sql", + "original_file_path": "models/analysis/contracted_stg_sales_0.sql", + "unique_id": "model.generated_10.contracted_stg_sales_0", + "fqn": [ + "generated_10", + "models", + "analysis", + "contracted_stg_sales_0" + ], + "alias": "contracted_stg_sales_0", + "config": { + "enabled": true, + "alias": null, + "schema": "main", + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "table", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Contracted copy of raw_events_1", + "columns": { + "id": { + "name": "id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "label": { + "name": "label", + "description": "", + "meta": {}, + "data_type": "VARCHAR(34)", + "constraints": [], + "quote": null, + "tags": [] + }, + "amount": { + "name": "amount", + "description": "", + "meta": {}, + "data_type": "DOUBLE", + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.contracted_stg_sales_0", + "depends_on": { + "macros": [], + "nodes": [ + "source.generated_10.raw_events_1" + ] + } + }, + "model.generated_10.regional_stg_inventory_0": { + "database": null, + "schema": "main", + "name": "regional_stg_inventory_0", + "resource_type": "model", + "package_name": "generated_10", + "path": "models/analysis/regional_stg_inventory_0.sql", + "original_file_path": "models/analysis/regional_stg_inventory_0.sql", + "unique_id": "model.generated_10.regional_stg_inventory_0", + "fqn": [ + "generated_10", + "models", + "analysis", + "regional_stg_inventory_0" + ], + "alias": "regional_stg_inventory_0", + "config": { + "enabled": true, + "alias": null, + "schema": "main", + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "table", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Regional copy of stg_inventory_0", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.regional_stg_inventory_0", + "depends_on": { + "macros": [], + "nodes": [ + "model.generated_10.stg_inventory_0" + ] + } + }, + "model.generated_10.audited_stg_fulfillment_1": { + "database": null, + "schema": "main", + "name": "audited_stg_fulfillment_1", + "resource_type": "model", + "package_name": "generated_10", + "path": "models/features/audited_stg_fulfillment_1.sql", + "original_file_path": "models/features/audited_stg_fulfillment_1.sql", + "unique_id": "model.generated_10.audited_stg_fulfillment_1", + "fqn": [ + "generated_10", + "models", + "features", + "audited_stg_fulfillment_1" + ], + "alias": "audited_stg_fulfillment_1", + "config": { + "enabled": true, + "alias": null, + "schema": "main", + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "table", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Factory-audited copy of stg_fulfillment_1", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.audited_stg_fulfillment_1", + "depends_on": { + "macros": [], + "nodes": [ + "model.generated_10.stg_fulfillment_1" + ] + } + }, + "model.generated_10.check_history_stg_fulfillment_1": { + "database": null, + "schema": "main", + "name": "check_history_stg_fulfillment_1", + "resource_type": "model", + "package_name": "generated_10", + "path": "models/features/check_history_stg_fulfillment_1.sql", + "original_file_path": "models/features/check_history_stg_fulfillment_1.sql", + "unique_id": "model.generated_10.check_history_stg_fulfillment_1", + "fqn": [ + "generated_10", + "models", + "features", + "check_history_stg_fulfillment_1" + ], + "alias": "check_history_stg_fulfillment_1", + "config": { + "enabled": true, + "alias": null, + "schema": "main", + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "snapshot", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": [ + "id" + ], + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Check history of stg_fulfillment_1", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.check_history_stg_fulfillment_1", + "depends_on": { + "macros": [], + "nodes": [ + "model.generated_10.stg_fulfillment_1" + ] + } + }, + "model.generated_10.copied_stg_sales_0": { + "database": null, + "schema": "main", + "name": "copied_stg_sales_0", + "resource_type": "model", + "package_name": "generated_10", + "path": "models/features/copied_stg_sales_0.sql", + "original_file_path": "models/features/copied_stg_sales_0.sql", + "unique_id": "model.generated_10.copied_stg_sales_0", + "fqn": [ + "generated_10", + "models", + "features", + "copied_stg_sales_0" + ], + "alias": "copied_stg_sales_0", + "config": { + "enabled": true, + "alias": null, + "schema": "main", + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "copy_table", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Custom copy of stg_sales_0", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.copied_stg_sales_0", + "depends_on": { + "macros": [], + "nodes": [ + "model.generated_10.stg_sales_0" + ] + } + }, + "model.generated_10.flagged_stg_inventory_1": { + "database": null, + "schema": "main", + "name": "flagged_stg_inventory_1", + "resource_type": "model", + "package_name": "generated_10", + "path": "models/features/flagged_stg_inventory_1.sql", + "original_file_path": "models/features/flagged_stg_inventory_1.sql", + "unique_id": "model.generated_10.flagged_stg_inventory_1", + "fqn": [ + "generated_10", + "models", + "features", + "flagged_stg_inventory_1" + ], + "alias": "flagged_stg_inventory_1", + "config": { + "enabled": true, + "alias": null, + "schema": "main", + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "table", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Function-flagged copy of stg_inventory_1", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.flagged_stg_inventory_1", + "depends_on": { + "macros": [], + "nodes": [ + "model.generated_10.is_placed", + "model.generated_10.is_large", + "model.generated_10.stg_inventory_1" + ] + } + }, + "model.generated_10.guarded_stg_inventory_1": { + "database": null, + "schema": "main", + "name": "guarded_stg_inventory_1", + "resource_type": "model", + "package_name": "generated_10", + "path": "models/features/guarded_stg_inventory_1.sql", + "original_file_path": "models/features/guarded_stg_inventory_1.sql", + "unique_id": "model.generated_10.guarded_stg_inventory_1", + "fqn": [ + "generated_10", + "models", + "features", + "guarded_stg_inventory_1" + ], + "alias": "guarded_stg_inventory_1", + "config": { + "enabled": true, + "alias": null, + "schema": "main", + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "table", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Generic-audited copy of stg_inventory_1", + "columns": { + "amount": { + "name": "amount", + "description": "", + "meta": {}, + "data_type": null, + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.guarded_stg_inventory_1", + "depends_on": { + "macros": [], + "nodes": [ + "model.generated_10.stg_inventory_1" + ] + } + }, + "model.generated_10.merge_stg_sales_0": { + "database": null, + "schema": "main", + "name": "merge_stg_sales_0", + "resource_type": "model", + "package_name": "generated_10", + "path": "models/features/merge_stg_sales_0.sql", + "original_file_path": "models/features/merge_stg_sales_0.sql", + "unique_id": "model.generated_10.merge_stg_sales_0", + "fqn": [ + "generated_10", + "models", + "features", + "merge_stg_sales_0" + ], + "alias": "merge_stg_sales_0", + "config": { + "enabled": true, + "alias": null, + "schema": "main", + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "incremental", + "incremental_strategy": "merge", + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": [ + "id" + ], + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Incremental merge copy of stg_sales_0", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.merge_stg_sales_0", + "depends_on": { + "macros": [], + "nodes": [ + "model.generated_10.stg_sales_0" + ] + } + }, + "model.generated_10.product_labels": { + "database": null, + "schema": "main", + "name": "product_labels", + "resource_type": "model", + "package_name": "generated_10", + "path": "models/features/product_labels.sql", + "original_file_path": "models/features/product_labels.sql", + "unique_id": "model.generated_10.product_labels", + "fqn": [ + "generated_10", + "models", + "features", + "product_labels" + ], + "alias": "product_labels", + "config": { + "enabled": true, + "alias": null, + "schema": "main", + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Product labels from the loader", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.product_labels", + "depends_on": { + "macros": [], + "nodes": [ + "source.generated_10.loaded_products" + ] + } + }, + "model.generated_10.spaced_stg_sales_0": { + "database": null, + "schema": "main", + "name": "spaced_stg_sales_0", + "resource_type": "model", + "package_name": "generated_10", + "path": "models/features/spaced_stg_sales_0.sql", + "original_file_path": "models/features/spaced_stg_sales_0.sql", + "unique_id": "model.generated_10.spaced_stg_sales_0", + "fqn": [ + "generated_10", + "models", + "features", + "spaced_stg_sales_0" + ], + "alias": "spaced_stg_sales_0", + "config": { + "enabled": true, + "alias": null, + "schema": "main", + "database": null, + "tags": [ + "línea", + "東京" + ], + "meta": {}, + "group": null, + "materialized": "table", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [ + "línea", + "東京" + ], + "description": "Copy of stg_sales_0 with café ☕ counter", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.spaced_stg_sales_0", + "depends_on": { + "macros": [], + "nodes": [ + "model.generated_10.stg_sales_0" + ] + } + }, + "model.generated_10.taxed_stg_sales_0": { + "database": null, + "schema": "main", + "name": "taxed_stg_sales_0", + "resource_type": "model", + "package_name": "generated_10", + "path": "models/features/taxed_stg_sales_0.sql", + "original_file_path": "models/features/taxed_stg_sales_0.sql", + "unique_id": "model.generated_10.taxed_stg_sales_0", + "fqn": [ + "generated_10", + "models", + "features", + "taxed_stg_sales_0" + ], + "alias": "taxed_stg_sales_0", + "config": { + "enabled": true, + "alias": null, + "schema": "main", + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "table", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Taxed copy of stg_sales_0", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.taxed_stg_sales_0", + "depends_on": { + "macros": [], + "nodes": [ + "model.generated_10.stg_sales_0" + ] + } + }, + "model.generated_10.fulfillment_summary_0": { + "database": null, + "schema": "main", + "name": "fulfillment_summary_0", + "resource_type": "model", + "package_name": "generated_10", + "path": "models/fulfillment/marts/fulfillment_summary_0.sql", + "original_file_path": "models/fulfillment/marts/fulfillment_summary_0.sql", + "unique_id": "model.generated_10.fulfillment_summary_0", + "fqn": [ + "generated_10", + "models", + "fulfillment", + "marts", + "fulfillment_summary_0" + ], + "alias": "fulfillment_summary_0", + "config": { + "enabled": true, + "alias": null, + "schema": "main", + "database": null, + "tags": [ + "fulfillment", + "marts" + ], + "meta": {}, + "group": null, + "materialized": "table", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [ + "fulfillment", + "marts" + ], + "description": "Summary of fulfillment activity 0", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.fulfillment_summary_0", + "depends_on": { + "macros": [], + "nodes": [ + "model.generated_10.stg_fulfillment_1" + ] + } + }, + "model.generated_10.fulfillment_summary_1": { + "database": null, + "schema": "main", + "name": "fulfillment_summary_1", + "resource_type": "model", + "package_name": "generated_10", + "path": "models/fulfillment/marts/fulfillment_summary_1.sql", + "original_file_path": "models/fulfillment/marts/fulfillment_summary_1.sql", + "unique_id": "model.generated_10.fulfillment_summary_1", + "fqn": [ + "generated_10", + "models", + "fulfillment", + "marts", + "fulfillment_summary_1" + ], + "alias": "fulfillment_summary_1", + "config": { + "enabled": true, + "alias": null, + "schema": "main", + "database": null, + "tags": [ + "fulfillment", + "marts" + ], + "meta": {}, + "group": null, + "materialized": "table", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [ + "fulfillment", + "marts" + ], + "description": "Summary of fulfillment activity 1", + "columns": { + "id": { + "name": "id", + "description": "", + "meta": {}, + "data_type": null, + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.fulfillment_summary_1", + "depends_on": { + "macros": [], + "nodes": [ + "model.generated_10.stg_fulfillment_0" + ] + } + }, + "model.generated_10.stg_fulfillment_0": { + "database": null, + "schema": "main", + "name": "stg_fulfillment_0", + "resource_type": "model", + "package_name": "generated_10", + "path": "models/fulfillment/staging/stg_fulfillment_0.sql", + "original_file_path": "models/fulfillment/staging/stg_fulfillment_0.sql", + "unique_id": "model.generated_10.stg_fulfillment_0", + "fqn": [ + "generated_10", + "models", + "fulfillment", + "staging", + "stg_fulfillment_0" + ], + "alias": "stg_fulfillment_0", + "config": { + "enabled": true, + "alias": null, + "schema": "main", + "database": null, + "tags": [ + "staging" + ], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [ + "staging" + ], + "description": "Staged fulfillment events 0", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.stg_fulfillment_0", + "depends_on": { + "macros": [], + "nodes": [ + "source.generated_10.raw_events_2" + ] + } + }, + "model.generated_10.stg_fulfillment_1": { + "database": null, + "schema": "main", + "name": "stg_fulfillment_1", + "resource_type": "model", + "package_name": "generated_10", + "path": "models/fulfillment/staging/stg_fulfillment_1.sql", + "original_file_path": "models/fulfillment/staging/stg_fulfillment_1.sql", + "unique_id": "model.generated_10.stg_fulfillment_1", + "fqn": [ + "generated_10", + "models", + "fulfillment", + "staging", + "stg_fulfillment_1" + ], + "alias": "stg_fulfillment_1", + "config": { + "enabled": true, + "alias": null, + "schema": "main", + "database": null, + "tags": [ + "staging" + ], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [ + "staging" + ], + "description": "Staged fulfillment events 1", + "columns": { + "id": { + "name": "id", + "description": "", + "meta": {}, + "data_type": null, + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.stg_fulfillment_1", + "depends_on": { + "macros": [], + "nodes": [ + "source.generated_10.raw_events_0" + ] + } + }, + "model.generated_10.inventory_summary_0": { + "database": null, + "schema": "main", + "name": "inventory_summary_0", + "resource_type": "model", + "package_name": "generated_10", + "path": "models/inventory/marts/inventory_summary_0.sql", + "original_file_path": "models/inventory/marts/inventory_summary_0.sql", + "unique_id": "model.generated_10.inventory_summary_0", + "fqn": [ + "generated_10", + "models", + "inventory", + "marts", + "inventory_summary_0" + ], + "alias": "inventory_summary_0", + "config": { + "enabled": true, + "alias": null, + "schema": "main", + "database": null, + "tags": [ + "inventory", + "marts" + ], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [ + { + "sql": "SELECT 'main.inventory_summary_0' AS refreshed_relation, 'café ☕ counter' AS refresh_label", + "transaction": true, + "index": 0 + }, + { + "sql": "SELECT 'main.inventory_summary_0' AS refreshed", + "transaction": true, + "index": 1 + } + ], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [ + "inventory", + "marts" + ], + "description": "Summary of inventory activity 0 (東京 warehouse)", + "columns": {}, + "meta": { + "sqlbuild": { + "lifecycle_hooks": { + "post_hooks": [ + { + "type": "sql", + "statement": "SELECT 'main.inventory_summary_0' AS refreshed_relation, 'café ☕ counter' AS refresh_label", + "name": "record_refresh", + "relative_path": "hooks/sql/record_refresh.sql", + "definition_sql": "SELECT @'relation' AS refreshed_relation, @'label' AS refresh_label", + "kwargs": { + "relation": "@@CTX:destination.qualified", + "label": "café ☕ counter" + }, + "description": "Record a refresh for one relation" + }, + { + "type": "sql", + "statement": "SELECT 'main.inventory_summary_0' AS refreshed" + } + ] + } + } + }, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.inventory_summary_0", + "depends_on": { + "macros": [], + "nodes": [ + "model.generated_10.stg_inventory_1" + ] + } + }, + "model.generated_10.inventory_summary_1": { + "database": null, + "schema": "main", + "name": "inventory_summary_1", + "resource_type": "model", + "package_name": "generated_10", + "path": "models/inventory/marts/inventory_summary_1.sql", + "original_file_path": "models/inventory/marts/inventory_summary_1.sql", + "unique_id": "model.generated_10.inventory_summary_1", + "fqn": [ + "generated_10", + "models", + "inventory", + "marts", + "inventory_summary_1" + ], + "alias": "inventory_summary_1", + "config": { + "enabled": true, + "alias": null, + "schema": "main", + "database": null, + "tags": [ + "inventory", + "marts" + ], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [ + { + "sql": "SELECT 'main.inventory_summary_1' AS refreshed_relation, 'Ελληνικά orders' AS refresh_label", + "transaction": true, + "index": 0 + }, + { + "sql": "SELECT 'main.inventory_summary_1' AS refreshed", + "transaction": true, + "index": 1 + } + ], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [ + "inventory", + "marts" + ], + "description": "Summary of inventory activity 1", + "columns": {}, + "meta": { + "sqlbuild": { + "lifecycle_hooks": { + "post_hooks": [ + { + "type": "sql", + "statement": "SELECT 'main.inventory_summary_1' AS refreshed_relation, 'Ελληνικά orders' AS refresh_label", + "name": "record_refresh", + "relative_path": "hooks/sql/record_refresh.sql", + "definition_sql": "SELECT @'relation' AS refreshed_relation, @'label' AS refresh_label", + "kwargs": { + "relation": "@@CTX:destination.qualified", + "label": "Ελληνικά orders" + }, + "description": "Record a refresh for one relation" + }, + { + "type": "sql", + "statement": "SELECT 'main.inventory_summary_1' AS refreshed" + } + ] + } + } + }, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.inventory_summary_1", + "depends_on": { + "macros": [], + "nodes": [ + "model.generated_10.stg_inventory_1" + ] + } + }, + "model.generated_10.stg_inventory_0": { + "database": null, + "schema": "main", + "name": "stg_inventory_0", + "resource_type": "model", + "package_name": "generated_10", + "path": "models/inventory/staging/stg_inventory_0.sql", + "original_file_path": "models/inventory/staging/stg_inventory_0.sql", + "unique_id": "model.generated_10.stg_inventory_0", + "fqn": [ + "generated_10", + "models", + "inventory", + "staging", + "stg_inventory_0" + ], + "alias": "stg_inventory_0", + "config": { + "enabled": true, + "alias": null, + "schema": "main", + "database": null, + "tags": [ + "staging" + ], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [ + "staging" + ], + "description": "Staged inventory events 0 (café ☕ counter)", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.stg_inventory_0", + "depends_on": { + "macros": [], + "nodes": [ + "source.generated_10.raw_events_0" + ] + } + }, + "model.generated_10.stg_inventory_1": { + "database": null, + "schema": "main", + "name": "stg_inventory_1", + "resource_type": "model", + "package_name": "generated_10", + "path": "models/inventory/staging/stg_inventory_1.sql", + "original_file_path": "models/inventory/staging/stg_inventory_1.sql", + "unique_id": "model.generated_10.stg_inventory_1", + "fqn": [ + "generated_10", + "models", + "inventory", + "staging", + "stg_inventory_1" + ], + "alias": "stg_inventory_1", + "config": { + "enabled": true, + "alias": null, + "schema": "main", + "database": null, + "tags": [ + "staging" + ], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [ + "staging" + ], + "description": "Staged inventory events 1", + "columns": { + "id": { + "name": "id", + "description": "", + "meta": {}, + "data_type": null, + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.stg_inventory_1", + "depends_on": { + "macros": [], + "nodes": [ + "source.generated_10.raw_events_0" + ] + } + }, + "model.generated_10.large_events": { + "database": null, + "schema": "main", + "name": "large_events", + "resource_type": "model", + "package_name": "generated_10", + "path": "models/rendering/large_events.sql", + "original_file_path": "models/rendering/large_events.sql", + "unique_id": "model.generated_10.large_events", + "fqn": [ + "generated_10", + "models", + "rendering", + "large_events" + ], + "alias": "large_events", + "config": { + "enabled": true, + "alias": null, + "schema": "main", + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "table", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Events above a threshold from a table function", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.large_events", + "depends_on": { + "macros": [], + "nodes": [ + "model.generated_10.table_fn__events_above" + ] + } + }, + "model.generated_10.latest_raw_events_2": { + "database": null, + "schema": "main", + "name": "latest_raw_events_2", + "resource_type": "model", + "package_name": "generated_10", + "path": "models/rendering/latest_raw_events_2.sql", + "original_file_path": "models/rendering/latest_raw_events_2.sql", + "unique_id": "model.generated_10.latest_raw_events_2", + "fqn": [ + "generated_10", + "models", + "rendering", + "latest_raw_events_2" + ], + "alias": "latest_raw_events_2", + "config": { + "enabled": true, + "alias": null, + "schema": "main", + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "table", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Newest rows of raw_events_2", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.latest_raw_events_2", + "depends_on": { + "macros": [], + "nodes": [ + "source.generated_10.raw_events_2" + ] + } + }, + "model.generated_10.stated_stg_fulfillment_1": { + "database": null, + "schema": "main", + "name": "stated_stg_fulfillment_1", + "resource_type": "model", + "package_name": "generated_10", + "path": "models/rendering/stated_stg_fulfillment_1.sql", + "original_file_path": "models/rendering/stated_stg_fulfillment_1.sql", + "unique_id": "model.generated_10.stated_stg_fulfillment_1", + "fqn": [ + "generated_10", + "models", + "rendering", + "stated_stg_fulfillment_1" + ], + "alias": "stated_stg_fulfillment_1", + "config": { + "enabled": true, + "alias": null, + "schema": "main", + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "table", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Enum-typed copy of stg_fulfillment_1", + "columns": { + "id": { + "name": "id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "status": { + "name": "status", + "description": "", + "meta": {}, + "data_type": "VARCHAR", + "constraints": [], + "quote": null, + "tags": [] + }, + "tier": { + "name": "tier", + "description": "", + "meta": {}, + "data_type": "VARCHAR", + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.stated_stg_fulfillment_1", + "depends_on": { + "macros": [], + "nodes": [ + "model.generated_10.stg_fulfillment_1" + ] + } + }, + "model.generated_10.unioned_event_ids": { + "database": null, + "schema": "main", + "name": "unioned_event_ids", + "resource_type": "model", + "package_name": "generated_10", + "path": "models/rendering/unioned_event_ids.sql", + "original_file_path": "models/rendering/unioned_event_ids.sql", + "unique_id": "model.generated_10.unioned_event_ids", + "fqn": [ + "generated_10", + "models", + "rendering", + "unioned_event_ids" + ], + "alias": "unioned_event_ids", + "config": { + "enabled": true, + "alias": null, + "schema": "main", + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "table", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Ids from two staged relations", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.unioned_event_ids", + "depends_on": { + "macros": [], + "nodes": [ + "model.generated_10.stg_fulfillment_0", + "model.generated_10.stg_fulfillment_1" + ] + } + }, + "model.generated_10.sales_summary_0": { + "database": null, + "schema": "main", + "name": "sales_summary_0", + "resource_type": "model", + "package_name": "generated_10", + "path": "models/sales/marts/sales_summary_0.sql", + "original_file_path": "models/sales/marts/sales_summary_0.sql", + "unique_id": "model.generated_10.sales_summary_0", + "fqn": [ + "generated_10", + "models", + "sales", + "marts", + "sales_summary_0" + ], + "alias": "sales_summary_0", + "config": { + "enabled": true, + "alias": null, + "schema": "main", + "database": null, + "tags": [ + "sales", + "marts" + ], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [ + { + "sql": "SELECT 'main.sales_summary_0' AS refreshed_relation, 'Zürich – depot' AS refresh_label", + "transaction": true, + "index": 0 + }, + { + "sql": "SELECT 'main.sales_summary_0' AS refreshed", + "transaction": true, + "index": 1 + } + ], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [ + "sales", + "marts" + ], + "description": "Summary of sales activity 0", + "columns": {}, + "meta": { + "sqlbuild": { + "lifecycle_hooks": { + "post_hooks": [ + { + "type": "sql", + "statement": "SELECT 'main.sales_summary_0' AS refreshed_relation, 'Zürich – depot' AS refresh_label", + "name": "record_refresh", + "relative_path": "hooks/sql/record_refresh.sql", + "definition_sql": "SELECT @'relation' AS refreshed_relation, @'label' AS refresh_label", + "kwargs": { + "relation": "@@CTX:destination.qualified", + "label": "Zürich – depot" + }, + "description": "Record a refresh for one relation" + }, + { + "type": "sql", + "statement": "SELECT 'main.sales_summary_0' AS refreshed" + } + ] + } + } + }, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.sales_summary_0", + "depends_on": { + "macros": [], + "nodes": [ + "model.generated_10.stg_sales_0" + ] + } + }, + "model.generated_10.stg_sales_0": { + "database": null, + "schema": "main", + "name": "stg_sales_0", + "resource_type": "model", + "package_name": "generated_10", + "path": "models/sales/staging/stg_sales_0.sql", + "original_file_path": "models/sales/staging/stg_sales_0.sql", + "unique_id": "model.generated_10.stg_sales_0", + "fqn": [ + "generated_10", + "models", + "sales", + "staging", + "stg_sales_0" + ], + "alias": "stg_sales_0", + "config": { + "enabled": true, + "alias": null, + "schema": "main", + "database": null, + "tags": [ + "staging" + ], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [ + "staging" + ], + "description": "Staged sales events 0", + "columns": { + "id": { + "name": "id", + "description": "", + "meta": {}, + "data_type": null, + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.stg_sales_0", + "depends_on": { + "macros": [], + "nodes": [ + "source.generated_10.raw_events_1" + ] + } + } + }, + "sources": { + "source.generated_10.loaded_products": { + "database": null, + "schema": "main", + "name": "loaded_products", + "resource_type": "source", + "package_name": "generated_10", + "path": "sources/managed.yml", + "original_file_path": "sources/managed.yml", + "unique_id": "source.generated_10.loaded_products", + "fqn": [ + "generated_10", + "loaded_products" + ], + "source_name": "loaded_products", + "source_description": "", + "loader": "", + "identifier": "loaded_products", + "quoting": {}, + "loaded_at_field": null, + "freshness": null, + "external": null, + "description": "Products loaded by a project loader.", + "columns": { + "id": { + "name": "id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "label": { + "name": "label", + "description": "", + "meta": {}, + "data_type": "VARCHAR", + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": { + "sqlbuild": { + "loader": "loaded_products", + "auto_load": false, + "write_strategy": "table" + } + }, + "source_meta": {}, + "tags": [], + "config": {}, + "patch_path": null, + "unrendered_config": {}, + "relation_name": null + }, + "source.generated_10.raw_events_0": { + "database": null, + "schema": "", + "name": "raw_events_0", + "resource_type": "source", + "package_name": "generated_10", + "path": "sources/raw.yml", + "original_file_path": "sources/raw.yml", + "unique_id": "source.generated_10.raw_events_0", + "fqn": [ + "generated_10", + "raw_events_0" + ], + "source_name": "raw_events_0", + "source_description": "", + "loader": "", + "identifier": "raw_events_0", + "quoting": {}, + "loaded_at_field": null, + "freshness": null, + "external": null, + "description": "Generated event feed 0.", + "columns": { + "id": { + "name": "id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "amount": { + "name": "amount", + "description": "", + "meta": {}, + "data_type": "DOUBLE", + "constraints": [], + "quote": null, + "tags": [] + }, + "status": { + "name": "status", + "description": "", + "meta": {}, + "data_type": "VARCHAR", + "constraints": [], + "quote": null, + "tags": [] + }, + "created_at": { + "name": "created_at", + "description": "", + "meta": {}, + "data_type": "TIMESTAMP", + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": {}, + "source_meta": {}, + "tags": [], + "config": {}, + "patch_path": null, + "unrendered_config": {}, + "relation_name": null + }, + "source.generated_10.raw_events_1": { + "database": null, + "schema": "", + "name": "raw_events_1", + "resource_type": "source", + "package_name": "generated_10", + "path": "sources/raw.yml", + "original_file_path": "sources/raw.yml", + "unique_id": "source.generated_10.raw_events_1", + "fqn": [ + "generated_10", + "raw_events_1" + ], + "source_name": "raw_events_1", + "source_description": "", + "loader": "", + "identifier": "raw_events_1", + "quoting": {}, + "loaded_at_field": null, + "freshness": null, + "external": null, + "description": "Generated event feed 1.", + "columns": { + "id": { + "name": "id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "amount": { + "name": "amount", + "description": "", + "meta": {}, + "data_type": "DOUBLE", + "constraints": [], + "quote": null, + "tags": [] + }, + "status": { + "name": "status", + "description": "", + "meta": {}, + "data_type": "VARCHAR", + "constraints": [], + "quote": null, + "tags": [] + }, + "created_at": { + "name": "created_at", + "description": "", + "meta": {}, + "data_type": "TIMESTAMP", + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": {}, + "source_meta": {}, + "tags": [], + "config": {}, + "patch_path": null, + "unrendered_config": {}, + "relation_name": null + }, + "source.generated_10.raw_events_2": { + "database": null, + "schema": "", + "name": "raw_events_2", + "resource_type": "source", + "package_name": "generated_10", + "path": "sources/raw.yml", + "original_file_path": "sources/raw.yml", + "unique_id": "source.generated_10.raw_events_2", + "fqn": [ + "generated_10", + "raw_events_2" + ], + "source_name": "raw_events_2", + "source_description": "", + "loader": "", + "identifier": "raw_events_2", + "quoting": {}, + "loaded_at_field": null, + "freshness": null, + "external": null, + "description": "Generated event feed 2.", + "columns": { + "id": { + "name": "id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "amount": { + "name": "amount", + "description": "", + "meta": {}, + "data_type": "DOUBLE", + "constraints": [], + "quote": null, + "tags": [] + }, + "status": { + "name": "status", + "description": "", + "meta": {}, + "data_type": "VARCHAR", + "constraints": [], + "quote": null, + "tags": [] + }, + "created_at": { + "name": "created_at", + "description": "", + "meta": {}, + "data_type": "TIMESTAMP", + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": {}, + "source_meta": {}, + "tags": [], + "config": {}, + "patch_path": null, + "unrendered_config": {}, + "relation_name": null + } + }, + "macros": { + "macro.generated_10.adjust_1": { + "name": "adjust_1", + "resource_type": "macro", + "package_name": "generated_10", + "path": "macros/adjust_1.py", + "original_file_path": "macros/adjust_1.py", + "unique_id": "macro.generated_10.adjust_1", + "macro_sql": "def adjust_1_round(expression: str) -> str:\n \"\"\"Round an amount to two places.\"\"\"\n return f\"ROUND({expression}, 2)\"\n\n\ndef adjust_1(expression: str) -> str:\n \"\"\"Double and round an amount.\"\"\"\n return adjust_1_round(f\"({expression} * 2)\")\n", + "depends_on": { + "macros": [ + "macro.generated_10.adjust_1_round" + ] + }, + "description": "", + "meta": { + "sqlbuild_visibility": "global", + "sqlbuild_scope_path": null + }, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "arguments": [], + "supported_languages": null + }, + "macro.generated_10.adjust_1_round": { + "name": "adjust_1_round", + "resource_type": "macro", + "package_name": "generated_10", + "path": "macros/adjust_1.py", + "original_file_path": "macros/adjust_1.py", + "unique_id": "macro.generated_10.adjust_1_round", + "macro_sql": "def adjust_1_round(expression: str) -> str:\n \"\"\"Round an amount to two places.\"\"\"\n return f\"ROUND({expression}, 2)\"\n\n\ndef adjust_1(expression: str) -> str:\n \"\"\"Double and round an amount.\"\"\"\n return adjust_1_round(f\"({expression} * 2)\")\n", + "depends_on": { + "macros": [] + }, + "description": "", + "meta": { + "sqlbuild_visibility": "global", + "sqlbuild_scope_path": null + }, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "arguments": [], + "supported_languages": null + }, + "macro.generated_10.round_amount": { + "name": "round_amount", + "resource_type": "macro", + "package_name": "generated_10", + "path": "macros/rounding.py", + "original_file_path": "macros/rounding.py", + "unique_id": "macro.generated_10.round_amount", + "macro_sql": "def round_amount(expression: str, places: int = 2) -> str:\n \"\"\"Round an amount.\"\"\"\n return f\"ROUND({expression}, {places})\"\n", + "depends_on": { + "macros": [] + }, + "description": "", + "meta": { + "sqlbuild_visibility": "global", + "sqlbuild_scope_path": null + }, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "arguments": [], + "supported_languages": null + }, + "macro.generated_10.taxed": { + "name": "taxed", + "resource_type": "macro", + "package_name": "generated_10", + "path": "macros/tax.py", + "original_file_path": "macros/tax.py", + "unique_id": "macro.generated_10.taxed", + "macro_sql": "from macros.rounding import round_amount\n\n\ndef taxed(expression: str) -> str:\n \"\"\"Add tax to an amount, rounded.\"\"\"\n return round_amount(f\"{expression} * 1.2\")\n", + "depends_on": { + "macros": [ + "macro.generated_10.round_amount" + ] + }, + "description": "", + "meta": { + "sqlbuild_visibility": "global", + "sqlbuild_scope_path": null + }, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "arguments": [], + "supported_languages": null + }, + "macro.generated_10.analysis_region": { + "name": "analysis_region", + "resource_type": "macro", + "package_name": "generated_10", + "path": "models/analysis/_sqlbuild/_macros/regions.py", + "original_file_path": "models/analysis/_sqlbuild/_macros/regions.py", + "unique_id": "macro.generated_10.analysis_region", + "macro_sql": "def analysis_region() -> str:\n \"\"\"Return the region rows are assigned to.\"\"\"\n return \"'north'\"\n", + "depends_on": { + "macros": [] + }, + "description": "", + "meta": { + "sqlbuild_visibility": "local", + "sqlbuild_scope_path": "models/analysis" + }, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "arguments": [], + "supported_languages": null + }, + "macro.generated_10.adjust_0": { + "name": "adjust_0", + "resource_type": "macro", + "package_name": "generated_10", + "path": "models/fulfillment/_sqlbuild/macros/adjust_0.py", + "original_file_path": "models/fulfillment/_sqlbuild/macros/adjust_0.py", + "unique_id": "macro.generated_10.adjust_0", + "macro_sql": "def adjust_0(ctx, expression: str) -> str:\n \"\"\"Scale an amount by the configured discount.\"\"\"\n rate = ctx.vars[\"discount_rate\"]\n return f\"({expression} * (1 - {rate}))\"\n", + "depends_on": { + "macros": [] + }, + "description": "", + "meta": { + "sqlbuild_visibility": "inherited", + "sqlbuild_scope_path": "models/fulfillment" + }, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "arguments": [], + "supported_languages": null + }, + "macro.generated_10.adjust_2": { + "name": "adjust_2", + "resource_type": "macro", + "package_name": "generated_10", + "path": "models/inventory/staging/_sqlbuild/_macros/adjust_2.py", + "original_file_path": "models/inventory/staging/_sqlbuild/_macros/adjust_2.py", + "unique_id": "macro.generated_10.adjust_2", + "macro_sql": "def adjust_2(expression: str) -> str:\n \"\"\"Offset an amount.\"\"\"\n return f\"({expression} + 2 + 1)\"\n", + "depends_on": { + "macros": [] + }, + "description": "", + "meta": { + "sqlbuild_visibility": "local", + "sqlbuild_scope_path": "models/inventory/staging" + }, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "arguments": [], + "supported_languages": null + }, + "macro.generated_10.latest_rows": { + "name": "latest_rows", + "resource_type": "macro", + "package_name": "generated_10", + "path": "models/rendering/_sqlbuild/_macros/relations.py", + "original_file_path": "models/rendering/_sqlbuild/_macros/relations.py", + "unique_id": "macro.generated_10.latest_rows", + "macro_sql": "from sqlbuild.refs import SqlResourceRef\n\n\ndef union_ids(relations: list[SqlResourceRef]) -> str:\n \"\"\"Union the ids of several relations.\"\"\"\n selects = [f\"SELECT id FROM {relation}\" for relation in relations]\n return \" UNION ALL \".join(selects)\n\n\ndef latest_rows(relation: SqlResourceRef, limit: int = 2) -> str:\n \"\"\"Return the newest rows of one relation.\"\"\"\n return f\"(SELECT id, amount FROM {relation} ORDER BY id DESC LIMIT {limit})\"\n", + "depends_on": { + "macros": [] + }, + "description": "", + "meta": { + "sqlbuild_visibility": "local", + "sqlbuild_scope_path": "models/rendering" + }, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "arguments": [], + "supported_languages": null + }, + "macro.generated_10.union_ids": { + "name": "union_ids", + "resource_type": "macro", + "package_name": "generated_10", + "path": "models/rendering/_sqlbuild/_macros/relations.py", + "original_file_path": "models/rendering/_sqlbuild/_macros/relations.py", + "unique_id": "macro.generated_10.union_ids", + "macro_sql": "from sqlbuild.refs import SqlResourceRef\n\n\ndef union_ids(relations: list[SqlResourceRef]) -> str:\n \"\"\"Union the ids of several relations.\"\"\"\n selects = [f\"SELECT id FROM {relation}\" for relation in relations]\n return \" UNION ALL \".join(selects)\n\n\ndef latest_rows(relation: SqlResourceRef, limit: int = 2) -> str:\n \"\"\"Return the newest rows of one relation.\"\"\"\n return f\"(SELECT id, amount FROM {relation} ORDER BY id DESC LIMIT {limit})\"\n", + "depends_on": { + "macros": [] + }, + "description": "", + "meta": { + "sqlbuild_visibility": "local", + "sqlbuild_scope_path": "models/rendering" + }, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "arguments": [], + "supported_languages": null + }, + "macro.generated_10__sqlbuild_hooks.record_refresh": { + "name": "record_refresh", + "resource_type": "macro", + "package_name": "generated_10__sqlbuild_hooks", + "path": "hooks/sql/record_refresh.sql", + "original_file_path": "hooks/sql/record_refresh.sql", + "unique_id": "macro.generated_10__sqlbuild_hooks.record_refresh", + "macro_sql": "SELECT @'relation' AS refreshed_relation, @'label' AS refresh_label", + "depends_on": { + "macros": [] + }, + "description": "Record a refresh for one relation", + "meta": { + "sqlbuild_resource_type": "sql_hook", + "sqlbuild_hook_name": "record_refresh" + }, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "arguments": [], + "supported_languages": null + } + }, + "exposures": {}, + "metrics": {}, + "groups": {}, + "selectors": {}, + "disabled": {}, + "parent_map": { + "model.generated_10.chain_1_stg_sales_0": [ + "model.generated_10.contracted_stg_sales_0" + ], + "model.generated_10.chain_2_stg_sales_0": [ + "model.generated_10.chain_1_stg_sales_0" + ], + "model.generated_10.chain_3_stg_sales_0": [ + "model.generated_10.chain_2_stg_sales_0" + ], + "model.generated_10.checked_stg_fulfillment_0": [ + "model.generated_10.stg_fulfillment_0" + ], + "model.generated_10.contracted_stg_sales_0": [ + "source.generated_10.raw_events_1" + ], + "model.generated_10.regional_stg_inventory_0": [ + "model.generated_10.stg_inventory_0" + ], + "model.generated_10.audited_stg_fulfillment_1": [ + "model.generated_10.stg_fulfillment_1" + ], + "model.generated_10.check_history_stg_fulfillment_1": [ + "model.generated_10.stg_fulfillment_1" + ], + "model.generated_10.copied_stg_sales_0": [ + "model.generated_10.stg_sales_0" + ], + "model.generated_10.flagged_stg_inventory_1": [ + "model.generated_10.is_placed", + "model.generated_10.is_large", + "model.generated_10.stg_inventory_1" + ], + "model.generated_10.guarded_stg_inventory_1": [ + "model.generated_10.stg_inventory_1" + ], + "model.generated_10.merge_stg_sales_0": [ + "model.generated_10.stg_sales_0" + ], + "model.generated_10.product_labels": [ + "source.generated_10.loaded_products" + ], + "model.generated_10.spaced_stg_sales_0": [ + "model.generated_10.stg_sales_0" + ], + "model.generated_10.taxed_stg_sales_0": [ + "model.generated_10.stg_sales_0" + ], + "model.generated_10.fulfillment_summary_0": [ + "model.generated_10.stg_fulfillment_1" + ], + "model.generated_10.fulfillment_summary_1": [ + "model.generated_10.stg_fulfillment_0" + ], + "model.generated_10.stg_fulfillment_0": [ + "source.generated_10.raw_events_2" + ], + "model.generated_10.stg_fulfillment_1": [ + "source.generated_10.raw_events_0" + ], + "model.generated_10.inventory_summary_0": [ + "model.generated_10.stg_inventory_1" + ], + "model.generated_10.inventory_summary_1": [ + "model.generated_10.stg_inventory_1" + ], + "model.generated_10.stg_inventory_0": [ + "source.generated_10.raw_events_0" + ], + "model.generated_10.stg_inventory_1": [ + "source.generated_10.raw_events_0" + ], + "model.generated_10.large_events": [ + "model.generated_10.table_fn__events_above" + ], + "model.generated_10.latest_raw_events_2": [ + "source.generated_10.raw_events_2" + ], + "model.generated_10.stated_stg_fulfillment_1": [ + "model.generated_10.stg_fulfillment_1" + ], + "model.generated_10.unioned_event_ids": [ + "model.generated_10.stg_fulfillment_0", + "model.generated_10.stg_fulfillment_1" + ], + "model.generated_10.sales_summary_0": [ + "model.generated_10.stg_sales_0" + ], + "model.generated_10.stg_sales_0": [ + "source.generated_10.raw_events_1" + ], + "source.generated_10.loaded_products": [], + "source.generated_10.raw_events_0": [], + "source.generated_10.raw_events_1": [], + "source.generated_10.raw_events_2": [], + "model.generated_10.is_placed": [], + "model.generated_10.table_fn__events_above": [ + "model.generated_10.stg_inventory_0" + ], + "model.generated_10.is_large": [] + }, + "group_map": {}, + "docs": {}, + "saved_queries": {}, + "semantic_models": {}, + "unit_tests": {} + } +} diff --git a/tests/goldens/compiler/seed/11.json b/tests/goldens/compiler/seed/11.json new file mode 100644 index 0000000000..fb6be39da4 --- /dev/null +++ b/tests/goldens/compiler/seed/11.json @@ -0,0 +1,45 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "D002", + "message": "MODEL(...) in '/models/fulfillment/marts/fulfillment_summary_0.sql' contains invalid SQLBuild header syntax: expected key at position 267" + } + ] + }, + { + "label": "compile-store-warm", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "D002", + "message": "MODEL(...) in '/models/fulfillment/marts/fulfillment_summary_0.sql' contains invalid SQLBuild header syntax: expected key at position 267" + } + ] + }, + { + "label": "plan", + "exit_code": 1, + "diagnostics": [ + "Project discovery START", + "Project assembly START", + "Declaration parsing START", + "Declaration parsing FAIL ()", + "Project assembly FAIL ()", + "Project discovery FAIL ()", + "error[D002]: MODEL(...) in '/models/fulfillment/marts/fulfillment_summary_0.sql' contains invalid SQLBuild header syntax: expected key at position 267", + "", + "" + ] + } + ], + "compiled": {}, + "manifest": null +} diff --git a/tests/goldens/compiler/seed/2.json b/tests/goldens/compiler/seed/2.json new file mode 100644 index 0000000000..67c315c293 --- /dev/null +++ b/tests/goldens/compiler/seed/2.json @@ -0,0 +1,84 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "P001", + "message": "Model file models/sales/marts/sales_summary_1.sql references unknown model 'missing_model'" + } + ] + }, + { + "label": "compile-store-warm", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "P001", + "message": "Model file models/sales/marts/sales_summary_1.sql references unknown model 'missing_model'" + } + ] + }, + { + "label": "plan", + "exit_code": 1, + "diagnostics": [ + "Project discovery START", + "Project assembly START", + "Declaration parsing START", + "Declaration parsing OK ()", + "Python discovery START", + "Python discovery OK ()", + "Project assembly OK ()", + "Project discovery OK ()", + "Compiling project...", + "error[P001]: Model file models/sales/marts/sales_summary_1.sql references unknown model 'missing_model'", + "", + "" + ] + }, + { + "label": "compile-select", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "P001", + "message": "Model file models/sales/marts/sales_summary_1.sql references unknown model 'missing_model'" + } + ] + }, + { + "label": "compile-no-analysis", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "P001", + "message": "Model file models/sales/marts/sales_summary_1.sql references unknown model 'missing_model'" + } + ] + }, + { + "label": "compile-rich-lineage", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "P001", + "message": "Model file models/sales/marts/sales_summary_1.sql references unknown model 'missing_model'" + } + ] + } + ], + "compiled": {}, + "manifest": null +} diff --git a/tests/goldens/compiler/seed/3.json b/tests/goldens/compiler/seed/3.json new file mode 100644 index 0000000000..ca042e5bf8 --- /dev/null +++ b/tests/goldens/compiler/seed/3.json @@ -0,0 +1,2396 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 0, + "diagnostics": [] + }, + { + "label": "compile-store-warm", + "exit_code": 0, + "diagnostics": [] + }, + { + "label": "plan", + "exit_code": 0, + "diagnostics": [] + } + ], + "compiled": { + "audits/generic/audited_event_totals/amount_floor.sql": [ + "SELECT *", + "FROM __ref(\"audited_event_totals\")", + "WHERE amount < -23", + "" + ], + "audits/generic/audited_events/accepted_values__id.sql": [ + "SELECT id", + "FROM __source(\"audited_events\")", + "WHERE id IS NOT NULL", + " AND id NOT IN (1, 2)", + "" + ], + "audits/generic/audited_events/not_null__id.sql": [ + "SELECT id", + "FROM __source(\"audited_events\")", + "WHERE id IS NULL", + "" + ], + "audits/generic/audited_events/unique__id.sql": [ + "SELECT id, COUNT(*) AS duplicate_count", + "FROM __source(\"audited_events\")", + "WHERE id IS NOT NULL", + "GROUP BY id", + "HAVING COUNT(*) > 1", + "" + ], + "audits/generic/billing_summary_1/not_null__id.sql": [ + "SELECT id", + "FROM __ref(\"billing_summary_1\")", + "WHERE id IS NULL", + "" + ], + "audits/generic/channel_codes/not_null__id.sql": [ + "SELECT id", + "FROM __seed(\"channel_codes\")", + "WHERE id IS NULL", + "" + ], + "audits/generic/checked_stg_billing_1/accepted_values__status.sql": [ + "SELECT status", + "FROM __ref(\"checked_stg_billing_1\")", + "WHERE status IS NOT NULL", + " AND status NOT IN ('placed', 'expédié', '取消', 'returned ✓')", + "" + ], + "audits/generic/checked_stg_billing_1/expression_is_true.sql": [ + "SELECT *", + "FROM __ref(\"checked_stg_billing_1\")", + "WHERE NOT (amount >= -1000)", + "" + ], + "audits/generic/checked_stg_billing_1/relationships__id.sql": [ + "SELECT id", + "FROM __ref(\"checked_stg_billing_1\")", + "WHERE id IS NOT NULL", + " AND id NOT IN (", + " SELECT id", + " FROM __ref(\"stg_billing_1\")", + " WHERE id IS NOT NULL", + " )", + "" + ], + "audits/generic/configured_events/accepted_values__id.sql": [ + "SELECT id", + "FROM __source(\"configured_events\")", + "WHERE id IS NOT NULL", + " AND id NOT IN ('1')", + "" + ], + "audits/generic/configured_stg_billing_1/accepted_values__status.sql": [ + "SELECT status", + "FROM __ref(\"configured_stg_billing_1\")", + "WHERE status IS NOT NULL", + " AND status NOT IN ('placed', 'shipped')", + "" + ], + "audits/generic/configured_stg_billing_1/configured_amount_floor.sql": [ + "SELECT *", + "FROM __ref(\"configured_stg_billing_1\")", + "WHERE amount < -25", + "" + ], + "audits/generic/configured_stg_billing_1/configured_id_present__id.sql": [ + "SELECT id", + "FROM __ref(\"configured_stg_billing_1\")", + "WHERE id IS NULL", + "" + ], + "audits/generic/configured_stg_billing_1/unique__id.sql": [ + "SELECT id, COUNT(*) AS duplicate_count", + "FROM __ref(\"configured_stg_billing_1\")", + "WHERE id IS NOT NULL", + "GROUP BY id", + "HAVING COUNT(*) > 1", + "" + ], + "audits/generic/stg_billing_0/not_null__id.sql": [ + "SELECT id", + "FROM __ref(\"stg_billing_0\")", + "WHERE id IS NULL", + "" + ], + "functions/sql/configured_label.sql": [ + "CREATE OR REPLACE MACRO main.configured_label(raw_status) AS (", + "UPPER(raw_status)", + ")", + "" + ], + "functions/sql/scaled_amount.sql": [ + "CREATE OR REPLACE MACRO main.scaled_amount(p_amount, p_factor) AS (", + "p_amount * p_factor", + ")", + "" + ], + "functions/sql/table_fn__amount_bands.sql": [ + "CREATE OR REPLACE MACRO main.table_fn__amount_bands(p_floor) AS TABLE", + "SELECT CAST(id AS INTEGER) AS id,", + " CASE WHEN amount > p_floor THEN 'high' ELSE 'low' END AS band", + "FROM __ref(\"stg_billing_1\")", + "" + ], + "models/analysis/banded_amounts.sql": [ + "SELECT bands.id, bands.band", + "FROM __table_fn(\"table_fn__amount_bands\")(8.5) AS bands", + "" + ], + "models/analysis/checked_stg_billing_1.sql": [ + "SELECT id, amount, status", + "FROM __ref(\"stg_billing_1\")", + "" + ], + "models/analysis/regional_stg_billing_1.sql": [ + "SELECT id, amount, 'east' AS region", + "FROM __ref(\"stg_billing_1\")", + "" + ], + "models/analysis/scaled_stg_billing_1.sql": [ + "SELECT id, __udf(\"scaled_amount\")(amount, 8) AS scaled_amount", + "FROM __ref(\"stg_billing_1\")", + "" + ], + "models/billing/marts/billing_summary_0.sql": [ + "SELECT", + " t.id,", + " t.amount,", + " t.status,", + " t.created_at,", + " t.amount + 299 AS threshold_2_amount,", + " (t.amount + 0 + 1) AS adjust_0_amount", + "FROM __ref(\"stg_billing_1\") t", + "LEFT JOIN __ref(\"stg_billing_0\") j1 ON t.id = j1.id", + "" + ], + "models/billing/marts/billing_summary_1.sql": [ + "SELECT", + " t.id,", + " t.amount,", + " t.status,", + " t.created_at,", + " t.amount + 299 AS threshold_2_amount", + "FROM __ref(\"stg_billing_1\") t", + "" + ], + "models/billing/staging/stg_billing_0.sql": [ + "SELECT", + " t.id,", + " t.amount,", + " t.status,", + " t.created_at,", + " t.amount + 374 AS threshold_0_amount,", + " 'Zürich – depot' AS threshold_1_label", + "FROM __source(\"raw_events_2\") t", + "" + ], + "models/billing/staging/stg_billing_1.sql": [ + "SELECT", + " t.id,", + " t.amount,", + " t.status,", + " t.created_at,", + " 'Zürich – depot' AS threshold_1_label,", + " t.amount + 299 AS threshold_2_amount,", + " (t.amount * (1 - 0.15)) AS adjust_1_amount", + "FROM __source(\"raw_events_2\") t", + "WHERE t.status <> 'expédié'", + "" + ], + "models/features/batched_watermark_stg_billing_1.sql": [ + "SELECT CAST(created_at AS DATE) AS created_at, SUM(amount) AS amount", + "FROM __ref(\"stg_billing_1\")", + "GROUP BY CAST(created_at AS DATE)", + "" + ], + "models/features/hooked_stg_billing_1.sql": [ + "SELECT id, amount", + "FROM __ref(\"stg_billing_1\")", + "" + ], + "models/features/kept_stg_billing_1.sql": [ + "SELECT id, status, amount * 0.5 AS weighted", + "FROM __ref(\"stg_billing_1\")", + "WHERE status IN ('取消', 'expédié')", + " AND amount * 1.0 IN (0.5, 1.25, 2500.75) OR id > 0", + "" + ], + "models/rendering/audited_event_totals.sql": [ + "SELECT c.label, SUM(e.amount) AS amount", + "FROM __source(\"audited_events\") e", + "JOIN __seed(\"channel_codes\") c ON e.id = c.id", + "GROUP BY c.label", + "" + ], + "models/rendering/configured_stg_billing_1.sql": [ + "SELECT id, amount, status", + "FROM __ref(\"stg_billing_1\")", + "" + ], + "models/rendering/interpolated_stg_billing_0.sql": [ + "SELECT id,", + " '東京 warehouse' AS site,", + " $$--東京 warehouse /* $$ AS site_note,", + " 'web-orders' AS channel,", + " amount * 0.15 AS discounted", + "FROM __ref(\"stg_billing_0\")", + "" + ], + "models/rendering/legacy_event_ids.sql": [ + "SELECT id", + "FROM __ref(\"stg_billing_1\") AS legacy", + "" + ], + "models/rendering/policy_stg_billing_1.sql": [ + "SELECT id, amount, ('web' || '/dev/generated_duckdb/south' || '@web-orders') AS channel,", + " 4 AS minimum_amount,", + " 'store' AS fallback_channel", + "FROM __ref(\"stg_billing_1\")", + "WHERE amount >= 4", + "" + ], + "models/rendering/windowed_stg_billing_0.sql": [ + "SELECT id, created_at", + "FROM __ref(\"stg_billing_0\")", + "WHERE CAST(created_at AS DATE) >= CAST(@@@window_start AS DATE)", + "" + ], + "tests/macro doubles_amounts/doubles_amounts.sql": [ + "WITH input_values AS (SELECT 4 AS amount),", + "__actual__macro_doubles_amounts AS (SELECT (amount * 2) AS doubled", + " FROM input_values),", + "__expected__macro_doubles_amounts AS (SELECT 8 AS doubled)", + "SELECT 0 AS step_index, 'macro doubles_amounts' AS model_name, (SELECT COUNT(*) FROM __actual__macro_doubles_amounts) AS actual_count, (SELECT COUNT(*) FROM __expected__macro_doubles_amounts) AS expected_count, (SELECT COUNT(*) FROM (SELECT * FROM __actual__macro_doubles_amounts EXCEPT SELECT * FROM __expected__macro_doubles_amounts) AS __sqlbuild_mismatch) AS unexpected_count, (SELECT COUNT(*) FROM (SELECT * FROM __expected__macro_doubles_amounts EXCEPT SELECT * FROM __actual__macro_doubles_amounts) AS __sqlbuild_missing) AS missing_count", + "" + ], + "tests/regional_stg_billing_1/test_regional_stg_billing_1.sql": [ + "WITH __ref__stg_billing_1 AS (SELECT 1 AS id, CAST(4 AS DOUBLE) AS amount, 'placed' AS status,", + " TIMESTAMP '2026-01-01 00:00:00' AS created_at),", + "__ref__regional_stg_billing_1 AS (SELECT id, amount, 'west' AS region", + "FROM __ref__stg_billing_1),", + "__actual__regional_stg_billing_1 AS (SELECT * FROM __ref__regional_stg_billing_1),", + "__expected__regional_stg_billing_1 AS (SELECT 1 AS id, CAST(4 AS DOUBLE) AS amount, 'west' AS region)", + "SELECT 0 AS step_index, 'regional_stg_billing_1' AS model_name, (SELECT COUNT(*) FROM __actual__regional_stg_billing_1) AS actual_count, (SELECT COUNT(*) FROM __expected__regional_stg_billing_1) AS expected_count, (SELECT COUNT(*) FROM (SELECT id, amount, region FROM __actual__regional_stg_billing_1 EXCEPT SELECT id, amount, region FROM __expected__regional_stg_billing_1) AS __sqlbuild_mismatch) AS unexpected_count, (SELECT COUNT(*) FROM (SELECT id, amount, region FROM __expected__regional_stg_billing_1 EXCEPT SELECT id, amount, region FROM __actual__regional_stg_billing_1) AS __sqlbuild_missing) AS missing_count", + "" + ], + "tests/stg_billing_0/test_stg_billing_0.sql": [ + "WITH __source__raw_events_2 AS (SELECT 1 AS id, CAST(10 AS DOUBLE) AS amount, 'placed' AS status,", + " TIMESTAMP '2026-01-01 00:00:00' AS created_at),", + "__ref__stg_billing_0 AS (SELECT", + " t.id,", + " t.amount,", + " t.status,", + " t.created_at,", + " t.amount + 374 AS threshold_0_amount,", + " 'Zürich – depot' AS threshold_1_label", + "FROM __source__raw_events_2 t),", + "__actual__stg_billing_0 AS (SELECT * FROM __ref__stg_billing_0),", + "__expected__stg_billing_0 AS (SELECT 1 AS id, 'placed' AS status)", + "SELECT 0 AS step_index, 'stg_billing_0' AS model_name, (SELECT COUNT(*) FROM __actual__stg_billing_0) AS actual_count, (SELECT COUNT(*) FROM __expected__stg_billing_0) AS expected_count, (SELECT COUNT(*) FROM (SELECT id, status FROM __actual__stg_billing_0 EXCEPT SELECT id, status FROM __expected__stg_billing_0) AS __sqlbuild_mismatch) AS unexpected_count, (SELECT COUNT(*) FROM (SELECT id, status FROM __expected__stg_billing_0 EXCEPT SELECT id, status FROM __actual__stg_billing_0) AS __sqlbuild_missing) AS missing_count", + "" + ] + }, + "manifest": { + "nodes": { + "model.generated_3.banded_amounts": { + "database": null, + "schema": "main", + "name": "banded_amounts", + "resource_type": "model", + "package_name": "generated_3", + "path": "models/analysis/banded_amounts.sql", + "original_file_path": "models/analysis/banded_amounts.sql", + "unique_id": "model.generated_3.banded_amounts", + "fqn": [ + "generated_3", + "models", + "analysis", + "banded_amounts" + ], + "alias": "banded_amounts", + "config": { + "enabled": true, + "alias": null, + "schema": "main", + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Amount bands from a table function", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.banded_amounts", + "depends_on": { + "macros": [], + "nodes": [ + "model.generated_3.table_fn__amount_bands" + ] + } + }, + "model.generated_3.checked_stg_billing_1": { + "database": null, + "schema": "main", + "name": "checked_stg_billing_1", + "resource_type": "model", + "package_name": "generated_3", + "path": "models/analysis/checked_stg_billing_1.sql", + "original_file_path": "models/analysis/checked_stg_billing_1.sql", + "unique_id": "model.generated_3.checked_stg_billing_1", + "fqn": [ + "generated_3", + "models", + "analysis", + "checked_stg_billing_1" + ], + "alias": "checked_stg_billing_1", + "config": { + "enabled": true, + "alias": null, + "schema": "main", + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "table", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": [ + "id" + ], + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Metadata-checked copy of stg_billing_1", + "columns": { + "id": { + "name": "id", + "description": "", + "meta": {}, + "data_type": null, + "constraints": [], + "quote": null, + "tags": [] + }, + "status": { + "name": "status", + "description": "", + "meta": {}, + "data_type": null, + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.checked_stg_billing_1", + "depends_on": { + "macros": [], + "nodes": [ + "model.generated_3.stg_billing_1" + ] + } + }, + "model.generated_3.regional_stg_billing_1": { + "database": null, + "schema": "main", + "name": "regional_stg_billing_1", + "resource_type": "model", + "package_name": "generated_3", + "path": "models/analysis/regional_stg_billing_1.sql", + "original_file_path": "models/analysis/regional_stg_billing_1.sql", + "unique_id": "model.generated_3.regional_stg_billing_1", + "fqn": [ + "generated_3", + "models", + "analysis", + "regional_stg_billing_1" + ], + "alias": "regional_stg_billing_1", + "config": { + "enabled": true, + "alias": null, + "schema": "main", + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Regional copy of stg_billing_1", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.regional_stg_billing_1", + "depends_on": { + "macros": [], + "nodes": [ + "model.generated_3.stg_billing_1" + ] + } + }, + "model.generated_3.scaled_stg_billing_1": { + "database": null, + "schema": "main", + "name": "scaled_stg_billing_1", + "resource_type": "model", + "package_name": "generated_3", + "path": "models/analysis/scaled_stg_billing_1.sql", + "original_file_path": "models/analysis/scaled_stg_billing_1.sql", + "unique_id": "model.generated_3.scaled_stg_billing_1", + "fqn": [ + "generated_3", + "models", + "analysis", + "scaled_stg_billing_1" + ], + "alias": "scaled_stg_billing_1", + "config": { + "enabled": true, + "alias": null, + "schema": "main", + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Scaled amounts of stg_billing_1", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.scaled_stg_billing_1", + "depends_on": { + "macros": [], + "nodes": [ + "model.generated_3.scaled_amount", + "model.generated_3.stg_billing_1" + ] + } + }, + "model.generated_3.billing_summary_0": { + "database": null, + "schema": "main", + "name": "billing_summary_0", + "resource_type": "model", + "package_name": "generated_3", + "path": "models/billing/marts/billing_summary_0.sql", + "original_file_path": "models/billing/marts/billing_summary_0.sql", + "unique_id": "model.generated_3.billing_summary_0", + "fqn": [ + "generated_3", + "models", + "billing", + "marts", + "billing_summary_0" + ], + "alias": "billing_summary_0", + "config": { + "enabled": true, + "alias": null, + "schema": "main", + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Summary of billing activity 0", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.billing_summary_0", + "depends_on": { + "macros": [], + "nodes": [ + "model.generated_3.stg_billing_1", + "model.generated_3.stg_billing_0" + ] + } + }, + "model.generated_3.billing_summary_1": { + "database": null, + "schema": "main", + "name": "billing_summary_1", + "resource_type": "model", + "package_name": "generated_3", + "path": "models/billing/marts/billing_summary_1.sql", + "original_file_path": "models/billing/marts/billing_summary_1.sql", + "unique_id": "model.generated_3.billing_summary_1", + "fqn": [ + "generated_3", + "models", + "billing", + "marts", + "billing_summary_1" + ], + "alias": "billing_summary_1", + "config": { + "enabled": true, + "alias": null, + "schema": "main", + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [ + { + "sql": "SELECT 'main.billing_summary_1' AS refreshed_relation, 'Zürich – depot' AS refresh_label", + "transaction": true, + "index": 0 + }, + { + "sql": "SELECT 'main.billing_summary_1' AS refreshed", + "transaction": true, + "index": 1 + } + ], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Summary of billing activity 1", + "columns": { + "id": { + "name": "id", + "description": "", + "meta": {}, + "data_type": null, + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": { + "sqlbuild": { + "lifecycle_hooks": { + "post_hooks": [ + { + "type": "sql", + "statement": "SELECT 'main.billing_summary_1' AS refreshed_relation, 'Zürich – depot' AS refresh_label", + "name": "record_refresh", + "relative_path": "models/billing/marts/_sqlbuild/_hooks/sql/record_refresh.sql", + "definition_sql": "SELECT @'relation' AS refreshed_relation, @'label' AS refresh_label", + "kwargs": { + "relation": "@@CTX:destination.qualified", + "label": "Zürich – depot" + }, + "description": "Record a refresh for one relation" + }, + { + "type": "sql", + "statement": "SELECT 'main.billing_summary_1' AS refreshed" + } + ] + } + } + }, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.billing_summary_1", + "depends_on": { + "macros": [], + "nodes": [ + "model.generated_3.stg_billing_1" + ] + } + }, + "model.generated_3.stg_billing_0": { + "database": null, + "schema": "main", + "name": "stg_billing_0", + "resource_type": "model", + "package_name": "generated_3", + "path": "models/billing/staging/stg_billing_0.sql", + "original_file_path": "models/billing/staging/stg_billing_0.sql", + "unique_id": "model.generated_3.stg_billing_0", + "fqn": [ + "generated_3", + "models", + "billing", + "staging", + "stg_billing_0" + ], + "alias": "stg_billing_0", + "config": { + "enabled": true, + "alias": null, + "schema": "main", + "database": null, + "tags": [ + "staging" + ], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [ + "staging" + ], + "description": "Staged billing events 0", + "columns": { + "id": { + "name": "id", + "description": "", + "meta": {}, + "data_type": null, + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.stg_billing_0", + "depends_on": { + "macros": [], + "nodes": [ + "source.generated_3.raw_events_2" + ] + } + }, + "model.generated_3.stg_billing_1": { + "database": null, + "schema": "main", + "name": "stg_billing_1", + "resource_type": "model", + "package_name": "generated_3", + "path": "models/billing/staging/stg_billing_1.sql", + "original_file_path": "models/billing/staging/stg_billing_1.sql", + "unique_id": "model.generated_3.stg_billing_1", + "fqn": [ + "generated_3", + "models", + "billing", + "staging", + "stg_billing_1" + ], + "alias": "stg_billing_1", + "config": { + "enabled": true, + "alias": null, + "schema": "main", + "database": null, + "tags": [ + "staging" + ], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [ + "staging" + ], + "description": "Staged billing events 1 (Zürich – depot)", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.stg_billing_1", + "depends_on": { + "macros": [], + "nodes": [ + "source.generated_3.raw_events_2" + ] + } + }, + "model.generated_3.batched_watermark_stg_billing_1": { + "database": null, + "schema": "main", + "name": "batched_watermark_stg_billing_1", + "resource_type": "model", + "package_name": "generated_3", + "path": "models/features/batched_watermark_stg_billing_1.sql", + "original_file_path": "models/features/batched_watermark_stg_billing_1.sql", + "unique_id": "model.generated_3.batched_watermark_stg_billing_1", + "fqn": [ + "generated_3", + "models", + "features", + "batched_watermark_stg_billing_1" + ], + "alias": "batched_watermark_stg_billing_1", + "config": { + "enabled": true, + "alias": null, + "schema": "main", + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "incremental", + "incremental_strategy": "delete_insert", + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Microbatched watermark totals of stg_billing_1", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.batched_watermark_stg_billing_1", + "depends_on": { + "macros": [], + "nodes": [ + "model.generated_3.stg_billing_1" + ] + } + }, + "model.generated_3.hooked_stg_billing_1": { + "database": null, + "schema": "main", + "name": "hooked_stg_billing_1", + "resource_type": "model", + "package_name": "generated_3", + "path": "models/features/hooked_stg_billing_1.sql", + "original_file_path": "models/features/hooked_stg_billing_1.sql", + "unique_id": "model.generated_3.hooked_stg_billing_1", + "fqn": [ + "generated_3", + "models", + "features", + "hooked_stg_billing_1" + ], + "alias": "hooked_stg_billing_1", + "config": { + "enabled": true, + "alias": null, + "schema": "main", + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "table", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Hooked copy of stg_billing_1", + "columns": {}, + "meta": { + "sqlbuild": { + "lifecycle_hooks": { + "post_hooks": [ + { + "type": "python", + "name": "note_refresh", + "kwargs": { + "channel": "東京 warehouse" + }, + "relative_path": "models/features/_sqlbuild/_hooks/python/notify.py", + "definition_hash": "55440ff19f0c4f231ad57d453227a3c9d584b81234ee4b3338a39ccdf59814a8", + "version_hash": "03b4f786f8ab2736e5be2e4de5016bd16ea44d84beca000ec2a53a397e503eb6" + } + ] + } + } + }, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.hooked_stg_billing_1", + "depends_on": { + "macros": [], + "nodes": [ + "model.generated_3.stg_billing_1" + ] + } + }, + "model.generated_3.kept_stg_billing_1": { + "database": null, + "schema": "main", + "name": "kept_stg_billing_1", + "resource_type": "model", + "package_name": "generated_3", + "path": "models/features/kept_stg_billing_1.sql", + "original_file_path": "models/features/kept_stg_billing_1.sql", + "unique_id": "model.generated_3.kept_stg_billing_1", + "fqn": [ + "generated_3", + "models", + "features", + "kept_stg_billing_1" + ], + "alias": "kept_stg_billing_1", + "config": { + "enabled": true, + "alias": null, + "schema": "main", + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Statuses kept from stg_billing_1", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.kept_stg_billing_1", + "depends_on": { + "macros": [], + "nodes": [ + "model.generated_3.stg_billing_1" + ] + } + }, + "model.generated_3.audited_event_totals": { + "database": null, + "schema": "main", + "name": "audited_event_totals", + "resource_type": "model", + "package_name": "generated_3", + "path": "models/rendering/audited_event_totals.sql", + "original_file_path": "models/rendering/audited_event_totals.sql", + "unique_id": "model.generated_3.audited_event_totals", + "fqn": [ + "generated_3", + "models", + "rendering", + "audited_event_totals" + ], + "alias": "audited_event_totals", + "config": { + "enabled": true, + "alias": null, + "schema": "main", + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Totals of the audited events per channel", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.audited_event_totals", + "depends_on": { + "macros": [], + "nodes": [ + "source.generated_3.audited_events", + "seed.generated_3.channel_codes" + ] + } + }, + "model.generated_3.configured_stg_billing_1": { + "database": null, + "schema": "main", + "name": "configured_stg_billing_1", + "resource_type": "model", + "package_name": "generated_3", + "path": "models/rendering/configured_stg_billing_1.sql", + "original_file_path": "models/rendering/configured_stg_billing_1.sql", + "unique_id": "model.generated_3.configured_stg_billing_1", + "fqn": [ + "generated_3", + "models", + "rendering", + "configured_stg_billing_1" + ], + "alias": "configured_stg_billing_1", + "config": { + "enabled": true, + "alias": null, + "schema": "main", + "database": null, + "tags": [ + "development" + ], + "meta": {}, + "group": null, + "materialized": "table", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [ + "development" + ], + "description": "Configured copy of stg_billing_1 (réglé)", + "columns": { + "id": { + "name": "id", + "description": "Row key", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "amount": { + "name": "amount", + "description": "Montant réglé", + "meta": {}, + "data_type": "DOUBLE", + "constraints": [], + "quote": null, + "tags": [] + }, + "status": { + "name": "status", + "description": "", + "meta": {}, + "data_type": null, + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.configured_stg_billing_1", + "depends_on": { + "macros": [], + "nodes": [ + "model.generated_3.stg_billing_1" + ] + } + }, + "model.generated_3.interpolated_stg_billing_0": { + "database": null, + "schema": "main", + "name": "interpolated_stg_billing_0", + "resource_type": "model", + "package_name": "generated_3", + "path": "models/rendering/interpolated_stg_billing_0.sql", + "original_file_path": "models/rendering/interpolated_stg_billing_0.sql", + "unique_id": "model.generated_3.interpolated_stg_billing_0", + "fqn": [ + "generated_3", + "models", + "rendering", + "interpolated_stg_billing_0" + ], + "alias": "interpolated_stg_billing_0", + "config": { + "enabled": true, + "alias": null, + "schema": "main", + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "table", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [ + { + "sql": "SELECT 'main.interpolated_stg_billing_0' AS refreshed_relation, 'Ελληνικά orders' AS refresh_label", + "transaction": true, + "index": 0 + }, + { + "sql": "SELECT 'refreshed ' || 'main.interpolated_stg_billing_0' AS note", + "transaction": true, + "index": 1 + }, + { + "sql": "SELECT 'dev' AS target, 'interpolated_stg_billing_0' AS model", + "transaction": true, + "index": 2 + } + ], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Interpolated copy of stg_billing_0", + "columns": {}, + "meta": { + "sqlbuild": { + "lifecycle_hooks": { + "post_hooks": [ + { + "type": "sql", + "statement": "SELECT 'main.interpolated_stg_billing_0' AS refreshed_relation, 'Ελληνικά orders' AS refresh_label", + "name": "announce_refresh", + "relative_path": "models/rendering/_sqlbuild/_hooks/sql/announce_refresh.sql", + "definition_sql": "SELECT @'relation' AS refreshed_relation, @'label' AS refresh_label", + "kwargs": { + "relation": "@@CTX:destination.qualified", + "label": "Ελληνικά orders" + }, + "description": "Announce one refreshed relation" + }, + { + "type": "sql", + "statement": "SELECT 'refreshed ' || 'main.interpolated_stg_billing_0' AS note" + }, + { + "type": "sql", + "statement": "SELECT 'dev' AS target, 'interpolated_stg_billing_0' AS model" + } + ] + } + } + }, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.interpolated_stg_billing_0", + "depends_on": { + "macros": [], + "nodes": [ + "model.generated_3.stg_billing_0" + ] + } + }, + "model.generated_3.legacy_event_ids": { + "database": null, + "schema": "main", + "name": "legacy_event_ids", + "resource_type": "model", + "package_name": "generated_3", + "path": "models/rendering/legacy_event_ids.sql", + "original_file_path": "models/rendering/legacy_event_ids.sql", + "unique_id": "model.generated_3.legacy_event_ids", + "fqn": [ + "generated_3", + "models", + "rendering", + "legacy_event_ids" + ], + "alias": "legacy_event_ids", + "config": { + "enabled": true, + "alias": null, + "schema": "main", + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Ids read through a macro-generated reference", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.legacy_event_ids", + "depends_on": { + "macros": [], + "nodes": [ + "model.generated_3.stg_billing_1" + ] + } + }, + "model.generated_3.policy_stg_billing_1": { + "database": null, + "schema": "main", + "name": "policy_stg_billing_1", + "resource_type": "model", + "package_name": "generated_3", + "path": "models/rendering/policy_stg_billing_1.sql", + "original_file_path": "models/rendering/policy_stg_billing_1.sql", + "unique_id": "model.generated_3.policy_stg_billing_1", + "fqn": [ + "generated_3", + "models", + "rendering", + "policy_stg_billing_1" + ], + "alias": "policy_stg_billing_1", + "config": { + "enabled": true, + "alias": null, + "schema": "main", + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Policy-filtered copy of stg_billing_1", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.policy_stg_billing_1", + "depends_on": { + "macros": [], + "nodes": [ + "model.generated_3.stg_billing_1" + ] + } + }, + "model.generated_3.windowed_stg_billing_0": { + "database": null, + "schema": "main", + "name": "windowed_stg_billing_0", + "resource_type": "model", + "package_name": "generated_3", + "path": "models/rendering/windowed_stg_billing_0.sql", + "original_file_path": "models/rendering/windowed_stg_billing_0.sql", + "unique_id": "model.generated_3.windowed_stg_billing_0", + "fqn": [ + "generated_3", + "models", + "rendering", + "windowed_stg_billing_0" + ], + "alias": "windowed_stg_billing_0", + "config": { + "enabled": true, + "alias": null, + "schema": "main", + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "windowed_copy", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Runtime-windowed copy of stg_billing_0", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.windowed_stg_billing_0", + "depends_on": { + "macros": [], + "nodes": [ + "model.generated_3.stg_billing_0" + ] + } + }, + "seed.generated_3.channel_codes": { + "database": null, + "schema": "main", + "name": "channel_codes", + "resource_type": "seed", + "package_name": "generated_3", + "path": "seeds/channel_codes.csv", + "original_file_path": "seeds/channel_codes.csv", + "unique_id": "seed.generated_3.channel_codes", + "fqn": [ + "generated_3", + "seeds", + "channel_codes" + ], + "alias": "channel_codes", + "config": { + "enabled": true, + "alias": null, + "schema": "main", + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "seed", + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + }, + "delimiter": ",", + "quote_columns": null + }, + "tags": [], + "description": "Channel codes with seed audits.", + "columns": { + "id": { + "name": "id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "label": { + "name": "label", + "description": "", + "meta": {}, + "data_type": "VARCHAR", + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.channel_codes", + "root_path": null, + "depends_on": { + "macros": [] + } + } + }, + "sources": { + "source.generated_3.audited_events": { + "database": null, + "schema": "", + "name": "audited_events", + "resource_type": "source", + "package_name": "generated_3", + "path": "sources/audited.yml", + "original_file_path": "sources/audited.yml", + "unique_id": "source.generated_3.audited_events", + "fqn": [ + "generated_3", + "audited_events" + ], + "source_name": "audited_events", + "source_description": "", + "loader": "", + "identifier": "audited_events", + "quoting": {}, + "loaded_at_field": null, + "freshness": null, + "external": null, + "description": "Events with source audits.", + "columns": { + "id": { + "name": "id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "amount": { + "name": "amount", + "description": "", + "meta": {}, + "data_type": "DOUBLE", + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": {}, + "source_meta": {}, + "tags": [], + "config": {}, + "patch_path": null, + "unrendered_config": {}, + "relation_name": null + }, + "source.generated_3.configured_events": { + "database": null, + "schema": "", + "name": "configured_events", + "resource_type": "source", + "package_name": "generated_3", + "path": "sources/configured.yml", + "original_file_path": "sources/configured.yml", + "unique_id": "source.generated_3.configured_events", + "fqn": [ + "generated_3", + "configured_events" + ], + "source_name": "configured_events", + "source_description": "", + "loader": "", + "identifier": "configured_events", + "quoting": {}, + "loaded_at_field": null, + "freshness": null, + "external": null, + "description": "Configured feed events", + "columns": { + "id": { + "name": "id", + "description": "Key id", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "amount": { + "name": "amount", + "description": "", + "meta": {}, + "data_type": "DOUBLE", + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": { + "owner": "orders team" + }, + "source_meta": {}, + "tags": [], + "config": {}, + "patch_path": null, + "unrendered_config": {}, + "relation_name": null + }, + "source.generated_3.raw_events_0": { + "database": null, + "schema": "", + "name": "raw_events_0", + "resource_type": "source", + "package_name": "generated_3", + "path": "sources/raw.yml", + "original_file_path": "sources/raw.yml", + "unique_id": "source.generated_3.raw_events_0", + "fqn": [ + "generated_3", + "raw_events_0" + ], + "source_name": "raw_events_0", + "source_description": "", + "loader": "", + "identifier": "raw_events_0", + "quoting": {}, + "loaded_at_field": null, + "freshness": null, + "external": null, + "description": "Generated event feed 0.", + "columns": { + "id": { + "name": "id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "amount": { + "name": "amount", + "description": "", + "meta": {}, + "data_type": "DOUBLE", + "constraints": [], + "quote": null, + "tags": [] + }, + "status": { + "name": "status", + "description": "", + "meta": {}, + "data_type": "VARCHAR", + "constraints": [], + "quote": null, + "tags": [] + }, + "created_at": { + "name": "created_at", + "description": "", + "meta": {}, + "data_type": "TIMESTAMP", + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": {}, + "source_meta": {}, + "tags": [], + "config": {}, + "patch_path": null, + "unrendered_config": {}, + "relation_name": null + }, + "source.generated_3.raw_events_1": { + "database": null, + "schema": "", + "name": "raw_events_1", + "resource_type": "source", + "package_name": "generated_3", + "path": "sources/raw.yml", + "original_file_path": "sources/raw.yml", + "unique_id": "source.generated_3.raw_events_1", + "fqn": [ + "generated_3", + "raw_events_1" + ], + "source_name": "raw_events_1", + "source_description": "", + "loader": "", + "identifier": "raw_events_1", + "quoting": {}, + "loaded_at_field": null, + "freshness": null, + "external": null, + "description": "Generated event feed 1.", + "columns": { + "id": { + "name": "id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "amount": { + "name": "amount", + "description": "", + "meta": {}, + "data_type": "DOUBLE", + "constraints": [], + "quote": null, + "tags": [] + }, + "status": { + "name": "status", + "description": "", + "meta": {}, + "data_type": "VARCHAR", + "constraints": [], + "quote": null, + "tags": [] + }, + "created_at": { + "name": "created_at", + "description": "", + "meta": {}, + "data_type": "TIMESTAMP", + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": {}, + "source_meta": {}, + "tags": [], + "config": {}, + "patch_path": null, + "unrendered_config": {}, + "relation_name": null + }, + "source.generated_3.raw_events_2": { + "database": null, + "schema": "", + "name": "raw_events_2", + "resource_type": "source", + "package_name": "generated_3", + "path": "sources/raw.yml", + "original_file_path": "sources/raw.yml", + "unique_id": "source.generated_3.raw_events_2", + "fqn": [ + "generated_3", + "raw_events_2" + ], + "source_name": "raw_events_2", + "source_description": "", + "loader": "", + "identifier": "raw_events_2", + "quoting": {}, + "loaded_at_field": null, + "freshness": null, + "external": null, + "description": "Generated event feed 2.", + "columns": { + "id": { + "name": "id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "amount": { + "name": "amount", + "description": "", + "meta": {}, + "data_type": "DOUBLE", + "constraints": [], + "quote": null, + "tags": [] + }, + "status": { + "name": "status", + "description": "", + "meta": {}, + "data_type": "VARCHAR", + "constraints": [], + "quote": null, + "tags": [] + }, + "created_at": { + "name": "created_at", + "description": "", + "meta": {}, + "data_type": "TIMESTAMP", + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": {}, + "source_meta": {}, + "tags": [], + "config": {}, + "patch_path": null, + "unrendered_config": {}, + "relation_name": null + } + }, + "macros": { + "macro.generated_3.analysis_region": { + "name": "analysis_region", + "resource_type": "macro", + "package_name": "generated_3", + "path": "models/analysis/_sqlbuild/_macros/regions.py", + "original_file_path": "models/analysis/_sqlbuild/_macros/regions.py", + "unique_id": "macro.generated_3.analysis_region", + "macro_sql": "def analysis_region() -> str:\n \"\"\"Return the region rows are assigned to.\"\"\"\n return \"'east'\"\n", + "depends_on": { + "macros": [] + }, + "description": "", + "meta": { + "sqlbuild_visibility": "local", + "sqlbuild_scope_path": "models/analysis" + }, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "arguments": [], + "supported_languages": null + }, + "macro.generated_3.adjust_0": { + "name": "adjust_0", + "resource_type": "macro", + "package_name": "generated_3", + "path": "models/billing/marts/_sqlbuild/_macros/adjust_0.py", + "original_file_path": "models/billing/marts/_sqlbuild/_macros/adjust_0.py", + "unique_id": "macro.generated_3.adjust_0", + "macro_sql": "def adjust_0(expression: str) -> str:\n \"\"\"Offset an amount.\"\"\"\n return f\"({expression} + 0 + 1)\"\n", + "depends_on": { + "macros": [] + }, + "description": "", + "meta": { + "sqlbuild_visibility": "local", + "sqlbuild_scope_path": "models/billing/marts" + }, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "arguments": [], + "supported_languages": null + }, + "macro.generated_3.adjust_1": { + "name": "adjust_1", + "resource_type": "macro", + "package_name": "generated_3", + "path": "models/billing/staging/_sqlbuild/_macros/adjust_1.py", + "original_file_path": "models/billing/staging/_sqlbuild/_macros/adjust_1.py", + "unique_id": "macro.generated_3.adjust_1", + "macro_sql": "def adjust_1(ctx, expression: str) -> str:\n \"\"\"Scale an amount by the configured discount.\"\"\"\n rate = ctx.vars[\"discount_rate\"]\n return f\"({expression} * (1 - {rate}))\"\n", + "depends_on": { + "macros": [] + }, + "description": "", + "meta": { + "sqlbuild_visibility": "local", + "sqlbuild_scope_path": "models/billing/staging" + }, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "arguments": [], + "supported_languages": null + }, + "macro.generated_3.legacy_events": { + "name": "legacy_events", + "resource_type": "macro", + "package_name": "generated_3", + "path": "models/rendering/_sqlbuild/_macros/legacy.py", + "original_file_path": "models/rendering/_sqlbuild/_macros/legacy.py", + "unique_id": "macro.generated_3.legacy_events", + "macro_sql": "def legacy_events() -> str:\n \"\"\"Return a staged relation the way a migrated project wrote it.\"\"\"\n return '__ref(\"stg_billing_1\")'\n", + "depends_on": { + "macros": [] + }, + "description": "", + "meta": { + "sqlbuild_visibility": "local", + "sqlbuild_scope_path": "models/rendering" + }, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "arguments": [], + "supported_languages": null + }, + "macro.generated_3.refresh_note": { + "name": "refresh_note", + "resource_type": "macro", + "package_name": "generated_3", + "path": "models/rendering/_sqlbuild/_macros/notes.py", + "original_file_path": "models/rendering/_sqlbuild/_macros/notes.py", + "unique_id": "macro.generated_3.refresh_note", + "macro_sql": "def refresh_note(relation: str) -> str:\n \"\"\"Describe one refreshed relation.\"\"\"\n return f\"'refreshed ' || '{relation}'\"\n", + "depends_on": { + "macros": [] + }, + "description": "", + "meta": { + "sqlbuild_visibility": "local", + "sqlbuild_scope_path": "models/rendering" + }, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "arguments": [], + "supported_languages": null + }, + "macro.generated_3.channel_label": { + "name": "channel_label", + "resource_type": "macro", + "package_name": "generated_3", + "path": "models/rendering/_sqlbuild/_macros/policy.py", + "original_file_path": "models/rendering/_sqlbuild/_macros/policy.py", + "unique_id": "macro.generated_3.channel_label", + "macro_sql": "import os\n\n\ndef minimum_amount_filter(ctx, column: str) -> str:\n \"\"\"Keep rows at or above the configured minimum amount.\"\"\"\n if ctx.constants[\"minimum_render_amount\"] is None:\n return \"TRUE\"\n return f\"{column} >= {ctx.render_constant('minimum_render_amount')}\"\n\n\ndef channel_label(ctx) -> str:\n \"\"\"Label rows with the web channel, target and adapter.\"\"\"\n channel = ctx.render_enum_member(enum_name=\"render_channel\", member_name=\"WEB\")\n region = ctx.vars[\"region\"]\n return f\"{channel} || '/{ctx.target_name}/{ctx.adapter_name}/{region}'\"\n\n\ndef with_deployment(expression: str) -> str:\n \"\"\"Suffix an expression with the deployment channel from the environment.\"\"\"\n channel = os.environ.get(\"SQB_DIFFERENTIAL_CHANNEL\", \"none\")\n return f\"({expression} || '@{channel}')\"\n", + "depends_on": { + "macros": [] + }, + "description": "", + "meta": { + "sqlbuild_visibility": "local", + "sqlbuild_scope_path": "models/rendering" + }, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "arguments": [], + "supported_languages": null + }, + "macro.generated_3.minimum_amount_filter": { + "name": "minimum_amount_filter", + "resource_type": "macro", + "package_name": "generated_3", + "path": "models/rendering/_sqlbuild/_macros/policy.py", + "original_file_path": "models/rendering/_sqlbuild/_macros/policy.py", + "unique_id": "macro.generated_3.minimum_amount_filter", + "macro_sql": "import os\n\n\ndef minimum_amount_filter(ctx, column: str) -> str:\n \"\"\"Keep rows at or above the configured minimum amount.\"\"\"\n if ctx.constants[\"minimum_render_amount\"] is None:\n return \"TRUE\"\n return f\"{column} >= {ctx.render_constant('minimum_render_amount')}\"\n\n\ndef channel_label(ctx) -> str:\n \"\"\"Label rows with the web channel, target and adapter.\"\"\"\n channel = ctx.render_enum_member(enum_name=\"render_channel\", member_name=\"WEB\")\n region = ctx.vars[\"region\"]\n return f\"{channel} || '/{ctx.target_name}/{ctx.adapter_name}/{region}'\"\n\n\ndef with_deployment(expression: str) -> str:\n \"\"\"Suffix an expression with the deployment channel from the environment.\"\"\"\n channel = os.environ.get(\"SQB_DIFFERENTIAL_CHANNEL\", \"none\")\n return f\"({expression} || '@{channel}')\"\n", + "depends_on": { + "macros": [] + }, + "description": "", + "meta": { + "sqlbuild_visibility": "local", + "sqlbuild_scope_path": "models/rendering" + }, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "arguments": [], + "supported_languages": null + }, + "macro.generated_3.with_deployment": { + "name": "with_deployment", + "resource_type": "macro", + "package_name": "generated_3", + "path": "models/rendering/_sqlbuild/_macros/policy.py", + "original_file_path": "models/rendering/_sqlbuild/_macros/policy.py", + "unique_id": "macro.generated_3.with_deployment", + "macro_sql": "import os\n\n\ndef minimum_amount_filter(ctx, column: str) -> str:\n \"\"\"Keep rows at or above the configured minimum amount.\"\"\"\n if ctx.constants[\"minimum_render_amount\"] is None:\n return \"TRUE\"\n return f\"{column} >= {ctx.render_constant('minimum_render_amount')}\"\n\n\ndef channel_label(ctx) -> str:\n \"\"\"Label rows with the web channel, target and adapter.\"\"\"\n channel = ctx.render_enum_member(enum_name=\"render_channel\", member_name=\"WEB\")\n region = ctx.vars[\"region\"]\n return f\"{channel} || '/{ctx.target_name}/{ctx.adapter_name}/{region}'\"\n\n\ndef with_deployment(expression: str) -> str:\n \"\"\"Suffix an expression with the deployment channel from the environment.\"\"\"\n channel = os.environ.get(\"SQB_DIFFERENTIAL_CHANNEL\", \"none\")\n return f\"({expression} || '@{channel}')\"\n", + "depends_on": { + "macros": [] + }, + "description": "", + "meta": { + "sqlbuild_visibility": "local", + "sqlbuild_scope_path": "models/rendering" + }, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "arguments": [], + "supported_languages": null + }, + "macro.generated_3.doubled": { + "name": "doubled", + "resource_type": "macro", + "package_name": "generated_3", + "path": "tests/unit/_macros/scaling.py", + "original_file_path": "tests/unit/_macros/scaling.py", + "unique_id": "macro.generated_3.doubled", + "macro_sql": "def doubled(expression: str) -> str:\n \"\"\"Double an amount.\"\"\"\n return f\"({expression} * 2)\"\n", + "depends_on": { + "macros": [] + }, + "description": "", + "meta": { + "sqlbuild_visibility": "local", + "sqlbuild_scope_path": "tests/unit" + }, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "arguments": [], + "supported_languages": null + }, + "macro.generated_3__sqlbuild_hooks.record_refresh": { + "name": "record_refresh", + "resource_type": "macro", + "package_name": "generated_3__sqlbuild_hooks", + "path": "models/billing/marts/_sqlbuild/_hooks/sql/record_refresh.sql", + "original_file_path": "models/billing/marts/_sqlbuild/_hooks/sql/record_refresh.sql", + "unique_id": "macro.generated_3__sqlbuild_hooks.record_refresh", + "macro_sql": "SELECT @'relation' AS refreshed_relation, @'label' AS refresh_label", + "depends_on": { + "macros": [] + }, + "description": "Record a refresh for one relation", + "meta": { + "sqlbuild_resource_type": "sql_hook", + "sqlbuild_hook_name": "record_refresh" + }, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "arguments": [], + "supported_languages": null + }, + "macro.generated_3__sqlbuild_hooks.announce_refresh": { + "name": "announce_refresh", + "resource_type": "macro", + "package_name": "generated_3__sqlbuild_hooks", + "path": "models/rendering/_sqlbuild/_hooks/sql/announce_refresh.sql", + "original_file_path": "models/rendering/_sqlbuild/_hooks/sql/announce_refresh.sql", + "unique_id": "macro.generated_3__sqlbuild_hooks.announce_refresh", + "macro_sql": "SELECT @'relation' AS refreshed_relation, @'label' AS refresh_label", + "depends_on": { + "macros": [] + }, + "description": "Announce one refreshed relation", + "meta": { + "sqlbuild_resource_type": "sql_hook", + "sqlbuild_hook_name": "announce_refresh" + }, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "arguments": [], + "supported_languages": null + } + }, + "exposures": {}, + "metrics": {}, + "groups": {}, + "selectors": {}, + "disabled": {}, + "parent_map": { + "model.generated_3.banded_amounts": [ + "model.generated_3.table_fn__amount_bands" + ], + "model.generated_3.checked_stg_billing_1": [ + "model.generated_3.stg_billing_1" + ], + "model.generated_3.regional_stg_billing_1": [ + "model.generated_3.stg_billing_1" + ], + "model.generated_3.scaled_stg_billing_1": [ + "model.generated_3.scaled_amount", + "model.generated_3.stg_billing_1" + ], + "model.generated_3.billing_summary_0": [ + "model.generated_3.stg_billing_1", + "model.generated_3.stg_billing_0" + ], + "model.generated_3.billing_summary_1": [ + "model.generated_3.stg_billing_1" + ], + "model.generated_3.stg_billing_0": [ + "source.generated_3.raw_events_2" + ], + "model.generated_3.stg_billing_1": [ + "source.generated_3.raw_events_2" + ], + "model.generated_3.batched_watermark_stg_billing_1": [ + "model.generated_3.stg_billing_1" + ], + "model.generated_3.hooked_stg_billing_1": [ + "model.generated_3.stg_billing_1" + ], + "model.generated_3.kept_stg_billing_1": [ + "model.generated_3.stg_billing_1" + ], + "model.generated_3.audited_event_totals": [ + "source.generated_3.audited_events", + "seed.generated_3.channel_codes" + ], + "model.generated_3.configured_stg_billing_1": [ + "model.generated_3.stg_billing_1" + ], + "model.generated_3.interpolated_stg_billing_0": [ + "model.generated_3.stg_billing_0" + ], + "model.generated_3.legacy_event_ids": [ + "model.generated_3.stg_billing_1" + ], + "model.generated_3.policy_stg_billing_1": [ + "model.generated_3.stg_billing_1" + ], + "model.generated_3.windowed_stg_billing_0": [ + "model.generated_3.stg_billing_0" + ], + "source.generated_3.audited_events": [], + "source.generated_3.configured_events": [], + "source.generated_3.raw_events_0": [], + "source.generated_3.raw_events_1": [], + "source.generated_3.raw_events_2": [], + "seed.generated_3.channel_codes": [], + "model.generated_3.configured_label": [], + "model.generated_3.scaled_amount": [], + "model.generated_3.table_fn__amount_bands": [ + "model.generated_3.stg_billing_1" + ] + }, + "group_map": {}, + "docs": {}, + "saved_queries": {}, + "semantic_models": {}, + "unit_tests": {} + } +} diff --git a/tests/goldens/compiler/seed/4.json b/tests/goldens/compiler/seed/4.json new file mode 100644 index 0000000000..c97add18f8 --- /dev/null +++ b/tests/goldens/compiler/seed/4.json @@ -0,0 +1,3131 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 0, + "diagnostics": [] + }, + { + "label": "compile-store-warm", + "exit_code": 0, + "diagnostics": [] + }, + { + "label": "plan", + "exit_code": 0, + "diagnostics": [] + } + ], + "compiled": { + "audits/generic/configured_events/accepted_values__id.sql": [ + "SELECT id", + "FROM __source(\"configured_events\")", + "WHERE id IS NOT NULL", + " AND id NOT IN ('1')", + "" + ], + "audits/generic/configured_stg_support_0/accepted_values__status.sql": [ + "SELECT status", + "FROM __ref(\"configured_stg_support_0\")", + "WHERE status IS NOT NULL", + " AND status NOT IN ('placed', 'shipped')", + "" + ], + "audits/generic/configured_stg_support_0/configured_amount_floor.sql": [ + "SELECT *", + "FROM __ref(\"configured_stg_support_0\")", + "WHERE amount < -30", + "" + ], + "audits/generic/configured_stg_support_0/configured_id_present__id.sql": [ + "SELECT id", + "FROM __ref(\"configured_stg_support_0\")", + "WHERE id IS NULL", + "" + ], + "audits/generic/configured_stg_support_0/unique__id.sql": [ + "SELECT id, COUNT(*) AS duplicate_count", + "FROM __ref(\"configured_stg_support_0\")", + "WHERE id IS NOT NULL", + "GROUP BY id", + "HAVING COUNT(*) > 1", + "" + ], + "audits/generic/guarded_stg_support_0/amount_at_least__amount.sql": [ + "SELECT *", + "FROM __ref(\"guarded_stg_support_0\")", + "WHERE amount < 0", + "" + ], + "audits/generic/shaped_stg_support_0/not_null__id.sql": [ + "SELECT id", + "FROM __ref(\"shaped_stg_support_0\")", + "WHERE id IS NULL", + "" + ], + "audits/generic/stg_support_0/not_null__id.sql": [ + "SELECT id", + "FROM __ref(\"stg_support_0\")", + "WHERE id IS NULL", + "" + ], + "audits/generic/support_summary_0/not_null__id.sql": [ + "SELECT id", + "FROM __ref(\"support_summary_0\")", + "WHERE id IS NULL", + "" + ], + "audits/singular/support_summary_1_matches.sql": [ + "SELECT a.id", + "FROM __ref(\"support_summary_1\") a", + "LEFT JOIN __ref(\"support_summary_0\") b ON a.id = b.id", + "WHERE b.id IS NULL", + "" + ], + "functions/sql/configured_label.sql": [ + "CREATE OR REPLACE MACRO ci_checks.configured_label(raw_status) AS (", + "UPPER(raw_status)", + ")", + "" + ], + "models/analysis/combined_stg_support_0.sql": [ + "WITH recent AS (", + " SELECT id, created_at FROM __ref(\"stg_support_0\") WHERE amount > 0", + "),", + "older AS (", + " SELECT id, created_at FROM __ref(\"stg_support_0\")", + ")", + "SELECT id, created_at FROM recent", + "UNION ALL", + "SELECT id, created_at FROM older", + "" + ], + "models/analysis/every_open_event.sql": [ + "SELECT *", + "FROM __source(\"open_events\")", + "" + ], + "models/analysis/every_raw_events_0.sql": [ + "SELECT *", + "FROM __source(\"raw_events_0\")", + "" + ], + "models/analysis/every_stg_support_0.sql": [ + "SELECT *", + "FROM __ref(\"stg_support_0\")", + "" + ], + "models/analysis/passed_stg_support_0.sql": [ + "WITH passthrough AS (", + " SELECT id, created_at FROM __ref(\"stg_support_0\")", + ")", + "SELECT id, created_at", + "FROM passthrough", + "" + ], + "models/analysis/pivot_input_stg_support_0.sql": [ + "SELECT CAST(id AS INTEGER) AS id, CAST(status AS VARCHAR) AS status, amount", + "FROM __ref(\"stg_support_0\")", + "" + ], + "models/analysis/pivoted_stg_support_0.sql": [ + "PIVOT __ref(\"pivot_input_stg_support_0\")", + "ON status", + "USING MAX(amount)", + "GROUP BY id", + "" + ], + "models/analysis/quoted_stg_support_0.sql": [ + "SELECT \"ID\" AS \"OrderKey\", \"Amount\", \"Status\" AS \"Status\"", + "FROM __ref(\"stg_support_0\")", + "" + ], + "models/analysis/regional_stg_support_0.sql": [ + "SELECT id, amount, 'north' AS region", + "FROM __ref(\"stg_support_0\")", + "" + ], + "models/analysis/typed_stg_support_0.sql": [ + "SELECT id, amount * 2 AS doubled_amount, CAST(status AS VARCHAR) AS status_text,", + " created_at, NULL AS missing_note", + "FROM __ref(\"stg_support_0\")", + "" + ], + "models/analysis/unanalysed_stg_support_0.sql": [ + "SELECT id, amount", + "FROM __ref(\"stg_support_0\")", + "WHERE id = ANY [1, 2]", + "" + ], + "models/features/append_stg_support_0.sql": [ + "SELECT id, amount, status, created_at", + "FROM __ref(\"stg_support_0\")", + "" + ], + "models/features/batched_rolling_window_stg_support_0.sql": [ + "SELECT CAST(created_at AS DATE) AS created_at, SUM(amount) AS amount", + "FROM __ref(\"stg_support_0\")", + "GROUP BY CAST(created_at AS DATE)", + "" + ], + "models/features/guarded_stg_support_0.sql": [ + "SELECT id, amount", + "FROM __ref(\"stg_support_0\")", + "" + ], + "models/features/product_labels.sql": [ + "SELECT id, label", + "FROM __source(\"loaded_products\")", + "" + ], + "models/features/shaped_stg_support_0.sql": [ + "SELECT CAST(id AS INTEGER) AS id, status", + "FROM __ref(\"stg_support_0\")", + "" + ], + "models/features/timestamp_history_stg_support_0.sql": [ + "SELECT id, status, created_at", + "FROM __ref(\"stg_support_0\")", + "" + ], + "models/rendering/bounded_stg_support_0.sql": [ + "SELECT id, amount, created_at", + "FROM __ref(\"stg_support_0\")", + "WHERE created_at >= __cursor_start()", + " AND created_at < __cursor_end()", + "" + ], + "models/rendering/configured_stg_support_0.sql": [ + "SELECT id, amount, status", + "FROM __ref(\"stg_support_0\")", + "" + ], + "models/rendering/latest_raw_events_0.sql": [ + "SELECT id, amount", + "FROM (SELECT id, amount FROM __source(\"raw_events_0\") ORDER BY id DESC LIMIT 2) AS latest", + "" + ], + "models/rendering/policy_stg_support_0.sql": [ + "SELECT id, amount, ('web' || '/ci/generated_duckdb/west' || '@web-orders') AS channel,", + " 1 AS minimum_amount,", + " 'store' AS fallback_channel", + "FROM __ref(\"stg_support_0\")", + "WHERE amount >= 1", + "" + ], + "models/rendering/unioned_event_ids.sql": [ + "SELECT id", + "FROM (SELECT id FROM __ref(\"stg_support_0\") UNION ALL SELECT id FROM __ref(\"stg_support_0\")) AS unioned", + "" + ], + "models/support/marts/support_summary_0.sql": [ + "SELECT", + " t.id,", + " t.amount,", + " t.status,", + " t.created_at,", + " ROUND(ROUND((t.amount * 2), 2), 2) AS adjust_1_amount,", + " j1.label AS lookup_label_1", + "FROM __ref(\"stg_support_0\") t", + "LEFT JOIN __seed(\"lookup_1\") j1 ON t.id = j1.id", + "" + ], + "models/support/marts/support_summary_1.sql": [ + "SELECT", + " t.id,", + " t.amount,", + " t.status,", + " t.created_at,", + " t.amount * 2.4700 AS threshold_0_amount,", + " ROUND(ROUND((t.amount * 2), 2), 2) AS adjust_0_amount,", + " ROUND(ROUND((t.amount * 2), 2), 2) AS adjust_1_amount,", + " j1.label AS lookup_label_1,", + " '東京 warehouse' AS site_label", + "FROM __ref(\"stg_support_0\") t", + "LEFT JOIN __seed(\"lookup_0\") j1 ON t.id = j1.id", + "WHERE t.status <> 'returned ✓'", + "" + ], + "models/support/marts/support_summary_2.sql": [ + "SELECT", + " t.id,", + " t.amount,", + " t.status,", + " t.created_at,", + " ROUND(ROUND((t.amount * 2), 2), 2) AS adjust_1_amount,", + " ROUND(ROUND((t.amount * 2), 2), 2) AS adjust_2_amount,", + " j1.label AS lookup_label_1,", + " '東京 warehouse' AS site_label", + "FROM __ref(\"stg_support_0\") t", + "LEFT JOIN __seed(\"lookup_1\") j1 ON t.id = j1.id", + "" + ], + "models/support/staging/stg_support_0.sql": [ + "SELECT", + " t.id,", + " t.amount,", + " t.status,", + " t.created_at,", + " t.amount * 2.4700 AS threshold_0_amount,", + " ROUND(ROUND((t.amount * 2), 2), 2) AS adjust_2_amount", + "FROM __source(\"raw_events_0\") t", + "" + ], + "tests/regional_stg_support_0/test_regional_stg_support_0.sql": [ + "WITH __ref__stg_support_0 AS (SELECT 1 AS id, CAST(4 AS DOUBLE) AS amount, 'placed' AS status,", + " TIMESTAMP '2026-01-01 00:00:00' AS created_at),", + "__ref__regional_stg_support_0 AS (SELECT id, amount, 'west' AS region", + "FROM __ref__stg_support_0),", + "__actual__regional_stg_support_0 AS (SELECT * FROM __ref__regional_stg_support_0),", + "__expected__regional_stg_support_0 AS (SELECT 1 AS id, CAST(4 AS DOUBLE) AS amount, 'west' AS region)", + "SELECT 0 AS step_index, 'regional_stg_support_0' AS model_name, (SELECT COUNT(*) FROM __actual__regional_stg_support_0) AS actual_count, (SELECT COUNT(*) FROM __expected__regional_stg_support_0) AS expected_count, (SELECT COUNT(*) FROM (SELECT id, amount, region FROM __actual__regional_stg_support_0 EXCEPT SELECT id, amount, region FROM __expected__regional_stg_support_0) AS __sqlbuild_mismatch) AS unexpected_count, (SELECT COUNT(*) FROM (SELECT id, amount, region FROM __expected__regional_stg_support_0 EXCEPT SELECT id, amount, region FROM __actual__regional_stg_support_0) AS __sqlbuild_missing) AS missing_count", + "" + ], + "tests/stg_support_0/test_stg_support_0.sql": [ + "WITH __source__raw_events_0 AS (SELECT 1 AS id, CAST(10 AS DOUBLE) AS amount, 'placed' AS status,", + " TIMESTAMP '2026-01-01 00:00:00' AS created_at),", + "__ref__stg_support_0 AS (SELECT", + " t.id,", + " t.amount,", + " t.status,", + " t.created_at,", + " t.amount * 2.4700 AS threshold_0_amount,", + " ROUND(ROUND((t.amount * 2), 2), 2) AS adjust_2_amount", + "FROM __source__raw_events_0 t),", + "__actual__stg_support_0 AS (SELECT * FROM __ref__stg_support_0),", + "__expected__stg_support_0 AS (SELECT 1 AS id, 'placed' AS status)", + "SELECT 0 AS step_index, 'stg_support_0' AS model_name, (SELECT COUNT(*) FROM __actual__stg_support_0) AS actual_count, (SELECT COUNT(*) FROM __expected__stg_support_0) AS expected_count, (SELECT COUNT(*) FROM (SELECT id, status FROM __actual__stg_support_0 EXCEPT SELECT id, status FROM __expected__stg_support_0) AS __sqlbuild_mismatch) AS unexpected_count, (SELECT COUNT(*) FROM (SELECT id, status FROM __expected__stg_support_0 EXCEPT SELECT id, status FROM __actual__stg_support_0) AS __sqlbuild_missing) AS missing_count", + "" + ], + "tests/tests/unit/test_stg_support_0_cases/block_1__labelled.sql": [ + "WITH __source__raw_events_0 AS (SELECT 1 AS id, CAST(2.25 AS DOUBLE) AS amount,", + " '東京 warehouse' AS status,", + " TIMESTAMP '2026-01-01 00:00:00' AS created_at),", + "__ref__stg_support_0 AS (SELECT", + " t.id,", + " t.amount,", + " t.status,", + " t.created_at,", + " t.amount * 2.4700 AS threshold_0_amount,", + " ROUND(ROUND((t.amount * 2), 2), 2) AS adjust_2_amount", + "FROM __source__raw_events_0 t),", + "__actual__stg_support_0 AS (SELECT * FROM __ref__stg_support_0),", + "__expected__stg_support_0 AS (SELECT 1 AS id)", + "SELECT 0 AS step_index, 'stg_support_0' AS model_name, (SELECT COUNT(*) FROM __actual__stg_support_0) AS actual_count, (SELECT COUNT(*) FROM __expected__stg_support_0) AS expected_count, (SELECT COUNT(*) FROM (SELECT id FROM __actual__stg_support_0 EXCEPT SELECT id FROM __expected__stg_support_0) AS __sqlbuild_mismatch) AS unexpected_count, (SELECT COUNT(*) FROM (SELECT id FROM __expected__stg_support_0 EXCEPT SELECT id FROM __actual__stg_support_0) AS __sqlbuild_missing) AS missing_count", + "" + ], + "tests/tests/unit/test_stg_support_0_cases/block_1__placed.sql": [ + "WITH __source__raw_events_0 AS (SELECT 1 AS id, CAST(1.5 AS DOUBLE) AS amount,", + " 'placed' AS status,", + " TIMESTAMP '2026-01-01 00:00:00' AS created_at),", + "__ref__stg_support_0 AS (SELECT", + " t.id,", + " t.amount,", + " t.status,", + " t.created_at,", + " t.amount * 2.4700 AS threshold_0_amount,", + " ROUND(ROUND((t.amount * 2), 2), 2) AS adjust_2_amount", + "FROM __source__raw_events_0 t),", + "__actual__stg_support_0 AS (SELECT * FROM __ref__stg_support_0),", + "__expected__stg_support_0 AS (SELECT 1 AS id)", + "SELECT 0 AS step_index, 'stg_support_0' AS model_name, (SELECT COUNT(*) FROM __actual__stg_support_0) AS actual_count, (SELECT COUNT(*) FROM __expected__stg_support_0) AS expected_count, (SELECT COUNT(*) FROM (SELECT id FROM __actual__stg_support_0 EXCEPT SELECT id FROM __expected__stg_support_0) AS __sqlbuild_mismatch) AS unexpected_count, (SELECT COUNT(*) FROM (SELECT id FROM __expected__stg_support_0 EXCEPT SELECT id FROM __actual__stg_support_0) AS __sqlbuild_missing) AS missing_count", + "" + ] + }, + "manifest": { + "nodes": { + "model.generated_4.combined_stg_support_0": { + "database": null, + "schema": "ci_checks", + "name": "combined_stg_support_0", + "resource_type": "model", + "package_name": "generated_4", + "path": "models/analysis/combined_stg_support_0.sql", + "original_file_path": "models/analysis/combined_stg_support_0.sql", + "unique_id": "model.generated_4.combined_stg_support_0", + "fqn": [ + "generated_4", + "models", + "analysis", + "combined_stg_support_0" + ], + "alias": "combined_stg_support_0", + "config": { + "enabled": true, + "alias": null, + "schema": "ci_checks", + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "table", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Recent and older rows of stg_support_0 and stg_support_0", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "ci_checks.combined_stg_support_0", + "depends_on": { + "macros": [], + "nodes": [ + "model.generated_4.stg_support_0" + ] + } + }, + "model.generated_4.every_open_event": { + "database": null, + "schema": "ci_checks", + "name": "every_open_event", + "resource_type": "model", + "package_name": "generated_4", + "path": "models/analysis/every_open_event.sql", + "original_file_path": "models/analysis/every_open_event.sql", + "unique_id": "model.generated_4.every_open_event", + "fqn": [ + "generated_4", + "models", + "analysis", + "every_open_event" + ], + "alias": "every_open_event", + "config": { + "enabled": true, + "alias": null, + "schema": "ci_checks", + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "table", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Every column of an open source", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "ci_checks.every_open_event", + "depends_on": { + "macros": [], + "nodes": [ + "source.generated_4.open_events" + ] + } + }, + "model.generated_4.every_raw_events_0": { + "database": null, + "schema": "ci_checks", + "name": "every_raw_events_0", + "resource_type": "model", + "package_name": "generated_4", + "path": "models/analysis/every_raw_events_0.sql", + "original_file_path": "models/analysis/every_raw_events_0.sql", + "unique_id": "model.generated_4.every_raw_events_0", + "fqn": [ + "generated_4", + "models", + "analysis", + "every_raw_events_0" + ], + "alias": "every_raw_events_0", + "config": { + "enabled": true, + "alias": null, + "schema": "ci_checks", + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "table", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Every column of raw_events_0", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "ci_checks.every_raw_events_0", + "depends_on": { + "macros": [], + "nodes": [ + "source.generated_4.raw_events_0" + ] + } + }, + "model.generated_4.every_stg_support_0": { + "database": null, + "schema": "ci_checks", + "name": "every_stg_support_0", + "resource_type": "model", + "package_name": "generated_4", + "path": "models/analysis/every_stg_support_0.sql", + "original_file_path": "models/analysis/every_stg_support_0.sql", + "unique_id": "model.generated_4.every_stg_support_0", + "fqn": [ + "generated_4", + "models", + "analysis", + "every_stg_support_0" + ], + "alias": "every_stg_support_0", + "config": { + "enabled": true, + "alias": null, + "schema": "ci_checks", + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "table", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Every column of stg_support_0", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "ci_checks.every_stg_support_0", + "depends_on": { + "macros": [], + "nodes": [ + "model.generated_4.stg_support_0" + ] + } + }, + "model.generated_4.passed_stg_support_0": { + "database": null, + "schema": "ci_checks", + "name": "passed_stg_support_0", + "resource_type": "model", + "package_name": "generated_4", + "path": "models/analysis/passed_stg_support_0.sql", + "original_file_path": "models/analysis/passed_stg_support_0.sql", + "unique_id": "model.generated_4.passed_stg_support_0", + "fqn": [ + "generated_4", + "models", + "analysis", + "passed_stg_support_0" + ], + "alias": "passed_stg_support_0", + "config": { + "enabled": true, + "alias": null, + "schema": "ci_checks", + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "table", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Columns of stg_support_0 passed through a CTE", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "ci_checks.passed_stg_support_0", + "depends_on": { + "macros": [], + "nodes": [ + "model.generated_4.stg_support_0" + ] + } + }, + "model.generated_4.pivot_input_stg_support_0": { + "database": null, + "schema": "ci_checks", + "name": "pivot_input_stg_support_0", + "resource_type": "model", + "package_name": "generated_4", + "path": "models/analysis/pivot_input_stg_support_0.sql", + "original_file_path": "models/analysis/pivot_input_stg_support_0.sql", + "unique_id": "model.generated_4.pivot_input_stg_support_0", + "fqn": [ + "generated_4", + "models", + "analysis", + "pivot_input_stg_support_0" + ], + "alias": "pivot_input_stg_support_0", + "config": { + "enabled": true, + "alias": null, + "schema": "ci_checks", + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "table", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Contracted pivot input from stg_support_0", + "columns": { + "id": { + "name": "id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "status": { + "name": "status", + "description": "", + "meta": {}, + "data_type": "VARCHAR", + "constraints": [], + "quote": null, + "tags": [] + }, + "amount": { + "name": "amount", + "description": "", + "meta": {}, + "data_type": "DOUBLE", + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "ci_checks.pivot_input_stg_support_0", + "depends_on": { + "macros": [], + "nodes": [ + "model.generated_4.stg_support_0" + ] + } + }, + "model.generated_4.pivoted_stg_support_0": { + "database": null, + "schema": "ci_checks", + "name": "pivoted_stg_support_0", + "resource_type": "model", + "package_name": "generated_4", + "path": "models/analysis/pivoted_stg_support_0.sql", + "original_file_path": "models/analysis/pivoted_stg_support_0.sql", + "unique_id": "model.generated_4.pivoted_stg_support_0", + "fqn": [ + "generated_4", + "models", + "analysis", + "pivoted_stg_support_0" + ], + "alias": "pivoted_stg_support_0", + "config": { + "enabled": true, + "alias": null, + "schema": "ci_checks", + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "table", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Amounts of stg_support_0 pivoted by status", + "columns": { + "id": { + "name": "id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": { + "sqlbuild": { + "dynamic_columns": [ + { + "name": "status_amounts", + "pivot_column": "status", + "value_column": "amount", + "aggregate": "MAX", + "type": "DOUBLE", + "name_pattern": null + } + ] + } + }, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "ci_checks.pivoted_stg_support_0", + "depends_on": { + "macros": [], + "nodes": [ + "model.generated_4.pivot_input_stg_support_0" + ] + } + }, + "model.generated_4.quoted_stg_support_0": { + "database": null, + "schema": "ci_checks", + "name": "quoted_stg_support_0", + "resource_type": "model", + "package_name": "generated_4", + "path": "models/analysis/quoted_stg_support_0.sql", + "original_file_path": "models/analysis/quoted_stg_support_0.sql", + "unique_id": "model.generated_4.quoted_stg_support_0", + "fqn": [ + "generated_4", + "models", + "analysis", + "quoted_stg_support_0" + ], + "alias": "quoted_stg_support_0", + "config": { + "enabled": true, + "alias": null, + "schema": "ci_checks", + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "table", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Quoted identifiers over stg_support_0", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "ci_checks.quoted_stg_support_0", + "depends_on": { + "macros": [], + "nodes": [ + "model.generated_4.stg_support_0" + ] + } + }, + "model.generated_4.regional_stg_support_0": { + "database": null, + "schema": "ci_checks", + "name": "regional_stg_support_0", + "resource_type": "model", + "package_name": "generated_4", + "path": "models/analysis/regional_stg_support_0.sql", + "original_file_path": "models/analysis/regional_stg_support_0.sql", + "unique_id": "model.generated_4.regional_stg_support_0", + "fqn": [ + "generated_4", + "models", + "analysis", + "regional_stg_support_0" + ], + "alias": "regional_stg_support_0", + "config": { + "enabled": true, + "alias": null, + "schema": "ci_checks", + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "table", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Regional copy of stg_support_0", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "ci_checks.regional_stg_support_0", + "depends_on": { + "macros": [], + "nodes": [ + "model.generated_4.stg_support_0" + ] + } + }, + "model.generated_4.typed_stg_support_0": { + "database": null, + "schema": "ci_checks", + "name": "typed_stg_support_0", + "resource_type": "model", + "package_name": "generated_4", + "path": "models/analysis/typed_stg_support_0.sql", + "original_file_path": "models/analysis/typed_stg_support_0.sql", + "unique_id": "model.generated_4.typed_stg_support_0", + "fqn": [ + "generated_4", + "models", + "analysis", + "typed_stg_support_0" + ], + "alias": "typed_stg_support_0", + "config": { + "enabled": true, + "alias": null, + "schema": "ci_checks", + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "table", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Typed and untyped outputs of stg_support_0", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "ci_checks.typed_stg_support_0", + "depends_on": { + "macros": [], + "nodes": [ + "model.generated_4.stg_support_0" + ] + } + }, + "model.generated_4.unanalysed_stg_support_0": { + "database": null, + "schema": "ci_checks", + "name": "unanalysed_stg_support_0", + "resource_type": "model", + "package_name": "generated_4", + "path": "models/analysis/unanalysed_stg_support_0.sql", + "original_file_path": "models/analysis/unanalysed_stg_support_0.sql", + "unique_id": "model.generated_4.unanalysed_stg_support_0", + "fqn": [ + "generated_4", + "models", + "analysis", + "unanalysed_stg_support_0" + ], + "alias": "unanalysed_stg_support_0", + "config": { + "enabled": true, + "alias": null, + "schema": "ci_checks", + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "table", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Copy of stg_support_0 that SQL analysis cannot parse", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "ci_checks.unanalysed_stg_support_0", + "depends_on": { + "macros": [], + "nodes": [ + "model.generated_4.stg_support_0" + ] + } + }, + "model.generated_4.append_stg_support_0": { + "database": null, + "schema": "ci_checks", + "name": "append_stg_support_0", + "resource_type": "model", + "package_name": "generated_4", + "path": "models/features/append_stg_support_0.sql", + "original_file_path": "models/features/append_stg_support_0.sql", + "unique_id": "model.generated_4.append_stg_support_0", + "fqn": [ + "generated_4", + "models", + "features", + "append_stg_support_0" + ], + "alias": "append_stg_support_0", + "config": { + "enabled": true, + "alias": null, + "schema": "ci_checks", + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "incremental", + "incremental_strategy": "append", + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Incremental append copy of stg_support_0", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "ci_checks.append_stg_support_0", + "depends_on": { + "macros": [], + "nodes": [ + "model.generated_4.stg_support_0" + ] + } + }, + "model.generated_4.batched_rolling_window_stg_support_0": { + "database": null, + "schema": "ci_checks", + "name": "batched_rolling_window_stg_support_0", + "resource_type": "model", + "package_name": "generated_4", + "path": "models/features/batched_rolling_window_stg_support_0.sql", + "original_file_path": "models/features/batched_rolling_window_stg_support_0.sql", + "unique_id": "model.generated_4.batched_rolling_window_stg_support_0", + "fqn": [ + "generated_4", + "models", + "features", + "batched_rolling_window_stg_support_0" + ], + "alias": "batched_rolling_window_stg_support_0", + "config": { + "enabled": true, + "alias": null, + "schema": "ci_checks", + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "incremental", + "incremental_strategy": "delete_insert", + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Microbatched rolling_window totals of stg_support_0", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "ci_checks.batched_rolling_window_stg_support_0", + "depends_on": { + "macros": [], + "nodes": [ + "model.generated_4.stg_support_0" + ] + } + }, + "model.generated_4.guarded_stg_support_0": { + "database": null, + "schema": "ci_checks", + "name": "guarded_stg_support_0", + "resource_type": "model", + "package_name": "generated_4", + "path": "models/features/guarded_stg_support_0.sql", + "original_file_path": "models/features/guarded_stg_support_0.sql", + "unique_id": "model.generated_4.guarded_stg_support_0", + "fqn": [ + "generated_4", + "models", + "features", + "guarded_stg_support_0" + ], + "alias": "guarded_stg_support_0", + "config": { + "enabled": true, + "alias": null, + "schema": "ci_checks", + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "table", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Generic-audited copy of stg_support_0", + "columns": { + "amount": { + "name": "amount", + "description": "", + "meta": {}, + "data_type": null, + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "ci_checks.guarded_stg_support_0", + "depends_on": { + "macros": [], + "nodes": [ + "model.generated_4.stg_support_0" + ] + } + }, + "model.generated_4.product_labels": { + "database": null, + "schema": "ci_checks", + "name": "product_labels", + "resource_type": "model", + "package_name": "generated_4", + "path": "models/features/product_labels.sql", + "original_file_path": "models/features/product_labels.sql", + "unique_id": "model.generated_4.product_labels", + "fqn": [ + "generated_4", + "models", + "features", + "product_labels" + ], + "alias": "product_labels", + "config": { + "enabled": true, + "alias": null, + "schema": "ci_checks", + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Product labels from the loader", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "ci_checks.product_labels", + "depends_on": { + "macros": [], + "nodes": [ + "source.generated_4.loaded_products" + ] + } + }, + "model.generated_4.shaped_stg_support_0": { + "database": null, + "schema": "ci_checks", + "name": "shaped_stg_support_0", + "resource_type": "model", + "package_name": "generated_4", + "path": "models/features/shaped_stg_support_0.sql", + "original_file_path": "models/features/shaped_stg_support_0.sql", + "unique_id": "model.generated_4.shaped_stg_support_0", + "fqn": [ + "generated_4", + "models", + "features", + "shaped_stg_support_0" + ], + "alias": "shaped_stg_support_0", + "config": { + "enabled": true, + "alias": null, + "schema": "ci_checks", + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "table", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Schema-shaped copy of stg_support_0", + "columns": { + "id": { + "name": "id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "status": { + "name": "status", + "description": "", + "meta": {}, + "data_type": "VARCHAR", + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "ci_checks.shaped_stg_support_0", + "depends_on": { + "macros": [], + "nodes": [ + "model.generated_4.stg_support_0" + ] + } + }, + "model.generated_4.timestamp_history_stg_support_0": { + "database": null, + "schema": "ci_checks", + "name": "timestamp_history_stg_support_0", + "resource_type": "model", + "package_name": "generated_4", + "path": "models/features/timestamp_history_stg_support_0.sql", + "original_file_path": "models/features/timestamp_history_stg_support_0.sql", + "unique_id": "model.generated_4.timestamp_history_stg_support_0", + "fqn": [ + "generated_4", + "models", + "features", + "timestamp_history_stg_support_0" + ], + "alias": "timestamp_history_stg_support_0", + "config": { + "enabled": true, + "alias": null, + "schema": "ci_checks", + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "snapshot", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": [ + "id" + ], + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Timestamp history of stg_support_0", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "ci_checks.timestamp_history_stg_support_0", + "depends_on": { + "macros": [], + "nodes": [ + "model.generated_4.stg_support_0" + ] + } + }, + "model.generated_4.bounded_stg_support_0": { + "database": null, + "schema": "ci_checks", + "name": "bounded_stg_support_0", + "resource_type": "model", + "package_name": "generated_4", + "path": "models/rendering/bounded_stg_support_0.sql", + "original_file_path": "models/rendering/bounded_stg_support_0.sql", + "unique_id": "model.generated_4.bounded_stg_support_0", + "fqn": [ + "generated_4", + "models", + "rendering", + "bounded_stg_support_0" + ], + "alias": "bounded_stg_support_0", + "config": { + "enabled": true, + "alias": null, + "schema": "ci_checks", + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "incremental", + "incremental_strategy": "delete_insert", + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": [ + "id" + ], + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Cursor-bounded copy of stg_support_0", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "ci_checks.bounded_stg_support_0", + "depends_on": { + "macros": [], + "nodes": [ + "model.generated_4.stg_support_0" + ] + } + }, + "model.generated_4.configured_stg_support_0": { + "database": null, + "schema": "ci_checks", + "name": "configured_stg_support_0", + "resource_type": "model", + "package_name": "generated_4", + "path": "models/rendering/configured_stg_support_0.sql", + "original_file_path": "models/rendering/configured_stg_support_0.sql", + "unique_id": "model.generated_4.configured_stg_support_0", + "fqn": [ + "generated_4", + "models", + "rendering", + "configured_stg_support_0" + ], + "alias": "configured_stg_support_0", + "config": { + "enabled": true, + "alias": null, + "schema": "ci_checks", + "database": null, + "tags": [ + "release" + ], + "meta": {}, + "group": null, + "materialized": "table", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [ + "release" + ], + "description": "Configured copy of stg_support_0 (réglé)", + "columns": { + "id": { + "name": "id", + "description": "Row key", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "amount": { + "name": "amount", + "description": "Montant réglé", + "meta": {}, + "data_type": "DOUBLE", + "constraints": [], + "quote": null, + "tags": [] + }, + "status": { + "name": "status", + "description": "", + "meta": {}, + "data_type": null, + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "ci_checks.configured_stg_support_0", + "depends_on": { + "macros": [], + "nodes": [ + "model.generated_4.stg_support_0" + ] + } + }, + "model.generated_4.latest_raw_events_0": { + "database": null, + "schema": "ci_checks", + "name": "latest_raw_events_0", + "resource_type": "model", + "package_name": "generated_4", + "path": "models/rendering/latest_raw_events_0.sql", + "original_file_path": "models/rendering/latest_raw_events_0.sql", + "unique_id": "model.generated_4.latest_raw_events_0", + "fqn": [ + "generated_4", + "models", + "rendering", + "latest_raw_events_0" + ], + "alias": "latest_raw_events_0", + "config": { + "enabled": true, + "alias": null, + "schema": "ci_checks", + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "table", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Newest rows of raw_events_0", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "ci_checks.latest_raw_events_0", + "depends_on": { + "macros": [], + "nodes": [ + "source.generated_4.raw_events_0" + ] + } + }, + "model.generated_4.policy_stg_support_0": { + "database": null, + "schema": "ci_checks", + "name": "policy_stg_support_0", + "resource_type": "model", + "package_name": "generated_4", + "path": "models/rendering/policy_stg_support_0.sql", + "original_file_path": "models/rendering/policy_stg_support_0.sql", + "unique_id": "model.generated_4.policy_stg_support_0", + "fqn": [ + "generated_4", + "models", + "rendering", + "policy_stg_support_0" + ], + "alias": "policy_stg_support_0", + "config": { + "enabled": true, + "alias": null, + "schema": "ci_checks", + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "table", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Policy-filtered copy of stg_support_0", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "ci_checks.policy_stg_support_0", + "depends_on": { + "macros": [], + "nodes": [ + "model.generated_4.stg_support_0" + ] + } + }, + "model.generated_4.unioned_event_ids": { + "database": null, + "schema": "ci_checks", + "name": "unioned_event_ids", + "resource_type": "model", + "package_name": "generated_4", + "path": "models/rendering/unioned_event_ids.sql", + "original_file_path": "models/rendering/unioned_event_ids.sql", + "unique_id": "model.generated_4.unioned_event_ids", + "fqn": [ + "generated_4", + "models", + "rendering", + "unioned_event_ids" + ], + "alias": "unioned_event_ids", + "config": { + "enabled": true, + "alias": null, + "schema": "ci_checks", + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "table", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Ids from two staged relations", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "ci_checks.unioned_event_ids", + "depends_on": { + "macros": [], + "nodes": [ + "model.generated_4.stg_support_0" + ] + } + }, + "model.generated_4.support_summary_0": { + "database": null, + "schema": "ci_checks", + "name": "support_summary_0", + "resource_type": "model", + "package_name": "generated_4", + "path": "models/support/marts/support_summary_0.sql", + "original_file_path": "models/support/marts/support_summary_0.sql", + "unique_id": "model.generated_4.support_summary_0", + "fqn": [ + "generated_4", + "models", + "support", + "marts", + "support_summary_0" + ], + "alias": "support_summary_0", + "config": { + "enabled": true, + "alias": null, + "schema": "ci_checks", + "database": null, + "tags": [ + "support", + "marts" + ], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [ + { + "sql": "SELECT 'ci_checks.support_summary_0' AS refreshed_relation, 'naïve résumé' AS refresh_label", + "transaction": true, + "index": 0 + }, + { + "sql": "SELECT 'ci_checks.support_summary_0' AS refreshed", + "transaction": true, + "index": 1 + } + ], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [ + "support", + "marts" + ], + "description": "Summary of support activity 0 (東京 warehouse)", + "columns": { + "id": { + "name": "id", + "description": "", + "meta": {}, + "data_type": null, + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": { + "sqlbuild": { + "lifecycle_hooks": { + "post_hooks": [ + { + "type": "sql", + "statement": "SELECT 'ci_checks.support_summary_0' AS refreshed_relation, 'naïve résumé' AS refresh_label", + "name": "record_refresh", + "relative_path": "models/support/marts/_sqlbuild/_hooks/sql/record_refresh.sql", + "definition_sql": "SELECT @'relation' AS refreshed_relation, @'label' AS refresh_label", + "kwargs": { + "relation": "@@CTX:destination.qualified", + "label": "naïve résumé" + }, + "description": "Record a refresh for one relation" + }, + { + "type": "sql", + "statement": "SELECT 'ci_checks.support_summary_0' AS refreshed" + } + ] + } + } + }, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "ci_checks.support_summary_0", + "depends_on": { + "macros": [], + "nodes": [ + "model.generated_4.stg_support_0", + "seed.generated_4.lookup_1" + ] + } + }, + "model.generated_4.support_summary_1": { + "database": null, + "schema": "ci_checks", + "name": "support_summary_1", + "resource_type": "model", + "package_name": "generated_4", + "path": "models/support/marts/support_summary_1.sql", + "original_file_path": "models/support/marts/support_summary_1.sql", + "unique_id": "model.generated_4.support_summary_1", + "fqn": [ + "generated_4", + "models", + "support", + "marts", + "support_summary_1" + ], + "alias": "support_summary_1", + "config": { + "enabled": true, + "alias": null, + "schema": "ci_checks", + "database": null, + "tags": [ + "support", + "marts" + ], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [ + "support", + "marts" + ], + "description": "Summary of support activity 1", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "ci_checks.support_summary_1", + "depends_on": { + "macros": [], + "nodes": [ + "model.generated_4.stg_support_0", + "seed.generated_4.lookup_0" + ] + } + }, + "model.generated_4.support_summary_2": { + "database": null, + "schema": "ci_checks", + "name": "support_summary_2", + "resource_type": "model", + "package_name": "generated_4", + "path": "models/support/marts/support_summary_2.sql", + "original_file_path": "models/support/marts/support_summary_2.sql", + "unique_id": "model.generated_4.support_summary_2", + "fqn": [ + "generated_4", + "models", + "support", + "marts", + "support_summary_2" + ], + "alias": "support_summary_2", + "config": { + "enabled": true, + "alias": null, + "schema": "ci_checks", + "database": null, + "tags": [ + "support", + "marts" + ], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [ + "support", + "marts" + ], + "description": "Summary of support activity 2 (café ☕ counter)", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "ci_checks.support_summary_2", + "depends_on": { + "macros": [], + "nodes": [ + "model.generated_4.stg_support_0", + "seed.generated_4.lookup_1" + ] + } + }, + "model.generated_4.stg_support_0": { + "database": null, + "schema": "ci_checks", + "name": "stg_support_0", + "resource_type": "model", + "package_name": "generated_4", + "path": "models/support/staging/stg_support_0.sql", + "original_file_path": "models/support/staging/stg_support_0.sql", + "unique_id": "model.generated_4.stg_support_0", + "fqn": [ + "generated_4", + "models", + "support", + "staging", + "stg_support_0" + ], + "alias": "stg_support_0", + "config": { + "enabled": true, + "alias": null, + "schema": "ci_checks", + "database": null, + "tags": [ + "staging" + ], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [ + "staging" + ], + "description": "Staged support events 0 (naïve résumé)", + "columns": { + "id": { + "name": "id", + "description": "", + "meta": {}, + "data_type": null, + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "ci_checks.stg_support_0", + "depends_on": { + "macros": [], + "nodes": [ + "source.generated_4.raw_events_0" + ] + } + }, + "seed.generated_4.lookup_0": { + "database": null, + "schema": "ci_checks", + "name": "lookup_0", + "resource_type": "seed", + "package_name": "generated_4", + "path": "seeds/lookup_0.csv", + "original_file_path": "seeds/lookup_0.csv", + "unique_id": "seed.generated_4.lookup_0", + "fqn": [ + "generated_4", + "seeds", + "lookup_0" + ], + "alias": "lookup_0", + "config": { + "enabled": true, + "alias": null, + "schema": "ci_checks", + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "seed", + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + }, + "delimiter": ",", + "quote_columns": null + }, + "tags": [], + "description": "Generated lookup 0.", + "columns": { + "id": { + "name": "id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "label": { + "name": "label", + "description": "", + "meta": {}, + "data_type": "VARCHAR", + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "ci_checks.lookup_0", + "root_path": null, + "depends_on": { + "macros": [] + } + }, + "seed.generated_4.lookup_1": { + "database": null, + "schema": "ci_checks", + "name": "lookup_1", + "resource_type": "seed", + "package_name": "generated_4", + "path": "seeds/lookup_1.csv", + "original_file_path": "seeds/lookup_1.csv", + "unique_id": "seed.generated_4.lookup_1", + "fqn": [ + "generated_4", + "seeds", + "lookup_1" + ], + "alias": "lookup_1", + "config": { + "enabled": true, + "alias": null, + "schema": "ci_checks", + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "seed", + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + }, + "delimiter": ",", + "quote_columns": null + }, + "tags": [], + "description": "Generated lookup 1.", + "columns": { + "id": { + "name": "id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "label": { + "name": "label", + "description": "", + "meta": {}, + "data_type": "VARCHAR", + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "ci_checks.lookup_1", + "root_path": null, + "depends_on": { + "macros": [] + } + } + }, + "sources": { + "source.generated_4.configured_events": { + "database": null, + "schema": "", + "name": "configured_events", + "resource_type": "source", + "package_name": "generated_4", + "path": "sources/configured.yml", + "original_file_path": "sources/configured.yml", + "unique_id": "source.generated_4.configured_events", + "fqn": [ + "generated_4", + "configured_events" + ], + "source_name": "configured_events", + "source_description": "", + "loader": "", + "identifier": "configured_events", + "quoting": {}, + "loaded_at_field": null, + "freshness": null, + "external": null, + "description": "Configured feed events", + "columns": { + "id": { + "name": "id", + "description": "Key id", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "amount": { + "name": "amount", + "description": "", + "meta": {}, + "data_type": "DOUBLE", + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": { + "owner": "orders team" + }, + "source_meta": {}, + "tags": [], + "config": {}, + "patch_path": null, + "unrendered_config": {}, + "relation_name": null + }, + "source.generated_4.loaded_products": { + "database": null, + "schema": "ci_checks", + "name": "loaded_products", + "resource_type": "source", + "package_name": "generated_4", + "path": "sources/managed.yml", + "original_file_path": "sources/managed.yml", + "unique_id": "source.generated_4.loaded_products", + "fqn": [ + "generated_4", + "loaded_products" + ], + "source_name": "loaded_products", + "source_description": "", + "loader": "", + "identifier": "loaded_products", + "quoting": {}, + "loaded_at_field": null, + "freshness": null, + "external": null, + "description": "Products loaded by a project loader.", + "columns": { + "id": { + "name": "id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "label": { + "name": "label", + "description": "", + "meta": {}, + "data_type": "VARCHAR", + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": { + "sqlbuild": { + "loader": "loaded_products", + "auto_load": false, + "write_strategy": "table" + } + }, + "source_meta": {}, + "tags": [], + "config": {}, + "patch_path": null, + "unrendered_config": {}, + "relation_name": null + }, + "source.generated_4.open_events": { + "database": null, + "schema": "", + "name": "open_events", + "resource_type": "source", + "package_name": "generated_4", + "path": "sources/open.yml", + "original_file_path": "sources/open.yml", + "unique_id": "source.generated_4.open_events", + "fqn": [ + "generated_4", + "open_events" + ], + "source_name": "open_events", + "source_description": "", + "loader": "", + "identifier": "open_events", + "quoting": {}, + "loaded_at_field": null, + "freshness": null, + "external": null, + "description": "Events whose columns are only known to the warehouse.", + "columns": {}, + "meta": {}, + "source_meta": {}, + "tags": [], + "config": {}, + "patch_path": null, + "unrendered_config": {}, + "relation_name": null + }, + "source.generated_4.raw_events_0": { + "database": null, + "schema": "", + "name": "raw_events_0", + "resource_type": "source", + "package_name": "generated_4", + "path": "sources/raw.yml", + "original_file_path": "sources/raw.yml", + "unique_id": "source.generated_4.raw_events_0", + "fqn": [ + "generated_4", + "raw_events_0" + ], + "source_name": "raw_events_0", + "source_description": "", + "loader": "", + "identifier": "raw_events_0", + "quoting": {}, + "loaded_at_field": null, + "freshness": null, + "external": null, + "description": "Generated event feed 0.", + "columns": { + "id": { + "name": "id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "amount": { + "name": "amount", + "description": "", + "meta": {}, + "data_type": "DOUBLE", + "constraints": [], + "quote": null, + "tags": [] + }, + "status": { + "name": "status", + "description": "", + "meta": {}, + "data_type": "VARCHAR", + "constraints": [], + "quote": null, + "tags": [] + }, + "created_at": { + "name": "created_at", + "description": "", + "meta": {}, + "data_type": "TIMESTAMP", + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": {}, + "source_meta": {}, + "tags": [], + "config": {}, + "patch_path": null, + "unrendered_config": {}, + "relation_name": null + } + }, + "macros": { + "macro.generated_4.analysis_region": { + "name": "analysis_region", + "resource_type": "macro", + "package_name": "generated_4", + "path": "models/analysis/_sqlbuild/_macros/regions.py", + "original_file_path": "models/analysis/_sqlbuild/_macros/regions.py", + "unique_id": "macro.generated_4.analysis_region", + "macro_sql": "def analysis_region() -> str:\n \"\"\"Return the region rows are assigned to.\"\"\"\n return \"'north'\"\n", + "depends_on": { + "macros": [] + }, + "description": "", + "meta": { + "sqlbuild_visibility": "local", + "sqlbuild_scope_path": "models/analysis" + }, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "arguments": [], + "supported_languages": null + }, + "macro.generated_4.channel_label": { + "name": "channel_label", + "resource_type": "macro", + "package_name": "generated_4", + "path": "models/rendering/_sqlbuild/_macros/policy.py", + "original_file_path": "models/rendering/_sqlbuild/_macros/policy.py", + "unique_id": "macro.generated_4.channel_label", + "macro_sql": "import os\n\n\ndef minimum_amount_filter(ctx, column: str) -> str:\n \"\"\"Keep rows at or above the configured minimum amount.\"\"\"\n if ctx.constants[\"minimum_render_amount\"] is None:\n return \"TRUE\"\n return f\"{column} >= {ctx.render_constant('minimum_render_amount')}\"\n\n\ndef channel_label(ctx) -> str:\n \"\"\"Label rows with the web channel, target and adapter.\"\"\"\n channel = ctx.render_enum_member(enum_name=\"render_channel\", member_name=\"WEB\")\n region = ctx.vars[\"region\"]\n return f\"{channel} || '/{ctx.target_name}/{ctx.adapter_name}/{region}'\"\n\n\ndef with_deployment(expression: str) -> str:\n \"\"\"Suffix an expression with the deployment channel from the environment.\"\"\"\n channel = os.environ.get(\"SQB_DIFFERENTIAL_CHANNEL\", \"none\")\n return f\"({expression} || '@{channel}')\"\n", + "depends_on": { + "macros": [] + }, + "description": "", + "meta": { + "sqlbuild_visibility": "local", + "sqlbuild_scope_path": "models/rendering" + }, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "arguments": [], + "supported_languages": null + }, + "macro.generated_4.minimum_amount_filter": { + "name": "minimum_amount_filter", + "resource_type": "macro", + "package_name": "generated_4", + "path": "models/rendering/_sqlbuild/_macros/policy.py", + "original_file_path": "models/rendering/_sqlbuild/_macros/policy.py", + "unique_id": "macro.generated_4.minimum_amount_filter", + "macro_sql": "import os\n\n\ndef minimum_amount_filter(ctx, column: str) -> str:\n \"\"\"Keep rows at or above the configured minimum amount.\"\"\"\n if ctx.constants[\"minimum_render_amount\"] is None:\n return \"TRUE\"\n return f\"{column} >= {ctx.render_constant('minimum_render_amount')}\"\n\n\ndef channel_label(ctx) -> str:\n \"\"\"Label rows with the web channel, target and adapter.\"\"\"\n channel = ctx.render_enum_member(enum_name=\"render_channel\", member_name=\"WEB\")\n region = ctx.vars[\"region\"]\n return f\"{channel} || '/{ctx.target_name}/{ctx.adapter_name}/{region}'\"\n\n\ndef with_deployment(expression: str) -> str:\n \"\"\"Suffix an expression with the deployment channel from the environment.\"\"\"\n channel = os.environ.get(\"SQB_DIFFERENTIAL_CHANNEL\", \"none\")\n return f\"({expression} || '@{channel}')\"\n", + "depends_on": { + "macros": [] + }, + "description": "", + "meta": { + "sqlbuild_visibility": "local", + "sqlbuild_scope_path": "models/rendering" + }, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "arguments": [], + "supported_languages": null + }, + "macro.generated_4.with_deployment": { + "name": "with_deployment", + "resource_type": "macro", + "package_name": "generated_4", + "path": "models/rendering/_sqlbuild/_macros/policy.py", + "original_file_path": "models/rendering/_sqlbuild/_macros/policy.py", + "unique_id": "macro.generated_4.with_deployment", + "macro_sql": "import os\n\n\ndef minimum_amount_filter(ctx, column: str) -> str:\n \"\"\"Keep rows at or above the configured minimum amount.\"\"\"\n if ctx.constants[\"minimum_render_amount\"] is None:\n return \"TRUE\"\n return f\"{column} >= {ctx.render_constant('minimum_render_amount')}\"\n\n\ndef channel_label(ctx) -> str:\n \"\"\"Label rows with the web channel, target and adapter.\"\"\"\n channel = ctx.render_enum_member(enum_name=\"render_channel\", member_name=\"WEB\")\n region = ctx.vars[\"region\"]\n return f\"{channel} || '/{ctx.target_name}/{ctx.adapter_name}/{region}'\"\n\n\ndef with_deployment(expression: str) -> str:\n \"\"\"Suffix an expression with the deployment channel from the environment.\"\"\"\n channel = os.environ.get(\"SQB_DIFFERENTIAL_CHANNEL\", \"none\")\n return f\"({expression} || '@{channel}')\"\n", + "depends_on": { + "macros": [] + }, + "description": "", + "meta": { + "sqlbuild_visibility": "local", + "sqlbuild_scope_path": "models/rendering" + }, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "arguments": [], + "supported_languages": null + }, + "macro.generated_4.latest_rows": { + "name": "latest_rows", + "resource_type": "macro", + "package_name": "generated_4", + "path": "models/rendering/_sqlbuild/_macros/relations.py", + "original_file_path": "models/rendering/_sqlbuild/_macros/relations.py", + "unique_id": "macro.generated_4.latest_rows", + "macro_sql": "from sqlbuild.refs import SqlResourceRef\n\n\ndef union_ids(relations: list[SqlResourceRef]) -> str:\n \"\"\"Union the ids of several relations.\"\"\"\n selects = [f\"SELECT id FROM {relation}\" for relation in relations]\n return \" UNION ALL \".join(selects)\n\n\ndef latest_rows(relation: SqlResourceRef, limit: int = 2) -> str:\n \"\"\"Return the newest rows of one relation.\"\"\"\n return f\"(SELECT id, amount FROM {relation} ORDER BY id DESC LIMIT {limit})\"\n", + "depends_on": { + "macros": [] + }, + "description": "", + "meta": { + "sqlbuild_visibility": "local", + "sqlbuild_scope_path": "models/rendering" + }, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "arguments": [], + "supported_languages": null + }, + "macro.generated_4.union_ids": { + "name": "union_ids", + "resource_type": "macro", + "package_name": "generated_4", + "path": "models/rendering/_sqlbuild/_macros/relations.py", + "original_file_path": "models/rendering/_sqlbuild/_macros/relations.py", + "unique_id": "macro.generated_4.union_ids", + "macro_sql": "from sqlbuild.refs import SqlResourceRef\n\n\ndef union_ids(relations: list[SqlResourceRef]) -> str:\n \"\"\"Union the ids of several relations.\"\"\"\n selects = [f\"SELECT id FROM {relation}\" for relation in relations]\n return \" UNION ALL \".join(selects)\n\n\ndef latest_rows(relation: SqlResourceRef, limit: int = 2) -> str:\n \"\"\"Return the newest rows of one relation.\"\"\"\n return f\"(SELECT id, amount FROM {relation} ORDER BY id DESC LIMIT {limit})\"\n", + "depends_on": { + "macros": [] + }, + "description": "", + "meta": { + "sqlbuild_visibility": "local", + "sqlbuild_scope_path": "models/rendering" + }, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "arguments": [], + "supported_languages": null + }, + "macro.generated_4.adjust_2": { + "name": "adjust_2", + "resource_type": "macro", + "package_name": "generated_4", + "path": "models/support/_sqlbuild/macros/adjust_2.py", + "original_file_path": "models/support/_sqlbuild/macros/adjust_2.py", + "unique_id": "macro.generated_4.adjust_2", + "macro_sql": "def adjust_2_round(expression: str) -> str:\n \"\"\"Round an amount to two places.\"\"\"\n return f\"ROUND({expression}, 2)\"\n\n\ndef adjust_2(expression: str) -> str:\n \"\"\"Double and round an amount.\"\"\"\n return adjust_2_round(f\"({expression} * 2)\")\n", + "depends_on": { + "macros": [ + "macro.generated_4.adjust_2_round" + ] + }, + "description": "", + "meta": { + "sqlbuild_visibility": "inherited", + "sqlbuild_scope_path": "models/support" + }, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "arguments": [], + "supported_languages": null + }, + "macro.generated_4.adjust_2_round": { + "name": "adjust_2_round", + "resource_type": "macro", + "package_name": "generated_4", + "path": "models/support/_sqlbuild/macros/adjust_2.py", + "original_file_path": "models/support/_sqlbuild/macros/adjust_2.py", + "unique_id": "macro.generated_4.adjust_2_round", + "macro_sql": "def adjust_2_round(expression: str) -> str:\n \"\"\"Round an amount to two places.\"\"\"\n return f\"ROUND({expression}, 2)\"\n\n\ndef adjust_2(expression: str) -> str:\n \"\"\"Double and round an amount.\"\"\"\n return adjust_2_round(f\"({expression} * 2)\")\n", + "depends_on": { + "macros": [] + }, + "description": "", + "meta": { + "sqlbuild_visibility": "inherited", + "sqlbuild_scope_path": "models/support" + }, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "arguments": [], + "supported_languages": null + }, + "macro.generated_4.adjust_0": { + "name": "adjust_0", + "resource_type": "macro", + "package_name": "generated_4", + "path": "models/support/marts/_sqlbuild/_macros/adjust_0.py", + "original_file_path": "models/support/marts/_sqlbuild/_macros/adjust_0.py", + "unique_id": "macro.generated_4.adjust_0", + "macro_sql": "def adjust_0_round(expression: str) -> str:\n \"\"\"Round an amount to two places.\"\"\"\n return f\"ROUND({expression}, 2)\"\n\n\ndef adjust_0(expression: str) -> str:\n \"\"\"Double and round an amount.\"\"\"\n return adjust_0_round(f\"({expression} * 2)\")\n", + "depends_on": { + "macros": [ + "macro.generated_4.adjust_0_round" + ] + }, + "description": "", + "meta": { + "sqlbuild_visibility": "local", + "sqlbuild_scope_path": "models/support/marts" + }, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "arguments": [], + "supported_languages": null + }, + "macro.generated_4.adjust_0_round": { + "name": "adjust_0_round", + "resource_type": "macro", + "package_name": "generated_4", + "path": "models/support/marts/_sqlbuild/_macros/adjust_0.py", + "original_file_path": "models/support/marts/_sqlbuild/_macros/adjust_0.py", + "unique_id": "macro.generated_4.adjust_0_round", + "macro_sql": "def adjust_0_round(expression: str) -> str:\n \"\"\"Round an amount to two places.\"\"\"\n return f\"ROUND({expression}, 2)\"\n\n\ndef adjust_0(expression: str) -> str:\n \"\"\"Double and round an amount.\"\"\"\n return adjust_0_round(f\"({expression} * 2)\")\n", + "depends_on": { + "macros": [] + }, + "description": "", + "meta": { + "sqlbuild_visibility": "local", + "sqlbuild_scope_path": "models/support/marts" + }, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "arguments": [], + "supported_languages": null + }, + "macro.generated_4.adjust_1": { + "name": "adjust_1", + "resource_type": "macro", + "package_name": "generated_4", + "path": "models/support/marts/_sqlbuild/_macros/adjust_1.py", + "original_file_path": "models/support/marts/_sqlbuild/_macros/adjust_1.py", + "unique_id": "macro.generated_4.adjust_1", + "macro_sql": "def adjust_1_round(expression: str) -> str:\n \"\"\"Round an amount to two places.\"\"\"\n return f\"ROUND({expression}, 2)\"\n\n\ndef adjust_1(expression: str) -> str:\n \"\"\"Double and round an amount.\"\"\"\n return adjust_1_round(f\"({expression} * 2)\")\n", + "depends_on": { + "macros": [ + "macro.generated_4.adjust_1_round" + ] + }, + "description": "", + "meta": { + "sqlbuild_visibility": "local", + "sqlbuild_scope_path": "models/support/marts" + }, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "arguments": [], + "supported_languages": null + }, + "macro.generated_4.adjust_1_round": { + "name": "adjust_1_round", + "resource_type": "macro", + "package_name": "generated_4", + "path": "models/support/marts/_sqlbuild/_macros/adjust_1.py", + "original_file_path": "models/support/marts/_sqlbuild/_macros/adjust_1.py", + "unique_id": "macro.generated_4.adjust_1_round", + "macro_sql": "def adjust_1_round(expression: str) -> str:\n \"\"\"Round an amount to two places.\"\"\"\n return f\"ROUND({expression}, 2)\"\n\n\ndef adjust_1(expression: str) -> str:\n \"\"\"Double and round an amount.\"\"\"\n return adjust_1_round(f\"({expression} * 2)\")\n", + "depends_on": { + "macros": [] + }, + "description": "", + "meta": { + "sqlbuild_visibility": "local", + "sqlbuild_scope_path": "models/support/marts" + }, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "arguments": [], + "supported_languages": null + }, + "macro.generated_4__sqlbuild_hooks.record_refresh": { + "name": "record_refresh", + "resource_type": "macro", + "package_name": "generated_4__sqlbuild_hooks", + "path": "models/support/marts/_sqlbuild/_hooks/sql/record_refresh.sql", + "original_file_path": "models/support/marts/_sqlbuild/_hooks/sql/record_refresh.sql", + "unique_id": "macro.generated_4__sqlbuild_hooks.record_refresh", + "macro_sql": "SELECT @'relation' AS refreshed_relation, @'label' AS refresh_label", + "depends_on": { + "macros": [] + }, + "description": "Record a refresh for one relation", + "meta": { + "sqlbuild_resource_type": "sql_hook", + "sqlbuild_hook_name": "record_refresh" + }, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "arguments": [], + "supported_languages": null + } + }, + "exposures": {}, + "metrics": {}, + "groups": {}, + "selectors": {}, + "disabled": {}, + "parent_map": { + "model.generated_4.combined_stg_support_0": [ + "model.generated_4.stg_support_0" + ], + "model.generated_4.every_open_event": [ + "source.generated_4.open_events" + ], + "model.generated_4.every_raw_events_0": [ + "source.generated_4.raw_events_0" + ], + "model.generated_4.every_stg_support_0": [ + "model.generated_4.stg_support_0" + ], + "model.generated_4.passed_stg_support_0": [ + "model.generated_4.stg_support_0" + ], + "model.generated_4.pivot_input_stg_support_0": [ + "model.generated_4.stg_support_0" + ], + "model.generated_4.pivoted_stg_support_0": [ + "model.generated_4.pivot_input_stg_support_0" + ], + "model.generated_4.quoted_stg_support_0": [ + "model.generated_4.stg_support_0" + ], + "model.generated_4.regional_stg_support_0": [ + "model.generated_4.stg_support_0" + ], + "model.generated_4.typed_stg_support_0": [ + "model.generated_4.stg_support_0" + ], + "model.generated_4.unanalysed_stg_support_0": [ + "model.generated_4.stg_support_0" + ], + "model.generated_4.append_stg_support_0": [ + "model.generated_4.stg_support_0" + ], + "model.generated_4.batched_rolling_window_stg_support_0": [ + "model.generated_4.stg_support_0" + ], + "model.generated_4.guarded_stg_support_0": [ + "model.generated_4.stg_support_0" + ], + "model.generated_4.product_labels": [ + "source.generated_4.loaded_products" + ], + "model.generated_4.shaped_stg_support_0": [ + "model.generated_4.stg_support_0" + ], + "model.generated_4.timestamp_history_stg_support_0": [ + "model.generated_4.stg_support_0" + ], + "model.generated_4.bounded_stg_support_0": [ + "model.generated_4.stg_support_0" + ], + "model.generated_4.configured_stg_support_0": [ + "model.generated_4.stg_support_0" + ], + "model.generated_4.latest_raw_events_0": [ + "source.generated_4.raw_events_0" + ], + "model.generated_4.policy_stg_support_0": [ + "model.generated_4.stg_support_0" + ], + "model.generated_4.unioned_event_ids": [ + "model.generated_4.stg_support_0" + ], + "model.generated_4.support_summary_0": [ + "model.generated_4.stg_support_0", + "seed.generated_4.lookup_1" + ], + "model.generated_4.support_summary_1": [ + "model.generated_4.stg_support_0", + "seed.generated_4.lookup_0" + ], + "model.generated_4.support_summary_2": [ + "model.generated_4.stg_support_0", + "seed.generated_4.lookup_1" + ], + "model.generated_4.stg_support_0": [ + "source.generated_4.raw_events_0" + ], + "source.generated_4.configured_events": [], + "source.generated_4.loaded_products": [], + "source.generated_4.open_events": [], + "source.generated_4.raw_events_0": [], + "seed.generated_4.lookup_0": [], + "seed.generated_4.lookup_1": [], + "model.generated_4.configured_label": [] + }, + "group_map": {}, + "docs": {}, + "saved_queries": {}, + "semantic_models": {}, + "unit_tests": {} + } +} diff --git a/tests/goldens/compiler/seed/5.json b/tests/goldens/compiler/seed/5.json new file mode 100644 index 0000000000..a6ccbc7f30 --- /dev/null +++ b/tests/goldens/compiler/seed/5.json @@ -0,0 +1,48 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "P001", + "message": "Macro name collision for 'adjust_0': macros/adjust_0.py and macros/duplicate_adjust_0.py" + } + ] + }, + { + "label": "compile-store-warm", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "P001", + "message": "Macro name collision for 'adjust_0': macros/adjust_0.py and macros/duplicate_adjust_0.py" + } + ] + }, + { + "label": "plan", + "exit_code": 1, + "diagnostics": [ + "Project discovery START", + "Project assembly START", + "Declaration parsing START", + "Declaration parsing OK ()", + "Python discovery START", + "Python discovery OK ()", + "Project assembly OK ()", + "Project discovery OK ()", + "Compiling project...", + "error[P001]: Macro name collision for 'adjust_0': macros/adjust_0.py and macros/duplicate_adjust_0.py", + "", + "" + ] + } + ], + "compiled": {}, + "manifest": null +} diff --git a/tests/goldens/compiler/seed/6.json b/tests/goldens/compiler/seed/6.json new file mode 100644 index 0000000000..55ea99ab86 --- /dev/null +++ b/tests/goldens/compiler/seed/6.json @@ -0,0 +1,3234 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 0, + "diagnostics": [] + }, + { + "label": "compile-store-warm", + "exit_code": 0, + "diagnostics": [] + }, + { + "label": "plan", + "exit_code": 0, + "diagnostics": [] + }, + { + "label": "compile-select", + "exit_code": 0, + "diagnostics": [] + }, + { + "label": "compile-no-analysis", + "exit_code": 0, + "diagnostics": [] + }, + { + "label": "compile-rich-lineage", + "exit_code": 0, + "diagnostics": [] + } + ], + "compiled": { + "audits/generic/audited_stg_fulfillment_1/id_present__id.sql": [ + "SELECT id", + "FROM __ref(\"audited_stg_fulfillment_1\")", + "WHERE id IS NULL", + "" + ], + "audits/generic/checked_stg_fulfillment_2/accepted_values__status.sql": [ + "SELECT status", + "FROM __ref(\"checked_stg_fulfillment_2\")", + "WHERE status IS NOT NULL", + " AND status NOT IN ('placed', 'expédié', '取消', 'returned ✓')", + "" + ], + "audits/generic/checked_stg_fulfillment_2/expression_is_true.sql": [ + "SELECT *", + "FROM __ref(\"checked_stg_fulfillment_2\")", + "WHERE NOT (amount >= -1000)", + "" + ], + "audits/generic/checked_stg_fulfillment_2/relationships__id.sql": [ + "SELECT id", + "FROM __ref(\"checked_stg_fulfillment_2\")", + "WHERE id IS NOT NULL", + " AND id NOT IN (", + " SELECT id", + " FROM __ref(\"stg_fulfillment_2\")", + " WHERE id IS NOT NULL", + " )", + "" + ], + "audits/generic/fulfillment_summary_0/not_null__id.sql": [ + "SELECT id", + "FROM __ref(\"fulfillment_summary_0\")", + "WHERE id IS NULL", + "" + ], + "audits/generic/sales_summary_0/not_null__id.sql": [ + "SELECT id", + "FROM __ref(\"sales_summary_0\")", + "WHERE id IS NULL", + "" + ], + "audits/generic/sales_summary_1/not_null__id.sql": [ + "SELECT id", + "FROM __ref(\"sales_summary_1\")", + "WHERE id IS NULL", + "" + ], + "audits/generic/shaped_stg_inventory_2/not_null__id.sql": [ + "SELECT id", + "FROM __ref(\"shaped_stg_inventory_2\")", + "WHERE id IS NULL", + "" + ], + "audits/generic/stated_stg_inventory_2/accepted_values__status.sql": [ + "SELECT status", + "FROM __ref(\"stated_stg_inventory_2\")", + "WHERE status IS NOT NULL", + " AND status NOT IN ('placed', 'expédié', '取消', 'returned ✓')", + "" + ], + "audits/generic/stg_fulfillment_0/not_null__id.sql": [ + "SELECT id", + "FROM __ref(\"stg_fulfillment_0\")", + "WHERE id IS NULL", + "" + ], + "audits/generic/stg_fulfillment_2/not_null__id.sql": [ + "SELECT id", + "FROM __ref(\"stg_fulfillment_2\")", + "WHERE id IS NULL", + "" + ], + "audits/generic/stg_inventory_1/not_null__id.sql": [ + "SELECT id", + "FROM __ref(\"stg_inventory_1\")", + "WHERE id IS NULL", + "" + ], + "functions/python/is_large.sql": [ + "REGISTER PYTHON FUNCTION is_large(DOUBLE) RETURNS BOOLEAN", + "" + ], + "functions/sql/is_placed.sql": [ + "CREATE OR REPLACE MACRO main.is_placed(event_status) AS (", + "event_status = 'placed'", + ")", + "" + ], + "functions/sql/table_fn__events_above.sql": [ + "CREATE OR REPLACE MACRO main.table_fn__events_above(p_minimum) AS TABLE", + "SELECT id, amount", + "FROM __ref(\"stg_sales_0\")", + "WHERE amount > p_minimum", + "" + ], + "models/analysis/chain_1_stg_fulfillment_2.sql": [ + "SELECT id, label, amount", + "FROM __ref(\"contracted_stg_fulfillment_2\")", + "" + ], + "models/analysis/chain_2_stg_fulfillment_2.sql": [ + "SELECT id, UPPER(label) AS label, amount", + "FROM __ref(\"chain_1_stg_fulfillment_2\")", + "" + ], + "models/analysis/chain_3_stg_fulfillment_2.sql": [ + "SELECT id, label, amount", + "FROM __ref(\"chain_2_stg_fulfillment_2\")", + "" + ], + "models/analysis/checked_stg_fulfillment_2.sql": [ + "SELECT id, amount, status", + "FROM __ref(\"stg_fulfillment_2\")", + "" + ], + "models/analysis/contracted_stg_fulfillment_2.sql": [ + "SELECT CAST(id AS INTEGER) AS id, CAST(status AS VARCHAR(18)) AS label,", + " amount", + "FROM __source(\"raw_events_0\")", + "" + ], + "models/analysis/regional_stg_fulfillment_0.sql": [ + "SELECT id, amount, 'north' AS region", + "FROM __ref(\"stg_fulfillment_0\")", + "" + ], + "models/features/audited_stg_fulfillment_1.sql": [ + "SELECT id, amount", + "FROM __ref(\"stg_fulfillment_1\")", + "" + ], + "models/features/batched_rolling_window_stg_sales_0.sql": [ + "SELECT CAST(created_at AS DATE) AS created_at, SUM(amount) AS amount", + "FROM __ref(\"stg_sales_0\")", + "GROUP BY CAST(created_at AS DATE)", + "" + ], + "models/features/check_history_stg_inventory_1.sql": [ + "SELECT id, status, amount", + "FROM __ref(\"stg_inventory_1\")", + "" + ], + "models/features/copied_stg_fulfillment_2.sql": [ + "SELECT id, status", + "FROM __ref(\"stg_fulfillment_2\")", + "" + ], + "models/features/flagged_stg_sales_0.sql": [ + "SELECT id, __udf(\"is_placed\")(status) AS is_placed,", + " __udf(\"is_large\")(amount) AS is_large", + "FROM __ref(\"stg_sales_0\")", + "" + ], + "models/features/hooked_stg_inventory_1.sql": [ + "SELECT id, amount", + "FROM __ref(\"stg_inventory_1\")", + "" + ], + "models/features/merge_stg_inventory_0.sql": [ + "SELECT id, amount, status, created_at", + "FROM __ref(\"stg_inventory_0\")", + "" + ], + "models/features/shaped_stg_inventory_2.sql": [ + "SELECT CAST(id AS INTEGER) AS id, status", + "FROM __ref(\"stg_inventory_2\")", + "" + ], + "models/features/spaced_stg_fulfillment_0.sql": [ + "-- Généré pour café ☕ counter", + "SELECT", + "\tid,", + "\t'Ελληνικά orders' AS label, -- étiquette ✓", + "\tamount", + "FROM __ref(\"stg_fulfillment_0\")", + "" + ], + "models/features/taxed_stg_inventory_0.sql": [ + "SELECT id, ROUND(amount * 1.2, 2) AS taxed, ROUND(amount, 1) AS rounded", + "FROM __ref(\"stg_inventory_0\")", + "" + ], + "models/fulfillment/marts/fulfillment_summary_0.sql": [ + "SELECT", + " t.id,", + " t.amount,", + " t.status,", + " t.created_at,", + " 3 AS priority,", + " (t.amount + LENGTH('east')) AS adjust_1_amount", + "FROM __ref(\"stg_fulfillment_0\") t", + "" + ], + "models/fulfillment/staging/stg_fulfillment_0.sql": [ + "SELECT", + " t.id,", + " t.amount,", + " t.status,", + " t.created_at,", + " 3 AS priority,", + " 'Ελληνικά orders' AS threshold_0_label,", + " '東京 warehouse' AS site_label", + "FROM __source(\"raw_events_0\") t", + "" + ], + "models/fulfillment/staging/stg_fulfillment_1.sql": [ + "SELECT", + " t.id,", + " t.amount,", + " t.status,", + " t.created_at", + "FROM __source(\"raw_events_0\") t", + "" + ], + "models/fulfillment/staging/stg_fulfillment_2.sql": [ + "SELECT", + " t.id,", + " t.amount,", + " t.status,", + " t.created_at,", + " ROUND(ROUND((t.amount * 2), 2), 2) AS adjust_0_amount", + "FROM __source(\"raw_events_0\") t", + "" + ], + "models/inventory/marts/inventory_summary_0.sql": [ + "SELECT", + " t.id,", + " t.amount,", + " t.status,", + " t.created_at", + "FROM __ref(\"stg_inventory_1\") t", + "WHERE t.status <> '取消'", + "" + ], + "models/inventory/marts/inventory_summary_1.sql": [ + "SELECT", + " t.id,", + " t.amount,", + " t.status,", + " t.created_at", + "FROM __ref(\"stg_inventory_2\") t", + "" + ], + "models/inventory/staging/stg_inventory_0.sql": [ + "SELECT", + " t.id,", + " t.amount,", + " t.status,", + " t.created_at,", + " '東京 warehouse' AS site_label", + "FROM __source(\"raw_events_0\") t", + "" + ], + "models/inventory/staging/stg_inventory_1.sql": [ + "SELECT", + " t.id,", + " t.amount,", + " t.status,", + " t.created_at", + "FROM __source(\"raw_events_0\") t", + "" + ], + "models/inventory/staging/stg_inventory_2.sql": [ + "SELECT", + " t.id,", + " t.amount,", + " t.status,", + " t.created_at,", + " ROUND(ROUND((t.amount * 2), 2), 2) AS adjust_0_amount,", + " '東京 warehouse' AS site_label", + "FROM __source(\"raw_events_0\") t", + "" + ], + "models/rendering/large_events.sql": [ + "SELECT id, amount", + "FROM __table_fn(\"table_fn__events_above\")(4)", + "" + ], + "models/rendering/stated_stg_inventory_2.sql": [ + "SELECT CAST(id AS INTEGER) AS id, status,", + " CASE WHEN amount >= 9 THEN 'HIGH'", + " ELSE 'LOW' END AS tier", + "FROM __ref(\"stg_inventory_2\")", + "" + ], + "models/sales/marts/sales_summary_0.sql": [ + "SELECT", + " t.id,", + " t.amount,", + " t.status,", + " t.created_at,", + " ROUND(ROUND((t.amount * 2), 2), 2) AS adjust_0_amount", + "FROM __ref(\"stg_sales_0\") t", + "" + ], + "models/sales/marts/sales_summary_1.sql": [ + "SELECT", + " t.id,", + " t.amount,", + " t.status,", + " t.created_at", + "FROM __ref(\"stg_sales_0\") t", + "LEFT JOIN __ref(\"stg_inventory_1\") j1 ON t.id = j1.id", + "" + ], + "models/sales/staging/stg_sales_0.sql": [ + "SELECT", + " t.id,", + " t.amount,", + " t.status,", + " t.created_at,", + " '東京 warehouse' AS site_label", + "FROM __source(\"raw_events_0\") t", + "" + ], + "tests/macro doubles_amounts/doubles_amounts.sql": [ + "WITH input_values AS (SELECT 4 AS amount),", + "__actual__macro_doubles_amounts AS (SELECT (amount * 2) AS doubled", + " FROM input_values),", + "__expected__macro_doubles_amounts AS (SELECT 8 AS doubled)", + "SELECT 0 AS step_index, 'macro doubles_amounts' AS model_name, (SELECT COUNT(*) FROM __actual__macro_doubles_amounts) AS actual_count, (SELECT COUNT(*) FROM __expected__macro_doubles_amounts) AS expected_count, (SELECT COUNT(*) FROM (SELECT * FROM __actual__macro_doubles_amounts EXCEPT SELECT * FROM __expected__macro_doubles_amounts) AS __sqlbuild_mismatch) AS unexpected_count, (SELECT COUNT(*) FROM (SELECT * FROM __expected__macro_doubles_amounts EXCEPT SELECT * FROM __actual__macro_doubles_amounts) AS __sqlbuild_missing) AS missing_count", + "" + ], + "tests/macro taxes_amounts/taxes_amounts.sql": [ + "WITH input_values AS (SELECT 10 AS amount),", + "__actual__macro_taxes_amounts AS (SELECT ROUND(amount * 1.2, 2) AS taxed", + " FROM input_values),", + "__expected__macro_taxes_amounts AS (SELECT 12.0 AS taxed)", + "SELECT 0 AS step_index, 'macro taxes_amounts' AS model_name, (SELECT COUNT(*) FROM __actual__macro_taxes_amounts) AS actual_count, (SELECT COUNT(*) FROM __expected__macro_taxes_amounts) AS expected_count, (SELECT COUNT(*) FROM (SELECT * FROM __actual__macro_taxes_amounts EXCEPT SELECT * FROM __expected__macro_taxes_amounts) AS __sqlbuild_mismatch) AS unexpected_count, (SELECT COUNT(*) FROM (SELECT * FROM __expected__macro_taxes_amounts EXCEPT SELECT * FROM __actual__macro_taxes_amounts) AS __sqlbuild_missing) AS missing_count", + "" + ], + "tests/regional_stg_fulfillment_0/test_regional_stg_fulfillment_0.sql": [ + "WITH __ref__stg_fulfillment_0 AS (SELECT 1 AS id, CAST(4 AS DOUBLE) AS amount, 'placed' AS status,", + " TIMESTAMP '2026-01-01 00:00:00' AS created_at),", + "__ref__regional_stg_fulfillment_0 AS (SELECT id, amount, 'west' AS region", + "FROM __ref__stg_fulfillment_0),", + "__actual__regional_stg_fulfillment_0 AS (SELECT * FROM __ref__regional_stg_fulfillment_0),", + "__expected__regional_stg_fulfillment_0 AS (SELECT 1 AS id, CAST(4 AS DOUBLE) AS amount, 'west' AS region)", + "SELECT 0 AS step_index, 'regional_stg_fulfillment_0' AS model_name, (SELECT COUNT(*) FROM __actual__regional_stg_fulfillment_0) AS actual_count, (SELECT COUNT(*) FROM __expected__regional_stg_fulfillment_0) AS expected_count, (SELECT COUNT(*) FROM (SELECT id, amount, region FROM __actual__regional_stg_fulfillment_0 EXCEPT SELECT id, amount, region FROM __expected__regional_stg_fulfillment_0) AS __sqlbuild_mismatch) AS unexpected_count, (SELECT COUNT(*) FROM (SELECT id, amount, region FROM __expected__regional_stg_fulfillment_0 EXCEPT SELECT id, amount, region FROM __actual__regional_stg_fulfillment_0) AS __sqlbuild_missing) AS missing_count", + "" + ], + "tests/stg_sales_0/test_stg_sales_0.sql": [ + "WITH __source__raw_events_0 AS (SELECT 1 AS id, CAST(10 AS DOUBLE) AS amount, 'placed' AS status,", + " TIMESTAMP '2026-01-01 00:00:00' AS created_at),", + "__ref__stg_sales_0 AS (SELECT", + " t.id,", + " t.amount,", + " t.status,", + " t.created_at,", + " '東京 warehouse' AS site_label", + "FROM __source__raw_events_0 t),", + "__actual__stg_sales_0 AS (SELECT * FROM __ref__stg_sales_0),", + "__expected__stg_sales_0 AS (SELECT 1 AS id, 'placed' AS status)", + "SELECT 0 AS step_index, 'stg_sales_0' AS model_name, (SELECT COUNT(*) FROM __actual__stg_sales_0) AS actual_count, (SELECT COUNT(*) FROM __expected__stg_sales_0) AS expected_count, (SELECT COUNT(*) FROM (SELECT id, status FROM __actual__stg_sales_0 EXCEPT SELECT id, status FROM __expected__stg_sales_0) AS __sqlbuild_mismatch) AS unexpected_count, (SELECT COUNT(*) FROM (SELECT id, status FROM __expected__stg_sales_0 EXCEPT SELECT id, status FROM __actual__stg_sales_0) AS __sqlbuild_missing) AS missing_count", + "" + ], + "tests/udf detects_placed_events/detects_placed_events.sql": [ + "WITH input_values AS (SELECT 'placed' AS event_status),", + "__actual__udf_detects_placed_events AS (SELECT event_status, main.is_placed(event_status) AS is_placed", + " FROM input_values),", + "__expected__udf_detects_placed_events AS (SELECT 'placed' AS event_status, TRUE AS is_placed)", + "SELECT 0 AS step_index, 'udf detects_placed_events' AS model_name, (SELECT COUNT(*) FROM __actual__udf_detects_placed_events) AS actual_count, (SELECT COUNT(*) FROM __expected__udf_detects_placed_events) AS expected_count, (SELECT COUNT(*) FROM (SELECT * FROM __actual__udf_detects_placed_events EXCEPT SELECT * FROM __expected__udf_detects_placed_events) AS __sqlbuild_mismatch) AS unexpected_count, (SELECT COUNT(*) FROM (SELECT * FROM __expected__udf_detects_placed_events EXCEPT SELECT * FROM __actual__udf_detects_placed_events) AS __sqlbuild_missing) AS missing_count", + "" + ] + }, + "manifest": { + "nodes": { + "model.generated_6.chain_1_stg_fulfillment_2": { + "database": null, + "schema": "main", + "name": "chain_1_stg_fulfillment_2", + "resource_type": "model", + "package_name": "generated_6", + "path": "models/analysis/chain_1_stg_fulfillment_2.sql", + "original_file_path": "models/analysis/chain_1_stg_fulfillment_2.sql", + "unique_id": "model.generated_6.chain_1_stg_fulfillment_2", + "fqn": [ + "generated_6", + "models", + "analysis", + "chain_1_stg_fulfillment_2" + ], + "alias": "chain_1_stg_fulfillment_2", + "config": { + "enabled": true, + "alias": null, + "schema": "main", + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "table", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Level 1 below the contracted copy of stg_fulfillment_2", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.chain_1_stg_fulfillment_2", + "depends_on": { + "macros": [], + "nodes": [ + "model.generated_6.contracted_stg_fulfillment_2" + ] + } + }, + "model.generated_6.chain_2_stg_fulfillment_2": { + "database": null, + "schema": "main", + "name": "chain_2_stg_fulfillment_2", + "resource_type": "model", + "package_name": "generated_6", + "path": "models/analysis/chain_2_stg_fulfillment_2.sql", + "original_file_path": "models/analysis/chain_2_stg_fulfillment_2.sql", + "unique_id": "model.generated_6.chain_2_stg_fulfillment_2", + "fqn": [ + "generated_6", + "models", + "analysis", + "chain_2_stg_fulfillment_2" + ], + "alias": "chain_2_stg_fulfillment_2", + "config": { + "enabled": true, + "alias": null, + "schema": "main", + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "table", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Level 2 below the contracted copy of stg_fulfillment_2", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.chain_2_stg_fulfillment_2", + "depends_on": { + "macros": [], + "nodes": [ + "model.generated_6.chain_1_stg_fulfillment_2" + ] + } + }, + "model.generated_6.chain_3_stg_fulfillment_2": { + "database": null, + "schema": "main", + "name": "chain_3_stg_fulfillment_2", + "resource_type": "model", + "package_name": "generated_6", + "path": "models/analysis/chain_3_stg_fulfillment_2.sql", + "original_file_path": "models/analysis/chain_3_stg_fulfillment_2.sql", + "unique_id": "model.generated_6.chain_3_stg_fulfillment_2", + "fqn": [ + "generated_6", + "models", + "analysis", + "chain_3_stg_fulfillment_2" + ], + "alias": "chain_3_stg_fulfillment_2", + "config": { + "enabled": true, + "alias": null, + "schema": "main", + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "table", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Level 3 below the contracted copy of stg_fulfillment_2", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.chain_3_stg_fulfillment_2", + "depends_on": { + "macros": [], + "nodes": [ + "model.generated_6.chain_2_stg_fulfillment_2" + ] + } + }, + "model.generated_6.checked_stg_fulfillment_2": { + "database": null, + "schema": "main", + "name": "checked_stg_fulfillment_2", + "resource_type": "model", + "package_name": "generated_6", + "path": "models/analysis/checked_stg_fulfillment_2.sql", + "original_file_path": "models/analysis/checked_stg_fulfillment_2.sql", + "unique_id": "model.generated_6.checked_stg_fulfillment_2", + "fqn": [ + "generated_6", + "models", + "analysis", + "checked_stg_fulfillment_2" + ], + "alias": "checked_stg_fulfillment_2", + "config": { + "enabled": true, + "alias": null, + "schema": "main", + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "table", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": [ + "id" + ], + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Metadata-checked copy of stg_fulfillment_2", + "columns": { + "id": { + "name": "id", + "description": "", + "meta": {}, + "data_type": null, + "constraints": [], + "quote": null, + "tags": [] + }, + "status": { + "name": "status", + "description": "", + "meta": {}, + "data_type": null, + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.checked_stg_fulfillment_2", + "depends_on": { + "macros": [], + "nodes": [ + "model.generated_6.stg_fulfillment_2" + ] + } + }, + "model.generated_6.contracted_stg_fulfillment_2": { + "database": null, + "schema": "main", + "name": "contracted_stg_fulfillment_2", + "resource_type": "model", + "package_name": "generated_6", + "path": "models/analysis/contracted_stg_fulfillment_2.sql", + "original_file_path": "models/analysis/contracted_stg_fulfillment_2.sql", + "unique_id": "model.generated_6.contracted_stg_fulfillment_2", + "fqn": [ + "generated_6", + "models", + "analysis", + "contracted_stg_fulfillment_2" + ], + "alias": "contracted_stg_fulfillment_2", + "config": { + "enabled": true, + "alias": null, + "schema": "main", + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "table", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Contracted copy of raw_events_0", + "columns": { + "id": { + "name": "id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "label": { + "name": "label", + "description": "", + "meta": {}, + "data_type": "VARCHAR(18)", + "constraints": [], + "quote": null, + "tags": [] + }, + "amount": { + "name": "amount", + "description": "", + "meta": {}, + "data_type": "DOUBLE", + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.contracted_stg_fulfillment_2", + "depends_on": { + "macros": [], + "nodes": [ + "source.generated_6.raw_events_0" + ] + } + }, + "model.generated_6.regional_stg_fulfillment_0": { + "database": null, + "schema": "main", + "name": "regional_stg_fulfillment_0", + "resource_type": "model", + "package_name": "generated_6", + "path": "models/analysis/regional_stg_fulfillment_0.sql", + "original_file_path": "models/analysis/regional_stg_fulfillment_0.sql", + "unique_id": "model.generated_6.regional_stg_fulfillment_0", + "fqn": [ + "generated_6", + "models", + "analysis", + "regional_stg_fulfillment_0" + ], + "alias": "regional_stg_fulfillment_0", + "config": { + "enabled": true, + "alias": null, + "schema": "main", + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "table", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Regional copy of stg_fulfillment_0", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.regional_stg_fulfillment_0", + "depends_on": { + "macros": [], + "nodes": [ + "model.generated_6.stg_fulfillment_0" + ] + } + }, + "model.generated_6.audited_stg_fulfillment_1": { + "database": null, + "schema": "main", + "name": "audited_stg_fulfillment_1", + "resource_type": "model", + "package_name": "generated_6", + "path": "models/features/audited_stg_fulfillment_1.sql", + "original_file_path": "models/features/audited_stg_fulfillment_1.sql", + "unique_id": "model.generated_6.audited_stg_fulfillment_1", + "fqn": [ + "generated_6", + "models", + "features", + "audited_stg_fulfillment_1" + ], + "alias": "audited_stg_fulfillment_1", + "config": { + "enabled": true, + "alias": null, + "schema": "main", + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "table", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Factory-audited copy of stg_fulfillment_1", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.audited_stg_fulfillment_1", + "depends_on": { + "macros": [], + "nodes": [ + "model.generated_6.stg_fulfillment_1" + ] + } + }, + "model.generated_6.batched_rolling_window_stg_sales_0": { + "database": null, + "schema": "main", + "name": "batched_rolling_window_stg_sales_0", + "resource_type": "model", + "package_name": "generated_6", + "path": "models/features/batched_rolling_window_stg_sales_0.sql", + "original_file_path": "models/features/batched_rolling_window_stg_sales_0.sql", + "unique_id": "model.generated_6.batched_rolling_window_stg_sales_0", + "fqn": [ + "generated_6", + "models", + "features", + "batched_rolling_window_stg_sales_0" + ], + "alias": "batched_rolling_window_stg_sales_0", + "config": { + "enabled": true, + "alias": null, + "schema": "main", + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "incremental", + "incremental_strategy": "delete_insert", + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Microbatched rolling_window totals of stg_sales_0", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.batched_rolling_window_stg_sales_0", + "depends_on": { + "macros": [], + "nodes": [ + "model.generated_6.stg_sales_0" + ] + } + }, + "model.generated_6.check_history_stg_inventory_1": { + "database": null, + "schema": "main", + "name": "check_history_stg_inventory_1", + "resource_type": "model", + "package_name": "generated_6", + "path": "models/features/check_history_stg_inventory_1.sql", + "original_file_path": "models/features/check_history_stg_inventory_1.sql", + "unique_id": "model.generated_6.check_history_stg_inventory_1", + "fqn": [ + "generated_6", + "models", + "features", + "check_history_stg_inventory_1" + ], + "alias": "check_history_stg_inventory_1", + "config": { + "enabled": true, + "alias": null, + "schema": "main", + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "snapshot", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": [ + "id" + ], + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Check history of stg_inventory_1", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.check_history_stg_inventory_1", + "depends_on": { + "macros": [], + "nodes": [ + "model.generated_6.stg_inventory_1" + ] + } + }, + "model.generated_6.copied_stg_fulfillment_2": { + "database": null, + "schema": "main", + "name": "copied_stg_fulfillment_2", + "resource_type": "model", + "package_name": "generated_6", + "path": "models/features/copied_stg_fulfillment_2.sql", + "original_file_path": "models/features/copied_stg_fulfillment_2.sql", + "unique_id": "model.generated_6.copied_stg_fulfillment_2", + "fqn": [ + "generated_6", + "models", + "features", + "copied_stg_fulfillment_2" + ], + "alias": "copied_stg_fulfillment_2", + "config": { + "enabled": true, + "alias": null, + "schema": "main", + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "copy_table", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Custom copy of stg_fulfillment_2", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.copied_stg_fulfillment_2", + "depends_on": { + "macros": [], + "nodes": [ + "model.generated_6.stg_fulfillment_2" + ] + } + }, + "model.generated_6.flagged_stg_sales_0": { + "database": null, + "schema": "main", + "name": "flagged_stg_sales_0", + "resource_type": "model", + "package_name": "generated_6", + "path": "models/features/flagged_stg_sales_0.sql", + "original_file_path": "models/features/flagged_stg_sales_0.sql", + "unique_id": "model.generated_6.flagged_stg_sales_0", + "fqn": [ + "generated_6", + "models", + "features", + "flagged_stg_sales_0" + ], + "alias": "flagged_stg_sales_0", + "config": { + "enabled": true, + "alias": null, + "schema": "main", + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "table", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Function-flagged copy of stg_sales_0", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.flagged_stg_sales_0", + "depends_on": { + "macros": [], + "nodes": [ + "model.generated_6.is_placed", + "model.generated_6.is_large", + "model.generated_6.stg_sales_0" + ] + } + }, + "model.generated_6.hooked_stg_inventory_1": { + "database": null, + "schema": "main", + "name": "hooked_stg_inventory_1", + "resource_type": "model", + "package_name": "generated_6", + "path": "models/features/hooked_stg_inventory_1.sql", + "original_file_path": "models/features/hooked_stg_inventory_1.sql", + "unique_id": "model.generated_6.hooked_stg_inventory_1", + "fqn": [ + "generated_6", + "models", + "features", + "hooked_stg_inventory_1" + ], + "alias": "hooked_stg_inventory_1", + "config": { + "enabled": true, + "alias": null, + "schema": "main", + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "table", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Hooked copy of stg_inventory_1", + "columns": {}, + "meta": { + "sqlbuild": { + "lifecycle_hooks": { + "post_hooks": [ + { + "type": "python", + "name": "note_refresh", + "kwargs": { + "channel": "café ☕ counter" + }, + "relative_path": "models/features/_sqlbuild/_hooks/python/notify.py", + "definition_hash": "55440ff19f0c4f231ad57d453227a3c9d584b81234ee4b3338a39ccdf59814a8", + "version_hash": "03b4f786f8ab2736e5be2e4de5016bd16ea44d84beca000ec2a53a397e503eb6" + } + ] + } + } + }, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.hooked_stg_inventory_1", + "depends_on": { + "macros": [], + "nodes": [ + "model.generated_6.stg_inventory_1" + ] + } + }, + "model.generated_6.merge_stg_inventory_0": { + "database": null, + "schema": "main", + "name": "merge_stg_inventory_0", + "resource_type": "model", + "package_name": "generated_6", + "path": "models/features/merge_stg_inventory_0.sql", + "original_file_path": "models/features/merge_stg_inventory_0.sql", + "unique_id": "model.generated_6.merge_stg_inventory_0", + "fqn": [ + "generated_6", + "models", + "features", + "merge_stg_inventory_0" + ], + "alias": "merge_stg_inventory_0", + "config": { + "enabled": true, + "alias": null, + "schema": "main", + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "incremental", + "incremental_strategy": "merge", + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": [ + "id" + ], + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Incremental merge copy of stg_inventory_0", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.merge_stg_inventory_0", + "depends_on": { + "macros": [], + "nodes": [ + "model.generated_6.stg_inventory_0" + ] + } + }, + "model.generated_6.shaped_stg_inventory_2": { + "database": null, + "schema": "main", + "name": "shaped_stg_inventory_2", + "resource_type": "model", + "package_name": "generated_6", + "path": "models/features/shaped_stg_inventory_2.sql", + "original_file_path": "models/features/shaped_stg_inventory_2.sql", + "unique_id": "model.generated_6.shaped_stg_inventory_2", + "fqn": [ + "generated_6", + "models", + "features", + "shaped_stg_inventory_2" + ], + "alias": "shaped_stg_inventory_2", + "config": { + "enabled": true, + "alias": null, + "schema": "main", + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "table", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Schema-shaped copy of stg_inventory_2", + "columns": { + "id": { + "name": "id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "status": { + "name": "status", + "description": "", + "meta": {}, + "data_type": "VARCHAR", + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.shaped_stg_inventory_2", + "depends_on": { + "macros": [], + "nodes": [ + "model.generated_6.stg_inventory_2" + ] + } + }, + "model.generated_6.spaced_stg_fulfillment_0": { + "database": null, + "schema": "main", + "name": "spaced_stg_fulfillment_0", + "resource_type": "model", + "package_name": "generated_6", + "path": "models/features/spaced_stg_fulfillment_0.sql", + "original_file_path": "models/features/spaced_stg_fulfillment_0.sql", + "unique_id": "model.generated_6.spaced_stg_fulfillment_0", + "fqn": [ + "generated_6", + "models", + "features", + "spaced_stg_fulfillment_0" + ], + "alias": "spaced_stg_fulfillment_0", + "config": { + "enabled": true, + "alias": null, + "schema": "main", + "database": null, + "tags": [ + "línea", + "東京" + ], + "meta": {}, + "group": null, + "materialized": "table", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [ + "línea", + "東京" + ], + "description": "Copy of stg_fulfillment_0 with 東京 warehouse", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.spaced_stg_fulfillment_0", + "depends_on": { + "macros": [], + "nodes": [ + "model.generated_6.stg_fulfillment_0" + ] + } + }, + "model.generated_6.taxed_stg_inventory_0": { + "database": null, + "schema": "main", + "name": "taxed_stg_inventory_0", + "resource_type": "model", + "package_name": "generated_6", + "path": "models/features/taxed_stg_inventory_0.sql", + "original_file_path": "models/features/taxed_stg_inventory_0.sql", + "unique_id": "model.generated_6.taxed_stg_inventory_0", + "fqn": [ + "generated_6", + "models", + "features", + "taxed_stg_inventory_0" + ], + "alias": "taxed_stg_inventory_0", + "config": { + "enabled": true, + "alias": null, + "schema": "main", + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "table", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Taxed copy of stg_inventory_0", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.taxed_stg_inventory_0", + "depends_on": { + "macros": [], + "nodes": [ + "model.generated_6.stg_inventory_0" + ] + } + }, + "model.generated_6.fulfillment_summary_0": { + "database": null, + "schema": "main", + "name": "fulfillment_summary_0", + "resource_type": "model", + "package_name": "generated_6", + "path": "models/fulfillment/marts/fulfillment_summary_0.sql", + "original_file_path": "models/fulfillment/marts/fulfillment_summary_0.sql", + "unique_id": "model.generated_6.fulfillment_summary_0", + "fqn": [ + "generated_6", + "models", + "fulfillment", + "marts", + "fulfillment_summary_0" + ], + "alias": "fulfillment_summary_0", + "config": { + "enabled": true, + "alias": null, + "schema": "main", + "database": null, + "tags": [ + "fulfillment", + "marts" + ], + "meta": {}, + "group": null, + "materialized": "table", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [ + { + "sql": "SELECT 'main.fulfillment_summary_0' AS refreshed_relation, 'Ελληνικά orders' AS refresh_label", + "transaction": true, + "index": 0 + }, + { + "sql": "SELECT 'main.fulfillment_summary_0' AS refreshed", + "transaction": true, + "index": 1 + } + ], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [ + "fulfillment", + "marts" + ], + "description": "Summary of fulfillment activity 0", + "columns": { + "id": { + "name": "id", + "description": "", + "meta": {}, + "data_type": null, + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": { + "sqlbuild": { + "lifecycle_hooks": { + "post_hooks": [ + { + "type": "sql", + "statement": "SELECT 'main.fulfillment_summary_0' AS refreshed_relation, 'Ελληνικά orders' AS refresh_label", + "name": "record_refresh", + "relative_path": "hooks/sql/record_refresh.sql", + "definition_sql": "SELECT @'relation' AS refreshed_relation, @'label' AS refresh_label", + "kwargs": { + "relation": "@@CTX:destination.qualified", + "label": "Ελληνικά orders" + }, + "description": "Record a refresh for one relation" + }, + { + "type": "sql", + "statement": "SELECT 'main.fulfillment_summary_0' AS refreshed" + } + ] + } + } + }, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.fulfillment_summary_0", + "depends_on": { + "macros": [], + "nodes": [ + "model.generated_6.stg_fulfillment_0" + ] + } + }, + "model.generated_6.stg_fulfillment_0": { + "database": null, + "schema": "main", + "name": "stg_fulfillment_0", + "resource_type": "model", + "package_name": "generated_6", + "path": "models/fulfillment/staging/stg_fulfillment_0.sql", + "original_file_path": "models/fulfillment/staging/stg_fulfillment_0.sql", + "unique_id": "model.generated_6.stg_fulfillment_0", + "fqn": [ + "generated_6", + "models", + "fulfillment", + "staging", + "stg_fulfillment_0" + ], + "alias": "stg_fulfillment_0", + "config": { + "enabled": true, + "alias": null, + "schema": "main", + "database": null, + "tags": [ + "staging" + ], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [ + "staging" + ], + "description": "Staged fulfillment events 0", + "columns": { + "id": { + "name": "id", + "description": "", + "meta": {}, + "data_type": null, + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.stg_fulfillment_0", + "depends_on": { + "macros": [], + "nodes": [ + "source.generated_6.raw_events_0" + ] + } + }, + "model.generated_6.stg_fulfillment_1": { + "database": null, + "schema": "main", + "name": "stg_fulfillment_1", + "resource_type": "model", + "package_name": "generated_6", + "path": "models/fulfillment/staging/stg_fulfillment_1.sql", + "original_file_path": "models/fulfillment/staging/stg_fulfillment_1.sql", + "unique_id": "model.generated_6.stg_fulfillment_1", + "fqn": [ + "generated_6", + "models", + "fulfillment", + "staging", + "stg_fulfillment_1" + ], + "alias": "stg_fulfillment_1", + "config": { + "enabled": true, + "alias": null, + "schema": "main", + "database": null, + "tags": [ + "staging" + ], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [ + "staging" + ], + "description": "Staged fulfillment events 1 (東京 warehouse)", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.stg_fulfillment_1", + "depends_on": { + "macros": [], + "nodes": [ + "source.generated_6.raw_events_0" + ] + } + }, + "model.generated_6.stg_fulfillment_2": { + "database": null, + "schema": "main", + "name": "stg_fulfillment_2", + "resource_type": "model", + "package_name": "generated_6", + "path": "models/fulfillment/staging/stg_fulfillment_2.sql", + "original_file_path": "models/fulfillment/staging/stg_fulfillment_2.sql", + "unique_id": "model.generated_6.stg_fulfillment_2", + "fqn": [ + "generated_6", + "models", + "fulfillment", + "staging", + "stg_fulfillment_2" + ], + "alias": "stg_fulfillment_2", + "config": { + "enabled": true, + "alias": null, + "schema": "main", + "database": null, + "tags": [ + "staging" + ], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [ + "staging" + ], + "description": "Staged fulfillment events 2", + "columns": { + "id": { + "name": "id", + "description": "", + "meta": {}, + "data_type": null, + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.stg_fulfillment_2", + "depends_on": { + "macros": [], + "nodes": [ + "source.generated_6.raw_events_0" + ] + } + }, + "model.generated_6.inventory_summary_0": { + "database": null, + "schema": "main", + "name": "inventory_summary_0", + "resource_type": "model", + "package_name": "generated_6", + "path": "models/inventory/marts/inventory_summary_0.sql", + "original_file_path": "models/inventory/marts/inventory_summary_0.sql", + "unique_id": "model.generated_6.inventory_summary_0", + "fqn": [ + "generated_6", + "models", + "inventory", + "marts", + "inventory_summary_0" + ], + "alias": "inventory_summary_0", + "config": { + "enabled": true, + "alias": null, + "schema": "main", + "database": null, + "tags": [ + "inventory", + "marts" + ], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [ + { + "sql": "SELECT 'main.inventory_summary_0' AS refreshed_relation, 'naïve résumé' AS refresh_label", + "transaction": true, + "index": 0 + }, + { + "sql": "SELECT 'main.inventory_summary_0' AS refreshed", + "transaction": true, + "index": 1 + } + ], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [ + "inventory", + "marts" + ], + "description": "Summary of inventory activity 0", + "columns": {}, + "meta": { + "sqlbuild": { + "lifecycle_hooks": { + "post_hooks": [ + { + "type": "sql", + "statement": "SELECT 'main.inventory_summary_0' AS refreshed_relation, 'naïve résumé' AS refresh_label", + "name": "record_refresh", + "relative_path": "hooks/sql/record_refresh.sql", + "definition_sql": "SELECT @'relation' AS refreshed_relation, @'label' AS refresh_label", + "kwargs": { + "relation": "@@CTX:destination.qualified", + "label": "naïve résumé" + }, + "description": "Record a refresh for one relation" + }, + { + "type": "sql", + "statement": "SELECT 'main.inventory_summary_0' AS refreshed" + } + ] + } + } + }, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.inventory_summary_0", + "depends_on": { + "macros": [], + "nodes": [ + "model.generated_6.stg_inventory_1" + ] + } + }, + "model.generated_6.inventory_summary_1": { + "database": null, + "schema": "main", + "name": "inventory_summary_1", + "resource_type": "model", + "package_name": "generated_6", + "path": "models/inventory/marts/inventory_summary_1.sql", + "original_file_path": "models/inventory/marts/inventory_summary_1.sql", + "unique_id": "model.generated_6.inventory_summary_1", + "fqn": [ + "generated_6", + "models", + "inventory", + "marts", + "inventory_summary_1" + ], + "alias": "inventory_summary_1", + "config": { + "enabled": true, + "alias": null, + "schema": "main", + "database": null, + "tags": [ + "inventory", + "marts" + ], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [ + { + "sql": "SELECT 'main.inventory_summary_1' AS refreshed_relation, 'café ☕ counter' AS refresh_label", + "transaction": true, + "index": 0 + }, + { + "sql": "SELECT 'main.inventory_summary_1' AS refreshed", + "transaction": true, + "index": 1 + } + ], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [ + "inventory", + "marts" + ], + "description": "Summary of inventory activity 1", + "columns": {}, + "meta": { + "sqlbuild": { + "lifecycle_hooks": { + "post_hooks": [ + { + "type": "sql", + "statement": "SELECT 'main.inventory_summary_1' AS refreshed_relation, 'café ☕ counter' AS refresh_label", + "name": "record_refresh", + "relative_path": "hooks/sql/record_refresh.sql", + "definition_sql": "SELECT @'relation' AS refreshed_relation, @'label' AS refresh_label", + "kwargs": { + "relation": "@@CTX:destination.qualified", + "label": "café ☕ counter" + }, + "description": "Record a refresh for one relation" + }, + { + "type": "sql", + "statement": "SELECT 'main.inventory_summary_1' AS refreshed" + } + ] + } + } + }, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.inventory_summary_1", + "depends_on": { + "macros": [], + "nodes": [ + "model.generated_6.stg_inventory_2" + ] + } + }, + "model.generated_6.stg_inventory_0": { + "database": null, + "schema": "main", + "name": "stg_inventory_0", + "resource_type": "model", + "package_name": "generated_6", + "path": "models/inventory/staging/stg_inventory_0.sql", + "original_file_path": "models/inventory/staging/stg_inventory_0.sql", + "unique_id": "model.generated_6.stg_inventory_0", + "fqn": [ + "generated_6", + "models", + "inventory", + "staging", + "stg_inventory_0" + ], + "alias": "stg_inventory_0", + "config": { + "enabled": true, + "alias": null, + "schema": "main", + "database": null, + "tags": [ + "staging" + ], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [ + "staging" + ], + "description": "Staged inventory events 0 (Ελληνικά orders)", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.stg_inventory_0", + "depends_on": { + "macros": [], + "nodes": [ + "source.generated_6.raw_events_0" + ] + } + }, + "model.generated_6.stg_inventory_1": { + "database": null, + "schema": "main", + "name": "stg_inventory_1", + "resource_type": "model", + "package_name": "generated_6", + "path": "models/inventory/staging/stg_inventory_1.sql", + "original_file_path": "models/inventory/staging/stg_inventory_1.sql", + "unique_id": "model.generated_6.stg_inventory_1", + "fqn": [ + "generated_6", + "models", + "inventory", + "staging", + "stg_inventory_1" + ], + "alias": "stg_inventory_1", + "config": { + "enabled": true, + "alias": null, + "schema": "main", + "database": null, + "tags": [ + "staging" + ], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [ + "staging" + ], + "description": "Staged inventory events 1", + "columns": { + "id": { + "name": "id", + "description": "", + "meta": {}, + "data_type": null, + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.stg_inventory_1", + "depends_on": { + "macros": [], + "nodes": [ + "source.generated_6.raw_events_0" + ] + } + }, + "model.generated_6.stg_inventory_2": { + "database": null, + "schema": "main", + "name": "stg_inventory_2", + "resource_type": "model", + "package_name": "generated_6", + "path": "models/inventory/staging/stg_inventory_2.sql", + "original_file_path": "models/inventory/staging/stg_inventory_2.sql", + "unique_id": "model.generated_6.stg_inventory_2", + "fqn": [ + "generated_6", + "models", + "inventory", + "staging", + "stg_inventory_2" + ], + "alias": "stg_inventory_2", + "config": { + "enabled": true, + "alias": null, + "schema": "main", + "database": null, + "tags": [ + "staging" + ], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [ + "staging" + ], + "description": "Staged inventory events 2", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.stg_inventory_2", + "depends_on": { + "macros": [], + "nodes": [ + "source.generated_6.raw_events_0" + ] + } + }, + "model.generated_6.large_events": { + "database": null, + "schema": "main", + "name": "large_events", + "resource_type": "model", + "package_name": "generated_6", + "path": "models/rendering/large_events.sql", + "original_file_path": "models/rendering/large_events.sql", + "unique_id": "model.generated_6.large_events", + "fqn": [ + "generated_6", + "models", + "rendering", + "large_events" + ], + "alias": "large_events", + "config": { + "enabled": true, + "alias": null, + "schema": "main", + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "table", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Events above a threshold from a table function", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.large_events", + "depends_on": { + "macros": [], + "nodes": [ + "model.generated_6.table_fn__events_above" + ] + } + }, + "model.generated_6.stated_stg_inventory_2": { + "database": null, + "schema": "main", + "name": "stated_stg_inventory_2", + "resource_type": "model", + "package_name": "generated_6", + "path": "models/rendering/stated_stg_inventory_2.sql", + "original_file_path": "models/rendering/stated_stg_inventory_2.sql", + "unique_id": "model.generated_6.stated_stg_inventory_2", + "fqn": [ + "generated_6", + "models", + "rendering", + "stated_stg_inventory_2" + ], + "alias": "stated_stg_inventory_2", + "config": { + "enabled": true, + "alias": null, + "schema": "main", + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "table", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Enum-typed copy of stg_inventory_2", + "columns": { + "id": { + "name": "id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "status": { + "name": "status", + "description": "", + "meta": {}, + "data_type": "VARCHAR", + "constraints": [], + "quote": null, + "tags": [] + }, + "tier": { + "name": "tier", + "description": "", + "meta": {}, + "data_type": "VARCHAR", + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.stated_stg_inventory_2", + "depends_on": { + "macros": [], + "nodes": [ + "model.generated_6.stg_inventory_2" + ] + } + }, + "model.generated_6.sales_summary_0": { + "database": null, + "schema": "main", + "name": "sales_summary_0", + "resource_type": "model", + "package_name": "generated_6", + "path": "models/sales/marts/sales_summary_0.sql", + "original_file_path": "models/sales/marts/sales_summary_0.sql", + "unique_id": "model.generated_6.sales_summary_0", + "fqn": [ + "generated_6", + "models", + "sales", + "marts", + "sales_summary_0" + ], + "alias": "sales_summary_0", + "config": { + "enabled": true, + "alias": null, + "schema": "main", + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "table", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [ + { + "sql": "SELECT 'main.sales_summary_0' AS refreshed_relation, '東京 warehouse' AS refresh_label", + "transaction": true, + "index": 0 + }, + { + "sql": "SELECT 'main.sales_summary_0' AS refreshed", + "transaction": true, + "index": 1 + } + ], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Summary of sales activity 0", + "columns": { + "id": { + "name": "id", + "description": "", + "meta": {}, + "data_type": null, + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": { + "sqlbuild": { + "lifecycle_hooks": { + "post_hooks": [ + { + "type": "sql", + "statement": "SELECT 'main.sales_summary_0' AS refreshed_relation, '東京 warehouse' AS refresh_label", + "name": "record_refresh", + "relative_path": "hooks/sql/record_refresh.sql", + "definition_sql": "SELECT @'relation' AS refreshed_relation, @'label' AS refresh_label", + "kwargs": { + "relation": "@@CTX:destination.qualified", + "label": "東京 warehouse" + }, + "description": "Record a refresh for one relation" + }, + { + "type": "sql", + "statement": "SELECT 'main.sales_summary_0' AS refreshed" + } + ] + } + } + }, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.sales_summary_0", + "depends_on": { + "macros": [], + "nodes": [ + "model.generated_6.stg_sales_0" + ] + } + }, + "model.generated_6.sales_summary_1": { + "database": null, + "schema": "main", + "name": "sales_summary_1", + "resource_type": "model", + "package_name": "generated_6", + "path": "models/sales/marts/sales_summary_1.sql", + "original_file_path": "models/sales/marts/sales_summary_1.sql", + "unique_id": "model.generated_6.sales_summary_1", + "fqn": [ + "generated_6", + "models", + "sales", + "marts", + "sales_summary_1" + ], + "alias": "sales_summary_1", + "config": { + "enabled": true, + "alias": null, + "schema": "main", + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "table", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Summary of sales activity 1 (café ☕ counter)", + "columns": { + "id": { + "name": "id", + "description": "", + "meta": {}, + "data_type": null, + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.sales_summary_1", + "depends_on": { + "macros": [], + "nodes": [ + "model.generated_6.stg_sales_0", + "model.generated_6.stg_inventory_1" + ] + } + }, + "model.generated_6.stg_sales_0": { + "database": null, + "schema": "main", + "name": "stg_sales_0", + "resource_type": "model", + "package_name": "generated_6", + "path": "models/sales/staging/stg_sales_0.sql", + "original_file_path": "models/sales/staging/stg_sales_0.sql", + "unique_id": "model.generated_6.stg_sales_0", + "fqn": [ + "generated_6", + "models", + "sales", + "staging", + "stg_sales_0" + ], + "alias": "stg_sales_0", + "config": { + "enabled": true, + "alias": null, + "schema": "main", + "database": null, + "tags": [ + "staging" + ], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [ + "staging" + ], + "description": "Staged sales events 0", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.stg_sales_0", + "depends_on": { + "macros": [], + "nodes": [ + "source.generated_6.raw_events_0" + ] + } + } + }, + "sources": { + "source.generated_6.raw_events_0": { + "database": null, + "schema": "", + "name": "raw_events_0", + "resource_type": "source", + "package_name": "generated_6", + "path": "sources/raw.yml", + "original_file_path": "sources/raw.yml", + "unique_id": "source.generated_6.raw_events_0", + "fqn": [ + "generated_6", + "raw_events_0" + ], + "source_name": "raw_events_0", + "source_description": "", + "loader": "", + "identifier": "raw_events_0", + "quoting": {}, + "loaded_at_field": null, + "freshness": null, + "external": null, + "description": "Generated event feed 0.", + "columns": { + "id": { + "name": "id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "amount": { + "name": "amount", + "description": "", + "meta": {}, + "data_type": "DOUBLE", + "constraints": [], + "quote": null, + "tags": [] + }, + "status": { + "name": "status", + "description": "", + "meta": {}, + "data_type": "VARCHAR", + "constraints": [], + "quote": null, + "tags": [] + }, + "created_at": { + "name": "created_at", + "description": "", + "meta": {}, + "data_type": "TIMESTAMP", + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": {}, + "source_meta": {}, + "tags": [], + "config": {}, + "patch_path": null, + "unrendered_config": {}, + "relation_name": null + } + }, + "macros": { + "macro.generated_6.adjust_0": { + "name": "adjust_0", + "resource_type": "macro", + "package_name": "generated_6", + "path": "macros/adjust_0.py", + "original_file_path": "macros/adjust_0.py", + "unique_id": "macro.generated_6.adjust_0", + "macro_sql": "def adjust_0_round(expression: str) -> str:\n \"\"\"Round an amount to two places.\"\"\"\n return f\"ROUND({expression}, 2)\"\n\n\ndef adjust_0(expression: str) -> str:\n \"\"\"Double and round an amount.\"\"\"\n return adjust_0_round(f\"({expression} * 2)\")\n", + "depends_on": { + "macros": [ + "macro.generated_6.adjust_0_round" + ] + }, + "description": "", + "meta": { + "sqlbuild_visibility": "global", + "sqlbuild_scope_path": null + }, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "arguments": [], + "supported_languages": null + }, + "macro.generated_6.adjust_0_round": { + "name": "adjust_0_round", + "resource_type": "macro", + "package_name": "generated_6", + "path": "macros/adjust_0.py", + "original_file_path": "macros/adjust_0.py", + "unique_id": "macro.generated_6.adjust_0_round", + "macro_sql": "def adjust_0_round(expression: str) -> str:\n \"\"\"Round an amount to two places.\"\"\"\n return f\"ROUND({expression}, 2)\"\n\n\ndef adjust_0(expression: str) -> str:\n \"\"\"Double and round an amount.\"\"\"\n return adjust_0_round(f\"({expression} * 2)\")\n", + "depends_on": { + "macros": [] + }, + "description": "", + "meta": { + "sqlbuild_visibility": "global", + "sqlbuild_scope_path": null + }, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "arguments": [], + "supported_languages": null + }, + "macro.generated_6.round_amount": { + "name": "round_amount", + "resource_type": "macro", + "package_name": "generated_6", + "path": "macros/rounding.py", + "original_file_path": "macros/rounding.py", + "unique_id": "macro.generated_6.round_amount", + "macro_sql": "def round_amount(expression: str, places: int = 2) -> str:\n \"\"\"Round an amount.\"\"\"\n return f\"ROUND({expression}, {places})\"\n", + "depends_on": { + "macros": [] + }, + "description": "", + "meta": { + "sqlbuild_visibility": "global", + "sqlbuild_scope_path": null + }, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "arguments": [], + "supported_languages": null + }, + "macro.generated_6.taxed": { + "name": "taxed", + "resource_type": "macro", + "package_name": "generated_6", + "path": "macros/tax.py", + "original_file_path": "macros/tax.py", + "unique_id": "macro.generated_6.taxed", + "macro_sql": "from macros.rounding import round_amount\n\n\ndef taxed(expression: str) -> str:\n \"\"\"Add tax to an amount, rounded.\"\"\"\n return round_amount(f\"{expression} * 1.2\")\n", + "depends_on": { + "macros": [ + "macro.generated_6.round_amount" + ] + }, + "description": "", + "meta": { + "sqlbuild_visibility": "global", + "sqlbuild_scope_path": null + }, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "arguments": [], + "supported_languages": null + }, + "macro.generated_6.analysis_region": { + "name": "analysis_region", + "resource_type": "macro", + "package_name": "generated_6", + "path": "models/analysis/_sqlbuild/_macros/regions.py", + "original_file_path": "models/analysis/_sqlbuild/_macros/regions.py", + "unique_id": "macro.generated_6.analysis_region", + "macro_sql": "def analysis_region() -> str:\n \"\"\"Return the region rows are assigned to.\"\"\"\n return \"'north'\"\n", + "depends_on": { + "macros": [] + }, + "description": "", + "meta": { + "sqlbuild_visibility": "local", + "sqlbuild_scope_path": "models/analysis" + }, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "arguments": [], + "supported_languages": null + }, + "macro.generated_6.adjust_1": { + "name": "adjust_1", + "resource_type": "macro", + "package_name": "generated_6", + "path": "models/fulfillment/marts/_sqlbuild/_macros/adjust_1.py", + "original_file_path": "models/fulfillment/marts/_sqlbuild/_macros/adjust_1.py", + "unique_id": "macro.generated_6.adjust_1", + "macro_sql": "import os\n\n\ndef adjust_1(expression: str) -> str:\n \"\"\"Tag an amount with the deployment region.\"\"\"\n region = os.environ.get(\"SQB_DIFFERENTIAL_REGION\", \"east\")\n return f\"({expression} + LENGTH('{region}'))\"\n", + "depends_on": { + "macros": [] + }, + "description": "", + "meta": { + "sqlbuild_visibility": "local", + "sqlbuild_scope_path": "models/fulfillment/marts" + }, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "arguments": [], + "supported_languages": null + }, + "macro.generated_6.doubled": { + "name": "doubled", + "resource_type": "macro", + "package_name": "generated_6", + "path": "tests/unit/_macros/scaling.py", + "original_file_path": "tests/unit/_macros/scaling.py", + "unique_id": "macro.generated_6.doubled", + "macro_sql": "def doubled(expression: str) -> str:\n \"\"\"Double an amount.\"\"\"\n return f\"({expression} * 2)\"\n", + "depends_on": { + "macros": [] + }, + "description": "", + "meta": { + "sqlbuild_visibility": "local", + "sqlbuild_scope_path": "tests/unit" + }, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "arguments": [], + "supported_languages": null + }, + "macro.generated_6__sqlbuild_hooks.record_refresh": { + "name": "record_refresh", + "resource_type": "macro", + "package_name": "generated_6__sqlbuild_hooks", + "path": "hooks/sql/record_refresh.sql", + "original_file_path": "hooks/sql/record_refresh.sql", + "unique_id": "macro.generated_6__sqlbuild_hooks.record_refresh", + "macro_sql": "SELECT @'relation' AS refreshed_relation, @'label' AS refresh_label", + "depends_on": { + "macros": [] + }, + "description": "Record a refresh for one relation", + "meta": { + "sqlbuild_resource_type": "sql_hook", + "sqlbuild_hook_name": "record_refresh" + }, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "arguments": [], + "supported_languages": null + } + }, + "exposures": {}, + "metrics": {}, + "groups": {}, + "selectors": {}, + "disabled": {}, + "parent_map": { + "model.generated_6.chain_1_stg_fulfillment_2": [ + "model.generated_6.contracted_stg_fulfillment_2" + ], + "model.generated_6.chain_2_stg_fulfillment_2": [ + "model.generated_6.chain_1_stg_fulfillment_2" + ], + "model.generated_6.chain_3_stg_fulfillment_2": [ + "model.generated_6.chain_2_stg_fulfillment_2" + ], + "model.generated_6.checked_stg_fulfillment_2": [ + "model.generated_6.stg_fulfillment_2" + ], + "model.generated_6.contracted_stg_fulfillment_2": [ + "source.generated_6.raw_events_0" + ], + "model.generated_6.regional_stg_fulfillment_0": [ + "model.generated_6.stg_fulfillment_0" + ], + "model.generated_6.audited_stg_fulfillment_1": [ + "model.generated_6.stg_fulfillment_1" + ], + "model.generated_6.batched_rolling_window_stg_sales_0": [ + "model.generated_6.stg_sales_0" + ], + "model.generated_6.check_history_stg_inventory_1": [ + "model.generated_6.stg_inventory_1" + ], + "model.generated_6.copied_stg_fulfillment_2": [ + "model.generated_6.stg_fulfillment_2" + ], + "model.generated_6.flagged_stg_sales_0": [ + "model.generated_6.is_placed", + "model.generated_6.is_large", + "model.generated_6.stg_sales_0" + ], + "model.generated_6.hooked_stg_inventory_1": [ + "model.generated_6.stg_inventory_1" + ], + "model.generated_6.merge_stg_inventory_0": [ + "model.generated_6.stg_inventory_0" + ], + "model.generated_6.shaped_stg_inventory_2": [ + "model.generated_6.stg_inventory_2" + ], + "model.generated_6.spaced_stg_fulfillment_0": [ + "model.generated_6.stg_fulfillment_0" + ], + "model.generated_6.taxed_stg_inventory_0": [ + "model.generated_6.stg_inventory_0" + ], + "model.generated_6.fulfillment_summary_0": [ + "model.generated_6.stg_fulfillment_0" + ], + "model.generated_6.stg_fulfillment_0": [ + "source.generated_6.raw_events_0" + ], + "model.generated_6.stg_fulfillment_1": [ + "source.generated_6.raw_events_0" + ], + "model.generated_6.stg_fulfillment_2": [ + "source.generated_6.raw_events_0" + ], + "model.generated_6.inventory_summary_0": [ + "model.generated_6.stg_inventory_1" + ], + "model.generated_6.inventory_summary_1": [ + "model.generated_6.stg_inventory_2" + ], + "model.generated_6.stg_inventory_0": [ + "source.generated_6.raw_events_0" + ], + "model.generated_6.stg_inventory_1": [ + "source.generated_6.raw_events_0" + ], + "model.generated_6.stg_inventory_2": [ + "source.generated_6.raw_events_0" + ], + "model.generated_6.large_events": [ + "model.generated_6.table_fn__events_above" + ], + "model.generated_6.stated_stg_inventory_2": [ + "model.generated_6.stg_inventory_2" + ], + "model.generated_6.sales_summary_0": [ + "model.generated_6.stg_sales_0" + ], + "model.generated_6.sales_summary_1": [ + "model.generated_6.stg_sales_0", + "model.generated_6.stg_inventory_1" + ], + "model.generated_6.stg_sales_0": [ + "source.generated_6.raw_events_0" + ], + "source.generated_6.raw_events_0": [], + "model.generated_6.is_placed": [], + "model.generated_6.table_fn__events_above": [ + "model.generated_6.stg_sales_0" + ], + "model.generated_6.is_large": [] + }, + "group_map": {}, + "docs": {}, + "saved_queries": {}, + "semantic_models": {}, + "unit_tests": {} + } +} diff --git a/tests/goldens/compiler/seed/7.json b/tests/goldens/compiler/seed/7.json new file mode 100644 index 0000000000..3d5cdcde4e --- /dev/null +++ b/tests/goldens/compiler/seed/7.json @@ -0,0 +1,48 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "P001", + "message": "Unknown macro '@not_a_macro' in '/models/inventory/marts/inventory_summary_0.sql'. Available macros: adjust_0, adjust_1" + } + ] + }, + { + "label": "compile-store-warm", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "P001", + "message": "Unknown macro '@not_a_macro' in '/models/inventory/marts/inventory_summary_0.sql'. Available macros: adjust_0, adjust_1" + } + ] + }, + { + "label": "plan", + "exit_code": 1, + "diagnostics": [ + "Project discovery START", + "Project assembly START", + "Declaration parsing START", + "Declaration parsing OK ()", + "Python discovery START", + "Python discovery OK ()", + "Project assembly OK ()", + "Project discovery OK ()", + "Compiling project...", + "error[P001]: Unknown macro '@not_a_macro' in '/models/inventory/marts/inventory_summary_0.sql'. Available macros: adjust_0, adjust_1", + "", + "" + ] + } + ], + "compiled": {}, + "manifest": null +} diff --git a/tests/goldens/compiler/seed/8.json b/tests/goldens/compiler/seed/8.json new file mode 100644 index 0000000000..cbc5d67c7d --- /dev/null +++ b/tests/goldens/compiler/seed/8.json @@ -0,0 +1,2770 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 0, + "diagnostics": [] + }, + { + "label": "compile-store-warm", + "exit_code": 0, + "diagnostics": [] + }, + { + "label": "plan", + "exit_code": 0, + "diagnostics": [] + } + ], + "compiled": { + "audits/generic/audited_stg_support_0/id_present__id.sql": [ + "SELECT id", + "FROM __ref(\"audited_stg_support_0\")", + "WHERE id IS NULL", + "" + ], + "audits/generic/checked_stg_support_0/accepted_values__status.sql": [ + "SELECT status", + "FROM __ref(\"checked_stg_support_0\")", + "WHERE status IS NOT NULL", + " AND status NOT IN ('placed', 'expédié', '取消', 'returned ✓')", + "" + ], + "audits/generic/checked_stg_support_0/expression_is_true.sql": [ + "SELECT *", + "FROM __ref(\"checked_stg_support_0\")", + "WHERE NOT (amount >= -1000)", + "" + ], + "audits/generic/checked_stg_support_0/relationships__id.sql": [ + "SELECT id", + "FROM __ref(\"checked_stg_support_0\")", + "WHERE id IS NOT NULL", + " AND id NOT IN (", + " SELECT id", + " FROM __ref(\"stg_support_0\")", + " WHERE id IS NOT NULL", + " )", + "" + ], + "audits/generic/configured_events/accepted_values__id.sql": [ + "SELECT id", + "FROM __source(\"configured_events\")", + "WHERE id IS NOT NULL", + " AND id NOT IN ('1')", + "" + ], + "audits/generic/configured_stg_support_0/accepted_values__status.sql": [ + "SELECT status", + "FROM __ref(\"configured_stg_support_0\")", + "WHERE status IS NOT NULL", + " AND status NOT IN ('placed', 'shipped')", + "" + ], + "audits/generic/configured_stg_support_0/configured_amount_floor.sql": [ + "SELECT *", + "FROM __ref(\"configured_stg_support_0\")", + "WHERE amount < -40", + "" + ], + "audits/generic/configured_stg_support_0/configured_id_present__id.sql": [ + "SELECT id", + "FROM __ref(\"configured_stg_support_0\")", + "WHERE id IS NULL", + "" + ], + "audits/generic/configured_stg_support_0/unique__id.sql": [ + "SELECT id, COUNT(*) AS duplicate_count", + "FROM __ref(\"configured_stg_support_0\")", + "WHERE id IS NOT NULL", + "GROUP BY id", + "HAVING COUNT(*) > 1", + "" + ], + "audits/generic/labelled_stg_support_0/tidy_status__status.sql": [ + "SELECT *", + "FROM __ref(\"labelled_stg_support_0\")", + "WHERE status <> LOWER(TRIM(status))", + "" + ], + "audits/generic/shaped_stg_support_0/not_null__id.sql": [ + "SELECT id", + "FROM __ref(\"shaped_stg_support_0\")", + "WHERE id IS NULL", + "" + ], + "audits/generic/stg_support_0/not_null__id.sql": [ + "SELECT id", + "FROM __ref(\"stg_support_0\")", + "WHERE id IS NULL", + "" + ], + "audits/generic/support_summary_0/not_null__id.sql": [ + "SELECT id", + "FROM __ref(\"support_summary_0\")", + "WHERE id IS NULL", + "" + ], + "audits/generic/support_summary_1/not_null__id.sql": [ + "SELECT id", + "FROM __ref(\"support_summary_1\")", + "WHERE id IS NULL", + "" + ], + "audits/singular/labelled_channels_match.sql": [ + "SELECT l.id", + "FROM __ref(\"labelled_stg_support_0\") l", + "JOIN __ref(\"channel_labels\") c ON l.id = c.id", + "WHERE l.status = c.channel", + "" + ], + "functions/sql/configured_label.sql": [ + "CREATE OR REPLACE MACRO ci_checks.configured_label(raw_status) AS (", + "UPPER(raw_status)", + ")", + "" + ], + "functions/sql/tidy_status.sql": [ + "CREATE OR REPLACE MACRO ci_checks.tidy_status(raw_status) AS (", + "LOWER(TRIM(raw_status))", + ")", + "" + ], + "models/analysis/checked_stg_support_0.sql": [ + "SELECT id, amount, status", + "FROM __ref(\"stg_support_0\")", + "" + ], + "models/analysis/pivot_input_stg_support_0.sql": [ + "SELECT CAST(id AS INTEGER) AS id, CAST(status AS VARCHAR) AS status, amount", + "FROM __ref(\"stg_support_0\")", + "" + ], + "models/analysis/pivoted_stg_support_0.sql": [ + "PIVOT __ref(\"pivot_input_stg_support_0\")", + "ON status", + "USING MAX(amount)", + "GROUP BY id", + "" + ], + "models/analysis/unanalysed_stg_support_0.sql": [ + "SELECT id, amount", + "FROM __ref(\"stg_support_0\")", + "WHERE id = ANY [1, 2]", + "" + ], + "models/features/append_stg_support_0.sql": [ + "SELECT id, amount, status, created_at", + "FROM __ref(\"stg_support_0\")", + "" + ], + "models/features/audited_stg_support_0.sql": [ + "SELECT id, amount", + "FROM __ref(\"stg_support_0\")", + "" + ], + "models/features/batched_rolling_window_stg_support_0.sql": [ + "SELECT CAST(created_at AS DATE) AS created_at, SUM(amount) AS amount", + "FROM __ref(\"stg_support_0\")", + "GROUP BY CAST(created_at AS DATE)", + "" + ], + "models/features/hooked_stg_support_0.sql": [ + "SELECT id, amount", + "FROM __ref(\"stg_support_0\")", + "" + ], + "models/features/product_labels.sql": [ + "SELECT id, label", + "FROM __source(\"loaded_products\")", + "" + ], + "models/features/shaped_stg_support_0.sql": [ + "SELECT CAST(id AS INTEGER) AS id, status", + "FROM __ref(\"stg_support_0\")", + "" + ], + "models/features/taxed_stg_support_0.sql": [ + "SELECT id, ROUND(amount * 1.2, 2) AS taxed, ROUND(amount, 1) AS rounded", + "FROM __ref(\"stg_support_0\")", + "" + ], + "models/rendering/bounded_stg_support_0.sql": [ + "SELECT id, amount, created_at", + "FROM __ref(\"stg_support_0\")", + "WHERE created_at >= __cursor_start()", + " AND created_at < __cursor_end()", + "" + ], + "models/rendering/channel_labels.sql": [ + "SELECT id, channel", + "FROM __source(\"labelled_channels\")", + "" + ], + "models/rendering/configured_stg_support_0.sql": [ + "SELECT id, amount, status", + "FROM __ref(\"stg_support_0\")", + "" + ], + "models/rendering/labelled_stg_support_0.sql": [ + "SELECT id, LOWER(TRIM(status)) AS status,", + " __udf(\"tidy_status\")(status) AS tidied", + "FROM __ref(\"stg_support_0\")", + "" + ], + "models/rendering/latest_raw_events_1.sql": [ + "SELECT id, amount", + "FROM (SELECT id, amount FROM __source(\"raw_events_1\") ORDER BY id DESC LIMIT 2) AS latest", + "" + ], + "models/rendering/unioned_event_ids.sql": [ + "SELECT id", + "FROM (SELECT id FROM __ref(\"stg_support_0\") UNION ALL SELECT id FROM __ref(\"stg_support_0\")) AS unioned", + "" + ], + "models/support/marts/support_summary_0.sql": [ + "SELECT", + " t.id,", + " t.amount,", + " t.status,", + " t.created_at,", + " t.amount + 50 AS threshold_2_amount,", + " (t.amount * (1 - 0.15)) AS adjust_0_amount,", + " (t.amount + 2 + 1) AS adjust_2_amount", + "FROM __ref(\"stg_support_0\") t", + "WHERE t.status <> 'placed'", + "" + ], + "models/support/marts/support_summary_1.sql": [ + "SELECT", + " t.id,", + " t.amount,", + " t.status,", + " t.created_at,", + " t.amount + 237 AS threshold_0_amount,", + " (t.amount * (1 - 0.15)) AS adjust_0_amount,", + " (t.amount + LENGTH('east')) AS adjust_1_amount,", + " 'naïve résumé' AS site_label", + "FROM __ref(\"stg_support_0\") t", + "" + ], + "models/support/marts/support_summary_2.sql": [ + "SELECT", + " t.id,", + " t.amount,", + " t.status,", + " t.created_at,", + " t.amount * 0.15 AS threshold_1_amount,", + " t.amount + 50 AS threshold_2_amount,", + " (t.amount + LENGTH('east')) AS adjust_1_amount,", + " (t.amount + 2 + 1) AS adjust_2_amount", + "FROM __ref(\"stg_support_0\") t", + "" + ], + "models/support/staging/stg_support_0.sql": [ + "SELECT", + " t.id,", + " t.amount,", + " t.status,", + " t.created_at,", + " t.amount + 237 AS threshold_0_amount,", + " t.amount + 50 AS threshold_2_amount,", + " (t.amount + LENGTH('east')) AS adjust_1_amount,", + " (t.amount + 2 + 1) AS adjust_2_amount", + "FROM __source(\"raw_events_0\") t", + "WHERE t.status <> 'expédié'", + "" + ], + "tests/_chain_/labelled_stg_support_0__stg_support_0/test_labelled_stg_support_0.sql": [ + "WITH __source__raw_events_0 AS (SELECT 1 AS id, CAST(1 AS DOUBLE) AS amount, 'placed' AS status, TIMESTAMP '2026-01-01 00:00:00' AS created_at UNION ALL SELECT 2 AS id, CAST(2 AS DOUBLE) AS amount, 'placed' AS status, TIMESTAMP '2026-01-02 00:00:00' AS created_at),", + "__ref__stg_support_0 AS (SELECT", + " t.id,", + " t.amount,", + " t.status,", + " t.created_at,", + " t.amount + 237 AS threshold_0_amount,", + " t.amount + 50 AS threshold_2_amount,", + " (t.amount + LENGTH('east')) AS adjust_1_amount,", + " (t.amount + 2 + 1) AS adjust_2_amount", + "FROM __source__raw_events_0 t", + "WHERE t.status <> 'expédié'),", + "__ref__labelled_stg_support_0 AS (SELECT id, LOWER(TRIM(status)) AS status,", + " ci_checks.tidy_status(status) AS tidied", + "FROM __ref__stg_support_0),", + "__actual__labelled_stg_support_0 AS (SELECT * FROM __ref__labelled_stg_support_0),", + "__expected__labelled_stg_support_0 AS (SELECT 1 AS id, LOWER(TRIM('placed')) AS status", + " UNION ALL", + " SELECT 2 AS id, 'placed' AS status)", + "SELECT 1 AS step_index, 'labelled_stg_support_0' AS model_name, (SELECT COUNT(*) FROM __actual__labelled_stg_support_0) AS actual_count, (SELECT COUNT(*) FROM __expected__labelled_stg_support_0) AS expected_count, (SELECT COUNT(*) FROM (SELECT id, status FROM __actual__labelled_stg_support_0 EXCEPT SELECT id, status FROM __expected__labelled_stg_support_0) AS __sqlbuild_mismatch) AS unexpected_count, (SELECT COUNT(*) FROM (SELECT id, status FROM __expected__labelled_stg_support_0 EXCEPT SELECT id, status FROM __actual__labelled_stg_support_0) AS __sqlbuild_missing) AS missing_count", + "" + ], + "tests/macro doubles_amounts/doubles_amounts.sql": [ + "WITH input_values AS (SELECT 4 AS amount),", + "__actual__macro_doubles_amounts AS (SELECT (amount * 2) AS doubled", + " FROM input_values),", + "__expected__macro_doubles_amounts AS (SELECT 8 AS doubled)", + "SELECT 0 AS step_index, 'macro doubles_amounts' AS model_name, (SELECT COUNT(*) FROM __actual__macro_doubles_amounts) AS actual_count, (SELECT COUNT(*) FROM __expected__macro_doubles_amounts) AS expected_count, (SELECT COUNT(*) FROM (SELECT * FROM __actual__macro_doubles_amounts EXCEPT SELECT * FROM __expected__macro_doubles_amounts) AS __sqlbuild_mismatch) AS unexpected_count, (SELECT COUNT(*) FROM (SELECT * FROM __expected__macro_doubles_amounts EXCEPT SELECT * FROM __actual__macro_doubles_amounts) AS __sqlbuild_missing) AS missing_count", + "" + ], + "tests/macro taxes_amounts/taxes_amounts.sql": [ + "WITH input_values AS (SELECT 10 AS amount),", + "__actual__macro_taxes_amounts AS (SELECT ROUND(amount * 1.2, 2) AS taxed", + " FROM input_values),", + "__expected__macro_taxes_amounts AS (SELECT 12.0 AS taxed)", + "SELECT 0 AS step_index, 'macro taxes_amounts' AS model_name, (SELECT COUNT(*) FROM __actual__macro_taxes_amounts) AS actual_count, (SELECT COUNT(*) FROM __expected__macro_taxes_amounts) AS expected_count, (SELECT COUNT(*) FROM (SELECT * FROM __actual__macro_taxes_amounts EXCEPT SELECT * FROM __expected__macro_taxes_amounts) AS __sqlbuild_mismatch) AS unexpected_count, (SELECT COUNT(*) FROM (SELECT * FROM __expected__macro_taxes_amounts EXCEPT SELECT * FROM __actual__macro_taxes_amounts) AS __sqlbuild_missing) AS missing_count", + "" + ], + "tests/stg_support_0/test_stg_support_0.sql": [ + "WITH __source__raw_events_0 AS (SELECT 1 AS id, CAST(10 AS DOUBLE) AS amount, 'placed' AS status,", + " TIMESTAMP '2026-01-01 00:00:00' AS created_at),", + "__ref__stg_support_0 AS (SELECT", + " t.id,", + " t.amount,", + " t.status,", + " t.created_at,", + " t.amount + 237 AS threshold_0_amount,", + " t.amount + 50 AS threshold_2_amount,", + " (t.amount + LENGTH('east')) AS adjust_1_amount,", + " (t.amount + 2 + 1) AS adjust_2_amount", + "FROM __source__raw_events_0 t", + "WHERE t.status <> 'expédié'),", + "__actual__stg_support_0 AS (SELECT * FROM __ref__stg_support_0),", + "__expected__stg_support_0 AS (SELECT 1 AS id, 'placed' AS status)", + "SELECT 0 AS step_index, 'stg_support_0' AS model_name, (SELECT COUNT(*) FROM __actual__stg_support_0) AS actual_count, (SELECT COUNT(*) FROM __expected__stg_support_0) AS expected_count, (SELECT COUNT(*) FROM (SELECT id, status FROM __actual__stg_support_0 EXCEPT SELECT id, status FROM __expected__stg_support_0) AS __sqlbuild_mismatch) AS unexpected_count, (SELECT COUNT(*) FROM (SELECT id, status FROM __expected__stg_support_0 EXCEPT SELECT id, status FROM __actual__stg_support_0) AS __sqlbuild_missing) AS missing_count", + "" + ], + "tests/tests/unit/test_stg_support_0_cases/block_1__labelled.sql": [ + "WITH __source__raw_events_0 AS (SELECT 1 AS id, CAST(2.25 AS DOUBLE) AS amount,", + " 'café ☕ counter' AS status,", + " TIMESTAMP '2026-01-01 00:00:00' AS created_at),", + "__ref__stg_support_0 AS (SELECT", + " t.id,", + " t.amount,", + " t.status,", + " t.created_at,", + " t.amount + 237 AS threshold_0_amount,", + " t.amount + 50 AS threshold_2_amount,", + " (t.amount + LENGTH('east')) AS adjust_1_amount,", + " (t.amount + 2 + 1) AS adjust_2_amount", + "FROM __source__raw_events_0 t", + "WHERE t.status <> 'expédié'),", + "__actual__stg_support_0 AS (SELECT * FROM __ref__stg_support_0),", + "__expected__stg_support_0 AS (SELECT 1 AS id)", + "SELECT 0 AS step_index, 'stg_support_0' AS model_name, (SELECT COUNT(*) FROM __actual__stg_support_0) AS actual_count, (SELECT COUNT(*) FROM __expected__stg_support_0) AS expected_count, (SELECT COUNT(*) FROM (SELECT id FROM __actual__stg_support_0 EXCEPT SELECT id FROM __expected__stg_support_0) AS __sqlbuild_mismatch) AS unexpected_count, (SELECT COUNT(*) FROM (SELECT id FROM __expected__stg_support_0 EXCEPT SELECT id FROM __actual__stg_support_0) AS __sqlbuild_missing) AS missing_count", + "" + ], + "tests/tests/unit/test_stg_support_0_cases/block_1__placed.sql": [ + "WITH __source__raw_events_0 AS (SELECT 1 AS id, CAST(1.5 AS DOUBLE) AS amount,", + " 'placed' AS status,", + " TIMESTAMP '2026-01-01 00:00:00' AS created_at),", + "__ref__stg_support_0 AS (SELECT", + " t.id,", + " t.amount,", + " t.status,", + " t.created_at,", + " t.amount + 237 AS threshold_0_amount,", + " t.amount + 50 AS threshold_2_amount,", + " (t.amount + LENGTH('east')) AS adjust_1_amount,", + " (t.amount + 2 + 1) AS adjust_2_amount", + "FROM __source__raw_events_0 t", + "WHERE t.status <> 'expédié'),", + "__actual__stg_support_0 AS (SELECT * FROM __ref__stg_support_0),", + "__expected__stg_support_0 AS (SELECT 1 AS id)", + "SELECT 0 AS step_index, 'stg_support_0' AS model_name, (SELECT COUNT(*) FROM __actual__stg_support_0) AS actual_count, (SELECT COUNT(*) FROM __expected__stg_support_0) AS expected_count, (SELECT COUNT(*) FROM (SELECT id FROM __actual__stg_support_0 EXCEPT SELECT id FROM __expected__stg_support_0) AS __sqlbuild_mismatch) AS unexpected_count, (SELECT COUNT(*) FROM (SELECT id FROM __expected__stg_support_0 EXCEPT SELECT id FROM __actual__stg_support_0) AS __sqlbuild_missing) AS missing_count", + "" + ] + }, + "manifest": { + "nodes": { + "model.generated_8.checked_stg_support_0": { + "database": null, + "schema": "ci_checks", + "name": "checked_stg_support_0", + "resource_type": "model", + "package_name": "generated_8", + "path": "models/analysis/checked_stg_support_0.sql", + "original_file_path": "models/analysis/checked_stg_support_0.sql", + "unique_id": "model.generated_8.checked_stg_support_0", + "fqn": [ + "generated_8", + "models", + "analysis", + "checked_stg_support_0" + ], + "alias": "checked_stg_support_0", + "config": { + "enabled": true, + "alias": null, + "schema": "ci_checks", + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "table", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": [ + "id" + ], + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Metadata-checked copy of stg_support_0", + "columns": { + "id": { + "name": "id", + "description": "", + "meta": {}, + "data_type": null, + "constraints": [], + "quote": null, + "tags": [] + }, + "status": { + "name": "status", + "description": "", + "meta": {}, + "data_type": null, + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "ci_checks.checked_stg_support_0", + "depends_on": { + "macros": [], + "nodes": [ + "model.generated_8.stg_support_0" + ] + } + }, + "model.generated_8.pivot_input_stg_support_0": { + "database": null, + "schema": "ci_checks", + "name": "pivot_input_stg_support_0", + "resource_type": "model", + "package_name": "generated_8", + "path": "models/analysis/pivot_input_stg_support_0.sql", + "original_file_path": "models/analysis/pivot_input_stg_support_0.sql", + "unique_id": "model.generated_8.pivot_input_stg_support_0", + "fqn": [ + "generated_8", + "models", + "analysis", + "pivot_input_stg_support_0" + ], + "alias": "pivot_input_stg_support_0", + "config": { + "enabled": true, + "alias": null, + "schema": "ci_checks", + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "table", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Contracted pivot input from stg_support_0", + "columns": { + "id": { + "name": "id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "status": { + "name": "status", + "description": "", + "meta": {}, + "data_type": "VARCHAR", + "constraints": [], + "quote": null, + "tags": [] + }, + "amount": { + "name": "amount", + "description": "", + "meta": {}, + "data_type": "DOUBLE", + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "ci_checks.pivot_input_stg_support_0", + "depends_on": { + "macros": [], + "nodes": [ + "model.generated_8.stg_support_0" + ] + } + }, + "model.generated_8.pivoted_stg_support_0": { + "database": null, + "schema": "ci_checks", + "name": "pivoted_stg_support_0", + "resource_type": "model", + "package_name": "generated_8", + "path": "models/analysis/pivoted_stg_support_0.sql", + "original_file_path": "models/analysis/pivoted_stg_support_0.sql", + "unique_id": "model.generated_8.pivoted_stg_support_0", + "fqn": [ + "generated_8", + "models", + "analysis", + "pivoted_stg_support_0" + ], + "alias": "pivoted_stg_support_0", + "config": { + "enabled": true, + "alias": null, + "schema": "ci_checks", + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "table", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Amounts of stg_support_0 pivoted by status", + "columns": { + "id": { + "name": "id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": { + "sqlbuild": { + "dynamic_columns": [ + { + "name": "status_amounts", + "pivot_column": "status", + "value_column": "amount", + "aggregate": "MAX", + "type": "DOUBLE", + "name_pattern": null + } + ] + } + }, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "ci_checks.pivoted_stg_support_0", + "depends_on": { + "macros": [], + "nodes": [ + "model.generated_8.pivot_input_stg_support_0" + ] + } + }, + "model.generated_8.unanalysed_stg_support_0": { + "database": null, + "schema": "ci_checks", + "name": "unanalysed_stg_support_0", + "resource_type": "model", + "package_name": "generated_8", + "path": "models/analysis/unanalysed_stg_support_0.sql", + "original_file_path": "models/analysis/unanalysed_stg_support_0.sql", + "unique_id": "model.generated_8.unanalysed_stg_support_0", + "fqn": [ + "generated_8", + "models", + "analysis", + "unanalysed_stg_support_0" + ], + "alias": "unanalysed_stg_support_0", + "config": { + "enabled": true, + "alias": null, + "schema": "ci_checks", + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Copy of stg_support_0 that SQL analysis cannot parse", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "ci_checks.unanalysed_stg_support_0", + "depends_on": { + "macros": [], + "nodes": [ + "model.generated_8.stg_support_0" + ] + } + }, + "model.generated_8.append_stg_support_0": { + "database": null, + "schema": "ci_checks", + "name": "append_stg_support_0", + "resource_type": "model", + "package_name": "generated_8", + "path": "models/features/append_stg_support_0.sql", + "original_file_path": "models/features/append_stg_support_0.sql", + "unique_id": "model.generated_8.append_stg_support_0", + "fqn": [ + "generated_8", + "models", + "features", + "append_stg_support_0" + ], + "alias": "append_stg_support_0", + "config": { + "enabled": true, + "alias": null, + "schema": "ci_checks", + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "incremental", + "incremental_strategy": "append", + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Incremental append copy of stg_support_0", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "ci_checks.append_stg_support_0", + "depends_on": { + "macros": [], + "nodes": [ + "model.generated_8.stg_support_0" + ] + } + }, + "model.generated_8.audited_stg_support_0": { + "database": null, + "schema": "ci_checks", + "name": "audited_stg_support_0", + "resource_type": "model", + "package_name": "generated_8", + "path": "models/features/audited_stg_support_0.sql", + "original_file_path": "models/features/audited_stg_support_0.sql", + "unique_id": "model.generated_8.audited_stg_support_0", + "fqn": [ + "generated_8", + "models", + "features", + "audited_stg_support_0" + ], + "alias": "audited_stg_support_0", + "config": { + "enabled": true, + "alias": null, + "schema": "ci_checks", + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Factory-audited copy of stg_support_0", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "ci_checks.audited_stg_support_0", + "depends_on": { + "macros": [], + "nodes": [ + "model.generated_8.stg_support_0" + ] + } + }, + "model.generated_8.batched_rolling_window_stg_support_0": { + "database": null, + "schema": "ci_checks", + "name": "batched_rolling_window_stg_support_0", + "resource_type": "model", + "package_name": "generated_8", + "path": "models/features/batched_rolling_window_stg_support_0.sql", + "original_file_path": "models/features/batched_rolling_window_stg_support_0.sql", + "unique_id": "model.generated_8.batched_rolling_window_stg_support_0", + "fqn": [ + "generated_8", + "models", + "features", + "batched_rolling_window_stg_support_0" + ], + "alias": "batched_rolling_window_stg_support_0", + "config": { + "enabled": true, + "alias": null, + "schema": "ci_checks", + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "incremental", + "incremental_strategy": "delete_insert", + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Microbatched rolling_window totals of stg_support_0", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "ci_checks.batched_rolling_window_stg_support_0", + "depends_on": { + "macros": [], + "nodes": [ + "model.generated_8.stg_support_0" + ] + } + }, + "model.generated_8.hooked_stg_support_0": { + "database": null, + "schema": "ci_checks", + "name": "hooked_stg_support_0", + "resource_type": "model", + "package_name": "generated_8", + "path": "models/features/hooked_stg_support_0.sql", + "original_file_path": "models/features/hooked_stg_support_0.sql", + "unique_id": "model.generated_8.hooked_stg_support_0", + "fqn": [ + "generated_8", + "models", + "features", + "hooked_stg_support_0" + ], + "alias": "hooked_stg_support_0", + "config": { + "enabled": true, + "alias": null, + "schema": "ci_checks", + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "table", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Hooked copy of stg_support_0", + "columns": {}, + "meta": { + "sqlbuild": { + "lifecycle_hooks": { + "post_hooks": [ + { + "type": "python", + "name": "note_refresh", + "kwargs": { + "channel": "東京 warehouse" + }, + "relative_path": "models/features/_sqlbuild/_hooks/python/notify.py", + "definition_hash": "55440ff19f0c4f231ad57d453227a3c9d584b81234ee4b3338a39ccdf59814a8", + "version_hash": "03b4f786f8ab2736e5be2e4de5016bd16ea44d84beca000ec2a53a397e503eb6" + } + ] + } + } + }, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "ci_checks.hooked_stg_support_0", + "depends_on": { + "macros": [], + "nodes": [ + "model.generated_8.stg_support_0" + ] + } + }, + "model.generated_8.product_labels": { + "database": null, + "schema": "ci_checks", + "name": "product_labels", + "resource_type": "model", + "package_name": "generated_8", + "path": "models/features/product_labels.sql", + "original_file_path": "models/features/product_labels.sql", + "unique_id": "model.generated_8.product_labels", + "fqn": [ + "generated_8", + "models", + "features", + "product_labels" + ], + "alias": "product_labels", + "config": { + "enabled": true, + "alias": null, + "schema": "ci_checks", + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Product labels from the loader", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "ci_checks.product_labels", + "depends_on": { + "macros": [], + "nodes": [ + "source.generated_8.loaded_products" + ] + } + }, + "model.generated_8.shaped_stg_support_0": { + "database": null, + "schema": "ci_checks", + "name": "shaped_stg_support_0", + "resource_type": "model", + "package_name": "generated_8", + "path": "models/features/shaped_stg_support_0.sql", + "original_file_path": "models/features/shaped_stg_support_0.sql", + "unique_id": "model.generated_8.shaped_stg_support_0", + "fqn": [ + "generated_8", + "models", + "features", + "shaped_stg_support_0" + ], + "alias": "shaped_stg_support_0", + "config": { + "enabled": true, + "alias": null, + "schema": "ci_checks", + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Schema-shaped copy of stg_support_0", + "columns": { + "id": { + "name": "id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "status": { + "name": "status", + "description": "", + "meta": {}, + "data_type": "VARCHAR", + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "ci_checks.shaped_stg_support_0", + "depends_on": { + "macros": [], + "nodes": [ + "model.generated_8.stg_support_0" + ] + } + }, + "model.generated_8.taxed_stg_support_0": { + "database": null, + "schema": "ci_checks", + "name": "taxed_stg_support_0", + "resource_type": "model", + "package_name": "generated_8", + "path": "models/features/taxed_stg_support_0.sql", + "original_file_path": "models/features/taxed_stg_support_0.sql", + "unique_id": "model.generated_8.taxed_stg_support_0", + "fqn": [ + "generated_8", + "models", + "features", + "taxed_stg_support_0" + ], + "alias": "taxed_stg_support_0", + "config": { + "enabled": true, + "alias": null, + "schema": "ci_checks", + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Taxed copy of stg_support_0", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "ci_checks.taxed_stg_support_0", + "depends_on": { + "macros": [], + "nodes": [ + "model.generated_8.stg_support_0" + ] + } + }, + "model.generated_8.bounded_stg_support_0": { + "database": null, + "schema": "ci_checks", + "name": "bounded_stg_support_0", + "resource_type": "model", + "package_name": "generated_8", + "path": "models/rendering/bounded_stg_support_0.sql", + "original_file_path": "models/rendering/bounded_stg_support_0.sql", + "unique_id": "model.generated_8.bounded_stg_support_0", + "fqn": [ + "generated_8", + "models", + "rendering", + "bounded_stg_support_0" + ], + "alias": "bounded_stg_support_0", + "config": { + "enabled": true, + "alias": null, + "schema": "ci_checks", + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "incremental", + "incremental_strategy": "delete_insert", + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": [ + "id" + ], + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Cursor-bounded copy of stg_support_0", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "ci_checks.bounded_stg_support_0", + "depends_on": { + "macros": [], + "nodes": [ + "model.generated_8.stg_support_0" + ] + } + }, + "model.generated_8.channel_labels": { + "database": null, + "schema": "ci_checks", + "name": "channel_labels", + "resource_type": "model", + "package_name": "generated_8", + "path": "models/rendering/channel_labels.sql", + "original_file_path": "models/rendering/channel_labels.sql", + "unique_id": "model.generated_8.channel_labels", + "fqn": [ + "generated_8", + "models", + "rendering", + "channel_labels" + ], + "alias": "channel_labels", + "config": { + "enabled": true, + "alias": null, + "schema": "ci_checks", + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Normalised channel labels", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "ci_checks.channel_labels", + "depends_on": { + "macros": [], + "nodes": [ + "source.generated_8.labelled_channels" + ] + } + }, + "model.generated_8.configured_stg_support_0": { + "database": null, + "schema": "ci_checks", + "name": "configured_stg_support_0", + "resource_type": "model", + "package_name": "generated_8", + "path": "models/rendering/configured_stg_support_0.sql", + "original_file_path": "models/rendering/configured_stg_support_0.sql", + "unique_id": "model.generated_8.configured_stg_support_0", + "fqn": [ + "generated_8", + "models", + "rendering", + "configured_stg_support_0" + ], + "alias": "configured_stg_support_0", + "config": { + "enabled": true, + "alias": null, + "schema": "ci_checks", + "database": null, + "tags": [ + "release" + ], + "meta": {}, + "group": null, + "materialized": "table", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [ + "release" + ], + "description": "Configured copy of stg_support_0 (réglé)", + "columns": { + "id": { + "name": "id", + "description": "Row key", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "amount": { + "name": "amount", + "description": "Montant réglé", + "meta": {}, + "data_type": "DOUBLE", + "constraints": [], + "quote": null, + "tags": [] + }, + "status": { + "name": "status", + "description": "", + "meta": {}, + "data_type": null, + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "ci_checks.configured_stg_support_0", + "depends_on": { + "macros": [], + "nodes": [ + "model.generated_8.stg_support_0" + ] + } + }, + "model.generated_8.labelled_stg_support_0": { + "database": null, + "schema": "ci_checks", + "name": "labelled_stg_support_0", + "resource_type": "model", + "package_name": "generated_8", + "path": "models/rendering/labelled_stg_support_0.sql", + "original_file_path": "models/rendering/labelled_stg_support_0.sql", + "unique_id": "model.generated_8.labelled_stg_support_0", + "fqn": [ + "generated_8", + "models", + "rendering", + "labelled_stg_support_0" + ], + "alias": "labelled_stg_support_0", + "config": { + "enabled": true, + "alias": null, + "schema": "ci_checks", + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Labelled copy of stg_support_0", + "columns": { + "status": { + "name": "status", + "description": "", + "meta": {}, + "data_type": null, + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "ci_checks.labelled_stg_support_0", + "depends_on": { + "macros": [], + "nodes": [ + "model.generated_8.tidy_status", + "model.generated_8.stg_support_0" + ] + } + }, + "model.generated_8.latest_raw_events_1": { + "database": null, + "schema": "ci_checks", + "name": "latest_raw_events_1", + "resource_type": "model", + "package_name": "generated_8", + "path": "models/rendering/latest_raw_events_1.sql", + "original_file_path": "models/rendering/latest_raw_events_1.sql", + "unique_id": "model.generated_8.latest_raw_events_1", + "fqn": [ + "generated_8", + "models", + "rendering", + "latest_raw_events_1" + ], + "alias": "latest_raw_events_1", + "config": { + "enabled": true, + "alias": null, + "schema": "ci_checks", + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Newest rows of raw_events_1", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "ci_checks.latest_raw_events_1", + "depends_on": { + "macros": [], + "nodes": [ + "source.generated_8.raw_events_1" + ] + } + }, + "model.generated_8.unioned_event_ids": { + "database": null, + "schema": "ci_checks", + "name": "unioned_event_ids", + "resource_type": "model", + "package_name": "generated_8", + "path": "models/rendering/unioned_event_ids.sql", + "original_file_path": "models/rendering/unioned_event_ids.sql", + "unique_id": "model.generated_8.unioned_event_ids", + "fqn": [ + "generated_8", + "models", + "rendering", + "unioned_event_ids" + ], + "alias": "unioned_event_ids", + "config": { + "enabled": true, + "alias": null, + "schema": "ci_checks", + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Ids from two staged relations", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "ci_checks.unioned_event_ids", + "depends_on": { + "macros": [], + "nodes": [ + "model.generated_8.stg_support_0" + ] + } + }, + "model.generated_8.support_summary_0": { + "database": null, + "schema": "ci_checks", + "name": "support_summary_0", + "resource_type": "model", + "package_name": "generated_8", + "path": "models/support/marts/support_summary_0.sql", + "original_file_path": "models/support/marts/support_summary_0.sql", + "unique_id": "model.generated_8.support_summary_0", + "fqn": [ + "generated_8", + "models", + "support", + "marts", + "support_summary_0" + ], + "alias": "support_summary_0", + "config": { + "enabled": true, + "alias": null, + "schema": "ci_checks", + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [ + { + "sql": "SELECT 'ci_checks.support_summary_0' AS refreshed_relation, 'naïve résumé' AS refresh_label", + "transaction": true, + "index": 0 + }, + { + "sql": "SELECT 'ci_checks.support_summary_0' AS refreshed", + "transaction": true, + "index": 1 + } + ], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Summary of support activity 0", + "columns": { + "id": { + "name": "id", + "description": "", + "meta": {}, + "data_type": null, + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": { + "sqlbuild": { + "lifecycle_hooks": { + "post_hooks": [ + { + "type": "sql", + "statement": "SELECT 'ci_checks.support_summary_0' AS refreshed_relation, 'naïve résumé' AS refresh_label", + "name": "record_refresh", + "relative_path": "models/support/marts/_sqlbuild/_hooks/sql/record_refresh.sql", + "definition_sql": "SELECT @'relation' AS refreshed_relation, @'label' AS refresh_label", + "kwargs": { + "relation": "@@CTX:destination.qualified", + "label": "naïve résumé" + }, + "description": "Record a refresh for one relation" + }, + { + "type": "sql", + "statement": "SELECT 'ci_checks.support_summary_0' AS refreshed" + } + ] + } + } + }, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "ci_checks.support_summary_0", + "depends_on": { + "macros": [], + "nodes": [ + "model.generated_8.stg_support_0" + ] + } + }, + "model.generated_8.support_summary_1": { + "database": null, + "schema": "ci_checks", + "name": "support_summary_1", + "resource_type": "model", + "package_name": "generated_8", + "path": "models/support/marts/support_summary_1.sql", + "original_file_path": "models/support/marts/support_summary_1.sql", + "unique_id": "model.generated_8.support_summary_1", + "fqn": [ + "generated_8", + "models", + "support", + "marts", + "support_summary_1" + ], + "alias": "support_summary_1", + "config": { + "enabled": true, + "alias": null, + "schema": "ci_checks", + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [ + { + "sql": "SELECT 'ci_checks.support_summary_1' AS refreshed_relation, '東京 warehouse' AS refresh_label", + "transaction": true, + "index": 0 + }, + { + "sql": "SELECT 'ci_checks.support_summary_1' AS refreshed", + "transaction": true, + "index": 1 + } + ], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Summary of support activity 1", + "columns": { + "id": { + "name": "id", + "description": "", + "meta": {}, + "data_type": null, + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": { + "sqlbuild": { + "lifecycle_hooks": { + "post_hooks": [ + { + "type": "sql", + "statement": "SELECT 'ci_checks.support_summary_1' AS refreshed_relation, '東京 warehouse' AS refresh_label", + "name": "record_refresh", + "relative_path": "models/support/marts/_sqlbuild/_hooks/sql/record_refresh.sql", + "definition_sql": "SELECT @'relation' AS refreshed_relation, @'label' AS refresh_label", + "kwargs": { + "relation": "@@CTX:destination.qualified", + "label": "東京 warehouse" + }, + "description": "Record a refresh for one relation" + }, + { + "type": "sql", + "statement": "SELECT 'ci_checks.support_summary_1' AS refreshed" + } + ] + } + } + }, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "ci_checks.support_summary_1", + "depends_on": { + "macros": [], + "nodes": [ + "model.generated_8.stg_support_0" + ] + } + }, + "model.generated_8.support_summary_2": { + "database": null, + "schema": "ci_checks", + "name": "support_summary_2", + "resource_type": "model", + "package_name": "generated_8", + "path": "models/support/marts/support_summary_2.sql", + "original_file_path": "models/support/marts/support_summary_2.sql", + "unique_id": "model.generated_8.support_summary_2", + "fqn": [ + "generated_8", + "models", + "support", + "marts", + "support_summary_2" + ], + "alias": "support_summary_2", + "config": { + "enabled": true, + "alias": null, + "schema": "ci_checks", + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [ + { + "sql": "SELECT 'ci_checks.support_summary_2' AS refreshed_relation, 'Zürich – depot' AS refresh_label", + "transaction": true, + "index": 0 + }, + { + "sql": "SELECT 'ci_checks.support_summary_2' AS refreshed", + "transaction": true, + "index": 1 + } + ], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Summary of support activity 2", + "columns": {}, + "meta": { + "sqlbuild": { + "lifecycle_hooks": { + "post_hooks": [ + { + "type": "sql", + "statement": "SELECT 'ci_checks.support_summary_2' AS refreshed_relation, 'Zürich – depot' AS refresh_label", + "name": "record_refresh", + "relative_path": "models/support/marts/_sqlbuild/_hooks/sql/record_refresh.sql", + "definition_sql": "SELECT @'relation' AS refreshed_relation, @'label' AS refresh_label", + "kwargs": { + "relation": "@@CTX:destination.qualified", + "label": "Zürich – depot" + }, + "description": "Record a refresh for one relation" + }, + { + "type": "sql", + "statement": "SELECT 'ci_checks.support_summary_2' AS refreshed" + } + ] + } + } + }, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "ci_checks.support_summary_2", + "depends_on": { + "macros": [], + "nodes": [ + "model.generated_8.stg_support_0" + ] + } + }, + "model.generated_8.stg_support_0": { + "database": null, + "schema": "ci_checks", + "name": "stg_support_0", + "resource_type": "model", + "package_name": "generated_8", + "path": "models/support/staging/stg_support_0.sql", + "original_file_path": "models/support/staging/stg_support_0.sql", + "unique_id": "model.generated_8.stg_support_0", + "fqn": [ + "generated_8", + "models", + "support", + "staging", + "stg_support_0" + ], + "alias": "stg_support_0", + "config": { + "enabled": true, + "alias": null, + "schema": "ci_checks", + "database": null, + "tags": [ + "staging" + ], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [ + "staging" + ], + "description": "Staged support events 0 (東京 warehouse)", + "columns": { + "id": { + "name": "id", + "description": "", + "meta": {}, + "data_type": null, + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "ci_checks.stg_support_0", + "depends_on": { + "macros": [], + "nodes": [ + "source.generated_8.raw_events_0" + ] + } + } + }, + "sources": { + "source.generated_8.configured_events": { + "database": null, + "schema": "", + "name": "configured_events", + "resource_type": "source", + "package_name": "generated_8", + "path": "sources/configured.yml", + "original_file_path": "sources/configured.yml", + "unique_id": "source.generated_8.configured_events", + "fqn": [ + "generated_8", + "configured_events" + ], + "source_name": "configured_events", + "source_description": "", + "loader": "", + "identifier": "configured_events", + "quoting": {}, + "loaded_at_field": null, + "freshness": null, + "external": null, + "description": "Configured feed events", + "columns": { + "id": { + "name": "id", + "description": "Key id", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "amount": { + "name": "amount", + "description": "", + "meta": {}, + "data_type": "DOUBLE", + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": { + "owner": "orders team" + }, + "source_meta": {}, + "tags": [], + "config": {}, + "patch_path": null, + "unrendered_config": {}, + "relation_name": null + }, + "source.generated_8.labelled_channels": { + "database": null, + "schema": "", + "name": "labelled_channels", + "resource_type": "source", + "package_name": "generated_8", + "path": "sources/labelled.yml", + "original_file_path": "sources/labelled.yml", + "unique_id": "source.generated_8.labelled_channels", + "fqn": [ + "generated_8", + "labelled_channels" + ], + "source_name": "labelled_channels", + "source_description": "", + "loader": "", + "identifier": "labelled_channels", + "quoting": {}, + "loaded_at_field": null, + "freshness": null, + "external": null, + "description": "Channel labels normalised in the source expression.", + "columns": { + "id": { + "name": "id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "channel": { + "name": "channel", + "description": "", + "meta": {}, + "data_type": "VARCHAR", + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": {}, + "source_meta": {}, + "tags": [], + "config": {}, + "patch_path": null, + "unrendered_config": {}, + "relation_name": null + }, + "source.generated_8.loaded_products": { + "database": null, + "schema": "ci_checks", + "name": "loaded_products", + "resource_type": "source", + "package_name": "generated_8", + "path": "sources/managed.yml", + "original_file_path": "sources/managed.yml", + "unique_id": "source.generated_8.loaded_products", + "fqn": [ + "generated_8", + "loaded_products" + ], + "source_name": "loaded_products", + "source_description": "", + "loader": "", + "identifier": "loaded_products", + "quoting": {}, + "loaded_at_field": null, + "freshness": null, + "external": null, + "description": "Products loaded by a project loader.", + "columns": { + "id": { + "name": "id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "label": { + "name": "label", + "description": "", + "meta": {}, + "data_type": "VARCHAR", + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": { + "sqlbuild": { + "loader": "loaded_products", + "auto_load": false, + "write_strategy": "table" + } + }, + "source_meta": {}, + "tags": [], + "config": {}, + "patch_path": null, + "unrendered_config": {}, + "relation_name": null + }, + "source.generated_8.raw_events_0": { + "database": null, + "schema": "", + "name": "raw_events_0", + "resource_type": "source", + "package_name": "generated_8", + "path": "sources/raw.yml", + "original_file_path": "sources/raw.yml", + "unique_id": "source.generated_8.raw_events_0", + "fqn": [ + "generated_8", + "raw_events_0" + ], + "source_name": "raw_events_0", + "source_description": "", + "loader": "", + "identifier": "raw_events_0", + "quoting": {}, + "loaded_at_field": null, + "freshness": null, + "external": null, + "description": "Generated event feed 0.", + "columns": { + "id": { + "name": "id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "amount": { + "name": "amount", + "description": "", + "meta": {}, + "data_type": "DOUBLE", + "constraints": [], + "quote": null, + "tags": [] + }, + "status": { + "name": "status", + "description": "", + "meta": {}, + "data_type": "VARCHAR", + "constraints": [], + "quote": null, + "tags": [] + }, + "created_at": { + "name": "created_at", + "description": "", + "meta": {}, + "data_type": "TIMESTAMP", + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": {}, + "source_meta": {}, + "tags": [], + "config": {}, + "patch_path": null, + "unrendered_config": {}, + "relation_name": null + }, + "source.generated_8.raw_events_1": { + "database": null, + "schema": "", + "name": "raw_events_1", + "resource_type": "source", + "package_name": "generated_8", + "path": "sources/raw.yml", + "original_file_path": "sources/raw.yml", + "unique_id": "source.generated_8.raw_events_1", + "fqn": [ + "generated_8", + "raw_events_1" + ], + "source_name": "raw_events_1", + "source_description": "", + "loader": "", + "identifier": "raw_events_1", + "quoting": {}, + "loaded_at_field": null, + "freshness": null, + "external": null, + "description": "Generated event feed 1.", + "columns": { + "id": { + "name": "id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "amount": { + "name": "amount", + "description": "", + "meta": {}, + "data_type": "DOUBLE", + "constraints": [], + "quote": null, + "tags": [] + }, + "status": { + "name": "status", + "description": "", + "meta": {}, + "data_type": "VARCHAR", + "constraints": [], + "quote": null, + "tags": [] + }, + "created_at": { + "name": "created_at", + "description": "", + "meta": {}, + "data_type": "TIMESTAMP", + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": {}, + "source_meta": {}, + "tags": [], + "config": {}, + "patch_path": null, + "unrendered_config": {}, + "relation_name": null + } + }, + "macros": { + "macro.generated_8.tidy_label": { + "name": "tidy_label", + "resource_type": "macro", + "package_name": "generated_8", + "path": "macros/labels.py", + "original_file_path": "macros/labels.py", + "unique_id": "macro.generated_8.tidy_label", + "macro_sql": "def tidy_label(expression: str) -> str:\n \"\"\"Trim and lower-case a label.\"\"\"\n return f\"LOWER(TRIM({expression}))\"\n", + "depends_on": { + "macros": [] + }, + "description": "", + "meta": { + "sqlbuild_visibility": "global", + "sqlbuild_scope_path": null + }, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "arguments": [], + "supported_languages": null + }, + "macro.generated_8.round_amount": { + "name": "round_amount", + "resource_type": "macro", + "package_name": "generated_8", + "path": "macros/rounding.py", + "original_file_path": "macros/rounding.py", + "unique_id": "macro.generated_8.round_amount", + "macro_sql": "def round_amount(expression: str, places: int = 2) -> str:\n \"\"\"Round an amount.\"\"\"\n return f\"ROUND({expression}, {places})\"\n", + "depends_on": { + "macros": [] + }, + "description": "", + "meta": { + "sqlbuild_visibility": "global", + "sqlbuild_scope_path": null + }, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "arguments": [], + "supported_languages": null + }, + "macro.generated_8.taxed": { + "name": "taxed", + "resource_type": "macro", + "package_name": "generated_8", + "path": "macros/tax.py", + "original_file_path": "macros/tax.py", + "unique_id": "macro.generated_8.taxed", + "macro_sql": "from macros.rounding import round_amount\n\n\ndef taxed(expression: str) -> str:\n \"\"\"Add tax to an amount, rounded.\"\"\"\n return round_amount(f\"{expression} * 1.2\")\n", + "depends_on": { + "macros": [ + "macro.generated_8.round_amount" + ] + }, + "description": "", + "meta": { + "sqlbuild_visibility": "global", + "sqlbuild_scope_path": null + }, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "arguments": [], + "supported_languages": null + }, + "macro.generated_8.latest_rows": { + "name": "latest_rows", + "resource_type": "macro", + "package_name": "generated_8", + "path": "models/rendering/_sqlbuild/_macros/relations.py", + "original_file_path": "models/rendering/_sqlbuild/_macros/relations.py", + "unique_id": "macro.generated_8.latest_rows", + "macro_sql": "from sqlbuild.refs import SqlResourceRef\n\n\ndef union_ids(relations: list[SqlResourceRef]) -> str:\n \"\"\"Union the ids of several relations.\"\"\"\n selects = [f\"SELECT id FROM {relation}\" for relation in relations]\n return \" UNION ALL \".join(selects)\n\n\ndef latest_rows(relation: SqlResourceRef, limit: int = 2) -> str:\n \"\"\"Return the newest rows of one relation.\"\"\"\n return f\"(SELECT id, amount FROM {relation} ORDER BY id DESC LIMIT {limit})\"\n", + "depends_on": { + "macros": [] + }, + "description": "", + "meta": { + "sqlbuild_visibility": "local", + "sqlbuild_scope_path": "models/rendering" + }, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "arguments": [], + "supported_languages": null + }, + "macro.generated_8.union_ids": { + "name": "union_ids", + "resource_type": "macro", + "package_name": "generated_8", + "path": "models/rendering/_sqlbuild/_macros/relations.py", + "original_file_path": "models/rendering/_sqlbuild/_macros/relations.py", + "unique_id": "macro.generated_8.union_ids", + "macro_sql": "from sqlbuild.refs import SqlResourceRef\n\n\ndef union_ids(relations: list[SqlResourceRef]) -> str:\n \"\"\"Union the ids of several relations.\"\"\"\n selects = [f\"SELECT id FROM {relation}\" for relation in relations]\n return \" UNION ALL \".join(selects)\n\n\ndef latest_rows(relation: SqlResourceRef, limit: int = 2) -> str:\n \"\"\"Return the newest rows of one relation.\"\"\"\n return f\"(SELECT id, amount FROM {relation} ORDER BY id DESC LIMIT {limit})\"\n", + "depends_on": { + "macros": [] + }, + "description": "", + "meta": { + "sqlbuild_visibility": "local", + "sqlbuild_scope_path": "models/rendering" + }, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "arguments": [], + "supported_languages": null + }, + "macro.generated_8.adjust_1": { + "name": "adjust_1", + "resource_type": "macro", + "package_name": "generated_8", + "path": "models/support/_sqlbuild/macros/adjust_1.py", + "original_file_path": "models/support/_sqlbuild/macros/adjust_1.py", + "unique_id": "macro.generated_8.adjust_1", + "macro_sql": "import os\n\n\ndef adjust_1(expression: str) -> str:\n \"\"\"Tag an amount with the deployment region.\"\"\"\n region = os.environ.get(\"SQB_DIFFERENTIAL_REGION\", \"east\")\n return f\"({expression} + LENGTH('{region}'))\"\n", + "depends_on": { + "macros": [] + }, + "description": "", + "meta": { + "sqlbuild_visibility": "inherited", + "sqlbuild_scope_path": "models/support" + }, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "arguments": [], + "supported_languages": null + }, + "macro.generated_8.adjust_2": { + "name": "adjust_2", + "resource_type": "macro", + "package_name": "generated_8", + "path": "models/support/_sqlbuild/macros/adjust_2.py", + "original_file_path": "models/support/_sqlbuild/macros/adjust_2.py", + "unique_id": "macro.generated_8.adjust_2", + "macro_sql": "def adjust_2(expression: str) -> str:\n \"\"\"Offset an amount.\"\"\"\n return f\"({expression} + 2 + 1)\"\n", + "depends_on": { + "macros": [] + }, + "description": "", + "meta": { + "sqlbuild_visibility": "inherited", + "sqlbuild_scope_path": "models/support" + }, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "arguments": [], + "supported_languages": null + }, + "macro.generated_8.adjust_0": { + "name": "adjust_0", + "resource_type": "macro", + "package_name": "generated_8", + "path": "models/support/marts/_sqlbuild/_macros/adjust_0.py", + "original_file_path": "models/support/marts/_sqlbuild/_macros/adjust_0.py", + "unique_id": "macro.generated_8.adjust_0", + "macro_sql": "def adjust_0(ctx, expression: str) -> str:\n \"\"\"Scale an amount by the configured discount.\"\"\"\n rate = ctx.vars[\"discount_rate\"]\n return f\"({expression} * (1 - {rate}))\"\n", + "depends_on": { + "macros": [] + }, + "description": "", + "meta": { + "sqlbuild_visibility": "local", + "sqlbuild_scope_path": "models/support/marts" + }, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "arguments": [], + "supported_languages": null + }, + "macro.generated_8.doubled": { + "name": "doubled", + "resource_type": "macro", + "package_name": "generated_8", + "path": "tests/unit/_macros/scaling.py", + "original_file_path": "tests/unit/_macros/scaling.py", + "unique_id": "macro.generated_8.doubled", + "macro_sql": "def doubled(expression: str) -> str:\n \"\"\"Double an amount.\"\"\"\n return f\"({expression} * 2)\"\n", + "depends_on": { + "macros": [] + }, + "description": "", + "meta": { + "sqlbuild_visibility": "local", + "sqlbuild_scope_path": "tests/unit" + }, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "arguments": [], + "supported_languages": null + }, + "macro.generated_8.event_rows": { + "name": "event_rows", + "resource_type": "macro", + "package_name": "generated_8", + "path": "tests/unit/rendering/_sqlbuild/_macros/fixtures.py", + "original_file_path": "tests/unit/rendering/_sqlbuild/_macros/fixtures.py", + "unique_id": "macro.generated_8.event_rows", + "macro_sql": "def event_rows(count: int = 2) -> str:\n \"\"\"Return fixture event rows.\"\"\"\n return \" UNION ALL \".join(\n f\"SELECT {index} AS id, CAST({index} AS DOUBLE) AS amount, \"\n f\"'placed' AS status, \"\n f\"TIMESTAMP '2026-01-0{index} 00:00:00' AS created_at\"\n for index in range(1, count + 1)\n )\n", + "depends_on": { + "macros": [] + }, + "description": "", + "meta": { + "sqlbuild_visibility": "local", + "sqlbuild_scope_path": "tests/unit/rendering" + }, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "arguments": [], + "supported_languages": null + }, + "macro.generated_8__sqlbuild_hooks.record_refresh": { + "name": "record_refresh", + "resource_type": "macro", + "package_name": "generated_8__sqlbuild_hooks", + "path": "models/support/marts/_sqlbuild/_hooks/sql/record_refresh.sql", + "original_file_path": "models/support/marts/_sqlbuild/_hooks/sql/record_refresh.sql", + "unique_id": "macro.generated_8__sqlbuild_hooks.record_refresh", + "macro_sql": "SELECT @'relation' AS refreshed_relation, @'label' AS refresh_label", + "depends_on": { + "macros": [] + }, + "description": "Record a refresh for one relation", + "meta": { + "sqlbuild_resource_type": "sql_hook", + "sqlbuild_hook_name": "record_refresh" + }, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "arguments": [], + "supported_languages": null + } + }, + "exposures": {}, + "metrics": {}, + "groups": {}, + "selectors": {}, + "disabled": {}, + "parent_map": { + "model.generated_8.checked_stg_support_0": [ + "model.generated_8.stg_support_0" + ], + "model.generated_8.pivot_input_stg_support_0": [ + "model.generated_8.stg_support_0" + ], + "model.generated_8.pivoted_stg_support_0": [ + "model.generated_8.pivot_input_stg_support_0" + ], + "model.generated_8.unanalysed_stg_support_0": [ + "model.generated_8.stg_support_0" + ], + "model.generated_8.append_stg_support_0": [ + "model.generated_8.stg_support_0" + ], + "model.generated_8.audited_stg_support_0": [ + "model.generated_8.stg_support_0" + ], + "model.generated_8.batched_rolling_window_stg_support_0": [ + "model.generated_8.stg_support_0" + ], + "model.generated_8.hooked_stg_support_0": [ + "model.generated_8.stg_support_0" + ], + "model.generated_8.product_labels": [ + "source.generated_8.loaded_products" + ], + "model.generated_8.shaped_stg_support_0": [ + "model.generated_8.stg_support_0" + ], + "model.generated_8.taxed_stg_support_0": [ + "model.generated_8.stg_support_0" + ], + "model.generated_8.bounded_stg_support_0": [ + "model.generated_8.stg_support_0" + ], + "model.generated_8.channel_labels": [ + "source.generated_8.labelled_channels" + ], + "model.generated_8.configured_stg_support_0": [ + "model.generated_8.stg_support_0" + ], + "model.generated_8.labelled_stg_support_0": [ + "model.generated_8.tidy_status", + "model.generated_8.stg_support_0" + ], + "model.generated_8.latest_raw_events_1": [ + "source.generated_8.raw_events_1" + ], + "model.generated_8.unioned_event_ids": [ + "model.generated_8.stg_support_0" + ], + "model.generated_8.support_summary_0": [ + "model.generated_8.stg_support_0" + ], + "model.generated_8.support_summary_1": [ + "model.generated_8.stg_support_0" + ], + "model.generated_8.support_summary_2": [ + "model.generated_8.stg_support_0" + ], + "model.generated_8.stg_support_0": [ + "source.generated_8.raw_events_0" + ], + "source.generated_8.configured_events": [], + "source.generated_8.labelled_channels": [], + "source.generated_8.loaded_products": [], + "source.generated_8.raw_events_0": [], + "source.generated_8.raw_events_1": [], + "model.generated_8.configured_label": [], + "model.generated_8.tidy_status": [] + }, + "group_map": {}, + "docs": {}, + "saved_queries": {}, + "semantic_models": {}, + "unit_tests": {} + } +} diff --git a/tests/goldens/compiler/seed/9.json b/tests/goldens/compiler/seed/9.json new file mode 100644 index 0000000000..cf030399cf --- /dev/null +++ b/tests/goldens/compiler/seed/9.json @@ -0,0 +1,3210 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 0, + "diagnostics": [] + }, + { + "label": "compile-store-warm", + "exit_code": 0, + "diagnostics": [] + }, + { + "label": "plan", + "exit_code": 0, + "diagnostics": [] + } + ], + "compiled": { + "audits/generic/billing_summary_0/not_null__id.sql": [ + "SELECT id", + "FROM __ref(\"billing_summary_0\")", + "WHERE id IS NULL", + "" + ], + "audits/generic/configured_events/accepted_values__id.sql": [ + "SELECT id", + "FROM __source(\"configured_events\")", + "WHERE id IS NOT NULL", + " AND id NOT IN ('1')", + "" + ], + "audits/generic/configured_stg_support_1/accepted_values__status.sql": [ + "SELECT status", + "FROM __ref(\"configured_stg_support_1\")", + "WHERE status IS NOT NULL", + " AND status NOT IN ('placed', 'shipped')", + "" + ], + "audits/generic/configured_stg_support_1/configured_amount_floor.sql": [ + "SELECT *", + "FROM __ref(\"configured_stg_support_1\")", + "WHERE amount < -37", + "" + ], + "audits/generic/configured_stg_support_1/configured_id_present__id.sql": [ + "SELECT id", + "FROM __ref(\"configured_stg_support_1\")", + "WHERE id IS NULL", + "" + ], + "audits/generic/configured_stg_support_1/unique__id.sql": [ + "SELECT id, COUNT(*) AS duplicate_count", + "FROM __ref(\"configured_stg_support_1\")", + "WHERE id IS NOT NULL", + "GROUP BY id", + "HAVING COUNT(*) > 1", + "" + ], + "audits/generic/guarded_stg_billing_0/amount_at_least__amount.sql": [ + "SELECT *", + "FROM __ref(\"guarded_stg_billing_0\")", + "WHERE amount < 0", + "" + ], + "audits/generic/labelled_stg_support_1/tidy_status__status.sql": [ + "SELECT *", + "FROM __ref(\"labelled_stg_support_1\")", + "WHERE status <> LOWER(TRIM(status))", + "" + ], + "audits/generic/stated_stg_support_0/accepted_values__status.sql": [ + "SELECT status", + "FROM __ref(\"stated_stg_support_0\")", + "WHERE status IS NOT NULL", + " AND status NOT IN ('placed', 'expédié', '取消', 'returned ✓')", + "" + ], + "audits/generic/stg_support_1/not_null__id.sql": [ + "SELECT id", + "FROM __ref(\"stg_support_1\")", + "WHERE id IS NULL", + "" + ], + "audits/singular/labelled_channels_match.sql": [ + "SELECT l.id", + "FROM __ref(\"labelled_stg_support_1\") l", + "JOIN __ref(\"channel_labels\") c ON l.id = c.id", + "WHERE l.status = c.channel", + "" + ], + "functions/python/is_large.sql": [ + "REGISTER PYTHON FUNCTION is_large(DOUBLE) RETURNS BOOLEAN", + "" + ], + "functions/sql/configured_label.sql": [ + "CREATE OR REPLACE MACRO ci_checks.configured_label(raw_status) AS (", + "UPPER(raw_status)", + ")", + "" + ], + "functions/sql/is_placed.sql": [ + "CREATE OR REPLACE MACRO ci_checks.is_placed(event_status) AS (", + "event_status = 'placed'", + ")", + "" + ], + "functions/sql/tidy_status.sql": [ + "CREATE OR REPLACE MACRO ci_checks.tidy_status(raw_status) AS (", + "LOWER(TRIM(raw_status))", + ")", + "" + ], + "models/analysis/combined_stg_billing_0.sql": [ + "WITH recent AS (", + " SELECT id, created_at FROM __ref(\"stg_billing_0\") WHERE amount > 0", + "),", + "older AS (", + " SELECT id, created_at FROM __ref(\"stg_support_0\")", + ")", + "SELECT id, created_at FROM recent", + "UNION ALL", + "SELECT id, created_at FROM older", + "" + ], + "models/analysis/every_open_event.sql": [ + "SELECT *", + "FROM __source(\"open_events\")", + "" + ], + "models/analysis/every_raw_events_1.sql": [ + "SELECT *", + "FROM __source(\"raw_events_1\")", + "" + ], + "models/analysis/every_stg_billing_0.sql": [ + "SELECT *", + "FROM __ref(\"stg_billing_0\")", + "" + ], + "models/analysis/passed_stg_billing_0.sql": [ + "WITH passthrough AS (", + " SELECT id, created_at FROM __ref(\"stg_billing_0\")", + ")", + "SELECT id, created_at", + "FROM passthrough", + "" + ], + "models/analysis/pivot_input_stg_support_0.sql": [ + "SELECT CAST(id AS INTEGER) AS id, CAST(status AS VARCHAR) AS status, amount", + "FROM __ref(\"stg_support_0\")", + "" + ], + "models/analysis/pivoted_stg_support_0.sql": [ + "PIVOT __ref(\"pivot_input_stg_support_0\")", + "ON status", + "USING MAX(amount)", + "GROUP BY id", + "" + ], + "models/analysis/quoted_stg_support_0.sql": [ + "SELECT \"ID\" AS \"OrderKey\", \"Amount\", \"Status\" AS \"Status\"", + "FROM __ref(\"stg_support_0\")", + "" + ], + "models/analysis/typed_stg_billing_0.sql": [ + "SELECT id, amount * 2 AS doubled_amount, CAST(status AS VARCHAR) AS status_text,", + " created_at, NULL AS missing_note", + "FROM __ref(\"stg_billing_0\")", + "" + ], + "models/analysis/unanalysed_stg_support_1.sql": [ + "SELECT id, amount", + "FROM __ref(\"stg_support_1\")", + "WHERE id = ANY [1, 2]", + "" + ], + "models/billing/marts/billing_summary_0.sql": [ + "SELECT", + " t.id,", + " t.amount,", + " t.status,", + " t.created_at,", + " t.amount * 2.4700 AS threshold_0_amount", + "FROM __ref(\"stg_billing_0\") t", + "WHERE t.status <> 'placed'", + "" + ], + "models/billing/staging/stg_billing_0.sql": [ + "SELECT", + " t.id,", + " t.amount,", + " t.status,", + " t.created_at,", + " t.amount * 2.4700 AS threshold_0_amount,", + " (t.amount + 0 + 1) AS adjust_0_amount,", + " 'Zürich – depot' AS site_label", + "FROM __source(\"raw_events_0\") t", + "" + ], + "models/features/append_stg_support_0.sql": [ + "SELECT id, amount, status, created_at", + "FROM __ref(\"stg_support_0\")", + "" + ], + "models/features/batched_rolling_window_stg_support_1.sql": [ + "SELECT CAST(created_at AS DATE) AS created_at, SUM(amount) AS amount", + "FROM __ref(\"stg_support_1\")", + "GROUP BY CAST(created_at AS DATE)", + "" + ], + "models/features/delete_insert_stg_billing_0.sql": [ + "SELECT id, amount, status, created_at", + "FROM __ref(\"stg_billing_0\")", + "" + ], + "models/features/flagged_stg_billing_0.sql": [ + "SELECT id, __udf(\"is_placed\")(status) AS is_placed,", + " __udf(\"is_large\")(amount) AS is_large", + "FROM __ref(\"stg_billing_0\")", + "" + ], + "models/features/guarded_stg_billing_0.sql": [ + "SELECT id, amount", + "FROM __ref(\"stg_billing_0\")", + "" + ], + "models/features/kept_stg_support_1.sql": [ + "SELECT id, status, amount * 0.5 AS weighted", + "FROM __ref(\"stg_support_1\")", + "WHERE status IN ('取消', 'placed')", + " AND amount * 1.0 IN (0.5, 1.25, 2500.75) OR id > 0", + "" + ], + "models/features/product_labels.sql": [ + "SELECT id, label", + "FROM __source(\"loaded_products\")", + "" + ], + "models/features/spaced_stg_support_0.sql": [ + "-- Généré pour café ☕ counter", + "SELECT", + "\tid,", + "\t'Zürich – depot' AS label, -- étiquette ✓", + "\tamount", + "FROM __ref(\"stg_support_0\")", + "" + ], + "models/features/timestamp_history_stg_support_1.sql": [ + "SELECT id, status, created_at", + "FROM __ref(\"stg_support_1\")", + "" + ], + "models/rendering/channel_labels.sql": [ + "SELECT id, channel", + "FROM __source(\"labelled_channels\")", + "" + ], + "models/rendering/configured_stg_support_1.sql": [ + "SELECT id, amount, status", + "FROM __ref(\"stg_support_1\")", + "" + ], + "models/rendering/labelled_stg_support_1.sql": [ + "SELECT id, LOWER(TRIM(status)) AS status,", + " __udf(\"tidy_status\")(status) AS tidied", + "FROM __ref(\"stg_support_1\")", + "" + ], + "models/rendering/legacy_event_ids.sql": [ + "SELECT id", + "FROM __ref(\"stg_support_0\") AS legacy", + "" + ], + "models/rendering/stated_stg_support_0.sql": [ + "SELECT CAST(id AS INTEGER) AS id, status,", + " CASE WHEN amount >= 2 THEN 'HIGH'", + " ELSE 'LOW' END AS tier", + "FROM __ref(\"stg_support_0\")", + "" + ], + "models/support/marts/support_summary_0.sql": [ + "SELECT", + " t.id,", + " t.amount,", + " t.status,", + " t.created_at,", + " 'Zürich – depot' AS site_label", + "FROM __ref(\"stg_support_0\") t", + "" + ], + "models/support/staging/stg_support_0.sql": [ + "SELECT", + " t.id,", + " t.amount,", + " t.status,", + " t.created_at,", + " t.amount * 2.4700 AS threshold_0_amount", + "FROM __source(\"raw_events_0\") t", + "" + ], + "models/support/staging/stg_support_1.sql": [ + "SELECT", + " t.id,", + " t.amount,", + " t.status,", + " t.created_at,", + " (t.amount + 0 + 1) AS adjust_0_amount", + "FROM __source(\"raw_events_0\") t", + "" + ], + "tests/_chain_/labelled_stg_support_1__stg_support_1/test_labelled_stg_support_1.sql": [ + "WITH __source__raw_events_0 AS (SELECT 1 AS id, CAST(1 AS DOUBLE) AS amount, 'placed' AS status, TIMESTAMP '2026-01-01 00:00:00' AS created_at UNION ALL SELECT 2 AS id, CAST(2 AS DOUBLE) AS amount, 'placed' AS status, TIMESTAMP '2026-01-02 00:00:00' AS created_at),", + "__ref__stg_support_1 AS (SELECT", + " t.id,", + " t.amount,", + " t.status,", + " t.created_at,", + " (t.amount + 0 + 1) AS adjust_0_amount", + "FROM __source__raw_events_0 t),", + "__ref__labelled_stg_support_1 AS (SELECT id, LOWER(TRIM(status)) AS status,", + " ci_checks.tidy_status(status) AS tidied", + "FROM __ref__stg_support_1),", + "__actual__labelled_stg_support_1 AS (SELECT * FROM __ref__labelled_stg_support_1),", + "__expected__labelled_stg_support_1 AS (SELECT 1 AS id, LOWER(TRIM('placed')) AS status", + " UNION ALL", + " SELECT 2 AS id, 'placed' AS status)", + "SELECT 1 AS step_index, 'labelled_stg_support_1' AS model_name, (SELECT COUNT(*) FROM __actual__labelled_stg_support_1) AS actual_count, (SELECT COUNT(*) FROM __expected__labelled_stg_support_1) AS expected_count, (SELECT COUNT(*) FROM (SELECT id, status FROM __actual__labelled_stg_support_1 EXCEPT SELECT id, status FROM __expected__labelled_stg_support_1) AS __sqlbuild_mismatch) AS unexpected_count, (SELECT COUNT(*) FROM (SELECT id, status FROM __expected__labelled_stg_support_1 EXCEPT SELECT id, status FROM __actual__labelled_stg_support_1) AS __sqlbuild_missing) AS missing_count", + "" + ], + "tests/stg_support_0/test_stg_support_0.sql": [ + "WITH __source__raw_events_0 AS (SELECT 1 AS id, CAST(10 AS DOUBLE) AS amount, 'placed' AS status,", + " TIMESTAMP '2026-01-01 00:00:00' AS created_at),", + "__ref__stg_support_0 AS (SELECT", + " t.id,", + " t.amount,", + " t.status,", + " t.created_at,", + " t.amount * 2.4700 AS threshold_0_amount", + "FROM __source__raw_events_0 t),", + "__actual__stg_support_0 AS (SELECT * FROM __ref__stg_support_0),", + "__expected__stg_support_0 AS (SELECT 1 AS id, 'placed' AS status)", + "SELECT 0 AS step_index, 'stg_support_0' AS model_name, (SELECT COUNT(*) FROM __actual__stg_support_0) AS actual_count, (SELECT COUNT(*) FROM __expected__stg_support_0) AS expected_count, (SELECT COUNT(*) FROM (SELECT id, status FROM __actual__stg_support_0 EXCEPT SELECT id, status FROM __expected__stg_support_0) AS __sqlbuild_mismatch) AS unexpected_count, (SELECT COUNT(*) FROM (SELECT id, status FROM __expected__stg_support_0 EXCEPT SELECT id, status FROM __actual__stg_support_0) AS __sqlbuild_missing) AS missing_count", + "" + ], + "tests/tests/unit/test_stg_support_0_cases/block_1__labelled.sql": [ + "WITH __source__raw_events_0 AS (SELECT 1 AS id, CAST(2.25 AS DOUBLE) AS amount,", + " 'Zürich – depot' AS status,", + " TIMESTAMP '2026-01-01 00:00:00' AS created_at),", + "__ref__stg_support_0 AS (SELECT", + " t.id,", + " t.amount,", + " t.status,", + " t.created_at,", + " t.amount * 2.4700 AS threshold_0_amount", + "FROM __source__raw_events_0 t),", + "__actual__stg_support_0 AS (SELECT * FROM __ref__stg_support_0),", + "__expected__stg_support_0 AS (SELECT 1 AS id)", + "SELECT 0 AS step_index, 'stg_support_0' AS model_name, (SELECT COUNT(*) FROM __actual__stg_support_0) AS actual_count, (SELECT COUNT(*) FROM __expected__stg_support_0) AS expected_count, (SELECT COUNT(*) FROM (SELECT id FROM __actual__stg_support_0 EXCEPT SELECT id FROM __expected__stg_support_0) AS __sqlbuild_mismatch) AS unexpected_count, (SELECT COUNT(*) FROM (SELECT id FROM __expected__stg_support_0 EXCEPT SELECT id FROM __actual__stg_support_0) AS __sqlbuild_missing) AS missing_count", + "" + ], + "tests/tests/unit/test_stg_support_0_cases/block_1__placed.sql": [ + "WITH __source__raw_events_0 AS (SELECT 1 AS id, CAST(1.5 AS DOUBLE) AS amount,", + " 'placed' AS status,", + " TIMESTAMP '2026-01-01 00:00:00' AS created_at),", + "__ref__stg_support_0 AS (SELECT", + " t.id,", + " t.amount,", + " t.status,", + " t.created_at,", + " t.amount * 2.4700 AS threshold_0_amount", + "FROM __source__raw_events_0 t),", + "__actual__stg_support_0 AS (SELECT * FROM __ref__stg_support_0),", + "__expected__stg_support_0 AS (SELECT 1 AS id)", + "SELECT 0 AS step_index, 'stg_support_0' AS model_name, (SELECT COUNT(*) FROM __actual__stg_support_0) AS actual_count, (SELECT COUNT(*) FROM __expected__stg_support_0) AS expected_count, (SELECT COUNT(*) FROM (SELECT id FROM __actual__stg_support_0 EXCEPT SELECT id FROM __expected__stg_support_0) AS __sqlbuild_mismatch) AS unexpected_count, (SELECT COUNT(*) FROM (SELECT id FROM __expected__stg_support_0 EXCEPT SELECT id FROM __actual__stg_support_0) AS __sqlbuild_missing) AS missing_count", + "" + ], + "tests/udf detects_placed_events/detects_placed_events.sql": [ + "WITH input_values AS (SELECT 'placed' AS event_status),", + "__actual__udf_detects_placed_events AS (SELECT event_status, ci_checks.is_placed(event_status) AS is_placed", + " FROM input_values),", + "__expected__udf_detects_placed_events AS (SELECT 'placed' AS event_status, TRUE AS is_placed)", + "SELECT 0 AS step_index, 'udf detects_placed_events' AS model_name, (SELECT COUNT(*) FROM __actual__udf_detects_placed_events) AS actual_count, (SELECT COUNT(*) FROM __expected__udf_detects_placed_events) AS expected_count, (SELECT COUNT(*) FROM (SELECT * FROM __actual__udf_detects_placed_events EXCEPT SELECT * FROM __expected__udf_detects_placed_events) AS __sqlbuild_mismatch) AS unexpected_count, (SELECT COUNT(*) FROM (SELECT * FROM __expected__udf_detects_placed_events EXCEPT SELECT * FROM __actual__udf_detects_placed_events) AS __sqlbuild_missing) AS missing_count", + "" + ] + }, + "manifest": { + "nodes": { + "model.generated_9.combined_stg_billing_0": { + "database": null, + "schema": "ci_checks", + "name": "combined_stg_billing_0", + "resource_type": "model", + "package_name": "generated_9", + "path": "models/analysis/combined_stg_billing_0.sql", + "original_file_path": "models/analysis/combined_stg_billing_0.sql", + "unique_id": "model.generated_9.combined_stg_billing_0", + "fqn": [ + "generated_9", + "models", + "analysis", + "combined_stg_billing_0" + ], + "alias": "combined_stg_billing_0", + "config": { + "enabled": true, + "alias": null, + "schema": "ci_checks", + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "table", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Recent and older rows of stg_billing_0 and stg_support_0", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "ci_checks.combined_stg_billing_0", + "depends_on": { + "macros": [], + "nodes": [ + "model.generated_9.stg_billing_0", + "model.generated_9.stg_support_0" + ] + } + }, + "model.generated_9.every_open_event": { + "database": null, + "schema": "ci_checks", + "name": "every_open_event", + "resource_type": "model", + "package_name": "generated_9", + "path": "models/analysis/every_open_event.sql", + "original_file_path": "models/analysis/every_open_event.sql", + "unique_id": "model.generated_9.every_open_event", + "fqn": [ + "generated_9", + "models", + "analysis", + "every_open_event" + ], + "alias": "every_open_event", + "config": { + "enabled": true, + "alias": null, + "schema": "ci_checks", + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "table", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Every column of an open source", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "ci_checks.every_open_event", + "depends_on": { + "macros": [], + "nodes": [ + "source.generated_9.open_events" + ] + } + }, + "model.generated_9.every_raw_events_1": { + "database": null, + "schema": "ci_checks", + "name": "every_raw_events_1", + "resource_type": "model", + "package_name": "generated_9", + "path": "models/analysis/every_raw_events_1.sql", + "original_file_path": "models/analysis/every_raw_events_1.sql", + "unique_id": "model.generated_9.every_raw_events_1", + "fqn": [ + "generated_9", + "models", + "analysis", + "every_raw_events_1" + ], + "alias": "every_raw_events_1", + "config": { + "enabled": true, + "alias": null, + "schema": "ci_checks", + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "table", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Every column of raw_events_1", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "ci_checks.every_raw_events_1", + "depends_on": { + "macros": [], + "nodes": [ + "source.generated_9.raw_events_1" + ] + } + }, + "model.generated_9.every_stg_billing_0": { + "database": null, + "schema": "ci_checks", + "name": "every_stg_billing_0", + "resource_type": "model", + "package_name": "generated_9", + "path": "models/analysis/every_stg_billing_0.sql", + "original_file_path": "models/analysis/every_stg_billing_0.sql", + "unique_id": "model.generated_9.every_stg_billing_0", + "fqn": [ + "generated_9", + "models", + "analysis", + "every_stg_billing_0" + ], + "alias": "every_stg_billing_0", + "config": { + "enabled": true, + "alias": null, + "schema": "ci_checks", + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "table", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Every column of stg_billing_0", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "ci_checks.every_stg_billing_0", + "depends_on": { + "macros": [], + "nodes": [ + "model.generated_9.stg_billing_0" + ] + } + }, + "model.generated_9.passed_stg_billing_0": { + "database": null, + "schema": "ci_checks", + "name": "passed_stg_billing_0", + "resource_type": "model", + "package_name": "generated_9", + "path": "models/analysis/passed_stg_billing_0.sql", + "original_file_path": "models/analysis/passed_stg_billing_0.sql", + "unique_id": "model.generated_9.passed_stg_billing_0", + "fqn": [ + "generated_9", + "models", + "analysis", + "passed_stg_billing_0" + ], + "alias": "passed_stg_billing_0", + "config": { + "enabled": true, + "alias": null, + "schema": "ci_checks", + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "table", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Columns of stg_billing_0 passed through a CTE", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "ci_checks.passed_stg_billing_0", + "depends_on": { + "macros": [], + "nodes": [ + "model.generated_9.stg_billing_0" + ] + } + }, + "model.generated_9.pivot_input_stg_support_0": { + "database": null, + "schema": "ci_checks", + "name": "pivot_input_stg_support_0", + "resource_type": "model", + "package_name": "generated_9", + "path": "models/analysis/pivot_input_stg_support_0.sql", + "original_file_path": "models/analysis/pivot_input_stg_support_0.sql", + "unique_id": "model.generated_9.pivot_input_stg_support_0", + "fqn": [ + "generated_9", + "models", + "analysis", + "pivot_input_stg_support_0" + ], + "alias": "pivot_input_stg_support_0", + "config": { + "enabled": true, + "alias": null, + "schema": "ci_checks", + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "table", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Contracted pivot input from stg_support_0", + "columns": { + "id": { + "name": "id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "status": { + "name": "status", + "description": "", + "meta": {}, + "data_type": "VARCHAR", + "constraints": [], + "quote": null, + "tags": [] + }, + "amount": { + "name": "amount", + "description": "", + "meta": {}, + "data_type": "DOUBLE", + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "ci_checks.pivot_input_stg_support_0", + "depends_on": { + "macros": [], + "nodes": [ + "model.generated_9.stg_support_0" + ] + } + }, + "model.generated_9.pivoted_stg_support_0": { + "database": null, + "schema": "ci_checks", + "name": "pivoted_stg_support_0", + "resource_type": "model", + "package_name": "generated_9", + "path": "models/analysis/pivoted_stg_support_0.sql", + "original_file_path": "models/analysis/pivoted_stg_support_0.sql", + "unique_id": "model.generated_9.pivoted_stg_support_0", + "fqn": [ + "generated_9", + "models", + "analysis", + "pivoted_stg_support_0" + ], + "alias": "pivoted_stg_support_0", + "config": { + "enabled": true, + "alias": null, + "schema": "ci_checks", + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "table", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Amounts of stg_support_0 pivoted by status", + "columns": { + "id": { + "name": "id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": { + "sqlbuild": { + "dynamic_columns": [ + { + "name": "status_amounts", + "pivot_column": "status", + "value_column": "amount", + "aggregate": "MAX", + "type": "DOUBLE", + "name_pattern": null + } + ] + } + }, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "ci_checks.pivoted_stg_support_0", + "depends_on": { + "macros": [], + "nodes": [ + "model.generated_9.pivot_input_stg_support_0" + ] + } + }, + "model.generated_9.quoted_stg_support_0": { + "database": null, + "schema": "ci_checks", + "name": "quoted_stg_support_0", + "resource_type": "model", + "package_name": "generated_9", + "path": "models/analysis/quoted_stg_support_0.sql", + "original_file_path": "models/analysis/quoted_stg_support_0.sql", + "unique_id": "model.generated_9.quoted_stg_support_0", + "fqn": [ + "generated_9", + "models", + "analysis", + "quoted_stg_support_0" + ], + "alias": "quoted_stg_support_0", + "config": { + "enabled": true, + "alias": null, + "schema": "ci_checks", + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "table", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Quoted identifiers over stg_support_0", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "ci_checks.quoted_stg_support_0", + "depends_on": { + "macros": [], + "nodes": [ + "model.generated_9.stg_support_0" + ] + } + }, + "model.generated_9.typed_stg_billing_0": { + "database": null, + "schema": "ci_checks", + "name": "typed_stg_billing_0", + "resource_type": "model", + "package_name": "generated_9", + "path": "models/analysis/typed_stg_billing_0.sql", + "original_file_path": "models/analysis/typed_stg_billing_0.sql", + "unique_id": "model.generated_9.typed_stg_billing_0", + "fqn": [ + "generated_9", + "models", + "analysis", + "typed_stg_billing_0" + ], + "alias": "typed_stg_billing_0", + "config": { + "enabled": true, + "alias": null, + "schema": "ci_checks", + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "table", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Typed and untyped outputs of stg_billing_0", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "ci_checks.typed_stg_billing_0", + "depends_on": { + "macros": [], + "nodes": [ + "model.generated_9.stg_billing_0" + ] + } + }, + "model.generated_9.unanalysed_stg_support_1": { + "database": null, + "schema": "ci_checks", + "name": "unanalysed_stg_support_1", + "resource_type": "model", + "package_name": "generated_9", + "path": "models/analysis/unanalysed_stg_support_1.sql", + "original_file_path": "models/analysis/unanalysed_stg_support_1.sql", + "unique_id": "model.generated_9.unanalysed_stg_support_1", + "fqn": [ + "generated_9", + "models", + "analysis", + "unanalysed_stg_support_1" + ], + "alias": "unanalysed_stg_support_1", + "config": { + "enabled": true, + "alias": null, + "schema": "ci_checks", + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "table", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Copy of stg_support_1 that SQL analysis cannot parse", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "ci_checks.unanalysed_stg_support_1", + "depends_on": { + "macros": [], + "nodes": [ + "model.generated_9.stg_support_1" + ] + } + }, + "model.generated_9.billing_summary_0": { + "database": null, + "schema": "ci_checks", + "name": "billing_summary_0", + "resource_type": "model", + "package_name": "generated_9", + "path": "models/billing/marts/billing_summary_0.sql", + "original_file_path": "models/billing/marts/billing_summary_0.sql", + "unique_id": "model.generated_9.billing_summary_0", + "fqn": [ + "generated_9", + "models", + "billing", + "marts", + "billing_summary_0" + ], + "alias": "billing_summary_0", + "config": { + "enabled": true, + "alias": null, + "schema": "ci_checks", + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "table", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [ + { + "sql": "SELECT 'ci_checks.billing_summary_0' AS refreshed_relation, 'Ελληνικά orders' AS refresh_label", + "transaction": true, + "index": 0 + }, + { + "sql": "SELECT 'ci_checks.billing_summary_0' AS refreshed", + "transaction": true, + "index": 1 + } + ], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Summary of billing activity 0 (Zürich – depot)", + "columns": { + "id": { + "name": "id", + "description": "", + "meta": {}, + "data_type": null, + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": { + "sqlbuild": { + "lifecycle_hooks": { + "post_hooks": [ + { + "type": "sql", + "statement": "SELECT 'ci_checks.billing_summary_0' AS refreshed_relation, 'Ελληνικά orders' AS refresh_label", + "name": "record_refresh", + "relative_path": "hooks/sql/record_refresh.sql", + "definition_sql": "SELECT @'relation' AS refreshed_relation, @'label' AS refresh_label", + "kwargs": { + "relation": "@@CTX:destination.qualified", + "label": "Ελληνικά orders" + }, + "description": "Record a refresh for one relation" + }, + { + "type": "sql", + "statement": "SELECT 'ci_checks.billing_summary_0' AS refreshed" + } + ] + } + } + }, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "ci_checks.billing_summary_0", + "depends_on": { + "macros": [], + "nodes": [ + "model.generated_9.stg_billing_0" + ] + } + }, + "model.generated_9.stg_billing_0": { + "database": null, + "schema": "ci_checks", + "name": "stg_billing_0", + "resource_type": "model", + "package_name": "generated_9", + "path": "models/billing/staging/stg_billing_0.sql", + "original_file_path": "models/billing/staging/stg_billing_0.sql", + "unique_id": "model.generated_9.stg_billing_0", + "fqn": [ + "generated_9", + "models", + "billing", + "staging", + "stg_billing_0" + ], + "alias": "stg_billing_0", + "config": { + "enabled": true, + "alias": null, + "schema": "ci_checks", + "database": null, + "tags": [ + "staging" + ], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [ + "staging" + ], + "description": "Staged billing events 0", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "ci_checks.stg_billing_0", + "depends_on": { + "macros": [], + "nodes": [ + "source.generated_9.raw_events_0" + ] + } + }, + "model.generated_9.append_stg_support_0": { + "database": null, + "schema": "ci_checks", + "name": "append_stg_support_0", + "resource_type": "model", + "package_name": "generated_9", + "path": "models/features/append_stg_support_0.sql", + "original_file_path": "models/features/append_stg_support_0.sql", + "unique_id": "model.generated_9.append_stg_support_0", + "fqn": [ + "generated_9", + "models", + "features", + "append_stg_support_0" + ], + "alias": "append_stg_support_0", + "config": { + "enabled": true, + "alias": null, + "schema": "ci_checks", + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "incremental", + "incremental_strategy": "append", + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Incremental append copy of stg_support_0", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "ci_checks.append_stg_support_0", + "depends_on": { + "macros": [], + "nodes": [ + "model.generated_9.stg_support_0" + ] + } + }, + "model.generated_9.batched_rolling_window_stg_support_1": { + "database": null, + "schema": "ci_checks", + "name": "batched_rolling_window_stg_support_1", + "resource_type": "model", + "package_name": "generated_9", + "path": "models/features/batched_rolling_window_stg_support_1.sql", + "original_file_path": "models/features/batched_rolling_window_stg_support_1.sql", + "unique_id": "model.generated_9.batched_rolling_window_stg_support_1", + "fqn": [ + "generated_9", + "models", + "features", + "batched_rolling_window_stg_support_1" + ], + "alias": "batched_rolling_window_stg_support_1", + "config": { + "enabled": true, + "alias": null, + "schema": "ci_checks", + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "incremental", + "incremental_strategy": "delete_insert", + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Microbatched rolling_window totals of stg_support_1", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "ci_checks.batched_rolling_window_stg_support_1", + "depends_on": { + "macros": [], + "nodes": [ + "model.generated_9.stg_support_1" + ] + } + }, + "model.generated_9.delete_insert_stg_billing_0": { + "database": null, + "schema": "ci_checks", + "name": "delete_insert_stg_billing_0", + "resource_type": "model", + "package_name": "generated_9", + "path": "models/features/delete_insert_stg_billing_0.sql", + "original_file_path": "models/features/delete_insert_stg_billing_0.sql", + "unique_id": "model.generated_9.delete_insert_stg_billing_0", + "fqn": [ + "generated_9", + "models", + "features", + "delete_insert_stg_billing_0" + ], + "alias": "delete_insert_stg_billing_0", + "config": { + "enabled": true, + "alias": null, + "schema": "ci_checks", + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "incremental", + "incremental_strategy": "delete_insert", + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": [ + "id" + ], + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Incremental delete_insert copy of stg_billing_0", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "ci_checks.delete_insert_stg_billing_0", + "depends_on": { + "macros": [], + "nodes": [ + "model.generated_9.stg_billing_0" + ] + } + }, + "model.generated_9.flagged_stg_billing_0": { + "database": null, + "schema": "ci_checks", + "name": "flagged_stg_billing_0", + "resource_type": "model", + "package_name": "generated_9", + "path": "models/features/flagged_stg_billing_0.sql", + "original_file_path": "models/features/flagged_stg_billing_0.sql", + "unique_id": "model.generated_9.flagged_stg_billing_0", + "fqn": [ + "generated_9", + "models", + "features", + "flagged_stg_billing_0" + ], + "alias": "flagged_stg_billing_0", + "config": { + "enabled": true, + "alias": null, + "schema": "ci_checks", + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "table", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Function-flagged copy of stg_billing_0", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "ci_checks.flagged_stg_billing_0", + "depends_on": { + "macros": [], + "nodes": [ + "model.generated_9.is_placed", + "model.generated_9.is_large", + "model.generated_9.stg_billing_0" + ] + } + }, + "model.generated_9.guarded_stg_billing_0": { + "database": null, + "schema": "ci_checks", + "name": "guarded_stg_billing_0", + "resource_type": "model", + "package_name": "generated_9", + "path": "models/features/guarded_stg_billing_0.sql", + "original_file_path": "models/features/guarded_stg_billing_0.sql", + "unique_id": "model.generated_9.guarded_stg_billing_0", + "fqn": [ + "generated_9", + "models", + "features", + "guarded_stg_billing_0" + ], + "alias": "guarded_stg_billing_0", + "config": { + "enabled": true, + "alias": null, + "schema": "ci_checks", + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "table", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Generic-audited copy of stg_billing_0", + "columns": { + "amount": { + "name": "amount", + "description": "", + "meta": {}, + "data_type": null, + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "ci_checks.guarded_stg_billing_0", + "depends_on": { + "macros": [], + "nodes": [ + "model.generated_9.stg_billing_0" + ] + } + }, + "model.generated_9.kept_stg_support_1": { + "database": null, + "schema": "ci_checks", + "name": "kept_stg_support_1", + "resource_type": "model", + "package_name": "generated_9", + "path": "models/features/kept_stg_support_1.sql", + "original_file_path": "models/features/kept_stg_support_1.sql", + "unique_id": "model.generated_9.kept_stg_support_1", + "fqn": [ + "generated_9", + "models", + "features", + "kept_stg_support_1" + ], + "alias": "kept_stg_support_1", + "config": { + "enabled": true, + "alias": null, + "schema": "ci_checks", + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "table", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Statuses kept from stg_support_1", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "ci_checks.kept_stg_support_1", + "depends_on": { + "macros": [], + "nodes": [ + "model.generated_9.stg_support_1" + ] + } + }, + "model.generated_9.product_labels": { + "database": null, + "schema": "ci_checks", + "name": "product_labels", + "resource_type": "model", + "package_name": "generated_9", + "path": "models/features/product_labels.sql", + "original_file_path": "models/features/product_labels.sql", + "unique_id": "model.generated_9.product_labels", + "fqn": [ + "generated_9", + "models", + "features", + "product_labels" + ], + "alias": "product_labels", + "config": { + "enabled": true, + "alias": null, + "schema": "ci_checks", + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Product labels from the loader", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "ci_checks.product_labels", + "depends_on": { + "macros": [], + "nodes": [ + "source.generated_9.loaded_products" + ] + } + }, + "model.generated_9.spaced_stg_support_0": { + "database": null, + "schema": "ci_checks", + "name": "spaced_stg_support_0", + "resource_type": "model", + "package_name": "generated_9", + "path": "models/features/spaced_stg_support_0.sql", + "original_file_path": "models/features/spaced_stg_support_0.sql", + "unique_id": "model.generated_9.spaced_stg_support_0", + "fqn": [ + "generated_9", + "models", + "features", + "spaced_stg_support_0" + ], + "alias": "spaced_stg_support_0", + "config": { + "enabled": true, + "alias": null, + "schema": "ci_checks", + "database": null, + "tags": [ + "línea", + "東京" + ], + "meta": {}, + "group": null, + "materialized": "table", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [ + "línea", + "東京" + ], + "description": "Copy of stg_support_0 with café ☕ counter", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "ci_checks.spaced_stg_support_0", + "depends_on": { + "macros": [], + "nodes": [ + "model.generated_9.stg_support_0" + ] + } + }, + "model.generated_9.timestamp_history_stg_support_1": { + "database": null, + "schema": "ci_checks", + "name": "timestamp_history_stg_support_1", + "resource_type": "model", + "package_name": "generated_9", + "path": "models/features/timestamp_history_stg_support_1.sql", + "original_file_path": "models/features/timestamp_history_stg_support_1.sql", + "unique_id": "model.generated_9.timestamp_history_stg_support_1", + "fqn": [ + "generated_9", + "models", + "features", + "timestamp_history_stg_support_1" + ], + "alias": "timestamp_history_stg_support_1", + "config": { + "enabled": true, + "alias": null, + "schema": "ci_checks", + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "snapshot", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": [ + "id" + ], + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Timestamp history of stg_support_1", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "ci_checks.timestamp_history_stg_support_1", + "depends_on": { + "macros": [], + "nodes": [ + "model.generated_9.stg_support_1" + ] + } + }, + "model.generated_9.channel_labels": { + "database": null, + "schema": "ci_checks", + "name": "channel_labels", + "resource_type": "model", + "package_name": "generated_9", + "path": "models/rendering/channel_labels.sql", + "original_file_path": "models/rendering/channel_labels.sql", + "unique_id": "model.generated_9.channel_labels", + "fqn": [ + "generated_9", + "models", + "rendering", + "channel_labels" + ], + "alias": "channel_labels", + "config": { + "enabled": true, + "alias": null, + "schema": "ci_checks", + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "table", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Normalised channel labels", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "ci_checks.channel_labels", + "depends_on": { + "macros": [], + "nodes": [ + "source.generated_9.labelled_channels" + ] + } + }, + "model.generated_9.configured_stg_support_1": { + "database": null, + "schema": "ci_checks", + "name": "configured_stg_support_1", + "resource_type": "model", + "package_name": "generated_9", + "path": "models/rendering/configured_stg_support_1.sql", + "original_file_path": "models/rendering/configured_stg_support_1.sql", + "unique_id": "model.generated_9.configured_stg_support_1", + "fqn": [ + "generated_9", + "models", + "rendering", + "configured_stg_support_1" + ], + "alias": "configured_stg_support_1", + "config": { + "enabled": true, + "alias": null, + "schema": "ci_checks", + "database": null, + "tags": [ + "release" + ], + "meta": {}, + "group": null, + "materialized": "table", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [ + "release" + ], + "description": "Configured copy of stg_support_1 (réglé)", + "columns": { + "id": { + "name": "id", + "description": "Row key", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "amount": { + "name": "amount", + "description": "Montant réglé", + "meta": {}, + "data_type": "DOUBLE", + "constraints": [], + "quote": null, + "tags": [] + }, + "status": { + "name": "status", + "description": "", + "meta": {}, + "data_type": null, + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "ci_checks.configured_stg_support_1", + "depends_on": { + "macros": [], + "nodes": [ + "model.generated_9.stg_support_1" + ] + } + }, + "model.generated_9.labelled_stg_support_1": { + "database": null, + "schema": "ci_checks", + "name": "labelled_stg_support_1", + "resource_type": "model", + "package_name": "generated_9", + "path": "models/rendering/labelled_stg_support_1.sql", + "original_file_path": "models/rendering/labelled_stg_support_1.sql", + "unique_id": "model.generated_9.labelled_stg_support_1", + "fqn": [ + "generated_9", + "models", + "rendering", + "labelled_stg_support_1" + ], + "alias": "labelled_stg_support_1", + "config": { + "enabled": true, + "alias": null, + "schema": "ci_checks", + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "table", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Labelled copy of stg_support_1", + "columns": { + "status": { + "name": "status", + "description": "", + "meta": {}, + "data_type": null, + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "ci_checks.labelled_stg_support_1", + "depends_on": { + "macros": [], + "nodes": [ + "model.generated_9.tidy_status", + "model.generated_9.stg_support_1" + ] + } + }, + "model.generated_9.legacy_event_ids": { + "database": null, + "schema": "ci_checks", + "name": "legacy_event_ids", + "resource_type": "model", + "package_name": "generated_9", + "path": "models/rendering/legacy_event_ids.sql", + "original_file_path": "models/rendering/legacy_event_ids.sql", + "unique_id": "model.generated_9.legacy_event_ids", + "fqn": [ + "generated_9", + "models", + "rendering", + "legacy_event_ids" + ], + "alias": "legacy_event_ids", + "config": { + "enabled": true, + "alias": null, + "schema": "ci_checks", + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "table", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Ids read through a macro-generated reference", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "ci_checks.legacy_event_ids", + "depends_on": { + "macros": [], + "nodes": [ + "model.generated_9.stg_support_0" + ] + } + }, + "model.generated_9.stated_stg_support_0": { + "database": null, + "schema": "ci_checks", + "name": "stated_stg_support_0", + "resource_type": "model", + "package_name": "generated_9", + "path": "models/rendering/stated_stg_support_0.sql", + "original_file_path": "models/rendering/stated_stg_support_0.sql", + "unique_id": "model.generated_9.stated_stg_support_0", + "fqn": [ + "generated_9", + "models", + "rendering", + "stated_stg_support_0" + ], + "alias": "stated_stg_support_0", + "config": { + "enabled": true, + "alias": null, + "schema": "ci_checks", + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "table", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Enum-typed copy of stg_support_0", + "columns": { + "id": { + "name": "id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "status": { + "name": "status", + "description": "", + "meta": {}, + "data_type": "VARCHAR", + "constraints": [], + "quote": null, + "tags": [] + }, + "tier": { + "name": "tier", + "description": "", + "meta": {}, + "data_type": "VARCHAR", + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "ci_checks.stated_stg_support_0", + "depends_on": { + "macros": [], + "nodes": [ + "model.generated_9.stg_support_0" + ] + } + }, + "model.generated_9.support_summary_0": { + "database": null, + "schema": "ci_checks", + "name": "support_summary_0", + "resource_type": "model", + "package_name": "generated_9", + "path": "models/support/marts/support_summary_0.sql", + "original_file_path": "models/support/marts/support_summary_0.sql", + "unique_id": "model.generated_9.support_summary_0", + "fqn": [ + "generated_9", + "models", + "support", + "marts", + "support_summary_0" + ], + "alias": "support_summary_0", + "config": { + "enabled": true, + "alias": null, + "schema": "ci_checks", + "database": null, + "tags": [ + "support", + "marts" + ], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [ + { + "sql": "SELECT 'ci_checks.support_summary_0' AS refreshed_relation, 'café ☕ counter' AS refresh_label", + "transaction": true, + "index": 0 + }, + { + "sql": "SELECT 'ci_checks.support_summary_0' AS refreshed", + "transaction": true, + "index": 1 + } + ], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [ + "support", + "marts" + ], + "description": "Summary of support activity 0", + "columns": {}, + "meta": { + "sqlbuild": { + "lifecycle_hooks": { + "post_hooks": [ + { + "type": "sql", + "statement": "SELECT 'ci_checks.support_summary_0' AS refreshed_relation, 'café ☕ counter' AS refresh_label", + "name": "record_refresh", + "relative_path": "hooks/sql/record_refresh.sql", + "definition_sql": "SELECT @'relation' AS refreshed_relation, @'label' AS refresh_label", + "kwargs": { + "relation": "@@CTX:destination.qualified", + "label": "café ☕ counter" + }, + "description": "Record a refresh for one relation" + }, + { + "type": "sql", + "statement": "SELECT 'ci_checks.support_summary_0' AS refreshed" + } + ] + } + } + }, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "ci_checks.support_summary_0", + "depends_on": { + "macros": [], + "nodes": [ + "model.generated_9.stg_support_0" + ] + } + }, + "model.generated_9.stg_support_0": { + "database": null, + "schema": "ci_checks", + "name": "stg_support_0", + "resource_type": "model", + "package_name": "generated_9", + "path": "models/support/staging/stg_support_0.sql", + "original_file_path": "models/support/staging/stg_support_0.sql", + "unique_id": "model.generated_9.stg_support_0", + "fqn": [ + "generated_9", + "models", + "support", + "staging", + "stg_support_0" + ], + "alias": "stg_support_0", + "config": { + "enabled": true, + "alias": null, + "schema": "ci_checks", + "database": null, + "tags": [ + "staging" + ], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [ + "staging" + ], + "description": "Staged support events 0", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "ci_checks.stg_support_0", + "depends_on": { + "macros": [], + "nodes": [ + "source.generated_9.raw_events_0" + ] + } + }, + "model.generated_9.stg_support_1": { + "database": null, + "schema": "ci_checks", + "name": "stg_support_1", + "resource_type": "model", + "package_name": "generated_9", + "path": "models/support/staging/stg_support_1.sql", + "original_file_path": "models/support/staging/stg_support_1.sql", + "unique_id": "model.generated_9.stg_support_1", + "fqn": [ + "generated_9", + "models", + "support", + "staging", + "stg_support_1" + ], + "alias": "stg_support_1", + "config": { + "enabled": true, + "alias": null, + "schema": "ci_checks", + "database": null, + "tags": [ + "staging" + ], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [ + "staging" + ], + "description": "Staged support events 1", + "columns": { + "id": { + "name": "id", + "description": "", + "meta": {}, + "data_type": null, + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "ci_checks.stg_support_1", + "depends_on": { + "macros": [], + "nodes": [ + "source.generated_9.raw_events_0" + ] + } + } + }, + "sources": { + "source.generated_9.configured_events": { + "database": null, + "schema": "", + "name": "configured_events", + "resource_type": "source", + "package_name": "generated_9", + "path": "sources/configured.yml", + "original_file_path": "sources/configured.yml", + "unique_id": "source.generated_9.configured_events", + "fqn": [ + "generated_9", + "configured_events" + ], + "source_name": "configured_events", + "source_description": "", + "loader": "", + "identifier": "configured_events", + "quoting": {}, + "loaded_at_field": null, + "freshness": null, + "external": null, + "description": "Configured feed events", + "columns": { + "id": { + "name": "id", + "description": "Key id", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "amount": { + "name": "amount", + "description": "", + "meta": {}, + "data_type": "DOUBLE", + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": { + "owner": "orders team" + }, + "source_meta": {}, + "tags": [], + "config": {}, + "patch_path": null, + "unrendered_config": {}, + "relation_name": null + }, + "source.generated_9.labelled_channels": { + "database": null, + "schema": "", + "name": "labelled_channels", + "resource_type": "source", + "package_name": "generated_9", + "path": "sources/labelled.yml", + "original_file_path": "sources/labelled.yml", + "unique_id": "source.generated_9.labelled_channels", + "fqn": [ + "generated_9", + "labelled_channels" + ], + "source_name": "labelled_channels", + "source_description": "", + "loader": "", + "identifier": "labelled_channels", + "quoting": {}, + "loaded_at_field": null, + "freshness": null, + "external": null, + "description": "Channel labels normalised in the source expression.", + "columns": { + "id": { + "name": "id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "channel": { + "name": "channel", + "description": "", + "meta": {}, + "data_type": "VARCHAR", + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": {}, + "source_meta": {}, + "tags": [], + "config": {}, + "patch_path": null, + "unrendered_config": {}, + "relation_name": null + }, + "source.generated_9.loaded_products": { + "database": null, + "schema": "ci_checks", + "name": "loaded_products", + "resource_type": "source", + "package_name": "generated_9", + "path": "sources/managed.yml", + "original_file_path": "sources/managed.yml", + "unique_id": "source.generated_9.loaded_products", + "fqn": [ + "generated_9", + "loaded_products" + ], + "source_name": "loaded_products", + "source_description": "", + "loader": "", + "identifier": "loaded_products", + "quoting": {}, + "loaded_at_field": null, + "freshness": null, + "external": null, + "description": "Products loaded by a project loader.", + "columns": { + "id": { + "name": "id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "label": { + "name": "label", + "description": "", + "meta": {}, + "data_type": "VARCHAR", + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": { + "sqlbuild": { + "loader": "loaded_products", + "auto_load": false, + "write_strategy": "table" + } + }, + "source_meta": {}, + "tags": [], + "config": {}, + "patch_path": null, + "unrendered_config": {}, + "relation_name": null + }, + "source.generated_9.open_events": { + "database": null, + "schema": "", + "name": "open_events", + "resource_type": "source", + "package_name": "generated_9", + "path": "sources/open.yml", + "original_file_path": "sources/open.yml", + "unique_id": "source.generated_9.open_events", + "fqn": [ + "generated_9", + "open_events" + ], + "source_name": "open_events", + "source_description": "", + "loader": "", + "identifier": "open_events", + "quoting": {}, + "loaded_at_field": null, + "freshness": null, + "external": null, + "description": "Events whose columns are only known to the warehouse.", + "columns": {}, + "meta": {}, + "source_meta": {}, + "tags": [], + "config": {}, + "patch_path": null, + "unrendered_config": {}, + "relation_name": null + }, + "source.generated_9.raw_events_0": { + "database": null, + "schema": "", + "name": "raw_events_0", + "resource_type": "source", + "package_name": "generated_9", + "path": "sources/raw.yml", + "original_file_path": "sources/raw.yml", + "unique_id": "source.generated_9.raw_events_0", + "fqn": [ + "generated_9", + "raw_events_0" + ], + "source_name": "raw_events_0", + "source_description": "", + "loader": "", + "identifier": "raw_events_0", + "quoting": {}, + "loaded_at_field": null, + "freshness": null, + "external": null, + "description": "Generated event feed 0.", + "columns": { + "id": { + "name": "id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "amount": { + "name": "amount", + "description": "", + "meta": {}, + "data_type": "DOUBLE", + "constraints": [], + "quote": null, + "tags": [] + }, + "status": { + "name": "status", + "description": "", + "meta": {}, + "data_type": "VARCHAR", + "constraints": [], + "quote": null, + "tags": [] + }, + "created_at": { + "name": "created_at", + "description": "", + "meta": {}, + "data_type": "TIMESTAMP", + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": {}, + "source_meta": {}, + "tags": [], + "config": {}, + "patch_path": null, + "unrendered_config": {}, + "relation_name": null + }, + "source.generated_9.raw_events_1": { + "database": null, + "schema": "", + "name": "raw_events_1", + "resource_type": "source", + "package_name": "generated_9", + "path": "sources/raw.yml", + "original_file_path": "sources/raw.yml", + "unique_id": "source.generated_9.raw_events_1", + "fqn": [ + "generated_9", + "raw_events_1" + ], + "source_name": "raw_events_1", + "source_description": "", + "loader": "", + "identifier": "raw_events_1", + "quoting": {}, + "loaded_at_field": null, + "freshness": null, + "external": null, + "description": "Generated event feed 1.", + "columns": { + "id": { + "name": "id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "amount": { + "name": "amount", + "description": "", + "meta": {}, + "data_type": "DOUBLE", + "constraints": [], + "quote": null, + "tags": [] + }, + "status": { + "name": "status", + "description": "", + "meta": {}, + "data_type": "VARCHAR", + "constraints": [], + "quote": null, + "tags": [] + }, + "created_at": { + "name": "created_at", + "description": "", + "meta": {}, + "data_type": "TIMESTAMP", + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": {}, + "source_meta": {}, + "tags": [], + "config": {}, + "patch_path": null, + "unrendered_config": {}, + "relation_name": null + } + }, + "macros": { + "macro.generated_9.adjust_0": { + "name": "adjust_0", + "resource_type": "macro", + "package_name": "generated_9", + "path": "macros/adjust_0.py", + "original_file_path": "macros/adjust_0.py", + "unique_id": "macro.generated_9.adjust_0", + "macro_sql": "def adjust_0(expression: str) -> str:\n \"\"\"Offset an amount.\"\"\"\n return f\"({expression} + 0 + 1)\"\n", + "depends_on": { + "macros": [] + }, + "description": "", + "meta": { + "sqlbuild_visibility": "global", + "sqlbuild_scope_path": null + }, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "arguments": [], + "supported_languages": null + }, + "macro.generated_9.tidy_label": { + "name": "tidy_label", + "resource_type": "macro", + "package_name": "generated_9", + "path": "macros/labels.py", + "original_file_path": "macros/labels.py", + "unique_id": "macro.generated_9.tidy_label", + "macro_sql": "def tidy_label(expression: str) -> str:\n \"\"\"Trim and lower-case a label.\"\"\"\n return f\"LOWER(TRIM({expression}))\"\n", + "depends_on": { + "macros": [] + }, + "description": "", + "meta": { + "sqlbuild_visibility": "global", + "sqlbuild_scope_path": null + }, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "arguments": [], + "supported_languages": null + }, + "macro.generated_9.legacy_events": { + "name": "legacy_events", + "resource_type": "macro", + "package_name": "generated_9", + "path": "models/rendering/_sqlbuild/_macros/legacy.py", + "original_file_path": "models/rendering/_sqlbuild/_macros/legacy.py", + "unique_id": "macro.generated_9.legacy_events", + "macro_sql": "def legacy_events() -> str:\n \"\"\"Return a staged relation the way a migrated project wrote it.\"\"\"\n return '__ref(\"stg_support_0\")'\n", + "depends_on": { + "macros": [] + }, + "description": "", + "meta": { + "sqlbuild_visibility": "local", + "sqlbuild_scope_path": "models/rendering" + }, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "arguments": [], + "supported_languages": null + }, + "macro.generated_9.event_rows": { + "name": "event_rows", + "resource_type": "macro", + "package_name": "generated_9", + "path": "tests/unit/rendering/_sqlbuild/_macros/fixtures.py", + "original_file_path": "tests/unit/rendering/_sqlbuild/_macros/fixtures.py", + "unique_id": "macro.generated_9.event_rows", + "macro_sql": "def event_rows(count: int = 2) -> str:\n \"\"\"Return fixture event rows.\"\"\"\n return \" UNION ALL \".join(\n f\"SELECT {index} AS id, CAST({index} AS DOUBLE) AS amount, \"\n f\"'placed' AS status, \"\n f\"TIMESTAMP '2026-01-0{index} 00:00:00' AS created_at\"\n for index in range(1, count + 1)\n )\n", + "depends_on": { + "macros": [] + }, + "description": "", + "meta": { + "sqlbuild_visibility": "local", + "sqlbuild_scope_path": "tests/unit/rendering" + }, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "arguments": [], + "supported_languages": null + }, + "macro.generated_9__sqlbuild_hooks.record_refresh": { + "name": "record_refresh", + "resource_type": "macro", + "package_name": "generated_9__sqlbuild_hooks", + "path": "hooks/sql/record_refresh.sql", + "original_file_path": "hooks/sql/record_refresh.sql", + "unique_id": "macro.generated_9__sqlbuild_hooks.record_refresh", + "macro_sql": "SELECT @'relation' AS refreshed_relation, @'label' AS refresh_label", + "depends_on": { + "macros": [] + }, + "description": "Record a refresh for one relation", + "meta": { + "sqlbuild_resource_type": "sql_hook", + "sqlbuild_hook_name": "record_refresh" + }, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "arguments": [], + "supported_languages": null + } + }, + "exposures": {}, + "metrics": {}, + "groups": {}, + "selectors": {}, + "disabled": {}, + "parent_map": { + "model.generated_9.combined_stg_billing_0": [ + "model.generated_9.stg_billing_0", + "model.generated_9.stg_support_0" + ], + "model.generated_9.every_open_event": [ + "source.generated_9.open_events" + ], + "model.generated_9.every_raw_events_1": [ + "source.generated_9.raw_events_1" + ], + "model.generated_9.every_stg_billing_0": [ + "model.generated_9.stg_billing_0" + ], + "model.generated_9.passed_stg_billing_0": [ + "model.generated_9.stg_billing_0" + ], + "model.generated_9.pivot_input_stg_support_0": [ + "model.generated_9.stg_support_0" + ], + "model.generated_9.pivoted_stg_support_0": [ + "model.generated_9.pivot_input_stg_support_0" + ], + "model.generated_9.quoted_stg_support_0": [ + "model.generated_9.stg_support_0" + ], + "model.generated_9.typed_stg_billing_0": [ + "model.generated_9.stg_billing_0" + ], + "model.generated_9.unanalysed_stg_support_1": [ + "model.generated_9.stg_support_1" + ], + "model.generated_9.billing_summary_0": [ + "model.generated_9.stg_billing_0" + ], + "model.generated_9.stg_billing_0": [ + "source.generated_9.raw_events_0" + ], + "model.generated_9.append_stg_support_0": [ + "model.generated_9.stg_support_0" + ], + "model.generated_9.batched_rolling_window_stg_support_1": [ + "model.generated_9.stg_support_1" + ], + "model.generated_9.delete_insert_stg_billing_0": [ + "model.generated_9.stg_billing_0" + ], + "model.generated_9.flagged_stg_billing_0": [ + "model.generated_9.is_placed", + "model.generated_9.is_large", + "model.generated_9.stg_billing_0" + ], + "model.generated_9.guarded_stg_billing_0": [ + "model.generated_9.stg_billing_0" + ], + "model.generated_9.kept_stg_support_1": [ + "model.generated_9.stg_support_1" + ], + "model.generated_9.product_labels": [ + "source.generated_9.loaded_products" + ], + "model.generated_9.spaced_stg_support_0": [ + "model.generated_9.stg_support_0" + ], + "model.generated_9.timestamp_history_stg_support_1": [ + "model.generated_9.stg_support_1" + ], + "model.generated_9.channel_labels": [ + "source.generated_9.labelled_channels" + ], + "model.generated_9.configured_stg_support_1": [ + "model.generated_9.stg_support_1" + ], + "model.generated_9.labelled_stg_support_1": [ + "model.generated_9.tidy_status", + "model.generated_9.stg_support_1" + ], + "model.generated_9.legacy_event_ids": [ + "model.generated_9.stg_support_0" + ], + "model.generated_9.stated_stg_support_0": [ + "model.generated_9.stg_support_0" + ], + "model.generated_9.support_summary_0": [ + "model.generated_9.stg_support_0" + ], + "model.generated_9.stg_support_0": [ + "source.generated_9.raw_events_0" + ], + "model.generated_9.stg_support_1": [ + "source.generated_9.raw_events_0" + ], + "source.generated_9.configured_events": [], + "source.generated_9.labelled_channels": [], + "source.generated_9.loaded_products": [], + "source.generated_9.open_events": [], + "source.generated_9.raw_events_0": [], + "source.generated_9.raw_events_1": [], + "model.generated_9.configured_label": [], + "model.generated_9.is_placed": [], + "model.generated_9.tidy_status": [], + "model.generated_9.is_large": [] + }, + "group_map": {}, + "docs": {}, + "saved_queries": {}, + "semantic_models": {}, + "unit_tests": {} + } +} diff --git a/tests/unit/scripts/compiler_differential/_helpers/goldens/__init__.py b/tests/unit/scripts/compiler_differential/_helpers/goldens/__init__.py new file mode 100644 index 0000000000..c18b17ecc8 --- /dev/null +++ b/tests/unit/scripts/compiler_differential/_helpers/goldens/__init__.py @@ -0,0 +1 @@ +"""Unit tests for the golden compile outputs.""" diff --git a/tests/unit/scripts/compiler_differential/_helpers/goldens/_test_types.py b/tests/unit/scripts/compiler_differential/_helpers/goldens/_test_types.py new file mode 100644 index 0000000000..9a73b9bfbb --- /dev/null +++ b/tests/unit/scripts/compiler_differential/_helpers/goldens/_test_types.py @@ -0,0 +1,38 @@ +"""Test case types for the golden compile outputs.""" + +from __future__ import annotations + +from dataclasses import dataclass +from pathlib import Path + +from scripts.compiler_differential.models import EngineRun + + +@dataclass(frozen=True) +class GoldenPathTestCase: + """A corpus project name and where its golden lives, if anywhere.""" + + description: str + project: str + expected_path: Path | None + + +@dataclass(frozen=True) +class GoldenPayloadTestCase: + """One engine run and the masked golden it must produce.""" + + description: str + run: EngineRun + masked_paths: tuple[str, ...] + expected_payload: dict[str, object] + + +@dataclass(frozen=True) +class GoldenCheckTestCase: + """A recorded golden, the runs checked against it, and the differences reported.""" + + description: str + project: str + recorded: tuple[EngineRun, ...] + checked: tuple[EngineRun, ...] + expected_differences: tuple[tuple[str, tuple[str, str] | None], ...] diff --git a/tests/unit/scripts/compiler_differential/_helpers/goldens/helpers.py b/tests/unit/scripts/compiler_differential/_helpers/goldens/helpers.py new file mode 100644 index 0000000000..a4eca5e4ff --- /dev/null +++ b/tests/unit/scripts/compiler_differential/_helpers/goldens/helpers.py @@ -0,0 +1,46 @@ +"""Engine runs for the golden output tests.""" + +from __future__ import annotations + +import json + +from scripts.compiler_differential.models import CommandOutcome, EngineRun + +PROJECT_PATH: str = "/work/failure__unknown-ref/project" + + +def engine_run(*, engine: str, message: str, compiled_sql: str) -> EngineRun: + """A one-command run whose first diagnostic carries `message` and one compiled model.""" + + report: dict[str, object] = { + "compile_timings": {"total": 1.5}, + "diagnostics": [{"code": "P001", "message": message, "severity": "error"}], + } + return EngineRun( + engine=engine, + outcomes=( + CommandOutcome( + label="compile", exit_code=1, stdout=json.dumps(report), stderr="done in 12ms\n" + ), + ), + compiled={"marts/customer_totals.sql": compiled_sql.encode("utf-8")}, + manifest=json.dumps( + { + "metadata": {"generated_at": "now"}, + "child_map": {}, + "nodes": { + "model.orders.customer_totals": { + "name": "customer_totals", + "compiled_code": compiled_sql, + "raw_code": "SELECT 1", + "checksum": {"checksum": "abc"}, + "config": {"materialized": "table"}, + } + }, + "sources": {}, + "macros": {}, + "parent_map": {"model.orders.customer_totals": []}, + } + ), + dag=None, + ) diff --git a/tests/unit/scripts/compiler_differential/_helpers/goldens/test_compiler_goldens.py b/tests/unit/scripts/compiler_differential/_helpers/goldens/test_compiler_goldens.py new file mode 100644 index 0000000000..2e761d9f32 --- /dev/null +++ b/tests/unit/scripts/compiler_differential/_helpers/goldens/test_compiler_goldens.py @@ -0,0 +1,183 @@ +"""Golden compile outputs are masked, located per corpus project, checked and rewritten.""" + +from __future__ import annotations + +from pathlib import Path + +import pytest + +from scripts.compiler_differential._helpers.goldens.goldens import ( + golden_differences, + golden_path, + golden_payload, +) +from scripts.compiler_differential.models import CorpusProject, Difference, ExpectedOutcome +from tests.unit.scripts.compiler_differential._helpers.goldens._test_types import ( + GoldenCheckTestCase, + GoldenPathTestCase, + GoldenPayloadTestCase, +) +from tests.unit.scripts.compiler_differential._helpers.goldens.helpers import ( + PROJECT_PATH, + engine_run, +) + +_SQL: str = "SELECT customer_id\nFROM orders -- 20261009T010203Z_0123456789ab\n" +_MESSAGE: str = f"model references unknown model 'ghost' in '{PROJECT_PATH}/models/a.sql'" + + +@pytest.mark.parametrize( + "test_case", + [ + GoldenPathTestCase( + description="seed", project="seed/3", expected_path=Path("goldens/seed/3.json") + ), + GoldenPathTestCase( + description="dialect_seed", + project="seed/0-snowflake", + expected_path=Path("goldens/seed/0-snowflake.json"), + ), + GoldenPathTestCase( + description="failure_case", + project="failure/unknown-ref", + expected_path=Path("goldens/failure/unknown-ref.json"), + ), + GoldenPathTestCase(description="dense_has_none", project="dense/3000", expected_path=None), + ], + ids=lambda case: case.description, +) +def test_given_corpus_project_when_locating_its_golden_then_only_golden_corpora_have_one( + test_case: GoldenPathTestCase, +) -> None: + path: Path | None = golden_path(golden_dir=Path("goldens"), project=test_case.project) + + assert path == test_case.expected_path + + +@pytest.mark.parametrize( + "test_case", + [ + GoldenPayloadTestCase( + description="paths_ids_timings_and_repeated_sql_masked", + run=engine_run(engine="python", message=_MESSAGE, compiled_sql=_SQL), + masked_paths=(PROJECT_PATH,), + expected_payload={ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "code": "P001", + "message": ( + "model references unknown model 'ghost' in " + "'/models/a.sql'" + ), + "severity": "error", + } + ], + } + ], + "compiled": { + "marts/customer_totals.sql": [ + "SELECT customer_id", + "FROM orders -- ", + "", + ] + }, + "manifest": { + "nodes": { + "model.orders.customer_totals": { + "name": "customer_totals", + "config": {"materialized": "table"}, + } + }, + "sources": {}, + "macros": {}, + "parent_map": {"model.orders.customer_totals": []}, + }, + }, + ) + ], + ids=lambda case: case.description, +) +def test_given_engine_run_when_building_golden_then_run_specific_noise_is_masked( + test_case: GoldenPayloadTestCase, +) -> None: + payload: dict[str, object] = golden_payload( + run=test_case.run, masked_paths=test_case.masked_paths + ) + + assert payload == test_case.expected_payload + + +@pytest.mark.parametrize( + "test_case", + [ + GoldenCheckTestCase( + description="identical_engines_match", + project="failure/unknown-ref", + recorded=(engine_run(engine="python", message=_MESSAGE, compiled_sql=_SQL),), + checked=( + engine_run(engine="python", message=_MESSAGE, compiled_sql=_SQL), + engine_run(engine="native", message=_MESSAGE, compiled_sql=_SQL), + ), + expected_differences=(), + ), + GoldenCheckTestCase( + description="changed_sql_names_the_engine", + project="failure/unknown-ref", + recorded=(engine_run(engine="python", message=_MESSAGE, compiled_sql=_SQL),), + checked=( + engine_run(engine="python", message=_MESSAGE, compiled_sql=_SQL), + engine_run(engine="native", message=_MESSAGE, compiled_sql="SELECT 2\n"), + ), + expected_differences=(("golden (native)", ("golden", "native")),), + ), + GoldenCheckTestCase( + description="missing_failure_golden_fails", + project="failure/unknown-ref", + recorded=(), + checked=(engine_run(engine="python", message=_MESSAGE, compiled_sql=_SQL),), + expected_differences=(("golden unknown-ref.json", ("golden", "hint")),), + ), + GoldenCheckTestCase( + description="missing_seed_golden_is_skipped", + project="seed/40", + recorded=(), + checked=(engine_run(engine="python", message=_MESSAGE, compiled_sql=_SQL),), + expected_differences=(), + ), + ], + ids=lambda case: case.description, +) +def test_given_recorded_golden_when_checking_runs_then_each_differing_engine_is_reported( + test_case: GoldenCheckTestCase, tmp_path: Path +) -> None: + project: CorpusProject = CorpusProject( + name=test_case.project, commands=(), expected=ExpectedOutcome() + ) + for recorded in test_case.recorded: + _ = golden_differences( + project=project, + runs=(recorded,), + golden_dir=tmp_path, + mode="update", + masked_paths=(PROJECT_PATH,), + ) + + differences: list[Difference] = golden_differences( + project=project, + runs=test_case.checked, + golden_dir=tmp_path, + mode="check", + masked_paths=(PROJECT_PATH,), + ) + + assert [(difference.artifact, difference.labels) for difference in differences] == list( + test_case.expected_differences + ) + + +if __name__ == "__main__": + pytest.main([__file__, "-vv"]) diff --git a/tests/unit/scripts/compiler_differential/_helpers/running/_test_types.py b/tests/unit/scripts/compiler_differential/_helpers/running/_test_types.py index 6233c40c85..00ac0da16f 100644 --- a/tests/unit/scripts/compiler_differential/_helpers/running/_test_types.py +++ b/tests/unit/scripts/compiler_differential/_helpers/running/_test_types.py @@ -5,7 +5,12 @@ from collections.abc import Callable from dataclasses import dataclass -from scripts.compiler_differential.models import ExpectedOutcome, ProjectComparison +from scripts.compiler_differential.models import ( + AnalysisRecords, + ExpectedOutcome, + ProjectComparison, + RecordedRun, +) @dataclass(frozen=True) @@ -74,4 +79,37 @@ class AnalysisRecordsTestCase: files: dict[str, str] expected_wheel_sites: dict[tuple[str, str], int] expected_deferrals: dict[tuple[str, str], int] + expected_fallbacks: dict[tuple[str, str], int] expected_lines: tuple[str, ...] + + +@dataclass(frozen=True) +class FallbackGateTestCase: + """An allow-list file, what one run observed, and the problems the gate must report.""" + + description: str + allow_list: str + observed: dict[tuple[str, str, str, str], dict[str, int]] + run: RecordedRun + expected_problems: list[str] + + +@dataclass(frozen=True) +class FallbackRecordsTestCase: + """Per-engine records of one project and the allow-list entries they add up to.""" + + description: str + project: str + records: tuple[AnalysisRecords, AnalysisRecords] + expected_observed: dict[tuple[str, str, str, str], dict[str, int]] + + +@dataclass(frozen=True) +class FallbackRewriteTestCase: + """An allow-list, a run that rewrites it, and the counts the rewritten list must hold.""" + + description: str + allow_list: str + observed: dict[tuple[str, str, str, str], dict[str, int]] + run: RecordedRun + expected_counts: dict[tuple[str, str, str, str], dict[str, int]] diff --git a/tests/unit/scripts/compiler_differential/_helpers/running/test_native_fallbacks.py b/tests/unit/scripts/compiler_differential/_helpers/running/test_native_fallbacks.py new file mode 100644 index 0000000000..c1fbb0f988 --- /dev/null +++ b/tests/unit/scripts/compiler_differential/_helpers/running/test_native_fallbacks.py @@ -0,0 +1,199 @@ +"""The fallback allow-list fails on new, changed and vanished entries and records new counts.""" + +from __future__ import annotations + +from pathlib import Path + +import pytest + +from scripts.compiler_differential._helpers.running.native_fallbacks import ( + fallback_problems, + observed_fallbacks, + read_allow_list, + write_allow_list, +) +from scripts.compiler_differential.models import ( + AllowList, + AnalysisRecords, + ProjectComparison, + RecordedRun, +) +from scripts.compiler_differential.types import FallbackKey +from tests.unit.scripts.compiler_differential._helpers.running._test_types import ( + FallbackGateTestCase, + FallbackRecordsTestCase, + FallbackRewriteTestCase, +) + +_VARIABLES: FallbackKey = ("native", "model_loop", "model_loop.sql_variables", "deferred") +_SESSION: FallbackKey = ("native-preview", "model_analysis", "analysis_session", "session") +_SHIPPED_RUN: RecordedRun = RecordedRun( + engines=("python", "native"), corpora=("seeds", "failures"), seed_start=0, seeds=12 +) +_LIST_HEAD: str = "seed_start = 0\nseeds = 12\n" +_VARIABLES_ENTRY: str = ( + '\n[[entry]]\nengine = "native"\nstage = "model_loop"\n' + 'site = "model_loop.sql_variables"\nkind = "deferred"\n' +) + + +@pytest.mark.parametrize( + "test_case", + [ + FallbackRecordsTestCase( + description="fallbacks_and_deferrals_keyed_by_engine_stage_site_kind", + project="seed/3", + records=( + AnalysisRecords(), + AnalysisRecords( + fallbacks={("model_loop.sql_variables", "deferred"): 2}, + deferrals={("session", "analysis_session"): 1}, + ), + ), + expected_observed={ + ("native-preview", "model_loop", "model_loop.sql_variables", "deferred"): { + "seed": 2 + }, + _SESSION: {"seed": 1}, + }, + ) + ], + ids=lambda case: case.description, +) +def test_given_engine_records_when_observing_then_entries_are_summed_per_corpus( + test_case: FallbackRecordsTestCase, +) -> None: + comparison: ProjectComparison = ProjectComparison( + project=test_case.project, differences=(), seconds=1.0, records=test_case.records + ) + + observed: dict[FallbackKey, dict[str, int]] = observed_fallbacks( + comparisons=[comparison], engines=("python", "native-preview") + ) + + assert observed == test_case.expected_observed + + +@pytest.mark.parametrize( + "test_case", + [ + FallbackGateTestCase( + description="exact_counts_pass", + allow_list=_LIST_HEAD + _VARIABLES_ENTRY + "counts = { failure = 3, seed = 195 }\n", + observed={_VARIABLES: {"failure": 3, "seed": 195}}, + run=_SHIPPED_RUN, + expected_problems=[], + ), + FallbackGateTestCase( + description="unlisted_entry_fails", + allow_list=_LIST_HEAD, + observed={_VARIABLES: {"seed": 195}}, + run=_SHIPPED_RUN, + expected_problems=[ + "native model_loop model_loop.sql_variables deferred (seed): 195 not on the " + "allow-list; port it or list it with a reason" + ], + ), + FallbackGateTestCase( + description="vanished_entry_fails_until_removed", + allow_list=_LIST_HEAD + _VARIABLES_ENTRY + "counts = { seed = 195 }\n", + observed={}, + run=_SHIPPED_RUN, + expected_problems=[ + "native model_loop model_loop.sql_variables deferred (seed): listed but no " + "longer occurs; remove it from the allow-list" + ], + ), + FallbackGateTestCase( + description="changed_count_fails", + allow_list=_LIST_HEAD + _VARIABLES_ENTRY + "counts = { seed = 195 }\n", + observed={_VARIABLES: {"seed": 196}}, + run=_SHIPPED_RUN, + expected_problems=[ + "native model_loop model_loop.sql_variables deferred (seed): 196 where the " + "allow-list expects 195; update the count" + ], + ), + FallbackGateTestCase( + description="upper_bound_allows_fewer_but_not_more", + allow_list=_LIST_HEAD + _VARIABLES_ENTRY + "max_counts = { failure = 5, seed = 200 }\n", + observed={_VARIABLES: {"failure": 6, "seed": 150}}, + run=_SHIPPED_RUN, + expected_problems=[ + "native model_loop model_loop.sql_variables deferred (failure): 6 exceeds the " + "allowed 5" + ], + ), + FallbackGateTestCase( + description="engines_and_corpora_outside_the_run_are_not_checked", + allow_list=_LIST_HEAD + + _VARIABLES_ENTRY + + "counts = { fixture = 4, seed = 195 }\n" + + '\n[[entry]]\nengine = "native-preview"\nstage = "model_analysis"\n' + + 'site = "analysis_session"\nkind = "session"\ncounts = { seed = 1 }\n', + observed={_VARIABLES: {"seed": 195}}, + run=_SHIPPED_RUN, + expected_problems=[], + ), + FallbackGateTestCase( + description="other_seed_range_is_refused", + allow_list=_LIST_HEAD, + observed={}, + run=RecordedRun( + engines=("python", "native"), corpora=("seeds",), seed_start=0, seeds=60 + ), + expected_problems=[ + "the allow-list holds counts for --seed-start 0 --seeds 12; this run used " + "--seed-start 0 --seeds 60" + ], + ), + ], + ids=lambda case: case.description, +) +def test_given_allow_list_when_checking_a_run_then_every_difference_is_a_problem( + test_case: FallbackGateTestCase, tmp_path: Path +) -> None: + path: Path = tmp_path / "native_fallbacks.toml" + _ = path.write_text(test_case.allow_list, encoding="utf-8") + + problems: list[str] = fallback_problems( + observed=test_case.observed, allowed=read_allow_list(path), run=test_case.run + ) + + assert problems == test_case.expected_problems + + +@pytest.mark.parametrize( + "test_case", + [ + FallbackRewriteTestCase( + description="other_engines_kept_and_the_run_recounted", + allow_list=_LIST_HEAD + + '\n[[entry]]\nengine = "native-preview"\nstage = "model_analysis"\n' + + 'site = "analysis_session"\nkind = "session"\ncounts = { seed = 1 }\n' + + _VARIABLES_ENTRY + + "counts = { seed = 9 }\n", + observed={_VARIABLES: {"failure": 3, "seed": 195}}, + run=_SHIPPED_RUN, + expected_counts={_SESSION: {"seed": 1}, _VARIABLES: {"failure": 3, "seed": 195}}, + ) + ], + ids=lambda case: case.description, +) +def test_given_recorded_run_when_rewriting_then_other_engines_are_kept_and_the_run_passes( + test_case: FallbackRewriteTestCase, tmp_path: Path +) -> None: + path: Path = tmp_path / "native_fallbacks.toml" + _ = path.write_text(test_case.allow_list, encoding="utf-8") + + write_allow_list(path=path, observed=test_case.observed, run=test_case.run) + + rewritten: AllowList = read_allow_list(path) + assert rewritten.counts == test_case.expected_counts + assert ( + fallback_problems(observed=test_case.observed, allowed=rewritten, run=test_case.run) == [] + ) + + +if __name__ == "__main__": + pytest.main([__file__, "-vv"]) diff --git a/tests/unit/scripts/compiler_differential/_helpers/running/test_records.py b/tests/unit/scripts/compiler_differential/_helpers/running/test_records.py index 2f3a00bccd..ffac8427af 100644 --- a/tests/unit/scripts/compiler_differential/_helpers/running/test_records.py +++ b/tests/unit/scripts/compiler_differential/_helpers/running/test_records.py @@ -1,4 +1,4 @@ -"""Wheel-site and deferral records are summed per engine, site and corpus, never gated.""" +"""Wheel-site, deferral and fallback records are summed per engine, site and corpus.""" from __future__ import annotations @@ -33,16 +33,26 @@ "2-plan/analysis-deferrals-12.jsonl": ( json.dumps({"kind": "legacy_fallback", "site": "compact.py"}) + "\n" ), + "0-compile/native-fallbacks-11.json": json.dumps( + {"fallbacks": [["model_loop.sql_variables", "deferred", 2]]} + ), + "2-plan/native-fallbacks-12.json": json.dumps( + {"fallbacks": [["model_loop.sql_variables", "deferred", 1]]} + ), }, expected_wheel_sites={(_SITE, "parse_one"): 5}, expected_deferrals={("legacy_fallback", "compact.py"): 1}, + expected_fallbacks={("model_loop.sql_variables", "deferred"): 3}, expected_lines=( "Polyglot wheel calls (python):", f" 5 {_SITE} parse_one (seed 5)", "Analysis deferrals (python):", " 1 legacy_fallback compact.py (seed 1)", + "Native-to-Python fallbacks (python):", + " 3 model_loop.sql_variables deferred (seed 3)", "Polyglot wheel calls (native-preview): none recorded", "Analysis deferrals (native-preview): none recorded", + "Native-to-Python fallbacks (native-preview): none recorded", ), ) ], @@ -71,9 +81,10 @@ def test_given_record_files_when_reporting_then_counts_are_summed_per_engine_and ) ) - assert (records.wheel_sites, records.deferrals) == ( + assert (records.wheel_sites, records.deferrals, records.fallbacks) == ( test_case.expected_wheel_sites, test_case.expected_deferrals, + test_case.expected_fallbacks, ) assert report.splitlines() == list(test_case.expected_lines) diff --git a/tests/unit/src/sqlbuild/compiler/frontier/_test_types.py b/tests/unit/src/sqlbuild/compiler/frontier/_test_types.py index 232c66c2c5..51aa39f3d7 100644 --- a/tests/unit/src/sqlbuild/compiler/frontier/_test_types.py +++ b/tests/unit/src/sqlbuild/compiler/frontier/_test_types.py @@ -5,7 +5,12 @@ from collections.abc import Callable from dataclasses import dataclass -from sqlbuild.compiler.frontier.types import CompilerEngine, CompilerStage, NativeStage +from sqlbuild.compiler.frontier.types import ( + CompilerEngine, + CompilerStage, + NativeFallbackSite, + NativeStage, +) @dataclass(frozen=True) @@ -143,3 +148,12 @@ class NativeStageCouplingTestCase: dependent: NativeStage dependency: NativeStage expected_engines_without_dependency: frozenset[CompilerEngine] + + +@dataclass(frozen=True) +class NativeFallbackRecorderTestCase: + """Fallbacks one process records and the record file it writes at exit.""" + + description: str + recorded: tuple[tuple[NativeFallbackSite, str], ...] + expected_files: list[list[list[object]]] diff --git a/tests/unit/src/sqlbuild/compiler/frontier/test_native_fallback_recorder.py b/tests/unit/src/sqlbuild/compiler/frontier/test_native_fallback_recorder.py new file mode 100644 index 0000000000..ef6d634e54 --- /dev/null +++ b/tests/unit/src/sqlbuild/compiler/frontier/test_native_fallback_recorder.py @@ -0,0 +1,64 @@ +"""Native-to-Python fallbacks are counted per site and kind and written once per process.""" + +from __future__ import annotations + +import json +import os +from pathlib import Path + +import pytest + +from sqlbuild.compiler.frontier.classes.native_fallback_recorder import NativeFallbackRecorder +from sqlbuild.compiler.frontier.constants import NATIVE_FALLBACK_RECORD_PREFIX +from sqlbuild.compiler.frontier.types import NativeFallbackSite +from tests.unit.src.sqlbuild.compiler.frontier._test_types import NativeFallbackRecorderTestCase + + +@pytest.mark.parametrize( + "test_case", + [ + NativeFallbackRecorderTestCase( + description="counts_per_site_and_kind_sorted", + recorded=( + (NativeFallbackSite.SQL_VARIABLES, "deferred"), + (NativeFallbackSite.REFERENCE_SCAN, "deferred"), + (NativeFallbackSite.SQL_VARIABLES, "deferred"), + (NativeFallbackSite.SQL_VARIABLES, "error"), + ), + expected_files=[ + [ + ["model_loop.sql_variables", "deferred", 2], + ["model_loop.sql_variables", "error", 1], + ["reference_extraction.scan", "deferred", 1], + ] + ], + ), + NativeFallbackRecorderTestCase( + description="no_fallback_writes_no_file", + recorded=(), + expected_files=[], + ), + ], + ids=lambda case: case.description, +) +def test_given_recorded_fallbacks_when_writing_then_one_file_holds_their_counts( + test_case: NativeFallbackRecorderTestCase, tmp_path: Path +) -> None: + recorder: NativeFallbackRecorder = NativeFallbackRecorder(directory=tmp_path / "records") + for site, kind in test_case.recorded: + recorder.record(site=site.value, kind=kind) + + recorder.write() + + written: list[list[list[object]]] = [ + json.loads(path.read_text(encoding="utf-8"))["fallbacks"] + for path in sorted((tmp_path / "records").glob(f"{NATIVE_FALLBACK_RECORD_PREFIX}*")) + ] + assert [path.name for path in (tmp_path / "records").glob("*")] == [ + f"{NATIVE_FALLBACK_RECORD_PREFIX}{os.getpid()}.json" + ] * len(test_case.expected_files) + assert written == test_case.expected_files + + +if __name__ == "__main__": + pytest.main([__file__, "-vv"]) From 0a806f687fbe131d8fd518e1661fa3c99c1892e7 Mon Sep 17 00:00:00 2001 From: Kevin Longe <273747162+kvlonge@users.noreply.github.com> Date: Fri, 9 Oct 2026 07:31:32 +0100 Subject: [PATCH 13/31] test(native): record goldens and allow-list entries for the merged semantic checks --- scripts/compiler_differential/constants.py | 3 + .../native_fallbacks.toml | 21 +- ...mantic-qualified-alias-unknown-column.json | 277 +++++++++++++++++ .../semantic-recovered-downstream-uses.json | 278 ++++++++++++++++++ ...ntic-rejected-opt-out-counts-findings.json | 276 +++++++++++++++++ .../semantic-temporal-comparison-help.json | 278 ++++++++++++++++++ .../semantic-type-error-unchecked-uses.json | 278 ++++++++++++++++++ 7 files changed, 1404 insertions(+), 7 deletions(-) create mode 100644 tests/goldens/compiler/failure/semantic-qualified-alias-unknown-column.json create mode 100644 tests/goldens/compiler/failure/semantic-recovered-downstream-uses.json create mode 100644 tests/goldens/compiler/failure/semantic-rejected-opt-out-counts-findings.json create mode 100644 tests/goldens/compiler/failure/semantic-temporal-comparison-help.json create mode 100644 tests/goldens/compiler/failure/semantic-type-error-unchecked-uses.json diff --git a/scripts/compiler_differential/constants.py b/scripts/compiler_differential/constants.py index fd38e6e757..f26dc7a323 100644 --- a/scripts/compiler_differential/constants.py +++ b/scripts/compiler_differential/constants.py @@ -21,6 +21,9 @@ NATIVE_FALLBACK_DEFERRAL_STAGES: dict[str, str] = { "analysis_session": "model_analysis", "fast_columns.py": "lineage_facts", + "type_recovery.py": "semantic_checks", + "recovery.py": "semantic_checks", + "metadata_validation.py": "semantic_checks", } NATIVE_FALLBACK_DEFERRAL_UNKNOWN_STAGE: str = "analysis" NATIVE_FALLBACK_LIST_HEADER: str = ( diff --git a/scripts/compiler_differential/native_fallbacks.toml b/scripts/compiler_differential/native_fallbacks.toml index 85d81f8cb2..3fca9ce038 100644 --- a/scripts/compiler_differential/native_fallbacks.toml +++ b/scripts/compiler_differential/native_fallbacks.toml @@ -79,14 +79,14 @@ engine = "native" stage = "model_config" site = "model_config.python_templates" kind = "config" -counts = { failure = 93, seed = 36 } +counts = { failure = 98, seed = 36 } [[entry]] engine = "native" stage = "model_config" site = "model_config.python_templates" kind = "connection" -counts = { failure = 95, seed = 63 } +counts = { failure = 100, seed = 63 } [[entry]] engine = "native" @@ -114,7 +114,7 @@ engine = "native" stage = "model_loop" site = "model_loop.cursor_intrinsic_validation" kind = "deferred" -counts = { failure = 356, seed = 1100 } +counts = { failure = 366, seed = 1100 } [[entry]] engine = "native" @@ -191,7 +191,7 @@ engine = "native-preview" stage = "model_analysis" site = "analysis_session" kind = "input_enrichment" -counts = { example = 52, failure = 160, fixture = 52, seed = 614 } +counts = { example = 52, failure = 170, fixture = 52, seed = 614 } [[entry]] engine = "native-preview" @@ -226,14 +226,14 @@ engine = "native-preview" stage = "model_config" site = "model_config.python_templates" kind = "config" -counts = { example = 12, failure = 93, fixture = 8, seed = 36 } +counts = { example = 12, failure = 98, fixture = 8, seed = 36 } [[entry]] engine = "native-preview" stage = "model_config" site = "model_config.python_templates" kind = "connection" -counts = { example = 18, failure = 95, fixture = 9, seed = 63 } +counts = { example = 18, failure = 100, fixture = 9, seed = 63 } [[entry]] engine = "native-preview" @@ -261,7 +261,7 @@ engine = "native-preview" stage = "model_loop" site = "model_loop.cursor_intrinsic_validation" kind = "deferred" -counts = { example = 56, failure = 356, fixture = 64, seed = 1100 } +counts = { example = 56, failure = 366, fixture = 64, seed = 1100 } [[entry]] engine = "native-preview" @@ -283,3 +283,10 @@ stage = "model_loop" site = "model_loop.sql_variables" kind = "error" counts = { failure = 2 } + +[[entry]] +engine = "native-preview" +stage = "semantic_checks" +site = "metadata_validation.py" +kind = "metadata_checks" +counts = { example = 12, failure = 95, fixture = 8, seed = 36 } diff --git a/tests/goldens/compiler/failure/semantic-qualified-alias-unknown-column.json b/tests/goldens/compiler/failure/semantic-qualified-alias-unknown-column.json new file mode 100644 index 0000000000..662ed8627a --- /dev/null +++ b/tests/goldens/compiler/failure/semantic-qualified-alias-unknown-column.json @@ -0,0 +1,277 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "B002", + "message": "Unknown column 'amonut' in stg_orders (as o)", + "resource_type": "model", + "resource_name": "customer_totals", + "path": "models/marts/customer_totals.sql", + "line": 5, + "column": 29, + "location": { + "path": "models/marts/customer_totals.sql", + "line": 5, + "column": 29, + "end_line": 5, + "end_column": 35 + }, + "help": "did you mean 'amount'?", + "notes": [ + "stg_orders has: amount, status, customer_id, order_id" + ] + } + ] + } + ], + "compiled": { + "models/marts/customer_totals.sql": [ + "SELECT o.customer_id, SUM(o.amonut) AS total_amount", + "FROM __ref(\"stg_orders\") AS o", + "GROUP BY o.customer_id", + "" + ], + "models/staging/stg_orders.sql": [ + "SELECT order_id, customer_id, amount, status", + "FROM __source(\"raw_orders\")", + "" + ] + }, + "manifest": { + "nodes": { + "model.failure_corpus.customer_totals": { + "database": null, + "schema": "main", + "name": "customer_totals", + "resource_type": "model", + "package_name": "failure_corpus", + "path": "models/marts/customer_totals.sql", + "original_file_path": "models/marts/customer_totals.sql", + "unique_id": "model.failure_corpus.customer_totals", + "fqn": [ + "failure_corpus", + "models", + "marts", + "customer_totals" + ], + "alias": "customer_totals", + "config": { + "enabled": true, + "alias": null, + "schema": null, + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Order totals per customer", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.customer_totals", + "depends_on": { + "macros": [], + "nodes": [ + "model.failure_corpus.stg_orders" + ] + } + }, + "model.failure_corpus.stg_orders": { + "database": null, + "schema": "main", + "name": "stg_orders", + "resource_type": "model", + "package_name": "failure_corpus", + "path": "models/staging/stg_orders.sql", + "original_file_path": "models/staging/stg_orders.sql", + "unique_id": "model.failure_corpus.stg_orders", + "fqn": [ + "failure_corpus", + "models", + "staging", + "stg_orders" + ], + "alias": "stg_orders", + "config": { + "enabled": true, + "alias": null, + "schema": null, + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Staged orders", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.stg_orders", + "depends_on": { + "macros": [], + "nodes": [ + "source.failure_corpus.raw_orders" + ] + } + } + }, + "sources": { + "source.failure_corpus.raw_orders": { + "database": null, + "schema": "", + "name": "raw_orders", + "resource_type": "source", + "package_name": "failure_corpus", + "path": "sources/raw.yml", + "original_file_path": "sources/raw.yml", + "unique_id": "source.failure_corpus.raw_orders", + "fqn": [ + "failure_corpus", + "raw_orders" + ], + "source_name": "raw_orders", + "source_description": "", + "loader": "", + "identifier": "raw_orders", + "quoting": {}, + "loaded_at_field": null, + "freshness": null, + "external": null, + "description": "Orders feed.", + "columns": { + "order_id": { + "name": "order_id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "customer_id": { + "name": "customer_id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "amount": { + "name": "amount", + "description": "", + "meta": {}, + "data_type": "DOUBLE", + "constraints": [], + "quote": null, + "tags": [] + }, + "status": { + "name": "status", + "description": "", + "meta": {}, + "data_type": "VARCHAR", + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": {}, + "source_meta": {}, + "tags": [], + "config": {}, + "patch_path": null, + "unrendered_config": {}, + "relation_name": null + } + }, + "macros": {}, + "exposures": {}, + "metrics": {}, + "groups": {}, + "selectors": {}, + "disabled": {}, + "parent_map": { + "model.failure_corpus.customer_totals": [ + "model.failure_corpus.stg_orders" + ], + "model.failure_corpus.stg_orders": [ + "source.failure_corpus.raw_orders" + ], + "source.failure_corpus.raw_orders": [] + }, + "group_map": {}, + "docs": {}, + "saved_queries": {}, + "semantic_models": {}, + "unit_tests": {} + } +} diff --git a/tests/goldens/compiler/failure/semantic-recovered-downstream-uses.json b/tests/goldens/compiler/failure/semantic-recovered-downstream-uses.json new file mode 100644 index 0000000000..6a894d3e8f --- /dev/null +++ b/tests/goldens/compiler/failure/semantic-recovered-downstream-uses.json @@ -0,0 +1,278 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "B002", + "message": "Unknown column 'amonut' in raw_orders", + "resource_type": "model", + "resource_name": "stg_orders", + "path": "models/staging/stg_orders.sql", + "line": 5, + "column": 31, + "location": { + "path": "models/staging/stg_orders.sql", + "line": 5, + "column": 31, + "end_line": 5, + "end_column": 37 + }, + "help": "did you mean 'amount'?", + "notes": [ + "raw_orders has: amount, status, customer_id, order_id", + "1 downstream uses of stg_orders.amount were not checked because of this error" + ] + } + ] + } + ], + "compiled": { + "models/marts/customer_totals.sql": [ + "SELECT customer_id, SUM(amount) AS total_amount", + "FROM __ref(\"stg_orders\")", + "GROUP BY customer_id", + "" + ], + "models/staging/stg_orders.sql": [ + "SELECT order_id, customer_id, amonut, status", + "FROM __source(\"raw_orders\")", + "" + ] + }, + "manifest": { + "nodes": { + "model.failure_corpus.customer_totals": { + "database": null, + "schema": "main", + "name": "customer_totals", + "resource_type": "model", + "package_name": "failure_corpus", + "path": "models/marts/customer_totals.sql", + "original_file_path": "models/marts/customer_totals.sql", + "unique_id": "model.failure_corpus.customer_totals", + "fqn": [ + "failure_corpus", + "models", + "marts", + "customer_totals" + ], + "alias": "customer_totals", + "config": { + "enabled": true, + "alias": null, + "schema": null, + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Order totals per customer", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.customer_totals", + "depends_on": { + "macros": [], + "nodes": [ + "model.failure_corpus.stg_orders" + ] + } + }, + "model.failure_corpus.stg_orders": { + "database": null, + "schema": "main", + "name": "stg_orders", + "resource_type": "model", + "package_name": "failure_corpus", + "path": "models/staging/stg_orders.sql", + "original_file_path": "models/staging/stg_orders.sql", + "unique_id": "model.failure_corpus.stg_orders", + "fqn": [ + "failure_corpus", + "models", + "staging", + "stg_orders" + ], + "alias": "stg_orders", + "config": { + "enabled": true, + "alias": null, + "schema": null, + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Staged orders", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.stg_orders", + "depends_on": { + "macros": [], + "nodes": [ + "source.failure_corpus.raw_orders" + ] + } + } + }, + "sources": { + "source.failure_corpus.raw_orders": { + "database": null, + "schema": "", + "name": "raw_orders", + "resource_type": "source", + "package_name": "failure_corpus", + "path": "sources/raw.yml", + "original_file_path": "sources/raw.yml", + "unique_id": "source.failure_corpus.raw_orders", + "fqn": [ + "failure_corpus", + "raw_orders" + ], + "source_name": "raw_orders", + "source_description": "", + "loader": "", + "identifier": "raw_orders", + "quoting": {}, + "loaded_at_field": null, + "freshness": null, + "external": null, + "description": "Orders feed.", + "columns": { + "order_id": { + "name": "order_id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "customer_id": { + "name": "customer_id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "amount": { + "name": "amount", + "description": "", + "meta": {}, + "data_type": "DOUBLE", + "constraints": [], + "quote": null, + "tags": [] + }, + "status": { + "name": "status", + "description": "", + "meta": {}, + "data_type": "VARCHAR", + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": {}, + "source_meta": {}, + "tags": [], + "config": {}, + "patch_path": null, + "unrendered_config": {}, + "relation_name": null + } + }, + "macros": {}, + "exposures": {}, + "metrics": {}, + "groups": {}, + "selectors": {}, + "disabled": {}, + "parent_map": { + "model.failure_corpus.customer_totals": [ + "model.failure_corpus.stg_orders" + ], + "model.failure_corpus.stg_orders": [ + "source.failure_corpus.raw_orders" + ], + "source.failure_corpus.raw_orders": [] + }, + "group_map": {}, + "docs": {}, + "saved_queries": {}, + "semantic_models": {}, + "unit_tests": {} + } +} diff --git a/tests/goldens/compiler/failure/semantic-rejected-opt-out-counts-findings.json b/tests/goldens/compiler/failure/semantic-rejected-opt-out-counts-findings.json new file mode 100644 index 0000000000..588ae44763 --- /dev/null +++ b/tests/goldens/compiler/failure/semantic-rejected-opt-out-counts-findings.json @@ -0,0 +1,276 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "P009", + "message": "`sql_analysis false` is not needed for model 'customer_totals'", + "resource_type": "model", + "resource_name": "customer_totals", + "path": "models/marts/customer_totals.sql", + "line": 3, + "column": 3, + "location": { + "path": "models/marts/customer_totals.sql", + "line": 3, + "column": 3 + }, + "help": "remove `sql_analysis false` and fix the findings it was hiding: 2 unknown columns, 1 type mismatch (run `sqb compile` to see them)\n = help: to allow `sql_analysis false` on any model, SQL test or audit, set this in sqlbuild_project.toml:\n [settings]\n require_sql_analysis = false", + "notes": [ + "sqlbuild_project.toml sets [settings] require_sql_analysis = true, which only allows `sql_analysis false` on SQL that SQLBuild cannot parse; this model parses successfully." + ] + } + ] + } + ], + "compiled": { + "models/marts/customer_totals.sql": [ + "SELECT customer_id, SUM(refund) AS total_amount, MAX(amonut) AS top_amount", + "FROM __ref(\"stg_orders\")", + "WHERE status > 5", + "GROUP BY customer_id", + "" + ], + "models/staging/stg_orders.sql": [ + "SELECT order_id, customer_id, amount, status", + "FROM __source(\"raw_orders\")", + "" + ] + }, + "manifest": { + "nodes": { + "model.failure_corpus.customer_totals": { + "database": null, + "schema": "main", + "name": "customer_totals", + "resource_type": "model", + "package_name": "failure_corpus", + "path": "models/marts/customer_totals.sql", + "original_file_path": "models/marts/customer_totals.sql", + "unique_id": "model.failure_corpus.customer_totals", + "fqn": [ + "failure_corpus", + "models", + "marts", + "customer_totals" + ], + "alias": "customer_totals", + "config": { + "enabled": true, + "alias": null, + "schema": null, + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Order totals per customer", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.customer_totals", + "depends_on": { + "macros": [], + "nodes": [ + "model.failure_corpus.stg_orders" + ] + } + }, + "model.failure_corpus.stg_orders": { + "database": null, + "schema": "main", + "name": "stg_orders", + "resource_type": "model", + "package_name": "failure_corpus", + "path": "models/staging/stg_orders.sql", + "original_file_path": "models/staging/stg_orders.sql", + "unique_id": "model.failure_corpus.stg_orders", + "fqn": [ + "failure_corpus", + "models", + "staging", + "stg_orders" + ], + "alias": "stg_orders", + "config": { + "enabled": true, + "alias": null, + "schema": null, + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Staged orders", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.stg_orders", + "depends_on": { + "macros": [], + "nodes": [ + "source.failure_corpus.raw_orders" + ] + } + } + }, + "sources": { + "source.failure_corpus.raw_orders": { + "database": null, + "schema": "", + "name": "raw_orders", + "resource_type": "source", + "package_name": "failure_corpus", + "path": "sources/raw.yml", + "original_file_path": "sources/raw.yml", + "unique_id": "source.failure_corpus.raw_orders", + "fqn": [ + "failure_corpus", + "raw_orders" + ], + "source_name": "raw_orders", + "source_description": "", + "loader": "", + "identifier": "raw_orders", + "quoting": {}, + "loaded_at_field": null, + "freshness": null, + "external": null, + "description": "Orders feed.", + "columns": { + "order_id": { + "name": "order_id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "customer_id": { + "name": "customer_id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "amount": { + "name": "amount", + "description": "", + "meta": {}, + "data_type": "DOUBLE", + "constraints": [], + "quote": null, + "tags": [] + }, + "status": { + "name": "status", + "description": "", + "meta": {}, + "data_type": "VARCHAR", + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": {}, + "source_meta": {}, + "tags": [], + "config": {}, + "patch_path": null, + "unrendered_config": {}, + "relation_name": null + } + }, + "macros": {}, + "exposures": {}, + "metrics": {}, + "groups": {}, + "selectors": {}, + "disabled": {}, + "parent_map": { + "model.failure_corpus.customer_totals": [ + "model.failure_corpus.stg_orders" + ], + "model.failure_corpus.stg_orders": [ + "source.failure_corpus.raw_orders" + ], + "source.failure_corpus.raw_orders": [] + }, + "group_map": {}, + "docs": {}, + "saved_queries": {}, + "semantic_models": {}, + "unit_tests": {} + } +} diff --git a/tests/goldens/compiler/failure/semantic-temporal-comparison-help.json b/tests/goldens/compiler/failure/semantic-temporal-comparison-help.json new file mode 100644 index 0000000000..877af0724a --- /dev/null +++ b/tests/goldens/compiler/failure/semantic-temporal-comparison-help.json @@ -0,0 +1,278 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "B217", + "message": "Incompatible comparison between TIMESTAMP and INTEGER", + "resource_type": "model", + "resource_name": "customer_totals", + "path": "models/marts/customer_totals.sql", + "line": 7, + "column": 7, + "location": { + "path": "models/marts/customer_totals.sql", + "line": 7, + "column": 7, + "end_line": 7, + "end_column": 23 + }, + "help": "compare with a timestamp, for example TIMESTAMP '2026-04-01'", + "notes": [ + "o.ordered_at is TIMESTAMP, 5 is INTEGER" + ] + } + ] + } + ], + "compiled": { + "models/marts/customer_totals.sql": [ + "SELECT o.customer_id, SUM(o.amount) AS total_amount", + "FROM __ref(\"stg_orders\") AS o", + "WHERE o.ordered_at > 5", + "GROUP BY o.customer_id", + "" + ], + "models/staging/stg_orders.sql": [ + "SELECT order_id, customer_id, amount, status, CAST('2026-01-01' AS TIMESTAMP) AS ordered_at", + "FROM __source(\"raw_orders\")", + "" + ] + }, + "manifest": { + "nodes": { + "model.failure_corpus.customer_totals": { + "database": null, + "schema": "main", + "name": "customer_totals", + "resource_type": "model", + "package_name": "failure_corpus", + "path": "models/marts/customer_totals.sql", + "original_file_path": "models/marts/customer_totals.sql", + "unique_id": "model.failure_corpus.customer_totals", + "fqn": [ + "failure_corpus", + "models", + "marts", + "customer_totals" + ], + "alias": "customer_totals", + "config": { + "enabled": true, + "alias": null, + "schema": null, + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Order totals per customer", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.customer_totals", + "depends_on": { + "macros": [], + "nodes": [ + "model.failure_corpus.stg_orders" + ] + } + }, + "model.failure_corpus.stg_orders": { + "database": null, + "schema": "main", + "name": "stg_orders", + "resource_type": "model", + "package_name": "failure_corpus", + "path": "models/staging/stg_orders.sql", + "original_file_path": "models/staging/stg_orders.sql", + "unique_id": "model.failure_corpus.stg_orders", + "fqn": [ + "failure_corpus", + "models", + "staging", + "stg_orders" + ], + "alias": "stg_orders", + "config": { + "enabled": true, + "alias": null, + "schema": null, + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Staged orders", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.stg_orders", + "depends_on": { + "macros": [], + "nodes": [ + "source.failure_corpus.raw_orders" + ] + } + } + }, + "sources": { + "source.failure_corpus.raw_orders": { + "database": null, + "schema": "", + "name": "raw_orders", + "resource_type": "source", + "package_name": "failure_corpus", + "path": "sources/raw.yml", + "original_file_path": "sources/raw.yml", + "unique_id": "source.failure_corpus.raw_orders", + "fqn": [ + "failure_corpus", + "raw_orders" + ], + "source_name": "raw_orders", + "source_description": "", + "loader": "", + "identifier": "raw_orders", + "quoting": {}, + "loaded_at_field": null, + "freshness": null, + "external": null, + "description": "Orders feed.", + "columns": { + "order_id": { + "name": "order_id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "customer_id": { + "name": "customer_id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "amount": { + "name": "amount", + "description": "", + "meta": {}, + "data_type": "DOUBLE", + "constraints": [], + "quote": null, + "tags": [] + }, + "status": { + "name": "status", + "description": "", + "meta": {}, + "data_type": "VARCHAR", + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": {}, + "source_meta": {}, + "tags": [], + "config": {}, + "patch_path": null, + "unrendered_config": {}, + "relation_name": null + } + }, + "macros": {}, + "exposures": {}, + "metrics": {}, + "groups": {}, + "selectors": {}, + "disabled": {}, + "parent_map": { + "model.failure_corpus.customer_totals": [ + "model.failure_corpus.stg_orders" + ], + "model.failure_corpus.stg_orders": [ + "source.failure_corpus.raw_orders" + ], + "source.failure_corpus.raw_orders": [] + }, + "group_map": {}, + "docs": {}, + "saved_queries": {}, + "semantic_models": {}, + "unit_tests": {} + } +} diff --git a/tests/goldens/compiler/failure/semantic-type-error-unchecked-uses.json b/tests/goldens/compiler/failure/semantic-type-error-unchecked-uses.json new file mode 100644 index 0000000000..6b3daf288b --- /dev/null +++ b/tests/goldens/compiler/failure/semantic-type-error-unchecked-uses.json @@ -0,0 +1,278 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "B212", + "message": "Arithmetic operation expects NUMERIC-compatible operands, found STRING and INTEGER", + "resource_type": "model", + "resource_name": "stg_orders", + "path": "models/staging/stg_orders.sql", + "line": 5, + "column": 31, + "location": { + "path": "models/staging/stg_orders.sql", + "line": 5, + "column": 31, + "end_line": 5, + "end_column": 41 + }, + "help": "use operands supported by this arithmetic operator, or convert them explicitly", + "notes": [ + "1 downstream output uses were not type-checked because of this error", + "status is TEXT, 1 is INTEGER" + ] + } + ] + } + ], + "compiled": { + "models/marts/customer_totals.sql": [ + "SELECT customer_id, SUM(amount) AS total_amount", + "FROM __ref(\"stg_orders\")", + "GROUP BY customer_id", + "" + ], + "models/staging/stg_orders.sql": [ + "SELECT order_id, customer_id, status + 1 AS amount, status", + "FROM __source(\"raw_orders\")", + "" + ] + }, + "manifest": { + "nodes": { + "model.failure_corpus.customer_totals": { + "database": null, + "schema": "main", + "name": "customer_totals", + "resource_type": "model", + "package_name": "failure_corpus", + "path": "models/marts/customer_totals.sql", + "original_file_path": "models/marts/customer_totals.sql", + "unique_id": "model.failure_corpus.customer_totals", + "fqn": [ + "failure_corpus", + "models", + "marts", + "customer_totals" + ], + "alias": "customer_totals", + "config": { + "enabled": true, + "alias": null, + "schema": null, + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Order totals per customer", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.customer_totals", + "depends_on": { + "macros": [], + "nodes": [ + "model.failure_corpus.stg_orders" + ] + } + }, + "model.failure_corpus.stg_orders": { + "database": null, + "schema": "main", + "name": "stg_orders", + "resource_type": "model", + "package_name": "failure_corpus", + "path": "models/staging/stg_orders.sql", + "original_file_path": "models/staging/stg_orders.sql", + "unique_id": "model.failure_corpus.stg_orders", + "fqn": [ + "failure_corpus", + "models", + "staging", + "stg_orders" + ], + "alias": "stg_orders", + "config": { + "enabled": true, + "alias": null, + "schema": null, + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Staged orders", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.stg_orders", + "depends_on": { + "macros": [], + "nodes": [ + "source.failure_corpus.raw_orders" + ] + } + } + }, + "sources": { + "source.failure_corpus.raw_orders": { + "database": null, + "schema": "", + "name": "raw_orders", + "resource_type": "source", + "package_name": "failure_corpus", + "path": "sources/raw.yml", + "original_file_path": "sources/raw.yml", + "unique_id": "source.failure_corpus.raw_orders", + "fqn": [ + "failure_corpus", + "raw_orders" + ], + "source_name": "raw_orders", + "source_description": "", + "loader": "", + "identifier": "raw_orders", + "quoting": {}, + "loaded_at_field": null, + "freshness": null, + "external": null, + "description": "Orders feed.", + "columns": { + "order_id": { + "name": "order_id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "customer_id": { + "name": "customer_id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "amount": { + "name": "amount", + "description": "", + "meta": {}, + "data_type": "DOUBLE", + "constraints": [], + "quote": null, + "tags": [] + }, + "status": { + "name": "status", + "description": "", + "meta": {}, + "data_type": "VARCHAR", + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": {}, + "source_meta": {}, + "tags": [], + "config": {}, + "patch_path": null, + "unrendered_config": {}, + "relation_name": null + } + }, + "macros": {}, + "exposures": {}, + "metrics": {}, + "groups": {}, + "selectors": {}, + "disabled": {}, + "parent_map": { + "model.failure_corpus.customer_totals": [ + "model.failure_corpus.stg_orders" + ], + "model.failure_corpus.stg_orders": [ + "source.failure_corpus.raw_orders" + ], + "source.failure_corpus.raw_orders": [] + }, + "group_map": {}, + "docs": {}, + "saved_queries": {}, + "semantic_models": {}, + "unit_tests": {} + } +} From c815a7afb792264fe5557c7f70d463c87ed25a30 Mon Sep 17 00:00:00 2001 From: Kevin Longe <273747162+kvlonge@users.noreply.github.com> Date: Fri, 9 Oct 2026 07:43:58 +0100 Subject: [PATCH 14/31] test(native): regenerate goldens and the fallback allow-list from main with one command --- Makefile | 21 +- scripts/compiler_differential/README.md | 68 +-- scripts/compiler_differential/constants.py | 4 +- .../native_fallbacks.toml | 21 +- ...contract-dynamic-family-type-mismatch.json | 328 +++++++++++++++ .../contract-errors-across-models.json | 380 +++++++++++++++++ ...ct-immediate-promotion-several-models.json | 321 ++++++++++++++ .../contract-implicit-missing-column.json | 303 +++++++++++++ .../contract-local-immediate-promotion.json | 302 +++++++++++++ .../contract-named-schema-columns.json | 332 +++++++++++++++ .../contract-order-every-column-family.json | 398 ++++++++++++++++++ ...semantic-cursor-inputs-unknown-column.json | 274 ++++++++++++ ...semantic-source-cursor-unknown-column.json | 330 +++++++++++++++ ...ic-udf-qualified-cast-argument-family.json | 281 +++++++++++++ 14 files changed, 3310 insertions(+), 53 deletions(-) create mode 100644 tests/goldens/compiler/failure/contract-dynamic-family-type-mismatch.json create mode 100644 tests/goldens/compiler/failure/contract-errors-across-models.json create mode 100644 tests/goldens/compiler/failure/contract-immediate-promotion-several-models.json create mode 100644 tests/goldens/compiler/failure/contract-implicit-missing-column.json create mode 100644 tests/goldens/compiler/failure/contract-local-immediate-promotion.json create mode 100644 tests/goldens/compiler/failure/contract-named-schema-columns.json create mode 100644 tests/goldens/compiler/failure/contract-order-every-column-family.json create mode 100644 tests/goldens/compiler/failure/semantic-cursor-inputs-unknown-column.json create mode 100644 tests/goldens/compiler/failure/semantic-source-cursor-unknown-column.json create mode 100644 tests/goldens/compiler/failure/semantic-udf-qualified-cast-argument-family.json diff --git a/Makefile b/Makefile index 7ca518ad55..8fefcca499 100644 --- a/Makefile +++ b/Makefile @@ -8,7 +8,7 @@ SHELL := /bin/bash test-e2e-cold-compile-performance test-e2e-cache-compile-performance \ test-e2e-dense-compile-performance test-e2e-varied-cache-performance \ compiler-differential compiler-differential-shipped compiler-differential-dense \ - compiler-goldens + compiler-baselines format: uv run ruff format . @@ -295,8 +295,7 @@ COMPILER_DIFFERENTIAL_SHIPPED_SEEDS ?= 12 COMPILER_DIFFERENTIAL_DENSE_MODELS ?= 3000 COMPILER_DIFFERENTIAL_ARGS ?= # Native-to-Python fallbacks and analysis deferrals must match the allow-list exactly, and every -# output its golden. Pass `--native-fallbacks update` in COMPILER_DIFFERENTIAL_ARGS to rewrite -# the list (see scripts/compiler_differential/README.md). +# output its golden. `make compiler-baselines` rewrites both (see scripts/compiler_differential/README.md). COMPILER_DIFFERENTIAL_GATES := --native-fallbacks check --goldens check compiler-differential: @@ -312,13 +311,15 @@ compiler-differential-shipped: --jobs $(COMPILER_DIFFERENTIAL_JOBS) --seeds $(COMPILER_DIFFERENTIAL_SHIPPED_SEEDS) \ $(COMPILER_DIFFERENTIAL_GATES) $(COMPILER_DIFFERENTIAL_ARGS) -# Rewrite tests/goldens/compiler from the python oracle (from native once Python is deleted); -# review the diff like code. See scripts/compiler_differential/README.md. -compiler-goldens: - env PYTHONUNBUFFERED=1 uv run python -m scripts.run_compiler_differential \ - --engines python native --corpus fixtures examples seeds failures \ - --jobs $(COMPILER_DIFFERENTIAL_JOBS) --seeds $(COMPILER_DIFFERENTIAL_SEEDS) \ - --goldens update $(COMPILER_DIFFERENTIAL_ARGS) +# Rewrite the goldens (from the python oracle) and the fallback allow-list from both CI runs. +# Review the diff like code; see scripts/compiler_differential/README.md. +COMPILER_DIFFERENTIAL_CI_ARGS := --stage-captures --require-discovery-coverage --require-render-coverage +compiler-baselines: + rm -rf tests/goldens/compiler + $(MAKE) compiler-differential COMPILER_DIFFERENTIAL_ARGS="$(COMPILER_DIFFERENTIAL_CI_ARGS) \ + --require-analysis-coverage --native-fallbacks update --goldens update" + $(MAKE) compiler-differential-shipped COMPILER_DIFFERENTIAL_ARGS="$(COMPILER_DIFFERENTIAL_CI_ARGS) \ + --native-fallbacks update --goldens update" compiler-differential-dense: env PYTHONUNBUFFERED=1 uv run python -m scripts.run_compiler_differential \ diff --git a/scripts/compiler_differential/README.md b/scripts/compiler_differential/README.md index 9a7da926d9..f7008bb79e 100644 --- a/scripts/compiler_differential/README.md +++ b/scripts/compiler_differential/README.md @@ -15,7 +15,8 @@ does not add its own parity harness, property suite or E2E file. `native_fallbacks.toml` is the one allow-list of all work native code still hands back to Python. It covers both kinds of record: - shipped-stage fallbacks, from `report_native_fallback(site=NativeFallbackSite., kind=...)`; -- preview analysis-stage deferrals, from `record_analysis_deferral` and `record_lineage_deferral`. +- preview analysis-stage deferrals: every `analysis-deferrals-*.jsonl` record (analysis session, + lineage, semantic checks, contracts). Every entry names an engine, stage, site and kind, with its exact count per corpus. Use `max_counts` instead of `counts` only for a count that is not stable, and give the reason in a @@ -29,28 +30,38 @@ sets. `--native-fallbacks check` runs in both make targets and fails CI in both The counts hold for the make targets' corpus (`--seeds 12`); other seed ranges are refused. -**How a PR updates the list** - -1. A port deletes the Python code it replaces in the same PR. Its sites drop to zero and the gate - names the entries to remove; removing them is the port's test that the fallbacks are gone. - When the last entry for a `NativeFallbackSite` goes, delete the member and its - `report_native_fallback` call too. -2. If the corpus does not reach a site yet, first add a failure case or seed feature that does. -3. To rewrite the list from a run, use the same arguments as CI and add `--native-fallbacks - update` to both targets: - - ```sh - make compiler-differential COMPILER_DIFFERENTIAL_ARGS="--stage-captures \ - --require-discovery-coverage --require-render-coverage --require-analysis-coverage \ - --native-fallbacks update" - make compiler-differential-shipped COMPILER_DIFFERENTIAL_ARGS="--stage-captures \ - --require-discovery-coverage --require-render-coverage --native-fallbacks update" - ``` - - Review the diff. Any added entry or higher count needs a reason in the PR body; never add one - just to make CI pass. -4. The gate is proven by sabotage in `test_native_fallback_gate.py`: forcing a native stage to - defer everything must fail it. A new "native did the work" gate needs the same proof. +**How a PR updates the list and the goldens** + +One command rewrites both from the current tree: + +```sh +make compiler-baselines +``` + +It deletes `tests/goldens/compiler`, then runs the two CI differentials with `--native-fallbacks +update --goldens update`. Commit the resulting `native_fallbacks.toml` and `tests/goldens/` diff +in the same PR as the change that caused it. + +- A port deletes the Python code it replaces in the same PR. Its entries then disappear from the + list, and that removal is the port's test that the fallbacks are gone. When the last entry for + a `NativeFallbackSite` goes, delete the member and its `report_native_fallback` call too. +- If the corpus does not reach a site yet, first add a failure case or seed feature that does. +- A PR that merges after a list or golden change on main reruns `make compiler-baselines` after + rebasing and commits the result. + +**What a reviewer checks in the diff** + +- `native_fallbacks.toml`: + - Removed entries and lower counts are expected from ports. + - Every added entry, higher count or new `max_counts` needs a stated reason in the PR body. + Never add one just to make CI pass. +- Goldens: + - Every changed golden is an intended output change, named in the PR body. + - An output-neutral port changes no golden. + - New goldens belong to new corpus cases. +- Proof by sabotage: `test_native_fallback_gate.py` proves the gate by sabotage, so forcing a + native stage to defer everything must fail it. A new "native did the work" gate needs the same + proof. ### Golden outputs @@ -63,16 +74,17 @@ Project paths, the SQLBuild version, invocation ids and timings are masked. - `--goldens check` (in both make targets) compares every engine with the goldens, so an output-neutral port needs no new parity suite. -- `make compiler-goldens` rewrites them from the python oracle. Run it only when output changes - on purpose, and review the diff like code. +- `make compiler-baselines` rewrites them from the python oracle. Run it only when output + changes on purpose, and review the diff like code. - Seeds outside the recorded range have no golden and are skipped; any other project without a golden fails. - Once the Python stages are deleted, goldens are rewritten from `native`. ### All-engine user-facing errors -`engine_error_cases()` in `_helpers/corpus/failure_cases.py` lists user-facing errors with their exact text. Each case -is part of the failure corpus, so it runs in both differentials and has a golden. It also runs in +`engine_error_cases()` in `_helpers/corpus/failure_cases.py` lists user-facing errors with their +exact text. Each case is part of the failure corpus, so it runs in both differentials and has a +golden. It also runs in `tests/e2e/scripts/compiler_differential/test_engine_error_cases.py` on the `python`, `native` and `native-preview` engines, which must all report its `expected_code` and `expected_message`. @@ -87,4 +99,4 @@ failure_case( ) ``` -Then it runs `make compiler-goldens` to record the new case's golden. +Then it runs `make compiler-baselines` to record the new case's golden. diff --git a/scripts/compiler_differential/constants.py b/scripts/compiler_differential/constants.py index f26dc7a323..c7e8d68783 100644 --- a/scripts/compiler_differential/constants.py +++ b/scripts/compiler_differential/constants.py @@ -24,6 +24,8 @@ "type_recovery.py": "semantic_checks", "recovery.py": "semantic_checks", "metadata_validation.py": "semantic_checks", + "contracts/columns.py": "contracts", + "contracts/promotion.py": "contracts", } NATIVE_FALLBACK_DEFERRAL_UNKNOWN_STAGE: str = "analysis" NATIVE_FALLBACK_LIST_HEADER: str = ( @@ -48,7 +50,7 @@ ) GOLDEN_PATH_MASK: str = "" GOLDEN_VERSION_MASK: str = "" -GOLDEN_MISSING_HINT: str = "no golden; record it with `make compiler-goldens`" +GOLDEN_MISSING_HINT: str = "no golden; record it with `make compiler-baselines`" ENGINE_NAMES: tuple[str, ...] = ("python", "native", "native-preview") DEFAULT_ENGINES: tuple[str, str] = ("python", "native-preview") SQB_ENTRY: str = "import sys; from sqlbuild.cli.entry.main.entry import main; sys.exit(main())" diff --git a/scripts/compiler_differential/native_fallbacks.toml b/scripts/compiler_differential/native_fallbacks.toml index 3fca9ce038..fec5686009 100644 --- a/scripts/compiler_differential/native_fallbacks.toml +++ b/scripts/compiler_differential/native_fallbacks.toml @@ -79,14 +79,14 @@ engine = "native" stage = "model_config" site = "model_config.python_templates" kind = "config" -counts = { failure = 98, seed = 36 } +counts = { failure = 108, seed = 36 } [[entry]] engine = "native" stage = "model_config" site = "model_config.python_templates" kind = "connection" -counts = { failure = 100, seed = 63 } +counts = { failure = 110, seed = 63 } [[entry]] engine = "native" @@ -114,7 +114,7 @@ engine = "native" stage = "model_loop" site = "model_loop.cursor_intrinsic_validation" kind = "deferred" -counts = { failure = 366, seed = 1100 } +counts = { failure = 387, seed = 1100 } [[entry]] engine = "native" @@ -191,7 +191,7 @@ engine = "native-preview" stage = "model_analysis" site = "analysis_session" kind = "input_enrichment" -counts = { example = 52, failure = 170, fixture = 52, seed = 614 } +counts = { example = 52, failure = 182, fixture = 52, seed = 614 } [[entry]] engine = "native-preview" @@ -226,14 +226,14 @@ engine = "native-preview" stage = "model_config" site = "model_config.python_templates" kind = "config" -counts = { example = 12, failure = 98, fixture = 8, seed = 36 } +counts = { example = 12, failure = 108, fixture = 8, seed = 36 } [[entry]] engine = "native-preview" stage = "model_config" site = "model_config.python_templates" kind = "connection" -counts = { example = 18, failure = 100, fixture = 9, seed = 63 } +counts = { example = 18, failure = 110, fixture = 9, seed = 63 } [[entry]] engine = "native-preview" @@ -261,7 +261,7 @@ engine = "native-preview" stage = "model_loop" site = "model_loop.cursor_intrinsic_validation" kind = "deferred" -counts = { example = 56, failure = 366, fixture = 64, seed = 1100 } +counts = { example = 56, failure = 387, fixture = 64, seed = 1100 } [[entry]] engine = "native-preview" @@ -283,10 +283,3 @@ stage = "model_loop" site = "model_loop.sql_variables" kind = "error" counts = { failure = 2 } - -[[entry]] -engine = "native-preview" -stage = "semantic_checks" -site = "metadata_validation.py" -kind = "metadata_checks" -counts = { example = 12, failure = 95, fixture = 8, seed = 36 } diff --git a/tests/goldens/compiler/failure/contract-dynamic-family-type-mismatch.json b/tests/goldens/compiler/failure/contract-dynamic-family-type-mismatch.json new file mode 100644 index 0000000000..fda60c4471 --- /dev/null +++ b/tests/goldens/compiler/failure/contract-dynamic-family-type-mismatch.json @@ -0,0 +1,328 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "contract", + "severity": "error", + "code": "K002", + "message": "dynamic column family 'status_amounts' inferred as DOUBLE but declared type is VARCHAR", + "resource_type": "model", + "resource_name": "customer_totals", + "path": "models/marts/customer_totals.sql", + "help": "correct the family type or the pivot aggregate input type" + } + ] + } + ], + "compiled": { + "models/marts/customer_totals.sql": [ + "PIVOT __ref(\"stg_orders\")", + "ON status", + "USING MAX(amount)", + "GROUP BY customer_id", + "" + ], + "models/staging/stg_orders.sql": [ + "SELECT CAST(order_id AS INTEGER) AS order_id,", + " CAST(customer_id AS INTEGER) AS customer_id,", + " CAST(amount AS DOUBLE) AS amount, CAST(status AS VARCHAR) AS status", + "FROM __source(\"raw_orders\")", + "" + ] + }, + "manifest": { + "nodes": { + "model.failure_corpus.customer_totals": { + "database": null, + "schema": "main", + "name": "customer_totals", + "resource_type": "model", + "package_name": "failure_corpus", + "path": "models/marts/customer_totals.sql", + "original_file_path": "models/marts/customer_totals.sql", + "unique_id": "model.failure_corpus.customer_totals", + "fqn": [ + "failure_corpus", + "models", + "marts", + "customer_totals" + ], + "alias": "customer_totals", + "config": { + "enabled": true, + "alias": null, + "schema": null, + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "table", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Order amounts per status", + "columns": { + "customer_id": { + "name": "customer_id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": { + "sqlbuild": { + "dynamic_columns": [ + { + "name": "status_amounts", + "pivot_column": "status", + "value_column": "amount", + "aggregate": "MAX", + "type": "VARCHAR", + "name_pattern": null + } + ] + } + }, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.customer_totals", + "depends_on": { + "macros": [], + "nodes": [ + "model.failure_corpus.stg_orders" + ] + } + }, + "model.failure_corpus.stg_orders": { + "database": null, + "schema": "main", + "name": "stg_orders", + "resource_type": "model", + "package_name": "failure_corpus", + "path": "models/staging/stg_orders.sql", + "original_file_path": "models/staging/stg_orders.sql", + "unique_id": "model.failure_corpus.stg_orders", + "fqn": [ + "failure_corpus", + "models", + "staging", + "stg_orders" + ], + "alias": "stg_orders", + "config": { + "enabled": true, + "alias": null, + "schema": null, + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "table", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Staged orders", + "columns": { + "order_id": { + "name": "order_id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "customer_id": { + "name": "customer_id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "amount": { + "name": "amount", + "description": "", + "meta": {}, + "data_type": "DOUBLE", + "constraints": [], + "quote": null, + "tags": [] + }, + "status": { + "name": "status", + "description": "", + "meta": {}, + "data_type": "VARCHAR", + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.stg_orders", + "depends_on": { + "macros": [], + "nodes": [ + "source.failure_corpus.raw_orders" + ] + } + } + }, + "sources": { + "source.failure_corpus.raw_orders": { + "database": null, + "schema": "", + "name": "raw_orders", + "resource_type": "source", + "package_name": "failure_corpus", + "path": "sources/raw.yml", + "original_file_path": "sources/raw.yml", + "unique_id": "source.failure_corpus.raw_orders", + "fqn": [ + "failure_corpus", + "raw_orders" + ], + "source_name": "raw_orders", + "source_description": "", + "loader": "", + "identifier": "raw_orders", + "quoting": {}, + "loaded_at_field": null, + "freshness": null, + "external": null, + "description": "Orders feed.", + "columns": { + "order_id": { + "name": "order_id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "customer_id": { + "name": "customer_id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "amount": { + "name": "amount", + "description": "", + "meta": {}, + "data_type": "DOUBLE", + "constraints": [], + "quote": null, + "tags": [] + }, + "status": { + "name": "status", + "description": "", + "meta": {}, + "data_type": "VARCHAR", + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": {}, + "source_meta": {}, + "tags": [], + "config": {}, + "patch_path": null, + "unrendered_config": {}, + "relation_name": null + } + }, + "macros": {}, + "exposures": {}, + "metrics": {}, + "groups": {}, + "selectors": {}, + "disabled": {}, + "parent_map": { + "model.failure_corpus.customer_totals": [ + "model.failure_corpus.stg_orders" + ], + "model.failure_corpus.stg_orders": [ + "source.failure_corpus.raw_orders" + ], + "source.failure_corpus.raw_orders": [] + }, + "group_map": {}, + "docs": {}, + "saved_queries": {}, + "semantic_models": {}, + "unit_tests": {} + } +} diff --git a/tests/goldens/compiler/failure/contract-errors-across-models.json b/tests/goldens/compiler/failure/contract-errors-across-models.json new file mode 100644 index 0000000000..76a591bf7f --- /dev/null +++ b/tests/goldens/compiler/failure/contract-errors-across-models.json @@ -0,0 +1,380 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "contract", + "severity": "error", + "code": "K002", + "message": "column 'total_amount' inferred as DOUBLE but declared type is BIGINT", + "resource_type": "model", + "resource_name": "typed_totals", + "column_name": "total_amount", + "path": "models/marts/typed_totals.sql", + "line": 3, + "column": 12, + "location": { + "path": "models/marts/typed_totals.sql", + "line": 3, + "column": 12, + "end_line": 3, + "end_column": 24 + }, + "related_locations": [ + { + "label": "output", + "location": { + "path": "models/marts/typed_totals.sql", + "line": 6, + "column": 8, + "end_line": 6, + "end_column": 43 + }, + "message": "inferred DOUBLE" + } + ], + "help": "change the declared type or cast the expression explicitly" + }, + { + "phase": "contract", + "severity": "error", + "code": "K006", + "message": "model 'stg_orders' has contract enforced but declares no columns", + "resource_type": "model", + "resource_name": "stg_orders", + "path": "models/staging/stg_orders.sql", + "help": "add MODEL(columns (...)), declare a proven MODEL(dynamic_columns (...)) family, or set contract none for this model" + } + ] + } + ], + "compiled": { + "models/marts/customer_totals.sql": [ + "SELECT customer_id, CAST(SUM(amount) AS VARCHAR) AS total_amount", + "FROM __ref(\"stg_orders\")", + "GROUP BY customer_id", + "" + ], + "models/marts/typed_totals.sql": [ + "SELECT CAST(1.5 AS DOUBLE) AS total_amount", + "" + ], + "models/staging/stg_orders.sql": [ + "SELECT order_id, customer_id, amount, status", + "FROM __source(\"raw_orders\")", + "" + ] + }, + "manifest": { + "nodes": { + "model.failure_corpus.customer_totals": { + "database": null, + "schema": "main", + "name": "customer_totals", + "resource_type": "model", + "package_name": "failure_corpus", + "path": "models/marts/customer_totals.sql", + "original_file_path": "models/marts/customer_totals.sql", + "unique_id": "model.failure_corpus.customer_totals", + "fqn": [ + "failure_corpus", + "models", + "marts", + "customer_totals" + ], + "alias": "customer_totals", + "config": { + "enabled": true, + "alias": null, + "schema": null, + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Order totals per customer", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.customer_totals", + "depends_on": { + "macros": [], + "nodes": [ + "model.failure_corpus.stg_orders" + ] + } + }, + "model.failure_corpus.typed_totals": { + "database": null, + "schema": "main", + "name": "typed_totals", + "resource_type": "model", + "package_name": "failure_corpus", + "path": "models/marts/typed_totals.sql", + "original_file_path": "models/marts/typed_totals.sql", + "unique_id": "model.failure_corpus.typed_totals", + "fqn": [ + "failure_corpus", + "models", + "marts", + "typed_totals" + ], + "alias": "typed_totals", + "config": { + "enabled": true, + "alias": null, + "schema": null, + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Typed totals", + "columns": { + "total_amount": { + "name": "total_amount", + "description": "", + "meta": {}, + "data_type": "BIGINT", + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.typed_totals", + "depends_on": { + "macros": [], + "nodes": [] + } + }, + "model.failure_corpus.stg_orders": { + "database": null, + "schema": "main", + "name": "stg_orders", + "resource_type": "model", + "package_name": "failure_corpus", + "path": "models/staging/stg_orders.sql", + "original_file_path": "models/staging/stg_orders.sql", + "unique_id": "model.failure_corpus.stg_orders", + "fqn": [ + "failure_corpus", + "models", + "staging", + "stg_orders" + ], + "alias": "stg_orders", + "config": { + "enabled": true, + "alias": null, + "schema": null, + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Staged orders", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.stg_orders", + "depends_on": { + "macros": [], + "nodes": [ + "source.failure_corpus.raw_orders" + ] + } + } + }, + "sources": { + "source.failure_corpus.raw_orders": { + "database": null, + "schema": "", + "name": "raw_orders", + "resource_type": "source", + "package_name": "failure_corpus", + "path": "sources/raw.yml", + "original_file_path": "sources/raw.yml", + "unique_id": "source.failure_corpus.raw_orders", + "fqn": [ + "failure_corpus", + "raw_orders" + ], + "source_name": "raw_orders", + "source_description": "", + "loader": "", + "identifier": "raw_orders", + "quoting": {}, + "loaded_at_field": null, + "freshness": null, + "external": null, + "description": "Orders feed.", + "columns": { + "order_id": { + "name": "order_id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "customer_id": { + "name": "customer_id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "amount": { + "name": "amount", + "description": "", + "meta": {}, + "data_type": "DOUBLE", + "constraints": [], + "quote": null, + "tags": [] + }, + "status": { + "name": "status", + "description": "", + "meta": {}, + "data_type": "VARCHAR", + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": {}, + "source_meta": {}, + "tags": [], + "config": {}, + "patch_path": null, + "unrendered_config": {}, + "relation_name": null + } + }, + "macros": {}, + "exposures": {}, + "metrics": {}, + "groups": {}, + "selectors": {}, + "disabled": {}, + "parent_map": { + "model.failure_corpus.customer_totals": [ + "model.failure_corpus.stg_orders" + ], + "model.failure_corpus.typed_totals": [], + "model.failure_corpus.stg_orders": [ + "source.failure_corpus.raw_orders" + ], + "source.failure_corpus.raw_orders": [] + }, + "group_map": {}, + "docs": {}, + "saved_queries": {}, + "semantic_models": {}, + "unit_tests": {} + } +} diff --git a/tests/goldens/compiler/failure/contract-immediate-promotion-several-models.json b/tests/goldens/compiler/failure/contract-immediate-promotion-several-models.json new file mode 100644 index 0000000000..c6fab88b86 --- /dev/null +++ b/tests/goldens/compiler/failure/contract-immediate-promotion-several-models.json @@ -0,0 +1,321 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "contract", + "severity": "error", + "code": "K011", + "message": "model 'customer_totals': contract enforced requires staged table promotion; immediate table promotion cannot validate runtime output before target mutation", + "resource_type": "model", + "resource_name": "customer_totals", + "path": "models/marts/customer_totals.sql", + "help": "sqlbuild_project.toml sets [settings] table_promotion_mode = \"immediate\"; enforced contracts are validated in a staging table before promotion\n = help: remove that line to use the default staged promotion, or set this in sqlbuild_project.toml:\n [settings]\n table_promotion_mode = \"staged\"\n = help: or remove the model's enforced contract: `contract enforced` in its MODEL header, or `contract = \"enforced\"` in the [defaults] or [path_defaults] entry that applies to it" + } + ] + } + ], + "compiled": { + "models/marts/customer_totals.sql": [ + "SELECT customer_id, SUM(amount) AS total_amount", + "FROM __ref(\"stg_orders\")", + "GROUP BY customer_id", + "" + ], + "models/staging/stg_orders.sql": [ + "SELECT order_id, customer_id, amount, status", + "FROM __source(\"raw_orders\")", + "" + ] + }, + "manifest": { + "nodes": { + "model.failure_corpus.customer_totals": { + "database": null, + "schema": "main", + "name": "customer_totals", + "resource_type": "model", + "package_name": "failure_corpus", + "path": "models/marts/customer_totals.sql", + "original_file_path": "models/marts/customer_totals.sql", + "unique_id": "model.failure_corpus.customer_totals", + "fqn": [ + "failure_corpus", + "models", + "marts", + "customer_totals" + ], + "alias": "customer_totals", + "config": { + "enabled": true, + "alias": null, + "schema": null, + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "table", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Order totals per customer", + "columns": { + "customer_id": { + "name": "customer_id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "total_amount": { + "name": "total_amount", + "description": "", + "meta": {}, + "data_type": "DOUBLE", + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.customer_totals", + "depends_on": { + "macros": [], + "nodes": [ + "model.failure_corpus.stg_orders" + ] + } + }, + "model.failure_corpus.stg_orders": { + "database": null, + "schema": "main", + "name": "stg_orders", + "resource_type": "model", + "package_name": "failure_corpus", + "path": "models/staging/stg_orders.sql", + "original_file_path": "models/staging/stg_orders.sql", + "unique_id": "model.failure_corpus.stg_orders", + "fqn": [ + "failure_corpus", + "models", + "staging", + "stg_orders" + ], + "alias": "stg_orders", + "config": { + "enabled": true, + "alias": null, + "schema": null, + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Staged orders", + "columns": { + "order_id": { + "name": "order_id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "customer_id": { + "name": "customer_id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "amount": { + "name": "amount", + "description": "", + "meta": {}, + "data_type": "DOUBLE", + "constraints": [], + "quote": null, + "tags": [] + }, + "status": { + "name": "status", + "description": "", + "meta": {}, + "data_type": "VARCHAR", + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.stg_orders", + "depends_on": { + "macros": [], + "nodes": [ + "source.failure_corpus.raw_orders" + ] + } + } + }, + "sources": { + "source.failure_corpus.raw_orders": { + "database": null, + "schema": "", + "name": "raw_orders", + "resource_type": "source", + "package_name": "failure_corpus", + "path": "sources/raw.yml", + "original_file_path": "sources/raw.yml", + "unique_id": "source.failure_corpus.raw_orders", + "fqn": [ + "failure_corpus", + "raw_orders" + ], + "source_name": "raw_orders", + "source_description": "", + "loader": "", + "identifier": "raw_orders", + "quoting": {}, + "loaded_at_field": null, + "freshness": null, + "external": null, + "description": "Orders feed.", + "columns": { + "order_id": { + "name": "order_id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "customer_id": { + "name": "customer_id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "amount": { + "name": "amount", + "description": "", + "meta": {}, + "data_type": "DOUBLE", + "constraints": [], + "quote": null, + "tags": [] + }, + "status": { + "name": "status", + "description": "", + "meta": {}, + "data_type": "VARCHAR", + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": {}, + "source_meta": {}, + "tags": [], + "config": {}, + "patch_path": null, + "unrendered_config": {}, + "relation_name": null + } + }, + "macros": {}, + "exposures": {}, + "metrics": {}, + "groups": {}, + "selectors": {}, + "disabled": {}, + "parent_map": { + "model.failure_corpus.customer_totals": [ + "model.failure_corpus.stg_orders" + ], + "model.failure_corpus.stg_orders": [ + "source.failure_corpus.raw_orders" + ], + "source.failure_corpus.raw_orders": [] + }, + "group_map": {}, + "docs": {}, + "saved_queries": {}, + "semantic_models": {}, + "unit_tests": {} + } +} diff --git a/tests/goldens/compiler/failure/contract-implicit-missing-column.json b/tests/goldens/compiler/failure/contract-implicit-missing-column.json new file mode 100644 index 0000000000..87a901d28f --- /dev/null +++ b/tests/goldens/compiler/failure/contract-implicit-missing-column.json @@ -0,0 +1,303 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "contract", + "severity": "error", + "code": "K001", + "message": "declared column 'order_count' was not found in statically inferred output for model 'customer_totals'", + "resource_type": "model", + "resource_name": "customer_totals", + "column_name": "order_count", + "path": "models/marts/customer_totals.sql", + "line": 6, + "column": 5, + "location": { + "path": "models/marts/customer_totals.sql", + "line": 6, + "column": 5, + "end_line": 6, + "end_column": 16 + }, + "help": "add order_count to the SELECT list or correct/remove MODEL(columns (...)); MODEL(columns (...)) is validated using static SQL analysis because settings.column_contract_mode is \"implicit\" (the default). If this project intentionally uses columns only for metadata and audits, set [settings] column_contract_mode = \"explicit\"; models with contract enforced remain validated" + } + ] + } + ], + "compiled": { + "models/marts/customer_totals.sql": [ + "SELECT customer_id, SUM(amount) AS total_amount", + "FROM __ref(\"stg_orders\")", + "GROUP BY customer_id", + "" + ], + "models/staging/stg_orders.sql": [ + "SELECT order_id, customer_id, amount, status", + "FROM __source(\"raw_orders\")", + "" + ] + }, + "manifest": { + "nodes": { + "model.failure_corpus.customer_totals": { + "database": null, + "schema": "main", + "name": "customer_totals", + "resource_type": "model", + "package_name": "failure_corpus", + "path": "models/marts/customer_totals.sql", + "original_file_path": "models/marts/customer_totals.sql", + "unique_id": "model.failure_corpus.customer_totals", + "fqn": [ + "failure_corpus", + "models", + "marts", + "customer_totals" + ], + "alias": "customer_totals", + "config": { + "enabled": true, + "alias": null, + "schema": null, + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Order totals per customer", + "columns": { + "customer_id": { + "name": "customer_id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "total_amount": { + "name": "total_amount", + "description": "", + "meta": {}, + "data_type": "DOUBLE", + "constraints": [], + "quote": null, + "tags": [] + }, + "order_count": { + "name": "order_count", + "description": "", + "meta": {}, + "data_type": "BIGINT", + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.customer_totals", + "depends_on": { + "macros": [], + "nodes": [ + "model.failure_corpus.stg_orders" + ] + } + }, + "model.failure_corpus.stg_orders": { + "database": null, + "schema": "main", + "name": "stg_orders", + "resource_type": "model", + "package_name": "failure_corpus", + "path": "models/staging/stg_orders.sql", + "original_file_path": "models/staging/stg_orders.sql", + "unique_id": "model.failure_corpus.stg_orders", + "fqn": [ + "failure_corpus", + "models", + "staging", + "stg_orders" + ], + "alias": "stg_orders", + "config": { + "enabled": true, + "alias": null, + "schema": null, + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Staged orders", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.stg_orders", + "depends_on": { + "macros": [], + "nodes": [ + "source.failure_corpus.raw_orders" + ] + } + } + }, + "sources": { + "source.failure_corpus.raw_orders": { + "database": null, + "schema": "", + "name": "raw_orders", + "resource_type": "source", + "package_name": "failure_corpus", + "path": "sources/raw.yml", + "original_file_path": "sources/raw.yml", + "unique_id": "source.failure_corpus.raw_orders", + "fqn": [ + "failure_corpus", + "raw_orders" + ], + "source_name": "raw_orders", + "source_description": "", + "loader": "", + "identifier": "raw_orders", + "quoting": {}, + "loaded_at_field": null, + "freshness": null, + "external": null, + "description": "Orders feed.", + "columns": { + "order_id": { + "name": "order_id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "customer_id": { + "name": "customer_id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "amount": { + "name": "amount", + "description": "", + "meta": {}, + "data_type": "DOUBLE", + "constraints": [], + "quote": null, + "tags": [] + }, + "status": { + "name": "status", + "description": "", + "meta": {}, + "data_type": "VARCHAR", + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": {}, + "source_meta": {}, + "tags": [], + "config": {}, + "patch_path": null, + "unrendered_config": {}, + "relation_name": null + } + }, + "macros": {}, + "exposures": {}, + "metrics": {}, + "groups": {}, + "selectors": {}, + "disabled": {}, + "parent_map": { + "model.failure_corpus.customer_totals": [ + "model.failure_corpus.stg_orders" + ], + "model.failure_corpus.stg_orders": [ + "source.failure_corpus.raw_orders" + ], + "source.failure_corpus.raw_orders": [] + }, + "group_map": {}, + "docs": {}, + "saved_queries": {}, + "semantic_models": {}, + "unit_tests": {} + } +} diff --git a/tests/goldens/compiler/failure/contract-local-immediate-promotion.json b/tests/goldens/compiler/failure/contract-local-immediate-promotion.json new file mode 100644 index 0000000000..ffbc3c59c3 --- /dev/null +++ b/tests/goldens/compiler/failure/contract-local-immediate-promotion.json @@ -0,0 +1,302 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "contract", + "severity": "error", + "code": "K011", + "message": "model 'stg_orders': contract enforced requires staged table promotion; immediate table promotion cannot validate runtime output before target mutation", + "resource_type": "model", + "resource_name": "stg_orders", + "path": "models/staging/stg_orders.sql", + "help": "sqlbuild_local.toml sets [settings] table_promotion_mode = \"immediate\"; enforced contracts are validated in a staging table before promotion\n = help: remove that line to use the default staged promotion, or set this in sqlbuild_local.toml:\n [settings]\n table_promotion_mode = \"staged\"\n = help: or remove the model's enforced contract: `contract enforced` in its MODEL header, or `contract = \"enforced\"` in the [defaults] or [path_defaults] entry that applies to it" + } + ] + } + ], + "compiled": { + "models/marts/customer_totals.sql": [ + "SELECT customer_id, SUM(amount) AS total_amount", + "FROM __ref(\"stg_orders\")", + "GROUP BY customer_id", + "" + ], + "models/staging/stg_orders.sql": [ + "SELECT order_id, customer_id, amount, status", + "FROM __source(\"raw_orders\")", + "" + ] + }, + "manifest": { + "nodes": { + "model.failure_corpus.customer_totals": { + "database": null, + "schema": "main", + "name": "customer_totals", + "resource_type": "model", + "package_name": "failure_corpus", + "path": "models/marts/customer_totals.sql", + "original_file_path": "models/marts/customer_totals.sql", + "unique_id": "model.failure_corpus.customer_totals", + "fqn": [ + "failure_corpus", + "models", + "marts", + "customer_totals" + ], + "alias": "customer_totals", + "config": { + "enabled": true, + "alias": null, + "schema": null, + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Order totals per customer", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.customer_totals", + "depends_on": { + "macros": [], + "nodes": [ + "model.failure_corpus.stg_orders" + ] + } + }, + "model.failure_corpus.stg_orders": { + "database": null, + "schema": "main", + "name": "stg_orders", + "resource_type": "model", + "package_name": "failure_corpus", + "path": "models/staging/stg_orders.sql", + "original_file_path": "models/staging/stg_orders.sql", + "unique_id": "model.failure_corpus.stg_orders", + "fqn": [ + "failure_corpus", + "models", + "staging", + "stg_orders" + ], + "alias": "stg_orders", + "config": { + "enabled": true, + "alias": null, + "schema": null, + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "table", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Staged orders", + "columns": { + "order_id": { + "name": "order_id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "customer_id": { + "name": "customer_id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "amount": { + "name": "amount", + "description": "", + "meta": {}, + "data_type": "DOUBLE", + "constraints": [], + "quote": null, + "tags": [] + }, + "status": { + "name": "status", + "description": "", + "meta": {}, + "data_type": "VARCHAR", + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.stg_orders", + "depends_on": { + "macros": [], + "nodes": [ + "source.failure_corpus.raw_orders" + ] + } + } + }, + "sources": { + "source.failure_corpus.raw_orders": { + "database": null, + "schema": "", + "name": "raw_orders", + "resource_type": "source", + "package_name": "failure_corpus", + "path": "sources/raw.yml", + "original_file_path": "sources/raw.yml", + "unique_id": "source.failure_corpus.raw_orders", + "fqn": [ + "failure_corpus", + "raw_orders" + ], + "source_name": "raw_orders", + "source_description": "", + "loader": "", + "identifier": "raw_orders", + "quoting": {}, + "loaded_at_field": null, + "freshness": null, + "external": null, + "description": "Orders feed.", + "columns": { + "order_id": { + "name": "order_id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "customer_id": { + "name": "customer_id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "amount": { + "name": "amount", + "description": "", + "meta": {}, + "data_type": "DOUBLE", + "constraints": [], + "quote": null, + "tags": [] + }, + "status": { + "name": "status", + "description": "", + "meta": {}, + "data_type": "VARCHAR", + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": {}, + "source_meta": {}, + "tags": [], + "config": {}, + "patch_path": null, + "unrendered_config": {}, + "relation_name": null + } + }, + "macros": {}, + "exposures": {}, + "metrics": {}, + "groups": {}, + "selectors": {}, + "disabled": {}, + "parent_map": { + "model.failure_corpus.customer_totals": [ + "model.failure_corpus.stg_orders" + ], + "model.failure_corpus.stg_orders": [ + "source.failure_corpus.raw_orders" + ], + "source.failure_corpus.raw_orders": [] + }, + "group_map": {}, + "docs": {}, + "saved_queries": {}, + "semantic_models": {}, + "unit_tests": {} + } +} diff --git a/tests/goldens/compiler/failure/contract-named-schema-columns.json b/tests/goldens/compiler/failure/contract-named-schema-columns.json new file mode 100644 index 0000000000..6c70207613 --- /dev/null +++ b/tests/goldens/compiler/failure/contract-named-schema-columns.json @@ -0,0 +1,332 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "contract", + "severity": "error", + "code": "K005", + "message": "column 'status' is not declared in enforced contract for model 'stg_orders'", + "resource_type": "model", + "resource_name": "stg_orders", + "column_name": "status", + "path": "models/staging/stg_orders.sql", + "line": 7, + "column": 39, + "location": { + "path": "models/staging/stg_orders.sql", + "line": 7, + "column": 39, + "end_line": 7, + "end_column": 45 + }, + "help": "add the column to the named SCHEMA or remove it from the SELECT list" + }, + { + "phase": "contract", + "severity": "error", + "code": "K001", + "message": "enforced contract column 'shipped_at' was not found in statically inferred output for model 'stg_orders'", + "resource_type": "model", + "resource_name": "stg_orders", + "column_name": "shipped_at", + "path": "models/staging/_sqlbuild/_schemas/staged_order.sql", + "line": 8, + "column": 5, + "location": { + "path": "models/staging/_sqlbuild/_schemas/staged_order.sql", + "line": 8, + "column": 5, + "end_line": 8, + "end_column": 15 + }, + "help": "add shipped_at to the SELECT list or correct the named SCHEMA declaration; validation is active because this model declares contract enforced" + } + ] + } + ], + "compiled": { + "models/marts/customer_totals.sql": [ + "SELECT customer_id, SUM(amount) AS total_amount", + "FROM __ref(\"stg_orders\")", + "GROUP BY customer_id", + "" + ], + "models/staging/stg_orders.sql": [ + "SELECT order_id, customer_id, amount, status", + "FROM __source(\"raw_orders\")", + "" + ] + }, + "manifest": { + "nodes": { + "model.failure_corpus.customer_totals": { + "database": null, + "schema": "main", + "name": "customer_totals", + "resource_type": "model", + "package_name": "failure_corpus", + "path": "models/marts/customer_totals.sql", + "original_file_path": "models/marts/customer_totals.sql", + "unique_id": "model.failure_corpus.customer_totals", + "fqn": [ + "failure_corpus", + "models", + "marts", + "customer_totals" + ], + "alias": "customer_totals", + "config": { + "enabled": true, + "alias": null, + "schema": null, + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Order totals per customer", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.customer_totals", + "depends_on": { + "macros": [], + "nodes": [ + "model.failure_corpus.stg_orders" + ] + } + }, + "model.failure_corpus.stg_orders": { + "database": null, + "schema": "main", + "name": "stg_orders", + "resource_type": "model", + "package_name": "failure_corpus", + "path": "models/staging/stg_orders.sql", + "original_file_path": "models/staging/stg_orders.sql", + "unique_id": "model.failure_corpus.stg_orders", + "fqn": [ + "failure_corpus", + "models", + "staging", + "stg_orders" + ], + "alias": "stg_orders", + "config": { + "enabled": true, + "alias": null, + "schema": null, + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Staged orders", + "columns": { + "order_id": { + "name": "order_id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "customer_id": { + "name": "customer_id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "amount": { + "name": "amount", + "description": "", + "meta": {}, + "data_type": "DOUBLE", + "constraints": [], + "quote": null, + "tags": [] + }, + "shipped_at": { + "name": "shipped_at", + "description": "", + "meta": {}, + "data_type": "TIMESTAMP", + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.stg_orders", + "depends_on": { + "macros": [], + "nodes": [ + "source.failure_corpus.raw_orders" + ] + } + } + }, + "sources": { + "source.failure_corpus.raw_orders": { + "database": null, + "schema": "", + "name": "raw_orders", + "resource_type": "source", + "package_name": "failure_corpus", + "path": "sources/raw.yml", + "original_file_path": "sources/raw.yml", + "unique_id": "source.failure_corpus.raw_orders", + "fqn": [ + "failure_corpus", + "raw_orders" + ], + "source_name": "raw_orders", + "source_description": "", + "loader": "", + "identifier": "raw_orders", + "quoting": {}, + "loaded_at_field": null, + "freshness": null, + "external": null, + "description": "Orders feed.", + "columns": { + "order_id": { + "name": "order_id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "customer_id": { + "name": "customer_id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "amount": { + "name": "amount", + "description": "", + "meta": {}, + "data_type": "DOUBLE", + "constraints": [], + "quote": null, + "tags": [] + }, + "status": { + "name": "status", + "description": "", + "meta": {}, + "data_type": "VARCHAR", + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": {}, + "source_meta": {}, + "tags": [], + "config": {}, + "patch_path": null, + "unrendered_config": {}, + "relation_name": null + } + }, + "macros": {}, + "exposures": {}, + "metrics": {}, + "groups": {}, + "selectors": {}, + "disabled": {}, + "parent_map": { + "model.failure_corpus.customer_totals": [ + "model.failure_corpus.stg_orders" + ], + "model.failure_corpus.stg_orders": [ + "source.failure_corpus.raw_orders" + ], + "source.failure_corpus.raw_orders": [] + }, + "group_map": {}, + "docs": {}, + "saved_queries": {}, + "semantic_models": {}, + "unit_tests": {} + } +} diff --git a/tests/goldens/compiler/failure/contract-order-every-column-family.json b/tests/goldens/compiler/failure/contract-order-every-column-family.json new file mode 100644 index 0000000000..8726fb269d --- /dev/null +++ b/tests/goldens/compiler/failure/contract-order-every-column-family.json @@ -0,0 +1,398 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "contract", + "severity": "error", + "code": "K005", + "message": "column 'status' is not declared in enforced contract for model 'stg_orders'", + "resource_type": "model", + "resource_name": "stg_orders", + "column_name": "status", + "path": "models/staging/stg_orders.sql", + "line": 12, + "column": 64, + "location": { + "path": "models/staging/stg_orders.sql", + "line": 12, + "column": 64, + "end_line": 12, + "end_column": 70 + }, + "help": "add the column to MODEL(columns) or remove it from the SELECT list" + }, + { + "phase": "contract", + "severity": "error", + "code": "K002", + "message": "column 'order_id' inferred as INTEGER but declared type is VARCHAR", + "resource_type": "model", + "resource_name": "stg_orders", + "column_name": "order_id", + "path": "models/staging/stg_orders.sql", + "line": 5, + "column": 5, + "location": { + "path": "models/staging/stg_orders.sql", + "line": 5, + "column": 5, + "end_line": 5, + "end_column": 13 + }, + "related_locations": [ + { + "label": "output", + "location": { + "path": "models/staging/stg_orders.sql", + "line": 12, + "column": 8, + "end_line": 12, + "end_column": 16 + }, + "message": "inferred INTEGER" + } + ], + "help": "change the declared type or cast the expression explicitly" + }, + { + "phase": "contract", + "severity": "error", + "code": "K004", + "message": "column 'customer_id' is declared non-null but may be nullable", + "resource_type": "model", + "resource_name": "stg_orders", + "column_name": "customer_id", + "path": "models/staging/stg_orders.sql", + "line": 6, + "column": 5, + "location": { + "path": "models/staging/stg_orders.sql", + "line": 6, + "column": 5, + "end_line": 6, + "end_column": 16 + }, + "related_locations": [ + { + "label": "output", + "location": { + "path": "models/staging/stg_orders.sql", + "line": 12, + "column": 18, + "end_line": 12, + "end_column": 54 + }, + "message": "output expression proven nullable" + } + ], + "help": "use COALESCE, filter nulls explicitly, or remove the non-null contract" + }, + { + "phase": "contract", + "severity": "error", + "code": "K001", + "message": "enforced contract column 'shipped_at' was not found in statically inferred output for model 'stg_orders'", + "resource_type": "model", + "resource_name": "stg_orders", + "column_name": "shipped_at", + "path": "models/staging/stg_orders.sql", + "line": 7, + "column": 5, + "location": { + "path": "models/staging/stg_orders.sql", + "line": 7, + "column": 5, + "end_line": 7, + "end_column": 15 + }, + "help": "add shipped_at to the SELECT list or correct MODEL(columns (...)); validation is active because this model declares contract enforced" + } + ] + } + ], + "compiled": { + "models/marts/customer_totals.sql": [ + "SELECT customer_id, SUM(amount) AS total_amount", + "FROM __ref(\"stg_orders\")", + "GROUP BY customer_id", + "" + ], + "models/staging/stg_orders.sql": [ + "SELECT order_id, CAST(NULL AS INTEGER) AS customer_id, amount, status", + "FROM __source(\"raw_orders\")", + "" + ] + }, + "manifest": { + "nodes": { + "model.failure_corpus.customer_totals": { + "database": null, + "schema": "main", + "name": "customer_totals", + "resource_type": "model", + "package_name": "failure_corpus", + "path": "models/marts/customer_totals.sql", + "original_file_path": "models/marts/customer_totals.sql", + "unique_id": "model.failure_corpus.customer_totals", + "fqn": [ + "failure_corpus", + "models", + "marts", + "customer_totals" + ], + "alias": "customer_totals", + "config": { + "enabled": true, + "alias": null, + "schema": null, + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Order totals per customer", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.customer_totals", + "depends_on": { + "macros": [], + "nodes": [ + "model.failure_corpus.stg_orders" + ] + } + }, + "model.failure_corpus.stg_orders": { + "database": null, + "schema": "main", + "name": "stg_orders", + "resource_type": "model", + "package_name": "failure_corpus", + "path": "models/staging/stg_orders.sql", + "original_file_path": "models/staging/stg_orders.sql", + "unique_id": "model.failure_corpus.stg_orders", + "fqn": [ + "failure_corpus", + "models", + "staging", + "stg_orders" + ], + "alias": "stg_orders", + "config": { + "enabled": true, + "alias": null, + "schema": null, + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Staged orders", + "columns": { + "order_id": { + "name": "order_id", + "description": "", + "meta": {}, + "data_type": "VARCHAR", + "constraints": [], + "quote": null, + "tags": [] + }, + "customer_id": { + "name": "customer_id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "shipped_at": { + "name": "shipped_at", + "description": "", + "meta": {}, + "data_type": "TIMESTAMP", + "constraints": [], + "quote": null, + "tags": [] + }, + "amount": { + "name": "amount", + "description": "", + "meta": {}, + "data_type": "DOUBLE", + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.stg_orders", + "depends_on": { + "macros": [], + "nodes": [ + "source.failure_corpus.raw_orders" + ] + } + } + }, + "sources": { + "source.failure_corpus.raw_orders": { + "database": null, + "schema": "", + "name": "raw_orders", + "resource_type": "source", + "package_name": "failure_corpus", + "path": "sources/raw.yml", + "original_file_path": "sources/raw.yml", + "unique_id": "source.failure_corpus.raw_orders", + "fqn": [ + "failure_corpus", + "raw_orders" + ], + "source_name": "raw_orders", + "source_description": "", + "loader": "", + "identifier": "raw_orders", + "quoting": {}, + "loaded_at_field": null, + "freshness": null, + "external": null, + "description": "Orders feed.", + "columns": { + "order_id": { + "name": "order_id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "customer_id": { + "name": "customer_id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "amount": { + "name": "amount", + "description": "", + "meta": {}, + "data_type": "DOUBLE", + "constraints": [], + "quote": null, + "tags": [] + }, + "status": { + "name": "status", + "description": "", + "meta": {}, + "data_type": "VARCHAR", + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": {}, + "source_meta": {}, + "tags": [], + "config": {}, + "patch_path": null, + "unrendered_config": {}, + "relation_name": null + } + }, + "macros": {}, + "exposures": {}, + "metrics": {}, + "groups": {}, + "selectors": {}, + "disabled": {}, + "parent_map": { + "model.failure_corpus.customer_totals": [ + "model.failure_corpus.stg_orders" + ], + "model.failure_corpus.stg_orders": [ + "source.failure_corpus.raw_orders" + ], + "source.failure_corpus.raw_orders": [] + }, + "group_map": {}, + "docs": {}, + "saved_queries": {}, + "semantic_models": {}, + "unit_tests": {} + } +} diff --git a/tests/goldens/compiler/failure/semantic-cursor-inputs-unknown-column.json b/tests/goldens/compiler/failure/semantic-cursor-inputs-unknown-column.json new file mode 100644 index 0000000000..ea2df9429b --- /dev/null +++ b/tests/goldens/compiler/failure/semantic-cursor-inputs-unknown-column.json @@ -0,0 +1,274 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "B300", + "message": "cursor_inputs stg_orders references unknown column 'placed_at'", + "resource_type": "model", + "resource_name": "customer_totals", + "path": "models/marts/customer_totals.sql", + "line": 9, + "column": 16, + "location": { + "path": "models/marts/customer_totals.sql", + "line": 9, + "column": 16 + }, + "help": "change the metadata column name to an existing output column" + } + ] + } + ], + "compiled": { + "models/marts/customer_totals.sql": [ + "SELECT customer_id, SUM(amount) AS total_amount", + "FROM __ref(\"stg_orders\")", + "GROUP BY customer_id", + "" + ], + "models/staging/stg_orders.sql": [ + "SELECT order_id, customer_id, amount, status", + "FROM __source(\"raw_orders\")", + "" + ] + }, + "manifest": { + "nodes": { + "model.failure_corpus.customer_totals": { + "database": null, + "schema": "main", + "name": "customer_totals", + "resource_type": "model", + "package_name": "failure_corpus", + "path": "models/marts/customer_totals.sql", + "original_file_path": "models/marts/customer_totals.sql", + "unique_id": "model.failure_corpus.customer_totals", + "fqn": [ + "failure_corpus", + "models", + "marts", + "customer_totals" + ], + "alias": "customer_totals", + "config": { + "enabled": true, + "alias": null, + "schema": null, + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "incremental", + "incremental_strategy": "delete_insert", + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": [ + "customer_id" + ], + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Order totals per customer", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.customer_totals", + "depends_on": { + "macros": [], + "nodes": [ + "model.failure_corpus.stg_orders" + ] + } + }, + "model.failure_corpus.stg_orders": { + "database": null, + "schema": "main", + "name": "stg_orders", + "resource_type": "model", + "package_name": "failure_corpus", + "path": "models/staging/stg_orders.sql", + "original_file_path": "models/staging/stg_orders.sql", + "unique_id": "model.failure_corpus.stg_orders", + "fqn": [ + "failure_corpus", + "models", + "staging", + "stg_orders" + ], + "alias": "stg_orders", + "config": { + "enabled": true, + "alias": null, + "schema": null, + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Staged orders", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.stg_orders", + "depends_on": { + "macros": [], + "nodes": [ + "source.failure_corpus.raw_orders" + ] + } + } + }, + "sources": { + "source.failure_corpus.raw_orders": { + "database": null, + "schema": "", + "name": "raw_orders", + "resource_type": "source", + "package_name": "failure_corpus", + "path": "sources/raw.yml", + "original_file_path": "sources/raw.yml", + "unique_id": "source.failure_corpus.raw_orders", + "fqn": [ + "failure_corpus", + "raw_orders" + ], + "source_name": "raw_orders", + "source_description": "", + "loader": "", + "identifier": "raw_orders", + "quoting": {}, + "loaded_at_field": null, + "freshness": null, + "external": null, + "description": "Orders feed.", + "columns": { + "order_id": { + "name": "order_id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "customer_id": { + "name": "customer_id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "amount": { + "name": "amount", + "description": "", + "meta": {}, + "data_type": "DOUBLE", + "constraints": [], + "quote": null, + "tags": [] + }, + "status": { + "name": "status", + "description": "", + "meta": {}, + "data_type": "VARCHAR", + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": {}, + "source_meta": {}, + "tags": [], + "config": {}, + "patch_path": null, + "unrendered_config": {}, + "relation_name": null + } + }, + "macros": {}, + "exposures": {}, + "metrics": {}, + "groups": {}, + "selectors": {}, + "disabled": {}, + "parent_map": { + "model.failure_corpus.customer_totals": [ + "model.failure_corpus.stg_orders" + ], + "model.failure_corpus.stg_orders": [ + "source.failure_corpus.raw_orders" + ], + "source.failure_corpus.raw_orders": [] + }, + "group_map": {}, + "docs": {}, + "saved_queries": {}, + "semantic_models": {}, + "unit_tests": {} + } +} diff --git a/tests/goldens/compiler/failure/semantic-source-cursor-unknown-column.json b/tests/goldens/compiler/failure/semantic-source-cursor-unknown-column.json new file mode 100644 index 0000000000..112420a01c --- /dev/null +++ b/tests/goldens/compiler/failure/semantic-source-cursor-unknown-column.json @@ -0,0 +1,330 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "B300", + "message": "cursor_column references unknown column 'loaded_at'", + "resource_type": "source", + "resource_name": "raw_events", + "path": "sources/raw.yml", + "line": 21, + "column": 20, + "location": { + "path": "sources/raw.yml", + "line": 21, + "column": 20 + }, + "help": "change the metadata column name to an existing output column" + } + ] + } + ], + "compiled": { + "models/marts/customer_totals.sql": [ + "SELECT customer_id, SUM(amount) AS total_amount", + "FROM __ref(\"stg_orders\")", + "GROUP BY customer_id", + "" + ], + "models/staging/stg_orders.sql": [ + "SELECT order_id, customer_id, amount, status", + "FROM __source(\"raw_orders\")", + "" + ] + }, + "manifest": { + "nodes": { + "model.failure_corpus.customer_totals": { + "database": null, + "schema": "main", + "name": "customer_totals", + "resource_type": "model", + "package_name": "failure_corpus", + "path": "models/marts/customer_totals.sql", + "original_file_path": "models/marts/customer_totals.sql", + "unique_id": "model.failure_corpus.customer_totals", + "fqn": [ + "failure_corpus", + "models", + "marts", + "customer_totals" + ], + "alias": "customer_totals", + "config": { + "enabled": true, + "alias": null, + "schema": null, + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Order totals per customer", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.customer_totals", + "depends_on": { + "macros": [], + "nodes": [ + "model.failure_corpus.stg_orders" + ] + } + }, + "model.failure_corpus.stg_orders": { + "database": null, + "schema": "main", + "name": "stg_orders", + "resource_type": "model", + "package_name": "failure_corpus", + "path": "models/staging/stg_orders.sql", + "original_file_path": "models/staging/stg_orders.sql", + "unique_id": "model.failure_corpus.stg_orders", + "fqn": [ + "failure_corpus", + "models", + "staging", + "stg_orders" + ], + "alias": "stg_orders", + "config": { + "enabled": true, + "alias": null, + "schema": null, + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Staged orders", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.stg_orders", + "depends_on": { + "macros": [], + "nodes": [ + "source.failure_corpus.raw_orders" + ] + } + } + }, + "sources": { + "source.failure_corpus.raw_orders": { + "database": null, + "schema": "", + "name": "raw_orders", + "resource_type": "source", + "package_name": "failure_corpus", + "path": "sources/raw.yml", + "original_file_path": "sources/raw.yml", + "unique_id": "source.failure_corpus.raw_orders", + "fqn": [ + "failure_corpus", + "raw_orders" + ], + "source_name": "raw_orders", + "source_description": "", + "loader": "", + "identifier": "raw_orders", + "quoting": {}, + "loaded_at_field": null, + "freshness": null, + "external": null, + "description": "Orders feed.", + "columns": { + "order_id": { + "name": "order_id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "customer_id": { + "name": "customer_id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "amount": { + "name": "amount", + "description": "", + "meta": {}, + "data_type": "DOUBLE", + "constraints": [], + "quote": null, + "tags": [] + }, + "status": { + "name": "status", + "description": "", + "meta": {}, + "data_type": "VARCHAR", + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": {}, + "source_meta": {}, + "tags": [], + "config": {}, + "patch_path": null, + "unrendered_config": {}, + "relation_name": null + }, + "source.failure_corpus.raw_events": { + "database": null, + "schema": "", + "name": "raw_events", + "resource_type": "source", + "package_name": "failure_corpus", + "path": "sources/raw.yml", + "original_file_path": "sources/raw.yml", + "unique_id": "source.failure_corpus.raw_events", + "fqn": [ + "failure_corpus", + "raw_events" + ], + "source_name": "raw_events", + "source_description": "", + "loader": "", + "identifier": "raw_events", + "quoting": {}, + "loaded_at_field": null, + "freshness": null, + "external": null, + "description": "Loaded events.", + "columns": { + "event_id": { + "name": "event_id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "load_seq": { + "name": "load_seq", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": { + "sqlbuild": { + "loader": "raw_events", + "auto_load": false, + "write_strategy": "append", + "cursor_column": "loaded_at" + } + }, + "source_meta": {}, + "tags": [], + "config": {}, + "patch_path": null, + "unrendered_config": {}, + "relation_name": null + } + }, + "macros": {}, + "exposures": {}, + "metrics": {}, + "groups": {}, + "selectors": {}, + "disabled": {}, + "parent_map": { + "model.failure_corpus.customer_totals": [ + "model.failure_corpus.stg_orders" + ], + "model.failure_corpus.stg_orders": [ + "source.failure_corpus.raw_orders" + ], + "source.failure_corpus.raw_orders": [], + "source.failure_corpus.raw_events": [] + }, + "group_map": {}, + "docs": {}, + "saved_queries": {}, + "semantic_models": {}, + "unit_tests": {} + } +} diff --git a/tests/goldens/compiler/failure/semantic-udf-qualified-cast-argument-family.json b/tests/goldens/compiler/failure/semantic-udf-qualified-cast-argument-family.json new file mode 100644 index 0000000000..591093f15e --- /dev/null +++ b/tests/goldens/compiler/failure/semantic-udf-qualified-cast-argument-family.json @@ -0,0 +1,281 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "B301", + "message": "Function 'scaled_amount' argument 'p_factor' expects INTEGER, received TEXT; convert the argument explicitly", + "resource_type": "model", + "resource_name": "customer_totals", + "path": "models/marts/customer_totals.sql", + "line": 6, + "column": 10, + "location": { + "path": "models/marts/customer_totals.sql", + "line": 6, + "column": 10 + }, + "help": "align this declared metadata or audit value with the column or argument type" + } + ] + } + ], + "compiled": { + "functions/sql/scaled_amount.sql": [ + "CREATE OR REPLACE MACRO main.scaled_amount(p_amount, p_factor) AS (", + "p_amount * p_factor", + ")", + "" + ], + "models/marts/customer_totals.sql": [ + "SELECT o.customer_id,", + " __udf(\"scaled_amount\")(o.amount, CAST(o.status AS VARCHAR)) AS total_amount", + "FROM __ref(\"stg_orders\") AS o", + "" + ], + "models/staging/stg_orders.sql": [ + "SELECT order_id, customer_id, amount, status", + "FROM __source(\"raw_orders\")", + "" + ] + }, + "manifest": { + "nodes": { + "model.failure_corpus.customer_totals": { + "database": null, + "schema": "main", + "name": "customer_totals", + "resource_type": "model", + "package_name": "failure_corpus", + "path": "models/marts/customer_totals.sql", + "original_file_path": "models/marts/customer_totals.sql", + "unique_id": "model.failure_corpus.customer_totals", + "fqn": [ + "failure_corpus", + "models", + "marts", + "customer_totals" + ], + "alias": "customer_totals", + "config": { + "enabled": true, + "alias": null, + "schema": null, + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Order totals per customer", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.customer_totals", + "depends_on": { + "macros": [], + "nodes": [ + "model.failure_corpus.scaled_amount", + "model.failure_corpus.stg_orders" + ] + } + }, + "model.failure_corpus.stg_orders": { + "database": null, + "schema": "main", + "name": "stg_orders", + "resource_type": "model", + "package_name": "failure_corpus", + "path": "models/staging/stg_orders.sql", + "original_file_path": "models/staging/stg_orders.sql", + "unique_id": "model.failure_corpus.stg_orders", + "fqn": [ + "failure_corpus", + "models", + "staging", + "stg_orders" + ], + "alias": "stg_orders", + "config": { + "enabled": true, + "alias": null, + "schema": null, + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Staged orders", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.stg_orders", + "depends_on": { + "macros": [], + "nodes": [ + "source.failure_corpus.raw_orders" + ] + } + } + }, + "sources": { + "source.failure_corpus.raw_orders": { + "database": null, + "schema": "", + "name": "raw_orders", + "resource_type": "source", + "package_name": "failure_corpus", + "path": "sources/raw.yml", + "original_file_path": "sources/raw.yml", + "unique_id": "source.failure_corpus.raw_orders", + "fqn": [ + "failure_corpus", + "raw_orders" + ], + "source_name": "raw_orders", + "source_description": "", + "loader": "", + "identifier": "raw_orders", + "quoting": {}, + "loaded_at_field": null, + "freshness": null, + "external": null, + "description": "Orders feed.", + "columns": { + "order_id": { + "name": "order_id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "customer_id": { + "name": "customer_id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "amount": { + "name": "amount", + "description": "", + "meta": {}, + "data_type": "DOUBLE", + "constraints": [], + "quote": null, + "tags": [] + }, + "status": { + "name": "status", + "description": "", + "meta": {}, + "data_type": "VARCHAR", + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": {}, + "source_meta": {}, + "tags": [], + "config": {}, + "patch_path": null, + "unrendered_config": {}, + "relation_name": null + } + }, + "macros": {}, + "exposures": {}, + "metrics": {}, + "groups": {}, + "selectors": {}, + "disabled": {}, + "parent_map": { + "model.failure_corpus.customer_totals": [ + "model.failure_corpus.scaled_amount", + "model.failure_corpus.stg_orders" + ], + "model.failure_corpus.stg_orders": [ + "source.failure_corpus.raw_orders" + ], + "source.failure_corpus.raw_orders": [], + "model.failure_corpus.scaled_amount": [] + }, + "group_map": {}, + "docs": {}, + "saved_queries": {}, + "semantic_models": {}, + "unit_tests": {} + } +} From 81ab203a46fb6a103acf35cb1a7107c318b7c10c Mon Sep 17 00:00:00 2001 From: Kevin Longe <273747162+kvlonge@users.noreply.github.com> Date: Fri, 9 Oct 2026 09:48:44 +0100 Subject: [PATCH 15/31] test(native): count native answers per stage and close the review's gate holes Every native stage now reports the work it answered, so a stage switched to Python makes its allow-list entries vanish and fails the gate. The scenario expected-model scan, declaration contexts, the macro memo, effective vars, preview stubs and type normalization count their Python paths. Seed goldens are required inside a recorded range, fixture and example goldens are checked by the shipped differential too, golden inputs trigger the differential, and a failing gate says how to record intended changes. --- .github/workflows/verify.yml | 4 +- Makefile | 4 +- scripts/compiler_differential/README.md | 54 ++- .../_helpers/comparing/project_comparison.py | 22 +- .../_helpers/corpus/emitted_codes.py | 11 +- .../_helpers/goldens/goldens.py | 72 +++- .../_helpers/running/native_fallbacks.py | 13 +- .../_helpers/running/report.py | 8 + scripts/compiler_differential/constants.py | 11 +- .../main/differential.py | 6 + .../native_fallbacks.toml | 383 +++++++++++++++++- .../_helpers/type_normalization.py | 6 +- .../_helpers/compile/target_writer.py | 8 +- .../compile/_helpers/assembly/project.py | 2 +- .../compile/_helpers/assembly/targets.py | 2 +- .../compile/_helpers/attachment/audits.py | 5 +- .../compile/_helpers/attachment/core.py | 11 +- .../_helpers/attachment/declaration_scope.py | 4 +- .../compile/_helpers/attachment/functions.py | 6 +- .../_helpers/attachment/model_config.py | 7 +- .../attachment/scope_relationships.py | 6 +- .../compile/_helpers/diagnostics/recovery.py | 2 + .../_helpers/macro_bridge/call_store.py | 2 + .../_helpers/macro_bridge/expansion.py | 6 +- .../_helpers/native_stages/assembly.py | 18 +- .../_helpers/native_stages/sql_tests.py | 8 +- .../compile/_helpers/refs/references.py | 4 +- .../_helpers/render/context_templates.py | 2 +- .../_helpers/render/cursor_intrinsics.py | 5 +- .../compile/_helpers/render/declarations.py | 7 +- .../compile/_helpers/render/macros.py | 2 +- .../compile/_helpers/render/sql_vars.py | 2 +- .../compile/_helpers/scenarios/core.py | 4 +- .../compile/_helpers/sql_tests/core.py | 7 +- .../compile/main/expand_sql_with_spans.py | 2 +- .../compile/main/expand_template_data.py | 2 +- .../contracts/main/promotion_conflicts.py | 2 + .../compiler/contracts/main/validate.py | 2 + .../_helpers/native/declaration_files.py | 11 +- .../discovery/_helpers/native/model_files.py | 3 + .../frontier/_helpers/process_recorder.py | 12 + .../classes/native_fallback_recorder.py | 10 +- src/sqlbuild/compiler/frontier/constants.py | 1 + .../frontier/main/report_native_answer.py | 14 + ..._fallback.py => report_native_fallback.py} | 12 +- src/sqlbuild/compiler/frontier/types.py | 5 + src/sqlbuild/compiler/lineage/main/columns.py | 2 + .../macro_bridge/classes/macro_bridge.py | 3 +- .../main/run_with_macro_bridge.py | 2 +- .../main/_parse_native_header_metadata.py | 2 +- .../scripts/compiler_differential/helpers.py | 18 + .../test_native_fallback_gate.py | 39 ++ ...ssion-enriched-subquery-type-mismatch.json | 306 ++++++++++++++ ...-session-pivot-family-column-mismatch.json | 367 +++++++++++++++++ ...alysis-session-pivot-values-on-clause.json | 367 +++++++++++++++++ tests/goldens/compiler/seed_range.toml | 2 + .../_helpers/goldens/_test_types.py | 2 +- .../_helpers/goldens/test_compiler_goldens.py | 17 +- .../_helpers/running/_test_types.py | 1 + .../_helpers/running/test_native_fallbacks.py | 31 ++ .../_helpers/running/test_report.py | 19 +- 61 files changed, 1847 insertions(+), 121 deletions(-) create mode 100644 src/sqlbuild/compiler/frontier/_helpers/process_recorder.py create mode 100644 src/sqlbuild/compiler/frontier/main/report_native_answer.py rename src/sqlbuild/compiler/frontier/main/{_report_native_fallback.py => report_native_fallback.py} (64%) create mode 100644 tests/goldens/compiler/failure/analysis-session-enriched-subquery-type-mismatch.json create mode 100644 tests/goldens/compiler/failure/analysis-session-pivot-family-column-mismatch.json create mode 100644 tests/goldens/compiler/failure/analysis-session-pivot-values-on-clause.json create mode 100644 tests/goldens/compiler/seed_range.toml diff --git a/.github/workflows/verify.yml b/.github/workflows/verify.yml index fcdec38771..5d32014938 100644 --- a/.github/workflows/verify.yml +++ b/.github/workflows/verify.yml @@ -96,7 +96,7 @@ jobs: ;; esac case "${path}" in - scripts/compiler_differential/*|scripts/run_compiler_differential.py|tests/*/scripts/compiler_differential/*|tests/goldens/*|src/sqlbuild/cli/commands/_helpers/compile/*|.github/workflows/verify.yml|Makefile) + scripts/compiler_differential/*|scripts/run_compiler_differential.py|tests/*/scripts/compiler_differential/*|tests/goldens/*|tests/e2e/fixtures/*|website/examples/*|src/sqlbuild/cli/commands/_helpers/compile/*|.github/workflows/verify.yml|Makefile) compiler_differential=true ;; esac @@ -317,7 +317,7 @@ jobs: path: ${{ runner.temp }}/compiler-differential-wheel-sites.json if-no-files-found: warn retention-days: 30 - - name: Compare generated seeds and the failure corpus (python vs shipped native) + - name: Compare fixtures, examples, generated seeds and the failure corpus (python vs shipped native) run: >- make compiler-differential-shipped COMPILER_DIFFERENTIAL_ARGS="--stage-captures --require-discovery-coverage diff --git a/Makefile b/Makefile index 8fefcca499..2d119c858e 100644 --- a/Makefile +++ b/Makefile @@ -304,10 +304,10 @@ compiler-differential: $(COMPILER_DIFFERENTIAL_GATES) $(COMPILER_DIFFERENTIAL_ARGS) # python vs native-preview above covers every native stage; this keeps the shipped `native` default -# (native discovery and rendering) covered on a bounded corpus. +# (native discovery and rendering) and its goldens covered on the same corpus. compiler-differential-shipped: env PYTHONUNBUFFERED=1 uv run python -m scripts.run_compiler_differential \ - --engines python native --corpus seeds failures \ + --engines python native --corpus fixtures examples seeds failures \ --jobs $(COMPILER_DIFFERENTIAL_JOBS) --seeds $(COMPILER_DIFFERENTIAL_SHIPPED_SEEDS) \ $(COMPILER_DIFFERENTIAL_GATES) $(COMPILER_DIFFERENTIAL_ARGS) diff --git a/scripts/compiler_differential/README.md b/scripts/compiler_differential/README.md index f7008bb79e..8867510fc4 100644 --- a/scripts/compiler_differential/README.md +++ b/scripts/compiler_differential/README.md @@ -3,7 +3,11 @@ `make compiler-differential` compiles fixtures, examples, generated seeds and the failure corpus under two compiler engines and fails on the first differing artifact. `make compiler-differential-shipped` does the same for the shipped `native` default. Both also enforce -the two shared gates below. +the shared gates below. + +For ad hoc runs (one project, other seeds, other engines), call the script directly, for example +`uv run python -m scripts.run_compiler_differential --corpus --project --engines python +native`. The make targets fix the corpus the allow-list and goldens are recorded for. ## Shared gates for native ports @@ -12,11 +16,19 @@ does not add its own parity harness, property suite or E2E file. ### Native-to-Python fallbacks and analysis deferrals -`native_fallbacks.toml` is the one allow-list of all work native code still hands back to Python. -It covers both kinds of record: -- shipped-stage fallbacks, from `report_native_fallback(site=NativeFallbackSite., kind=...)`; +`native_fallbacks.toml` is the one allow-list of all work native code still hands back to Python, +and of the work each native stage answers itself. It covers three kinds of record: +- fallbacks, from `report_native_fallback(site=NativeFallbackSite., kind=...)` on every + path where a native stage hands work to Python, or runs Python with no native path yet; - preview analysis-stage deferrals: every `analysis-deferrals-*.jsonl` record (analysis session, - lineage, semantic checks, contracts). + lineage, semantic checks, contracts); +- native answers, from `report_native_answer(stage=NativeStage., kind=...)` where a + stage's native path produced the result: files, models, scans or calls. Their site is + `.native`. + +Counting only fallbacks cannot tell a stage that answers natively from one that no longer runs +natively at all. The answer counts can: if a stage is switched to Python, its `.native` +entries vanish and the gate says so. Every entry names an engine, stage, site and kind, with its exact count per corpus. Use `max_counts` instead of `counts` only for a count that is not stable, and give the reason in a @@ -26,7 +38,8 @@ The records cost nothing unless `SQLBUILD_ANALYSIS_RECORD_DIR` is set, which onl sets. `--native-fallbacks check` runs in both make targets and fails CI in both directions: - an entry, engine or corpus count that is not on the list fails; - a listed entry whose count changed or that no longer occurs also fails, and the message says to - update or remove it, so the list only shrinks. + update or remove it, so the fallback list only shrinks; +- a vanished native answer fails as work that now runs in Python. The counts hold for the make targets' corpus (`--seeds 12`); other seed ranges are refused. @@ -42,9 +55,10 @@ It deletes `tests/goldens/compiler`, then runs the two CI differentials with `-- update --goldens update`. Commit the resulting `native_fallbacks.toml` and `tests/goldens/` diff in the same PR as the change that caused it. -- A port deletes the Python code it replaces in the same PR. Its entries then disappear from the - list, and that removal is the port's test that the fallbacks are gone. When the last entry for - a `NativeFallbackSite` goes, delete the member and its `report_native_fallback` call too. +- A port deletes the Python code it replaces in the same PR. Its fallback entries then disappear + from the list, and that removal is the port's test that the fallbacks are gone. Its answer + counts usually rise. When the last entry for a `NativeFallbackSite` goes, delete the member + and its `report_native_fallback` call too. - If the corpus does not reach a site yet, first add a failure case or seed feature that does. - A PR that merges after a list or golden change on main reruns `make compiler-baselines` after rebasing and commits the result. @@ -52,9 +66,13 @@ in the same PR as the change that caused it. **What a reviewer checks in the diff** - `native_fallbacks.toml`: - - Removed entries and lower counts are expected from ports. - - Every added entry, higher count or new `max_counts` needs a stated reason in the PR body. - Never add one just to make CI pass. + - Removed fallback entries and lower fallback counts are expected from ports. + - Every added fallback entry, higher fallback count or new `max_counts` needs a stated reason + in the PR body. Never add one just to make CI pass. + - A removed or lower `.native` answer count means less native work. It needs a reason + in the PR body. +- Code: every new path that hands work to Python calls `report_native_fallback`, and every new + native path calls `report_native_answer` where its result is used. - Goldens: - Every changed golden is an intended output change, named in the PR body. - An output-neutral port changes no golden. @@ -70,14 +88,20 @@ in the same PR as the change that caused it. - the compiled SQL, one array item per line; - the manifest without its volatile metadata and the SQL it repeats. -Project paths, the SQLBuild version, invocation ids and timings are masked. +The project directory is masked as `` and the harness work directory as ``, +whether or not the path is resolved. The SQLBuild version, invocation ids and timings are also +masked. + +Goldens hold outputs only, not stage captures: a seed's captures are about 690 KB, too large to +review. Cache facts are covered by the incremental-versus-no-cache tests instead. - `--goldens check` (in both make targets) compares every engine with the goldens, so an output-neutral port needs no new parity suite. - `make compiler-baselines` rewrites them from the python oracle. Run it only when output changes on purpose, and review the diff like code. -- Seeds outside the recorded range have no golden and are skipped; any other project without a - golden fails. +- `tests/goldens/compiler/seed_range.toml` records the seed range the goldens were written for. + Every seed in that range, and every fixture, example and failure case, must have a golden. + Seeds outside the range (ad hoc runs with more seeds) are skipped. - Once the Python stages are deleted, goldens are rewritten from `native`. ### All-engine user-facing errors diff --git a/scripts/compiler_differential/_helpers/comparing/project_comparison.py b/scripts/compiler_differential/_helpers/comparing/project_comparison.py index eab8c75d95..8ab9cd2697 100644 --- a/scripts/compiler_differential/_helpers/comparing/project_comparison.py +++ b/scripts/compiler_differential/_helpers/comparing/project_comparison.py @@ -13,12 +13,14 @@ from scripts.compiler_differential._helpers.coverage.analysis import project_analysis_kinds from scripts.compiler_differential._helpers.coverage.discovery import project_discovery_kinds from scripts.compiler_differential._helpers.coverage.render import project_render_kinds -from scripts.compiler_differential._helpers.goldens.goldens import golden_differences +from scripts.compiler_differential._helpers.goldens.goldens import ( + golden_differences, + golden_path_masks, +) from scripts.compiler_differential._helpers.running.execution import run_engine from scripts.compiler_differential.constants import ( ERROR_SEVERITY, LEFT_SIDE, - PROJECT_DIRECTORY, RIGHT_SIDE, WARNING_SEVERITY, ) @@ -72,7 +74,7 @@ def compare_project(*, project: CorpusProject, options: DifferentialOptions) -> runs=(left, right), golden_dir=options.golden_dir, mode=options.golden_mode, - masked_paths=_masked_paths(case_dir=case_dir, work_dir=options.work_dir), + masked_paths=golden_path_masks(case_dir=case_dir, work_dir=options.work_dir), ) if options.golden_mode is not None and options.golden_dir is not None else () @@ -93,20 +95,6 @@ def compare_project(*, project: CorpusProject, options: DifferentialOptions) -> ) -def _masked_paths(*, case_dir: Path, work_dir: Path) -> tuple[str, ...]: - """The run's absolute paths, longest first, so goldens never mention a temporary directory.""" - - workspace: Path = case_dir / PROJECT_DIRECTORY - paths: set[str] = { - str(workspace), - str(workspace.resolve()), - str(work_dir), - str(work_dir.resolve()), - } - ordered: list[str] = sorted(paths, key=lambda path: (-len(path), path)) - return tuple(ordered) - - def _materialized_source(*, project: CorpusProject, case_dir: Path) -> Path: if project.source_dir is not None: return project.source_dir diff --git a/scripts/compiler_differential/_helpers/corpus/emitted_codes.py b/scripts/compiler_differential/_helpers/corpus/emitted_codes.py index e34b38f0ac..4bfc505a39 100644 --- a/scripts/compiler_differential/_helpers/corpus/emitted_codes.py +++ b/scripts/compiler_differential/_helpers/corpus/emitted_codes.py @@ -12,6 +12,7 @@ first_diagnostic_message, ) from scripts.compiler_differential._helpers.corpus.corpus import build_corpus +from scripts.compiler_differential._helpers.goldens.goldens import golden_path_masks, masked_text from scripts.compiler_differential._helpers.running.execution import run_engine from scripts.compiler_differential.constants import ( ANALYSIS_CODE_SCAN_ROOTS, @@ -20,9 +21,7 @@ DIAGNOSTIC_CODE_PATTERN, ERROR_SEVERITY, EXPECT_SUCCESS, - GOLDEN_PATH_MASK, LEFT_SIDE, - PROJECT_DIRECTORY, RENDER_CODE_SCAN_EXCLUDED, RENDER_RAISED_CODES, WARNING_SEVERITY, @@ -201,7 +200,6 @@ def _emitted_codes(*, project: CorpusProject, options: DifferentialOptions) -> E options=options, ) first: dict[str, object] = first_diagnostic(outcome=run.outcomes[0], severity=ERROR_SEVERITY) - project_dir: str = str(case_dir / PROJECT_DIRECTORY) message: str | None = first_diagnostic_message(outcome=run.outcomes[0], severity=ERROR_SEVERITY) notes: object = first.get("notes") line: object = first.get("line") @@ -210,7 +208,12 @@ def _emitted_codes(*, project: CorpusProject, options: DifferentialOptions) -> E errors=diagnostic_codes(outcome=run.outcomes[0], severity=ERROR_SEVERITY), warnings=diagnostic_codes(outcome=run.outcomes[0], severity=WARNING_SEVERITY), first_error_message=( - None if message is None else message.replace(project_dir, GOLDEN_PATH_MASK) + None + if message is None + else masked_text( + text=message, + masks=golden_path_masks(case_dir=case_dir, work_dir=options.work_dir), + ) ), first_error_help=None if first.get("help") is None else str(first.get("help")), first_error_notes=tuple(str(note) for note in notes) if isinstance(notes, list) else (), diff --git a/scripts/compiler_differential/_helpers/goldens/goldens.py b/scripts/compiler_differential/_helpers/goldens/goldens.py index c2472fc671..4bb0784b85 100644 --- a/scripts/compiler_differential/_helpers/goldens/goldens.py +++ b/scripts/compiler_differential/_helpers/goldens/goldens.py @@ -3,6 +3,7 @@ from __future__ import annotations import json +import tomllib from importlib.metadata import version from pathlib import Path @@ -16,15 +17,19 @@ ) from scripts.compiler_differential.constants import ( GOLDEN_CORPUS_PREFIXES, + GOLDEN_LABEL, GOLDEN_MANIFEST_DROPPED_KEYS, GOLDEN_MANIFEST_RESOURCE_SECTIONS, GOLDEN_MISSING_HINT, GOLDEN_MODE_CHECK, - GOLDEN_OPTIONAL_CORPORA, GOLDEN_PATH_MASK, GOLDEN_RESOURCE_DROPPED_FIELDS, + GOLDEN_SEED_CORPUS, + GOLDEN_SEED_RANGE_FILE, GOLDEN_SUFFIX, GOLDEN_VERSION_MASK, + GOLDEN_WORK_MASK, + PROJECT_DIRECTORY, ) from scripts.compiler_differential.models import ( CommandOutcome, @@ -43,7 +48,7 @@ def golden_differences( runs: tuple[EngineRun, ...], golden_dir: Path, mode: str, - masked_paths: tuple[str, ...], + masked_paths: tuple[tuple[str, str], ...], ) -> list[Difference]: """Check every run against the project's golden, or rewrite it from the first (oracle) run.""" @@ -58,7 +63,9 @@ def golden_differences( _ = path.write_text(golden_text(payloads[0]), encoding="utf-8") return [] if not path.is_file(): - if path.parent.name in GOLDEN_OPTIONAL_CORPORA: + if path.parent.name == GOLDEN_SEED_CORPUS and not _seed_in_recorded_range( + golden_dir=golden_dir, name=path.stem + ): return [] return [ Difference( @@ -67,7 +74,7 @@ def golden_differences( location="file", left="", right=GOLDEN_MISSING_HINT, - labels=("golden", "hint"), + labels=(GOLDEN_LABEL, "hint"), ) ] golden: object = json.loads(path.read_text(encoding="utf-8")) @@ -82,7 +89,7 @@ def golden_differences( location=divergence.location, left=divergence.left, right=divergence.right, - labels=("golden", run.engine), + labels=(GOLDEN_LABEL, run.engine), ) ) return found @@ -97,7 +104,9 @@ def golden_path(*, golden_dir: Path, project: str) -> Path | None: return golden_dir / corpus / f"{name}{GOLDEN_SUFFIX}" -def golden_payload(*, run: EngineRun, masked_paths: tuple[str, ...]) -> dict[str, object]: +def golden_payload( + *, run: EngineRun, masked_paths: tuple[tuple[str, str], ...] +) -> dict[str, object]: """Return a run's diagnostics, compiled SQL and slimmed manifest with run noise masked.""" return { @@ -122,7 +131,9 @@ def golden_text(payload: dict[str, object]) -> str: return json.dumps(payload, indent=1, ensure_ascii=False) + "\n" -def _command(*, outcome: CommandOutcome, masked_paths: tuple[str, ...]) -> dict[str, object]: +def _command( + *, outcome: CommandOutcome, masked_paths: tuple[tuple[str, str], ...] +) -> dict[str, object]: stdout: str = _masked(text=outcome.stdout, masked_paths=masked_paths) try: report: dict[str, object] | None = as_json_object(json.loads(stdout)) @@ -141,7 +152,7 @@ def _command(*, outcome: CommandOutcome, masked_paths: tuple[str, ...]) -> dict[ } -def _manifest(*, text: str | None, masked_paths: tuple[str, ...]) -> object | None: +def _manifest(*, text: str | None, masked_paths: tuple[tuple[str, str], ...]) -> object | None: if text is None: return None manifest: dict[str, object] = ( @@ -165,11 +176,50 @@ def _without_code(resource: object) -> object: } -def _masked(*, text: str, masked_paths: tuple[str, ...]) -> str: - for path in masked_paths: - text = text.replace(path, GOLDEN_PATH_MASK) +def _masked(*, text: str, masked_paths: tuple[tuple[str, str], ...]) -> str: + text = masked_text(text=text, masks=masked_paths) return normalize_artifact_text(text).replace(_SQLBUILD_VERSION, GOLDEN_VERSION_MASK) def _lines(*, text: str) -> list[str]: return text.split("\n") + + +def write_golden_seed_range(*, golden_dir: Path, seed_start: int, seeds: int) -> None: + """Record the seed range the goldens hold; every seed in it must keep its golden.""" + + golden_dir.mkdir(parents=True, exist_ok=True) + _ = (golden_dir / GOLDEN_SEED_RANGE_FILE).write_text( + f"seed_start = {seed_start}\nseeds = {seeds}\n", encoding="utf-8" + ) + + +def _seed_in_recorded_range(*, golden_dir: Path, name: str) -> bool: + path: Path = golden_dir / GOLDEN_SEED_RANGE_FILE + if not path.is_file(): + return False + recorded: dict[str, object] = tomllib.loads(path.read_text(encoding="utf-8")) + start: int = int(str(recorded.get("seed_start", 0))) + index: int = int(name.split("-", 1)[0]) + return start <= index < start + int(str(recorded.get("seeds", 0))) + + +def golden_path_masks(*, case_dir: Path, work_dir: Path) -> tuple[tuple[str, str], ...]: + """`(path, token)` for the project and work directories, resolved or not, longest first.""" + + workspace: Path = case_dir / PROJECT_DIRECTORY + masks: set[tuple[str, str]] = { + (str(workspace), GOLDEN_PATH_MASK), + (str(workspace.resolve()), GOLDEN_PATH_MASK), + (str(work_dir), GOLDEN_WORK_MASK), + (str(work_dir.resolve()), GOLDEN_WORK_MASK), + } + return tuple(sorted(masks, key=lambda mask: (-len(mask[0]), mask[0]))) + + +def masked_text(*, text: str, masks: tuple[tuple[str, str], ...]) -> str: + """Replace every masked path with its token, longest path first.""" + + for path, token in masks: + text = text.replace(path, token) + return text diff --git a/scripts/compiler_differential/_helpers/running/native_fallbacks.py b/scripts/compiler_differential/_helpers/running/native_fallbacks.py index a70d6e5e10..a4e580a19f 100644 --- a/scripts/compiler_differential/_helpers/running/native_fallbacks.py +++ b/scripts/compiler_differential/_helpers/running/native_fallbacks.py @@ -8,6 +8,7 @@ from scripts.compiler_differential.constants import ( CORPUS_SEEDS, + NATIVE_FALLBACK_ANSWER_SUFFIX, NATIVE_FALLBACK_COUNTS_FIELD, NATIVE_FALLBACK_DEFERRAL_STAGES, NATIVE_FALLBACK_DEFERRAL_UNKNOWN_STAGE, @@ -123,6 +124,7 @@ def fallback_problems( for corpus in sorted({*actual, *(exact or {}), *(bound or {})} & corpora): problem: str | None = _corpus_problem( label=_label(key=key, corpus=corpus), + answer=key[2].endswith(NATIVE_FALLBACK_ANSWER_SUFFIX), actual=actual.get(corpus, 0), exact=None if exact is None else exact.get(corpus, 0), bound=None if bound is None else bound.get(corpus, 0), @@ -180,7 +182,16 @@ def write_allow_list( _ = path.write_text("\n".join(lines) + "\n", encoding="utf-8") -def _corpus_problem(*, label: str, actual: int, exact: int | None, bound: int | None) -> str | None: +def _corpus_problem( + *, label: str, answer: bool, actual: int, exact: int | None, bound: int | None +) -> str | None: + if answer and exact is None and bound is None: + return f"{label}: native answered {actual}, not on the allow-list; record it" + if answer and actual == 0: + return ( + f"{label}: native no longer answers here, so this work now runs in Python; " + "restore the native path, or record the change with a reason" + ) if exact is None and bound is None: return f"{label}: {actual} not on the allow-list; port it or list it with a reason" if actual == 0: diff --git a/scripts/compiler_differential/_helpers/running/report.py b/scripts/compiler_differential/_helpers/running/report.py index 61289da4d1..e227a7a18b 100644 --- a/scripts/compiler_differential/_helpers/running/report.py +++ b/scripts/compiler_differential/_helpers/running/report.py @@ -4,7 +4,9 @@ from scripts.compiler_differential.constants import ( ANALYSIS_INDIRECT_KINDS, + BASELINES_HINT, CONFIG_ONLY_KINDS, + GOLDEN_LABEL, RENDER_CONFIG_ONLY_KINDS, RENDER_INDIRECT_KINDS, ) @@ -57,9 +59,15 @@ def format_summary( ) if gate_failures: lines.append(f"Native fallback allow-list: {len(gate_failures)} problems (listed above)") + if gate_failures or any(_golden_differs(comparison) for comparison in differing): + lines.append(BASELINES_HINT) return "\n".join(lines) +def _golden_differs(comparison: ProjectComparison) -> bool: + return any(GOLDEN_LABEL in (difference.labels or ()) for difference in comparison.differences) + + def format_discovery_coverage(*, covered: frozenset[str], required: tuple[str, ...]) -> str: """Report how many required discovery input kinds the seed corpus exercised.""" diff --git a/scripts/compiler_differential/constants.py b/scripts/compiler_differential/constants.py index c7e8d68783..d41561ce5d 100644 --- a/scripts/compiler_differential/constants.py +++ b/scripts/compiler_differential/constants.py @@ -15,6 +15,12 @@ NATIVE_FALLBACK_MODE_UPDATE: str = "update" NATIVE_FALLBACK_MODES: tuple[str, ...] = ("check", NATIVE_FALLBACK_MODE_UPDATE) NATIVE_FALLBACK_SITE_SEPARATOR: str = "." +NATIVE_FALLBACK_ANSWER_SUFFIX: str = ".native" +BASELINES_HINT: str = ( + "Intended? Run `make compiler-baselines` and commit the changes to " + "scripts/compiler_differential/native_fallbacks.toml and tests/goldens/compiler, with a " + "reason in the PR body for every added entry, higher fallback count or changed golden." +) NATIVE_FALLBACK_MAX_COUNTS_FIELD: str = "max_counts" NATIVE_FALLBACK_COUNTS_FIELD: str = "counts" NATIVE_FALLBACK_PROJECT_CORPUS: str = "project" @@ -41,14 +47,17 @@ GOLDEN_CORPUS_PREFIXES: frozenset[str] = frozenset( {"fixture", "example", "seed", "failure", "project"} ) -GOLDEN_OPTIONAL_CORPORA: frozenset[str] = frozenset({"seed"}) +GOLDEN_SEED_CORPUS: str = "seed" +GOLDEN_SEED_RANGE_FILE: str = "seed_range.toml" GOLDEN_SUFFIX: str = ".json" +GOLDEN_LABEL: str = "golden" GOLDEN_MANIFEST_DROPPED_KEYS: frozenset[str] = frozenset({"metadata", "child_map"}) GOLDEN_MANIFEST_RESOURCE_SECTIONS: tuple[str, ...] = ("nodes", "sources", "macros") GOLDEN_RESOURCE_DROPPED_FIELDS: frozenset[str] = frozenset( {"raw_code", "compiled_code", "checksum", "created_at"} ) GOLDEN_PATH_MASK: str = "" +GOLDEN_WORK_MASK: str = "" GOLDEN_VERSION_MASK: str = "" GOLDEN_MISSING_HINT: str = "no golden; record it with `make compiler-baselines`" ENGINE_NAMES: tuple[str, ...] = ("python", "native", "native-preview") diff --git a/scripts/compiler_differential/main/differential.py b/scripts/compiler_differential/main/differential.py index 622417aa59..1c22c74df0 100644 --- a/scripts/compiler_differential/main/differential.py +++ b/scripts/compiler_differential/main/differential.py @@ -12,6 +12,7 @@ from scripts.compiler_differential._helpers.coverage.analysis import required_analysis_kinds from scripts.compiler_differential._helpers.coverage.discovery import required_discovery_kinds from scripts.compiler_differential._helpers.coverage.render import required_render_kinds +from scripts.compiler_differential._helpers.goldens.goldens import write_golden_seed_range from scripts.compiler_differential._helpers.running.native_fallbacks import native_fallback_gate from scripts.compiler_differential._helpers.running.options import parse_expected_outcome from scripts.compiler_differential._helpers.running.records import write_wheel_site_report @@ -36,6 +37,7 @@ ENGINE_NAMES, EXPECT_SUCCESS, GOLDEN_DIRECTORY, + GOLDEN_MODE_UPDATE, GOLDEN_MODES, NATIVE_FALLBACK_LIST, NATIVE_FALLBACK_MODES, @@ -65,6 +67,10 @@ def run_compiler_differential(argv: list[str] | None = None) -> int: write_wheel_site_report( path=args.wheel_site_report, comparisons=comparisons, engines=options.engines ) + if args.goldens == GOLDEN_MODE_UPDATE and CORPUS_SEEDS in args.corpus: + write_golden_seed_range( + golden_dir=args.golden_dir, seed_start=args.seed_start, seeds=args.seeds + ) fallback_failures: tuple[str, ...] = native_fallback_gate( request=fallback_gate_request(args=args, engines=options.engines), comparisons=comparisons, diff --git a/scripts/compiler_differential/native_fallbacks.toml b/scripts/compiler_differential/native_fallbacks.toml index fec5686009..94b1ad458a 100644 --- a/scripts/compiler_differential/native_fallbacks.toml +++ b/scripts/compiler_differential/native_fallbacks.toml @@ -11,6 +11,48 @@ site = "attachments.audit_rendering" kind = "deferred" counts = { seed = 16 } +[[entry]] +engine = "native" +stage = "attachments" +site = "attachments.native" +kind = "audit_renderings" +counts = { example = 120, failure = 10, fixture = 112, seed = 354 } + +[[entry]] +engine = "native" +stage = "attachments" +site = "attachments.native" +kind = "cursor_intrinsic_rejections" +counts = { example = 160, failure = 164, fixture = 156, seed = 961 } + +[[entry]] +engine = "native" +stage = "attachments" +site = "attachments.native" +kind = "function_headers" +counts = { example = 8, failure = 7, fixture = 12, seed = 102 } + +[[entry]] +engine = "native" +stage = "attachments" +site = "attachments.native" +kind = "omitted_select_scans" +counts = { example = 24, failure = 18, fixture = 32, seed = 193 } + +[[entry]] +engine = "native" +stage = "attachments" +site = "attachments.native" +kind = "scenario_ctes" +counts = { example = 8, failure = 1, fixture = 12, seed = 37 } + +[[entry]] +engine = "native" +stage = "attachments" +site = "attachments.native" +kind = "seed_pairs" +counts = { example = 8, fixture = 4, seed = 26 } + [[entry]] engine = "native" stage = "attachments" @@ -18,6 +60,34 @@ site = "attachments.scenario_independence" kind = "deferred" counts = { failure = 1 } +[[entry]] +engine = "native" +stage = "declaration_files" +site = "declaration_files.native" +kind = "failed_files" +counts = { failure = 9 } + +[[entry]] +engine = "native" +stage = "declaration_files" +site = "declaration_files.native" +kind = "parsed_files" +counts = { example = 32, failure = 69, fixture = 20, seed = 799 } + +[[entry]] +engine = "native" +stage = "declaration_files" +site = "declaration_files.native" +kind = "seed_files" +counts = { example = 8, failure = 3, fixture = 4, seed = 43 } + +[[entry]] +engine = "native" +stage = "declaration_scopes" +site = "declaration_scopes.native" +kind = "scope_indexes" +counts = { example = 12, failure = 221, fixture = 12, seed = 44 } + [[entry]] engine = "native" stage = "declaration_scopes" @@ -30,14 +100,35 @@ engine = "native" stage = "declaration_scopes" site = "declaration_scopes.relationship_ctes" kind = "expected_names" -counts = { failure = 3, seed = 26 } +counts = { example = 1, failure = 3, fixture = 3, seed = 26 } + +[[entry]] +engine = "native" +stage = "discovery" +site = "discovery.native" +kind = "model_file_listings" +counts = { example = 12, failure = 286, fixture = 12, seed = 50 } + +[[entry]] +engine = "native" +stage = "macro_call_store" +site = "macro_call_store.native" +kind = "store_attachments" +counts = { example = 12, failure = 231, fixture = 12, seed = 47 } [[entry]] engine = "native" stage = "macro_calls" site = "macro_calls.arguments" kind = "deferred" -counts = { failure = 14, seed = 484 } +counts = { example = 6, failure = 14, fixture = 5, seed = 484 } + +[[entry]] +engine = "native" +stage = "macro_calls" +site = "macro_calls.native" +kind = "bridged_calls" +counts = { example = 32, failure = 9, fixture = 28, seed = 466 } [[entry]] engine = "native" @@ -67,54 +158,82 @@ site = "model_config.header_metadata" kind = "unsupported" counts = { failure = 1 } +[[entry]] +engine = "native" +stage = "model_config" +site = "model_config.native" +kind = "config_builds" +counts = { example = 56, failure = 298, fixture = 64, seed = 890 } + +[[entry]] +engine = "native" +stage = "model_config" +site = "model_config.native" +kind = "validations" +counts = { example = 56, failure = 365, fixture = 60, seed = 1091 } + [[entry]] engine = "native" stage = "model_config" site = "model_config.python_templates" kind = "api" -counts = { failure = 2, seed = 36 } +counts = { example = 16, failure = 2, seed = 36 } [[entry]] engine = "native" stage = "model_config" site = "model_config.python_templates" kind = "config" -counts = { failure = 108, seed = 36 } +counts = { example = 12, failure = 111, fixture = 8, seed = 36 } [[entry]] engine = "native" stage = "model_config" site = "model_config.python_templates" kind = "connection" -counts = { failure = 110, seed = 63 } +counts = { example = 18, failure = 113, fixture = 9, seed = 63 } + +[[entry]] +engine = "native" +stage = "model_config" +site = "model_config.python_templates" +kind = "effective_vars" +counts = { example = 29, failure = 236, fixture = 15, seed = 121 } [[entry]] engine = "native" stage = "model_config" site = "model_config.python_templates" kind = "seed_target" -counts = { seed = 30 } +counts = { example = 8, seed = 30 } [[entry]] engine = "native" stage = "model_config" site = "model_config.python_templates" kind = "source_target" -counts = { seed = 23 } +counts = { example = 8, seed = 23 } [[entry]] engine = "native" stage = "model_config" site = "model_config.validators" kind = "deferred" -counts = { failure = 2, seed = 3 } +counts = { failure = 2, fixture = 4, seed = 3 } [[entry]] engine = "native" stage = "model_loop" site = "model_loop.cursor_intrinsic_validation" kind = "deferred" -counts = { failure = 387, seed = 1100 } +counts = { example = 56, failure = 395, fixture = 64, seed = 1100 } + +[[entry]] +engine = "native" +stage = "model_loop" +site = "model_loop.declaration_context" +kind = "no_resource" +counts = { example = 24, failure = 9, fixture = 20, seed = 245 } [[entry]] engine = "native" @@ -123,6 +242,20 @@ site = "model_loop.declaration_references" kind = "after_variables" counts = { seed = 3 } +[[entry]] +engine = "native" +stage = "model_loop" +site = "model_loop.native" +kind = "declaration_expansions" +counts = { example = 56, failure = 406, fixture = 64, seed = 1100 } + +[[entry]] +engine = "native" +stage = "model_loop" +site = "model_loop.native" +kind = "variable_substitutions" +counts = { example = 56, failure = 448, fixture = 72, seed = 1157 } + [[entry]] engine = "native" stage = "model_loop" @@ -137,6 +270,13 @@ site = "model_loop.sql_variables" kind = "error" counts = { failure = 2 } +[[entry]] +engine = "native" +stage = "reference_extraction" +site = "reference_extraction.native" +kind = "reference_scans" +counts = { example = 250, failure = 448, fixture = 249, seed = 1523 } + [[entry]] engine = "native-preview" stage = "attachments" @@ -144,6 +284,48 @@ site = "attachments.audit_rendering" kind = "deferred" counts = { seed = 16 } +[[entry]] +engine = "native-preview" +stage = "attachments" +site = "attachments.native" +kind = "audit_renderings" +counts = { example = 120, failure = 10, fixture = 112, seed = 354 } + +[[entry]] +engine = "native-preview" +stage = "attachments" +site = "attachments.native" +kind = "cursor_intrinsic_rejections" +counts = { example = 160, failure = 164, fixture = 156, seed = 961 } + +[[entry]] +engine = "native-preview" +stage = "attachments" +site = "attachments.native" +kind = "function_headers" +counts = { example = 8, failure = 7, fixture = 12, seed = 102 } + +[[entry]] +engine = "native-preview" +stage = "attachments" +site = "attachments.native" +kind = "omitted_select_scans" +counts = { example = 24, failure = 18, fixture = 32, seed = 193 } + +[[entry]] +engine = "native-preview" +stage = "attachments" +site = "attachments.native" +kind = "scenario_ctes" +counts = { example = 8, failure = 1, fixture = 12, seed = 37 } + +[[entry]] +engine = "native-preview" +stage = "attachments" +site = "attachments.native" +kind = "seed_pairs" +counts = { example = 8, fixture = 4, seed = 26 } + [[entry]] engine = "native-preview" stage = "attachments" @@ -151,6 +333,48 @@ site = "attachments.scenario_independence" kind = "deferred" counts = { failure = 1 } +[[entry]] +engine = "native-preview" +stage = "contracts" +site = "contracts.native" +kind = "contract_validations" +counts = { example = 9, failure = 106, fixture = 6, seed = 25 } + +[[entry]] +engine = "native-preview" +stage = "contracts" +site = "contracts.native" +kind = "promotion_checks" +counts = { example = 12, failure = 106, fixture = 8, seed = 33 } + +[[entry]] +engine = "native-preview" +stage = "declaration_files" +site = "declaration_files.native" +kind = "failed_files" +counts = { failure = 9 } + +[[entry]] +engine = "native-preview" +stage = "declaration_files" +site = "declaration_files.native" +kind = "parsed_files" +counts = { example = 32, failure = 69, fixture = 20, seed = 799 } + +[[entry]] +engine = "native-preview" +stage = "declaration_files" +site = "declaration_files.native" +kind = "seed_files" +counts = { example = 8, failure = 3, fixture = 4, seed = 43 } + +[[entry]] +engine = "native-preview" +stage = "declaration_scopes" +site = "declaration_scopes.native" +kind = "scope_indexes" +counts = { example = 12, failure = 221, fixture = 12, seed = 44 } + [[entry]] engine = "native-preview" stage = "declaration_scopes" @@ -165,6 +389,27 @@ site = "declaration_scopes.relationship_ctes" kind = "expected_names" counts = { example = 1, failure = 3, fixture = 3, seed = 26 } +[[entry]] +engine = "native-preview" +stage = "discovery" +site = "discovery.native" +kind = "model_file_listings" +counts = { example = 12, failure = 286, fixture = 12, seed = 50 } + +[[entry]] +engine = "native-preview" +stage = "lineage_facts" +site = "lineage_facts.native" +kind = "lineage_graphs" +counts = { example = 9, failure = 106, fixture = 6, seed = 22 } + +[[entry]] +engine = "native-preview" +stage = "macro_call_store" +site = "macro_call_store.native" +kind = "store_attachments" +counts = { example = 12, failure = 231, fixture = 12, seed = 47 } + [[entry]] engine = "native-preview" stage = "macro_calls" @@ -172,6 +417,13 @@ site = "macro_calls.arguments" kind = "deferred" counts = { example = 6, failure = 14, fixture = 5, seed = 484 } +[[entry]] +engine = "native-preview" +stage = "macro_calls" +site = "macro_calls.native" +kind = "bridged_calls" +counts = { example = 32, failure = 9, fixture = 28, seed = 466 } + [[entry]] engine = "native-preview" stage = "macro_calls" @@ -191,7 +443,7 @@ engine = "native-preview" stage = "model_analysis" site = "analysis_session" kind = "input_enrichment" -counts = { example = 52, failure = 182, fixture = 52, seed = 614 } +counts = { failure = 1 } [[entry]] engine = "native-preview" @@ -200,6 +452,27 @@ site = "analysis_session" kind = "legacy_analysis" counts = { failure = 2, seed = 10 } +[[entry]] +engine = "native-preview" +stage = "model_analysis" +site = "model_analysis.native" +kind = "dynamic_contract_proofs" +counts = { seed = 3 } + +[[entry]] +engine = "native-preview" +stage = "model_analysis" +site = "model_analysis.native" +kind = "expression_shapes" +counts = { example = 4, failure = 111, fixture = 4, seed = 100 } + +[[entry]] +engine = "native-preview" +stage = "model_analysis" +site = "model_analysis.native" +kind = "model_analyses" +counts = { example = 56, failure = 214, fixture = 52, seed = 686 } + [[entry]] engine = "native-preview" stage = "model_config" @@ -214,6 +487,20 @@ site = "model_config.header_metadata" kind = "unsupported" counts = { failure = 1 } +[[entry]] +engine = "native-preview" +stage = "model_config" +site = "model_config.native" +kind = "config_builds" +counts = { example = 56, failure = 298, fixture = 64, seed = 890 } + +[[entry]] +engine = "native-preview" +stage = "model_config" +site = "model_config.native" +kind = "validations" +counts = { example = 56, failure = 365, fixture = 60, seed = 1091 } + [[entry]] engine = "native-preview" stage = "model_config" @@ -226,14 +513,21 @@ engine = "native-preview" stage = "model_config" site = "model_config.python_templates" kind = "config" -counts = { example = 12, failure = 108, fixture = 8, seed = 36 } +counts = { example = 12, failure = 111, fixture = 8, seed = 36 } [[entry]] engine = "native-preview" stage = "model_config" site = "model_config.python_templates" kind = "connection" -counts = { example = 18, failure = 110, fixture = 9, seed = 63 } +counts = { example = 18, failure = 113, fixture = 9, seed = 63 } + +[[entry]] +engine = "native-preview" +stage = "model_config" +site = "model_config.python_templates" +kind = "effective_vars" +counts = { example = 29, failure = 236, fixture = 15, seed = 121 } [[entry]] engine = "native-preview" @@ -261,7 +555,14 @@ engine = "native-preview" stage = "model_loop" site = "model_loop.cursor_intrinsic_validation" kind = "deferred" -counts = { example = 56, failure = 387, fixture = 64, seed = 1100 } +counts = { example = 56, failure = 395, fixture = 64, seed = 1100 } + +[[entry]] +engine = "native-preview" +stage = "model_loop" +site = "model_loop.declaration_context" +kind = "no_resource" +counts = { example = 24, failure = 9, fixture = 20, seed = 245 } [[entry]] engine = "native-preview" @@ -270,6 +571,20 @@ site = "model_loop.declaration_references" kind = "after_variables" counts = { seed = 3 } +[[entry]] +engine = "native-preview" +stage = "model_loop" +site = "model_loop.native" +kind = "declaration_expansions" +counts = { example = 56, failure = 406, fixture = 64, seed = 1100 } + +[[entry]] +engine = "native-preview" +stage = "model_loop" +site = "model_loop.native" +kind = "variable_substitutions" +counts = { example = 56, failure = 448, fixture = 72, seed = 1157 } + [[entry]] engine = "native-preview" stage = "model_loop" @@ -283,3 +598,45 @@ stage = "model_loop" site = "model_loop.sql_variables" kind = "error" counts = { failure = 2 } + +[[entry]] +engine = "native-preview" +stage = "project_assembly" +site = "project_assembly.assembly" +kind = "deferred" +counts = { example = 12, failure = 111, fixture = 8, seed = 36 } + +[[entry]] +engine = "native-preview" +stage = "reference_extraction" +site = "reference_extraction.native" +kind = "reference_scans" +counts = { example = 250, failure = 448, fixture = 249, seed = 1523 } + +[[entry]] +engine = "native-preview" +stage = "semantic_checks" +site = "semantic_checks.native" +kind = "semantic_completions" +counts = { example = 12, failure = 108, fixture = 8, seed = 36 } + +[[entry]] +engine = "native-preview" +stage = "sql_test_glue" +site = "sql_test_glue.assembly" +kind = "deferred" +counts = { example = 12, failure = 108, fixture = 8, seed = 36 } + +[[entry]] +engine = "native-preview" +stage = "sql_test_glue" +site = "sql_test_glue.planning" +kind = "deferred" +counts = { example = 1, failure = 3, fixture = 1, seed = 9 } + +[[entry]] +engine = "python" +stage = "discovery" +site = "discovery.native" +kind = "model_file_listings" +counts = { example = 12, failure = 286, fixture = 12, seed = 50 } diff --git a/src/sqlbuild/adapter/type_system/_helpers/type_normalization.py b/src/sqlbuild/adapter/type_system/_helpers/type_normalization.py index b169051872..98c8f6844c 100644 --- a/src/sqlbuild/adapter/type_system/_helpers/type_normalization.py +++ b/src/sqlbuild/adapter/type_system/_helpers/type_normalization.py @@ -43,7 +43,9 @@ UNBOUNDED_TEXT_TYPE_NAMES, ) from sqlbuild.compiler.frontier.main.native_stage_enabled import native_stage_enabled -from sqlbuild.compiler.frontier.types import NativeStage +from sqlbuild.compiler.frontier.main.report_native_answer import report_native_answer +from sqlbuild.compiler.frontier.main.report_native_fallback import report_native_fallback +from sqlbuild.compiler.frontier.types import NativeFallbackSite, NativeStage from sqlbuild.compiler.sql_analysis.main.import_polyglot import import_polyglot from sqlbuild.diagnostics.main.log_debug_event import log_debug_event @@ -59,7 +61,9 @@ def normalize_type(*, type_sql: str, dialect: TypeDialect | str | None) -> Norma type_sql=type_sql, dialect=dialect ) if native_normalized is not None: + report_native_answer(stage=NativeStage.TYPE_SYSTEM, kind="type_normalizations") return native_normalized + report_native_fallback(site=NativeFallbackSite.TYPE_NORMALIZATION) polyglot_normalized: NormalizedType | None = _normalize_with_polyglot( type_sql=type_sql, dialect=dialect, diff --git a/src/sqlbuild/cli/commands/_helpers/compile/target_writer.py b/src/sqlbuild/cli/commands/_helpers/compile/target_writer.py index 6f04c17d01..b86e310e10 100644 --- a/src/sqlbuild/cli/commands/_helpers/compile/target_writer.py +++ b/src/sqlbuild/cli/commands/_helpers/compile/target_writer.py @@ -48,6 +48,7 @@ FunctionLanguage, ) from sqlbuild.compiler.frontier.main.native_stage_enabled import native_stage_enabled +from sqlbuild.compiler.frontier.main.report_native_answer import report_native_answer from sqlbuild.compiler.frontier.types import NativeStage from sqlbuild.compiler.planner.exceptions import PlannerInputError from sqlbuild.compiler.planner.main.execution.sql_test_artifacts import ( @@ -574,10 +575,9 @@ def _plan_static_test_artifacts( with OperationLifecycle( operation_kind="project", operation_name="sql_test_planning" ) as lifecycle: + native_planning: bool = native_stage_enabled(NativeStage.SQL_TEST_GLUE) plan_artifacts: Callable[..., tuple[NativeSqlTestArtifact, ...]] = ( - plan_native_sql_test_artifacts - if native_stage_enabled(NativeStage.SQL_TEST_GLUE) - else plan_and_render_sql_test_artifacts + plan_native_sql_test_artifacts if native_planning else plan_and_render_sql_test_artifacts ) native_artifacts: tuple[NativeSqlTestArtifact, ...] = plan_artifacts( project=project, @@ -585,6 +585,8 @@ def _plan_static_test_artifacts( adapter=adapter, sql_analysis_enabled=project.settings.sql_analysis, ) + if native_planning: + report_native_answer(stage=NativeStage.SQL_TEST_GLUE, kind="sql_test_plans") lifecycle.completed(metadata={"item_count": len(native_artifacts)}) return native_artifacts diff --git a/src/sqlbuild/compiler/compile/_helpers/assembly/project.py b/src/sqlbuild/compiler/compile/_helpers/assembly/project.py index 01d0001065..a11284bf4e 100644 --- a/src/sqlbuild/compiler/compile/_helpers/assembly/project.py +++ b/src/sqlbuild/compiler/compile/_helpers/assembly/project.py @@ -163,8 +163,8 @@ DiagnosticSeverity, SqlTestMode, ) -from sqlbuild.compiler.frontier.main._report_native_fallback import report_native_fallback from sqlbuild.compiler.frontier.main.native_stage_enabled import native_stage_enabled +from sqlbuild.compiler.frontier.main.report_native_fallback import report_native_fallback from sqlbuild.compiler.frontier.types import NativeFallbackSite, NativeStage from sqlbuild.compiler.lineage.types import ColumnLineageMode, InferredNullability from sqlbuild.compiler.planner.types import ContractPolicy diff --git a/src/sqlbuild/compiler/compile/_helpers/assembly/targets.py b/src/sqlbuild/compiler/compile/_helpers/assembly/targets.py index 49ca82c5d2..0f535172a1 100644 --- a/src/sqlbuild/compiler/compile/_helpers/assembly/targets.py +++ b/src/sqlbuild/compiler/compile/_helpers/assembly/targets.py @@ -8,8 +8,8 @@ from sqlbuild.compiler.compile._helpers.render.templating import expand_template_data from sqlbuild.compiler.compile.constants import PRESERVE_TARGET_VALUE from sqlbuild.compiler.compile.models import CompiledRelationLocation, CompileModelInput -from sqlbuild.compiler.frontier.main._report_native_fallback import report_native_fallback from sqlbuild.compiler.frontier.main.native_stage_enabled import native_stage_enabled +from sqlbuild.compiler.frontier.main.report_native_fallback import report_native_fallback from sqlbuild.compiler.frontier.types import NativeFallbackSite, NativeStage from sqlbuild.spec.contracts.models import DefaultsConfig, SchemaSeedEntry, TargetConfig diff --git a/src/sqlbuild/compiler/compile/_helpers/attachment/audits.py b/src/sqlbuild/compiler/compile/_helpers/attachment/audits.py index cd3c9eba91..557365de07 100644 --- a/src/sqlbuild/compiler/compile/_helpers/attachment/audits.py +++ b/src/sqlbuild/compiler/compile/_helpers/attachment/audits.py @@ -72,8 +72,9 @@ DiscoveredAuditFile, DiscoveredProjectInputs, ) -from sqlbuild.compiler.frontier.main._report_native_fallback import report_native_fallback from sqlbuild.compiler.frontier.main.native_stage_enabled import native_stage_enabled +from sqlbuild.compiler.frontier.main.report_native_answer import report_native_answer +from sqlbuild.compiler.frontier.main.report_native_fallback import report_native_fallback from sqlbuild.compiler.frontier.types import NativeFallbackSite, NativeStage from sqlbuild.compiler.references.types import SqlReferenceKind from sqlbuild.compiler.scopes.models import ( @@ -663,6 +664,8 @@ def build_attached_audit_input( ) if native_rendering is None and native_stage_enabled(NativeStage.ATTACHMENTS): report_native_fallback(site=NativeFallbackSite.AUDIT_RENDERING) + if native_rendering is not None: + report_native_answer(stage=NativeStage.ATTACHMENTS, kind="audit_renderings") if native_rendering is not None and native_rendering.render_error is not None: raise CompileInputError(native_rendering.render_error, bridge_independent=True) rendered_sql_body, rendered_evidence_sql = ( diff --git a/src/sqlbuild/compiler/compile/_helpers/attachment/core.py b/src/sqlbuild/compiler/compile/_helpers/attachment/core.py index b762c83863..523b117c29 100644 --- a/src/sqlbuild/compiler/compile/_helpers/attachment/core.py +++ b/src/sqlbuild/compiler/compile/_helpers/attachment/core.py @@ -142,8 +142,9 @@ PythonHookEntry, SqlHookEntry, ) -from sqlbuild.compiler.frontier.main._report_native_fallback import report_native_fallback from sqlbuild.compiler.frontier.main.native_stage_enabled import native_stage_enabled +from sqlbuild.compiler.frontier.main.report_native_answer import report_native_answer +from sqlbuild.compiler.frontier.main.report_native_fallback import report_native_fallback from sqlbuild.compiler.frontier.types import NativeFallbackSite, NativeStage from sqlbuild.compiler.model_config.main._parse_native_header_metadata import ( parse_native_header_metadata, @@ -555,6 +556,9 @@ def _build_model_inputs( if native_stage_enabled(NativeStage.MODEL_LOOP) else () ) + report_native_answer( + stage=NativeStage.MODEL_LOOP, kind="variable_substitutions", units=len(prepared_files) + ) loop: _ModelInputLoop = _ModelInputLoop( discovered_inputs=discovered_inputs, context=context, @@ -746,6 +750,8 @@ def _build_model_input( if prepared_var_substituted_sql is not None else None ) + if declaration_expansion is not None and native_stage_enabled(NativeStage.MODEL_LOOP): + report_native_answer(stage=NativeStage.MODEL_LOOP, kind="declaration_expansions") if declaration_expansion is None: if native_stage_enabled(NativeStage.MODEL_LOOP): report_native_fallback( @@ -1235,6 +1241,7 @@ def _native_seed_inputs( "has no matching CSV file under seeds/", bridge_independent=True, ) + report_native_answer(stage=NativeStage.ATTACHMENTS, kind="seed_pairs", units=len(pairs)) return tuple( CompileSeedInput( seed_file=seed_files[file_index], @@ -1315,6 +1322,8 @@ def build_effective_vars( values.update(target_config.vars) values.update(local_config.vars) values.update(cli_vars) + if native_stage_enabled(NativeStage.MODEL_CONFIG): + report_native_fallback(site=NativeFallbackSite.PYTHON_TEMPLATES, kind="effective_vars") return expand_effective_vars(values) diff --git a/src/sqlbuild/compiler/compile/_helpers/attachment/declaration_scope.py b/src/sqlbuild/compiler/compile/_helpers/attachment/declaration_scope.py index 68cb653078..768429f794 100644 --- a/src/sqlbuild/compiler/compile/_helpers/attachment/declaration_scope.py +++ b/src/sqlbuild/compiler/compile/_helpers/attachment/declaration_scope.py @@ -27,8 +27,9 @@ ScopeRelationshipBuild, ) from sqlbuild.compiler.discovery.models import DiscoveredProjectInputs -from sqlbuild.compiler.frontier.main._report_native_fallback import report_native_fallback from sqlbuild.compiler.frontier.main.native_stage_enabled import native_stage_enabled +from sqlbuild.compiler.frontier.main.report_native_answer import report_native_answer +from sqlbuild.compiler.frontier.main.report_native_fallback import report_native_fallback from sqlbuild.compiler.frontier.types import NativeFallbackSite, NativeStage from sqlbuild.compiler.scopes.classes.native_scope_index import NativeScopeIndex from sqlbuild.compiler.scopes.exceptions import ScopeValidationError @@ -148,6 +149,7 @@ def _build_native_declaration_scope( if lookup is None: report_native_fallback(site=NativeFallbackSite.SCOPE_LOOKUP) return None + report_native_answer(stage=NativeStage.DECLARATION_SCOPES, kind="scope_indexes") return DeclarationScopeBuild( loaded_macros=loaded_macros, index=index, diff --git a/src/sqlbuild/compiler/compile/_helpers/attachment/functions.py b/src/sqlbuild/compiler/compile/_helpers/attachment/functions.py index e32aeb4c10..f88834410f 100644 --- a/src/sqlbuild/compiler/compile/_helpers/attachment/functions.py +++ b/src/sqlbuild/compiler/compile/_helpers/attachment/functions.py @@ -72,8 +72,9 @@ DiscoveredPythonFunctionFile, DiscoveredSqlFunctionFile, ) -from sqlbuild.compiler.frontier.main._report_native_fallback import report_native_fallback from sqlbuild.compiler.frontier.main.native_stage_enabled import native_stage_enabled +from sqlbuild.compiler.frontier.main.report_native_answer import report_native_answer +from sqlbuild.compiler.frontier.main.report_native_fallback import report_native_fallback from sqlbuild.compiler.frontier.types import NativeFallbackSite, NativeStage from sqlbuild.compiler.scopes.models import ResourceIdentity from sqlbuild.compiler.scopes.types import ResourceKind @@ -341,6 +342,7 @@ def _sql_function_header( if native and native_header is None: report_native_fallback(site=NativeFallbackSite.FUNCTION_HEADER, kind="sql") if native_header is not None: + report_native_answer(stage=NativeStage.ATTACHMENTS, kind="function_headers") _raise_header_failure(header=native_header, stage=HEADER_START_STAGE) arguments: tuple[FunctionArgument, ...] = _expanded_native_arguments( header=native_header, @@ -490,6 +492,8 @@ def _build_python_function_input( ) if native and native_header is None: report_native_fallback(site=NativeFallbackSite.FUNCTION_HEADER, kind="python") + if native_header is not None: + report_native_answer(stage=NativeStage.ATTACHMENTS, kind="function_headers") arguments: tuple[FunctionArgument, ...] returns: str runtime_version: str diff --git a/src/sqlbuild/compiler/compile/_helpers/attachment/model_config.py b/src/sqlbuild/compiler/compile/_helpers/attachment/model_config.py index 71c668c231..db35c7d23e 100644 --- a/src/sqlbuild/compiler/compile/_helpers/attachment/model_config.py +++ b/src/sqlbuild/compiler/compile/_helpers/attachment/model_config.py @@ -65,8 +65,9 @@ PythonHookEntry, SqlHookEntry, ) -from sqlbuild.compiler.frontier.main._report_native_fallback import report_native_fallback -from sqlbuild.compiler.frontier.types import NativeFallbackSite +from sqlbuild.compiler.frontier.main.report_native_answer import report_native_answer +from sqlbuild.compiler.frontier.main.report_native_fallback import report_native_fallback +from sqlbuild.compiler.frontier.types import NativeFallbackSite, NativeStage from sqlbuild.compiler.model_config.main._native_config_contains_macro_call import ( native_config_contains_macro_call, ) @@ -939,6 +940,7 @@ def build_native_model_config( if built is None: report_native_fallback(site=NativeFallbackSite.CONFIG_BUILD) return None + report_native_answer(stage=NativeStage.MODEL_CONFIG, kind="config_builds") if isinstance(built, _native.NativeConfigError): raise native_config_error(error=built, bridge_independent=True) values, header_keys, namespace, overrides, reads = built @@ -1025,6 +1027,7 @@ def validate_model_config( if isinstance(outcome, _native.NativeConfigError): raise native_validation_error(error=outcome, values=request.config.values) if outcome: + report_native_answer(stage=NativeStage.MODEL_CONFIG, kind="validations") return report_native_fallback(site=NativeFallbackSite.CONFIG_VALIDATORS) run_python_model_validators(context=context, request=request) diff --git a/src/sqlbuild/compiler/compile/_helpers/attachment/scope_relationships.py b/src/sqlbuild/compiler/compile/_helpers/attachment/scope_relationships.py index 268172dc73..00903c0608 100644 --- a/src/sqlbuild/compiler/compile/_helpers/attachment/scope_relationships.py +++ b/src/sqlbuild/compiler/compile/_helpers/attachment/scope_relationships.py @@ -33,7 +33,7 @@ ) from sqlbuild.compiler.compile.types import SqlTestMode from sqlbuild.compiler.discovery.models import DiscoveredProjectInputs, DiscoveredSqlTestFile -from sqlbuild.compiler.frontier.main._report_native_fallback import report_native_fallback +from sqlbuild.compiler.frontier.main.report_native_fallback import report_native_fallback from sqlbuild.compiler.frontier.types import NativeFallbackSite from sqlbuild.compiler.scopes.main._native_expected_model_names import ( native_expected_model_names, @@ -160,6 +160,10 @@ def extract_scope_relationship_facts( if isinstance(scanned, str): faults.append(ScopeRelationshipFault(scenario.relative_path, scanned)) continue + if scanned is None: + report_native_fallback( + site=NativeFallbackSite.SCOPE_RELATIONSHIP_CTES, kind="scenario_expected_names" + ) try: facts.append( RelationshipFact( diff --git a/src/sqlbuild/compiler/compile/_helpers/diagnostics/recovery.py b/src/sqlbuild/compiler/compile/_helpers/diagnostics/recovery.py index 521430a59e..0f0d862836 100644 --- a/src/sqlbuild/compiler/compile/_helpers/diagnostics/recovery.py +++ b/src/sqlbuild/compiler/compile/_helpers/diagnostics/recovery.py @@ -28,6 +28,7 @@ CompilerDiagnostic, ) from sqlbuild.compiler.frontier.main.native_stage_enabled import native_stage_enabled +from sqlbuild.compiler.frontier.main.report_native_answer import report_native_answer from sqlbuild.compiler.frontier.types import NativeStage from sqlbuild.compiler.semantic_checks.main._complete_native_semantic_diagnostics import ( complete_native_semantic_diagnostics, @@ -54,6 +55,7 @@ def complete_semantic_diagnostics( resource_sql_analysis=resource_sql_analysis, ) if native_project is not None: + report_native_answer(stage=NativeStage.SEMANTIC_CHECKS, kind="semantic_completions") return native_project project = recover_output_types(project=project, binding_results=binding_results) project = replace( diff --git a/src/sqlbuild/compiler/compile/_helpers/macro_bridge/call_store.py b/src/sqlbuild/compiler/compile/_helpers/macro_bridge/call_store.py index 8e8c3238f2..c9825a19c8 100644 --- a/src/sqlbuild/compiler/compile/_helpers/macro_bridge/call_store.py +++ b/src/sqlbuild/compiler/compile/_helpers/macro_bridge/call_store.py @@ -7,6 +7,7 @@ from sqlbuild.compiler.compile._helpers.attachment.target import build_compile_target_context from sqlbuild.compiler.discovery.models import DiscoveredProjectInputs from sqlbuild.compiler.frontier.main.native_stage_enabled import native_stage_enabled +from sqlbuild.compiler.frontier.main.report_native_answer import report_native_answer from sqlbuild.compiler.frontier.types import NativeStage from sqlbuild.compiler.macro_bridge.classes.macro_bridge import MacroBridge from sqlbuild.compiler.macro_bridge.main._claim_first_compile import claim_first_compile @@ -45,6 +46,7 @@ def target_context_with_macro_call_store( project_dir=project_dir, model_paths=_model_paths(discovered_inputs=discovered_inputs, project_dir=project_dir), ) + report_native_answer(stage=NativeStage.MACRO_CALL_STORE, kind="store_attachments") return target_context diff --git a/src/sqlbuild/compiler/compile/_helpers/macro_bridge/expansion.py b/src/sqlbuild/compiler/compile/_helpers/macro_bridge/expansion.py index ce4cfe0365..dd2456cccd 100644 --- a/src/sqlbuild/compiler/compile/_helpers/macro_bridge/expansion.py +++ b/src/sqlbuild/compiler/compile/_helpers/macro_bridge/expansion.py @@ -21,8 +21,9 @@ MacroExpansionState, ) from sqlbuild.compiler.frontier.exceptions import NativeStageMismatchError -from sqlbuild.compiler.frontier.main._report_native_fallback import report_native_fallback -from sqlbuild.compiler.frontier.types import NativeFallbackSite +from sqlbuild.compiler.frontier.main.report_native_answer import report_native_answer +from sqlbuild.compiler.frontier.main.report_native_fallback import report_native_fallback +from sqlbuild.compiler.frontier.types import NativeFallbackSite, NativeStage from sqlbuild.compiler.macro_bridge.classes.macro_bridge import MacroBridge from sqlbuild.compiler.macro_bridge.constants import ( ARGUMENT_REFERENCE_EVENT, @@ -86,6 +87,7 @@ def expand_bridged_sql_macros( ) for site, call_class in zip(sites, call_classes, strict=True) ] + report_native_answer(stage=NativeStage.MACRO_CALLS, kind="bridged_calls", units=len(sites)) return bridge.splice(sql=sql, sites=sites, outputs=outputs) diff --git a/src/sqlbuild/compiler/compile/_helpers/native_stages/assembly.py b/src/sqlbuild/compiler/compile/_helpers/native_stages/assembly.py index 2c1f1bc21d..1acb384266 100644 --- a/src/sqlbuild/compiler/compile/_helpers/native_stages/assembly.py +++ b/src/sqlbuild/compiler/compile/_helpers/native_stages/assembly.py @@ -25,7 +25,9 @@ ModelSqlAnalysis, ) from sqlbuild.compiler.frontier.main.native_stage_enabled import native_stage_enabled -from sqlbuild.compiler.frontier.types import NativeStage +from sqlbuild.compiler.frontier.main.report_native_answer import report_native_answer +from sqlbuild.compiler.frontier.main.report_native_fallback import report_native_fallback +from sqlbuild.compiler.frontier.types import NativeFallbackSite, NativeStage from sqlbuild.compiler.project_assembly.main._assemble_native_project_resources import ( assemble_native_project_resources, ) @@ -44,7 +46,7 @@ def project_resources_by_engine( if not native_stage_enabled(NativeStage.PROJECT_ASSEMBLY): return None - return assemble_native_project_resources( + resources: NativeProjectResources | None = assemble_native_project_resources( inputs=inputs, dialect=dialect, syntax_checks=model_syntax_checks( @@ -53,6 +55,11 @@ def project_resources_by_engine( analysis_succeeded=analysis_succeeded, ), ) + if resources is not None: + report_native_answer(stage=NativeStage.PROJECT_ASSEMBLY, kind="project_assemblies") + else: + report_native_fallback(site=NativeFallbackSite.PROJECT_ASSEMBLY) + return resources def expression_source_shapes_by_engine( @@ -65,6 +72,9 @@ def expression_source_shapes_by_engine( infer_native_expression_source_shapes(expressions=expressions, profile=profile) ) if native_shapes is not None: + report_native_answer( + stage=NativeStage.MODEL_ANALYSIS, kind="expression_shapes", units=len(native_shapes) + ) return native_shapes return get_expression_source_shapes(expressions=expressions, profile=profile) @@ -83,6 +93,9 @@ def analyze_model_sql_by_engine( ) ) if native_analyses is not None: + report_native_answer( + stage=NativeStage.MODEL_ANALYSIS, kind="model_analyses", units=len(native_analyses) + ) return native_analyses return python_analysis() @@ -105,5 +118,6 @@ def dynamic_column_contract_by_engine( **python_proof.keywords ) if native is not None: + report_native_answer(stage=NativeStage.MODEL_ANALYSIS, kind="dynamic_contract_proofs") return native return python_proof() diff --git a/src/sqlbuild/compiler/compile/_helpers/native_stages/sql_tests.py b/src/sqlbuild/compiler/compile/_helpers/native_stages/sql_tests.py index fde4b1cf87..e8f58c62ec 100644 --- a/src/sqlbuild/compiler/compile/_helpers/native_stages/sql_tests.py +++ b/src/sqlbuild/compiler/compile/_helpers/native_stages/sql_tests.py @@ -10,7 +10,9 @@ CompileSqlTestInput, ) from sqlbuild.compiler.frontier.main.native_stage_enabled import native_stage_enabled -from sqlbuild.compiler.frontier.types import NativeStage +from sqlbuild.compiler.frontier.main.report_native_answer import report_native_answer +from sqlbuild.compiler.frontier.main.report_native_fallback import report_native_fallback +from sqlbuild.compiler.frontier.types import NativeFallbackSite, NativeStage from sqlbuild.compiler.sql_test_glue.main._assemble_native_sql_tests import ( assemble_native_sql_tests, ) @@ -26,5 +28,9 @@ def assemble_sql_tests_by_engine( if native_stage_enabled(NativeStage.SQL_TEST_GLUE): native_tests: tuple[CompiledSqlTest, ...] | None = assemble_native_sql_tests(inputs=inputs) if native_tests is not None: + report_native_answer( + stage=NativeStage.SQL_TEST_GLUE, kind="sql_test_assemblies", units=len(native_tests) + ) return native_tests + report_native_fallback(site=NativeFallbackSite.SQL_TEST_ASSEMBLY) return tuple(assemble_python_test(test_input) for test_input in inputs.test_inputs) diff --git a/src/sqlbuild/compiler/compile/_helpers/refs/references.py b/src/sqlbuild/compiler/compile/_helpers/refs/references.py index a4032c8d3c..1d362798ee 100644 --- a/src/sqlbuild/compiler/compile/_helpers/refs/references.py +++ b/src/sqlbuild/compiler/compile/_helpers/refs/references.py @@ -33,8 +33,9 @@ DiagnosticSeverity, SqlReferenceScanFailure, ) -from sqlbuild.compiler.frontier.main._report_native_fallback import report_native_fallback from sqlbuild.compiler.frontier.main.native_stage_enabled import native_stage_enabled +from sqlbuild.compiler.frontier.main.report_native_answer import report_native_answer +from sqlbuild.compiler.frontier.main.report_native_fallback import report_native_fallback from sqlbuild.compiler.frontier.types import NativeFallbackSite, NativeStage from sqlbuild.compiler.references.types import SqlReferenceKind from sqlbuild.compiler.sql_analysis.main._find_matching_paren import find_matching_paren @@ -101,6 +102,7 @@ def _reference_scan_outcome( sql=sql, syntax=syntax ) if native is not None: + report_native_answer(stage=NativeStage.REFERENCE_EXTRACTION, kind="reference_scans") return native report_native_fallback(site=NativeFallbackSite.REFERENCE_SCAN) return python_reference_scan(sql=sql, syntax=syntax) diff --git a/src/sqlbuild/compiler/compile/_helpers/render/context_templates.py b/src/sqlbuild/compiler/compile/_helpers/render/context_templates.py index 93d4bea1f3..b995a5e940 100644 --- a/src/sqlbuild/compiler/compile/_helpers/render/context_templates.py +++ b/src/sqlbuild/compiler/compile/_helpers/render/context_templates.py @@ -15,8 +15,8 @@ PRESERVE_TARGET_VALUE, ) from sqlbuild.compiler.compile.types import CompileContextKey -from sqlbuild.compiler.frontier.main._report_native_fallback import report_native_fallback from sqlbuild.compiler.frontier.main.native_stage_enabled import native_stage_enabled +from sqlbuild.compiler.frontier.main.report_native_fallback import report_native_fallback from sqlbuild.compiler.frontier.types import NativeFallbackSite, NativeStage from sqlbuild.compiler.model_config.constants import ENVIRONMENT_READ from sqlbuild.compiler.model_config.main._expand_native_config_templates import ( diff --git a/src/sqlbuild/compiler/compile/_helpers/render/cursor_intrinsics.py b/src/sqlbuild/compiler/compile/_helpers/render/cursor_intrinsics.py index c9c3e3b33c..22b464a250 100644 --- a/src/sqlbuild/compiler/compile/_helpers/render/cursor_intrinsics.py +++ b/src/sqlbuild/compiler/compile/_helpers/render/cursor_intrinsics.py @@ -11,8 +11,9 @@ SQL_QUOTE_TOKENS, ) from sqlbuild.compiler.compile.exceptions import CompileInputError -from sqlbuild.compiler.frontier.main._report_native_fallback import report_native_fallback from sqlbuild.compiler.frontier.main.native_stage_enabled import native_stage_enabled +from sqlbuild.compiler.frontier.main.report_native_answer import report_native_answer +from sqlbuild.compiler.frontier.main.report_native_fallback import report_native_fallback from sqlbuild.compiler.frontier.types import NativeFallbackSite, NativeStage from sqlbuild.compiler.planner.constants import ( MICROBATCH_END_SENTINEL, @@ -67,6 +68,8 @@ def reject_cursor_intrinsics(*, sql: str, context: str) -> None: free, error = _native.sql_free_of_cursor_intrinsics( sql, [MICROBATCH_START_SENTINEL, MICROBATCH_END_SENTINEL], context ) + if free or error is not None: + report_native_answer(stage=NativeStage.ATTACHMENTS, kind="cursor_intrinsic_rejections") if free: return if error is not None: diff --git a/src/sqlbuild/compiler/compile/_helpers/render/declarations.py b/src/sqlbuild/compiler/compile/_helpers/render/declarations.py index 24f8ec36eb..644bb14747 100644 --- a/src/sqlbuild/compiler/compile/_helpers/render/declarations.py +++ b/src/sqlbuild/compiler/compile/_helpers/render/declarations.py @@ -32,8 +32,8 @@ EnumMember, ModelSchemaDeclaration, ) -from sqlbuild.compiler.frontier.main._report_native_fallback import report_native_fallback from sqlbuild.compiler.frontier.main.native_stage_enabled import native_stage_enabled +from sqlbuild.compiler.frontier.main.report_native_fallback import report_native_fallback from sqlbuild.compiler.frontier.types import NativeFallbackSite, NativeStage from sqlbuild.compiler.model_loop.constants import ( ENUM_REFERENCE_KIND_CODE, @@ -232,6 +232,11 @@ def resolve_declaration_context( and not _scope_query_parses_identity(resource=resource, target_path=target_path) else None ) + if native_context is None and resolver.native_contexts is not None: + report_native_fallback( + site=NativeFallbackSite.DECLARATION_CONTEXT, + kind="deferred" if resources else "no_resource", + ) context: DeclarationResolutionContext = ( native_context if native_context is not None diff --git a/src/sqlbuild/compiler/compile/_helpers/render/macros.py b/src/sqlbuild/compiler/compile/_helpers/render/macros.py index c67e699093..eb8ef33c10 100644 --- a/src/sqlbuild/compiler/compile/_helpers/render/macros.py +++ b/src/sqlbuild/compiler/compile/_helpers/render/macros.py @@ -49,8 +49,8 @@ DiscoveredMacroFile, EnumDeclaration, ) -from sqlbuild.compiler.frontier.main._report_native_fallback import report_native_fallback from sqlbuild.compiler.frontier.main.native_stage_enabled import native_stage_enabled +from sqlbuild.compiler.frontier.main.report_native_fallback import report_native_fallback from sqlbuild.compiler.frontier.types import NativeFallbackSite, NativeStage from sqlbuild.compiler.macro_bridge.classes.macro_bridge import MacroBridge from sqlbuild.compiler.macro_bridge.constants import ( diff --git a/src/sqlbuild/compiler/compile/_helpers/render/sql_vars.py b/src/sqlbuild/compiler/compile/_helpers/render/sql_vars.py index cb41087c36..e6e19e3b0d 100644 --- a/src/sqlbuild/compiler/compile/_helpers/render/sql_vars.py +++ b/src/sqlbuild/compiler/compile/_helpers/render/sql_vars.py @@ -37,8 +37,8 @@ MacroExpansionResult, ) from sqlbuild.compiler.compile.types import TypedSqlValueRenderer -from sqlbuild.compiler.frontier.main._report_native_fallback import report_native_fallback from sqlbuild.compiler.frontier.main.native_stage_enabled import native_stage_enabled +from sqlbuild.compiler.frontier.main.report_native_fallback import report_native_fallback from sqlbuild.compiler.frontier.types import NativeFallbackSite, NativeStage from sqlbuild.compiler.model_loop.main._scan_native_declaration_references import ( scan_native_declaration_references, diff --git a/src/sqlbuild/compiler/compile/_helpers/scenarios/core.py b/src/sqlbuild/compiler/compile/_helpers/scenarios/core.py index f9382e915c..11682b900c 100644 --- a/src/sqlbuild/compiler/compile/_helpers/scenarios/core.py +++ b/src/sqlbuild/compiler/compile/_helpers/scenarios/core.py @@ -29,8 +29,9 @@ CompileSqlScenarioCte, CompileSqlScenarioCtes, ) -from sqlbuild.compiler.frontier.main._report_native_fallback import report_native_fallback from sqlbuild.compiler.frontier.main.native_stage_enabled import native_stage_enabled +from sqlbuild.compiler.frontier.main.report_native_answer import report_native_answer +from sqlbuild.compiler.frontier.main.report_native_fallback import report_native_fallback from sqlbuild.compiler.frontier.types import NativeFallbackSite, NativeStage from sqlbuild.compiler.sql_analysis.models import SqlLexicalSyntax @@ -51,6 +52,7 @@ def extract_sql_scenario_ctes( else None ) if native_ctes is not None: + report_native_answer(stage=NativeStage.ATTACHMENTS, kind="scenario_ctes") return native_ctes if native_stage_enabled(NativeStage.ATTACHMENTS): report_native_fallback(site=NativeFallbackSite.SCENARIO_CTES) diff --git a/src/sqlbuild/compiler/compile/_helpers/sql_tests/core.py b/src/sqlbuild/compiler/compile/_helpers/sql_tests/core.py index f9a5ca7857..4157dd2385 100644 --- a/src/sqlbuild/compiler/compile/_helpers/sql_tests/core.py +++ b/src/sqlbuild/compiler/compile/_helpers/sql_tests/core.py @@ -29,8 +29,9 @@ from sqlbuild.compiler.discovery.main.omitted_ceremonial_select import ( omitted_ceremonial_select_offset, ) -from sqlbuild.compiler.frontier.main._report_native_fallback import report_native_fallback from sqlbuild.compiler.frontier.main.native_stage_enabled import native_stage_enabled +from sqlbuild.compiler.frontier.main.report_native_answer import report_native_answer +from sqlbuild.compiler.frontier.main.report_native_fallback import report_native_fallback from sqlbuild.compiler.frontier.types import NativeFallbackSite, NativeStage from sqlbuild.compiler.references.types import SqlReferenceKind from sqlbuild.compiler.sql_analysis.main._find_matching_paren import find_matching_paren @@ -158,7 +159,9 @@ def complete_omitted_ceremonial_select(*, sql: str, syntax: SqlLexicalSyntax) -> if native_stage_enabled(NativeStage.ATTACHMENTS) else (False, None) ) - if not answered: + if answered: + report_native_answer(stage=NativeStage.ATTACHMENTS, kind="omitted_select_scans") + else: if native_stage_enabled(NativeStage.ATTACHMENTS): report_native_fallback(site=NativeFallbackSite.OMITTED_SELECT) offset = omitted_ceremonial_select_offset(sql=sql, syntax=syntax) diff --git a/src/sqlbuild/compiler/compile/main/expand_sql_with_spans.py b/src/sqlbuild/compiler/compile/main/expand_sql_with_spans.py index e3e7690cbb..2b1771d007 100644 --- a/src/sqlbuild/compiler/compile/main/expand_sql_with_spans.py +++ b/src/sqlbuild/compiler/compile/main/expand_sql_with_spans.py @@ -11,8 +11,8 @@ ExpansionSpan, SqlExpansionContext, ) -from sqlbuild.compiler.frontier.main._report_native_fallback import report_native_fallback from sqlbuild.compiler.frontier.main.native_stage_enabled import native_stage_enabled +from sqlbuild.compiler.frontier.main.report_native_fallback import report_native_fallback from sqlbuild.compiler.frontier.types import NativeFallbackSite, NativeStage diff --git a/src/sqlbuild/compiler/compile/main/expand_template_data.py b/src/sqlbuild/compiler/compile/main/expand_template_data.py index ce71031cfc..ac93265bdf 100644 --- a/src/sqlbuild/compiler/compile/main/expand_template_data.py +++ b/src/sqlbuild/compiler/compile/main/expand_template_data.py @@ -5,8 +5,8 @@ from sqlbuild.compiler.compile._helpers.render.templating import ( expand_template_data as _expand_template_data, ) -from sqlbuild.compiler.frontier.main._report_native_fallback import report_native_fallback from sqlbuild.compiler.frontier.main.native_stage_enabled import native_stage_enabled +from sqlbuild.compiler.frontier.main.report_native_fallback import report_native_fallback from sqlbuild.compiler.frontier.types import NativeFallbackSite, NativeStage diff --git a/src/sqlbuild/compiler/contracts/main/promotion_conflicts.py b/src/sqlbuild/compiler/contracts/main/promotion_conflicts.py index 5e86bbab99..33edbfd775 100644 --- a/src/sqlbuild/compiler/contracts/main/promotion_conflicts.py +++ b/src/sqlbuild/compiler/contracts/main/promotion_conflicts.py @@ -9,6 +9,7 @@ native_promotion_conflict_diagnostics, ) from sqlbuild.compiler.frontier.main.native_stage_enabled import native_stage_enabled +from sqlbuild.compiler.frontier.main.report_native_answer import report_native_answer from sqlbuild.compiler.frontier.types import NativeStage @@ -25,6 +26,7 @@ def promotion_conflict_diagnostics( project=project, adapter_default=adapter_default, settings_file=settings_file ) if native is not None: + report_native_answer(stage=NativeStage.CONTRACTS, kind="promotion_checks") return native return promotion_conflict_diagnostics_impl( project=project, adapter_default=adapter_default, settings_file=settings_file diff --git a/src/sqlbuild/compiler/contracts/main/validate.py b/src/sqlbuild/compiler/contracts/main/validate.py index a3ea86bee3..a51a6dda3b 100644 --- a/src/sqlbuild/compiler/contracts/main/validate.py +++ b/src/sqlbuild/compiler/contracts/main/validate.py @@ -13,6 +13,7 @@ ) from sqlbuild.compiler.contracts.models import ContractValidationResult from sqlbuild.compiler.frontier.main.native_stage_enabled import native_stage_enabled +from sqlbuild.compiler.frontier.main.report_native_answer import report_native_answer from sqlbuild.compiler.frontier.types import NativeStage from sqlbuild.spec.contracts.types import ColumnContractMode @@ -29,6 +30,7 @@ def evaluate_model_contracts( project=project, dialect=dialect ) if native_result is not None: + report_native_answer(stage=NativeStage.CONTRACTS, kind="contract_validations") return native_result mode: ColumnContractMode = project.settings.column_contract_mode if not any(requires_contract_evaluation(model=model, mode=mode) for model in project.models): diff --git a/src/sqlbuild/compiler/discovery/_helpers/native/declaration_files.py b/src/sqlbuild/compiler/discovery/_helpers/native/declaration_files.py index 63c9348a73..b7bdf895a6 100644 --- a/src/sqlbuild/compiler/discovery/_helpers/native/declaration_files.py +++ b/src/sqlbuild/compiler/discovery/_helpers/native/declaration_files.py @@ -49,8 +49,9 @@ ModelSchemaDeclaration, ) from sqlbuild.compiler.discovery.types import NativeFileScope, NativeLocation, NativeScopeFields -from sqlbuild.compiler.frontier.main._report_native_fallback import report_native_fallback -from sqlbuild.compiler.frontier.types import NativeFallbackSite +from sqlbuild.compiler.frontier.main.report_native_answer import report_native_answer +from sqlbuild.compiler.frontier.main.report_native_fallback import report_native_fallback +from sqlbuild.compiler.frontier.types import NativeFallbackSite, NativeStage from sqlbuild.compiler.scopes.types import DeclarationKind, ScopeKind type _NativeDeclarationFile = tuple[str, NativeFileScope | None, tuple[object, ...]] @@ -190,10 +191,12 @@ def native_macro_files( def native_seed_files(*, project_dir: Path) -> tuple[DiscoveredSeedFile, ...]: """Discover the seed files natively.""" - return tuple( + seeds: tuple[DiscoveredSeedFile, ...] = tuple( DiscoveredSeedFile(file_path=project_dir / relative_path, relative_path=Path(relative_path)) for relative_path, _scope, _payload in _native_files(project_dir=project_dir, kind="seed") ) + report_native_answer(stage=NativeStage.DECLARATION_FILES, kind="seed_files", units=len(seeds)) + return seeds def retained_discovery_session(*, project_dir: Path) -> _native.NativeDiscoverySession | None: @@ -279,10 +282,12 @@ def _record[RecordT]( report_native_fallback(site=NativeFallbackSite.DECLARATION_FILE) return parse_with_python(relative_path=relative_path, scope=scope) if tag == NATIVE_PARSED_TAG: + report_native_answer(stage=NativeStage.DECLARATION_FILES, kind="parsed_files") return build( project_dir=project_dir, relative_path=relative_path, scope=scope, payload=payload ) if tag == NATIVE_FAILED_TAG: + report_native_answer(stage=NativeStage.DECLARATION_FILES, kind="failed_files") raise native_failure(payload) raise native_read_error(payload=payload, file_path=project_dir / relative_path) diff --git a/src/sqlbuild/compiler/discovery/_helpers/native/model_files.py b/src/sqlbuild/compiler/discovery/_helpers/native/model_files.py index 4c166f2493..ee78a71c88 100644 --- a/src/sqlbuild/compiler/discovery/_helpers/native/model_files.py +++ b/src/sqlbuild/compiler/discovery/_helpers/native/model_files.py @@ -28,6 +28,8 @@ ) from sqlbuild.compiler.discovery.models import DiscoveredSqlModelFile, DiscoveryFileFault from sqlbuild.compiler.discovery.types import NativeFiles, NativeLocation +from sqlbuild.compiler.frontier.main.report_native_answer import report_native_answer +from sqlbuild.compiler.frontier.types import NativeStage def discover_native_model_files( @@ -58,6 +60,7 @@ def discover_native_model_files( project_dir=project_dir, ) seed_snapshot_listings(project_dir=project_dir, tree=tree) + report_native_answer(stage=NativeStage.DISCOVERY, kind="model_file_listings") return materialise_native_files( project_dir=project_dir, files=( diff --git a/src/sqlbuild/compiler/frontier/_helpers/process_recorder.py b/src/sqlbuild/compiler/frontier/_helpers/process_recorder.py new file mode 100644 index 0000000000..37f53b4700 --- /dev/null +++ b/src/sqlbuild/compiler/frontier/_helpers/process_recorder.py @@ -0,0 +1,12 @@ +"""The process's debug recorder of native fallbacks and answers, created once.""" + +from functools import cache + +from sqlbuild.compiler.frontier.classes.native_fallback_recorder import NativeFallbackRecorder + + +@cache +def process_recorder() -> NativeFallbackRecorder | None: + """Return the recorder when the debug record directory is set, otherwise None.""" + + return NativeFallbackRecorder.from_environment() diff --git a/src/sqlbuild/compiler/frontier/classes/native_fallback_recorder.py b/src/sqlbuild/compiler/frontier/classes/native_fallback_recorder.py index 687ce91518..270c533694 100644 --- a/src/sqlbuild/compiler/frontier/classes/native_fallback_recorder.py +++ b/src/sqlbuild/compiler/frontier/classes/native_fallback_recorder.py @@ -1,4 +1,4 @@ -"""Debug-only count of the native-to-Python fallbacks one process took, written at exit.""" +"""Debug-only count of the native fallbacks and native answers of one process, written at exit.""" from __future__ import annotations @@ -14,7 +14,7 @@ class NativeFallbackRecorder: - """Count fallbacks by `(site, kind)` and write them once, when the process exits.""" + """Count fallbacks and answers by `(site, kind)` and write them once, at process exit.""" def __init__(self, *, directory: Path) -> None: self._directory: Path = directory @@ -29,11 +29,11 @@ def from_environment(cls) -> NativeFallbackRecorder | None: directory: str | None = os.environ.get(ANALYSIS_RECORD_DIR_ENV_VAR) return cls(directory=Path(directory)) if directory else None - def record(self, *, site: str, kind: str) -> None: - """Count one fallback at `site` for the reason `kind`.""" + def record(self, *, site: str, kind: str, units: int = 1) -> None: + """Count `units` fallbacks or answers at `site` of the sort `kind`.""" with self._lock: - self._counts[(site, kind)] += 1 + self._counts[(site, kind)] += units def write(self) -> None: """Write the counts so far as `{"fallbacks": [[site, kind, count], ...]}`; none, no file.""" diff --git a/src/sqlbuild/compiler/frontier/constants.py b/src/sqlbuild/compiler/frontier/constants.py index 52b2076c9e..cd1adcea0d 100644 --- a/src/sqlbuild/compiler/frontier/constants.py +++ b/src/sqlbuild/compiler/frontier/constants.py @@ -43,6 +43,7 @@ COMPILER_CACHE_DIRECTORY_NAME: str = "compiler" NATIVE_FALLBACK_RECORD_PREFIX: str = "native-fallbacks-" NATIVE_FALLBACK_DEFAULT_KIND: str = "deferred" +NATIVE_ANSWER_SITE_SUFFIX: str = ".native" STAGE_CAPTURE_DIR_ENV_VAR: str = "SQLBUILD_COMPILER_STAGE_CAPTURE_DIR" COMPILE_REUSE_DISABLE_ENV_VAR: str = "SQLBUILD_DISABLE_COMPILE_REUSE" STAGE_CAPTURE_SUFFIX: str = ".json" diff --git a/src/sqlbuild/compiler/frontier/main/report_native_answer.py b/src/sqlbuild/compiler/frontier/main/report_native_answer.py new file mode 100644 index 0000000000..4acc7b2f00 --- /dev/null +++ b/src/sqlbuild/compiler/frontier/main/report_native_answer.py @@ -0,0 +1,14 @@ +"""Count work a native stage answered itself, so a stage switched to Python is noticed.""" + +from sqlbuild.compiler.frontier._helpers.process_recorder import process_recorder +from sqlbuild.compiler.frontier.classes.native_fallback_recorder import NativeFallbackRecorder +from sqlbuild.compiler.frontier.constants import NATIVE_ANSWER_SITE_SUFFIX +from sqlbuild.compiler.frontier.types import NativeStage + + +def report_native_answer(*, stage: NativeStage, kind: str, units: int = 1) -> None: + """Record `units` of work of the sort `kind` that `stage` answered natively, when recording.""" + + recorder: NativeFallbackRecorder | None = process_recorder() + if recorder is not None and units: + recorder.record(site=f"{stage.value}{NATIVE_ANSWER_SITE_SUFFIX}", kind=kind, units=units) diff --git a/src/sqlbuild/compiler/frontier/main/_report_native_fallback.py b/src/sqlbuild/compiler/frontier/main/report_native_fallback.py similarity index 64% rename from src/sqlbuild/compiler/frontier/main/_report_native_fallback.py rename to src/sqlbuild/compiler/frontier/main/report_native_fallback.py index 51cfbec2d1..8c2f0ba101 100644 --- a/src/sqlbuild/compiler/frontier/main/_report_native_fallback.py +++ b/src/sqlbuild/compiler/frontier/main/report_native_fallback.py @@ -1,7 +1,6 @@ -"""Count one place where a shipped native stage handed its work back to Python.""" - -from functools import cache +"""Count one place where a native stage handed its work back to Python.""" +from sqlbuild.compiler.frontier._helpers.process_recorder import process_recorder from sqlbuild.compiler.frontier.classes.native_fallback_recorder import NativeFallbackRecorder from sqlbuild.compiler.frontier.constants import NATIVE_FALLBACK_DEFAULT_KIND from sqlbuild.compiler.frontier.types import NativeFallbackSite @@ -12,11 +11,6 @@ def report_native_fallback( ) -> None: """Record a fallback at `site` when the debug record directory is set; otherwise do nothing.""" - recorder: NativeFallbackRecorder | None = _process_recorder() + recorder: NativeFallbackRecorder | None = process_recorder() if recorder is not None: recorder.record(site=site.value, kind=kind) - - -@cache -def _process_recorder() -> NativeFallbackRecorder | None: - return NativeFallbackRecorder.from_environment() diff --git a/src/sqlbuild/compiler/frontier/types.py b/src/sqlbuild/compiler/frontier/types.py index c24e6de0ea..e3e38174ff 100644 --- a/src/sqlbuild/compiler/frontier/types.py +++ b/src/sqlbuild/compiler/frontier/types.py @@ -58,11 +58,13 @@ class NativeFallbackSite(StrEnum): SCOPE_RELATIONSHIP_CTES = "declaration_scopes.relationship_ctes" SQL_VARIABLES = "model_loop.sql_variables" DECLARATION_REFERENCES = "model_loop.declaration_references" + DECLARATION_CONTEXT = "model_loop.declaration_context" CURSOR_INTRINSIC_VALIDATION = "model_loop.cursor_intrinsic_validation" LINT_EXPANSION = "model_loop.lint_expansion" MACRO_CALL_SCAN = "macro_calls.scan" MACRO_CALL_RESOLUTION = "macro_calls.resolution" MACRO_CALL_SPLICE = "macro_calls.splice" + MACRO_CALL_MEMO = "macro_calls.memo" MACRO_ARGUMENTS = "macro_calls.arguments" MACRO_UNBRIDGED_EXPANSION = "macro_calls.unbridged_expansion" MACRO_BRIDGE_UNAVAILABLE = "macro_calls.bridge_unavailable" @@ -79,3 +81,6 @@ class NativeFallbackSite(StrEnum): SCENARIO_CTES = "attachments.scenario_ctes" SCENARIO_INDEPENDENCE = "attachments.scenario_independence" OMITTED_SELECT = "attachments.omitted_select" + PROJECT_ASSEMBLY = "project_assembly.assembly" + TYPE_NORMALIZATION = "type_system.normalization" + SQL_TEST_ASSEMBLY = "sql_test_glue.assembly" diff --git a/src/sqlbuild/compiler/lineage/main/columns.py b/src/sqlbuild/compiler/lineage/main/columns.py index fc44eab5bc..735a03c3d2 100644 --- a/src/sqlbuild/compiler/lineage/main/columns.py +++ b/src/sqlbuild/compiler/lineage/main/columns.py @@ -4,6 +4,7 @@ from sqlbuild.compiler.compile.models import CompiledProject from sqlbuild.compiler.frontier.main.native_stage_enabled import native_stage_enabled +from sqlbuild.compiler.frontier.main.report_native_answer import report_native_answer from sqlbuild.compiler.frontier.types import NativeStage from sqlbuild.compiler.lineage._helpers.fast_columns import ( build_fast_project_column_lineage, @@ -34,6 +35,7 @@ def build_project_column_lineage( project=project, dialect=dialect, model_names=model_names ) if native_lineage is not None: + report_native_answer(stage=NativeStage.LINEAGE_FACTS, kind="lineage_graphs") return native_lineage return build_fast_project_column_lineage( project=project, diff --git a/src/sqlbuild/compiler/macro_bridge/classes/macro_bridge.py b/src/sqlbuild/compiler/macro_bridge/classes/macro_bridge.py index cf8c115760..2ee2d9cf15 100644 --- a/src/sqlbuild/compiler/macro_bridge/classes/macro_bridge.py +++ b/src/sqlbuild/compiler/macro_bridge/classes/macro_bridge.py @@ -14,7 +14,7 @@ LoadedMacro, MacroContext, ) -from sqlbuild.compiler.frontier.main._report_native_fallback import report_native_fallback +from sqlbuild.compiler.frontier.main.report_native_fallback import report_native_fallback from sqlbuild.compiler.frontier.types import NativeFallbackSite from sqlbuild.compiler.macro_bridge._helpers.splicing import splice_text from sqlbuild.compiler.macro_bridge._helpers.store_environment import ( @@ -262,6 +262,7 @@ def record( ), ) except UnicodeEncodeError: + report_native_fallback(site=NativeFallbackSite.MACRO_CALL_MEMO, kind="unencodable") return def splice( diff --git a/src/sqlbuild/compiler/macro_bridge/main/run_with_macro_bridge.py b/src/sqlbuild/compiler/macro_bridge/main/run_with_macro_bridge.py index 3fff9a5d9f..3a38731cee 100644 --- a/src/sqlbuild/compiler/macro_bridge/main/run_with_macro_bridge.py +++ b/src/sqlbuild/compiler/macro_bridge/main/run_with_macro_bridge.py @@ -9,7 +9,7 @@ from contextvars import Token import sqlbuild._native as _native -from sqlbuild.compiler.frontier.main._report_native_fallback import report_native_fallback +from sqlbuild.compiler.frontier.main.report_native_fallback import report_native_fallback from sqlbuild.compiler.frontier.types import NativeFallbackSite from sqlbuild.compiler.macro_bridge.classes.macro_bridge import MacroBridge from sqlbuild.compiler.macro_bridge.constants import ACTIVE_MACRO_BRIDGE, STORE_MODULE_PATHS diff --git a/src/sqlbuild/compiler/model_config/main/_parse_native_header_metadata.py b/src/sqlbuild/compiler/model_config/main/_parse_native_header_metadata.py index 68c1b0302b..cc35ecbd20 100644 --- a/src/sqlbuild/compiler/model_config/main/_parse_native_header_metadata.py +++ b/src/sqlbuild/compiler/model_config/main/_parse_native_header_metadata.py @@ -9,7 +9,7 @@ import sqlbuild._native as _native from sqlbuild.compiler.auditing.types import AuditSeverity from sqlbuild.compiler.discovery.models import DiscoveredSqlModelFile -from sqlbuild.compiler.frontier.main._report_native_fallback import report_native_fallback +from sqlbuild.compiler.frontier.main.report_native_fallback import report_native_fallback from sqlbuild.compiler.frontier.types import NativeFallbackSite from sqlbuild.compiler.model_config.constants import ( AUDITS_HEADER_KEY, diff --git a/tests/e2e/scripts/compiler_differential/helpers.py b/tests/e2e/scripts/compiler_differential/helpers.py index 190f8ff012..0c39b1c9cc 100644 --- a/tests/e2e/scripts/compiler_differential/helpers.py +++ b/tests/e2e/scripts/compiler_differential/helpers.py @@ -111,6 +111,24 @@ def _perturbed(self, **changes): native.start_model_analysis_session = lambda *_args: None """ +_STAGE_DISABLE_SABOTAGE: str = """ +import sqlbuild.compiler.frontier.main.native_stage_enabled as enabled +from sqlbuild.compiler.frontier.types import NativeStage + +_original = enabled.native_stage_enabled +_disabled = {{{stages}}} +enabled.native_stage_enabled = lambda stage: stage not in _disabled and _original(stage) +""" + + +def stage_disable_sabotage(stages: tuple[str, ...]) -> str: + """A perturbation switching these native stages off, so their work runs in Python.""" + + return _STAGE_DISABLE_SABOTAGE.format( + stages=", ".join(f"NativeStage({stage!r})" for stage in stages) + ) + + _BROKEN_REF_FILES: dict[str, str] = { "sqlbuild_project.toml": ( 'name = "broken_orders"\nadapter = "duckdb"\n\n' diff --git a/tests/e2e/scripts/compiler_differential/test_native_fallback_gate.py b/tests/e2e/scripts/compiler_differential/test_native_fallback_gate.py index 88525982a8..49d72539bb 100644 --- a/tests/e2e/scripts/compiler_differential/test_native_fallback_gate.py +++ b/tests/e2e/scripts/compiler_differential/test_native_fallback_gate.py @@ -12,9 +12,25 @@ MODEL_ANALYSIS_SABOTAGE, REFERENCE_SCAN_SABOTAGE, harness_arguments, + stage_disable_sabotage, write_native_perturbation, ) +_WAFFLE_SHOP_NATIVE_STAGES: tuple[str, ...] = ( + "attachments", + "contracts", + "declaration_files", + "declaration_scopes", + "lineage_facts", + "macro_call_store", + "macro_calls", + "model_analysis", + "model_config", + "model_loop", + "reference_extraction", + "semantic_checks", +) +_ANSWER_VANISHED: str = "native no longer answers here, so this work now runs in Python" _VANISHED_ENTRY: str = ( '\n[[entry]]\nengine = "native-preview"\nstage = "reference_extraction"\n' 'site = "reference_extraction.scan"\nkind = "deferred"\ncounts = { project = 1 }\n' @@ -64,6 +80,29 @@ "Compiler differential FAILED: 0 of 1 projects differ", ), ), + NativeFallbackGateTestCase( + description="shipped_stages_switched_to_python_fail", + perturbation=stage_disable_sabotage(("reference_extraction", "declaration_files")), + appended_entries="", + expected_exit_code=1, + expected_lines=( + "Native fallback allow-list: native-preview reference_extraction " + f"reference_extraction.native reference_scans (project): {_ANSWER_VANISHED}", + "Native fallback allow-list: native-preview declaration_files " + f"declaration_files.native parsed_files (project): {_ANSWER_VANISHED}", + "Intended? Run `make compiler-baselines`", + ), + ), + NativeFallbackGateTestCase( + description="every_native_stage_switched_to_python_fails_by_name", + perturbation=stage_disable_sabotage(_WAFFLE_SHOP_NATIVE_STAGES), + appended_entries="", + expected_exit_code=1, + expected_lines=tuple( + f"Native fallback allow-list: native-preview {stage} {stage}.native " + for stage in _WAFFLE_SHOP_NATIVE_STAGES + ), + ), ], ids=lambda case: case.description, ) diff --git a/tests/goldens/compiler/failure/analysis-session-enriched-subquery-type-mismatch.json b/tests/goldens/compiler/failure/analysis-session-enriched-subquery-type-mismatch.json new file mode 100644 index 0000000000..346bfa9476 --- /dev/null +++ b/tests/goldens/compiler/failure/analysis-session-enriched-subquery-type-mismatch.json @@ -0,0 +1,306 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "contract", + "severity": "error", + "code": "K002", + "message": "column 'amount' inferred as DOUBLE but declared type is VARCHAR", + "resource_type": "model", + "resource_name": "customer_totals", + "column_name": "amount", + "path": "models/marts/customer_totals.sql", + "line": 6, + "column": 5, + "location": { + "path": "models/marts/customer_totals.sql", + "line": 6, + "column": 5, + "end_line": 6, + "end_column": 11 + }, + "related_locations": [ + { + "label": "output", + "location": { + "path": "models/marts/customer_totals.sql", + "line": 10, + "column": 21, + "end_line": 10, + "end_column": 27 + }, + "message": "inferred DOUBLE" + } + ], + "help": "change the declared type or cast the expression explicitly" + } + ] + } + ], + "compiled": { + "models/marts/customer_totals.sql": [ + "SELECT customer_id, amount", + "FROM (SELECT * FROM __ref(\"stg_orders\")) staged", + "" + ], + "models/staging/stg_orders.sql": [ + "SELECT order_id, customer_id, amount, status", + "FROM __source(\"raw_orders\")", + "" + ] + }, + "manifest": { + "nodes": { + "model.failure_corpus.customer_totals": { + "database": null, + "schema": "main", + "name": "customer_totals", + "resource_type": "model", + "package_name": "failure_corpus", + "path": "models/marts/customer_totals.sql", + "original_file_path": "models/marts/customer_totals.sql", + "unique_id": "model.failure_corpus.customer_totals", + "fqn": [ + "failure_corpus", + "models", + "marts", + "customer_totals" + ], + "alias": "customer_totals", + "config": { + "enabled": true, + "alias": null, + "schema": null, + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Order totals per customer", + "columns": { + "customer_id": { + "name": "customer_id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "amount": { + "name": "amount", + "description": "", + "meta": {}, + "data_type": "VARCHAR", + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.customer_totals", + "depends_on": { + "macros": [], + "nodes": [ + "model.failure_corpus.stg_orders" + ] + } + }, + "model.failure_corpus.stg_orders": { + "database": null, + "schema": "main", + "name": "stg_orders", + "resource_type": "model", + "package_name": "failure_corpus", + "path": "models/staging/stg_orders.sql", + "original_file_path": "models/staging/stg_orders.sql", + "unique_id": "model.failure_corpus.stg_orders", + "fqn": [ + "failure_corpus", + "models", + "staging", + "stg_orders" + ], + "alias": "stg_orders", + "config": { + "enabled": true, + "alias": null, + "schema": null, + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Staged orders", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.stg_orders", + "depends_on": { + "macros": [], + "nodes": [ + "source.failure_corpus.raw_orders" + ] + } + } + }, + "sources": { + "source.failure_corpus.raw_orders": { + "database": null, + "schema": "", + "name": "raw_orders", + "resource_type": "source", + "package_name": "failure_corpus", + "path": "sources/raw.yml", + "original_file_path": "sources/raw.yml", + "unique_id": "source.failure_corpus.raw_orders", + "fqn": [ + "failure_corpus", + "raw_orders" + ], + "source_name": "raw_orders", + "source_description": "", + "loader": "", + "identifier": "raw_orders", + "quoting": {}, + "loaded_at_field": null, + "freshness": null, + "external": null, + "description": "Orders feed.", + "columns": { + "order_id": { + "name": "order_id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "customer_id": { + "name": "customer_id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "amount": { + "name": "amount", + "description": "", + "meta": {}, + "data_type": "DOUBLE", + "constraints": [], + "quote": null, + "tags": [] + }, + "status": { + "name": "status", + "description": "", + "meta": {}, + "data_type": "VARCHAR", + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": {}, + "source_meta": {}, + "tags": [], + "config": {}, + "patch_path": null, + "unrendered_config": {}, + "relation_name": null + } + }, + "macros": {}, + "exposures": {}, + "metrics": {}, + "groups": {}, + "selectors": {}, + "disabled": {}, + "parent_map": { + "model.failure_corpus.customer_totals": [ + "model.failure_corpus.stg_orders" + ], + "model.failure_corpus.stg_orders": [ + "source.failure_corpus.raw_orders" + ], + "source.failure_corpus.raw_orders": [] + }, + "group_map": {}, + "docs": {}, + "saved_queries": {}, + "semantic_models": {}, + "unit_tests": {} + } +} diff --git a/tests/goldens/compiler/failure/analysis-session-pivot-family-column-mismatch.json b/tests/goldens/compiler/failure/analysis-session-pivot-family-column-mismatch.json new file mode 100644 index 0000000000..c4e0b17259 --- /dev/null +++ b/tests/goldens/compiler/failure/analysis-session-pivot-family-column-mismatch.json @@ -0,0 +1,367 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "contract", + "severity": "error", + "code": "K011", + "message": "model 'status_amounts' dynamic column contract is not proven: dynamic family 'status_amounts' declares pivot_column 'region' but the output pivot uses 'status'", + "resource_type": "model", + "resource_name": "status_amounts", + "path": "models/marts/status_amounts.sql", + "help": "project one supported runtime-dynamic pivot through a wildcard output boundary and declare every generated family" + } + ] + } + ], + "compiled": { + "models/marts/customer_totals.sql": [ + "SELECT customer_id, SUM(amount) AS total_amount", + "FROM __ref(\"stg_orders\")", + "GROUP BY customer_id", + "" + ], + "models/marts/status_amounts.sql": [ + "PIVOT __source(\"raw_orders\")", + "ON status", + "USING MAX(amount)", + "GROUP BY customer_id", + "" + ], + "models/staging/stg_orders.sql": [ + "SELECT order_id, customer_id, amount, status", + "FROM __source(\"raw_orders\")", + "" + ] + }, + "manifest": { + "nodes": { + "model.failure_corpus.customer_totals": { + "database": null, + "schema": "main", + "name": "customer_totals", + "resource_type": "model", + "package_name": "failure_corpus", + "path": "models/marts/customer_totals.sql", + "original_file_path": "models/marts/customer_totals.sql", + "unique_id": "model.failure_corpus.customer_totals", + "fqn": [ + "failure_corpus", + "models", + "marts", + "customer_totals" + ], + "alias": "customer_totals", + "config": { + "enabled": true, + "alias": null, + "schema": null, + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Order totals per customer", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.customer_totals", + "depends_on": { + "macros": [], + "nodes": [ + "model.failure_corpus.stg_orders" + ] + } + }, + "model.failure_corpus.status_amounts": { + "database": null, + "schema": "main", + "name": "status_amounts", + "resource_type": "model", + "package_name": "failure_corpus", + "path": "models/marts/status_amounts.sql", + "original_file_path": "models/marts/status_amounts.sql", + "unique_id": "model.failure_corpus.status_amounts", + "fqn": [ + "failure_corpus", + "models", + "marts", + "status_amounts" + ], + "alias": "status_amounts", + "config": { + "enabled": true, + "alias": null, + "schema": null, + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "table", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Order amounts pivoted by status", + "columns": { + "customer_id": { + "name": "customer_id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": { + "sqlbuild": { + "dynamic_columns": [ + { + "name": "status_amounts", + "pivot_column": "region", + "value_column": "amount", + "aggregate": "MAX", + "type": "DOUBLE", + "name_pattern": null + } + ] + } + }, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.status_amounts", + "depends_on": { + "macros": [], + "nodes": [ + "source.failure_corpus.raw_orders" + ] + } + }, + "model.failure_corpus.stg_orders": { + "database": null, + "schema": "main", + "name": "stg_orders", + "resource_type": "model", + "package_name": "failure_corpus", + "path": "models/staging/stg_orders.sql", + "original_file_path": "models/staging/stg_orders.sql", + "unique_id": "model.failure_corpus.stg_orders", + "fqn": [ + "failure_corpus", + "models", + "staging", + "stg_orders" + ], + "alias": "stg_orders", + "config": { + "enabled": true, + "alias": null, + "schema": null, + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Staged orders", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.stg_orders", + "depends_on": { + "macros": [], + "nodes": [ + "source.failure_corpus.raw_orders" + ] + } + } + }, + "sources": { + "source.failure_corpus.raw_orders": { + "database": null, + "schema": "", + "name": "raw_orders", + "resource_type": "source", + "package_name": "failure_corpus", + "path": "sources/raw.yml", + "original_file_path": "sources/raw.yml", + "unique_id": "source.failure_corpus.raw_orders", + "fqn": [ + "failure_corpus", + "raw_orders" + ], + "source_name": "raw_orders", + "source_description": "", + "loader": "", + "identifier": "raw_orders", + "quoting": {}, + "loaded_at_field": null, + "freshness": null, + "external": null, + "description": "Orders feed.", + "columns": { + "order_id": { + "name": "order_id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "customer_id": { + "name": "customer_id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "amount": { + "name": "amount", + "description": "", + "meta": {}, + "data_type": "DOUBLE", + "constraints": [], + "quote": null, + "tags": [] + }, + "status": { + "name": "status", + "description": "", + "meta": {}, + "data_type": "VARCHAR", + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": {}, + "source_meta": {}, + "tags": [], + "config": {}, + "patch_path": null, + "unrendered_config": {}, + "relation_name": null + } + }, + "macros": {}, + "exposures": {}, + "metrics": {}, + "groups": {}, + "selectors": {}, + "disabled": {}, + "parent_map": { + "model.failure_corpus.customer_totals": [ + "model.failure_corpus.stg_orders" + ], + "model.failure_corpus.status_amounts": [ + "source.failure_corpus.raw_orders" + ], + "model.failure_corpus.stg_orders": [ + "source.failure_corpus.raw_orders" + ], + "source.failure_corpus.raw_orders": [] + }, + "group_map": {}, + "docs": {}, + "saved_queries": {}, + "semantic_models": {}, + "unit_tests": {} + } +} diff --git a/tests/goldens/compiler/failure/analysis-session-pivot-values-on-clause.json b/tests/goldens/compiler/failure/analysis-session-pivot-values-on-clause.json new file mode 100644 index 0000000000..830a05d90f --- /dev/null +++ b/tests/goldens/compiler/failure/analysis-session-pivot-values-on-clause.json @@ -0,0 +1,367 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "contract", + "severity": "error", + "code": "K011", + "message": "model 'status_amounts' dynamic column contract is not proven: dynamic column contracts currently require exactly one pivot column", + "resource_type": "model", + "resource_name": "status_amounts", + "path": "models/marts/status_amounts.sql", + "help": "project one supported runtime-dynamic pivot through a wildcard output boundary and declare every generated family" + } + ] + } + ], + "compiled": { + "models/marts/customer_totals.sql": [ + "SELECT customer_id, SUM(amount) AS total_amount", + "FROM __ref(\"stg_orders\")", + "GROUP BY customer_id", + "" + ], + "models/marts/status_amounts.sql": [ + "PIVOT __source(\"raw_orders\")", + "ON status IN ('placed')", + "USING MAX(amount)", + "GROUP BY customer_id", + "" + ], + "models/staging/stg_orders.sql": [ + "SELECT order_id, customer_id, amount, status", + "FROM __source(\"raw_orders\")", + "" + ] + }, + "manifest": { + "nodes": { + "model.failure_corpus.customer_totals": { + "database": null, + "schema": "main", + "name": "customer_totals", + "resource_type": "model", + "package_name": "failure_corpus", + "path": "models/marts/customer_totals.sql", + "original_file_path": "models/marts/customer_totals.sql", + "unique_id": "model.failure_corpus.customer_totals", + "fqn": [ + "failure_corpus", + "models", + "marts", + "customer_totals" + ], + "alias": "customer_totals", + "config": { + "enabled": true, + "alias": null, + "schema": null, + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Order totals per customer", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.customer_totals", + "depends_on": { + "macros": [], + "nodes": [ + "model.failure_corpus.stg_orders" + ] + } + }, + "model.failure_corpus.status_amounts": { + "database": null, + "schema": "main", + "name": "status_amounts", + "resource_type": "model", + "package_name": "failure_corpus", + "path": "models/marts/status_amounts.sql", + "original_file_path": "models/marts/status_amounts.sql", + "unique_id": "model.failure_corpus.status_amounts", + "fqn": [ + "failure_corpus", + "models", + "marts", + "status_amounts" + ], + "alias": "status_amounts", + "config": { + "enabled": true, + "alias": null, + "schema": null, + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "table", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Order amounts pivoted by status", + "columns": { + "customer_id": { + "name": "customer_id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": { + "sqlbuild": { + "dynamic_columns": [ + { + "name": "status_amounts", + "pivot_column": "status", + "value_column": "amount", + "aggregate": "MAX", + "type": "DOUBLE", + "name_pattern": null + } + ] + } + }, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.status_amounts", + "depends_on": { + "macros": [], + "nodes": [ + "source.failure_corpus.raw_orders" + ] + } + }, + "model.failure_corpus.stg_orders": { + "database": null, + "schema": "main", + "name": "stg_orders", + "resource_type": "model", + "package_name": "failure_corpus", + "path": "models/staging/stg_orders.sql", + "original_file_path": "models/staging/stg_orders.sql", + "unique_id": "model.failure_corpus.stg_orders", + "fqn": [ + "failure_corpus", + "models", + "staging", + "stg_orders" + ], + "alias": "stg_orders", + "config": { + "enabled": true, + "alias": null, + "schema": null, + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Staged orders", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.stg_orders", + "depends_on": { + "macros": [], + "nodes": [ + "source.failure_corpus.raw_orders" + ] + } + } + }, + "sources": { + "source.failure_corpus.raw_orders": { + "database": null, + "schema": "", + "name": "raw_orders", + "resource_type": "source", + "package_name": "failure_corpus", + "path": "sources/raw.yml", + "original_file_path": "sources/raw.yml", + "unique_id": "source.failure_corpus.raw_orders", + "fqn": [ + "failure_corpus", + "raw_orders" + ], + "source_name": "raw_orders", + "source_description": "", + "loader": "", + "identifier": "raw_orders", + "quoting": {}, + "loaded_at_field": null, + "freshness": null, + "external": null, + "description": "Orders feed.", + "columns": { + "order_id": { + "name": "order_id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "customer_id": { + "name": "customer_id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "amount": { + "name": "amount", + "description": "", + "meta": {}, + "data_type": "DOUBLE", + "constraints": [], + "quote": null, + "tags": [] + }, + "status": { + "name": "status", + "description": "", + "meta": {}, + "data_type": "VARCHAR", + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": {}, + "source_meta": {}, + "tags": [], + "config": {}, + "patch_path": null, + "unrendered_config": {}, + "relation_name": null + } + }, + "macros": {}, + "exposures": {}, + "metrics": {}, + "groups": {}, + "selectors": {}, + "disabled": {}, + "parent_map": { + "model.failure_corpus.customer_totals": [ + "model.failure_corpus.stg_orders" + ], + "model.failure_corpus.status_amounts": [ + "source.failure_corpus.raw_orders" + ], + "model.failure_corpus.stg_orders": [ + "source.failure_corpus.raw_orders" + ], + "source.failure_corpus.raw_orders": [] + }, + "group_map": {}, + "docs": {}, + "saved_queries": {}, + "semantic_models": {}, + "unit_tests": {} + } +} diff --git a/tests/goldens/compiler/seed_range.toml b/tests/goldens/compiler/seed_range.toml new file mode 100644 index 0000000000..c0d6bdc52f --- /dev/null +++ b/tests/goldens/compiler/seed_range.toml @@ -0,0 +1,2 @@ +seed_start = 0 +seeds = 12 diff --git a/tests/unit/scripts/compiler_differential/_helpers/goldens/_test_types.py b/tests/unit/scripts/compiler_differential/_helpers/goldens/_test_types.py index 9a73b9bfbb..846594fefa 100644 --- a/tests/unit/scripts/compiler_differential/_helpers/goldens/_test_types.py +++ b/tests/unit/scripts/compiler_differential/_helpers/goldens/_test_types.py @@ -23,7 +23,7 @@ class GoldenPayloadTestCase: description: str run: EngineRun - masked_paths: tuple[str, ...] + masked_paths: tuple[tuple[str, str], ...] expected_payload: dict[str, object] diff --git a/tests/unit/scripts/compiler_differential/_helpers/goldens/test_compiler_goldens.py b/tests/unit/scripts/compiler_differential/_helpers/goldens/test_compiler_goldens.py index 2e761d9f32..68f158720a 100644 --- a/tests/unit/scripts/compiler_differential/_helpers/goldens/test_compiler_goldens.py +++ b/tests/unit/scripts/compiler_differential/_helpers/goldens/test_compiler_goldens.py @@ -10,6 +10,7 @@ golden_differences, golden_path, golden_payload, + write_golden_seed_range, ) from scripts.compiler_differential.models import CorpusProject, Difference, ExpectedOutcome from tests.unit.scripts.compiler_differential._helpers.goldens._test_types import ( @@ -60,7 +61,7 @@ def test_given_corpus_project_when_locating_its_golden_then_only_golden_corpora_ GoldenPayloadTestCase( description="paths_ids_timings_and_repeated_sql_masked", run=engine_run(engine="python", message=_MESSAGE, compiled_sql=_SQL), - masked_paths=(PROJECT_PATH,), + masked_paths=((PROJECT_PATH, ""),), expected_payload={ "commands": [ { @@ -142,12 +143,19 @@ def test_given_engine_run_when_building_golden_then_run_specific_noise_is_masked expected_differences=(("golden unknown-ref.json", ("golden", "hint")),), ), GoldenCheckTestCase( - description="missing_seed_golden_is_skipped", + description="missing_seed_golden_outside_the_recorded_range_is_skipped", project="seed/40", recorded=(), checked=(engine_run(engine="python", message=_MESSAGE, compiled_sql=_SQL),), expected_differences=(), ), + GoldenCheckTestCase( + description="missing_seed_golden_inside_the_recorded_range_fails", + project="seed/3-postgres", + recorded=(), + checked=(engine_run(engine="python", message=_MESSAGE, compiled_sql=_SQL),), + expected_differences=(("golden 3-postgres.json", ("golden", "hint")),), + ), ], ids=lambda case: case.description, ) @@ -157,13 +165,14 @@ def test_given_recorded_golden_when_checking_runs_then_each_differing_engine_is_ project: CorpusProject = CorpusProject( name=test_case.project, commands=(), expected=ExpectedOutcome() ) + write_golden_seed_range(golden_dir=tmp_path, seed_start=0, seeds=12) for recorded in test_case.recorded: _ = golden_differences( project=project, runs=(recorded,), golden_dir=tmp_path, mode="update", - masked_paths=(PROJECT_PATH,), + masked_paths=((PROJECT_PATH, ""),), ) differences: list[Difference] = golden_differences( @@ -171,7 +180,7 @@ def test_given_recorded_golden_when_checking_runs_then_each_differing_engine_is_ runs=test_case.checked, golden_dir=tmp_path, mode="check", - masked_paths=(PROJECT_PATH,), + masked_paths=((PROJECT_PATH, ""),), ) assert [(difference.artifact, difference.labels) for difference in differences] == list( diff --git a/tests/unit/scripts/compiler_differential/_helpers/running/_test_types.py b/tests/unit/scripts/compiler_differential/_helpers/running/_test_types.py index 00ac0da16f..e08c0665ac 100644 --- a/tests/unit/scripts/compiler_differential/_helpers/running/_test_types.py +++ b/tests/unit/scripts/compiler_differential/_helpers/running/_test_types.py @@ -67,6 +67,7 @@ class SummaryTestCase: description: str comparisons: list[ProjectComparison] missing_coverage: dict[str, tuple[str, ...]] + gate_failures: tuple[str, ...] expected_lines: tuple[str, ...] expected_absent: tuple[str, ...] diff --git a/tests/unit/scripts/compiler_differential/_helpers/running/test_native_fallbacks.py b/tests/unit/scripts/compiler_differential/_helpers/running/test_native_fallbacks.py index c1fbb0f988..c971b8b003 100644 --- a/tests/unit/scripts/compiler_differential/_helpers/running/test_native_fallbacks.py +++ b/tests/unit/scripts/compiler_differential/_helpers/running/test_native_fallbacks.py @@ -26,6 +26,16 @@ ) _VARIABLES: FallbackKey = ("native", "model_loop", "model_loop.sql_variables", "deferred") +_ANSWER: FallbackKey = ( + "native", + "reference_extraction", + "reference_extraction.native", + "reference_scans", +) +_ANSWER_ENTRY: str = ( + '\n[[entry]]\nengine = "native"\nstage = "reference_extraction"\n' + 'site = "reference_extraction.native"\nkind = "reference_scans"\n' +) _SESSION: FallbackKey = ("native-preview", "model_analysis", "analysis_session", "session") _SHIPPED_RUN: RecordedRun = RecordedRun( engines=("python", "native"), corpora=("seeds", "failures"), seed_start=0, seeds=12 @@ -135,6 +145,27 @@ def test_given_engine_records_when_observing_then_entries_are_summed_per_corpus( run=_SHIPPED_RUN, expected_problems=[], ), + FallbackGateTestCase( + description="vanished_native_answer_means_the_stage_runs_in_python", + allow_list=_LIST_HEAD + _ANSWER_ENTRY + "counts = { seed = 40 }\n", + observed={}, + run=_SHIPPED_RUN, + expected_problems=[ + "native reference_extraction reference_extraction.native reference_scans (seed): " + "native no longer answers here, so this work now runs in Python; restore the " + "native path, or record the change with a reason" + ], + ), + FallbackGateTestCase( + description="unlisted_native_answer_must_be_recorded", + allow_list=_LIST_HEAD, + observed={_ANSWER: {"seed": 40}}, + run=_SHIPPED_RUN, + expected_problems=[ + "native reference_extraction reference_extraction.native reference_scans (seed): " + "native answered 40, not on the allow-list; record it" + ], + ), FallbackGateTestCase( description="other_seed_range_is_refused", allow_list=_LIST_HEAD, diff --git a/tests/unit/scripts/compiler_differential/_helpers/running/test_report.py b/tests/unit/scripts/compiler_differential/_helpers/running/test_report.py index 16dc259015..cd1c3f8ee6 100644 --- a/tests/unit/scripts/compiler_differential/_helpers/running/test_report.py +++ b/tests/unit/scripts/compiler_differential/_helpers/running/test_report.py @@ -81,13 +81,15 @@ def test_given_covered_kinds_when_reporting_then_missing_kinds_are_named( description="identical_and_covered", comparisons=[_IDENTICAL], missing_coverage={"discovery": (), "render": ()}, + gate_failures=(), expected_lines=("Compiler differential passed: 1 projects identical",), - expected_absent=("FAILED",), + expected_absent=("FAILED", "make compiler-baselines"), ), SummaryTestCase( description="identical_but_coverage_missing", comparisons=[_IDENTICAL], missing_coverage={"discovery": ("providers", "crlf"), "render": ("path_default",)}, + gate_failures=(), expected_lines=( "Compiler differential FAILED: 0 of 1 projects differ", "Required discovery coverage missing: providers, crlf", @@ -99,11 +101,25 @@ def test_given_covered_kinds_when_reporting_then_missing_kinds_are_named( description="differences_and_coverage_missing", comparisons=[_IDENTICAL, _DIFFERING], missing_coverage={"discovery": ("providers",)}, + gate_failures=(), expected_lines=( "Compiler differential FAILED: 1 of 2 projects differ", "First difference: seed/1: `compile` stdout at /models/0", "Required discovery coverage missing: providers", ), + expected_absent=("passed", "make compiler-baselines"), + ), + SummaryTestCase( + description="gate_failure_says_how_to_record_intended_changes", + comparisons=[_IDENTICAL], + missing_coverage={}, + gate_failures=("native model_loop model_loop.native variable_substitutions (seed)",), + expected_lines=( + "Compiler differential FAILED: 0 of 1 projects differ", + "Native fallback allow-list: 1 problems (listed above)", + "Intended? Run `make compiler-baselines` and commit the changes to " + "scripts/compiler_differential/native_fallbacks.toml and tests/goldens/compiler", + ), expected_absent=("passed",), ), ], @@ -117,6 +133,7 @@ def test_given_results_and_coverage_when_summarizing_then_failures_never_read_as engines=("python", "native"), seconds=2.0, missing_coverage=test_case.missing_coverage, + gate_failures=test_case.gate_failures, ) assert all(line in summary for line in test_case.expected_lines), summary From 26f793a4d3c5f01db34c5e27bc3a5cf7bdcf3ccc Mon Sep 17 00:00:00 2001 From: Kevin Longe <273747162+kvlonge@users.noreply.github.com> Date: Fri, 9 Oct 2026 10:50:47 +0100 Subject: [PATCH 16/31] test(native): refuse vanished fallbacks without native answers and fail closed on goldens --- scripts/compiler_differential/README.md | 25 ++++++++- .../_helpers/goldens/goldens.py | 28 +++++++--- .../_helpers/running/native_fallbacks.py | 32 ++++++++++- scripts/compiler_differential/constants.py | 4 +- .../native_fallbacks.toml | 10 ++-- .../_helpers/macro_bridge/call_store.py | 2 - .../main/run_with_macro_bridge.py | 4 +- .../test_native_fallback_gate.py | 53 +++++++++++++++++- .../adapter/type_system/_test_types.py | 12 ++++ .../type_system/test_native_type_parity.py | 55 +++++++++++++++++++ .../_helpers/goldens/_test_types.py | 1 + .../_helpers/goldens/test_compiler_goldens.py | 26 ++++++++- .../_helpers/running/test_native_fallbacks.py | 40 ++++++++++++-- 13 files changed, 265 insertions(+), 27 deletions(-) diff --git a/scripts/compiler_differential/README.md b/scripts/compiler_differential/README.md index 8867510fc4..20a0a1e65e 100644 --- a/scripts/compiler_differential/README.md +++ b/scripts/compiler_differential/README.md @@ -39,10 +39,24 @@ sets. `--native-fallbacks check` runs in both make targets and fails CI in both - an entry, engine or corpus count that is not on the list fails; - a listed entry whose count changed or that no longer occurs also fails, and the message says to update or remove it, so the fallback list only shrinks; -- a vanished native answer fails as work that now runs in Python. +- a vanished native answer fails as work that now runs in Python; +- a listed fallback that vanished is refused outright, with "fallback disappeared but native + answers did not appear; the stage may be switched off", unless the same engine, stage and + corpus report more `.native` answers than the list records. A stage with only fallback + entries (`sql_test_glue`, `project_assembly`) would otherwise look like a finished port when it + is switched off. The counts hold for the make targets' corpus (`--seeds 12`); other seed ranges are refused. +Two stages the list cannot watch: +- `type_system`: the compile corpus never reaches type normalization. Its callers are the planner + and executor and the preview stages' Python fallbacks. `tests/integration/.../type_system/ + test_native_type_parity.py` proves instead that the public `normalize_type` asks native first + under `native-preview`. +- `discovery.native model_file_listings`: it is reported unconditionally inside the native model + file discovery, which has no fallback to Python, so this count cannot detect anything. It stays + only as a record that native discovery ran. + **How a PR updates the list and the goldens** One command rewrites both from the current tree: @@ -71,6 +85,9 @@ in the same PR as the change that caused it. in the PR body. Never add one just to make CI pass. - A removed or lower `.native` answer count means less native work. It needs a reason in the PR body. + - A removed fallback entry needs new or higher `.native` answers for the same stage in the + same diff: the port must report the work it now answers. A fallback that vanished with no new + answers is a stage switched off, not a port; the gate refuses it. - Code: every new path that hands work to Python calls `report_native_fallback`, and every new native path calls `report_native_answer` where its result is used. - Goldens: @@ -101,7 +118,11 @@ review. Cache facts are covered by the incremental-versus-no-cache tests instead changes on purpose, and review the diff like code. - `tests/goldens/compiler/seed_range.toml` records the seed range the goldens were written for. Every seed in that range, and every fixture, example and failure case, must have a golden. - Seeds outside the range (ad hoc runs with more seeds) are skipped. + Seeds outside the range (ad hoc runs with more seeds) are skipped. A missing `seed_range.toml` + fails the check for every seed project rather than skipping them. +- Dialect variants (`-postgres`, `-snowflake`) are generated for the first seed of a + run only, so a golden is required only for the variants of the recorded `seed_start`. An ad hoc + `--seed-start N` run checks its base seeds in the range and skips its variants. - Once the Python stages are deleted, goldens are rewritten from `native`. ### All-engine user-facing errors diff --git a/scripts/compiler_differential/_helpers/goldens/goldens.py b/scripts/compiler_differential/_helpers/goldens/goldens.py index 4bb0784b85..e5e2d3f837 100644 --- a/scripts/compiler_differential/_helpers/goldens/goldens.py +++ b/scripts/compiler_differential/_helpers/goldens/goldens.py @@ -62,9 +62,21 @@ def golden_differences( path.parent.mkdir(parents=True, exist_ok=True) _ = path.write_text(golden_text(payloads[0]), encoding="utf-8") return [] + seed_range: Path = golden_dir / GOLDEN_SEED_RANGE_FILE + if path.parent.name == GOLDEN_SEED_CORPUS and not seed_range.is_file(): + return [ + Difference( + project=project.name, + artifact=f"golden {seed_range.name}", + location="file", + left="", + right=GOLDEN_MISSING_HINT, + labels=(GOLDEN_LABEL, "hint"), + ) + ] if not path.is_file(): if path.parent.name == GOLDEN_SEED_CORPUS and not _seed_in_recorded_range( - golden_dir=golden_dir, name=path.stem + seed_range=seed_range, name=path.stem ): return [] return [ @@ -194,13 +206,15 @@ def write_golden_seed_range(*, golden_dir: Path, seed_start: int, seeds: int) -> ) -def _seed_in_recorded_range(*, golden_dir: Path, name: str) -> bool: - path: Path = golden_dir / GOLDEN_SEED_RANGE_FILE - if not path.is_file(): - return False - recorded: dict[str, object] = tomllib.loads(path.read_text(encoding="utf-8")) +def _seed_in_recorded_range(*, seed_range: Path, name: str) -> bool: + """Seeds in the recorded range need goldens; dialect variants only for its first seed.""" + + recorded: dict[str, object] = tomllib.loads(seed_range.read_text(encoding="utf-8")) start: int = int(str(recorded.get("seed_start", 0))) - index: int = int(name.split("-", 1)[0]) + seed, _, dialect = name.partition("-") + index: int = int(seed) + if dialect: + return index == start return start <= index < start + int(str(recorded.get("seeds", 0))) diff --git a/scripts/compiler_differential/_helpers/running/native_fallbacks.py b/scripts/compiler_differential/_helpers/running/native_fallbacks.py index a4e580a19f..b9d50c366f 100644 --- a/scripts/compiler_differential/_helpers/running/native_fallbacks.py +++ b/scripts/compiler_differential/_helpers/running/native_fallbacks.py @@ -114,6 +114,10 @@ def fallback_problems( f"{allowed.seeds}; this run used --seed-start {run.seed_start} --seeds {run.seeds}" ] corpora: frozenset[str] = _corpus_prefixes(run.corpora) + observed_answers: Counter[tuple[str, str, str]] = _answer_totals(observed) + allowed_answers: Counter[tuple[str, str, str]] = _answer_totals( + {**allowed.counts, **allowed.max_counts} + ) problems: list[str] = [] for key in sorted({*allowed.counts, *allowed.max_counts, *observed}): if key[0] not in run.engines: @@ -128,6 +132,8 @@ def fallback_problems( actual=actual.get(corpus, 0), exact=None if exact is None else exact.get(corpus, 0), bound=None if bound is None else bound.get(corpus, 0), + answers_rose=observed_answers[(key[0], key[1], corpus)] + > allowed_answers[(key[0], key[1], corpus)], ) if problem is not None: problems.append(problem) @@ -183,7 +189,13 @@ def write_allow_list( def _corpus_problem( - *, label: str, answer: bool, actual: int, exact: int | None, bound: int | None + *, + label: str, + answer: bool, + actual: int, + exact: int | None, + bound: int | None, + answers_rose: bool, ) -> str | None: if answer and exact is None and bound is None: return f"{label}: native answered {actual}, not on the allow-list; record it" @@ -194,6 +206,12 @@ def _corpus_problem( ) if exact is None and bound is None: return f"{label}: {actual} not on the allow-list; port it or list it with a reason" + if actual == 0 and not answers_rose: + return ( + f"{label}: fallback disappeared but native answers did not appear; the stage may be " + "switched off. A port that removes a fallback must report native answers for the " + "work it now does" + ) if actual == 0: return f"{label}: listed but no longer occurs; remove it from the allow-list" if bound is not None: @@ -203,6 +221,18 @@ def _corpus_problem( return None +def _answer_totals( + entries: dict[FallbackKey, dict[str, int]], +) -> Counter[tuple[str, str, str]]: + """Native answer counts summed by engine, stage and corpus.""" + + totals: Counter[tuple[str, str, str]] = Counter() + for (engine, stage, site, _), by_corpus in entries.items(): + for corpus, count in by_corpus.items(): + totals[(engine, stage, corpus)] += count * site.endswith(NATIVE_FALLBACK_ANSWER_SUFFIX) + return totals + + def _label(*, key: FallbackKey, corpus: str) -> str: engine, stage, site, kind = key return f"{engine} {stage} {site} {kind} ({corpus})" diff --git a/scripts/compiler_differential/constants.py b/scripts/compiler_differential/constants.py index d41561ce5d..1ee5525a4d 100644 --- a/scripts/compiler_differential/constants.py +++ b/scripts/compiler_differential/constants.py @@ -37,7 +37,9 @@ NATIVE_FALLBACK_LIST_HEADER: str = ( "# Every native-to-Python fallback and preview analysis deferral the CI corpus may still\n" "# reach, with its exact count per corpus. `--native-fallbacks check` fails on anything new,\n" - "# changed or gone. Ports delete their entries; see scripts/compiler_differential/README.md." + "# changed or gone. Ports delete their entries; see scripts/compiler_differential/README.md.\n" + "# The corpus does not reach type normalization (type_system): its callers are the planner,\n" + "# the executor and the preview fallbacks, so test_native_type_parity.py covers that stage." ) RECORDS_DIRECTORY: str = "records" GOLDEN_DIRECTORY: str = "tests/goldens/compiler" diff --git a/scripts/compiler_differential/native_fallbacks.toml b/scripts/compiler_differential/native_fallbacks.toml index 94b1ad458a..884641d18c 100644 --- a/scripts/compiler_differential/native_fallbacks.toml +++ b/scripts/compiler_differential/native_fallbacks.toml @@ -1,6 +1,8 @@ # Every native-to-Python fallback and preview analysis deferral the CI corpus may still # reach, with its exact count per corpus. `--native-fallbacks check` fails on anything new, # changed or gone. Ports delete their entries; see scripts/compiler_differential/README.md. +# The corpus does not reach type normalization (type_system): its callers are the planner, +# the executor and the preview fallbacks, so test_native_type_parity.py covers that stage. seed_start = 0 seeds = 12 @@ -113,8 +115,8 @@ counts = { example = 12, failure = 286, fixture = 12, seed = 50 } engine = "native" stage = "macro_call_store" site = "macro_call_store.native" -kind = "store_attachments" -counts = { example = 12, failure = 231, fixture = 12, seed = 47 } +kind = "served_calls" +counts = { example = 18, fixture = 15, seed = 192 } [[entry]] engine = "native" @@ -407,8 +409,8 @@ counts = { example = 9, failure = 106, fixture = 6, seed = 22 } engine = "native-preview" stage = "macro_call_store" site = "macro_call_store.native" -kind = "store_attachments" -counts = { example = 12, failure = 231, fixture = 12, seed = 47 } +kind = "served_calls" +counts = { example = 18, fixture = 15, seed = 192 } [[entry]] engine = "native-preview" diff --git a/src/sqlbuild/compiler/compile/_helpers/macro_bridge/call_store.py b/src/sqlbuild/compiler/compile/_helpers/macro_bridge/call_store.py index c9825a19c8..8e8c3238f2 100644 --- a/src/sqlbuild/compiler/compile/_helpers/macro_bridge/call_store.py +++ b/src/sqlbuild/compiler/compile/_helpers/macro_bridge/call_store.py @@ -7,7 +7,6 @@ from sqlbuild.compiler.compile._helpers.attachment.target import build_compile_target_context from sqlbuild.compiler.discovery.models import DiscoveredProjectInputs from sqlbuild.compiler.frontier.main.native_stage_enabled import native_stage_enabled -from sqlbuild.compiler.frontier.main.report_native_answer import report_native_answer from sqlbuild.compiler.frontier.types import NativeStage from sqlbuild.compiler.macro_bridge.classes.macro_bridge import MacroBridge from sqlbuild.compiler.macro_bridge.main._claim_first_compile import claim_first_compile @@ -46,7 +45,6 @@ def target_context_with_macro_call_store( project_dir=project_dir, model_paths=_model_paths(discovered_inputs=discovered_inputs, project_dir=project_dir), ) - report_native_answer(stage=NativeStage.MACRO_CALL_STORE, kind="store_attachments") return target_context diff --git a/src/sqlbuild/compiler/macro_bridge/main/run_with_macro_bridge.py b/src/sqlbuild/compiler/macro_bridge/main/run_with_macro_bridge.py index 3a38731cee..1d53c9869c 100644 --- a/src/sqlbuild/compiler/macro_bridge/main/run_with_macro_bridge.py +++ b/src/sqlbuild/compiler/macro_bridge/main/run_with_macro_bridge.py @@ -9,8 +9,9 @@ from contextvars import Token import sqlbuild._native as _native +from sqlbuild.compiler.frontier.main.report_native_answer import report_native_answer from sqlbuild.compiler.frontier.main.report_native_fallback import report_native_fallback -from sqlbuild.compiler.frontier.types import NativeFallbackSite +from sqlbuild.compiler.frontier.types import NativeFallbackSite, NativeStage from sqlbuild.compiler.macro_bridge.classes.macro_bridge import MacroBridge from sqlbuild.compiler.macro_bridge.constants import ACTIVE_MACRO_BRIDGE, STORE_MODULE_PATHS @@ -34,6 +35,7 @@ def run_with_macro_bridge[T](*, stage: Callable[[], T]) -> T: STORE_MODULE_PATHS.update(bridge.store_module_paths()) hits, misses, recorded = bridge.stats() store_hits, store_records = bridge.store_stats() + report_native_answer(stage=NativeStage.MACRO_CALL_STORE, kind="served_calls", units=store_hits) logging.getLogger(__name__).debug( "Native macro bridge: %d memo hits, %d store hits, %d misses, %d recorded calls, " "%d stored calls", diff --git a/tests/e2e/scripts/compiler_differential/test_native_fallback_gate.py b/tests/e2e/scripts/compiler_differential/test_native_fallback_gate.py index 49d72539bb..d871d0d367 100644 --- a/tests/e2e/scripts/compiler_differential/test_native_fallback_gate.py +++ b/tests/e2e/scripts/compiler_differential/test_native_fallback_gate.py @@ -31,6 +31,9 @@ "semantic_checks", ) _ANSWER_VANISHED: str = "native no longer answers here, so this work now runs in Python" +_FALLBACK_WITHOUT_ANSWERS: str = ( + "fallback disappeared but native answers did not appear; the stage may be switched off" +) _VANISHED_ENTRY: str = ( '\n[[entry]]\nengine = "native-preview"\nstage = "reference_extraction"\n' 'site = "reference_extraction.scan"\nkind = "deferred"\ncounts = { project = 1 }\n' @@ -48,14 +51,13 @@ expected_lines=("Compiler differential passed: 1 projects identical",), ), NativeFallbackGateTestCase( - description="entry_that_no_longer_occurs_fails", + description="entry_that_no_longer_occurs_without_new_native_answers_fails", perturbation="", appended_entries=_VANISHED_ENTRY, expected_exit_code=1, expected_lines=( "Native fallback allow-list: native-preview reference_extraction " - "reference_extraction.scan deferred (project): listed but no longer occurs; " - "remove it from the allow-list", + f"reference_extraction.scan deferred (project): {_FALLBACK_WITHOUT_ANSWERS}", ), ), NativeFallbackGateTestCase( @@ -142,5 +144,50 @@ def test_given_recorded_allow_list_when_native_defers_more_or_less_then_the_gate assert all(line in output for line in test_case.expected_lines), output +@pytest.mark.parametrize( + "test_case", + [ + NativeFallbackGateTestCase( + description="sql_test_glue_switched_off_is_not_a_finished_port", + perturbation=stage_disable_sabotage(("sql_test_glue",)), + appended_entries="", + expected_exit_code=1, + expected_lines=( + "Native fallback allow-list: native-preview sql_test_glue sql_test_glue.assembly " + f"deferred (fixture): {_FALLBACK_WITHOUT_ANSWERS}", + "Native fallback allow-list: native-preview sql_test_glue sql_test_glue.planning " + f"deferred (fixture): {_FALLBACK_WITHOUT_ANSWERS}", + "Native fallback allow-list: 2 problems", + ), + ), + ], + ids=lambda case: case.description, +) +def test_given_committed_allow_list_when_fallback_only_stage_is_switched_off_then_gate_refuses( + test_case: NativeFallbackGateTestCase, tmp_path: Path, capsys: pytest.CaptureFixture[str] +) -> None: + sabotage: Path = write_native_perturbation(tmp_path / "sabotage", source=test_case.perturbation) + _ = capsys.readouterr() + + exit_code: int = run_compiler_differential( + [ + "--corpus", + "fixtures", + "--jobs", + "2", + "--work-dir", + str(tmp_path / "check"), + "--native-fallbacks", + "check", + "--engine-env", + f"native-preview:PYTHONPATH={sabotage}", + ] + ) + + output: str = capsys.readouterr().out + assert exit_code == test_case.expected_exit_code, output + assert all(line in output for line in test_case.expected_lines), output + + if __name__ == "__main__": pytest.main([__file__, "-vv"]) diff --git a/tests/integration/src/sqlbuild/adapter/type_system/_test_types.py b/tests/integration/src/sqlbuild/adapter/type_system/_test_types.py index 762e82405f..1d687c65c5 100644 --- a/tests/integration/src/sqlbuild/adapter/type_system/_test_types.py +++ b/tests/integration/src/sqlbuild/adapter/type_system/_test_types.py @@ -33,3 +33,15 @@ class DeepTypeTestCase: description: str type_sql: str expected_native: None + + +@dataclass(frozen=True) +class PublicNativeTypeTestCase: + """Types normalized through the public entry point under the preview engine.""" + + description: str + engine: str + dialect: str + type_strings: tuple[str, ...] + expected_native_calls: tuple[tuple[str, str], ...] + expected_answered: int diff --git a/tests/integration/src/sqlbuild/adapter/type_system/test_native_type_parity.py b/tests/integration/src/sqlbuild/adapter/type_system/test_native_type_parity.py index eb481b27a3..93ab4d3b7c 100644 --- a/tests/integration/src/sqlbuild/adapter/type_system/test_native_type_parity.py +++ b/tests/integration/src/sqlbuild/adapter/type_system/test_native_type_parity.py @@ -3,13 +3,22 @@ from __future__ import annotations import random +from collections.abc import Callable import pytest +import sqlbuild._native as native_module +from sqlbuild.adapter.contract.models import NormalizedType +from sqlbuild.adapter.type_system._helpers.type_normalization import ( + normalize_type as cached_normalize_type, +) from sqlbuild.adapter.type_system.main._native_normalize_type import normalize_native_type +from sqlbuild.adapter.type_system.main.normalize_type import normalize_type +from sqlbuild.compiler.frontier.constants import COMPILER_ENGINE_ENV_VAR from tests.integration.src.sqlbuild.adapter.type_system._test_types import ( DeepTypeTestCase, GeneratedTypeParityTestCase, + PublicNativeTypeTestCase, TypeParityTestCase, ) from tests.integration.src.sqlbuild.adapter.type_system.helpers import ( @@ -169,5 +178,51 @@ def test_given_type_past_the_wheel_depth_when_normalizing_natively_then_it_defer ) +@pytest.mark.parametrize( + "test_case", + [ + PublicNativeTypeTestCase( + description="the public entry point asks native first under the preview engine", + engine="native-preview", + dialect="snowflake", + type_strings=("NUMBER(38, 0)", "VARCHAR", "TIMESTAMP_NTZ(9)"), + expected_native_calls=( + ("NUMBER(38, 0)", "snowflake"), + ("VARCHAR", "snowflake"), + ("TIMESTAMP_NTZ(9)", "snowflake"), + ), + expected_answered=3, + ), + ], + ids=lambda case: case.description, +) +def test_given_preview_engine_when_normalizing_publicly_then_native_answers( + test_case: PublicNativeTypeTestCase, monkeypatch: pytest.MonkeyPatch +) -> None: + calls: list[tuple[str, str]] = [] + answers: list[object] = [] + native_normalize: Callable[[str, str], object] = native_module.normalize_type + + def spy(type_sql: str, dialect: str) -> object: + calls.append((type_sql, dialect)) + answers.append(native_normalize(type_sql, dialect)) + return answers[-1] + + monkeypatch.setenv(COMPILER_ENGINE_ENV_VAR, test_case.engine) + monkeypatch.setattr(native_module, "normalize_type", spy) + cached_normalize_type.cache_clear() + normalized: list[NormalizedType] = [ + normalize_type(type_sql=type_sql, dialect=test_case.dialect) + for type_sql in test_case.type_strings + ] + cached_normalize_type.cache_clear() + + assert ( + tuple(calls), + sum(answer is not None for answer in answers), + len(normalized), + ) == (test_case.expected_native_calls, test_case.expected_answered, len(test_case.type_strings)) + + if __name__ == "__main__": pytest.main([__file__, "-vv"]) diff --git a/tests/unit/scripts/compiler_differential/_helpers/goldens/_test_types.py b/tests/unit/scripts/compiler_differential/_helpers/goldens/_test_types.py index 846594fefa..7f20ecb41a 100644 --- a/tests/unit/scripts/compiler_differential/_helpers/goldens/_test_types.py +++ b/tests/unit/scripts/compiler_differential/_helpers/goldens/_test_types.py @@ -36,3 +36,4 @@ class GoldenCheckTestCase: recorded: tuple[EngineRun, ...] checked: tuple[EngineRun, ...] expected_differences: tuple[tuple[str, tuple[str, str] | None], ...] + seed_range_dir: str = "" diff --git a/tests/unit/scripts/compiler_differential/_helpers/goldens/test_compiler_goldens.py b/tests/unit/scripts/compiler_differential/_helpers/goldens/test_compiler_goldens.py index 68f158720a..44e1fc9f37 100644 --- a/tests/unit/scripts/compiler_differential/_helpers/goldens/test_compiler_goldens.py +++ b/tests/unit/scripts/compiler_differential/_helpers/goldens/test_compiler_goldens.py @@ -151,10 +151,32 @@ def test_given_engine_run_when_building_golden_then_run_specific_noise_is_masked ), GoldenCheckTestCase( description="missing_seed_golden_inside_the_recorded_range_fails", + project="seed/3", + recorded=(), + checked=(engine_run(engine="python", message=_MESSAGE, compiled_sql=_SQL),), + expected_differences=(("golden 3.json", ("golden", "hint")),), + ), + GoldenCheckTestCase( + description="missing_dialect_variant_of_the_recorded_first_seed_fails", + project="seed/0-postgres", + recorded=(), + checked=(engine_run(engine="python", message=_MESSAGE, compiled_sql=_SQL),), + expected_differences=(("golden 0-postgres.json", ("golden", "hint")),), + ), + GoldenCheckTestCase( + description="dialect_variant_of_an_ad_hoc_first_seed_is_skipped", project="seed/3-postgres", recorded=(), checked=(engine_run(engine="python", message=_MESSAGE, compiled_sql=_SQL),), - expected_differences=(("golden 3-postgres.json", ("golden", "hint")),), + expected_differences=(), + ), + GoldenCheckTestCase( + description="missing_seed_range_fails_closed_even_with_the_seed_golden", + project="seed/3", + recorded=(engine_run(engine="python", message=_MESSAGE, compiled_sql=_SQL),), + checked=(engine_run(engine="python", message=_MESSAGE, compiled_sql=_SQL),), + expected_differences=(("golden seed_range.toml", ("golden", "hint")),), + seed_range_dir="elsewhere", ), ], ids=lambda case: case.description, @@ -165,7 +187,7 @@ def test_given_recorded_golden_when_checking_runs_then_each_differing_engine_is_ project: CorpusProject = CorpusProject( name=test_case.project, commands=(), expected=ExpectedOutcome() ) - write_golden_seed_range(golden_dir=tmp_path, seed_start=0, seeds=12) + write_golden_seed_range(golden_dir=tmp_path / test_case.seed_range_dir, seed_start=0, seeds=12) for recorded in test_case.recorded: _ = golden_differences( project=project, diff --git a/tests/unit/scripts/compiler_differential/_helpers/running/test_native_fallbacks.py b/tests/unit/scripts/compiler_differential/_helpers/running/test_native_fallbacks.py index c971b8b003..11ac5a0584 100644 --- a/tests/unit/scripts/compiler_differential/_helpers/running/test_native_fallbacks.py +++ b/tests/unit/scripts/compiler_differential/_helpers/running/test_native_fallbacks.py @@ -36,6 +36,17 @@ '\n[[entry]]\nengine = "native"\nstage = "reference_extraction"\n' 'site = "reference_extraction.native"\nkind = "reference_scans"\n' ) +_LOOP_ANSWER: FallbackKey = ("native", "model_loop", "model_loop.native", "models") +_LOOP_VARIABLE_ANSWER: FallbackKey = ( + "native", + "model_loop", + "model_loop.native", + "sql_variables", +) +_LOOP_ANSWER_ENTRY: str = ( + '\n[[entry]]\nengine = "native"\nstage = "model_loop"\n' + 'site = "model_loop.native"\nkind = "models"\n' +) _SESSION: FallbackKey = ("native-preview", "model_analysis", "analysis_session", "session") _SHIPPED_RUN: RecordedRun = RecordedRun( engines=("python", "native"), corpora=("seeds", "failures"), seed_start=0, seeds=12 @@ -105,13 +116,34 @@ def test_given_engine_records_when_observing_then_entries_are_summed_per_corpus( ], ), FallbackGateTestCase( - description="vanished_entry_fails_until_removed", - allow_list=_LIST_HEAD + _VARIABLES_ENTRY + "counts = { seed = 195 }\n", - observed={}, + description="vanished_entry_with_more_native_answers_fails_until_removed", + allow_list=_LIST_HEAD + + _VARIABLES_ENTRY + + "counts = { seed = 195 }\n" + + _LOOP_ANSWER_ENTRY + + "counts = { seed = 10 }\n", + observed={_LOOP_ANSWER: {"seed": 10}, _LOOP_VARIABLE_ANSWER: {"seed": 195}}, run=_SHIPPED_RUN, expected_problems=[ + "native model_loop model_loop.native sql_variables (seed): native answered 195, " + "not on the allow-list; record it", "native model_loop model_loop.sql_variables deferred (seed): listed but no " - "longer occurs; remove it from the allow-list" + "longer occurs; remove it from the allow-list", + ], + ), + FallbackGateTestCase( + description="vanished_entry_without_more_native_answers_means_the_stage_is_off", + allow_list=_LIST_HEAD + + _VARIABLES_ENTRY + + "counts = { seed = 195 }\n" + + _LOOP_ANSWER_ENTRY + + "counts = { seed = 10 }\n", + observed={_LOOP_ANSWER: {"seed": 10}}, + run=_SHIPPED_RUN, + expected_problems=[ + "native model_loop model_loop.sql_variables deferred (seed): fallback disappeared " + "but native answers did not appear; the stage may be switched off. A port that " + "removes a fallback must report native answers for the work it now does" ], ), FallbackGateTestCase( From 3e003c01e4434f1613729efe826c9cdbaa1b8c70 Mon Sep 17 00:00:00 2001 From: Kevin Longe <273747162+kvlonge@users.noreply.github.com> Date: Fri, 9 Oct 2026 11:41:35 +0100 Subject: [PATCH 17/31] test(native): count native SQL test planning and record baselines after the planning port --- .../native_fallbacks.toml | 76 ++--- .../_helpers/compile/target_writer.py | 4 +- .../test_native_fallback_gate.py | 4 +- .../sql-test-glue-inverted-window.json | 36 +++ .../failure/sql-test-glue-missing-mock.json | 280 ++++++++++++++++++ ...test-glue-window-without-cursor-model.json | 30 ++ 6 files changed, 389 insertions(+), 41 deletions(-) create mode 100644 tests/goldens/compiler/failure/sql-test-glue-inverted-window.json create mode 100644 tests/goldens/compiler/failure/sql-test-glue-missing-mock.json create mode 100644 tests/goldens/compiler/failure/sql-test-glue-window-without-cursor-model.json diff --git a/scripts/compiler_differential/native_fallbacks.toml b/scripts/compiler_differential/native_fallbacks.toml index 884641d18c..df94fecfbd 100644 --- a/scripts/compiler_differential/native_fallbacks.toml +++ b/scripts/compiler_differential/native_fallbacks.toml @@ -25,7 +25,7 @@ engine = "native" stage = "attachments" site = "attachments.native" kind = "cursor_intrinsic_rejections" -counts = { example = 160, failure = 164, fixture = 156, seed = 961 } +counts = { example = 160, failure = 170, fixture = 156, seed = 961 } [[entry]] engine = "native" @@ -39,7 +39,7 @@ engine = "native" stage = "attachments" site = "attachments.native" kind = "omitted_select_scans" -counts = { example = 24, failure = 18, fixture = 32, seed = 193 } +counts = { example = 24, failure = 21, fixture = 32, seed = 193 } [[entry]] engine = "native" @@ -88,7 +88,7 @@ engine = "native" stage = "declaration_scopes" site = "declaration_scopes.native" kind = "scope_indexes" -counts = { example = 12, failure = 221, fixture = 12, seed = 44 } +counts = { example = 12, failure = 224, fixture = 12, seed = 44 } [[entry]] engine = "native" @@ -109,7 +109,7 @@ engine = "native" stage = "discovery" site = "discovery.native" kind = "model_file_listings" -counts = { example = 12, failure = 286, fixture = 12, seed = 50 } +counts = { example = 12, failure = 289, fixture = 12, seed = 50 } [[entry]] engine = "native" @@ -165,14 +165,14 @@ engine = "native" stage = "model_config" site = "model_config.native" kind = "config_builds" -counts = { example = 56, failure = 298, fixture = 64, seed = 890 } +counts = { example = 56, failure = 302, fixture = 64, seed = 890 } [[entry]] engine = "native" stage = "model_config" site = "model_config.native" kind = "validations" -counts = { example = 56, failure = 365, fixture = 60, seed = 1091 } +counts = { example = 56, failure = 372, fixture = 60, seed = 1091 } [[entry]] engine = "native" @@ -186,21 +186,21 @@ engine = "native" stage = "model_config" site = "model_config.python_templates" kind = "config" -counts = { example = 12, failure = 111, fixture = 8, seed = 36 } +counts = { example = 12, failure = 114, fixture = 8, seed = 36 } [[entry]] engine = "native" stage = "model_config" site = "model_config.python_templates" kind = "connection" -counts = { example = 18, failure = 113, fixture = 9, seed = 63 } +counts = { example = 18, failure = 116, fixture = 9, seed = 63 } [[entry]] engine = "native" stage = "model_config" site = "model_config.python_templates" kind = "effective_vars" -counts = { example = 29, failure = 236, fixture = 15, seed = 121 } +counts = { example = 29, failure = 239, fixture = 15, seed = 121 } [[entry]] engine = "native" @@ -228,7 +228,7 @@ engine = "native" stage = "model_loop" site = "model_loop.cursor_intrinsic_validation" kind = "deferred" -counts = { example = 56, failure = 395, fixture = 64, seed = 1100 } +counts = { example = 56, failure = 402, fixture = 64, seed = 1100 } [[entry]] engine = "native" @@ -249,14 +249,14 @@ engine = "native" stage = "model_loop" site = "model_loop.native" kind = "declaration_expansions" -counts = { example = 56, failure = 406, fixture = 64, seed = 1100 } +counts = { example = 56, failure = 413, fixture = 64, seed = 1100 } [[entry]] engine = "native" stage = "model_loop" site = "model_loop.native" kind = "variable_substitutions" -counts = { example = 56, failure = 448, fixture = 72, seed = 1157 } +counts = { example = 56, failure = 455, fixture = 72, seed = 1157 } [[entry]] engine = "native" @@ -277,7 +277,7 @@ engine = "native" stage = "reference_extraction" site = "reference_extraction.native" kind = "reference_scans" -counts = { example = 250, failure = 448, fixture = 249, seed = 1523 } +counts = { example = 250, failure = 463, fixture = 249, seed = 1523 } [[entry]] engine = "native-preview" @@ -298,7 +298,7 @@ engine = "native-preview" stage = "attachments" site = "attachments.native" kind = "cursor_intrinsic_rejections" -counts = { example = 160, failure = 164, fixture = 156, seed = 961 } +counts = { example = 160, failure = 170, fixture = 156, seed = 961 } [[entry]] engine = "native-preview" @@ -312,7 +312,7 @@ engine = "native-preview" stage = "attachments" site = "attachments.native" kind = "omitted_select_scans" -counts = { example = 24, failure = 18, fixture = 32, seed = 193 } +counts = { example = 24, failure = 21, fixture = 32, seed = 193 } [[entry]] engine = "native-preview" @@ -340,14 +340,14 @@ engine = "native-preview" stage = "contracts" site = "contracts.native" kind = "contract_validations" -counts = { example = 9, failure = 106, fixture = 6, seed = 25 } +counts = { example = 9, failure = 109, fixture = 6, seed = 25 } [[entry]] engine = "native-preview" stage = "contracts" site = "contracts.native" kind = "promotion_checks" -counts = { example = 12, failure = 106, fixture = 8, seed = 33 } +counts = { example = 12, failure = 109, fixture = 8, seed = 33 } [[entry]] engine = "native-preview" @@ -375,7 +375,7 @@ engine = "native-preview" stage = "declaration_scopes" site = "declaration_scopes.native" kind = "scope_indexes" -counts = { example = 12, failure = 221, fixture = 12, seed = 44 } +counts = { example = 12, failure = 224, fixture = 12, seed = 44 } [[entry]] engine = "native-preview" @@ -396,14 +396,14 @@ engine = "native-preview" stage = "discovery" site = "discovery.native" kind = "model_file_listings" -counts = { example = 12, failure = 286, fixture = 12, seed = 50 } +counts = { example = 12, failure = 289, fixture = 12, seed = 50 } [[entry]] engine = "native-preview" stage = "lineage_facts" site = "lineage_facts.native" kind = "lineage_graphs" -counts = { example = 9, failure = 106, fixture = 6, seed = 22 } +counts = { example = 9, failure = 109, fixture = 6, seed = 22 } [[entry]] engine = "native-preview" @@ -466,14 +466,14 @@ engine = "native-preview" stage = "model_analysis" site = "model_analysis.native" kind = "expression_shapes" -counts = { example = 4, failure = 111, fixture = 4, seed = 100 } +counts = { example = 4, failure = 114, fixture = 4, seed = 100 } [[entry]] engine = "native-preview" stage = "model_analysis" site = "model_analysis.native" kind = "model_analyses" -counts = { example = 56, failure = 214, fixture = 52, seed = 686 } +counts = { example = 56, failure = 221, fixture = 52, seed = 686 } [[entry]] engine = "native-preview" @@ -494,14 +494,14 @@ engine = "native-preview" stage = "model_config" site = "model_config.native" kind = "config_builds" -counts = { example = 56, failure = 298, fixture = 64, seed = 890 } +counts = { example = 56, failure = 302, fixture = 64, seed = 890 } [[entry]] engine = "native-preview" stage = "model_config" site = "model_config.native" kind = "validations" -counts = { example = 56, failure = 365, fixture = 60, seed = 1091 } +counts = { example = 56, failure = 372, fixture = 60, seed = 1091 } [[entry]] engine = "native-preview" @@ -515,21 +515,21 @@ engine = "native-preview" stage = "model_config" site = "model_config.python_templates" kind = "config" -counts = { example = 12, failure = 111, fixture = 8, seed = 36 } +counts = { example = 12, failure = 114, fixture = 8, seed = 36 } [[entry]] engine = "native-preview" stage = "model_config" site = "model_config.python_templates" kind = "connection" -counts = { example = 18, failure = 113, fixture = 9, seed = 63 } +counts = { example = 18, failure = 116, fixture = 9, seed = 63 } [[entry]] engine = "native-preview" stage = "model_config" site = "model_config.python_templates" kind = "effective_vars" -counts = { example = 29, failure = 236, fixture = 15, seed = 121 } +counts = { example = 29, failure = 239, fixture = 15, seed = 121 } [[entry]] engine = "native-preview" @@ -557,7 +557,7 @@ engine = "native-preview" stage = "model_loop" site = "model_loop.cursor_intrinsic_validation" kind = "deferred" -counts = { example = 56, failure = 395, fixture = 64, seed = 1100 } +counts = { example = 56, failure = 402, fixture = 64, seed = 1100 } [[entry]] engine = "native-preview" @@ -578,14 +578,14 @@ engine = "native-preview" stage = "model_loop" site = "model_loop.native" kind = "declaration_expansions" -counts = { example = 56, failure = 406, fixture = 64, seed = 1100 } +counts = { example = 56, failure = 413, fixture = 64, seed = 1100 } [[entry]] engine = "native-preview" stage = "model_loop" site = "model_loop.native" kind = "variable_substitutions" -counts = { example = 56, failure = 448, fixture = 72, seed = 1157 } +counts = { example = 56, failure = 455, fixture = 72, seed = 1157 } [[entry]] engine = "native-preview" @@ -606,39 +606,39 @@ engine = "native-preview" stage = "project_assembly" site = "project_assembly.assembly" kind = "deferred" -counts = { example = 12, failure = 111, fixture = 8, seed = 36 } +counts = { example = 12, failure = 114, fixture = 8, seed = 36 } [[entry]] engine = "native-preview" stage = "reference_extraction" site = "reference_extraction.native" kind = "reference_scans" -counts = { example = 250, failure = 448, fixture = 249, seed = 1523 } +counts = { example = 250, failure = 463, fixture = 249, seed = 1523 } [[entry]] engine = "native-preview" stage = "semantic_checks" site = "semantic_checks.native" kind = "semantic_completions" -counts = { example = 12, failure = 108, fixture = 8, seed = 36 } +counts = { example = 12, failure = 111, fixture = 8, seed = 36 } [[entry]] engine = "native-preview" stage = "sql_test_glue" site = "sql_test_glue.assembly" kind = "deferred" -counts = { example = 12, failure = 108, fixture = 8, seed = 36 } +counts = { example = 12, failure = 111, fixture = 8, seed = 36 } [[entry]] engine = "native-preview" stage = "sql_test_glue" -site = "sql_test_glue.planning" -kind = "deferred" -counts = { example = 1, failure = 3, fixture = 1, seed = 9 } +site = "sql_test_glue.native" +kind = "sql_test_plans" +counts = { example = 1, failure = 4, fixture = 1, seed = 9 } [[entry]] engine = "python" stage = "discovery" site = "discovery.native" kind = "model_file_listings" -counts = { example = 12, failure = 286, fixture = 12, seed = 50 } +counts = { example = 12, failure = 289, fixture = 12, seed = 50 } diff --git a/src/sqlbuild/cli/commands/_helpers/compile/target_writer.py b/src/sqlbuild/cli/commands/_helpers/compile/target_writer.py index b86e310e10..7c37371978 100644 --- a/src/sqlbuild/cli/commands/_helpers/compile/target_writer.py +++ b/src/sqlbuild/cli/commands/_helpers/compile/target_writer.py @@ -577,7 +577,9 @@ def _plan_static_test_artifacts( ) as lifecycle: native_planning: bool = native_stage_enabled(NativeStage.SQL_TEST_GLUE) plan_artifacts: Callable[..., tuple[NativeSqlTestArtifact, ...]] = ( - plan_native_sql_test_artifacts if native_planning else plan_and_render_sql_test_artifacts + plan_native_sql_test_artifacts + if native_planning + else plan_and_render_sql_test_artifacts ) native_artifacts: tuple[NativeSqlTestArtifact, ...] = plan_artifacts( project=project, diff --git a/tests/e2e/scripts/compiler_differential/test_native_fallback_gate.py b/tests/e2e/scripts/compiler_differential/test_native_fallback_gate.py index d871d0d367..822f9e1a20 100644 --- a/tests/e2e/scripts/compiler_differential/test_native_fallback_gate.py +++ b/tests/e2e/scripts/compiler_differential/test_native_fallback_gate.py @@ -155,8 +155,8 @@ def test_given_recorded_allow_list_when_native_defers_more_or_less_then_the_gate expected_lines=( "Native fallback allow-list: native-preview sql_test_glue sql_test_glue.assembly " f"deferred (fixture): {_FALLBACK_WITHOUT_ANSWERS}", - "Native fallback allow-list: native-preview sql_test_glue sql_test_glue.planning " - f"deferred (fixture): {_FALLBACK_WITHOUT_ANSWERS}", + "Native fallback allow-list: native-preview sql_test_glue sql_test_glue.native " + f"sql_test_plans (fixture): {_ANSWER_VANISHED}", "Native fallback allow-list: 2 problems", ), ), diff --git a/tests/goldens/compiler/failure/sql-test-glue-inverted-window.json b/tests/goldens/compiler/failure/sql-test-glue-inverted-window.json new file mode 100644 index 0000000000..f4752ebca8 --- /dev/null +++ b/tests/goldens/compiler/failure/sql-test-glue-inverted-window.json @@ -0,0 +1,36 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "P001", + "message": "SQL test 'inverted_window' in 'tests/unit/test_customer_totals.sql': cursor_start '20' must be before the exclusive cursor_end '10' for model 'windowed_orders'" + } + ] + } + ], + "compiled": { + "models/marts/customer_totals.sql": [ + "SELECT customer_id, SUM(amount) AS total_amount", + "FROM __ref(\"stg_orders\")", + "GROUP BY customer_id", + "" + ], + "models/marts/windowed_orders.sql": [ + "SELECT order_id, amount", + "FROM __source(\"raw_orders\")", + "WHERE order_id >= __cursor_start() AND order_id < __cursor_end()", + "" + ], + "models/staging/stg_orders.sql": [ + "SELECT order_id, customer_id, amount, status", + "FROM __source(\"raw_orders\")", + "" + ] + }, + "manifest": null +} diff --git a/tests/goldens/compiler/failure/sql-test-glue-missing-mock.json b/tests/goldens/compiler/failure/sql-test-glue-missing-mock.json new file mode 100644 index 0000000000..b271f44a27 --- /dev/null +++ b/tests/goldens/compiler/failure/sql-test-glue-missing-mock.json @@ -0,0 +1,280 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "test", + "severity": "error", + "code": "S000", + "message": "test 'totals_without_source': model 'stg_orders' references __source(\"raw_orders\") which has no mock", + "resource_type": "sql_test", + "resource_name": "totals_without_source", + "path": "tests/unit/test_customer_totals.sql" + } + ] + } + ], + "compiled": { + "models/marts/customer_totals.sql": [ + "SELECT customer_id, SUM(amount) AS total_amount", + "FROM __ref(\"stg_orders\")", + "GROUP BY customer_id", + "" + ], + "models/staging/stg_orders.sql": [ + "SELECT order_id, customer_id, amount, status", + "FROM __source(\"raw_orders\")", + "" + ], + "tests/_chain_/customer_totals__stg_orders/totals_without_source.sql": [ + "WITH __ref__stg_orders AS (SELECT 10 AS customer_id, 5.0 AS amount),", + "__ref__customer_totals AS (SELECT customer_id, SUM(amount) AS total_amount", + "FROM __ref__stg_orders", + "GROUP BY customer_id),", + "__sqb_cte_0 AS (SELECT order_id, customer_id, amount, status", + "FROM __source(\"raw_orders\")),", + "__actual__customer_totals AS (SELECT * FROM __ref__customer_totals),", + "__expected__customer_totals AS (SELECT 10 AS customer_id, 5.0 AS total_amount),", + "__actual__stg_orders AS (SELECT * FROM __sqb_cte_0),", + "__expected__stg_orders AS (SELECT 10 AS customer_id, 5.0 AS amount)", + "SELECT 0 AS step_index, 'customer_totals' AS model_name, (SELECT COUNT(*) FROM __actual__customer_totals) AS actual_count, (SELECT COUNT(*) FROM __expected__customer_totals) AS expected_count, (SELECT COUNT(*) FROM (SELECT customer_id, total_amount FROM __actual__customer_totals EXCEPT SELECT customer_id, total_amount FROM __expected__customer_totals) AS __sqlbuild_mismatch) AS unexpected_count, (SELECT COUNT(*) FROM (SELECT customer_id, total_amount FROM __expected__customer_totals EXCEPT SELECT customer_id, total_amount FROM __actual__customer_totals) AS __sqlbuild_missing) AS missing_count", + "UNION ALL", + "SELECT 1 AS step_index, 'stg_orders' AS model_name, (SELECT COUNT(*) FROM __actual__stg_orders) AS actual_count, (SELECT COUNT(*) FROM __expected__stg_orders) AS expected_count, (SELECT COUNT(*) FROM (SELECT customer_id, amount FROM __actual__stg_orders EXCEPT SELECT customer_id, amount FROM __expected__stg_orders) AS __sqlbuild_mismatch) AS unexpected_count, (SELECT COUNT(*) FROM (SELECT customer_id, amount FROM __expected__stg_orders EXCEPT SELECT customer_id, amount FROM __actual__stg_orders) AS __sqlbuild_missing) AS missing_count", + "" + ] + }, + "manifest": { + "nodes": { + "model.failure_corpus.customer_totals": { + "database": null, + "schema": "main", + "name": "customer_totals", + "resource_type": "model", + "package_name": "failure_corpus", + "path": "models/marts/customer_totals.sql", + "original_file_path": "models/marts/customer_totals.sql", + "unique_id": "model.failure_corpus.customer_totals", + "fqn": [ + "failure_corpus", + "models", + "marts", + "customer_totals" + ], + "alias": "customer_totals", + "config": { + "enabled": true, + "alias": null, + "schema": null, + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Order totals per customer", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.customer_totals", + "depends_on": { + "macros": [], + "nodes": [ + "model.failure_corpus.stg_orders" + ] + } + }, + "model.failure_corpus.stg_orders": { + "database": null, + "schema": "main", + "name": "stg_orders", + "resource_type": "model", + "package_name": "failure_corpus", + "path": "models/staging/stg_orders.sql", + "original_file_path": "models/staging/stg_orders.sql", + "unique_id": "model.failure_corpus.stg_orders", + "fqn": [ + "failure_corpus", + "models", + "staging", + "stg_orders" + ], + "alias": "stg_orders", + "config": { + "enabled": true, + "alias": null, + "schema": null, + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Staged orders", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.stg_orders", + "depends_on": { + "macros": [], + "nodes": [ + "source.failure_corpus.raw_orders" + ] + } + } + }, + "sources": { + "source.failure_corpus.raw_orders": { + "database": null, + "schema": "", + "name": "raw_orders", + "resource_type": "source", + "package_name": "failure_corpus", + "path": "sources/raw.yml", + "original_file_path": "sources/raw.yml", + "unique_id": "source.failure_corpus.raw_orders", + "fqn": [ + "failure_corpus", + "raw_orders" + ], + "source_name": "raw_orders", + "source_description": "", + "loader": "", + "identifier": "raw_orders", + "quoting": {}, + "loaded_at_field": null, + "freshness": null, + "external": null, + "description": "Orders feed.", + "columns": { + "order_id": { + "name": "order_id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "customer_id": { + "name": "customer_id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "amount": { + "name": "amount", + "description": "", + "meta": {}, + "data_type": "DOUBLE", + "constraints": [], + "quote": null, + "tags": [] + }, + "status": { + "name": "status", + "description": "", + "meta": {}, + "data_type": "VARCHAR", + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": {}, + "source_meta": {}, + "tags": [], + "config": {}, + "patch_path": null, + "unrendered_config": {}, + "relation_name": null + } + }, + "macros": {}, + "exposures": {}, + "metrics": {}, + "groups": {}, + "selectors": {}, + "disabled": {}, + "parent_map": { + "model.failure_corpus.customer_totals": [ + "model.failure_corpus.stg_orders" + ], + "model.failure_corpus.stg_orders": [ + "source.failure_corpus.raw_orders" + ], + "source.failure_corpus.raw_orders": [] + }, + "group_map": {}, + "docs": {}, + "saved_queries": {}, + "semantic_models": {}, + "unit_tests": {} + } +} diff --git a/tests/goldens/compiler/failure/sql-test-glue-window-without-cursor-model.json b/tests/goldens/compiler/failure/sql-test-glue-window-without-cursor-model.json new file mode 100644 index 0000000000..2b0c5b3c1f --- /dev/null +++ b/tests/goldens/compiler/failure/sql-test-glue-window-without-cursor-model.json @@ -0,0 +1,30 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "P001", + "message": "SQL test 'plain_window' in 'tests/unit/test_customer_totals.sql' declares cursor_start or cursor_end, but no model it evaluates uses __cursor_start() or __cursor_end()" + } + ] + } + ], + "compiled": { + "models/marts/customer_totals.sql": [ + "SELECT customer_id, SUM(amount) AS total_amount", + "FROM __ref(\"stg_orders\")", + "GROUP BY customer_id", + "" + ], + "models/staging/stg_orders.sql": [ + "SELECT order_id, customer_id, amount, status", + "FROM __source(\"raw_orders\")", + "" + ] + }, + "manifest": null +} From d5c99e840f4a89a12a3aff42d79ce0dd64cc4207 Mon Sep 17 00:00:00 2001 From: Kevin Longe <273747162+kvlonge@users.noreply.github.com> Date: Fri, 9 Oct 2026 17:45:02 +0100 Subject: [PATCH 18/31] feat(native): feed semantic completion from the finished analysis session Type recovery and completion read each analysed model's output names and lineage from the finished native session instead of payload rows rebuilt from CompiledProject. Models whose analysis Python answered, failed, or whose names a dynamic pivot proof replaced keep the payload. --- .../analysis_session/_helpers/session.rs | 36 +++++++- .../main/finished_fact_models.rs | 8 ++ .../main/finished_model_facts.rs | 11 +++ .../src/assembly/analysis_session/main/mod.rs | 2 + .../src/assembly/analysis_session/models.rs | 12 ++- .../analysis_session/tests/helpers.rs | 39 +++++++-- .../tests/test_analysis_session.rs | 64 +++++++++++++- .../analysis_session/tests/test_types.rs | 7 ++ .../src/assembly/analysis_session/types.rs | 2 + .../_helpers/analysis_session/session.rs | 15 ++++ .../_helpers/semantic_checks/completion.rs | 44 +++++++--- .../bindings/_helpers/semantic_checks/mod.rs | 1 + .../_helpers/semantic_checks/session_facts.rs | 18 ++++ .../_helpers/semantic_checks/type_recovery.rs | 38 ++++++--- .../analysis_corpus/semantic_checks.py | 25 ++++++ src/sqlbuild/_native.pyi | 10 ++- .../compile/_helpers/assembly/project.py | 1 + .../compile/_helpers/diagnostics/recovery.py | 3 + .../semantic_checks/_helpers/completion.py | 33 ++++++-- .../semantic_checks/_helpers/payloads.py | 22 +++++ .../semantic_checks/_helpers/stage.py | 12 ++- .../semantic_checks/_helpers/type_recovery.py | 19 ++++- .../_complete_native_semantic_diagnostics.py | 4 + .../compiler/semantic_checks/_test_types.py | 15 ++++ .../compiler/semantic_checks/helpers.py | 58 ++++++++++++- .../test_native_semantic_checks.py | 83 +++++++++++++++++++ 26 files changed, 531 insertions(+), 51 deletions(-) create mode 100644 crates/sqlbuild-analysis/src/assembly/analysis_session/main/finished_fact_models.rs create mode 100644 crates/sqlbuild-analysis/src/assembly/analysis_session/main/finished_model_facts.rs create mode 100644 crates/sqlbuild-python/src/bindings/_helpers/semantic_checks/session_facts.rs diff --git a/crates/sqlbuild-analysis/src/assembly/analysis_session/_helpers/session.rs b/crates/sqlbuild-analysis/src/assembly/analysis_session/_helpers/session.rs index fa9ebdc21f..de54b97e79 100644 --- a/crates/sqlbuild-analysis/src/assembly/analysis_session/_helpers/session.rs +++ b/crates/sqlbuild-analysis/src/assembly/analysis_session/_helpers/session.rs @@ -24,8 +24,8 @@ use crate::assembly::analysis_session::constants::{ }; use crate::assembly::analysis_session::models::{ AnalysisSession, Awaiting, ColumnFact, Deferral, DeferredAnalysis, FinishedSession, - LineageFacts, ModelAnalysis, ModelOutcome, ModelRequest, Phase, PivotOutcome, PivotTables, - SessionOutcome, SessionRequest, SessionStep, + LineageFacts, LineageRow, ModelAnalysis, ModelOutcome, ModelRequest, Phase, PivotOutcome, + PivotTables, SessionModelFacts, SessionOutcome, SessionRequest, SessionStep, }; use crate::assembly::analysis_session::types::{Pairs, Shapes}; use crate::semantic_validation::models::ProjectCatalog; @@ -164,6 +164,12 @@ impl AnalysisSession { .ok_or("the session left a model unanalysed")?; let (schema_additions, analysis_names) = self.catalog.into_changes(); let request: SessionRequest = self.request; + let mut model_facts: HashMap = HashMap::new(); + for (model, outcome) in request.models.iter().zip(&models) { + if let Some(facts) = session_model_facts(&outcome.analysis) { + model_facts.insert(model.name.clone(), facts); + } + } let finished = FinishedSession { tables: PivotTables { dialect: request.dialect, @@ -173,6 +179,7 @@ impl AnalysisSession { families_by_table: request.dynamic_families_by_table, }, pool, + models: model_facts, }; let outcome = SessionOutcome { models, @@ -611,3 +618,28 @@ fn native_answer(model: usize, columns: Vec, has_star: bool) -> Defe binding_validated: false, } } + +/// A compiled model's output names and lineage from a successful analysis with native lineage. +fn session_model_facts(analysis: &ModelAnalysis) -> Option { + let rows: &Vec = match &analysis.lineage { + LineageFacts::Native(rows) | LineageFacts::NativeEnrichment(rows) => rows, + LineageFacts::PythonAnalysis | LineageFacts::PythonEnrichment => return None, + }; + analysis.analysis_succeeded.then(|| SessionModelFacts { + columns: analysis.columns.as_deref().map(column_names), + lineage: rows.iter().map(output_sources).collect(), + }) +} + +fn column_names(columns: &[ColumnFact]) -> Vec { + columns.iter().map(|column| column.name.clone()).collect() +} + +/// One lineage row as `(output column, [(resource name, column name)])`. +fn output_sources(row: &LineageRow) -> (String, Vec<(String, String)>) { + let mut sources: Vec<(String, String)> = Vec::with_capacity(row.sources.len()); + for (_, name, column) in &row.sources { + sources.push((name.clone(), column.clone())); + } + (row.output_column.clone(), sources) +} diff --git a/crates/sqlbuild-analysis/src/assembly/analysis_session/main/finished_fact_models.rs b/crates/sqlbuild-analysis/src/assembly/analysis_session/main/finished_fact_models.rs new file mode 100644 index 0000000000..13692c612d --- /dev/null +++ b/crates/sqlbuild-analysis/src/assembly/analysis_session/main/finished_fact_models.rs @@ -0,0 +1,8 @@ +//! The models whose output names and lineage a finished analysis session kept. + +use crate::assembly::analysis_session::models::FinishedSession; + +/// Names of the models whose facts the session kept, in no particular order. +pub fn finished_fact_models(session: &FinishedSession) -> Vec { + session.models.keys().cloned().collect() +} diff --git a/crates/sqlbuild-analysis/src/assembly/analysis_session/main/finished_model_facts.rs b/crates/sqlbuild-analysis/src/assembly/analysis_session/main/finished_model_facts.rs new file mode 100644 index 0000000000..bac994679b --- /dev/null +++ b/crates/sqlbuild-analysis/src/assembly/analysis_session/main/finished_model_facts.rs @@ -0,0 +1,11 @@ +//! The output names and lineage a finished analysis session kept for compiled models. + +use crate::assembly::analysis_session::models::{FinishedSession, SessionModelFacts}; + +/// The named model's facts, or None where the session kept none for it. +pub fn finished_model_facts<'a>( + session: &'a FinishedSession, + name: &str, +) -> Option<&'a SessionModelFacts> { + session.models.get(name) +} diff --git a/crates/sqlbuild-analysis/src/assembly/analysis_session/main/mod.rs b/crates/sqlbuild-analysis/src/assembly/analysis_session/main/mod.rs index f50a22f2ca..d3a5e4c347 100644 --- a/crates/sqlbuild-analysis/src/assembly/analysis_session/main/mod.rs +++ b/crates/sqlbuild-analysis/src/assembly/analysis_session/main/mod.rs @@ -1,5 +1,7 @@ pub mod expression_shapes; pub mod finish_analysis_session; +pub mod finished_fact_models; +pub mod finished_model_facts; pub mod prove_dynamic_contracts; pub mod prove_finished_dynamic_contracts; pub mod provide_deferred_analyses; diff --git a/crates/sqlbuild-analysis/src/assembly/analysis_session/models.rs b/crates/sqlbuild-analysis/src/assembly/analysis_session/models.rs index 565ccf3544..f5988b2b08 100644 --- a/crates/sqlbuild-analysis/src/assembly/analysis_session/models.rs +++ b/crates/sqlbuild-analysis/src/assembly/analysis_session/models.rs @@ -7,7 +7,7 @@ use rayon::ThreadPool; use crate::assembly::analysis_session::_helpers::catalog_state::SessionCatalog; use crate::assembly::analysis_session::_helpers::mappings::ShapeTable; -use crate::assembly::analysis_session::types::{Pairs, Shapes}; +use crate::assembly::analysis_session::types::{OutputSources, Pairs, Shapes}; use crate::semantic_validation::types::DiagnosticRow; /// One `ref`, `source`, `seed`, `table_fn` or `udf` call a model's SQL makes. @@ -88,11 +88,19 @@ pub struct PivotBatchRequest { pub models: Vec, } -/// A finished session's pivot tables and analysis pool, kept to prove models outside it. +/// A finished session's pivot tables, analysis pool and model facts, kept for later stages. #[derive(Debug)] pub struct FinishedSession { pub(crate) tables: PivotTables, pub(crate) pool: Result, String>, + pub(crate) models: HashMap, +} + +/// A compiled model's output names and lineage from an analysis the session completed natively. +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct SessionModelFacts { + pub columns: Option>, + pub lineage: OutputSources, } /// A model's dynamic pivot proof: none declared, left to Python, or proven natively. diff --git a/crates/sqlbuild-analysis/src/assembly/analysis_session/tests/helpers.rs b/crates/sqlbuild-analysis/src/assembly/analysis_session/tests/helpers.rs index 0970aafe27..5705bd4a1b 100644 --- a/crates/sqlbuild-analysis/src/assembly/analysis_session/tests/helpers.rs +++ b/crates/sqlbuild-analysis/src/assembly/analysis_session/tests/helpers.rs @@ -1,12 +1,15 @@ +use std::collections::HashMap; + use crate::assembly::analysis_session::_helpers::mappings::catalog_relations; use crate::assembly::analysis_session::main::finish_analysis_session::finish_analysis_session; +use crate::assembly::analysis_session::main::finished_model_facts::finished_model_facts; use crate::assembly::analysis_session::main::provide_deferred_analyses::provide_deferred_analyses; use crate::assembly::analysis_session::main::run_analysis_session::run_analysis_session; use crate::assembly::analysis_session::main::start_analysis_session::start_analysis_session; use crate::assembly::analysis_session::models::{ AnalysisSession, ColumnFact, ContractProof, Deferral, DeferredAnalysis, DynamicFamily, FinishedSession, ModelOutcome, ModelReference, ModelRequest, PivotBatchRequest, PivotModel, - PivotOutcome, PivotTables, SessionRequest, SessionStep, + PivotOutcome, PivotTables, SessionModelFacts, SessionRequest, SessionStep, }; use crate::assembly::analysis_session::tests::test_types::ModelSpec; use crate::assembly::analysis_session::types::{Pairs, Shapes}; @@ -113,11 +116,11 @@ pub(crate) fn started(request: SessionRequest) -> AnalysisSession { start_analysis_session(request, &catalog).expect("the models form no cycle") } -/// Run to completion, answering every deferral with `answer`; return the steps and outcomes. +/// Run to completion, answering every deferral with `answer`: the steps, outcomes and session. pub(crate) fn completed( mut session: AnalysisSession, answer: fn(&Deferral) -> DeferredAnalysis, -) -> (Vec, Vec) { +) -> (Vec, Vec, FinishedSession) { let mut done: bool = false; let steps: Vec = std::iter::from_fn(|| { (!done).then(|| { @@ -131,8 +134,8 @@ pub(crate) fn completed( }) }) .collect(); - let (outcome, _) = finish_analysis_session(session).expect("the session finished"); - (steps, outcome.models) + let (outcome, finished) = finish_analysis_session(session).expect("the session finished"); + (steps, outcome.models, finished) } /// `name type nullability` per column, or `failed` when analysis did not succeed. @@ -205,7 +208,7 @@ pub(crate) fn model_requests(models: &[ModelSpec]) -> Vec { /// Run `models` over the orders request: each step's lines and each model's description. pub(crate) fn session_lines(models: &[ModelSpec]) -> (Vec>, Vec>) { - let (steps, outcomes) = completed( + let (steps, outcomes, _) = completed( started(orders_request(model_requests(models))), empty_answer, ); @@ -215,6 +218,29 @@ pub(crate) fn session_lines(models: &[ModelSpec]) -> (Vec>, Vec Vec { + let (_, _, finished) = completed( + started(orders_request(model_requests(models))), + empty_answer, + ); + models + .iter() + .map(|(name, ..)| { + finished_model_facts(&finished, name).map_or("none".to_owned(), fact_line) + }) + .collect() +} + +fn fact_line(facts: &SessionModelFacts) -> String { + let lineage: Vec = facts + .lineage + .iter() + .map(|(output, sources)| format!("{output}<-{sources:?}")) + .collect(); + format!("{:?} {}", facts.columns, lineage.join(" ")) +} + /// Proofs over typed `raw_orders` and pivot `status_amounts`: the model, then a family-less one. pub(crate) fn pivot_request( dialect: &str, @@ -266,6 +292,7 @@ pub(crate) fn finished_session(tables: PivotTables) -> FinishedSession { FinishedSession { tables, pool: catalog("duckdb", &Vec::new()).analysis_pool(), + models: HashMap::new(), } } diff --git a/crates/sqlbuild-analysis/src/assembly/analysis_session/tests/test_analysis_session.rs b/crates/sqlbuild-analysis/src/assembly/analysis_session/tests/test_analysis_session.rs index ef2f2315bf..a2faa129b4 100644 --- a/crates/sqlbuild-analysis/src/assembly/analysis_session/tests/test_analysis_session.rs +++ b/crates/sqlbuild-analysis/src/assembly/analysis_session/tests/test_analysis_session.rs @@ -3,11 +3,11 @@ use crate::assembly::analysis_session::main::prove_finished_dynamic_contracts::p use crate::assembly::analysis_session::main::start_analysis_session::start_analysis_session; use crate::assembly::analysis_session::models::PivotOutcome; use crate::assembly::analysis_session::tests::helpers::{ - catalog, failed, finished_session, model_requests, orders_request, pivot_request, proven, - session_lines, + catalog, fact_lines, failed, finished_session, model_requests, orders_request, pivot_request, + proven, session_lines, }; use crate::assembly::analysis_session::tests::test_types::{ - PivotTestCase, SessionTestCase, UnscheduledTestCase, + PivotTestCase, SessionFactsTestCase, SessionTestCase, UnscheduledTestCase, }; #[test] @@ -127,6 +127,64 @@ fn given_models_when_running_the_session_then_defers_and_publishes_as_python_doe } } +#[test] +fn given_finished_sessions_when_reading_model_facts_then_keeps_only_native_successes() { + let test_cases = [ + SessionFactsTestCase { + description: "native lineage keeps output names and sources", + models: &[ + ( + "stg_orders", + "SELECT order_id, amount * 2 AS doubled FROM __source(\"raw_orders\")", + &["raw_orders"], + &[], + ), + ( + "orders_bad", + "SELECT missing_column FROM __source(\"raw_orders\")", + &["raw_orders"], + &[], + ), + ], + expected_facts: &[ + "Some([\"order_id\", \"doubled\"]) \ + order_id<-[(\"raw_orders\", \"order_id\")] \ + doubled<-[(\"raw_orders\", \"amount\")]", + "Some([\"missing_column\"]) missing_column<-[(\"raw_orders\", \"missing_column\")]", + ], + }, + SessionFactsTestCase { + description: "a model Python analysed keeps no session facts", + models: &[ + ( + "events", + "SELECT event_id FROM __source(\"raw_events\")", + &["raw_events"], + &[], + ), + ( + "events_mart", + "SELECT event_id, 1 AS one FROM __ref(\"events\")", + &[], + &["events"], + ), + ], + expected_facts: &[ + "Some([\"event_id\"]) event_id<-[(\"raw_events\", \"event_id\")]", + "none", + ], + }, + ]; + for test_case in test_cases { + assert_eq!( + fact_lines(test_case.models), + test_case.expected_facts, + "{}", + test_case.description + ); + } +} + #[test] fn given_unschedulable_models_when_starting_then_python_analyses() { let test_cases = [ diff --git a/crates/sqlbuild-analysis/src/assembly/analysis_session/tests/test_types.rs b/crates/sqlbuild-analysis/src/assembly/analysis_session/tests/test_types.rs index c09b783048..547582f4ff 100644 --- a/crates/sqlbuild-analysis/src/assembly/analysis_session/tests/test_types.rs +++ b/crates/sqlbuild-analysis/src/assembly/analysis_session/tests/test_types.rs @@ -17,6 +17,13 @@ pub(crate) struct SessionTestCase { pub(crate) expected_outcomes: &'static [&'static [&'static str]], } +pub(crate) struct SessionFactsTestCase { + pub(crate) description: &'static str, + pub(crate) models: &'static [ModelSpec], + /// `fact_lines` of each model, in request order. + pub(crate) expected_facts: &'static [&'static str], +} + pub(crate) struct UnscheduledTestCase { pub(crate) description: &'static str, pub(crate) models: &'static [ModelSpec], diff --git a/crates/sqlbuild-analysis/src/assembly/analysis_session/types.rs b/crates/sqlbuild-analysis/src/assembly/analysis_session/types.rs index 5c36906409..0dbac8f6a5 100644 --- a/crates/sqlbuild-analysis/src/assembly/analysis_session/types.rs +++ b/crates/sqlbuild-analysis/src/assembly/analysis_session/types.rs @@ -4,3 +4,5 @@ pub type Pairs = Vec<(String, String)>; /// Ordered relation shapes in Python dict order. pub type Shapes = Vec<(String, Pairs)>; +/// `(output column, [(resource name, column name)])` per output, in lineage order. +pub type OutputSources = Vec<(String, Vec<(String, String)>)>; diff --git a/crates/sqlbuild-python/src/bindings/_helpers/analysis_session/session.rs b/crates/sqlbuild-python/src/bindings/_helpers/analysis_session/session.rs index 284a8545d3..59e9dd7a86 100644 --- a/crates/sqlbuild-python/src/bindings/_helpers/analysis_session/session.rs +++ b/crates/sqlbuild-python/src/bindings/_helpers/analysis_session/session.rs @@ -4,6 +4,7 @@ use pyo3::prelude::{Bound, PyModule, PyModuleMethods, PyRef, PyResult, Python}; use pyo3::{pyclass, pyfunction, pymethods, wrap_pyfunction}; use sqlbuild_analysis::assembly::analysis_session::main::expression_shapes::expression_shapes; use sqlbuild_analysis::assembly::analysis_session::main::finish_analysis_session::finish_analysis_session; +use sqlbuild_analysis::assembly::analysis_session::main::finished_fact_models::finished_fact_models; use sqlbuild_analysis::assembly::analysis_session::main::prove_dynamic_contracts::prove_dynamic_contracts; use sqlbuild_analysis::assembly::analysis_session::main::prove_finished_dynamic_contracts::prove_finished_dynamic_contracts; use sqlbuild_analysis::assembly::analysis_session::main::provide_deferred_analyses::provide_deferred_analyses; @@ -124,6 +125,11 @@ pub(crate) struct NativeModelAnalysisSession { } impl NativeModelAnalysisSession { + /// The finished session later compile stages read, once `finish` has succeeded. + pub(crate) fn finished_session(&self) -> Option<&FinishedSession> { + self.finished.as_ref() + } + /// Keep `result`'s value, or end the session and keep its failure for Python's debug log. fn kept(&mut self, result: Result) -> Option { match result { @@ -139,6 +145,15 @@ impl NativeModelAnalysisSession { #[pymethods] impl NativeModelAnalysisSession { + /// Models whose output names and lineage the finished session keeps for later stages. + #[getter] + fn fact_models(&self) -> Vec { + self.finished + .as_ref() + .map(finished_fact_models) + .unwrap_or_default() + } + /// Advance; None means Python must analyse every model, no deferrals means done. fn run(&mut self, py: Python<'_>) -> Option { let mut session: AnalysisSession = self.inner.take()?; diff --git a/crates/sqlbuild-python/src/bindings/_helpers/semantic_checks/completion.rs b/crates/sqlbuild-python/src/bindings/_helpers/semantic_checks/completion.rs index c9ac573eaa..c01e4e67d9 100644 --- a/crates/sqlbuild-python/src/bindings/_helpers/semantic_checks/completion.rs +++ b/crates/sqlbuild-python/src/bindings/_helpers/semantic_checks/completion.rs @@ -8,7 +8,9 @@ use sqlbuild_analysis::semantic_checks::models::{ SemanticLocation, }; +use crate::bindings::_helpers::analysis_session::session::NativeModelAnalysisSession; use crate::bindings::_helpers::boundary::panics::compiler_error; +use crate::bindings::_helpers::semantic_checks::session_facts::session_model_facts; use crate::bindings::_helpers::semantic_checks::type_recovery::{LineageInput, lineage_outputs}; use crate::bindings::models::ProjectCatalog; use crate::bindings::types::CompilerDetach; @@ -33,8 +35,8 @@ type ModelInput = ( String, String, String, - Vec, - Vec, + Option>, + Option>, Vec, Option, ); @@ -66,14 +68,16 @@ type OutcomeRow = ( Vec, ); -/// Recover, explain and reject opt-outs on `catalog`'s analysis pool. +/// Recover, explain and reject opt-outs, reading facts the payload omits from `session`. #[pyfunction] +#[pyo3(signature = (catalog, request, session=None))] fn complete_semantic_checks( py: Python<'_>, catalog: PyRef<'_, ProjectCatalog>, request: RequestInput, + session: Option>, ) -> PyResult { - let request = completion_request(request); + let request = completion_request(request, session.as_deref()).map_err(compiler_error)?; let catalog = &catalog.inner; let outcome = py .compiler_detach(|| complete_semantic_diagnostics(&request, catalog)) @@ -106,13 +110,19 @@ fn location_row(location: SemanticLocation) -> LocationRow { ) } -fn completion_request((dialect, diagnostics, models, shapes): RequestInput) -> CompletionRequest { - CompletionRequest { +fn completion_request( + (dialect, diagnostics, models, shapes): RequestInput, + session: Option<&NativeModelAnalysisSession>, +) -> Result { + Ok(CompletionRequest { dialect, diagnostics: diagnostics.into_iter().map(semantic_diagnostic).collect(), - models: models.into_iter().map(completion_model).collect(), + models: models + .into_iter() + .map(|model| completion_model(model, session)) + .collect::>()?, shapes, - } + }) } fn semantic_diagnostic( @@ -134,8 +144,20 @@ fn semantic_diagnostic( fn completion_model( (name, query_sql, authored_sql, inferred_columns, lineage, binding_codes, opt_out): ModelInput, -) -> CompletionModel { - CompletionModel { + session: Option<&NativeModelAnalysisSession>, +) -> Result { + let inferred_columns: Vec = match inferred_columns { + Some(names) => names, + None => session_model_facts(session, &name)? + .columns + .clone() + .unwrap_or_default(), + }; + let lineage: Vec = match lineage { + Some(lineage) => lineage, + None => session_model_facts(session, &name)?.lineage.clone(), + }; + Ok(CompletionModel { name, query_sql, authored_sql, @@ -143,7 +165,7 @@ fn completion_model( lineage: lineage_outputs(lineage), binding_codes, rejected_opt_out_file: opt_out, - } + }) } fn completed_row(diagnostic: CompletedDiagnostic) -> CompletedRow { diff --git a/crates/sqlbuild-python/src/bindings/_helpers/semantic_checks/mod.rs b/crates/sqlbuild-python/src/bindings/_helpers/semantic_checks/mod.rs index 581ba801c2..1565c82749 100644 --- a/crates/sqlbuild-python/src/bindings/_helpers/semantic_checks/mod.rs +++ b/crates/sqlbuild-python/src/bindings/_helpers/semantic_checks/mod.rs @@ -1,4 +1,5 @@ pub(crate) mod completion; pub(crate) mod metadata; pub(crate) mod registration; +pub(crate) mod session_facts; pub(crate) mod type_recovery; diff --git a/crates/sqlbuild-python/src/bindings/_helpers/semantic_checks/session_facts.rs b/crates/sqlbuild-python/src/bindings/_helpers/semantic_checks/session_facts.rs new file mode 100644 index 0000000000..eaf244fa10 --- /dev/null +++ b/crates/sqlbuild-python/src/bindings/_helpers/semantic_checks/session_facts.rs @@ -0,0 +1,18 @@ +//! Model output names and lineage the semantic stages read from a finished analysis session. + +use sqlbuild_analysis::assembly::analysis_session::main::finished_model_facts::finished_model_facts; +use sqlbuild_analysis::assembly::analysis_session::models::SessionModelFacts; + +use crate::bindings::_helpers::analysis_session::session::NativeModelAnalysisSession; + +/// The facts the finished `session` kept for model `name`, which the payload left out. +pub(crate) fn session_model_facts<'a>( + session: Option<&'a NativeModelAnalysisSession>, + name: &str, +) -> Result<&'a SessionModelFacts, String> { + let finished = session + .and_then(NativeModelAnalysisSession::finished_session) + .ok_or("a model asked for session facts without a finished session")?; + finished_model_facts(finished, name) + .ok_or_else(|| format!("the finished session kept no facts for model {name}")) +} diff --git a/crates/sqlbuild-python/src/bindings/_helpers/semantic_checks/type_recovery.rs b/crates/sqlbuild-python/src/bindings/_helpers/semantic_checks/type_recovery.rs index 29d48a1739..801971e2a4 100644 --- a/crates/sqlbuild-python/src/bindings/_helpers/semantic_checks/type_recovery.rs +++ b/crates/sqlbuild-python/src/bindings/_helpers/semantic_checks/type_recovery.rs @@ -10,7 +10,9 @@ use sqlbuild_analysis::semantic_checks::models::{ }; use sqlbuild_analysis::semantic_checks::types::RevisedBinding; +use crate::bindings::_helpers::analysis_session::session::NativeModelAnalysisSession; use crate::bindings::_helpers::boundary::panics::compiler_error; +use crate::bindings::_helpers::semantic_checks::session_facts::session_model_facts; use crate::bindings::models::ProjectCatalog; use crate::bindings::types::CompilerDetach; @@ -22,7 +24,7 @@ type ModelInput = ( String, Option>, Vec, - Vec, + Option>, Vec<(usize, String, String, bool)>, Vec<(usize, String, String, Option, Option)>, ); @@ -84,14 +86,16 @@ impl SemanticTypeRecovery { } } -/// Plan type recovery on `catalog`'s analysis pool. +/// Plan type recovery, reading names and lineage from `session` for models with no lineage. #[pyfunction] +#[pyo3(signature = (catalog, request, session=None))] fn plan_semantic_type_recovery( py: Python<'_>, catalog: PyRef<'_, ProjectCatalog>, request: RequestInput, + session: Option>, ) -> PyResult { - let request = recovery_request(request); + let request = recovery_request(request, session.as_deref()).map_err(compiler_error)?; let catalog = &catalog.inner; let step = py .compiler_detach(|| plan_type_recovery(&request, catalog)) @@ -109,10 +113,16 @@ pub(crate) fn lineage_outputs(lineage: Vec) -> Vec .collect() } -fn recovery_request((dialect, models, diagnostics): RequestInput) -> TypeRecoveryRequest { - TypeRecoveryRequest { +fn recovery_request( + (dialect, models, diagnostics): RequestInput, + session: Option<&NativeModelAnalysisSession>, +) -> Result { + Ok(TypeRecoveryRequest { dialect, - models: models.into_iter().map(recovery_model).collect(), + models: models + .into_iter() + .map(|model| recovery_model(model, session)) + .collect::>()?, diagnostics: diagnostics .into_iter() .map(|(id, code, is_model, resource_name)| DiagnosticOwner { @@ -122,13 +132,21 @@ fn recovery_request((dialect, models, diagnostics): RequestInput) -> TypeRecover resource_name, }) .collect(), - } + }) } fn recovery_model( (name, query_sql, inferred_columns, references, lineage, bindings, raw_bindings): ModelInput, -) -> RecoveryModel { - RecoveryModel { + session: Option<&NativeModelAnalysisSession>, +) -> Result { + let (inferred_columns, lineage) = match lineage { + Some(lineage) => (inferred_columns, lineage), + None => { + let facts = session_model_facts(session, &name)?; + (facts.columns.clone(), facts.lineage.clone()) + } + }; + Ok(RecoveryModel { name, query_sql, inferred_columns, @@ -153,7 +171,7 @@ fn recovery_model( end, }) .collect(), - } + }) } pub(crate) fn register(module: &Bound<'_, PyModule>) -> PyResult<()> { diff --git a/scripts/compiler_differential/_helpers/analysis_corpus/semantic_checks.py b/scripts/compiler_differential/_helpers/analysis_corpus/semantic_checks.py index ddf0404a2c..753027e0f3 100644 --- a/scripts/compiler_differential/_helpers/analysis_corpus/semantic_checks.py +++ b/scripts/compiler_differential/_helpers/analysis_corpus/semantic_checks.py @@ -19,6 +19,13 @@ "amount, status", "amount, status, CAST('2026-01-01' AS TIMESTAMP) AS ordered_at" ) +_INTERMEDIATE_PATH: str = "models/intermediate/int_orders.sql" +_INTERMEDIATE: str = ( + 'MODEL (\n description "Doubled order amounts",\n);\n\n' + "SELECT customer_id, amount * 2 AS doubled_amount\n" + 'FROM __ref("stg_orders")\n' +) + _FUNCTION_PATH: str = "functions/sql/scaled_amount.sql" _SCALED_AMOUNT: str = ( "FUNCTION (\n" @@ -60,6 +67,24 @@ def semantic_checks_failure_cases() -> tuple[FailureCase, ...]: ), }, ), + failure_case( + name="semantic-recovered-uses-through-intermediate", + expected_code="B002", + expected_message="Unknown column 'amonut' in raw_orders", + expected_help="did you mean 'amount'?", + expected_notes=( + "raw_orders has: amount, status, customer_id, order_id", + "1 downstream uses of stg_orders.amount were not checked because of this error", + ), + files={ + **staging_files(FAILURE_BASE_STAGING.replace("amount, status", "amonut, status")), + _INTERMEDIATE_PATH: _INTERMEDIATE, + **mart_body_files( + "SELECT customer_id, SUM(doubled_amount) AS total_amount\n" + 'FROM __ref("int_orders")\nGROUP BY customer_id\n' + ), + }, + ), failure_case( name="semantic-qualified-alias-unknown-column", expected_code="B002", diff --git a/src/sqlbuild/_native.pyi b/src/sqlbuild/_native.pyi index 8bfeb38c3b..dbcdd70b6c 100644 --- a/src/sqlbuild/_native.pyi +++ b/src/sqlbuild/_native.pyi @@ -363,6 +363,8 @@ type _PivotContractRow = tuple[ ] class NativeModelAnalysisSession: + @property + def fact_models(self) -> list[str]: ... def run( self, ) -> ( @@ -481,13 +483,14 @@ def plan_semantic_type_recovery( str, list[str] | None, list[str], - list[tuple[str, list[tuple[str, str]]]], + list[tuple[str, list[tuple[str, str]]]] | None, list[tuple[int, str, str, bool]], list[tuple[int, str, str, int | None, int | None]], ] ], list[tuple[int, str, bool, str | None]], ], + session: NativeModelAnalysisSession | None = None, /, ) -> SemanticTypeRecovery: ... def check_semantic_metadata_rows( @@ -544,14 +547,15 @@ def complete_semantic_checks( str, str, str, - list[str], - list[tuple[str, list[tuple[str, str]]]], + list[str] | None, + list[tuple[str, list[tuple[str, str]]]] | None, list[str], str | None, ] ], list[tuple[str, list[tuple[str, str]]]], ], + session: NativeModelAnalysisSession | None = None, /, ) -> tuple[ str | None, diff --git a/src/sqlbuild/compiler/compile/_helpers/assembly/project.py b/src/sqlbuild/compiler/compile/_helpers/assembly/project.py index 2e925251bf..c30fde0930 100644 --- a/src/sqlbuild/compiler/compile/_helpers/assembly/project.py +++ b/src/sqlbuild/compiler/compile/_helpers/assembly/project.py @@ -503,6 +503,7 @@ def assemble_compiled_project( project=project, profile=profile, resource_sql_analysis=sql_analysis_enabled and not inputs.no_sql_validation, + native_session=native_session, binding_results={ name: analysis.polyglot_analysis.binding_diagnostics for name, analysis in model_sql_analysis_by_name.items() diff --git a/src/sqlbuild/compiler/compile/_helpers/diagnostics/recovery.py b/src/sqlbuild/compiler/compile/_helpers/diagnostics/recovery.py index 521430a59e..eb225ef54b 100644 --- a/src/sqlbuild/compiler/compile/_helpers/diagnostics/recovery.py +++ b/src/sqlbuild/compiler/compile/_helpers/diagnostics/recovery.py @@ -5,6 +5,7 @@ import re from collections import Counter from dataclasses import replace +from typing import Any from sqlbuild.adapter.contract.models import ExpressionInferenceProfile from sqlbuild.compiler.compile._helpers.assembly.metadata_validation import ( @@ -44,6 +45,7 @@ def complete_semantic_diagnostics( profile: ExpressionInferenceProfile, binding_results: dict[str, tuple[SqlBindingDiagnostic, ...]], resource_sql_analysis: bool = True, + native_session: Any | None = None, ) -> CompiledProject: """Recover type facts before metadata checks, then explain retained root diagnostics.""" if native_stage_enabled(NativeStage.SEMANTIC_CHECKS): @@ -52,6 +54,7 @@ def complete_semantic_diagnostics( profile=profile, binding_results=binding_results, resource_sql_analysis=resource_sql_analysis, + session=native_session, ) if native_project is not None: return native_project diff --git a/src/sqlbuild/compiler/semantic_checks/_helpers/completion.py b/src/sqlbuild/compiler/semantic_checks/_helpers/completion.py index a54b887900..8afb26b9d3 100644 --- a/src/sqlbuild/compiler/semantic_checks/_helpers/completion.py +++ b/src/sqlbuild/compiler/semantic_checks/_helpers/completion.py @@ -28,7 +28,11 @@ def complete_native_diagnostics( - *, project: CompiledProject, catalog: object + *, + project: CompiledProject, + catalog: object, + session: Any | None, + session_models: frozenset[str], ) -> CompiledProject | None: """Return the recovered, explained and opt-out-checked project, or None to use Python.""" @@ -40,10 +44,15 @@ def complete_native_diagnostics( request: Any = ( project.sql_analysis_dialect, [_diagnostic_payload(item=item, ids=ids) for item in project.diagnostics], - [_model_payload(model=model, lineage=recovers) for model in project.models], + [ + _model_payload(model=model, lineage=recovers, from_session=model.name in session_models) + for model in project.models + ], [(name, list(columns.items())) for name, columns in shapes.items()], ) - deferral, completed, model_bindings, order = _native.complete_semantic_checks(catalog, request) + deferral, completed, model_bindings, order = _native.complete_semantic_checks( + catalog, request, session + ) if deferral is not None: record_semantic_deferral(kind=deferral, site=COMPLETION_DEFERRAL_SITE) return None @@ -98,14 +107,14 @@ def _diagnostic_payload( ) -def _model_payload(*, model: CompiledModel, lineage: bool) -> tuple[Any, ...]: +def _model_payload(*, model: CompiledModel, lineage: bool, from_session: bool) -> tuple[Any, ...]: opt_out: SourceLocation | None = model.rejected_sql_analysis_opt_out return ( model.name, model.query_sql, model.authored_sql, - [column.name for column in model.inferred_columns or ()], - lineage_payload(model) if lineage else [], + None if from_session else [column.name for column in model.inferred_columns or ()], + _lineage(model=model, lineage=lineage, from_session=from_session), [item.code for item in model.binding_diagnostics], None if opt_out is None else opt_out.path.name, ) @@ -151,3 +160,15 @@ def _opt_out( notes=(note,), help=help_text, ) + + +def _lineage( + *, model: CompiledModel, lineage: bool, from_session: bool +) -> list[tuple[str, list[tuple[str, str]]]] | None: + """The model's lineage rows; None asks for the session's, and only recovery reads them.""" + + if not lineage: + return [] + if from_session: + return None + return lineage_payload(model) diff --git a/src/sqlbuild/compiler/semantic_checks/_helpers/payloads.py b/src/sqlbuild/compiler/semantic_checks/_helpers/payloads.py index b05811fc97..76d8e40a60 100644 --- a/src/sqlbuild/compiler/semantic_checks/_helpers/payloads.py +++ b/src/sqlbuild/compiler/semantic_checks/_helpers/payloads.py @@ -2,11 +2,14 @@ from __future__ import annotations +from typing import Any, cast + from sqlbuild.compiler.compile.models import ( CompiledLineageColumnFact, CompiledModel, CompiledProject, CompilerDiagnostic, + DynamicColumnContractProof, ) from sqlbuild.compiler.sql_analysis.models import SqlBindingDiagnostic @@ -17,6 +20,25 @@ def lineage_payload(model: CompiledModel) -> list[tuple[str, list[tuple[str, str return [_output_payload(output) for output in model.fast_lineage_columns or ()] +def session_fact_models(*, project: CompiledProject, session: Any | None) -> frozenset[str]: + """Models whose output names and lineage are the finished session's, so need no payload.""" + + if session is None: + return frozenset() + kept: frozenset[str] = frozenset(cast(list[str], session.fact_models)) + return frozenset( + model.name + for model in project.models + if model.name in kept and not _output_proven(model.dynamic_column_contract) + ) + + +def _output_proven(proof: DynamicColumnContractProof | None) -> bool: + """Whether a dynamic proof replaced the analysed output names.""" + + return proof is not None and proof.output_proven + + def _output_payload(output: CompiledLineageColumnFact) -> tuple[str, list[tuple[str, str]]]: return ( output.output_column, diff --git a/src/sqlbuild/compiler/semantic_checks/_helpers/stage.py b/src/sqlbuild/compiler/semantic_checks/_helpers/stage.py index 1468d8c319..922ca72ff2 100644 --- a/src/sqlbuild/compiler/semantic_checks/_helpers/stage.py +++ b/src/sqlbuild/compiler/semantic_checks/_helpers/stage.py @@ -3,6 +3,7 @@ from __future__ import annotations from dataclasses import replace +from typing import Any from sqlbuild.adapter.contract.models import ExpressionInferenceProfile from sqlbuild.compiler.compile.classes.semantic_completion_inputs import SemanticCompletionInputs @@ -10,6 +11,7 @@ from sqlbuild.compiler.semantic_checks._helpers.completion import complete_native_diagnostics from sqlbuild.compiler.semantic_checks._helpers.deferrals import record_semantic_deferral from sqlbuild.compiler.semantic_checks._helpers.metadata import native_metadata_diagnostics +from sqlbuild.compiler.semantic_checks._helpers.payloads import session_fact_models from sqlbuild.compiler.semantic_checks._helpers.type_recovery import recover_native_output_types from sqlbuild.compiler.semantic_checks.constants import ( COMPLETION_DEFERRAL_SITE, @@ -24,6 +26,7 @@ def completed_semantic_project( profile: ExpressionInferenceProfile, binding_results: dict[str, tuple[SqlBindingDiagnostic, ...]], resource_sql_analysis: bool, + session: Any | None, ) -> CompiledProject | None: """Python's completed project, or None where any native step hands the stage back.""" @@ -31,8 +34,13 @@ def completed_semantic_project( record_semantic_deferral(kind=NATIVE_SEMANTIC_NO_CATALOG, site=COMPLETION_DEFERRAL_SITE) return None catalog: object = project.binding_catalog.native + session_models: frozenset[str] = session_fact_models(project=project, session=session) recovered: CompiledProject | None = recover_native_output_types( - project=project, binding_results=binding_results, catalog=catalog + project=project, + binding_results=binding_results, + catalog=catalog, + session=session, + session_models=session_models, ) if recovered is None: return None @@ -49,4 +57,6 @@ def completed_semantic_project( return complete_native_diagnostics( project=replace(recovered, diagnostics=(*recovered.diagnostics, *metadata)), catalog=catalog, + session=session, + session_models=session_models, ) diff --git a/src/sqlbuild/compiler/semantic_checks/_helpers/type_recovery.py b/src/sqlbuild/compiler/semantic_checks/_helpers/type_recovery.py index c4ade28bdb..d433974d15 100644 --- a/src/sqlbuild/compiler/semantic_checks/_helpers/type_recovery.py +++ b/src/sqlbuild/compiler/semantic_checks/_helpers/type_recovery.py @@ -39,6 +39,8 @@ def recover_native_output_types( project: CompiledProject, binding_results: dict[str, tuple[SqlBindingDiagnostic, ...]], catalog: object, + session: Any | None, + session_models: frozenset[str], ) -> CompiledProject | None: """Return `recover_output_types`'s project, or None where Python must recover the types.""" @@ -49,7 +51,13 @@ def recover_native_output_types( request: Any = ( project.sql_analysis_dialect, [ - _model_payload(model=model, binding_results=binding_results, ids=ids, raw_ids=raw_ids) + _model_payload( + model=model, + binding_results=binding_results, + ids=ids, + raw_ids=raw_ids, + from_session=model.name in session_models, + ) for model in project.models ], [ @@ -62,7 +70,9 @@ def recover_native_output_types( for item in project.diagnostics ], ) - recovery: _native.SemanticTypeRecovery = _native.plan_semantic_type_recovery(catalog, request) + recovery: _native.SemanticTypeRecovery = _native.plan_semantic_type_recovery( + catalog, request, session + ) if recovery.status == NATIVE_TYPE_RECOVERY_UNCHANGED: return project if recovery.status == NATIVE_TYPE_RECOVERY_DEFERRED: @@ -120,6 +130,7 @@ def _model_payload( binding_results: dict[str, tuple[SqlBindingDiagnostic, ...]], ids: dict[CompilerDiagnostic, int], raw_ids: dict[SqlBindingDiagnostic, int], + from_session: bool, ) -> tuple[Any, ...]: raws: tuple[SqlBindingDiagnostic, ...] = ( binding_results.get(model.name, ()) if model.binding_diagnostics else () @@ -128,10 +139,10 @@ def _model_payload( model.name, model.query_sql, None - if model.inferred_columns is None + if from_session or model.inferred_columns is None else [column.name for column in model.inferred_columns], [reference.ref_name for reference in model.references], - lineage_payload(model), + None if from_session else lineage_payload(model), [(ids[item], item.code, item.message, item.is_error) for item in model.binding_diagnostics], [(raw_ids[raw], raw.code, raw.message, raw.start, raw.end) for raw in raws], ) diff --git a/src/sqlbuild/compiler/semantic_checks/main/_complete_native_semantic_diagnostics.py b/src/sqlbuild/compiler/semantic_checks/main/_complete_native_semantic_diagnostics.py index 762329e0a7..ca8f3e64ce 100644 --- a/src/sqlbuild/compiler/semantic_checks/main/_complete_native_semantic_diagnostics.py +++ b/src/sqlbuild/compiler/semantic_checks/main/_complete_native_semantic_diagnostics.py @@ -2,6 +2,8 @@ from __future__ import annotations +from typing import Any + from sqlbuild.adapter.contract.models import ExpressionInferenceProfile from sqlbuild.compiler.compile.models import CompiledProject from sqlbuild.compiler.semantic_checks._helpers.stage import completed_semantic_project @@ -14,6 +16,7 @@ def complete_native_semantic_diagnostics( profile: ExpressionInferenceProfile, binding_results: dict[str, tuple[SqlBindingDiagnostic, ...]], resource_sql_analysis: bool, + session: Any | None = None, ) -> CompiledProject | None: """Return the completed project, or None where Python must complete its diagnostics.""" @@ -22,4 +25,5 @@ def complete_native_semantic_diagnostics( profile=profile, binding_results=binding_results, resource_sql_analysis=resource_sql_analysis, + session=session, ) diff --git a/tests/integration/src/sqlbuild/compiler/semantic_checks/_test_types.py b/tests/integration/src/sqlbuild/compiler/semantic_checks/_test_types.py index 31dd072d29..10a8a6c3d3 100644 --- a/tests/integration/src/sqlbuild/compiler/semantic_checks/_test_types.py +++ b/tests/integration/src/sqlbuild/compiler/semantic_checks/_test_types.py @@ -40,3 +40,18 @@ class GeneratedMetadataParityTestCase: dialects: tuple[str, ...] expected_minimum_native: int expected_minimum_families: dict[str, int] + + +@dataclass(frozen=True) +class SessionCompletionTestCase: + """Seeded preview compiles whose completion reads analysed models from the native session.""" + + description: str + corpus: str + seed: int + count: int + model_count: int + expected_session_models: int + expected_payload_models: int + expected_proven_outputs: int + expected_minimum_codes: dict[str, int] diff --git a/tests/integration/src/sqlbuild/compiler/semantic_checks/helpers.py b/tests/integration/src/sqlbuild/compiler/semantic_checks/helpers.py index 97ed5ccb0d..8cbb77988d 100644 --- a/tests/integration/src/sqlbuild/compiler/semantic_checks/helpers.py +++ b/tests/integration/src/sqlbuild/compiler/semantic_checks/helpers.py @@ -13,6 +13,7 @@ import sqlbuild._native as native_module import sqlbuild.compiler.compile._helpers.assembly.project as assembly_project +import sqlbuild.compiler.semantic_checks._helpers.stage as semantic_stage from sqlbuild.adapter.contract.models import ExpressionInferenceProfile from sqlbuild.adapters.duckdb.classes.duckdb_adapter import DuckDbAdapter from sqlbuild.compiler.compile._helpers.assembly.metadata_validation import ( @@ -26,10 +27,14 @@ from sqlbuild.compiler.frontier.constants import COMPILER_ENGINE_ENV_VAR from sqlbuild.compiler.pipeline.main.graph import build_project_graph from sqlbuild.compiler.semantic_checks._helpers.metadata import native_metadata_diagnostics +from sqlbuild.compiler.semantic_checks._helpers.payloads import session_fact_models from sqlbuild.compiler.semantic_checks.main._complete_native_semantic_diagnostics import ( complete_native_semantic_diagnostics, ) from sqlbuild.compiler.sql_analysis.models import SqlBindingDiagnostic +from tests.integration.src.sqlbuild.compiler.analysis_session.helpers import ( + generated_analysis_files, +) _RELATIONS: dict[str, tuple[str, ...]] = { '__source("raw_orders")': ( @@ -81,6 +86,7 @@ class SemanticInputs: profile: ExpressionInferenceProfile binding_results: dict[str, tuple[SqlBindingDiagnostic, ...]] resource_sql_analysis: bool + native_session: Any | None = None def _typo(rng: random.Random, column: str) -> str: @@ -173,9 +179,13 @@ def generated_semantic_files( def captured_semantic_inputs( - *, project_dir: Path, files: dict[str, str], monkeypatch: pytest.MonkeyPatch + *, + project_dir: Path, + files: dict[str, str], + monkeypatch: pytest.MonkeyPatch, + engine: str = "python", ) -> SemanticInputs: - """Compile with the Python engine and keep the semantic completion stage's inputs.""" + """Compile with `engine` and keep the semantic completion stage's inputs.""" for relative_path, contents in files.items(): path: Path = project_dir / relative_path @@ -188,7 +198,7 @@ def capture(**arguments: Any) -> CompiledProject: return complete_semantic_diagnostics(**arguments) with monkeypatch.context() as patch: - patch.setenv(COMPILER_ENGINE_ENV_VAR, "python") + patch.setenv(COMPILER_ENGINE_ENV_VAR, engine) patch.setattr(assembly_project, "complete_semantic_diagnostics", capture) _ = build_project_graph( discovered_inputs=discover_project_inputs(project_dir=project_dir), @@ -248,9 +258,51 @@ def native_completion(inputs: SemanticInputs) -> CompiledProject | None: profile=inputs.profile, binding_results=inputs.binding_results, resource_sql_analysis=inputs.resource_sql_analysis, + session=inputs.native_session, ) +def session_corpus_files(*, corpus: str, rng: random.Random, model_count: int) -> dict[str, str]: + """One generated project from the failing-semantics or the analysis-session corpus.""" + + generators: dict[str, Callable[[], dict[str, str]]] = { + "semantic": lambda: generated_semantic_files( + rng=rng, model_count=model_count, require_analysis=False + ), + "analysis": lambda: generated_analysis_files(rng=rng, model_count=model_count), + } + return generators[corpus]() + + +def proven_output_count(project: CompiledProject) -> int: + """How many models a dynamic pivot proof gave their output names.""" + + return sum( + model.dynamic_column_contract is not None and model.dynamic_column_contract.output_proven + for model in project.models + ) + + +def without_session(inputs: SemanticInputs) -> SemanticInputs: + """The same stage inputs with every model's output names and lineage in the payload.""" + + return replace(inputs, native_session=None) + + +def record_session_models(*, monkeypatch: pytest.MonkeyPatch) -> list[frozenset[str]]: + """Keep each completion's models whose output names and lineage the session supplies.""" + + selections: list[frozenset[str]] = [] + + def recorded(**arguments: Any) -> frozenset[str]: + selected: frozenset[str] = session_fact_models(**arguments) + selections.append(selected) + return selected + + monkeypatch.setattr(semantic_stage, "session_fact_models", recorded) + return selections + + def completion_view(project: CompiledProject) -> tuple[object, ...]: """Everything the stage decides: diagnostics and each model's binding and type facts.""" diff --git a/tests/integration/src/sqlbuild/compiler/semantic_checks/test_native_semantic_checks.py b/tests/integration/src/sqlbuild/compiler/semantic_checks/test_native_semantic_checks.py index 96d98e0bbb..b597aa098c 100644 --- a/tests/integration/src/sqlbuild/compiler/semantic_checks/test_native_semantic_checks.py +++ b/tests/integration/src/sqlbuild/compiler/semantic_checks/test_native_semantic_checks.py @@ -14,6 +14,7 @@ from tests.integration.src.sqlbuild.compiler.semantic_checks._test_types import ( DeferredSemanticTestCase, GeneratedSemanticParityTestCase, + SessionCompletionTestCase, ) from tests.integration.src.sqlbuild.compiler.semantic_checks.helpers import ( SemanticInputs, @@ -24,9 +25,13 @@ generated_semantic_files, native_completion, note_kinds, + proven_output_count, python_completion, record_native_statuses, + record_session_models, + session_corpus_files, with_dialect, + without_session, ) _NOTE_KINDS: tuple[str, ...] = ("downstream output uses", "downstream uses of", " has: ") @@ -154,5 +159,83 @@ def test_given_deferred_projects_when_completing_then_python_completes_and_recor assert deferral_records(record_dir) == test_case.expected_kinds +@pytest.mark.parametrize( + "test_case", + [ + SessionCompletionTestCase( + description="failing semantics corpus", + corpus="semantic", + seed=20261010, + count=6, + model_count=12, + expected_session_models=44, + expected_payload_models=28, + expected_proven_outputs=0, + expected_minimum_codes={"B002": 1, "downstream output uses": 1}, + ), + SessionCompletionTestCase( + description="analysis session corpus with pivots, stars and set operations", + corpus="analysis", + seed=20261011, + count=4, + model_count=16, + expected_session_models=67, + expected_payload_models=29, + expected_proven_outputs=11, + expected_minimum_codes={"B002": 1}, + ), + ], + ids=lambda case: case.description, +) +def test_given_preview_compiles_when_completing_from_the_session_then_matches_payload_and_python( + test_case: SessionCompletionTestCase, + tmp_path: Path, + monkeypatch: pytest.MonkeyPatch, +) -> None: + rng: random.Random = random.Random(test_case.seed) + captured: list[SemanticInputs] = [ + captured_semantic_inputs( + project_dir=tmp_path / f"project_{index}", + files=session_corpus_files( + corpus=test_case.corpus, rng=rng, model_count=test_case.model_count + ), + monkeypatch=monkeypatch, + engine="native-preview", + ) + for index in range(test_case.count) + ] + selections: list[frozenset[str]] = record_session_models(monkeypatch=monkeypatch) + codes: Counter[str] = Counter() + python_views: list[tuple[object, ...]] = [] + payload_views: list[tuple[object, ...]] = [] + session_views: list[tuple[object, ...]] = [] + for inputs in captured: + python: CompiledProject = python_completion(inputs=inputs, monkeypatch=monkeypatch) + codes.update(item.code for item in python.diagnostics) + codes.update(note_kinds(python, _NOTE_KINDS)) + python_views.append(completion_view(python)) + payload_views.append(completion_view(completed_natively(without_session(inputs)))) + session_views.append(completion_view(completed_natively(inputs))) + session_models: int = sum(len(selected) for selected in selections) + model_count: int = sum(len(inputs.project.models) for inputs in captured) + + assert all(inputs.native_session is not None for inputs in captured) + assert session_views == python_views + assert payload_views == python_views + assert ( + session_models, + model_count - session_models, + sum(proven_output_count(inputs.project) for inputs in captured), + ) == ( + test_case.expected_session_models, + test_case.expected_payload_models, + test_case.expected_proven_outputs, + ) + assert { + code: min(codes[code], minimum) + for code, minimum in test_case.expected_minimum_codes.items() + } == test_case.expected_minimum_codes + + if __name__ == "__main__": pytest.main([__file__, "-vv"]) From 78f5d9099b4d7dfca7d5149ba34c2f60bec16a79 Mon Sep 17 00:00:00 2001 From: Kevin Longe <273747162+kvlonge@users.noreply.github.com> Date: Fri, 9 Oct 2026 18:00:52 +0100 Subject: [PATCH 19/31] test(native): record baselines after native project assembly --- .../native_fallbacks.toml | 91 ++++++++++--------- .../assembly-hook-syntax-unbuilt-dialect.json | 19 ++++ 2 files changed, 68 insertions(+), 42 deletions(-) create mode 100644 tests/goldens/compiler/failure/assembly-hook-syntax-unbuilt-dialect.json diff --git a/scripts/compiler_differential/native_fallbacks.toml b/scripts/compiler_differential/native_fallbacks.toml index df94fecfbd..db49cefc10 100644 --- a/scripts/compiler_differential/native_fallbacks.toml +++ b/scripts/compiler_differential/native_fallbacks.toml @@ -25,7 +25,7 @@ engine = "native" stage = "attachments" site = "attachments.native" kind = "cursor_intrinsic_rejections" -counts = { example = 160, failure = 170, fixture = 156, seed = 961 } +counts = { example = 160, failure = 172, fixture = 156, seed = 961 } [[entry]] engine = "native" @@ -88,7 +88,7 @@ engine = "native" stage = "declaration_scopes" site = "declaration_scopes.native" kind = "scope_indexes" -counts = { example = 12, failure = 224, fixture = 12, seed = 44 } +counts = { example = 12, failure = 225, fixture = 12, seed = 44 } [[entry]] engine = "native" @@ -109,7 +109,7 @@ engine = "native" stage = "discovery" site = "discovery.native" kind = "model_file_listings" -counts = { example = 12, failure = 289, fixture = 12, seed = 50 } +counts = { example = 12, failure = 290, fixture = 12, seed = 50 } [[entry]] engine = "native" @@ -165,14 +165,14 @@ engine = "native" stage = "model_config" site = "model_config.native" kind = "config_builds" -counts = { example = 56, failure = 302, fixture = 64, seed = 890 } +counts = { example = 56, failure = 304, fixture = 64, seed = 890 } [[entry]] engine = "native" stage = "model_config" site = "model_config.native" kind = "validations" -counts = { example = 56, failure = 372, fixture = 60, seed = 1091 } +counts = { example = 56, failure = 374, fixture = 60, seed = 1091 } [[entry]] engine = "native" @@ -186,21 +186,21 @@ engine = "native" stage = "model_config" site = "model_config.python_templates" kind = "config" -counts = { example = 12, failure = 114, fixture = 8, seed = 36 } +counts = { example = 12, failure = 115, fixture = 8, seed = 36 } [[entry]] engine = "native" stage = "model_config" site = "model_config.python_templates" kind = "connection" -counts = { example = 18, failure = 116, fixture = 9, seed = 63 } +counts = { example = 18, failure = 117, fixture = 9, seed = 63 } [[entry]] engine = "native" stage = "model_config" site = "model_config.python_templates" kind = "effective_vars" -counts = { example = 29, failure = 239, fixture = 15, seed = 121 } +counts = { example = 29, failure = 240, fixture = 15, seed = 121 } [[entry]] engine = "native" @@ -228,7 +228,7 @@ engine = "native" stage = "model_loop" site = "model_loop.cursor_intrinsic_validation" kind = "deferred" -counts = { example = 56, failure = 402, fixture = 64, seed = 1100 } +counts = { example = 56, failure = 404, fixture = 64, seed = 1100 } [[entry]] engine = "native" @@ -249,14 +249,14 @@ engine = "native" stage = "model_loop" site = "model_loop.native" kind = "declaration_expansions" -counts = { example = 56, failure = 413, fixture = 64, seed = 1100 } +counts = { example = 56, failure = 415, fixture = 64, seed = 1100 } [[entry]] engine = "native" stage = "model_loop" site = "model_loop.native" kind = "variable_substitutions" -counts = { example = 56, failure = 455, fixture = 72, seed = 1157 } +counts = { example = 56, failure = 457, fixture = 72, seed = 1157 } [[entry]] engine = "native" @@ -277,7 +277,21 @@ engine = "native" stage = "reference_extraction" site = "reference_extraction.native" kind = "reference_scans" -counts = { example = 250, failure = 463, fixture = 249, seed = 1523 } +counts = { example = 250, failure = 466, fixture = 249, seed = 1523 } + +[[entry]] +engine = "native-preview" +stage = "analysis" +site = "project_assembly" +kind = "syntax_error" +counts = { failure = 3 } + +[[entry]] +engine = "native-preview" +stage = "analysis" +site = "project_assembly" +kind = "unsupported_dialect" +counts = { failure = 1 } [[entry]] engine = "native-preview" @@ -298,7 +312,7 @@ engine = "native-preview" stage = "attachments" site = "attachments.native" kind = "cursor_intrinsic_rejections" -counts = { example = 160, failure = 170, fixture = 156, seed = 961 } +counts = { example = 160, failure = 172, fixture = 156, seed = 961 } [[entry]] engine = "native-preview" @@ -375,7 +389,7 @@ engine = "native-preview" stage = "declaration_scopes" site = "declaration_scopes.native" kind = "scope_indexes" -counts = { example = 12, failure = 224, fixture = 12, seed = 44 } +counts = { example = 12, failure = 225, fixture = 12, seed = 44 } [[entry]] engine = "native-preview" @@ -396,7 +410,7 @@ engine = "native-preview" stage = "discovery" site = "discovery.native" kind = "model_file_listings" -counts = { example = 12, failure = 289, fixture = 12, seed = 50 } +counts = { example = 12, failure = 290, fixture = 12, seed = 50 } [[entry]] engine = "native-preview" @@ -466,14 +480,14 @@ engine = "native-preview" stage = "model_analysis" site = "model_analysis.native" kind = "expression_shapes" -counts = { example = 4, failure = 114, fixture = 4, seed = 100 } +counts = { example = 4, failure = 115, fixture = 4, seed = 100 } [[entry]] engine = "native-preview" stage = "model_analysis" site = "model_analysis.native" kind = "model_analyses" -counts = { example = 56, failure = 221, fixture = 52, seed = 686 } +counts = { example = 56, failure = 223, fixture = 52, seed = 686 } [[entry]] engine = "native-preview" @@ -494,14 +508,14 @@ engine = "native-preview" stage = "model_config" site = "model_config.native" kind = "config_builds" -counts = { example = 56, failure = 302, fixture = 64, seed = 890 } +counts = { example = 56, failure = 304, fixture = 64, seed = 890 } [[entry]] engine = "native-preview" stage = "model_config" site = "model_config.native" kind = "validations" -counts = { example = 56, failure = 372, fixture = 60, seed = 1091 } +counts = { example = 56, failure = 374, fixture = 60, seed = 1091 } [[entry]] engine = "native-preview" @@ -515,35 +529,21 @@ engine = "native-preview" stage = "model_config" site = "model_config.python_templates" kind = "config" -counts = { example = 12, failure = 114, fixture = 8, seed = 36 } +counts = { example = 12, failure = 115, fixture = 8, seed = 36 } [[entry]] engine = "native-preview" stage = "model_config" site = "model_config.python_templates" kind = "connection" -counts = { example = 18, failure = 116, fixture = 9, seed = 63 } +counts = { example = 18, failure = 117, fixture = 9, seed = 63 } [[entry]] engine = "native-preview" stage = "model_config" site = "model_config.python_templates" kind = "effective_vars" -counts = { example = 29, failure = 239, fixture = 15, seed = 121 } - -[[entry]] -engine = "native-preview" -stage = "model_config" -site = "model_config.python_templates" -kind = "seed_target" -counts = { example = 8, seed = 30 } - -[[entry]] -engine = "native-preview" -stage = "model_config" -site = "model_config.python_templates" -kind = "source_target" -counts = { example = 8, seed = 23 } +counts = { example = 29, failure = 240, fixture = 15, seed = 121 } [[entry]] engine = "native-preview" @@ -557,7 +557,7 @@ engine = "native-preview" stage = "model_loop" site = "model_loop.cursor_intrinsic_validation" kind = "deferred" -counts = { example = 56, failure = 402, fixture = 64, seed = 1100 } +counts = { example = 56, failure = 404, fixture = 64, seed = 1100 } [[entry]] engine = "native-preview" @@ -578,14 +578,14 @@ engine = "native-preview" stage = "model_loop" site = "model_loop.native" kind = "declaration_expansions" -counts = { example = 56, failure = 413, fixture = 64, seed = 1100 } +counts = { example = 56, failure = 415, fixture = 64, seed = 1100 } [[entry]] engine = "native-preview" stage = "model_loop" site = "model_loop.native" kind = "variable_substitutions" -counts = { example = 56, failure = 455, fixture = 72, seed = 1157 } +counts = { example = 56, failure = 457, fixture = 72, seed = 1157 } [[entry]] engine = "native-preview" @@ -606,14 +606,21 @@ engine = "native-preview" stage = "project_assembly" site = "project_assembly.assembly" kind = "deferred" -counts = { example = 12, failure = 114, fixture = 8, seed = 36 } +counts = { failure = 4 } + +[[entry]] +engine = "native-preview" +stage = "project_assembly" +site = "project_assembly.native" +kind = "project_assemblies" +counts = { example = 12, failure = 111, fixture = 8, seed = 36 } [[entry]] engine = "native-preview" stage = "reference_extraction" site = "reference_extraction.native" kind = "reference_scans" -counts = { example = 250, failure = 463, fixture = 249, seed = 1523 } +counts = { example = 250, failure = 466, fixture = 249, seed = 1523 } [[entry]] engine = "native-preview" @@ -641,4 +648,4 @@ engine = "python" stage = "discovery" site = "discovery.native" kind = "model_file_listings" -counts = { example = 12, failure = 289, fixture = 12, seed = 50 } +counts = { example = 12, failure = 290, fixture = 12, seed = 50 } diff --git a/tests/goldens/compiler/failure/assembly-hook-syntax-unbuilt-dialect.json b/tests/goldens/compiler/failure/assembly-hook-syntax-unbuilt-dialect.json new file mode 100644 index 0000000000..e1bd80236f --- /dev/null +++ b/tests/goldens/compiler/failure/assembly-hook-syntax-unbuilt-dialect.json @@ -0,0 +1,19 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "P001", + "message": "Polyglot could not parse model 'customer_totals' post_hooks[0] inline_sql(\"...\") (/models/marts/customer_totals.sql): Invalid expression / Unexpected token", + "help": "if this SQL is valid for your warehouse, skip SQL analysis for this model, add this to the MODEL header:\n MODEL (\n sql_analysis false,\n ...\n );\n and please report the SQL so the parser can support it\n = help: SQL analysis is on for this project; to turn it off for every model, set this in sqlbuild_project.toml:\n [settings]\n sql_analysis = false\n = help: to skip SQL analysis for one run, use `--no-sql-analysis`" + } + ] + } + ], + "compiled": {}, + "manifest": null +} From 33c50dc805212d3f83afc76287623d6659ca07a2 Mon Sep 17 00:00:00 2001 From: Kevin Longe <273747162+kvlonge@users.noreply.github.com> Date: Fri, 9 Oct 2026 18:03:18 +0100 Subject: [PATCH 20/31] wip(native): share binding analyses across equal session members and guard model analysis CPU --- .github/workflows/verify.yml | 13 +- .../_helpers/catalog_state.rs | 21 + .../_helpers/compact_batch.rs | 468 +++++++++++++++++- .../assembly/analysis_session/constants.rs | 13 + .../_helpers/measure.py | 25 +- .../_helpers/report.py | 3 + .../compile_performance_ratio/constants.py | 1 + .../compile_performance_ratio/main/compare.py | 9 + .../compile/_helpers/assembly/project.py | 6 +- src/sqlbuild/compiler/profiling/constants.py | 1 + .../compiler/profiling/main/record_cpu.py | 24 + src/sqlbuild/compiler/profiling/types.py | 1 + .../main/build/test_compile_json_behavior.py | 1 + 13 files changed, 562 insertions(+), 24 deletions(-) create mode 100644 src/sqlbuild/compiler/profiling/main/record_cpu.py diff --git a/.github/workflows/verify.yml b/.github/workflows/verify.yml index 28478a2601..84833fa759 100644 --- a/.github/workflows/verify.yml +++ b/.github/workflows/verify.yml @@ -604,6 +604,13 @@ jobs: head_engine: native-preview gate_phase: contracts_cpu_ms max_ratio: "1.00" + - kind: dense + label: native-preview model analysis vs python + base_engine: python + head_engine: native-preview + gate_phase: model_analysis_cpu_ms + compile_args: --compile-arg=--select=* + max_ratio: "1.50" steps: - uses: actions/checkout@v4 - name: Check out the comparison base @@ -630,7 +637,7 @@ jobs: - name: Install base dependencies working-directory: base run: uv sync --locked --all-groups --all-extras - - name: Compare python and native-preview contract evaluation CPU on head + - name: Compare python and native-preview phase CPU on head if: matrix.gate_phase env: KIND: ${{ matrix.kind }} @@ -638,6 +645,7 @@ jobs: BASE_ENGINE: ${{ matrix.base_engine }} HEAD_ENGINE: ${{ matrix.head_engine }} GATE_PHASE: ${{ matrix.gate_phase }} + COMPILE_ARGS: ${{ matrix.compile_args }} run: | uv run python -m scripts.compare_compile_performance \ --kind "${KIND}" \ @@ -648,7 +656,8 @@ jobs: --head-python .venv/bin/python \ --base-engine "${BASE_ENGINE}" \ --head-engine "${HEAD_ENGINE}" \ - --gate-phase "${GATE_PHASE}" + --gate-phase "${GATE_PHASE}" \ + ${COMPILE_ARGS:+"${COMPILE_ARGS}"} - name: Compare base and head cold, warm and one-model edit compiles if: ${{ !matrix.gate_phase }} env: diff --git a/crates/sqlbuild-analysis/src/assembly/analysis_session/_helpers/catalog_state.rs b/crates/sqlbuild-analysis/src/assembly/analysis_session/_helpers/catalog_state.rs index a72ed4906b..975a6bdc4f 100644 --- a/crates/sqlbuild-analysis/src/assembly/analysis_session/_helpers/catalog_state.rs +++ b/crates/sqlbuild-analysis/src/assembly/analysis_session/_helpers/catalog_state.rs @@ -17,6 +17,8 @@ pub(crate) struct SessionCatalog { additions: Shapes, analysis_names: Vec, recorded_analysis: HashSet, + /// The `(types, nullability)` this session gave each relation's analysis. + analysis_shapes: HashMap, } impl SessionCatalog { @@ -28,6 +30,7 @@ impl SessionCatalog { additions: Vec::new(), analysis_names: Vec::new(), recorded_analysis: HashSet::new(), + analysis_shapes: HashMap::new(), } } @@ -86,6 +89,8 @@ impl SessionCatalog { name.clone(), (catalog_columns(&column_types), catalog_columns(columns)), ); + self.analysis_shapes + .insert(name.clone(), (column_types, columns.clone())); if self.recorded_analysis.insert(name.clone()) { self.analysis_names.push(name.clone()); } @@ -95,6 +100,22 @@ impl SessionCatalog { } } + /// The analysis shape this session set for `name`; inherited shapes are not known here. + pub(crate) fn session_analysis_shape(&self, name: &str) -> Option<&(Pairs, Pairs)> { + self.analysis_shapes.get(name) + } + + /// The shape a binding of `columns` for `name` resolves: Python's override or catalog entry. + pub(crate) fn effective_binding_shape(&self, name: &str, columns: &Pairs) -> Pairs { + if columns.is_empty() { + return Vec::new(); + } + match self.schemas.get(name) { + Some(known) if same_mapping(known, columns) => known.clone(), + _ => columns.clone(), + } + } + pub(crate) fn register_override(&mut self, overrides: Relations) -> usize { self.native.register_override(overrides) } diff --git a/crates/sqlbuild-analysis/src/assembly/analysis_session/_helpers/compact_batch.rs b/crates/sqlbuild-analysis/src/assembly/analysis_session/_helpers/compact_batch.rs index 5a3b0bd450..f40a8245bf 100644 --- a/crates/sqlbuild-analysis/src/assembly/analysis_session/_helpers/compact_batch.rs +++ b/crates/sqlbuild-analysis/src/assembly/analysis_session/_helpers/compact_batch.rs @@ -1,26 +1,33 @@ -//! One compact analysis batch built, run and projected exactly as Python's unshared batch. +//! One compact analysis batch built, shared, run and projected exactly as Python's batch. use std::collections::{HashMap, HashSet}; +use std::sync::LazyLock; +use regex::{Captures, Regex}; use serde_json::{Map, Value, json}; use crate::assembly::analysis_session::_helpers::catalog_state::SessionCatalog; use crate::assembly::analysis_session::_helpers::mappings::{ShapeTable, native_dialect}; use crate::assembly::analysis_session::constants::{ BINDING_SEVERITIES, COMPACT_WORKERS, CONFIDENCE_CODES, DEFAULT_BINDING_MESSAGE, FACT_LENGTH, - INVALID_NATIVE_RESPONSE, LEGACY_FALLBACK, LEGACY_RESPONSE_LENGTH, NULLABILITY_BY_CODE, - RESPONSE_LENGTH, SOURCE_LENGTH, TRANSFORM_CODES, + INVALID_NATIVE_RESPONSE, LEGACY_FALLBACK, LEGACY_RESPONSE_LENGTH, MIN_SHARED_MEMBERS, + NULLABILITY_BY_CODE, PYTHON_ASCII_SPACES, QUALIFIED_SCAN_TOKENS, REFERENCE_MARKER_PATTERN, + RELATION_STUB_PREFIX, RESPONSE_LENGTH, SOURCE_LENGTH, TRANSFORM_CODES, + UNSTUBBED_REFERENCE_KIND, }; use crate::assembly::analysis_session::models::{ColumnFact, LineageRow}; use crate::assembly::analysis_session::types::{Pairs, Shapes}; use crate::semantic_validation::main::diagnostics::binding_diagnostics; -use crate::semantic_validation::types::{DiagnosticRow, NormalizationRequest}; +use crate::semantic_validation::types::{DiagnosticRow, NormalizationRequest, Relations}; const INVALID_BATCH: &str = "native compact query analysis returned an invalid batch response"; const INVALID_TEMPLATE: &str = "native compact query analysis returned an invalid template"; const INVALID_COLUMN: &str = "native compact analysis returned invalid column facts"; +static REFERENCE_MARKER: LazyLock> = + LazyLock::new(|| Regex::new(REFERENCE_MARKER_PATTERN).map_err(|error| error.to_string())); /// One query of a batch with the facts Python's preparation reads. +#[derive(Clone)] pub(crate) struct BatchMember<'a> { pub(crate) query_sql: &'a str, pub(crate) placeholders: &'a Pairs, @@ -73,10 +80,34 @@ struct BatchResponse<'a> { templates: &'a [Value], } -/// The prepared request and each member's query index. +/// The prepared request, each member's query index and the queries members share. struct BatchPayload { request: Value, query_indexes: Vec, + shared_queries: HashSet, +} + +/// Python's `SharedBindingQuery`: one member's relation-stubbed query and its identity. +struct SharedQuery { + sql: String, + /// Each lineage reference's stub, in reference order. + stubs: Vec<(String, String)>, + key: String, +} + +impl SharedQuery { + fn stub<'a>(&'a self, name: &'a str) -> &'a str { + self.stubs + .iter() + .find(|(reference, _)| reference == name) + .map_or(name, |(_, stub)| stub.as_str()) + } +} + +/// One run's projected members and the shared members whose result Python recomputes. +struct BatchRun { + results: Vec, + inexact: Vec, } impl SessionCatalog { @@ -98,20 +129,218 @@ impl SessionCatalog { } self.prepare_analysis(&required, batch.types, batch.nullability); let cleaned: Vec = self.normalize_members(batch)?; - let payload: BatchPayload = self.batch_payload(batch, &cleaned)?; + let shared: Vec> = self.shared_queries(batch, &cleaned)?; + let BatchRun { + mut results, + inexact, + } = self.run_batch(batch, cleaned, &shared)?; + if !inexact.is_empty() { + let subset = Batch { + members: inexact + .iter() + .map(|index| batch.members[*index].clone()) + .collect(), + ..*batch + }; + let cleaned: Vec = inexact + .iter() + .map(|index| results[*index].cleaned_sql.clone()) + .collect(); + let unshared: Vec> = inexact.iter().map(|_| None).collect(); + let rerun: BatchRun = self.run_batch(&subset, cleaned, &unshared)?; + for (index, result) in inexact.into_iter().zip(rerun.results) { + results[index] = result; + } + } + self.with_separate_validation(&batch.members, results) + } + + /// Python's `_analyze_compact_inputs` run: prepare, analyse, project and find inexact sharing. + fn run_batch( + &mut self, + batch: &Batch<'_>, + cleaned: Vec, + shared: &[Option], + ) -> Result { + let payload: BatchPayload = self.batch_payload(batch, &cleaned, shared)?; let job = self.native.prepare_compact(&payload.request.to_string())?; let analysis = job .take_analysis() .ok_or("compact analysis job already ran")?; let response: Value = serde_json::from_str(&job.run(analysis)?).map_err(|error| error.to_string())?; + let inexact: Vec = inexact_shared_members(&response, &payload); let results: Vec = attach_validations( &response, &payload.query_indexes, batch.dialect, project_response(&response, cleaned)?, )?; - self.with_separate_validation(&batch.members, results) + Ok(BatchRun { results, inexact }) + } + + /// Python's `prepare_binding_queries` shared forms, for keys at least two members match. + fn shared_queries( + &self, + batch: &Batch<'_>, + cleaned: &[String], + ) -> Result>, String> { + let prekeys: Vec> = batch + .members + .iter() + .map(|member| { + member + .binding_schema + .map(|_| share_prekey(member)) + .transpose() + }) + .collect::>()?; + let mut prekey_counts: HashMap<&str, usize> = HashMap::new(); + for prekey in prekeys.iter().flatten() { + *prekey_counts.entry(prekey.as_str()).or_default() += 1; + } + let mut candidates: Vec<(usize, Vec<(String, String)>)> = Vec::new(); + for (index, (member, prekey)) in batch.members.iter().zip(&prekeys).enumerate() { + let (Some(schema), Some(prekey)) = (member.binding_schema, prekey) else { + continue; + }; + if prekey_counts.get(prekey.as_str()).copied().unwrap_or(0) < MIN_SHARED_MEMBERS { + continue; + } + if let Some(names) = self.shared_reference_names(&cleaned[index], member, schema) { + let stubs: Vec<(String, String)> = names + .into_iter() + .enumerate() + .map(|(position, name)| (name, format!("{RELATION_STUB_PREFIX}{position}"))) + .collect(); + candidates.push((index, stubs)); + } + } + let requests: Vec = candidates + .iter() + .map(|(index, stubs)| { + let member: &BatchMember<'_> = &batch.members[*index]; + ( + member.query_sql.to_owned(), + stubs.iter().cloned().collect(), + member.placeholders.iter().cloned().collect(), + ) + }) + .collect(); + let stubbed: Vec> = if requests.is_empty() { + Vec::new() + } else { + self.native + .normalize_analysis_sqls(batch.dialect, requests)? + }; + let mut shared: Vec> = batch.members.iter().map(|_| None).collect(); + for ((index, stubs), sql) in candidates.into_iter().zip(stubbed) { + let (Ok(sql), Some(schema)) = (sql, batch.members[index].binding_schema) else { + continue; + }; + let key: String = self.shared_key( + batch, + &sql, + &stubs, + schema, + batch.members[index].recover_cte_facts, + ); + shared[index] = Some(SharedQuery { sql, stubs, key }); + } + let mut key_counts: HashMap = HashMap::new(); + for query in shared.iter().flatten() { + *key_counts.entry(query.key.clone()).or_default() += 1; + } + Ok(shared + .into_iter() + .map(|query| { + query.filter(|query| { + key_counts.get(&query.key).copied().unwrap_or(0) >= MIN_SHARED_MEMBERS + }) + }) + .collect()) + } + + /// Python's `_shared_reference_names`, None where stubbing could change the analysis. + fn shared_reference_names( + &self, + cleaned_sql: &str, + member: &BatchMember<'_>, + schema: &Shapes, + ) -> Option> { + let names: Vec = member + .lineage_references + .iter() + .map(|(name, _, _)| name.clone()) + .collect(); + let folded: HashSet = names.iter().map(|name| name.to_lowercase()).collect(); + let plain_text: bool = cleaned_sql.is_ascii() && names.iter().all(|name| name.is_ascii()); + let quoted: bool = cleaned_sql.contains('"') + || schema.iter().any(|(relation, columns)| { + relation.contains('"') || columns.iter().any(|(column, _)| column.contains('"')) + }); + let collides: bool = names + .iter() + .any(|name| name.starts_with(RELATION_STUB_PREFIX)) + || cleaned_sql + .to_ascii_lowercase() + .contains(RELATION_STUB_PREFIX); + let unshaped: bool = names + .iter() + .any(|name| self.session_analysis_shape(name).is_none()); + let usable: bool = !names.is_empty() + && folded.len() == names.len() + && plain_text + && !quoted + && !collides + && !unshaped + && !names_qualify(cleaned_sql, &names); + usable.then_some(names) + } + + /// Python's `SharedBindingQuery.key`, with the CTE fact flag its query key adds. + fn shared_key( + &self, + batch: &Batch<'_>, + sql: &str, + stubs: &[(String, String)], + schema: &Shapes, + recover_cte_facts: bool, + ) -> String { + let analysis_shapes: Vec = stubs + .iter() + .map(|(name, stub)| { + let (types, nullability) = self + .session_analysis_shape(name) + .cloned() + .unwrap_or_default(); + json!([stub, types, nullability]) + }) + .collect(); + let mut binding_shapes: Vec = schema + .iter() + .map(|(relation, columns)| { + let alias: &str = stubs + .iter() + .find(|(name, _)| name == relation) + .map_or(relation.as_str(), |(_, stub)| stub.as_str()); + json!([ + alias, + !columns.is_empty(), + self.effective_binding_shape(relation, columns) + ]) + .to_string() + }) + .collect(); + binding_shapes.sort_unstable(); + json!([ + sql, + batch.dialect, + analysis_shapes, + binding_shapes, + recover_cte_facts + ]) + .to_string() } fn normalize_members(&self, batch: &Batch<'_>) -> Result, String> { @@ -137,6 +366,7 @@ impl SessionCatalog { &mut self, batch: &Batch<'_>, cleaned: &[String], + shared: &[Option], ) -> Result { let function_return_types: Map = batch .function_return_types @@ -145,20 +375,40 @@ impl SessionCatalog { .collect(); let mut queries: Vec = Vec::new(); let mut query_keys: HashMap = HashMap::new(); + let mut shared_queries: HashSet = HashSet::new(); let mut templates: Vec = Vec::new(); let mut template_keys: HashMap = HashMap::new(); let mut projections: Vec = Vec::with_capacity(batch.members.len()); let mut query_indexes: Vec = Vec::with_capacity(batch.members.len()); - for (member, sql) in batch.members.iter().zip(cleaned) { - let query: Value = self.member_query(batch.dialect, member, sql)?; - let query_index: usize = *query_keys.entry(query.to_string()).or_insert_with(|| { - queries.push(query); - queries.len() - 1 - }); + for ((member, sql), share) in batch.members.iter().zip(cleaned).zip(shared) { + let query_index: usize = match share { + Some(share) => { + let binding = self.member_binding(member, sql)?; + let index: usize = match query_keys.get(&share.key) { + Some(index) => *index, + None => { + let query: Value = + self.shared_member_query(batch.dialect, member, share, binding); + queries.push(query); + query_keys.insert(share.key.clone(), queries.len() - 1); + queries.len() - 1 + } + }; + shared_queries.insert(index); + index + } + None => { + let query: Value = self.member_query(batch.dialect, member, sql)?; + *query_keys.entry(query.to_string()).or_insert_with(|| { + queries.push(query); + queries.len() - 1 + }) + } + }; query_indexes.push(query_index); let template: Value = json!({ "queryIndex": query_index, - "references": lineage_resources(member), + "references": lineage_resources(member, share.as_ref()), "functionReturnTypes": function_return_types, "declaredColumnOrder": declared_column_order(member, batch.nullability), "recoverCteFacts": member.recover_cte_facts, @@ -174,7 +424,12 @@ impl SessionCatalog { let resource_names: Map = member .lineage_references .iter() - .map(|(name, _, resource)| (name.clone(), Value::String(resource.clone()))) + .map(|(name, _, resource)| { + let canonical: &str = share + .as_ref() + .map_or(name.as_str(), |share| share.stub(name)); + (canonical.to_owned(), Value::String(resource.clone())) + }) .collect(); projections .push(json!({"templateIndex": template_index, "resourceNames": resource_names})); @@ -187,9 +442,64 @@ impl SessionCatalog { "workers": COMPACT_WORKERS, }), query_indexes, + shared_queries, }) } + /// A bound member's sorted binding references and overrides, preparing its relations. + fn member_binding( + &mut self, + member: &BatchMember<'_>, + sql: &str, + ) -> Result<(Vec<(String, bool)>, Relations), String> { + let schema: &Shapes = member + .binding_schema + .ok_or("a shared member has no binding schema")?; + let (_, references, overrides) = self + .prepare(&[(sql, schema)]) + .pop() + .ok_or("binding preparation returned no request")?; + Ok((references, overrides)) + } + + /// Python's shared query: stubbed SQL with references, aliases and binding sorted by stub. + fn shared_member_query( + &mut self, + dialect: &str, + member: &BatchMember<'_>, + share: &SharedQuery, + (references, overrides): (Vec<(String, bool)>, Relations), + ) -> Value { + let mut lineage_names: Vec<&str> = member + .lineage_references + .iter() + .map(|(name, _, _)| name.as_str()) + .collect(); + lineage_names.sort_by_key(|name| share.stub(name)); + let analysis_references: Vec<[&str; 2]> = lineage_names + .iter() + .map(|name| [*name, share.stub(name)]) + .collect(); + let mut references: Vec<(String, bool)> = references; + references.sort_by(|(left, _), (right, _)| share.stub(left).cmp(share.stub(right))); + let aliases: Map = references + .iter() + .filter(|(name, _)| share.stub(name) != name.as_str()) + .map(|(name, _)| (name.clone(), Value::String(share.stub(name).to_owned()))) + .collect(); + let mut query: Value = json!({ + "sql": share.sql, + "dialect": dialect, + "analysis_references": analysis_references, + "binding_references": references, + "binding_aliases": aliases, + }); + if !overrides.is_empty() { + query["binding_override"] = json!(self.register_override(overrides)); + } + query + } + /// One member's query: analysis references and, when bound, its catalog binding. fn member_query( &mut self, @@ -260,19 +570,141 @@ impl SessionCatalog { } } -fn lineage_resources(member: &BatchMember<'_>) -> Map { +fn lineage_resources(member: &BatchMember<'_>, share: Option<&SharedQuery>) -> Map { member .lineage_references .iter() .map(|(name, resource_type, _)| { + let canonical: &str = share.map_or(name.as_str(), |share| share.stub(name)); ( - name.clone(), - json!({"resourceType": resource_type, "resourceName": name}), + canonical.to_owned(), + json!({"resourceType": resource_type, "resourceName": canonical}), ) }) .collect() } +/// Python's `binding_share_prekey`: authored SQL with lineage reference markers as stubs. +fn share_prekey(member: &BatchMember<'_>) -> Result { + let marker: &Regex = REFERENCE_MARKER.as_ref().map_err(Clone::clone)?; + let positions: HashMap<&str, usize> = member + .lineage_references + .iter() + .enumerate() + .map(|(position, (name, _, _))| (name.as_str(), position)) + .collect(); + Ok(marker + .replace_all(member.query_sql, |captures: &Captures<'_>| { + let whole: &str = captures.get(0).map_or("", |found| found.as_str()); + let kind: &str = captures.get(1).map_or("", |found| found.as_str()); + match captures + .get(2) + .and_then(|found| positions.get(found.as_str())) + { + Some(position) if kind != UNSTUBBED_REFERENCE_KIND => { + format!("{RELATION_STUB_PREFIX}{position}") + } + _ => whole.to_owned(), + } + }) + .into_owned()) +} + +/// Python's `_qualified_reference_names` is non-empty; text its scanner reads counts as qualified. +fn names_qualify(sql: &str, names: &[String]) -> bool { + if !sql.contains('.') { + return false; + } + if !sql.is_ascii() + || QUALIFIED_SCAN_TOKENS + .iter() + .any(|token| sql.contains(token)) + { + return true; + } + let folded: String = sql.to_ascii_lowercase(); + names.iter().any(|name| { + let name: String = name.to_lowercase(); + plain_identifier(&name) && is_qualifier(&folded, &name) + }) +} + +/// Python's `_PLAIN_IDENTIFIER_PATTERN`. +fn plain_identifier(name: &str) -> bool { + let mut characters = name.chars(); + characters + .next() + .is_some_and(|first| first.is_ascii_lowercase() || first == '_' || first == '$') + && characters.all(|character| { + character.is_ascii_lowercase() + || character.is_ascii_digit() + || character == '_' + || character == '$' + }) +} + +/// Python's `_is_qualifier`: `name` as a whole identifier followed by `.` after any spaces. +fn is_qualifier(folded_sql: &str, name: &str) -> bool { + let identifier = |byte: u8| byte.is_ascii_alphanumeric() || byte == b'_' || byte == b'$'; + let bytes: &[u8] = folded_sql.as_bytes(); + folded_sql.match_indices(name).any(|(start, _)| { + let mut end: usize = start + name.len(); + let bounded: bool = (start == 0 || !identifier(bytes[start - 1])) + && (end == bytes.len() || !identifier(bytes[end])); + while bounded && end < bytes.len() && PYTHON_ASCII_SPACES.as_bytes().contains(&bytes[end]) { + end += 1; + } + bounded && bytes.get(end) == Some(&b'.') + }) +} + +/// Python's `_inexact_shared_members`: shared members whose result depends on relation names. +fn inexact_shared_members(response: &Value, payload: &BatchPayload) -> Vec { + if payload.shared_queries.is_empty() { + return Vec::new(); + } + let (Some(analyses), Some(templates)) = ( + response.get("analyses").and_then(Value::as_array), + response.get("templates").and_then(Value::as_array), + ) else { + return Vec::new(); + }; + let validations: Option<&Vec> = response.get("validations").and_then(Value::as_array); + payload + .query_indexes + .iter() + .zip(analyses) + .enumerate() + .filter(|(_, (query, analysis))| { + payload.shared_queries.contains(query) && { + let template: Option<&Value> = analysis + .as_array() + .and_then(|entry| entry.first()) + .and_then(Value::as_u64) + .and_then(|index| usize::try_from(index).ok()) + .and_then(|index| templates.get(index)); + let validation: Option<&Value> = + validations.and_then(|validations| validations.get(**query)); + !shared_result_is_exact(template, validation) + } + }) + .map(|(member, _)| member) + .collect() +} + +/// Python's `shared_result_is_exact`: an analysed template without any binding diagnostic. +fn shared_result_is_exact(template: Option<&Value>, validation: Option<&Value>) -> bool { + let analysed: bool = template.is_some_and(|template| !template.is_string()); + analysed + && match validation { + None | Some(Value::Null) => true, + Some(validation) => validation + .get("errors") + .and_then(Value::as_array) + .is_some_and(Vec::is_empty), + } +} + /// A single-reference member's declared column order, as Python's template records it. fn declared_column_order<'a>( member: &BatchMember<'_>, diff --git a/crates/sqlbuild-analysis/src/assembly/analysis_session/constants.rs b/crates/sqlbuild-analysis/src/assembly/analysis_session/constants.rs index d0d6fa5520..c7661e62af 100644 --- a/crates/sqlbuild-analysis/src/assembly/analysis_session/constants.rs +++ b/crates/sqlbuild-analysis/src/assembly/analysis_session/constants.rs @@ -95,3 +95,16 @@ pub(crate) const RENDERED_TYPE_NAMES: [(&str, &str); 12] = [ ]; /// The stack a standalone dynamic pivot proof parses on, as the analysis workers have. pub(crate) const PIVOT_WORKER_STACK_BYTES: usize = 16 * 1024 * 1024; +/// Python's `COMPACT_RELATION_STUB_PREFIX`: the relation name a shared query binds instead. +pub(crate) const RELATION_STUB_PREFIX: &str = "__sqlbuild_project_input_"; +/// Python's `COMPACT_REFERENCE_MARKER_PATTERN`: a reference call and the name it reads. +pub(crate) const REFERENCE_MARKER_PATTERN: &str = + r#"__(ref|seed|source|dbt_ref)\((?:"[^"]+"\s*,\s*)?"([^"]+)"\)"#; +/// Python's `COMPACT_UNSTUBBED_REFERENCE_KIND`, a reference kind sharing never stubs. +pub(crate) const UNSTUBBED_REFERENCE_KIND: &str = "dbt_ref"; +/// Python's `MIN_SHARED_BINDING_QUERY_MEMBERS`. +pub(crate) const MIN_SHARED_MEMBERS: usize = 2; +/// Text whose presence sends Python's qualifier search to its token scanner. +pub(crate) const QUALIFIED_SCAN_TOKENS: [&str; 5] = ["\"", "`", "[", "--", "/*"]; +/// Python's `str.isspace` over ASCII. +pub(crate) const PYTHON_ASCII_SPACES: &str = " \t\n\r\x0b\x0c\x1c\x1d\x1e\x1f"; diff --git a/scripts/compile_performance_ratio/_helpers/measure.py b/scripts/compile_performance_ratio/_helpers/measure.py index 0f3882c47a..27a314ad22 100644 --- a/scripts/compile_performance_ratio/_helpers/measure.py +++ b/scripts/compile_performance_ratio/_helpers/measure.py @@ -118,6 +118,7 @@ def compare_builds( runs: int, modes: tuple[str, ...], engines: tuple[str | None, str | None] = (None, None), + compile_args: tuple[str, ...] = (), ) -> tuple[CompileComparison, ...]: """Alternate base and head compiles per mode; warm and edit reuse the cold project.""" @@ -133,7 +134,12 @@ def compare_builds( if mode == COLD_MODE: for label, python, project_dir, engine in builds: _ = _compile_once( - label=label, python=python, project_dir=project_dir, mode=mode, engine=engine + label=label, + python=python, + project_dir=project_dir, + mode=mode, + engine=engine, + compile_args=compile_args, ) cache_primed = False elif not cache_primed: @@ -144,6 +150,7 @@ def compare_builds( project_dir=project_dir, mode=WARM_MODE, engine=engine, + compile_args=compile_args, ) cache_primed = True results: dict[str, list[CompileRun]] = {BASE_LABEL: [], HEAD_LABEL: []} @@ -152,7 +159,12 @@ def compare_builds( if mode == EDIT_MODE: _ = apply_one_model_edit(project_dir=project_dir, revision=revision) run: CompileRun = _compile_once( - label=label, python=python, project_dir=project_dir, mode=mode, engine=engine + label=label, + python=python, + project_dir=project_dir, + mode=mode, + engine=engine, + compile_args=compile_args, ) _check_cache_use(run=run, mode=mode) results[label].append(run) @@ -201,7 +213,13 @@ def _check_cache_use(*, run: CompileRun, mode: str) -> None: def _compile_once( - *, label: str, python: Path, project_dir: Path, mode: str, engine: str | None + *, + label: str, + python: Path, + project_dir: Path, + mode: str, + engine: str | None, + compile_args: tuple[str, ...], ) -> CompileRun: cache_args: tuple[str, ...] = () if mode == COLD_MODE: @@ -223,6 +241,7 @@ def _compile_once( "compile", "--json", *cache_args, + *compile_args, ], capture_output=True, text=True, diff --git a/scripts/compile_performance_ratio/_helpers/report.py b/scripts/compile_performance_ratio/_helpers/report.py index 2cc764acf3..aabf811eea 100644 --- a/scripts/compile_performance_ratio/_helpers/report.py +++ b/scripts/compile_performance_ratio/_helpers/report.py @@ -20,6 +20,7 @@ def comparison_markdown( mode_max_ratios: Mapping[str, float] | None = None, engines: tuple[str | None, str | None] = (None, None), gate_phases: tuple[str, ...] = (), + compile_args: tuple[str, ...] = (), ) -> str: """Summarize medians, ratios and per-phase timings of every mode as Markdown tables.""" @@ -39,6 +40,8 @@ def comparison_markdown( ) if gate_phases: lines.extend((f"Gated phases: {', '.join(gate_phases)}.", "")) + if compile_args: + lines.extend((f"Compile arguments: `{' '.join(compile_args)}`.", "")) for comparison in comparisons: lines.extend( ( diff --git a/scripts/compile_performance_ratio/constants.py b/scripts/compile_performance_ratio/constants.py index d51fa2cd83..0a365a0367 100644 --- a/scripts/compile_performance_ratio/constants.py +++ b/scripts/compile_performance_ratio/constants.py @@ -47,6 +47,7 @@ "total_ms", "graph_ms", "model_analysis_ms", + "model_analysis_cpu_ms", "analysis_native_ms", "contracts_ms", "contracts_cpu_ms", diff --git a/scripts/compile_performance_ratio/main/compare.py b/scripts/compile_performance_ratio/main/compare.py index 322780b793..cbf0cbbe29 100644 --- a/scripts/compile_performance_ratio/main/compare.py +++ b/scripts/compile_performance_ratio/main/compare.py @@ -62,6 +62,7 @@ def compare_compile_performance(argv: list[str] | None = None) -> int: runs=args.runs, modes=modes, engines=(args.base_engine, args.head_engine), + compile_args=tuple(args.compile_args), ) mode_max_ratios: dict[str, float] = ( {} if args.edit_max_ratio is None else {EDIT_MODE: args.edit_max_ratio} @@ -93,6 +94,7 @@ def compare_compile_performance(argv: list[str] | None = None) -> int: mode_max_ratios=mode_max_ratios, engines=(args.base_engine, args.head_engine), gate_phases=gate_phases, + compile_args=tuple(args.compile_args), ) print(markdown) _ = append_summary(path=Path(summary_value) if summary_value else None, markdown=markdown) @@ -148,6 +150,13 @@ def _parse_args(argv: list[str] | None) -> argparse.Namespace: default=None, help="Compiler engine the head build runs; defaults to the build's own default.", ) + parser.add_argument( + "--compile-arg", + dest="compile_args", + action="append", + default=[], + help="Extra `sqb compile` argument for both builds, e.g. `--compile-arg=--select=*`.", + ) parser.add_argument( "--gate-phase", dest="gate_phases", diff --git a/src/sqlbuild/compiler/compile/_helpers/assembly/project.py b/src/sqlbuild/compiler/compile/_helpers/assembly/project.py index 87bf84e5ab..119e062eeb 100644 --- a/src/sqlbuild/compiler/compile/_helpers/assembly/project.py +++ b/src/sqlbuild/compiler/compile/_helpers/assembly/project.py @@ -166,6 +166,7 @@ from sqlbuild.compiler.lineage.types import ColumnLineageMode, InferredNullability from sqlbuild.compiler.planner.types import ContractPolicy from sqlbuild.compiler.profiling.main.record import record_compile_timing +from sqlbuild.compiler.profiling.main.record_cpu import record_compile_cpu_timing from sqlbuild.compiler.project_assembly.models import NativeProjectResources from sqlbuild.compiler.references.types import SqlReferenceKind from sqlbuild.compiler.resource_names.main.function_node_type import function_node_type @@ -326,7 +327,10 @@ def assemble_compiled_project( ) model_sql_analysis_by_name: dict[str, _ModelSqlAnalysis] = {} if sql_analysis_enabled: - with record_compile_timing("model_analysis_ms"): + with ( + record_compile_timing("model_analysis_ms"), + record_compile_cpu_timing("model_analysis_cpu_ms"), + ): python_analysis: partial[dict[str, _ModelSqlAnalysis]] = partial( _analyze_model_sql_in_parallel, known_functions=known_function_names(inputs.sql_function_inputs), diff --git a/src/sqlbuild/compiler/profiling/constants.py b/src/sqlbuild/compiler/profiling/constants.py index 1ff96dd869..7cf606a237 100644 --- a/src/sqlbuild/compiler/profiling/constants.py +++ b/src/sqlbuild/compiler/profiling/constants.py @@ -5,6 +5,7 @@ COMPILE_TIMING_PHASES: tuple[CompileTimingPhase, ...] = ( "attachment_ms", "model_analysis_ms", + "model_analysis_cpu_ms", "analysis_preparation_ms", "analysis_native_ms", "analysis_projection_ms", diff --git a/src/sqlbuild/compiler/profiling/main/record_cpu.py b/src/sqlbuild/compiler/profiling/main/record_cpu.py new file mode 100644 index 0000000000..18b1ab7aba --- /dev/null +++ b/src/sqlbuild/compiler/profiling/main/record_cpu.py @@ -0,0 +1,24 @@ +"""Compile phase CPU recording entry point.""" + +import time +from collections.abc import Iterator +from contextlib import contextmanager + +from sqlbuild.compiler.profiling.classes.context import CompileTimingContext +from sqlbuild.compiler.profiling.models import CompileTimingCollector +from sqlbuild.compiler.profiling.types import CompileTimingPhase + + +@contextmanager +def record_compile_cpu_timing(phase: CompileTimingPhase) -> Iterator[None]: + """Record the process CPU time an operation spans, including its worker threads.""" + + collector: CompileTimingCollector | None = CompileTimingContext.active.get() + if collector is None: + yield + return + start_ns: int = time.process_time_ns() + try: + yield + finally: + collector.add(phase=phase, elapsed_ns=time.process_time_ns() - start_ns) diff --git a/src/sqlbuild/compiler/profiling/types.py b/src/sqlbuild/compiler/profiling/types.py index 22f51e54cc..59ff6ef9d8 100644 --- a/src/sqlbuild/compiler/profiling/types.py +++ b/src/sqlbuild/compiler/profiling/types.py @@ -5,6 +5,7 @@ type CompileTimingPhase = Literal[ "attachment_ms", "model_analysis_ms", + "model_analysis_cpu_ms", "analysis_preparation_ms", "analysis_native_ms", "analysis_projection_ms", diff --git a/tests/e2e/src/sqlbuild/cli/commands/main/build/test_compile_json_behavior.py b/tests/e2e/src/sqlbuild/cli/commands/main/build/test_compile_json_behavior.py index dff9027895..c7b7680d7c 100644 --- a/tests/e2e/src/sqlbuild/cli/commands/main/build/test_compile_json_behavior.py +++ b/tests/e2e/src/sqlbuild/cli/commands/main/build/test_compile_json_behavior.py @@ -73,6 +73,7 @@ def test_given_waffle_shop_when_running_compile_json_then_it_reports_offline_que "custom_rules_ms", "early_lint_wait_ms", "model_analysis_ms", + "model_analysis_cpu_ms", "analysis_preparation_ms", "analysis_native_ms", "analysis_projection_ms", From 5cec154ae1e949688586506ad4a69b6e54aa5870 Mon Sep 17 00:00:00 2001 From: Kevin Longe <273747162+kvlonge@users.noreply.github.com> Date: Fri, 9 Oct 2026 18:12:28 +0100 Subject: [PATCH 21/31] test(native): pin shared, re-analysed and unshared session members against Python --- .../_helpers/catalog_state.rs | 9 +++ .../_helpers/compact_batch.rs | 2 + .../src/assembly/analysis_session/main/mod.rs | 1 + .../analysis_session/main/session_sharing.rs | 8 +++ .../_helpers/analysis_session/session.rs | 10 ++++ src/sqlbuild/_native.pyi | 2 + .../compiler/analysis_session/_test_types.py | 14 +++++ .../compiler/analysis_session/helpers.py | 54 ++++++++++++++++++ .../test_native_model_analysis.py | 56 +++++++++++++++++++ 9 files changed, 156 insertions(+) create mode 100644 crates/sqlbuild-analysis/src/assembly/analysis_session/main/session_sharing.rs diff --git a/crates/sqlbuild-analysis/src/assembly/analysis_session/_helpers/catalog_state.rs b/crates/sqlbuild-analysis/src/assembly/analysis_session/_helpers/catalog_state.rs index 975a6bdc4f..0bbe164e2e 100644 --- a/crates/sqlbuild-analysis/src/assembly/analysis_session/_helpers/catalog_state.rs +++ b/crates/sqlbuild-analysis/src/assembly/analysis_session/_helpers/catalog_state.rs @@ -19,6 +19,8 @@ pub(crate) struct SessionCatalog { recorded_analysis: HashSet, /// The `(types, nullability)` this session gave each relation's analysis. analysis_shapes: HashMap, + pub(crate) shared_members: usize, + pub(crate) reanalysed_members: usize, } impl SessionCatalog { @@ -31,6 +33,8 @@ impl SessionCatalog { analysis_names: Vec::new(), recorded_analysis: HashSet::new(), analysis_shapes: HashMap::new(), + shared_members: 0, + reanalysed_members: 0, } } @@ -100,6 +104,11 @@ impl SessionCatalog { } } + /// `(shared members, shared members re-analysed alone)` so far. + pub(crate) fn sharing(&self) -> (usize, usize) { + (self.shared_members, self.reanalysed_members) + } + /// The analysis shape this session set for `name`; inherited shapes are not known here. pub(crate) fn session_analysis_shape(&self, name: &str) -> Option<&(Pairs, Pairs)> { self.analysis_shapes.get(name) diff --git a/crates/sqlbuild-analysis/src/assembly/analysis_session/_helpers/compact_batch.rs b/crates/sqlbuild-analysis/src/assembly/analysis_session/_helpers/compact_batch.rs index f40a8245bf..eb7b4b8365 100644 --- a/crates/sqlbuild-analysis/src/assembly/analysis_session/_helpers/compact_batch.rs +++ b/crates/sqlbuild-analysis/src/assembly/analysis_session/_helpers/compact_batch.rs @@ -134,6 +134,8 @@ impl SessionCatalog { mut results, inexact, } = self.run_batch(batch, cleaned, &shared)?; + self.shared_members += shared.iter().flatten().count(); + self.reanalysed_members += inexact.len(); if !inexact.is_empty() { let subset = Batch { members: inexact diff --git a/crates/sqlbuild-analysis/src/assembly/analysis_session/main/mod.rs b/crates/sqlbuild-analysis/src/assembly/analysis_session/main/mod.rs index 6c5394355c..5c08bc3722 100644 --- a/crates/sqlbuild-analysis/src/assembly/analysis_session/main/mod.rs +++ b/crates/sqlbuild-analysis/src/assembly/analysis_session/main/mod.rs @@ -3,4 +3,5 @@ pub mod finish_analysis_session; pub mod prove_dynamic_contract; pub mod provide_deferred_analyses; pub mod run_analysis_session; +pub mod session_sharing; pub mod start_analysis_session; diff --git a/crates/sqlbuild-analysis/src/assembly/analysis_session/main/session_sharing.rs b/crates/sqlbuild-analysis/src/assembly/analysis_session/main/session_sharing.rs new file mode 100644 index 0000000000..3b67b99626 --- /dev/null +++ b/crates/sqlbuild-analysis/src/assembly/analysis_session/main/session_sharing.rs @@ -0,0 +1,8 @@ +//! How many of a session's analysed models shared a query, and how many it re-analysed. + +use crate::assembly::analysis_session::models::AnalysisSession; + +/// `(shared members, shared members re-analysed alone)` across the session's batches so far. +pub fn session_sharing(session: &AnalysisSession) -> (usize, usize) { + session.catalog.sharing() +} diff --git a/crates/sqlbuild-python/src/bindings/_helpers/analysis_session/session.rs b/crates/sqlbuild-python/src/bindings/_helpers/analysis_session/session.rs index 87af8f9b33..374e2bc188 100644 --- a/crates/sqlbuild-python/src/bindings/_helpers/analysis_session/session.rs +++ b/crates/sqlbuild-python/src/bindings/_helpers/analysis_session/session.rs @@ -7,6 +7,7 @@ use sqlbuild_analysis::assembly::analysis_session::main::finish_analysis_session use sqlbuild_analysis::assembly::analysis_session::main::prove_dynamic_contract::prove_dynamic_contract; use sqlbuild_analysis::assembly::analysis_session::main::provide_deferred_analyses::provide_deferred_analyses; use sqlbuild_analysis::assembly::analysis_session::main::run_analysis_session::run_analysis_session; +use sqlbuild_analysis::assembly::analysis_session::main::session_sharing::session_sharing; use sqlbuild_analysis::assembly::analysis_session::main::start_analysis_session::start_analysis_session; use sqlbuild_analysis::assembly::analysis_session::models::{ AnalysisSession, ColumnFact, Deferral, DeferredAnalysis, DynamicFamily, ExpressionShape, @@ -117,6 +118,7 @@ type FinishRow = (Vec, Shapes, Vec, Vec); pub(crate) struct NativeModelAnalysisSession { inner: Option, failure: Option, + sharing: (usize, usize), } impl NativeModelAnalysisSession { @@ -143,6 +145,7 @@ impl NativeModelAnalysisSession { run_analysis_session(&mut session).map(|step| (session, step)) }); let (session, step) = self.kept(result)?; + self.sharing = session_sharing(&session); self.inner = Some(session); Some(step_row(step)) } @@ -172,6 +175,12 @@ impl NativeModelAnalysisSession { fn failure(&self) -> Option { self.failure.clone() } + + /// `(shared members, shared members re-analysed alone)` as of the last step. + #[getter] + fn sharing(&self) -> (usize, usize) { + self.sharing + } } /// Start a session on `catalog`, or None where Python must analyse. @@ -189,6 +198,7 @@ fn start_model_analysis_session( Ok(session.map(|session| NativeModelAnalysisSession { inner: Some(session), failure: None, + sharing: (0, 0), })) } diff --git a/src/sqlbuild/_native.pyi b/src/sqlbuild/_native.pyi index 5d194a0bf1..ccd6fb79b2 100644 --- a/src/sqlbuild/_native.pyi +++ b/src/sqlbuild/_native.pyi @@ -422,6 +422,8 @@ class NativeModelAnalysisSession: ): ... @property def failure(self) -> str | None: ... + @property + def sharing(self) -> tuple[int, int]: ... def start_model_analysis_session( catalog: object, request: tuple[object, ...], / diff --git a/tests/integration/src/sqlbuild/compiler/analysis_session/_test_types.py b/tests/integration/src/sqlbuild/compiler/analysis_session/_test_types.py index f3ea3a0aac..9ce474e84b 100644 --- a/tests/integration/src/sqlbuild/compiler/analysis_session/_test_types.py +++ b/tests/integration/src/sqlbuild/compiler/analysis_session/_test_types.py @@ -49,3 +49,17 @@ class StandalonePivotProofTestCase: analysed_models: frozenset[str] expected_native_proofs: int expected_proven_by_model: dict[str, bool | None] + + +@dataclass(frozen=True) +class SharedAnalysisTestCase: + """Models whose equal queries share one analysis, some inexact and so re-analysed alone.""" + + description: str + regions: tuple[str, ...] + inexact_regions: tuple[str, ...] + dialects: tuple[str | None, ...] + expected_analysed: int + expected_shared: int + expected_reanalysed: int + expected_unshared: int diff --git a/tests/integration/src/sqlbuild/compiler/analysis_session/helpers.py b/tests/integration/src/sqlbuild/compiler/analysis_session/helpers.py index 47095774c0..8115e45b60 100644 --- a/tests/integration/src/sqlbuild/compiler/analysis_session/helpers.py +++ b/tests/integration/src/sqlbuild/compiler/analysis_session/helpers.py @@ -349,6 +349,60 @@ def generated_analysis_files(*, rng: random.Random, model_count: int) -> dict[st return files +def shared_analysis_files( + *, regions: tuple[str, ...], inexact_regions: tuple[str, ...] +) -> dict[str, str]: + """Equal regional models and rollups, missing-column readers and one unshared summary.""" + + regional: dict[str, str] = { + f"models/staging/orders_{region}.sql": ( + f'MODEL (description "Orders in {region}");\n\n' + 'SELECT order_id, customer_id, amount FROM __source("raw_orders")\n' + ) + for region in regions + } + rollups: dict[str, str] = { + f"models/marts/totals_{region}.sql": ( + f'MODEL (description "Customer totals in {region}");\n\n' + "SELECT customer_id, SUM(amount) AS total_amount\n" + f'FROM __ref("orders_{region}")\nGROUP BY customer_id\n' + ) + for region in regions + } + inexact: dict[str, str] = { + f"models/marts/missing_{region}.sql": ( + f'MODEL (description "Orders in {region} with an unknown column");\n\n' + f'SELECT customer_id, missing_column FROM __ref("orders_{region}")\n' + ) + for region in inexact_regions + } + return { + "sqlbuild_project.toml": _PROJECT_TOML, + "sources/raw.yml": _SOURCES, + **regional, + **rollups, + **inexact, + "models/marts/orders_summary.sql": ( + 'MODEL (description "Order count");\n\n' + f'SELECT COUNT(*) AS order_count FROM __ref("orders_{regions[0]}")\n' + ), + } + + +def started_sessions(*, monkeypatch: pytest.MonkeyPatch) -> list[Any]: + """Record every native session started; return the list they are appended to.""" + + started: list[Any] = [] + original: Callable[..., Any] = native_module.start_model_analysis_session + + def start(catalog: object, request: tuple[object, ...]) -> Any: + started.append(original(catalog, request)) + return started[-1] + + monkeypatch.setattr(native_module, "start_model_analysis_session", start) + return started + + def compile_inputs(*, project_dir: Path, files: dict[str, str]) -> CompileProjectInputs: """Write a project and attach its compile inputs with the DuckDB compile context.""" diff --git a/tests/integration/src/sqlbuild/compiler/analysis_session/test_native_model_analysis.py b/tests/integration/src/sqlbuild/compiler/analysis_session/test_native_model_analysis.py index 4e9527ea64..37ca83b82c 100644 --- a/tests/integration/src/sqlbuild/compiler/analysis_session/test_native_model_analysis.py +++ b/tests/integration/src/sqlbuild/compiler/analysis_session/test_native_model_analysis.py @@ -27,6 +27,7 @@ AnalysisFallbackTestCase, GeneratedAnalysisParityTestCase, SessionFailureTestCase, + SharedAnalysisTestCase, StandalonePivotProofTestCase, ) from tests.integration.src.sqlbuild.compiler.analysis_session.helpers import ( @@ -40,6 +41,8 @@ generated_analysis_files, native_pivot_proofs, pivot_project_files, + shared_analysis_files, + started_sessions, ) from tests.integration.src.sqlbuild.compiler.helpers import mismatches @@ -105,6 +108,59 @@ def test_given_generated_projects_when_analysing_natively_then_matches_python( assert parity.native_enrichments >= test_case.expected_minimum_native_enrichments +@pytest.mark.parametrize( + "test_case", + [ + SharedAnalysisTestCase( + description="equal regional queries, missing-column readers and a unique summary", + regions=("east", "west", "north", "south"), + inexact_regions=("east", "west", "north"), + dialects=("duckdb", "snowflake"), + expected_analysed=24, + expected_shared=22, + expected_reanalysed=6, + expected_unshared=2, + ) + ], + ids=lambda case: case.description, +) +def test_given_equal_model_queries_when_analysing_natively_then_shares_and_matches_python( + test_case: SharedAnalysisTestCase, tmp_path: Path, monkeypatch: pytest.MonkeyPatch +) -> None: + record_dir: Path = tmp_path / "records" + monkeypatch.setenv(ANALYSIS_RECORD_DIR_ENV_VAR, str(record_dir)) + inputs: CompileProjectInputs = compile_inputs( + project_dir=tmp_path / "project", + files=shared_analysis_files( + regions=test_case.regions, inexact_regions=test_case.inexact_regions + ), + ) + sessions: list[object] = started_sessions(monkeypatch=monkeypatch) + parity: AnalysisParity = AnalysisParity() + + _ = [ + compare_analyses(inputs=inputs, dialect=dialect, parity=parity, monkeypatch=monkeypatch) + for dialect in test_case.dialects + ] + + shared: int = sum(getattr(session, "sharing")[0] for session in sessions) + + assert mismatches(inputs=parity.names, expected=parity.python, actual=parity.native) == [] + assert ( + parity.analysed_models, + shared, + sum(getattr(session, "sharing")[1] for session in sessions), + parity.analysed_models - shared, + deferral_kinds(record_dir), + ) == ( + test_case.expected_analysed, + test_case.expected_shared, + test_case.expected_reanalysed, + test_case.expected_unshared, + Counter(), + ) + + @pytest.mark.parametrize( "test_case", [ From e4da4130f76562373cf8b90e0a4caf6b80aa726b Mon Sep 17 00:00:00 2001 From: Kevin Longe <273747162+kvlonge@users.noreply.github.com> Date: Fri, 9 Oct 2026 18:28:01 +0100 Subject: [PATCH 22/31] test(perf): cover the model analysis CPU gate with compile arguments --- .../compile_performance_ratio/_test_types.py | 2 ++ .../test_compare_compile_performance.py | 16 +++++++++++++++- 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/tests/e2e/scripts/compile_performance_ratio/_test_types.py b/tests/e2e/scripts/compile_performance_ratio/_test_types.py index 4df5ed04c2..dec1ba1132 100644 --- a/tests/e2e/scripts/compile_performance_ratio/_test_types.py +++ b/tests/e2e/scripts/compile_performance_ratio/_test_types.py @@ -32,3 +32,5 @@ class EnginePhaseRatioTestCase: phase_noise_floor_ms: str expected_return_code: int expected_fragments: tuple[str, ...] + gate_phase: str = "contracts_cpu_ms" + compile_args: tuple[str, ...] = () diff --git a/tests/e2e/scripts/compile_performance_ratio/test_compare_compile_performance.py b/tests/e2e/scripts/compile_performance_ratio/test_compare_compile_performance.py index cfda3aaa37..e85352ce58 100644 --- a/tests/e2e/scripts/compile_performance_ratio/test_compare_compile_performance.py +++ b/tests/e2e/scripts/compile_performance_ratio/test_compare_compile_performance.py @@ -193,6 +193,19 @@ def test_given_base_root_when_comparing_compile_performance_then_each_side_uses_ "**Result: failed.**", ), ), + EnginePhaseRatioTestCase( + description="model analysis CPU over its allowance with rules off fails on it alone", + max_ratio="0", + phase_noise_floor_ms="-1", + expected_return_code=1, + expected_fragments=( + "Compile arguments: `--select=*`.", + "| model_analysis_cpu_ms |", + "Compile performance regression: dense 20 cold model_analysis_cpu_ms:", + ), + gate_phase="model_analysis_cpu_ms", + compile_args=("--compile-arg=--select=*",), + ), ), ids=lambda case: case.description, ) @@ -223,11 +236,12 @@ def test_given_two_engines_when_gating_a_phase_then_enforces_only_that_phase( "--head-engine", "native-preview", "--gate-phase", - "contracts_cpu_ms", + test_case.gate_phase, "--max-ratio", test_case.max_ratio, "--phase-noise-floor-ms", test_case.phase_noise_floor_ms, + *test_case.compile_args, ], cwd=_REPO_ROOT, capture_output=True, From 8e8de9104989c8095a9c6796aae4cabf6bc9c4d9 Mon Sep 17 00:00:00 2001 From: Kevin Longe <273747162+kvlonge@users.noreply.github.com> Date: Fri, 9 Oct 2026 18:59:20 +0100 Subject: [PATCH 23/31] refactor(native): name the sharing helpers fensu requires and keep the CPU timer domain-private --- .../_helpers/compact_batch.rs | 98 ++++++++++++------- .../compile/_helpers/assembly/project.py | 2 +- .../main/{record_cpu.py => _record_cpu.py} | 0 .../test_native_model_analysis.py | 7 +- 4 files changed, 67 insertions(+), 40 deletions(-) rename src/sqlbuild/compiler/profiling/main/{record_cpu.py => _record_cpu.py} (100%) diff --git a/crates/sqlbuild-analysis/src/assembly/analysis_session/_helpers/compact_batch.rs b/crates/sqlbuild-analysis/src/assembly/analysis_session/_helpers/compact_batch.rs index eb7b4b8365..4b5186a968 100644 --- a/crates/sqlbuild-analysis/src/assembly/analysis_session/_helpers/compact_batch.rs +++ b/crates/sqlbuild-analysis/src/assembly/analysis_session/_helpers/compact_batch.rs @@ -97,10 +97,7 @@ struct SharedQuery { impl SharedQuery { fn stub<'a>(&'a self, name: &'a str) -> &'a str { - self.stubs - .iter() - .find(|(reference, _)| reference == name) - .map_or(name, |(_, stub)| stub.as_str()) + stub_name(&self.stubs, name) } } @@ -190,12 +187,7 @@ impl SessionCatalog { let prekeys: Vec> = batch .members .iter() - .map(|member| { - member - .binding_schema - .map(|_| share_prekey(member)) - .transpose() - }) + .map(member_prekey) .collect::>()?; let mut prekey_counts: HashMap<&str, usize> = HashMap::new(); for prekey in prekeys.iter().flatten() { @@ -241,11 +233,10 @@ impl SessionCatalog { continue; }; let key: String = self.shared_key( - batch, + batch.dialect, &sql, &stubs, - schema, - batch.members[index].recover_cte_facts, + (schema, batch.members[index].recover_cte_facts), ); shared[index] = Some(SharedQuery { sql, stubs, key }); } @@ -255,11 +246,7 @@ impl SessionCatalog { } Ok(shared .into_iter() - .map(|query| { - query.filter(|query| { - key_counts.get(&query.key).copied().unwrap_or(0) >= MIN_SHARED_MEMBERS - }) - }) + .map(|query| with_partner(query, &key_counts)) .collect()) } @@ -277,10 +264,7 @@ impl SessionCatalog { .collect(); let folded: HashSet = names.iter().map(|name| name.to_lowercase()).collect(); let plain_text: bool = cleaned_sql.is_ascii() && names.iter().all(|name| name.is_ascii()); - let quoted: bool = cleaned_sql.contains('"') - || schema.iter().any(|(relation, columns)| { - relation.contains('"') || columns.iter().any(|(column, _)| column.contains('"')) - }); + let quoted: bool = cleaned_sql.contains('"') || schema_quoted(schema); let collides: bool = names .iter() .any(|name| name.starts_with(RELATION_STUB_PREFIX)) @@ -303,11 +287,10 @@ impl SessionCatalog { /// Python's `SharedBindingQuery.key`, with the CTE fact flag its query key adds. fn shared_key( &self, - batch: &Batch<'_>, + dialect: &str, sql: &str, stubs: &[(String, String)], - schema: &Shapes, - recover_cte_facts: bool, + (schema, recover_cte_facts): (&Shapes, bool), ) -> String { let analysis_shapes: Vec = stubs .iter() @@ -322,10 +305,7 @@ impl SessionCatalog { let mut binding_shapes: Vec = schema .iter() .map(|(relation, columns)| { - let alias: &str = stubs - .iter() - .find(|(name, _)| name == relation) - .map_or(relation.as_str(), |(_, stub)| stub.as_str()); + let alias: &str = stub_name(stubs, relation); json!([ alias, !columns.is_empty(), @@ -337,7 +317,7 @@ impl SessionCatalog { binding_shapes.sort_unstable(); json!([ sql, - batch.dialect, + dialect, analysis_shapes, binding_shapes, recover_cte_facts @@ -679,12 +659,7 @@ fn inexact_shared_members(response: &Value, payload: &BatchPayload) -> Vec = analysis - .as_array() - .and_then(|entry| entry.first()) - .and_then(Value::as_u64) - .and_then(|index| usize::try_from(index).ok()) - .and_then(|index| templates.get(index)); + let template: Option<&Value> = analysis_template(analysis, templates); let validation: Option<&Value> = validations.and_then(|validations| validations.get(**query)); !shared_result_is_exact(template, validation) @@ -694,6 +669,57 @@ fn inexact_shared_members(response: &Value, payload: &BatchPayload) -> Vec(analysis: &Value, templates: &'a [Value]) -> Option<&'a Value> { + let index: u64 = analysis.as_array()?.first()?.as_u64()?; + match usize::try_from(index) { + Ok(index) => templates.get(index), + Err(_) => None, + } +} + +/// `stubs`' stub for `name`, or `name` itself when it has none. +fn stub_name<'a>(stubs: &'a [(String, String)], name: &'a str) -> &'a str { + for (reference, stub) in stubs { + if reference == name { + return stub; + } + } + name +} + +/// A bound member's prekey; members without a binding schema never share. +fn member_prekey(member: &BatchMember<'_>) -> Result, String> { + match member.binding_schema { + Some(_) => share_prekey(member).map(Some), + None => Ok(None), + } +} + +/// `query` when another member shares its key. +fn with_partner( + query: Option, + counts: &HashMap, +) -> Option { + let query: SharedQuery = query?; + (counts.get(&query.key).copied().unwrap_or(0) >= MIN_SHARED_MEMBERS).then_some(query) +} + +/// Whether a binding relation or column name carries a double quote. +fn schema_quoted(schema: &Shapes) -> bool { + for (relation, columns) in schema { + if relation.contains('"') { + return true; + } + for (column, _) in columns { + if column.contains('"') { + return true; + } + } + } + false +} + /// Python's `shared_result_is_exact`: an analysed template without any binding diagnostic. fn shared_result_is_exact(template: Option<&Value>, validation: Option<&Value>) -> bool { let analysed: bool = template.is_some_and(|template| !template.is_string()); diff --git a/src/sqlbuild/compiler/compile/_helpers/assembly/project.py b/src/sqlbuild/compiler/compile/_helpers/assembly/project.py index 119e062eeb..d2d769c6cc 100644 --- a/src/sqlbuild/compiler/compile/_helpers/assembly/project.py +++ b/src/sqlbuild/compiler/compile/_helpers/assembly/project.py @@ -165,8 +165,8 @@ ) from sqlbuild.compiler.lineage.types import ColumnLineageMode, InferredNullability from sqlbuild.compiler.planner.types import ContractPolicy +from sqlbuild.compiler.profiling.main._record_cpu import record_compile_cpu_timing from sqlbuild.compiler.profiling.main.record import record_compile_timing -from sqlbuild.compiler.profiling.main.record_cpu import record_compile_cpu_timing from sqlbuild.compiler.project_assembly.models import NativeProjectResources from sqlbuild.compiler.references.types import SqlReferenceKind from sqlbuild.compiler.resource_names.main.function_node_type import function_node_type diff --git a/src/sqlbuild/compiler/profiling/main/record_cpu.py b/src/sqlbuild/compiler/profiling/main/_record_cpu.py similarity index 100% rename from src/sqlbuild/compiler/profiling/main/record_cpu.py rename to src/sqlbuild/compiler/profiling/main/_record_cpu.py diff --git a/tests/integration/src/sqlbuild/compiler/analysis_session/test_native_model_analysis.py b/tests/integration/src/sqlbuild/compiler/analysis_session/test_native_model_analysis.py index 37ca83b82c..34e3dfddb0 100644 --- a/tests/integration/src/sqlbuild/compiler/analysis_session/test_native_model_analysis.py +++ b/tests/integration/src/sqlbuild/compiler/analysis_session/test_native_model_analysis.py @@ -6,6 +6,7 @@ from collections import Counter from dataclasses import replace from pathlib import Path +from typing import Any import pytest @@ -135,7 +136,7 @@ def test_given_equal_model_queries_when_analysing_natively_then_shares_and_match regions=test_case.regions, inexact_regions=test_case.inexact_regions ), ) - sessions: list[object] = started_sessions(monkeypatch=monkeypatch) + sessions: list[Any] = started_sessions(monkeypatch=monkeypatch) parity: AnalysisParity = AnalysisParity() _ = [ @@ -143,13 +144,13 @@ def test_given_equal_model_queries_when_analysing_natively_then_shares_and_match for dialect in test_case.dialects ] - shared: int = sum(getattr(session, "sharing")[0] for session in sessions) + shared: int = sum(session.sharing[0] for session in sessions) assert mismatches(inputs=parity.names, expected=parity.python, actual=parity.native) == [] assert ( parity.analysed_models, shared, - sum(getattr(session, "sharing")[1] for session in sessions), + sum(session.sharing[1] for session in sessions), parity.analysed_models - shared, deferral_kinds(record_dir), ) == ( From 0bca3af90b5fd9996633e13a70bd18577a9dd1b1 Mon Sep 17 00:00:00 2001 From: Kevin Longe <273747162+kvlonge@users.noreply.github.com> Date: Fri, 9 Oct 2026 19:02:09 +0100 Subject: [PATCH 24/31] refactor(perf): share one clock-driven timer between wall and CPU compile phases --- .../compiler/profiling/_helpers/__init__.py | 1 + .../compiler/profiling/_helpers/timing.py | 23 +++++++++++++++++++ .../compiler/profiling/main/_record_cpu.py | 19 ++++----------- .../compiler/profiling/main/record.py | 19 ++++----------- 4 files changed, 32 insertions(+), 30 deletions(-) create mode 100644 src/sqlbuild/compiler/profiling/_helpers/__init__.py create mode 100644 src/sqlbuild/compiler/profiling/_helpers/timing.py diff --git a/src/sqlbuild/compiler/profiling/_helpers/__init__.py b/src/sqlbuild/compiler/profiling/_helpers/__init__.py new file mode 100644 index 0000000000..64fb62aa62 --- /dev/null +++ b/src/sqlbuild/compiler/profiling/_helpers/__init__.py @@ -0,0 +1 @@ +"""Native model analysis session helpers.""" diff --git a/src/sqlbuild/compiler/profiling/_helpers/timing.py b/src/sqlbuild/compiler/profiling/_helpers/timing.py new file mode 100644 index 0000000000..42ccefd697 --- /dev/null +++ b/src/sqlbuild/compiler/profiling/_helpers/timing.py @@ -0,0 +1,23 @@ +"""Compile phase timing by a chosen clock.""" + +from collections.abc import Callable, Iterator +from contextlib import contextmanager + +from sqlbuild.compiler.profiling.classes.context import CompileTimingContext +from sqlbuild.compiler.profiling.models import CompileTimingCollector +from sqlbuild.compiler.profiling.types import CompileTimingPhase + + +@contextmanager +def recorded_timing(*, phase: CompileTimingPhase, clock: Callable[[], int]) -> Iterator[None]: + """Record an operation by `clock` when a compile timing collector is active.""" + + collector: CompileTimingCollector | None = CompileTimingContext.active.get() + if collector is None: + yield + return + start_ns: int = clock() + try: + yield + finally: + collector.add(phase=phase, elapsed_ns=clock() - start_ns) diff --git a/src/sqlbuild/compiler/profiling/main/_record_cpu.py b/src/sqlbuild/compiler/profiling/main/_record_cpu.py index 18b1ab7aba..a0a15e0ef7 100644 --- a/src/sqlbuild/compiler/profiling/main/_record_cpu.py +++ b/src/sqlbuild/compiler/profiling/main/_record_cpu.py @@ -1,24 +1,13 @@ """Compile phase CPU recording entry point.""" import time -from collections.abc import Iterator -from contextlib import contextmanager +from contextlib import AbstractContextManager -from sqlbuild.compiler.profiling.classes.context import CompileTimingContext -from sqlbuild.compiler.profiling.models import CompileTimingCollector +from sqlbuild.compiler.profiling._helpers.timing import recorded_timing from sqlbuild.compiler.profiling.types import CompileTimingPhase -@contextmanager -def record_compile_cpu_timing(phase: CompileTimingPhase) -> Iterator[None]: +def record_compile_cpu_timing(phase: CompileTimingPhase) -> AbstractContextManager[None]: """Record the process CPU time an operation spans, including its worker threads.""" - collector: CompileTimingCollector | None = CompileTimingContext.active.get() - if collector is None: - yield - return - start_ns: int = time.process_time_ns() - try: - yield - finally: - collector.add(phase=phase, elapsed_ns=time.process_time_ns() - start_ns) + return recorded_timing(phase=phase, clock=time.process_time_ns) diff --git a/src/sqlbuild/compiler/profiling/main/record.py b/src/sqlbuild/compiler/profiling/main/record.py index 5518e5a413..4053321d80 100644 --- a/src/sqlbuild/compiler/profiling/main/record.py +++ b/src/sqlbuild/compiler/profiling/main/record.py @@ -1,24 +1,13 @@ """Compile phase recording entry point.""" import time -from collections.abc import Iterator -from contextlib import contextmanager +from contextlib import AbstractContextManager -from sqlbuild.compiler.profiling.classes.context import CompileTimingContext -from sqlbuild.compiler.profiling.models import CompileTimingCollector +from sqlbuild.compiler.profiling._helpers.timing import recorded_timing from sqlbuild.compiler.profiling.types import CompileTimingPhase -@contextmanager -def record_compile_timing(phase: CompileTimingPhase) -> Iterator[None]: +def record_compile_timing(phase: CompileTimingPhase) -> AbstractContextManager[None]: """Record an operation when a compile timing collector is active.""" - collector: CompileTimingCollector | None = CompileTimingContext.active.get() - if collector is None: - yield - return - start_ns: int = time.perf_counter_ns() - try: - yield - finally: - collector.add(phase=phase, elapsed_ns=time.perf_counter_ns() - start_ns) + return recorded_timing(phase=phase, clock=time.perf_counter_ns) From ca84d84d3668fc182293063df731988080048d43 Mon Sep 17 00:00:00 2001 From: Kevin Longe <273747162+kvlonge@users.noreply.github.com> Date: Fri, 9 Oct 2026 20:06:24 +0100 Subject: [PATCH 25/31] perf(native): share equal inferred columns across session analyses and guard custom rule CPU Native session analyses built a new InferredColumn per model column, while Python's compact analysis shares one object per distinct column. Custom-rule hosts therefore pickled and decoded more distinct objects under native-preview. Adds custom_rules_cpu_ms (rule thread plus host children CPU) and a same-runner ratio entry for it. --- .github/workflows/verify.yml | 6 +++++ .../compile_performance_ratio/constants.py | 1 + .../cli/commands/_helpers/compile/pipeline.py | 1 + .../cli/commands/main/project/_compile.py | 1 + src/sqlbuild/cli/compile/models.py | 1 + .../analysis_session/_helpers/session_rows.py | 24 +++++++++++++++---- .../classes/native_model_analysis.py | 7 ++++-- .../_helpers/engine/custom_rules.py | 14 +++++++++++ .../rule_engine/_helpers/engine/native.py | 1 + .../rule_engine/_helpers/run/rules.py | 1 + src/sqlbuild/rule_engine/models.py | 3 +++ .../test_compare_compile_performance.py | 12 ++++++++++ .../main/build/test_compile_json_behavior.py | 1 + .../compile/test_dense_compile_performance.py | 1 + .../compiler/analysis_session/_test_types.py | 1 + .../compiler/analysis_session/helpers.py | 12 ++++++++++ .../test_native_model_analysis.py | 6 +++++ 17 files changed, 87 insertions(+), 6 deletions(-) diff --git a/.github/workflows/verify.yml b/.github/workflows/verify.yml index 84833fa759..618f4e5128 100644 --- a/.github/workflows/verify.yml +++ b/.github/workflows/verify.yml @@ -611,6 +611,12 @@ jobs: gate_phase: model_analysis_cpu_ms compile_args: --compile-arg=--select=* max_ratio: "1.50" + - kind: dense + label: native-preview custom rules vs python + base_engine: python + head_engine: native-preview + gate_phase: custom_rules_cpu_ms + max_ratio: "1.05" steps: - uses: actions/checkout@v4 - name: Check out the comparison base diff --git a/scripts/compile_performance_ratio/constants.py b/scripts/compile_performance_ratio/constants.py index 0a365a0367..2c061917a9 100644 --- a/scripts/compile_performance_ratio/constants.py +++ b/scripts/compile_performance_ratio/constants.py @@ -53,6 +53,7 @@ "contracts_cpu_ms", "built_in_rules_ms", "custom_rules_ms", + "custom_rules_cpu_ms", "test_planning_ms", "write_ms", ) diff --git a/src/sqlbuild/cli/commands/_helpers/compile/pipeline.py b/src/sqlbuild/cli/commands/_helpers/compile/pipeline.py index 5ecad25968..c30b70555b 100644 --- a/src/sqlbuild/cli/commands/_helpers/compile/pipeline.py +++ b/src/sqlbuild/cli/commands/_helpers/compile/pipeline.py @@ -260,6 +260,7 @@ def _analyze_compile_project( contract_cpu_ms=contract_cpu_ms, built_in_rules_ms=rules_result.built_in_ms, custom_rules_ms=rules_result.custom_ms, + custom_rules_cpu_ms=rules_result.custom_cpu_ms, early_lint_wait_ms=early_lint_wait_ms, rule_cache_hits=rules_result.cache_hits, rule_cache_misses=rules_result.cache_misses, diff --git a/src/sqlbuild/cli/commands/main/project/_compile.py b/src/sqlbuild/cli/commands/main/project/_compile.py index 6ea2d58d06..6552c8e429 100644 --- a/src/sqlbuild/cli/commands/main/project/_compile.py +++ b/src/sqlbuild/cli/commands/main/project/_compile.py @@ -169,6 +169,7 @@ def _run_compile_with_status( "contracts_cpu_ms": analysis.contract_cpu_ms, "built_in_rules_ms": analysis.built_in_rules_ms, "custom_rules_ms": analysis.custom_rules_ms, + "custom_rules_cpu_ms": analysis.custom_rules_cpu_ms, "early_lint_wait_ms": analysis.early_lint_wait_ms, "rule_cache_hits": analysis.rule_cache_hits, "rule_cache_misses": analysis.rule_cache_misses, diff --git a/src/sqlbuild/cli/compile/models.py b/src/sqlbuild/cli/compile/models.py index 1a166ca13a..0f36a5373d 100644 --- a/src/sqlbuild/cli/compile/models.py +++ b/src/sqlbuild/cli/compile/models.py @@ -45,6 +45,7 @@ class CompileAnalysis: contract_cpu_ms: int built_in_rules_ms: int = 0 custom_rules_ms: int = 0 + custom_rules_cpu_ms: int = 0 early_lint_wait_ms: int = 0 rule_cache_hits: int = 0 rule_cache_misses: int = 0 diff --git a/src/sqlbuild/compiler/analysis_session/_helpers/session_rows.py b/src/sqlbuild/compiler/analysis_session/_helpers/session_rows.py index 093c361e6e..7a67564147 100644 --- a/src/sqlbuild/compiler/analysis_session/_helpers/session_rows.py +++ b/src/sqlbuild/compiler/analysis_session/_helpers/session_rows.py @@ -134,12 +134,28 @@ def deferred_row(*, model: int, analysis: PolyglotAnalysisResult) -> DeferredRow def inferred_columns(rows: list[ColumnRow] | None) -> tuple[InferredColumn, ...] | None: + return shared_inferred_columns(rows=rows, shared={}) + + +def shared_inferred_columns( + *, rows: list[ColumnRow] | None, shared: dict[ColumnRow, InferredColumn] +) -> tuple[InferredColumn, ...] | None: + """Columns as Python's analysis shares them: one object per distinct column value.""" + if rows is None: return None - return tuple( - InferredColumn(name=name, type=data_type, nullability=InferredNullability(nullability)) - for name, data_type, nullability in rows - ) + return tuple(_shared_column(row=row, shared=shared) for row in rows) + + +def _shared_column(*, row: ColumnRow, shared: dict[ColumnRow, InferredColumn]) -> InferredColumn: + column: InferredColumn | None = shared.get(row) + if column is None: + name, data_type, nullability = row + column = InferredColumn( + name=name, type=data_type, nullability=InferredNullability(nullability) + ) + shared[row] = column + return column def binding_diagnostics(rows: list[DiagnosticRow]) -> tuple[SqlBindingDiagnostic, ...]: diff --git a/src/sqlbuild/compiler/analysis_session/classes/native_model_analysis.py b/src/sqlbuild/compiler/analysis_session/classes/native_model_analysis.py index 8fa6049f45..8d37ffca70 100644 --- a/src/sqlbuild/compiler/analysis_session/classes/native_model_analysis.py +++ b/src/sqlbuild/compiler/analysis_session/classes/native_model_analysis.py @@ -14,8 +14,8 @@ compact_lineage, contract_proof, deferred_row, - inferred_columns, lineage_facts, + shared_inferred_columns, ) from sqlbuild.compiler.analysis_session.constants import ( CONTRACT_DEFERRED, @@ -31,6 +31,7 @@ ) from sqlbuild.compiler.analysis_session.models import NativeModelAnalysisRequest from sqlbuild.compiler.analysis_session.types import ( + ColumnRow, DeferralRow, DeferredRow, FinishRow, @@ -42,6 +43,7 @@ from sqlbuild.compiler.compile.classes.python_model_analysis import PythonModelAnalysis from sqlbuild.compiler.compile.models import ( CompiledLineageColumnFact, + InferredColumn, ModelSqlAnalysis, ModelSqlAnalysisRequest, PolyglotAnalysisResult, @@ -69,6 +71,7 @@ def __init__( request.column_nullability_by_table ) self._kept: dict[tuple[int, str], PolyglotAnalysisResult] = {} + self._columns: dict[ColumnRow, InferredColumn] = {} self._deferrals: Counter[str] = Counter() def analyses( @@ -205,7 +208,7 @@ def _model_analysis( return ModelSqlAnalysis( polyglot_analysis=PolyglotAnalysisResult( analysis_succeeded=succeeded, - columns=inferred_columns(columns), + columns=shared_inferred_columns(rows=columns, shared=self._columns), lineage_columns=lineage, has_star=has_star, star_resolved=star_resolved, diff --git a/src/sqlbuild/rule_engine/_helpers/engine/custom_rules.py b/src/sqlbuild/rule_engine/_helpers/engine/custom_rules.py index b806fb398c..ccd1fe39d2 100644 --- a/src/sqlbuild/rule_engine/_helpers/engine/custom_rules.py +++ b/src/sqlbuild/rule_engine/_helpers/engine/custom_rules.py @@ -80,6 +80,7 @@ def evaluate_custom_rules_cached( """Reuse results whose recorded fact reads are unchanged and evaluate only the rest.""" started: float = time.monotonic() + started_cpu: float = _cpu_seconds() custom: tuple[Rule, ...] = tuple(sorted(rules, key=lambda rule: rule.code)) if not custom: return CustomRulesOutcome(findings=(), cache_hits=0, cache_misses=0, custom_ms=0) @@ -105,6 +106,7 @@ def evaluate_custom_rules_cached( cache_hits=0, cache_misses=len(custom), custom_ms=_elapsed_ms(started), + custom_cpu_ms=_cpu_ms(started_cpu), ) digests: FactDigests = FactDigests( views=_views_factory(project=facts_project, project_dir=project_dir, dialect=dialect) @@ -185,6 +187,7 @@ def evaluate_custom_rules_cached( cache_hits=hits, cache_misses=misses, custom_ms=_elapsed_ms(started), + custom_cpu_ms=_cpu_ms(started_cpu), ) @@ -476,5 +479,16 @@ def _digest(value: object) -> str: return hashlib.sha256(orjson.dumps(value, default=str)).hexdigest() +def _cpu_seconds() -> float: + """This thread's CPU plus that of finished child processes, which run the rule hosts.""" + + times: os.times_result = os.times() + return time.thread_time() + times.children_user + times.children_system + + +def _cpu_ms(started: float) -> int: + return int((_cpu_seconds() - started) * 1000) + + def _elapsed_ms(started: float) -> int: return round((time.monotonic() - started) * 1000) diff --git a/src/sqlbuild/rule_engine/_helpers/engine/native.py b/src/sqlbuild/rule_engine/_helpers/engine/native.py index d9cd1a569b..a28072f54c 100644 --- a/src/sqlbuild/rule_engine/_helpers/engine/native.py +++ b/src/sqlbuild/rule_engine/_helpers/engine/native.py @@ -161,6 +161,7 @@ def evaluate_native( cache_misses=(0 if reused else native_misses) + custom.cache_misses, built_in_ms=0 if reused else int(payload.get("built_in_ms", 0)), custom_ms=custom.custom_ms, + custom_cpu_ms=custom.custom_cpu_ms, ) diff --git a/src/sqlbuild/rule_engine/_helpers/run/rules.py b/src/sqlbuild/rule_engine/_helpers/run/rules.py index 9f5b2c8c32..6b4ccd8567 100644 --- a/src/sqlbuild/rule_engine/_helpers/run/rules.py +++ b/src/sqlbuild/rule_engine/_helpers/run/rules.py @@ -231,6 +231,7 @@ def evaluate_rules( unevaluated_resources=len({item.path for item in findings if item.unevaluated}), built_in_ms=sql_ms + result.built_in_ms, custom_ms=result.custom_ms, + custom_cpu_ms=result.custom_cpu_ms, cache_hits=result.cache_hits + sql_result.cache_hits, cache_misses=result.cache_misses + sql_result.cache_misses, skipped_type_proof_rules=skipped_type_proof_rules, diff --git a/src/sqlbuild/rule_engine/models.py b/src/sqlbuild/rule_engine/models.py index 0915f9ccfb..98831bae24 100644 --- a/src/sqlbuild/rule_engine/models.py +++ b/src/sqlbuild/rule_engine/models.py @@ -311,6 +311,7 @@ class RulesResult: cache_misses: int = 0 built_in_ms: int = 0 custom_ms: int = 0 + custom_cpu_ms: int = 0 @dataclass(frozen=True) @@ -346,6 +347,7 @@ class RulesRunResult: evaluated_models: int built_in_ms: int custom_ms: int + custom_cpu_ms: int = 0 unevaluated_resources: int = 0 cache_hits: int = 0 cache_misses: int = 0 @@ -431,6 +433,7 @@ class CustomRulesOutcome: cache_hits: int cache_misses: int custom_ms: int + custom_cpu_ms: int = 0 @dataclass(frozen=True) diff --git a/tests/e2e/scripts/compile_performance_ratio/test_compare_compile_performance.py b/tests/e2e/scripts/compile_performance_ratio/test_compare_compile_performance.py index e85352ce58..827a663c6c 100644 --- a/tests/e2e/scripts/compile_performance_ratio/test_compare_compile_performance.py +++ b/tests/e2e/scripts/compile_performance_ratio/test_compare_compile_performance.py @@ -206,6 +206,18 @@ def test_given_base_root_when_comparing_compile_performance_then_each_side_uses_ gate_phase="model_analysis_cpu_ms", compile_args=("--compile-arg=--select=*",), ), + EnginePhaseRatioTestCase( + description="custom rule CPU over its allowance fails on it alone", + max_ratio="0", + phase_noise_floor_ms="-1", + expected_return_code=1, + expected_fragments=( + "Gated phases: custom_rules_cpu_ms.", + "| custom_rules_cpu_ms |", + "Compile performance regression: dense 20 cold custom_rules_cpu_ms:", + ), + gate_phase="custom_rules_cpu_ms", + ), ), ids=lambda case: case.description, ) diff --git a/tests/e2e/src/sqlbuild/cli/commands/main/build/test_compile_json_behavior.py b/tests/e2e/src/sqlbuild/cli/commands/main/build/test_compile_json_behavior.py index c7b7680d7c..cbdbaa803f 100644 --- a/tests/e2e/src/sqlbuild/cli/commands/main/build/test_compile_json_behavior.py +++ b/tests/e2e/src/sqlbuild/cli/commands/main/build/test_compile_json_behavior.py @@ -71,6 +71,7 @@ def test_given_waffle_shop_when_running_compile_json_then_it_reports_offline_que "attachment_ms", "built_in_rules_ms", "custom_rules_ms", + "custom_rules_cpu_ms", "early_lint_wait_ms", "model_analysis_ms", "model_analysis_cpu_ms", diff --git a/tests/e2e/src/sqlbuild/cli/commands/main/compile/test_dense_compile_performance.py b/tests/e2e/src/sqlbuild/cli/commands/main/compile/test_dense_compile_performance.py index 606879ed41..dec0bf7ea8 100644 --- a/tests/e2e/src/sqlbuild/cli/commands/main/compile/test_dense_compile_performance.py +++ b/tests/e2e/src/sqlbuild/cli/commands/main/compile/test_dense_compile_performance.py @@ -125,6 +125,7 @@ def test_given_dense_project_when_compiling_cold_then_preserves_rules_semantics_ assert timings["rule_cache_misses"] == 2 * test_case.model_count + 1 assert timings["built_in_rules_ms"] > 0 assert timings["custom_rules_ms"] > 0 + assert timings["custom_rules_cpu_ms"] > 0 assert result.semantic_fingerprint == test_case.expected_fingerprint assert result.elapsed_seconds < test_case.expected_max_wall_seconds assert result.peak_rss_bytes < test_case.expected_max_rss_bytes diff --git a/tests/integration/src/sqlbuild/compiler/analysis_session/_test_types.py b/tests/integration/src/sqlbuild/compiler/analysis_session/_test_types.py index 9ce474e84b..42890b9cfb 100644 --- a/tests/integration/src/sqlbuild/compiler/analysis_session/_test_types.py +++ b/tests/integration/src/sqlbuild/compiler/analysis_session/_test_types.py @@ -63,3 +63,4 @@ class SharedAnalysisTestCase: expected_shared: int expected_reanalysed: int expected_unshared: int + expected_column_values: int diff --git a/tests/integration/src/sqlbuild/compiler/analysis_session/helpers.py b/tests/integration/src/sqlbuild/compiler/analysis_session/helpers.py index 8115e45b60..843644cb7a 100644 --- a/tests/integration/src/sqlbuild/compiler/analysis_session/helpers.py +++ b/tests/integration/src/sqlbuild/compiler/analysis_session/helpers.py @@ -40,6 +40,7 @@ CompiledLineageColumnFact, CompileProjectInputs, DynamicColumnContractProof, + InferredColumn, ModelSqlAnalysis, ) from sqlbuild.compiler.discovery.main.discover import discover_project_inputs @@ -430,6 +431,8 @@ class AnalysisParity: standalone_proofs: int = 0 proven_pivots: int = 0 native_enrichments: int = 0 + native_column_objects: int = 0 + native_column_values: int = 0 def compare_analyses( @@ -459,6 +462,11 @@ def models_by_both( ) ) python: dict[str, ModelSqlAnalysis] = python_analysis.func(**keywords) + columns: list[InferredColumn] = list( + chain.from_iterable(map(_native_columns, (native or {}).values())) + ) + parity.native_column_objects += len(set(map(id, columns))) + parity.native_column_values += len(set(columns)) parity.analysed_models += len(python) _append(parity, "model analyses", _analysis_views(python), _analysis_views(native)) _append( @@ -569,6 +577,10 @@ def deferral_kinds(record_dir: Path) -> Counter[str]: return Counter(f"{record['site']}:{record['kind']}" for record in records) +def _native_columns(analysis: ModelSqlAnalysis) -> tuple[InferredColumn, ...]: + return analysis.polyglot_analysis.columns or () + + def _append(parity: AnalysisParity, name: str, python: object, native: object) -> None: parity.names.append(name) parity.python.append(python) diff --git a/tests/integration/src/sqlbuild/compiler/analysis_session/test_native_model_analysis.py b/tests/integration/src/sqlbuild/compiler/analysis_session/test_native_model_analysis.py index 34e3dfddb0..39642d496e 100644 --- a/tests/integration/src/sqlbuild/compiler/analysis_session/test_native_model_analysis.py +++ b/tests/integration/src/sqlbuild/compiler/analysis_session/test_native_model_analysis.py @@ -107,6 +107,7 @@ def test_given_generated_projects_when_analysing_natively_then_matches_python( <= test_case.expected_maximum_enrichment_deferrals ) assert parity.native_enrichments >= test_case.expected_minimum_native_enrichments + assert parity.native_column_objects == parity.native_column_values > 0 @pytest.mark.parametrize( @@ -121,6 +122,7 @@ def test_given_generated_projects_when_analysing_natively_then_matches_python( expected_shared=22, expected_reanalysed=6, expected_unshared=2, + expected_column_values=12, ) ], ids=lambda case: case.description, @@ -153,12 +155,16 @@ def test_given_equal_model_queries_when_analysing_natively_then_shares_and_match sum(session.sharing[1] for session in sessions), parity.analysed_models - shared, deferral_kinds(record_dir), + parity.native_column_objects, + parity.native_column_values, ) == ( test_case.expected_analysed, test_case.expected_shared, test_case.expected_reanalysed, test_case.expected_unshared, Counter(), + test_case.expected_column_values, + test_case.expected_column_values, ) From 10fc7719b5d23d91f20769e6dab84dcfab75ffa9 Mon Sep 17 00:00:00 2001 From: Kevin Longe <273747162+kvlonge@users.noreply.github.com> Date: Fri, 9 Oct 2026 20:14:59 +0100 Subject: [PATCH 26/31] refactor(native): keep the shared column memo on the session analysis --- .../analysis_session/_helpers/session_rows.py | 24 ++++--------------- .../classes/native_model_analysis.py | 20 ++++++++++++++-- 2 files changed, 22 insertions(+), 22 deletions(-) diff --git a/src/sqlbuild/compiler/analysis_session/_helpers/session_rows.py b/src/sqlbuild/compiler/analysis_session/_helpers/session_rows.py index 7a67564147..093c361e6e 100644 --- a/src/sqlbuild/compiler/analysis_session/_helpers/session_rows.py +++ b/src/sqlbuild/compiler/analysis_session/_helpers/session_rows.py @@ -134,28 +134,12 @@ def deferred_row(*, model: int, analysis: PolyglotAnalysisResult) -> DeferredRow def inferred_columns(rows: list[ColumnRow] | None) -> tuple[InferredColumn, ...] | None: - return shared_inferred_columns(rows=rows, shared={}) - - -def shared_inferred_columns( - *, rows: list[ColumnRow] | None, shared: dict[ColumnRow, InferredColumn] -) -> tuple[InferredColumn, ...] | None: - """Columns as Python's analysis shares them: one object per distinct column value.""" - if rows is None: return None - return tuple(_shared_column(row=row, shared=shared) for row in rows) - - -def _shared_column(*, row: ColumnRow, shared: dict[ColumnRow, InferredColumn]) -> InferredColumn: - column: InferredColumn | None = shared.get(row) - if column is None: - name, data_type, nullability = row - column = InferredColumn( - name=name, type=data_type, nullability=InferredNullability(nullability) - ) - shared[row] = column - return column + return tuple( + InferredColumn(name=name, type=data_type, nullability=InferredNullability(nullability)) + for name, data_type, nullability in rows + ) def binding_diagnostics(rows: list[DiagnosticRow]) -> tuple[SqlBindingDiagnostic, ...]: diff --git a/src/sqlbuild/compiler/analysis_session/classes/native_model_analysis.py b/src/sqlbuild/compiler/analysis_session/classes/native_model_analysis.py index 8d37ffca70..86dd27bf9e 100644 --- a/src/sqlbuild/compiler/analysis_session/classes/native_model_analysis.py +++ b/src/sqlbuild/compiler/analysis_session/classes/native_model_analysis.py @@ -15,7 +15,6 @@ contract_proof, deferred_row, lineage_facts, - shared_inferred_columns, ) from sqlbuild.compiler.analysis_session.constants import ( CONTRACT_DEFERRED, @@ -179,6 +178,23 @@ def _record_catalog_changes( }, ) + def _inferred_columns(self, rows: list[ColumnRow] | None) -> tuple[InferredColumn, ...] | None: + """Columns as Python's analysis shares them: one object per distinct column value.""" + + if rows is None: + return None + return tuple(map(self._inferred_column, rows)) + + def _inferred_column(self, row: ColumnRow) -> InferredColumn: + column: InferredColumn | None = self._columns.get(row) + if column is None: + name, data_type, nullability = row + column = InferredColumn( + name=name, type=data_type, nullability=InferredNullability(nullability) + ) + self._columns[row] = column + return column + def _model_analysis( self, *, @@ -208,7 +224,7 @@ def _model_analysis( return ModelSqlAnalysis( polyglot_analysis=PolyglotAnalysisResult( analysis_succeeded=succeeded, - columns=shared_inferred_columns(rows=columns, shared=self._columns), + columns=self._inferred_columns(columns), lineage_columns=lineage, has_star=has_star, star_resolved=star_resolved, From bd20f844a5b39af02735e2cc85f6cea51bc11e18 Mon Sep 17 00:00:00 2001 From: Kevin Longe <273747162+kvlonge@users.noreply.github.com> Date: Fri, 9 Oct 2026 20:21:22 +0100 Subject: [PATCH 27/31] ci: gate native-preview custom rules CPU only against gross regressions --- .github/workflows/verify.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/verify.yml b/.github/workflows/verify.yml index 618f4e5128..85713175bf 100644 --- a/.github/workflows/verify.yml +++ b/.github/workflows/verify.yml @@ -616,7 +616,7 @@ jobs: base_engine: python head_engine: native-preview gate_phase: custom_rules_cpu_ms - max_ratio: "1.05" + max_ratio: "1.25" steps: - uses: actions/checkout@v4 - name: Check out the comparison base From e54140107bcc6c4a5cb75d2fbb2c9a4034ae6971 Mon Sep 17 00:00:00 2001 From: Kevin Longe <273747162+kvlonge@users.noreply.github.com> Date: Fri, 9 Oct 2026 23:02:53 +0100 Subject: [PATCH 28/31] chore(native): drop imports the combined analysis session no longer uses --- .../src/assembly/analysis_session/_helpers/dynamic_pivot.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/crates/sqlbuild-analysis/src/assembly/analysis_session/_helpers/dynamic_pivot.rs b/crates/sqlbuild-analysis/src/assembly/analysis_session/_helpers/dynamic_pivot.rs index a8fc19c020..fe152f03ee 100644 --- a/crates/sqlbuild-analysis/src/assembly/analysis_session/_helpers/dynamic_pivot.rs +++ b/crates/sqlbuild-analysis/src/assembly/analysis_session/_helpers/dynamic_pivot.rs @@ -3,10 +3,9 @@ use std::collections::{HashMap, HashSet}; use std::panic::{AssertUnwindSafe, catch_unwind}; -use polyglot_sql::{ComplexityGuardOptions, Dialect, Expression, ParseOptions}; use rayon::ThreadPool; use rayon::iter::{IntoParallelRefIterator, ParallelIterator}; -use serde_json::{Map, Value, json}; +use serde_json::{Map, Value}; use sqlbuild_core::panics::main::catch_compiler_panic::catch_compiler_panic; use crate::assembly::analysis_session::_helpers::dict_walk::{ From e87407a77228ce803d5d21c25b7b66db202e605c Mon Sep 17 00:00:00 2001 From: Kevin Longe <273747162+kvlonge@users.noreply.github.com> Date: Fri, 9 Oct 2026 23:28:58 +0100 Subject: [PATCH 29/31] test(native): record baselines for the combined M4 batch --- .../native_fallbacks.toml | 90 +++-- .../test_native_fallback_gate.py | 4 +- ...analysis-session-cte-fact-nullability.json | 309 +++++++++++++++ ...alysis-session-cte-fact-type-mismatch.json | 309 +++++++++++++++ ...c-recovered-uses-through-intermediate.json | 355 ++++++++++++++++++ ...st-glue-mock-reads-referencing-helper.json | 284 ++++++++++++++ tests/goldens/compiler/seed/0.json | 120 ++++++ tests/goldens/compiler/seed/10.json | 120 ++++++ tests/goldens/compiler/seed/6.json | 120 ++++++ 9 files changed, 1671 insertions(+), 40 deletions(-) create mode 100644 tests/goldens/compiler/failure/analysis-session-cte-fact-nullability.json create mode 100644 tests/goldens/compiler/failure/analysis-session-cte-fact-type-mismatch.json create mode 100644 tests/goldens/compiler/failure/semantic-recovered-uses-through-intermediate.json create mode 100644 tests/goldens/compiler/failure/sql-test-glue-mock-reads-referencing-helper.json diff --git a/scripts/compiler_differential/native_fallbacks.toml b/scripts/compiler_differential/native_fallbacks.toml index db49cefc10..6d596e22db 100644 --- a/scripts/compiler_differential/native_fallbacks.toml +++ b/scripts/compiler_differential/native_fallbacks.toml @@ -25,7 +25,7 @@ engine = "native" stage = "attachments" site = "attachments.native" kind = "cursor_intrinsic_rejections" -counts = { example = 160, failure = 172, fixture = 156, seed = 961 } +counts = { example = 160, failure = 177, fixture = 156, seed = 961 } [[entry]] engine = "native" @@ -39,7 +39,7 @@ engine = "native" stage = "attachments" site = "attachments.native" kind = "omitted_select_scans" -counts = { example = 24, failure = 21, fixture = 32, seed = 193 } +counts = { example = 24, failure = 22, fixture = 32, seed = 193 } [[entry]] engine = "native" @@ -88,14 +88,14 @@ engine = "native" stage = "declaration_scopes" site = "declaration_scopes.native" kind = "scope_indexes" -counts = { example = 12, failure = 225, fixture = 12, seed = 44 } +counts = { example = 12, failure = 229, fixture = 12, seed = 44 } [[entry]] engine = "native" stage = "declaration_scopes" site = "declaration_scopes.rebind_lookup" kind = "deferred" -counts = { failure = 1, seed = 781 } +counts = { failure = 1, seed = 802 } [[entry]] engine = "native" @@ -109,7 +109,7 @@ engine = "native" stage = "discovery" site = "discovery.native" kind = "model_file_listings" -counts = { example = 12, failure = 290, fixture = 12, seed = 50 } +counts = { example = 12, failure = 294, fixture = 12, seed = 50 } [[entry]] engine = "native" @@ -165,14 +165,14 @@ engine = "native" stage = "model_config" site = "model_config.native" kind = "config_builds" -counts = { example = 56, failure = 304, fixture = 64, seed = 890 } +counts = { example = 56, failure = 310, fixture = 64, seed = 914 } [[entry]] engine = "native" stage = "model_config" site = "model_config.native" kind = "validations" -counts = { example = 56, failure = 374, fixture = 60, seed = 1091 } +counts = { example = 56, failure = 383, fixture = 60, seed = 1115 } [[entry]] engine = "native" @@ -186,21 +186,21 @@ engine = "native" stage = "model_config" site = "model_config.python_templates" kind = "config" -counts = { example = 12, failure = 115, fixture = 8, seed = 36 } +counts = { example = 12, failure = 119, fixture = 8, seed = 36 } [[entry]] engine = "native" stage = "model_config" site = "model_config.python_templates" kind = "connection" -counts = { example = 18, failure = 117, fixture = 9, seed = 63 } +counts = { example = 18, failure = 121, fixture = 9, seed = 63 } [[entry]] engine = "native" stage = "model_config" site = "model_config.python_templates" kind = "effective_vars" -counts = { example = 29, failure = 240, fixture = 15, seed = 121 } +counts = { example = 29, failure = 244, fixture = 15, seed = 121 } [[entry]] engine = "native" @@ -228,7 +228,7 @@ engine = "native" stage = "model_loop" site = "model_loop.cursor_intrinsic_validation" kind = "deferred" -counts = { example = 56, failure = 404, fixture = 64, seed = 1100 } +counts = { example = 56, failure = 413, fixture = 64, seed = 1124 } [[entry]] engine = "native" @@ -249,14 +249,14 @@ engine = "native" stage = "model_loop" site = "model_loop.native" kind = "declaration_expansions" -counts = { example = 56, failure = 415, fixture = 64, seed = 1100 } +counts = { example = 56, failure = 424, fixture = 64, seed = 1124 } [[entry]] engine = "native" stage = "model_loop" site = "model_loop.native" kind = "variable_substitutions" -counts = { example = 56, failure = 457, fixture = 72, seed = 1157 } +counts = { example = 56, failure = 466, fixture = 72, seed = 1181 } [[entry]] engine = "native" @@ -277,7 +277,7 @@ engine = "native" stage = "reference_extraction" site = "reference_extraction.native" kind = "reference_scans" -counts = { example = 250, failure = 466, fixture = 249, seed = 1523 } +counts = { example = 250, failure = 479, fixture = 249, seed = 1532 } [[entry]] engine = "native-preview" @@ -293,6 +293,13 @@ site = "project_assembly" kind = "unsupported_dialect" counts = { failure = 1 } +[[entry]] +engine = "native-preview" +stage = "analysis" +site = "sql_test_assembly" +kind = "macro_mocks" +counts = { seed = 29 } + [[entry]] engine = "native-preview" stage = "attachments" @@ -312,7 +319,7 @@ engine = "native-preview" stage = "attachments" site = "attachments.native" kind = "cursor_intrinsic_rejections" -counts = { example = 160, failure = 172, fixture = 156, seed = 961 } +counts = { example = 160, failure = 177, fixture = 156, seed = 961 } [[entry]] engine = "native-preview" @@ -326,7 +333,7 @@ engine = "native-preview" stage = "attachments" site = "attachments.native" kind = "omitted_select_scans" -counts = { example = 24, failure = 21, fixture = 32, seed = 193 } +counts = { example = 24, failure = 22, fixture = 32, seed = 193 } [[entry]] engine = "native-preview" @@ -354,14 +361,14 @@ engine = "native-preview" stage = "contracts" site = "contracts.native" kind = "contract_validations" -counts = { example = 9, failure = 109, fixture = 6, seed = 25 } +counts = { example = 9, failure = 113, fixture = 6, seed = 25 } [[entry]] engine = "native-preview" stage = "contracts" site = "contracts.native" kind = "promotion_checks" -counts = { example = 12, failure = 109, fixture = 8, seed = 33 } +counts = { example = 12, failure = 113, fixture = 8, seed = 33 } [[entry]] engine = "native-preview" @@ -389,14 +396,14 @@ engine = "native-preview" stage = "declaration_scopes" site = "declaration_scopes.native" kind = "scope_indexes" -counts = { example = 12, failure = 225, fixture = 12, seed = 44 } +counts = { example = 12, failure = 229, fixture = 12, seed = 44 } [[entry]] engine = "native-preview" stage = "declaration_scopes" site = "declaration_scopes.rebind_lookup" kind = "deferred" -counts = { failure = 1, seed = 781 } +counts = { failure = 1, seed = 802 } [[entry]] engine = "native-preview" @@ -410,14 +417,14 @@ engine = "native-preview" stage = "discovery" site = "discovery.native" kind = "model_file_listings" -counts = { example = 12, failure = 290, fixture = 12, seed = 50 } +counts = { example = 12, failure = 294, fixture = 12, seed = 50 } [[entry]] engine = "native-preview" stage = "lineage_facts" site = "lineage_facts.native" kind = "lineage_graphs" -counts = { example = 9, failure = 109, fixture = 6, seed = 22 } +counts = { example = 9, failure = 113, fixture = 6, seed = 22 } [[entry]] engine = "native-preview" @@ -480,14 +487,14 @@ engine = "native-preview" stage = "model_analysis" site = "model_analysis.native" kind = "expression_shapes" -counts = { example = 4, failure = 115, fixture = 4, seed = 100 } +counts = { example = 4, failure = 119, fixture = 4, seed = 100 } [[entry]] engine = "native-preview" stage = "model_analysis" site = "model_analysis.native" kind = "model_analyses" -counts = { example = 56, failure = 223, fixture = 52, seed = 686 } +counts = { example = 56, failure = 232, fixture = 52, seed = 698 } [[entry]] engine = "native-preview" @@ -508,14 +515,14 @@ engine = "native-preview" stage = "model_config" site = "model_config.native" kind = "config_builds" -counts = { example = 56, failure = 304, fixture = 64, seed = 890 } +counts = { example = 56, failure = 310, fixture = 64, seed = 914 } [[entry]] engine = "native-preview" stage = "model_config" site = "model_config.native" kind = "validations" -counts = { example = 56, failure = 374, fixture = 60, seed = 1091 } +counts = { example = 56, failure = 383, fixture = 60, seed = 1115 } [[entry]] engine = "native-preview" @@ -529,21 +536,21 @@ engine = "native-preview" stage = "model_config" site = "model_config.python_templates" kind = "config" -counts = { example = 12, failure = 115, fixture = 8, seed = 36 } +counts = { example = 12, failure = 119, fixture = 8, seed = 36 } [[entry]] engine = "native-preview" stage = "model_config" site = "model_config.python_templates" kind = "connection" -counts = { example = 18, failure = 117, fixture = 9, seed = 63 } +counts = { example = 18, failure = 121, fixture = 9, seed = 63 } [[entry]] engine = "native-preview" stage = "model_config" site = "model_config.python_templates" kind = "effective_vars" -counts = { example = 29, failure = 240, fixture = 15, seed = 121 } +counts = { example = 29, failure = 244, fixture = 15, seed = 121 } [[entry]] engine = "native-preview" @@ -557,7 +564,7 @@ engine = "native-preview" stage = "model_loop" site = "model_loop.cursor_intrinsic_validation" kind = "deferred" -counts = { example = 56, failure = 404, fixture = 64, seed = 1100 } +counts = { example = 56, failure = 413, fixture = 64, seed = 1124 } [[entry]] engine = "native-preview" @@ -578,14 +585,14 @@ engine = "native-preview" stage = "model_loop" site = "model_loop.native" kind = "declaration_expansions" -counts = { example = 56, failure = 415, fixture = 64, seed = 1100 } +counts = { example = 56, failure = 424, fixture = 64, seed = 1124 } [[entry]] engine = "native-preview" stage = "model_loop" site = "model_loop.native" kind = "variable_substitutions" -counts = { example = 56, failure = 457, fixture = 72, seed = 1157 } +counts = { example = 56, failure = 466, fixture = 72, seed = 1181 } [[entry]] engine = "native-preview" @@ -613,39 +620,46 @@ engine = "native-preview" stage = "project_assembly" site = "project_assembly.native" kind = "project_assemblies" -counts = { example = 12, failure = 111, fixture = 8, seed = 36 } +counts = { example = 12, failure = 115, fixture = 8, seed = 36 } [[entry]] engine = "native-preview" stage = "reference_extraction" site = "reference_extraction.native" kind = "reference_scans" -counts = { example = 250, failure = 466, fixture = 249, seed = 1523 } +counts = { example = 250, failure = 477, fixture = 249, seed = 1532 } [[entry]] engine = "native-preview" stage = "semantic_checks" site = "semantic_checks.native" kind = "semantic_completions" -counts = { example = 12, failure = 111, fixture = 8, seed = 36 } +counts = { example = 12, failure = 115, fixture = 8, seed = 36 } [[entry]] engine = "native-preview" stage = "sql_test_glue" site = "sql_test_glue.assembly" kind = "deferred" -counts = { example = 12, failure = 111, fixture = 8, seed = 36 } +counts = { seed = 29 } + +[[entry]] +engine = "native-preview" +stage = "sql_test_glue" +site = "sql_test_glue.native" +kind = "sql_test_assemblies" +counts = { example = 16, failure = 7, fixture = 20, seed = 143 } [[entry]] engine = "native-preview" stage = "sql_test_glue" site = "sql_test_glue.native" kind = "sql_test_plans" -counts = { example = 1, failure = 4, fixture = 1, seed = 9 } +counts = { example = 1, failure = 5, fixture = 1, seed = 9 } [[entry]] engine = "python" stage = "discovery" site = "discovery.native" kind = "model_file_listings" -counts = { example = 12, failure = 290, fixture = 12, seed = 50 } +counts = { example = 12, failure = 294, fixture = 12, seed = 50 } diff --git a/tests/e2e/scripts/compiler_differential/test_native_fallback_gate.py b/tests/e2e/scripts/compiler_differential/test_native_fallback_gate.py index 822f9e1a20..45e8ceccec 100644 --- a/tests/e2e/scripts/compiler_differential/test_native_fallback_gate.py +++ b/tests/e2e/scripts/compiler_differential/test_native_fallback_gate.py @@ -153,8 +153,8 @@ def test_given_recorded_allow_list_when_native_defers_more_or_less_then_the_gate appended_entries="", expected_exit_code=1, expected_lines=( - "Native fallback allow-list: native-preview sql_test_glue sql_test_glue.assembly " - f"deferred (fixture): {_FALLBACK_WITHOUT_ANSWERS}", + "Native fallback allow-list: native-preview sql_test_glue sql_test_glue.native " + f"sql_test_assemblies (fixture): {_ANSWER_VANISHED}", "Native fallback allow-list: native-preview sql_test_glue sql_test_glue.native " f"sql_test_plans (fixture): {_ANSWER_VANISHED}", "Native fallback allow-list: 2 problems", diff --git a/tests/goldens/compiler/failure/analysis-session-cte-fact-nullability.json b/tests/goldens/compiler/failure/analysis-session-cte-fact-nullability.json new file mode 100644 index 0000000000..501ce3c08c --- /dev/null +++ b/tests/goldens/compiler/failure/analysis-session-cte-fact-nullability.json @@ -0,0 +1,309 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "contract", + "severity": "error", + "code": "K004", + "message": "column 'note' is declared non-null but may be nullable", + "resource_type": "model", + "resource_name": "customer_totals", + "column_name": "note", + "path": "models/marts/customer_totals.sql", + "line": 6, + "column": 5, + "location": { + "path": "models/marts/customer_totals.sql", + "line": 6, + "column": 5, + "end_line": 6, + "end_column": 9 + }, + "related_locations": [ + { + "label": "output", + "location": { + "path": "models/marts/customer_totals.sql", + "line": 14, + "column": 40, + "end_line": 14, + "end_column": 44 + }, + "message": "output expression proven nullable" + } + ], + "help": "use COALESCE, filter nulls explicitly, or remove the non-null contract" + } + ] + } + ], + "compiled": { + "models/marts/customer_totals.sql": [ + "WITH base AS (", + " SELECT customer_id, CAST(NULL AS VARCHAR) AS note", + " FROM __ref(\"stg_orders\")", + ")", + "SELECT customer_id + 0 AS customer_id, note FROM base", + "" + ], + "models/staging/stg_orders.sql": [ + "SELECT order_id, customer_id, amount, status", + "FROM __source(\"raw_orders\")", + "" + ] + }, + "manifest": { + "nodes": { + "model.failure_corpus.customer_totals": { + "database": null, + "schema": "main", + "name": "customer_totals", + "resource_type": "model", + "package_name": "failure_corpus", + "path": "models/marts/customer_totals.sql", + "original_file_path": "models/marts/customer_totals.sql", + "unique_id": "model.failure_corpus.customer_totals", + "fqn": [ + "failure_corpus", + "models", + "marts", + "customer_totals" + ], + "alias": "customer_totals", + "config": { + "enabled": true, + "alias": null, + "schema": null, + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Order totals per customer", + "columns": { + "customer_id": { + "name": "customer_id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "note": { + "name": "note", + "description": "", + "meta": {}, + "data_type": "VARCHAR", + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.customer_totals", + "depends_on": { + "macros": [], + "nodes": [ + "model.failure_corpus.stg_orders" + ] + } + }, + "model.failure_corpus.stg_orders": { + "database": null, + "schema": "main", + "name": "stg_orders", + "resource_type": "model", + "package_name": "failure_corpus", + "path": "models/staging/stg_orders.sql", + "original_file_path": "models/staging/stg_orders.sql", + "unique_id": "model.failure_corpus.stg_orders", + "fqn": [ + "failure_corpus", + "models", + "staging", + "stg_orders" + ], + "alias": "stg_orders", + "config": { + "enabled": true, + "alias": null, + "schema": null, + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Staged orders", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.stg_orders", + "depends_on": { + "macros": [], + "nodes": [ + "source.failure_corpus.raw_orders" + ] + } + } + }, + "sources": { + "source.failure_corpus.raw_orders": { + "database": null, + "schema": "", + "name": "raw_orders", + "resource_type": "source", + "package_name": "failure_corpus", + "path": "sources/raw.yml", + "original_file_path": "sources/raw.yml", + "unique_id": "source.failure_corpus.raw_orders", + "fqn": [ + "failure_corpus", + "raw_orders" + ], + "source_name": "raw_orders", + "source_description": "", + "loader": "", + "identifier": "raw_orders", + "quoting": {}, + "loaded_at_field": null, + "freshness": null, + "external": null, + "description": "Orders feed.", + "columns": { + "order_id": { + "name": "order_id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "customer_id": { + "name": "customer_id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "amount": { + "name": "amount", + "description": "", + "meta": {}, + "data_type": "DOUBLE", + "constraints": [], + "quote": null, + "tags": [] + }, + "status": { + "name": "status", + "description": "", + "meta": {}, + "data_type": "VARCHAR", + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": {}, + "source_meta": {}, + "tags": [], + "config": {}, + "patch_path": null, + "unrendered_config": {}, + "relation_name": null + } + }, + "macros": {}, + "exposures": {}, + "metrics": {}, + "groups": {}, + "selectors": {}, + "disabled": {}, + "parent_map": { + "model.failure_corpus.customer_totals": [ + "model.failure_corpus.stg_orders" + ], + "model.failure_corpus.stg_orders": [ + "source.failure_corpus.raw_orders" + ], + "source.failure_corpus.raw_orders": [] + }, + "group_map": {}, + "docs": {}, + "saved_queries": {}, + "semantic_models": {}, + "unit_tests": {} + } +} diff --git a/tests/goldens/compiler/failure/analysis-session-cte-fact-type-mismatch.json b/tests/goldens/compiler/failure/analysis-session-cte-fact-type-mismatch.json new file mode 100644 index 0000000000..3e09c1bcdc --- /dev/null +++ b/tests/goldens/compiler/failure/analysis-session-cte-fact-type-mismatch.json @@ -0,0 +1,309 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "contract", + "severity": "error", + "code": "K002", + "message": "column 'total_amount' inferred as DOUBLE but declared type is VARCHAR", + "resource_type": "model", + "resource_name": "customer_totals", + "column_name": "total_amount", + "path": "models/marts/customer_totals.sql", + "line": 6, + "column": 5, + "location": { + "path": "models/marts/customer_totals.sql", + "line": 6, + "column": 5, + "end_line": 6, + "end_column": 17 + }, + "related_locations": [ + { + "label": "output", + "location": { + "path": "models/marts/customer_totals.sql", + "line": 14, + "column": 40, + "end_line": 14, + "end_column": 52 + }, + "message": "inferred DOUBLE" + } + ], + "help": "change the declared type or cast the expression explicitly" + } + ] + } + ], + "compiled": { + "models/marts/customer_totals.sql": [ + "WITH totals AS (", + " SELECT customer_id, CAST(amount AS DOUBLE) AS total_amount", + " FROM __ref(\"stg_orders\")", + ")", + "SELECT customer_id + 0 AS customer_id, total_amount FROM totals", + "" + ], + "models/staging/stg_orders.sql": [ + "SELECT order_id, customer_id, amount, status", + "FROM __source(\"raw_orders\")", + "" + ] + }, + "manifest": { + "nodes": { + "model.failure_corpus.customer_totals": { + "database": null, + "schema": "main", + "name": "customer_totals", + "resource_type": "model", + "package_name": "failure_corpus", + "path": "models/marts/customer_totals.sql", + "original_file_path": "models/marts/customer_totals.sql", + "unique_id": "model.failure_corpus.customer_totals", + "fqn": [ + "failure_corpus", + "models", + "marts", + "customer_totals" + ], + "alias": "customer_totals", + "config": { + "enabled": true, + "alias": null, + "schema": null, + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Order totals per customer", + "columns": { + "customer_id": { + "name": "customer_id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "total_amount": { + "name": "total_amount", + "description": "", + "meta": {}, + "data_type": "VARCHAR", + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.customer_totals", + "depends_on": { + "macros": [], + "nodes": [ + "model.failure_corpus.stg_orders" + ] + } + }, + "model.failure_corpus.stg_orders": { + "database": null, + "schema": "main", + "name": "stg_orders", + "resource_type": "model", + "package_name": "failure_corpus", + "path": "models/staging/stg_orders.sql", + "original_file_path": "models/staging/stg_orders.sql", + "unique_id": "model.failure_corpus.stg_orders", + "fqn": [ + "failure_corpus", + "models", + "staging", + "stg_orders" + ], + "alias": "stg_orders", + "config": { + "enabled": true, + "alias": null, + "schema": null, + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Staged orders", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.stg_orders", + "depends_on": { + "macros": [], + "nodes": [ + "source.failure_corpus.raw_orders" + ] + } + } + }, + "sources": { + "source.failure_corpus.raw_orders": { + "database": null, + "schema": "", + "name": "raw_orders", + "resource_type": "source", + "package_name": "failure_corpus", + "path": "sources/raw.yml", + "original_file_path": "sources/raw.yml", + "unique_id": "source.failure_corpus.raw_orders", + "fqn": [ + "failure_corpus", + "raw_orders" + ], + "source_name": "raw_orders", + "source_description": "", + "loader": "", + "identifier": "raw_orders", + "quoting": {}, + "loaded_at_field": null, + "freshness": null, + "external": null, + "description": "Orders feed.", + "columns": { + "order_id": { + "name": "order_id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "customer_id": { + "name": "customer_id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "amount": { + "name": "amount", + "description": "", + "meta": {}, + "data_type": "DOUBLE", + "constraints": [], + "quote": null, + "tags": [] + }, + "status": { + "name": "status", + "description": "", + "meta": {}, + "data_type": "VARCHAR", + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": {}, + "source_meta": {}, + "tags": [], + "config": {}, + "patch_path": null, + "unrendered_config": {}, + "relation_name": null + } + }, + "macros": {}, + "exposures": {}, + "metrics": {}, + "groups": {}, + "selectors": {}, + "disabled": {}, + "parent_map": { + "model.failure_corpus.customer_totals": [ + "model.failure_corpus.stg_orders" + ], + "model.failure_corpus.stg_orders": [ + "source.failure_corpus.raw_orders" + ], + "source.failure_corpus.raw_orders": [] + }, + "group_map": {}, + "docs": {}, + "saved_queries": {}, + "semantic_models": {}, + "unit_tests": {} + } +} diff --git a/tests/goldens/compiler/failure/semantic-recovered-uses-through-intermediate.json b/tests/goldens/compiler/failure/semantic-recovered-uses-through-intermediate.json new file mode 100644 index 0000000000..ed37acb33b --- /dev/null +++ b/tests/goldens/compiler/failure/semantic-recovered-uses-through-intermediate.json @@ -0,0 +1,355 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "B002", + "message": "Unknown column 'amonut' in raw_orders", + "resource_type": "model", + "resource_name": "stg_orders", + "path": "models/staging/stg_orders.sql", + "line": 5, + "column": 31, + "location": { + "path": "models/staging/stg_orders.sql", + "line": 5, + "column": 31, + "end_line": 5, + "end_column": 37 + }, + "help": "did you mean 'amount'?", + "notes": [ + "raw_orders has: amount, status, customer_id, order_id", + "1 downstream uses of stg_orders.amount were not checked because of this error" + ] + } + ] + } + ], + "compiled": { + "models/intermediate/int_orders.sql": [ + "SELECT customer_id, amount * 2 AS doubled_amount", + "FROM __ref(\"stg_orders\")", + "" + ], + "models/marts/customer_totals.sql": [ + "SELECT customer_id, SUM(doubled_amount) AS total_amount", + "FROM __ref(\"int_orders\")", + "GROUP BY customer_id", + "" + ], + "models/staging/stg_orders.sql": [ + "SELECT order_id, customer_id, amonut, status", + "FROM __source(\"raw_orders\")", + "" + ] + }, + "manifest": { + "nodes": { + "model.failure_corpus.int_orders": { + "database": null, + "schema": "main", + "name": "int_orders", + "resource_type": "model", + "package_name": "failure_corpus", + "path": "models/intermediate/int_orders.sql", + "original_file_path": "models/intermediate/int_orders.sql", + "unique_id": "model.failure_corpus.int_orders", + "fqn": [ + "failure_corpus", + "models", + "intermediate", + "int_orders" + ], + "alias": "int_orders", + "config": { + "enabled": true, + "alias": null, + "schema": null, + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Doubled order amounts", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.int_orders", + "depends_on": { + "macros": [], + "nodes": [ + "model.failure_corpus.stg_orders" + ] + } + }, + "model.failure_corpus.customer_totals": { + "database": null, + "schema": "main", + "name": "customer_totals", + "resource_type": "model", + "package_name": "failure_corpus", + "path": "models/marts/customer_totals.sql", + "original_file_path": "models/marts/customer_totals.sql", + "unique_id": "model.failure_corpus.customer_totals", + "fqn": [ + "failure_corpus", + "models", + "marts", + "customer_totals" + ], + "alias": "customer_totals", + "config": { + "enabled": true, + "alias": null, + "schema": null, + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Order totals per customer", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.customer_totals", + "depends_on": { + "macros": [], + "nodes": [ + "model.failure_corpus.int_orders" + ] + } + }, + "model.failure_corpus.stg_orders": { + "database": null, + "schema": "main", + "name": "stg_orders", + "resource_type": "model", + "package_name": "failure_corpus", + "path": "models/staging/stg_orders.sql", + "original_file_path": "models/staging/stg_orders.sql", + "unique_id": "model.failure_corpus.stg_orders", + "fqn": [ + "failure_corpus", + "models", + "staging", + "stg_orders" + ], + "alias": "stg_orders", + "config": { + "enabled": true, + "alias": null, + "schema": null, + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Staged orders", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.stg_orders", + "depends_on": { + "macros": [], + "nodes": [ + "source.failure_corpus.raw_orders" + ] + } + } + }, + "sources": { + "source.failure_corpus.raw_orders": { + "database": null, + "schema": "", + "name": "raw_orders", + "resource_type": "source", + "package_name": "failure_corpus", + "path": "sources/raw.yml", + "original_file_path": "sources/raw.yml", + "unique_id": "source.failure_corpus.raw_orders", + "fqn": [ + "failure_corpus", + "raw_orders" + ], + "source_name": "raw_orders", + "source_description": "", + "loader": "", + "identifier": "raw_orders", + "quoting": {}, + "loaded_at_field": null, + "freshness": null, + "external": null, + "description": "Orders feed.", + "columns": { + "order_id": { + "name": "order_id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "customer_id": { + "name": "customer_id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "amount": { + "name": "amount", + "description": "", + "meta": {}, + "data_type": "DOUBLE", + "constraints": [], + "quote": null, + "tags": [] + }, + "status": { + "name": "status", + "description": "", + "meta": {}, + "data_type": "VARCHAR", + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": {}, + "source_meta": {}, + "tags": [], + "config": {}, + "patch_path": null, + "unrendered_config": {}, + "relation_name": null + } + }, + "macros": {}, + "exposures": {}, + "metrics": {}, + "groups": {}, + "selectors": {}, + "disabled": {}, + "parent_map": { + "model.failure_corpus.int_orders": [ + "model.failure_corpus.stg_orders" + ], + "model.failure_corpus.customer_totals": [ + "model.failure_corpus.int_orders" + ], + "model.failure_corpus.stg_orders": [ + "source.failure_corpus.raw_orders" + ], + "source.failure_corpus.raw_orders": [] + }, + "group_map": {}, + "docs": {}, + "saved_queries": {}, + "semantic_models": {}, + "unit_tests": {} + } +} diff --git a/tests/goldens/compiler/failure/sql-test-glue-mock-reads-referencing-helper.json b/tests/goldens/compiler/failure/sql-test-glue-mock-reads-referencing-helper.json new file mode 100644 index 0000000000..66df657efc --- /dev/null +++ b/tests/goldens/compiler/failure/sql-test-glue-mock-reads-referencing-helper.json @@ -0,0 +1,284 @@ +{ + "commands": [ + { + "label": "compile", + "exit_code": 1, + "diagnostics": [ + { + "phase": "compile", + "severity": "error", + "code": "P013", + "message": "SQL test mock '__ref__stg_orders' reads helper CTE 'orders_feed', which calls __source(\"raw_orders\"); mocks and fixtures are defined before the models the test runs, so the helper cannot be resolved for them", + "resource_type": "sql_test", + "resource_name": "mock_reads_helper", + "path": "tests/unit/test_customer_totals.sql", + "line": 4, + "column": 31, + "location": { + "path": "tests/unit/test_customer_totals.sql", + "line": 4, + "column": 31, + "end_line": 4, + "end_column": 53 + }, + "help": "Read a mock by its CTE name instead, for example FROM __source__raw_orders rather than FROM __source(\"raw_orders\"), defining __source__raw_orders AS (SELECT ...) if the test does not mock it, or write the rows of '__ref__stg_orders' directly." + } + ] + } + ], + "compiled": { + "models/marts/customer_totals.sql": [ + "SELECT customer_id, SUM(amount) AS total_amount", + "FROM __ref(\"stg_orders\")", + "GROUP BY customer_id", + "" + ], + "models/staging/stg_orders.sql": [ + "SELECT order_id, customer_id, amount, status", + "FROM __source(\"raw_orders\")", + "" + ], + "tests/customer_totals/mock_reads_helper.sql": [ + "WITH __ref__stg_orders AS (SELECT * FROM orders_feed),", + "__ref__customer_totals AS (SELECT customer_id, SUM(amount) AS total_amount", + "FROM __ref__stg_orders", + "GROUP BY customer_id),", + "__actual__customer_totals AS (SELECT * FROM __ref__customer_totals),", + "__expected__customer_totals AS (SELECT 10 AS customer_id, 5.0 AS total_amount)", + "SELECT 0 AS step_index, 'customer_totals' AS model_name, (SELECT COUNT(*) FROM __actual__customer_totals) AS actual_count, (SELECT COUNT(*) FROM __expected__customer_totals) AS expected_count, (SELECT COUNT(*) FROM (SELECT customer_id, total_amount FROM __actual__customer_totals EXCEPT SELECT customer_id, total_amount FROM __expected__customer_totals) AS __sqlbuild_mismatch) AS unexpected_count, (SELECT COUNT(*) FROM (SELECT customer_id, total_amount FROM __expected__customer_totals EXCEPT SELECT customer_id, total_amount FROM __actual__customer_totals) AS __sqlbuild_missing) AS missing_count", + "" + ] + }, + "manifest": { + "nodes": { + "model.failure_corpus.customer_totals": { + "database": null, + "schema": "main", + "name": "customer_totals", + "resource_type": "model", + "package_name": "failure_corpus", + "path": "models/marts/customer_totals.sql", + "original_file_path": "models/marts/customer_totals.sql", + "unique_id": "model.failure_corpus.customer_totals", + "fqn": [ + "failure_corpus", + "models", + "marts", + "customer_totals" + ], + "alias": "customer_totals", + "config": { + "enabled": true, + "alias": null, + "schema": null, + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Order totals per customer", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.customer_totals", + "depends_on": { + "macros": [], + "nodes": [ + "model.failure_corpus.stg_orders" + ] + } + }, + "model.failure_corpus.stg_orders": { + "database": null, + "schema": "main", + "name": "stg_orders", + "resource_type": "model", + "package_name": "failure_corpus", + "path": "models/staging/stg_orders.sql", + "original_file_path": "models/staging/stg_orders.sql", + "unique_id": "model.failure_corpus.stg_orders", + "fqn": [ + "failure_corpus", + "models", + "staging", + "stg_orders" + ], + "alias": "stg_orders", + "config": { + "enabled": true, + "alias": null, + "schema": null, + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "view", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Staged orders", + "columns": {}, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.stg_orders", + "depends_on": { + "macros": [], + "nodes": [ + "source.failure_corpus.raw_orders" + ] + } + } + }, + "sources": { + "source.failure_corpus.raw_orders": { + "database": null, + "schema": "", + "name": "raw_orders", + "resource_type": "source", + "package_name": "failure_corpus", + "path": "sources/raw.yml", + "original_file_path": "sources/raw.yml", + "unique_id": "source.failure_corpus.raw_orders", + "fqn": [ + "failure_corpus", + "raw_orders" + ], + "source_name": "raw_orders", + "source_description": "", + "loader": "", + "identifier": "raw_orders", + "quoting": {}, + "loaded_at_field": null, + "freshness": null, + "external": null, + "description": "Orders feed.", + "columns": { + "order_id": { + "name": "order_id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "customer_id": { + "name": "customer_id", + "description": "", + "meta": {}, + "data_type": "INTEGER", + "constraints": [], + "quote": null, + "tags": [] + }, + "amount": { + "name": "amount", + "description": "", + "meta": {}, + "data_type": "DOUBLE", + "constraints": [], + "quote": null, + "tags": [] + }, + "status": { + "name": "status", + "description": "", + "meta": {}, + "data_type": "VARCHAR", + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": {}, + "source_meta": {}, + "tags": [], + "config": {}, + "patch_path": null, + "unrendered_config": {}, + "relation_name": null + } + }, + "macros": {}, + "exposures": {}, + "metrics": {}, + "groups": {}, + "selectors": {}, + "disabled": {}, + "parent_map": { + "model.failure_corpus.customer_totals": [ + "model.failure_corpus.stg_orders" + ], + "model.failure_corpus.stg_orders": [ + "source.failure_corpus.raw_orders" + ], + "source.failure_corpus.raw_orders": [] + }, + "group_map": {}, + "docs": {}, + "saved_queries": {}, + "semantic_models": {}, + "unit_tests": {} + } +} diff --git a/tests/goldens/compiler/seed/0.json b/tests/goldens/compiler/seed/0.json index 28b8246e6b..3446f321b7 100644 --- a/tests/goldens/compiler/seed/0.json +++ b/tests/goldens/compiler/seed/0.json @@ -145,6 +145,17 @@ ")", "" ], + "models/analysis/cte_facts_stg_fulfillment_1.sql": [ + "WITH base AS (", + " SELECT id, CAST(amount AS DECIMAL(12, 2)) AS amount, 'event' AS kind,", + " COALESCE(status, 'unknown') AS status", + " FROM __ref(\"stg_fulfillment_1\")", + " WHERE id IS NOT NULL", + ")", + "SELECT id + 0 AS id, amount, UPPER(kind) AS kind, UPPER(status) AS status", + "FROM base", + "" + ], "models/analysis/pivot_input_stg_sales_0.sql": [ "SELECT CAST(id AS INTEGER) AS id, CAST(status AS VARCHAR) AS status, amount", "FROM __ref(\"stg_sales_0\")", @@ -456,6 +467,112 @@ }, "manifest": { "nodes": { + "model.generated_0.cte_facts_stg_fulfillment_1": { + "database": null, + "schema": "ci_checks", + "name": "cte_facts_stg_fulfillment_1", + "resource_type": "model", + "package_name": "generated_0", + "path": "models/analysis/cte_facts_stg_fulfillment_1.sql", + "original_file_path": "models/analysis/cte_facts_stg_fulfillment_1.sql", + "unique_id": "model.generated_0.cte_facts_stg_fulfillment_1", + "fqn": [ + "generated_0", + "models", + "analysis", + "cte_facts_stg_fulfillment_1" + ], + "alias": "cte_facts_stg_fulfillment_1", + "config": { + "enabled": true, + "alias": null, + "schema": "ci_checks", + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "table", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Contracted CTE reads of stg_fulfillment_1", + "columns": { + "id": { + "name": "id", + "description": "Recovered id", + "meta": {}, + "data_type": null, + "constraints": [], + "quote": null, + "tags": [] + }, + "amount": { + "name": "amount", + "description": "Recovered amount", + "meta": {}, + "data_type": null, + "constraints": [], + "quote": null, + "tags": [] + }, + "kind": { + "name": "kind", + "description": "Recovered kind", + "meta": {}, + "data_type": null, + "constraints": [], + "quote": null, + "tags": [] + }, + "status": { + "name": "status", + "description": "Recovered status", + "meta": {}, + "data_type": null, + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "ci_checks.cte_facts_stg_fulfillment_1", + "depends_on": { + "macros": [], + "nodes": [ + "model.generated_0.stg_fulfillment_1" + ] + } + }, "model.generated_0.pivot_input_stg_sales_0": { "database": null, "schema": "ci_checks", @@ -3363,6 +3480,9 @@ "selectors": {}, "disabled": {}, "parent_map": { + "model.generated_0.cte_facts_stg_fulfillment_1": [ + "model.generated_0.stg_fulfillment_1" + ], "model.generated_0.pivot_input_stg_sales_0": [ "model.generated_0.stg_sales_0" ], diff --git a/tests/goldens/compiler/seed/10.json b/tests/goldens/compiler/seed/10.json index e5d7177d6c..4ca491f751 100644 --- a/tests/goldens/compiler/seed/10.json +++ b/tests/goldens/compiler/seed/10.json @@ -149,6 +149,17 @@ "FROM __source(\"raw_events_1\")", "" ], + "models/analysis/cte_facts_stg_sales_0.sql": [ + "WITH base AS (", + " SELECT id, CAST(amount AS DECIMAL(12, 2)) AS amount, 'event' AS kind,", + " COALESCE(status, 'unknown') AS status", + " FROM __ref(\"stg_sales_0\")", + " WHERE id IS NOT NULL", + ")", + "SELECT id + 0 AS id, amount, UPPER(kind) AS kind, UPPER(status) AS status", + "FROM base", + "" + ], "models/analysis/regional_stg_inventory_0.sql": [ "SELECT id, amount, 'north' AS region", "FROM __ref(\"stg_inventory_0\")", @@ -769,6 +780,112 @@ ] } }, + "model.generated_10.cte_facts_stg_sales_0": { + "database": null, + "schema": "main", + "name": "cte_facts_stg_sales_0", + "resource_type": "model", + "package_name": "generated_10", + "path": "models/analysis/cte_facts_stg_sales_0.sql", + "original_file_path": "models/analysis/cte_facts_stg_sales_0.sql", + "unique_id": "model.generated_10.cte_facts_stg_sales_0", + "fqn": [ + "generated_10", + "models", + "analysis", + "cte_facts_stg_sales_0" + ], + "alias": "cte_facts_stg_sales_0", + "config": { + "enabled": true, + "alias": null, + "schema": "main", + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "table", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Contracted CTE reads of stg_sales_0", + "columns": { + "id": { + "name": "id", + "description": "Recovered id", + "meta": {}, + "data_type": null, + "constraints": [], + "quote": null, + "tags": [] + }, + "amount": { + "name": "amount", + "description": "Recovered amount", + "meta": {}, + "data_type": null, + "constraints": [], + "quote": null, + "tags": [] + }, + "kind": { + "name": "kind", + "description": "Recovered kind", + "meta": {}, + "data_type": null, + "constraints": [], + "quote": null, + "tags": [] + }, + "status": { + "name": "status", + "description": "Recovered status", + "meta": {}, + "data_type": null, + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.cte_facts_stg_sales_0", + "depends_on": { + "macros": [], + "nodes": [ + "model.generated_10.stg_sales_0" + ] + } + }, "model.generated_10.regional_stg_inventory_0": { "database": null, "schema": "main", @@ -3208,6 +3325,9 @@ "model.generated_10.contracted_stg_sales_0": [ "source.generated_10.raw_events_1" ], + "model.generated_10.cte_facts_stg_sales_0": [ + "model.generated_10.stg_sales_0" + ], "model.generated_10.regional_stg_inventory_0": [ "model.generated_10.stg_inventory_0" ], diff --git a/tests/goldens/compiler/seed/6.json b/tests/goldens/compiler/seed/6.json index 55ea99ab86..98fe263c63 100644 --- a/tests/goldens/compiler/seed/6.json +++ b/tests/goldens/compiler/seed/6.json @@ -154,6 +154,17 @@ "FROM __source(\"raw_events_0\")", "" ], + "models/analysis/cte_facts_stg_fulfillment_1.sql": [ + "WITH base AS (", + " SELECT id, CAST(amount AS DECIMAL(12, 2)) AS amount, 'event' AS kind,", + " COALESCE(status, 'unknown') AS status", + " FROM __ref(\"stg_fulfillment_1\")", + " WHERE id IS NOT NULL", + ")", + "SELECT id + 0 AS id, amount, UPPER(kind) AS kind, UPPER(status) AS status", + "FROM base", + "" + ], "models/analysis/regional_stg_fulfillment_0.sql": [ "SELECT id, amount, 'north' AS region", "FROM __ref(\"stg_fulfillment_0\")", @@ -794,6 +805,112 @@ ] } }, + "model.generated_6.cte_facts_stg_fulfillment_1": { + "database": null, + "schema": "main", + "name": "cte_facts_stg_fulfillment_1", + "resource_type": "model", + "package_name": "generated_6", + "path": "models/analysis/cte_facts_stg_fulfillment_1.sql", + "original_file_path": "models/analysis/cte_facts_stg_fulfillment_1.sql", + "unique_id": "model.generated_6.cte_facts_stg_fulfillment_1", + "fqn": [ + "generated_6", + "models", + "analysis", + "cte_facts_stg_fulfillment_1" + ], + "alias": "cte_facts_stg_fulfillment_1", + "config": { + "enabled": true, + "alias": null, + "schema": "main", + "database": null, + "tags": [], + "meta": {}, + "group": null, + "materialized": "table", + "incremental_strategy": null, + "merge_exclude_columns": null, + "persist_docs": {}, + "post-hook": [], + "pre-hook": [], + "quoting": {}, + "column_types": {}, + "full_refresh": null, + "unique_key": null, + "on_schema_change": "ignore", + "on_configuration_change": "apply", + "grants": {}, + "packages": [], + "docs": { + "show": true, + "node_color": null + }, + "contract": { + "enforced": false, + "alias_types": true + } + }, + "tags": [], + "description": "Contracted CTE reads of stg_fulfillment_1", + "columns": { + "id": { + "name": "id", + "description": "Recovered id", + "meta": {}, + "data_type": null, + "constraints": [], + "quote": null, + "tags": [] + }, + "amount": { + "name": "amount", + "description": "Recovered amount", + "meta": {}, + "data_type": null, + "constraints": [], + "quote": null, + "tags": [] + }, + "kind": { + "name": "kind", + "description": "Recovered kind", + "meta": {}, + "data_type": null, + "constraints": [], + "quote": null, + "tags": [] + }, + "status": { + "name": "status", + "description": "Recovered status", + "meta": {}, + "data_type": null, + "constraints": [], + "quote": null, + "tags": [] + } + }, + "meta": {}, + "group": null, + "docs": { + "show": true, + "node_color": null + }, + "patch_path": null, + "build_path": null, + "unrendered_config": {}, + "config_call_dict": {}, + "unrendered_config_call_dict": {}, + "relation_name": "main.cte_facts_stg_fulfillment_1", + "depends_on": { + "macros": [], + "nodes": [ + "model.generated_6.stg_fulfillment_1" + ] + } + }, "model.generated_6.regional_stg_fulfillment_0": { "database": null, "schema": "main", @@ -3140,6 +3257,9 @@ "model.generated_6.contracted_stg_fulfillment_2": [ "source.generated_6.raw_events_0" ], + "model.generated_6.cte_facts_stg_fulfillment_1": [ + "model.generated_6.stg_fulfillment_1" + ], "model.generated_6.regional_stg_fulfillment_0": [ "model.generated_6.stg_fulfillment_0" ], From 0f0a01ff7f1b42524eb9a7d5fdfb6d6b70179352 Mon Sep 17 00:00:00 2001 From: Kevin Longe <273747162+kvlonge@users.noreply.github.com> Date: Fri, 9 Oct 2026 23:33:50 +0100 Subject: [PATCH 30/31] refactor(native): move SQL test scope dependencies out of project assembly --- .../compile/_helpers/assembly/project.py | 65 +++--------------- .../compile/_helpers/sql_tests/scope_deps.py | 67 +++++++++++++++++++ 2 files changed, 75 insertions(+), 57 deletions(-) create mode 100644 src/sqlbuild/compiler/compile/_helpers/sql_tests/scope_deps.py diff --git a/src/sqlbuild/compiler/compile/_helpers/assembly/project.py b/src/sqlbuild/compiler/compile/_helpers/assembly/project.py index 1e15c794b5..aa496d3302 100644 --- a/src/sqlbuild/compiler/compile/_helpers/assembly/project.py +++ b/src/sqlbuild/compiler/compile/_helpers/assembly/project.py @@ -112,6 +112,11 @@ report_mocks_reading_referencing_helpers, ) from sqlbuild.compiler.compile._helpers.sql_tests.identity import build_sql_test_case_fingerprint +from sqlbuild.compiler.compile._helpers.sql_tests.scope_deps import ( + function_sql_test_scope_deps, + macro_sql_test_scope_deps, + udf_sql_test_scope_deps, +) from sqlbuild.compiler.compile.main._scope_index_with_compile_usages import ( scope_index_with_compile_usages, ) @@ -178,7 +183,6 @@ NativeProjectFacts, NativeProjectResources, ) -from sqlbuild.compiler.references.types import SqlReferenceKind from sqlbuild.compiler.resource_names.main.function_node_type import function_node_type from sqlbuild.compiler.scopes.models import ScopeIndex from sqlbuild.compiler.sql_analysis.constants import ( @@ -1731,17 +1735,17 @@ def _assemble_compiled_sql_test( target_model_names: tuple[str, ...] = () if isinstance(test_input.payload, CompileDirectLogicSqlTestInputPayload): if test_input.payload.mode == SqlTestMode.MACRO: - scope_deps = _macro_sql_test_scope_deps( + scope_deps = macro_sql_test_scope_deps( tested_macro_names=test_input.payload.tested_resource_names, model_inputs=model_inputs, ) elif test_input.payload.mode == SqlTestMode.UDF: - scope_deps = _udf_sql_test_scope_deps( + scope_deps = udf_sql_test_scope_deps( tested_udf_names=test_input.payload.tested_resource_names, model_inputs=model_inputs, ) else: - scope_deps = _function_sql_test_scope_deps( + scope_deps = function_sql_test_scope_deps( tested_function_names=test_input.payload.tested_resource_names, ) compiled_payload = CompiledDirectLogicSqlTestPayload( @@ -1862,59 +1866,6 @@ def _assemble_compiled_sql_test( ) -def _macro_sql_test_scope_deps( - *, tested_macro_names: tuple[str, ...], model_inputs: tuple[CompileModelInput, ...] -) -> tuple[CompiledObjectKey, ...]: - tested_names: frozenset[str] = frozenset(tested_macro_names) - scope_deps: list[CompiledObjectKey] = [] - model_input: CompileModelInput - for model_input in model_inputs: - model_macro_deps: frozenset[str] = frozenset( - model_input.macro_deps or find_macro_call_names(model_input.macro_source_sql) - ) - if not tested_names.intersection(model_macro_deps): - continue - scope_deps.append( - CompiledObjectKey( - resource_type=CompiledResourceType.MODEL, - name=model_input.model_file.file_path.stem, - ) - ) - return tuple(scope_deps) - - -def _udf_sql_test_scope_deps( - *, tested_udf_names: tuple[str, ...], model_inputs: tuple[CompileModelInput, ...] -) -> tuple[CompiledObjectKey, ...]: - tested_names: frozenset[str] = frozenset(tested_udf_names) - scope_deps: list[CompiledObjectKey] = [] - model_input: CompileModelInput - for model_input in model_inputs: - model_udf_deps: frozenset[str] = frozenset( - reference.ref_name - for reference in model_input.references - if reference.ref_kind == SqlReferenceKind.UDF - ) - if not tested_names.intersection(model_udf_deps): - continue - scope_deps.append( - CompiledObjectKey( - resource_type=CompiledResourceType.MODEL, - name=model_input.model_file.file_path.stem, - ) - ) - return tuple(scope_deps) - - -def _function_sql_test_scope_deps( - *, tested_function_names: tuple[str, ...] -) -> tuple[CompiledObjectKey, ...]: - return tuple( - CompiledObjectKey(resource_type=CompiledResourceType.TABLE_FN, name=function_name) - for function_name in tested_function_names - ) - - def _build_test_model_query_overrides( *, test_input: CompileSqlTestInput, diff --git a/src/sqlbuild/compiler/compile/_helpers/sql_tests/scope_deps.py b/src/sqlbuild/compiler/compile/_helpers/sql_tests/scope_deps.py new file mode 100644 index 0000000000..38463bfeae --- /dev/null +++ b/src/sqlbuild/compiler/compile/_helpers/sql_tests/scope_deps.py @@ -0,0 +1,67 @@ +"""Scope dependencies of macro, UDF and table-function SQL tests.""" + +from __future__ import annotations + +from sqlbuild.compiler.compile._helpers.render.macros import find_macro_call_names +from sqlbuild.compiler.compile.models import CompiledObjectKey, CompileModelInput +from sqlbuild.compiler.compile.types import CompiledResourceType +from sqlbuild.compiler.references.types import SqlReferenceKind + + +def macro_sql_test_scope_deps( + *, tested_macro_names: tuple[str, ...], model_inputs: tuple[CompileModelInput, ...] +) -> tuple[CompiledObjectKey, ...]: + """Return the models whose macro calls include a tested macro.""" + + tested_names: frozenset[str] = frozenset(tested_macro_names) + scope_deps: list[CompiledObjectKey] = [] + model_input: CompileModelInput + for model_input in model_inputs: + model_macro_deps: frozenset[str] = frozenset( + model_input.macro_deps or find_macro_call_names(model_input.macro_source_sql) + ) + if not tested_names.intersection(model_macro_deps): + continue + scope_deps.append( + CompiledObjectKey( + resource_type=CompiledResourceType.MODEL, + name=model_input.model_file.file_path.stem, + ) + ) + return tuple(scope_deps) + + +def udf_sql_test_scope_deps( + *, tested_udf_names: tuple[str, ...], model_inputs: tuple[CompileModelInput, ...] +) -> tuple[CompiledObjectKey, ...]: + """Return the models that call a tested UDF.""" + + tested_names: frozenset[str] = frozenset(tested_udf_names) + scope_deps: list[CompiledObjectKey] = [] + model_input: CompileModelInput + for model_input in model_inputs: + model_udf_deps: frozenset[str] = frozenset( + reference.ref_name + for reference in model_input.references + if reference.ref_kind == SqlReferenceKind.UDF + ) + if not tested_names.intersection(model_udf_deps): + continue + scope_deps.append( + CompiledObjectKey( + resource_type=CompiledResourceType.MODEL, + name=model_input.model_file.file_path.stem, + ) + ) + return tuple(scope_deps) + + +def function_sql_test_scope_deps( + *, tested_function_names: tuple[str, ...] +) -> tuple[CompiledObjectKey, ...]: + """Return the tested table functions as scope dependencies.""" + + return tuple( + CompiledObjectKey(resource_type=CompiledResourceType.TABLE_FN, name=function_name) + for function_name in tested_function_names + ) From 82592d610f50d0917f5a14b092034c9f468f95b8 Mon Sep 17 00:00:00 2001 From: Kevin Longe <273747162+kvlonge@users.noreply.github.com> Date: Fri, 9 Oct 2026 23:39:30 +0100 Subject: [PATCH 31/31] test(native): update session-fact counts for the combined generated corpus --- .../compiler/semantic_checks/test_native_semantic_checks.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/integration/src/sqlbuild/compiler/semantic_checks/test_native_semantic_checks.py b/tests/integration/src/sqlbuild/compiler/semantic_checks/test_native_semantic_checks.py index b597aa098c..49e1f01f6c 100644 --- a/tests/integration/src/sqlbuild/compiler/semantic_checks/test_native_semantic_checks.py +++ b/tests/integration/src/sqlbuild/compiler/semantic_checks/test_native_semantic_checks.py @@ -179,9 +179,9 @@ def test_given_deferred_projects_when_completing_then_python_completes_and_recor seed=20261011, count=4, model_count=16, - expected_session_models=67, - expected_payload_models=29, - expected_proven_outputs=11, + expected_session_models=68, + expected_payload_models=28, + expected_proven_outputs=10, expected_minimum_codes={"B002": 1}, ), ],