Skip to content
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ pub(crate) const NATIVE_HEADER_COLUMNS: &str = "native_header_columns";
pub(crate) const PARSED_HEADER_COLUMNS: &str = "parsed_header_columns";
/// Config values converted into typed nodes for a key a reader asked for.
pub(crate) const TYPED_CONFIG_READS: &str = "typed_config_reads";
/// Reads of a model or project that is not a native compiled view, read by attribute instead.
pub(crate) const COMPILED_VIEW_FALLBACKS: &str = "compiled_view_fallbacks";

/// Compile stage runs since the last reset, by stage.
#[pyclass]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ pub(crate) mod inputs_view;
pub(crate) mod model_input_view;
pub(crate) mod model_reads;
pub(crate) mod project_host;
pub(crate) mod project_reads;
pub(crate) mod project_view;
pub(crate) mod records;
pub(crate) mod registration;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
//! A compiled model's analysis outcomes as Rust data: views read their owner, records their fields.

use pyo3::prelude::{Bound, PyAny, PyAnyMethods, PyResult};
use pyo3::prelude::{Bound, PyAny, PyAnyMethods, PyResult, Python};

use crate::bindings::_helpers::compile_driver::stage_work::{COMPILED_VIEW_FALLBACKS, count_stage};
use crate::bindings::_helpers::compiled_views::compiled_model_view::{
DeclaredRow, LineageInput, NativeCompiledModel,
};
Expand All @@ -27,10 +28,22 @@ const TRANSFORM_VALUES: [&str; 6] = [
];
const CONFIDENCE_VALUES: [&str; 3] = ["unknown", "high", "medium"];

/// The native view behind a model object; any other object is a Python-built record, counted.
#[inline]
pub(crate) fn native_model<'a>(
model: &'a Bound<'_, PyAny>,
) -> PyResult<Option<&'a NativeCompiledModel>> {
if let Ok(view) = model.cast::<NativeCompiledModel>() {
return Ok(Some(view.get()));
}
count_stage(model.py(), COMPILED_VIEW_FALLBACKS, 1)?;
Ok(None)
}

/// Each inferred output column as `(name, type, nullability)`, `None` when none were inferred.
pub(crate) fn inferred_rows(model: &Bound<'_, PyAny>) -> PyResult<Option<Vec<InferredRow>>> {
if let Ok(view) = model.downcast::<NativeCompiledModel>() {
return view.get().inferred_rows(model.py());
if let Some(view) = native_model(model)? {
return view.inferred_rows(model.py());
}
let rows: Bound<'_, PyAny> = native_field(model, "inferred_rows")?;
if rows.is_none() {
Expand All @@ -56,24 +69,23 @@ fn column_names(rows: Vec<InferredRow>) -> Vec<String> {

/// Each declared column of the attached schema as `(name, type)`, `None` without a schema.
pub(crate) fn declared_rows(model: &Bound<'_, PyAny>) -> PyResult<Option<Vec<DeclaredRow>>> {
if let Ok(view) = model.downcast::<NativeCompiledModel>() {
return view.get().schema_column_rows(model.py());
if let Some(view) = native_model(model)? {
return view.schema_column_rows(model.py());
}
native_field(model, "schema_column_rows")?.extract()
}

/// Whether the attached schema declares dynamic column families.
pub(crate) fn dynamic_columns_declared(model: &Bound<'_, PyAny>) -> PyResult<bool> {
if let Ok(view) = model.downcast::<NativeCompiledModel>() {
return view.get().dynamic_columns_declared(model.py());
if let Some(view) = native_model(model)? {
return view.dynamic_columns_declared(model.py());
}
native_field(model, "has_dynamic_columns")?.is_truthy()
}

/// `(has star, star resolved)` of the model's analysed query.
pub(crate) fn star_flags(model: &Bound<'_, PyAny>) -> PyResult<(bool, bool)> {
if let Ok(view) = model.downcast::<NativeCompiledModel>() {
let view: &NativeCompiledModel = view.get();
if let Some(view) = native_model(model)? {
return Ok((
view.fast_lineage_has_star(model.py())?,
view.fast_lineage_star_resolved(model.py())?,
Expand All @@ -87,8 +99,8 @@ pub(crate) fn star_flags(model: &Bound<'_, PyAny>) -> PyResult<(bool, bool)> {

/// Output columns whose types semantic recovery forgot, in any order.
pub(crate) fn unchecked_columns(model: &Bound<'_, PyAny>) -> PyResult<Vec<String>> {
if let Ok(view) = model.downcast::<NativeCompiledModel>() {
return Ok(view.get().unchecked_columns().to_vec());
if let Some(view) = native_model(model)? {
return Ok(view.unchecked_columns().to_vec());
}
native_field(model, "unchecked_output_columns")?
.try_iter()?
Expand All @@ -98,8 +110,8 @@ pub(crate) fn unchecked_columns(model: &Bound<'_, PyAny>) -> PyResult<Vec<String

/// Each lineage output with the `(resource, column)` pairs it reads; empty without lineage.
pub(crate) fn lineage_inputs(model: &Bound<'_, PyAny>) -> PyResult<Vec<LineageInput>> {
if let Ok(view) = model.downcast::<NativeCompiledModel>() {
return Ok(view.get().lineage_inputs(model.py())?.unwrap_or_default());
if let Some(view) = native_model(model)? {
return Ok(view.lineage_inputs(model.py())?.unwrap_or_default());
}
let columns: Bound<'_, PyAny> = native_field(model, "fast_lineage_columns")?;
if !columns.is_truthy()? {
Expand All @@ -123,25 +135,25 @@ pub(crate) fn lineage_inputs(model: &Bound<'_, PyAny>) -> PyResult<Vec<LineageIn

/// Whether the native session validated the model's bindings.
pub(crate) fn binding_validated(model: &Bound<'_, PyAny>) -> PyResult<bool> {
if let Ok(view) = model.downcast::<NativeCompiledModel>() {
return view.get().binding_validated(model.py());
if let Some(view) = native_model(model)? {
return view.binding_validated(model.py());
}
native_field(model, "binding_validated")?.is_truthy()
}

/// Whether the model's dynamic pivot proof proved its output columns.
pub(crate) fn output_proven(model: &Bound<'_, PyAny>) -> PyResult<bool> {
if let Ok(view) = model.downcast::<NativeCompiledModel>() {
return view.get().output_proven(model.py());
if let Some(view) = native_model(model)? {
return view.output_proven(model.py());
}
let proof: Bound<'_, PyAny> = native_field(model, "dynamic_column_contract")?;
Ok(!proof.is_none() && native_field(&proof, "output_proven")?.is_truthy()?)
}

/// Each lineage output with its transform kind and confidence values, `None` without lineage.
pub(crate) fn lineage_facts(model: &Bound<'_, PyAny>) -> PyResult<Option<Vec<LineageFactRow>>> {
if let Ok(view) = model.downcast::<NativeCompiledModel>() {
let Some(rows) = view.get().lineage_rows(model.py())? else {
if let Some(view) = native_model(model)? {
let Some(rows) = view.lineage_rows(model.py())? else {
return Ok(None);
};
let mut facts: Vec<LineageFactRow> = Vec::with_capacity(rows.len());
Expand Down Expand Up @@ -190,8 +202,8 @@ fn coded(values: &[&str], code: u8) -> PyResult<String> {

/// The input relations of a dynamic pivot proof that proved the model's outputs, else `None`.
pub(crate) fn proven_inputs(model: &Bound<'_, PyAny>) -> PyResult<Option<Vec<String>>> {
if let Ok(view) = model.downcast::<NativeCompiledModel>() {
return view.get().proven_inputs(model.py());
if let Some(view) = native_model(model)? {
return view.proven_inputs(model.py());
}
let proof: Bound<'_, PyAny> = native_field(model, "dynamic_column_contract")?;
if proof.is_none() || !native_field(&proof, "output_proven")?.is_truthy()? {
Expand All @@ -202,41 +214,112 @@ pub(crate) fn proven_inputs(model: &Bound<'_, PyAny>) -> PyResult<Option<Vec<Str

/// The model's name.
pub(crate) fn model_name(model: &Bound<'_, PyAny>) -> PyResult<String> {
if let Ok(view) = model.downcast::<NativeCompiledModel>() {
return view.get().name(model.py());
if let Some(view) = native_model(model)? {
return view.name(model.py());
}
native_field(model, "name")?.extract()
}

/// The model's compiled query SQL.
pub(crate) fn query_sql<'py>(model: &Bound<'py, PyAny>) -> PyResult<Bound<'py, PyAny>> {
if let Ok(view) = model.downcast::<NativeCompiledModel>() {
return Ok(view.get().query_sql(model.py())?.into_any());
if let Some(view) = native_model(model)? {
return Ok(view.query_sql(model.py())?.into_any());
}
native_field(model, "query_sql")
}

/// The model's `CompiledObjectKey`.
pub(crate) fn model_key<'py>(model: &Bound<'py, PyAny>) -> PyResult<Bound<'py, PyAny>> {
if let Ok(view) = model.downcast::<NativeCompiledModel>() {
return Ok(view.get().key(model.py())?.into_bound(model.py()));
if let Some(view) = native_model(model)? {
return Ok(view.key(model.py())?.into_bound(model.py()));
}
native_field(model, "key")
}

/// The model's destination `CompiledRelationLocation`.
pub(crate) fn destination<'py>(model: &Bound<'py, PyAny>) -> PyResult<Bound<'py, PyAny>> {
if let Ok(view) = model.downcast::<NativeCompiledModel>() {
return Ok(view.get().destination(model.py())?.into_bound(model.py()));
if let Some(view) = native_model(model)? {
return Ok(view.destination(model.py())?.into_bound(model.py()));
}
native_field(model, "destination")
}

/// The model's name as a Python string.
pub(crate) fn name_object<'py>(model: &Bound<'py, PyAny>) -> PyResult<Bound<'py, PyAny>> {
if let Ok(view) = model.downcast::<NativeCompiledModel>() {
let name: String = view.get().name(model.py())?;
if let Some(view) = native_model(model)? {
let name: String = view.name(model.py())?;
return Ok(pyo3::types::PyString::new(model.py(), &name).into_any());
}
native_field(model, "name")
}

/// The model's attached `SchemaModelEntry`, or `None`.
pub(crate) fn schema_entry<'py>(model: &Bound<'py, PyAny>) -> PyResult<Bound<'py, PyAny>> {
let py: Python<'py> = model.py();
if let Some(view) = native_model(model)? {
return Ok(view.schema_entry(py)?.into_bound(py));
}
native_field(model, "schema_entry")
}

/// The model's `CompileModelConfig`.
pub(crate) fn config<'py>(model: &Bound<'py, PyAny>) -> PyResult<Bound<'py, PyAny>> {
let py: Python<'py> = model.py();
if let Some(view) = native_model(model)? {
return Ok(view.config(py)?.into_bound(py));
}
native_field(model, "config")
}

/// The model's `CompileSqlReference` tuple.
pub(crate) fn references<'py>(model: &Bound<'py, PyAny>) -> PyResult<Bound<'py, PyAny>> {
let py: Python<'py> = model.py();
if let Some(view) = native_model(model)? {
return Ok(view.references(py)?.into_bound(py).into_any());
}
native_field(model, "references")
}

/// The model file's relative path.
pub(crate) fn relative_path<'py>(model: &Bound<'py, PyAny>) -> PyResult<Bound<'py, PyAny>> {
let py: Python<'py> = model.py();
if let Some(view) = native_model(model)? {
return Ok(view.relative_path(py)?.into_bound(py));
}
native_field(model, "relative_path")
}

/// The model's `InferredColumn` tuple, or `None`.
pub(crate) fn inferred_columns<'py>(model: &Bound<'py, PyAny>) -> PyResult<Bound<'py, PyAny>> {
let py: Python<'py> = model.py();
if let Some(view) = native_model(model)? {
return Ok(view.inferred_columns(py)?.into_bound(py));
}
native_field(model, "inferred_columns")
}

/// `(resource type, name)` of each dependency.
pub(crate) fn dep_rows(model: &Bound<'_, PyAny>) -> PyResult<Vec<(String, String)>> {
if let Some(view) = native_model(model)? {
return view.dep_rows(model.py());
}
native_field(model, "dep_rows")?.extract()
}

/// The model's dependency `CompiledObjectKey` tuple.
pub(crate) fn deps<'py>(model: &Bound<'py, PyAny>) -> PyResult<Bound<'py, PyAny>> {
let py: Python<'py> = model.py();
if let Some(view) = native_model(model)? {
return Ok(view.deps(py)?.into_bound(py).into_any());
}
native_field(model, "deps")
}

/// The model's enum columns by name.
pub(crate) fn enum_columns<'py>(model: &Bound<'py, PyAny>) -> PyResult<Bound<'py, PyAny>> {
let py: Python<'py> = model.py();
if let Some(view) = native_model(model)? {
return Ok(view.enum_columns(py)?.into_bound(py).into_any());
}
native_field(model, "enum_columns")
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
//! A compiled project's resource collections, read from its native view.

use pyo3::prelude::{Bound, PyAny, PyResult, Python};

use crate::bindings::_helpers::compile_driver::stage_work::{COMPILED_VIEW_FALLBACKS, count_stage};
use crate::bindings::_helpers::compiled_views::project_view::NativeProjectView;
use crate::bindings::_helpers::records::fields::field as native_field;

const MODELS_FIELD: &str = "models";
const SOURCES_FIELD: &str = "sources";
const SEEDS_FIELD: &str = "seeds";
const FUNCTIONS_FIELD: &str = "functions";
const AUDITS_FIELD: &str = "audits";

/// The native view behind a project object; any other object is a Python-built record, counted.
#[inline]
fn native_project<'a>(project: &'a Bound<'_, PyAny>) -> PyResult<Option<&'a NativeProjectView>> {
if let Ok(view) = project.cast::<NativeProjectView>() {
return Ok(Some(view.get()));
}
count_stage(project.py(), COMPILED_VIEW_FALLBACKS, 1)?;
Ok(None)
}

/// The project's compiled models, in compile order.
pub(crate) fn models<'py>(project: &Bound<'py, PyAny>) -> PyResult<Bound<'py, PyAny>> {
let py: Python<'py> = project.py();
if let Some(view) = native_project(project)? {
return Ok(view.models(py).into_bound(py).into_any());
}
native_field(project, MODELS_FIELD)
}

/// The project's compiled sources.
pub(crate) fn sources<'py>(project: &Bound<'py, PyAny>) -> PyResult<Bound<'py, PyAny>> {
let py: Python<'py> = project.py();
if let Some(view) = native_project(project)? {
return Ok(view.sources(py).into_bound(py));
}
native_field(project, SOURCES_FIELD)
}

/// The project's compiled functions.
pub(crate) fn functions<'py>(project: &Bound<'py, PyAny>) -> PyResult<Bound<'py, PyAny>> {
let py: Python<'py> = project.py();
if let Some(view) = native_project(project)? {
return Ok(view.functions(py).into_bound(py));
}
native_field(project, FUNCTIONS_FIELD)
}

/// The project's compiled seeds.
pub(crate) fn seeds<'py>(project: &Bound<'py, PyAny>) -> PyResult<Bound<'py, PyAny>> {
let py: Python<'py> = project.py();
if let Some(view) = native_project(project)? {
return Ok(view.seeds(py).into_bound(py));
}
native_field(project, SEEDS_FIELD)
}

/// The project's compiled audits.
pub(crate) fn audits<'py>(project: &Bound<'py, PyAny>) -> PyResult<Bound<'py, PyAny>> {
let py: Python<'py> = project.py();
if let Some(view) = native_project(project)? {
return Ok(view.audits(py).into_bound(py));
}
native_field(project, AUDITS_FIELD)
}

/// The project's resource collection named `field`; any other field is read by attribute.
pub(crate) fn collection<'py>(
project: &Bound<'py, PyAny>,
field: &str,
) -> PyResult<Bound<'py, PyAny>> {
match field {
MODELS_FIELD => models(project),
SOURCES_FIELD => sources(project),
SEEDS_FIELD => seeds(project),
FUNCTIONS_FIELD => functions(project),
AUDITS_FIELD => audits(project),
_ => native_field(project, field),
}
}

/// The project's compiled SQL tests.
pub(crate) fn sql_tests<'py>(project: &Bound<'py, PyAny>) -> PyResult<Bound<'py, PyAny>> {
let py: Python<'py> = project.py();
if let Some(view) = native_project(project)? {
return Ok(view.sql_tests(py).into_bound(py));
}
native_field(project, "sql_tests")
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
use sqlbuild_host::entries::models::{ExecutorHost, UserCodeHost};

use crate::bindings::_helpers::compiled_views::model_reads;
use crate::bindings::_helpers::compiled_views::project_reads;
use crate::bindings::_helpers::contracts::spec_resolution::matching_dynamic_families;
use crate::bindings::_helpers::model_config::typed_config::{config_text, model_values};
use crate::bindings::_helpers::planner_changes::results::native_enum;
Expand Down Expand Up @@ -107,7 +108,7 @@ pub(crate) fn detect_changes<'py>(
let query_change_tracking: bool =
native_field(&native_field(project, "settings")?, "query_change_tracking")?.is_truthy()?;
let dialect: Option<String> = native_field(snapshot, "column_dialect")?.extract()?;
for function in native_field(project, "functions")?.try_iter()? {
for function in project_reads::functions(project)?.try_iter()? {
let function: Bound<'py, PyAny> = function?;
let name: Bound<'py, PyAny> = native_field(&function, "name")?;
let fingerprint_sql: String = compiled_function_fingerprint_sql(&function)?;
Expand Down Expand Up @@ -248,7 +249,7 @@ pub(crate) fn scope_model_changes<'py>(
}
};
let function_local_hashes: Bound<'py, PyAny> = function_local_hashes(
&native_field(project, "functions")?,
&project_reads::functions(project)?,
native_field(project, "sql_analysis_dialect")?
.extract::<Option<String>>()?
.as_deref(),
Expand Down Expand Up @@ -530,7 +531,7 @@ fn model_schema_findings<'py>(
for column in &validity {
warehouse.retain(|(name, _)| !column.eq_ignore_ascii_case(name));
}
let schema_entry: Bound<'py, PyAny> = native_field(model, "schema_entry")?;
let schema_entry: Bound<'py, PyAny> = model_reads::schema_entry(model)?;
let mut yml: Vec<(String, String)> = Vec::new();
let mut type_enforcement: bool = false;
let mut dynamic: Option<Bound<'py, PyAny>> = None;
Expand Down
Loading
Loading