diff --git a/Cargo.lock b/Cargo.lock index a9f5f63d09..956cfbd103 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4126,7 +4126,6 @@ version = "0.0.0" dependencies = [ "memchr", "polyglot-sql", - "polyglot-sql-function-catalogs", "rayon", "regex", "rustc-hash", diff --git a/Cargo.toml b/Cargo.toml index f03cafe024..608de3589a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -93,9 +93,6 @@ polyglot-sql = { git = "https://github.com/sqlstack-io/polyglot.git", rev = "876 "semantic", "stacker", ] } -polyglot-sql-function-catalogs = { git = "https://github.com/sqlstack-io/polyglot.git", rev = "8767d688c5b504ad609645e27ab77a260d91c5a1", default-features = false, features = [ - "dialect-duckdb", -] } blake3 = { version = "1", features = ["rayon"] } # Only sqlbuild-build-features uses these: each enables in the sqb build exactly the features the # extension build resolves for that crate, so both builds share one compile of every common crate. diff --git a/crates/sqlbuild-analysis/Cargo.toml b/crates/sqlbuild-analysis/Cargo.toml index 80a5ae2313..c13a71204f 100644 --- a/crates/sqlbuild-analysis/Cargo.toml +++ b/crates/sqlbuild-analysis/Cargo.toml @@ -10,7 +10,6 @@ rust-version.workspace = true [dependencies] memchr.workspace = true polyglot-sql.workspace = true -polyglot-sql-function-catalogs.workspace = true rayon.workspace = true regex.workspace = true rustc-hash.workspace = true diff --git a/crates/sqlbuild-analysis/src/query_analysis/_helpers/engine.rs b/crates/sqlbuild-analysis/src/query_analysis/_helpers/engine.rs index ba731740dd..cde00841a5 100644 --- a/crates/sqlbuild-analysis/src/query_analysis/_helpers/engine.rs +++ b/crates/sqlbuild-analysis/src/query_analysis/_helpers/engine.rs @@ -550,12 +550,8 @@ fn analyze_compact_query_work( if let (Some(sql), Ok(dialect)) = (diagnostic_sql, dialect) && let Some(validation) = result.validation.take() { - let local_probes = crate::semantic_validation::models::FunctionProbes::default(); - let probes = catalog.map_or(&local_probes, |catalog| catalog.function_probes.as_ref()); result.validation = Some(validation.and_then(|value| { - crate::semantic_validation::main::map_diagnostics::map_diagnostics( - &sql, dialect, value, probes, - ) + crate::semantic_validation::main::map_diagnostics::map_diagnostics(&sql, dialect, value) })); } result diff --git a/crates/sqlbuild-analysis/src/semantic_validation/_helpers/catalog.rs b/crates/sqlbuild-analysis/src/semantic_validation/_helpers/catalog.rs index 32fd8e8198..9ac0e4b46d 100644 --- a/crates/sqlbuild-analysis/src/semantic_validation/_helpers/catalog.rs +++ b/crates/sqlbuild-analysis/src/semantic_validation/_helpers/catalog.rs @@ -6,7 +6,7 @@ use std::sync::atomic::{AtomicUsize, Ordering}; use std::sync::{Arc, Mutex, PoisonError}; use crate::semantic_validation::models::{ - CatalogInput, Columns, CompactAnalysisJob, DiagnosticRow, FunctionProbes, ProjectCatalog, + CatalogInput, Columns, CompactAnalysisJob, DiagnosticRow, ProjectCatalog, }; use crate::semantic_validation::types::{ BindingRequest, NormalizationRequest, PreparedCompactAnalysis, Relations, @@ -160,7 +160,6 @@ impl ProjectCatalog { tables: HashMap::new(), overrides: Vec::new(), analysis_tables: HashMap::new(), - function_probes: Arc::new(FunctionProbes::default()), analysis_pool: Arc::default(), running_analyses: Arc::default(), }; @@ -176,15 +175,9 @@ impl ProjectCatalog { pub fn with_relations(&self, relations: Relations) -> Self { let mut catalog = Self { - dialect: self.dialect, - options: self.options.clone(), - quoted_ignore_case: self.quoted_ignore_case, tables: self.tables.clone(), - overrides: Vec::new(), analysis_tables: self.analysis_tables.clone(), - function_probes: Arc::new(FunctionProbes::default()), - analysis_pool: Arc::clone(&self.analysis_pool), - running_analyses: Arc::clone(&self.running_analyses), + ..self.analysis_view() }; catalog.update_relations(relations); catalog @@ -285,12 +278,7 @@ impl ProjectCatalog { .map(|(sql, references, overrides)| { let schema = self.schema(&references, overrides)?; let result = self.validate(&sql, &schema)?; - let result = diagnostics::map_diagnostics( - &sql, - self.dialect, - result, - &self.function_probes, - )?; + let result = diagnostics::map_diagnostics(&sql, self.dialect, result)?; Ok(result.errors.into_iter().map(diagnostic_row).collect()) }) .collect::, String>>() @@ -356,7 +344,6 @@ impl ProjectCatalog { tables: HashMap::new(), overrides: Vec::new(), analysis_tables: HashMap::new(), - function_probes: Arc::clone(&self.function_probes), analysis_pool: Arc::clone(&self.analysis_pool), running_analyses: Arc::clone(&self.running_analyses), } @@ -388,7 +375,6 @@ impl ProjectCatalog { tables: HashMap::new(), overrides: Vec::new(), analysis_tables: HashMap::new(), - function_probes: Arc::new(FunctionProbes::default()), analysis_pool: Arc::default(), running_analyses: Arc::default(), } diff --git a/crates/sqlbuild-analysis/src/semantic_validation/_helpers/diagnostics.rs b/crates/sqlbuild-analysis/src/semantic_validation/_helpers/diagnostics.rs index b198ae8a54..2b98a1f64e 100644 --- a/crates/sqlbuild-analysis/src/semantic_validation/_helpers/diagnostics.rs +++ b/crates/sqlbuild-analysis/src/semantic_validation/_helpers/diagnostics.rs @@ -6,7 +6,6 @@ use crate::semantic_validation::_helpers::sqlite_temporal_casts; use crate::semantic_validation::_helpers::truth_tests; use crate::semantic_validation::_helpers::type_locations::locate_type_findings; use crate::semantic_validation::models::DiagnosticRow; -use crate::semantic_validation::models::FunctionProbes; use polyglot_sql::expressions::Select; use polyglot_sql::{ Dialect, DialectType, Expression, ExpressionWalk, ValidationError, ValidationResult, @@ -149,7 +148,6 @@ pub(crate) fn map_diagnostics( sql: &str, dialect: DialectType, result: ValidationResult, - probes: &FunctionProbes, ) -> Result { let mut context = DiagnosticContext { sql, @@ -164,9 +162,7 @@ pub(crate) fn map_diagnostics( errors.push(error); } } - errors.extend(function_names::unsupported_function_errors( - sql, dialect, probes, - )); + errors.extend(function_names::unsupported_function_errors(sql, dialect)); errors.extend(truth_tests::unsupported_truth_test_errors(sql, dialect)); errors.extend(sqlite_temporal_casts::sqlite_temporal_cast_warnings( sql, dialect, @@ -418,7 +414,6 @@ pub(crate) fn binding_diagnostics( valid: false, errors, }, - &FunctionProbes::default(), )?; Ok(result.errors.into_iter().map(diagnostic_row).collect()) } diff --git a/crates/sqlbuild-analysis/src/semantic_validation/_helpers/function_names.rs b/crates/sqlbuild-analysis/src/semantic_validation/_helpers/function_names.rs index ca0337945e..0c697b9487 100644 --- a/crates/sqlbuild-analysis/src/semantic_validation/_helpers/function_names.rs +++ b/crates/sqlbuild-analysis/src/semantic_validation/_helpers/function_names.rs @@ -1,28 +1,17 @@ //! Built-in function spellings checked against the spellings the target dialect accepts. -use crate::semantic_validation::models::FunctionProbes; -use crate::semantic_validation::types::ProbeKey; -use polyglot_sql::{Dialect, DialectType, Expression, ValidationError}; -use polyglot_sql_function_catalogs::{CatalogSink, FunctionNameCase, FunctionSignature}; +use polyglot_sql::{DialectType, ValidationError}; use rustc_hash::{FxHashMap, FxHashSet}; use sqlbuild_sqltext::sql_scan::main::comment_end::comment_end; use sqlbuild_sqltext::sql_scan::main::non_code_end::non_code_end; use sqlbuild_sqltext::sql_scan::models::{QuotePolicy, Unclosed}; -use std::mem::Discriminant; -use std::sync::{LazyLock, OnceLock, PoisonError}; +use std::sync::LazyLock; const UNKNOWN_FUNCTION: &str = "B101"; -const SNOWFLAKE_FUNCTIONS: &str = include_str!("snowflake_functions.txt"); -const DUCKDB_CATALOG: &str = "duckdb"; -const PROBE_PREFIX: &str = "SELECT "; +const FUNCTION_SPELLINGS: &str = include_str!("function_spellings.txt"); +const SNOWFLAKE_DIALECT: &str = "snowflake"; +const DUCKDB_DIALECT: &str = "duckdb"; const TYPE_POSITION_KEYWORD: &str = "AS"; -const PROBE_ARITIES: usize = 9; -/// Keywords that may precede a parenthesis without being a function call. -const NON_CALL_KEYWORDS: &str = "ALL AND ANY AS ASOF AT BEFORE BETWEEN BY CASE CHANGES CONNECT CUBE \ - DEFINE DISTINCT DISTINCTROW ELSE END EXCEPT EXCLUDE EXISTS FILTER FROM HAVING ILIKE IN \ - INTERSECT INTERVAL IS JOIN LATERAL LIKE MATCH_CONDITION MATCH_RECOGNIZE MEASURES MINUS NOT ON \ - OR OVER PARTITION PATTERN PERCENT PIVOT PRIOR QUALIFY RANGE RENAME ROLLUP ROW ROWS SAMPLE \ - SELECT SETS SOME TABLE TABLESAMPLE THEN UNION UNPIVOT USING VALUES WHEN WHERE WITH WITHIN"; /// Words after which an expression, and so a function call, may start. const EXPRESSION_KEYWORDS: &str = "ALL AND ANY BETWEEN BY CASE DISTINCT ELSE ELSEIF ESCAPE HAVING \ @@ -37,10 +26,9 @@ const NOT_KEYWORD: &str = "NOT"; const MATERIALIZED_KEYWORD: &str = "MATERIALIZED"; const DISTINCT_KEYWORD: &str = "DISTINCT"; -type SpellingIndex = FxHashMap>; +/// Where one rejected spelling applies: the dialect, the argument count and the accepted spelling. +type SpellingRow = (&'static str, usize, &'static str); -static KEYWORDS: LazyLock> = - LazyLock::new(|| NON_CALL_KEYWORDS.split_whitespace().collect()); static EXPRESSION_STARTS: LazyLock> = LazyLock::new(|| EXPRESSION_KEYWORDS.split_whitespace().collect()); static CLAUSE_KEYWORDS: LazyLock> = @@ -49,17 +37,8 @@ static ROOT_FRAME: Frame = Frame { select_seen: false, clause: Clause::Other, }; -static SNOWFLAKE: LazyLock> = - LazyLock::new(|| accepted_names(SNOWFLAKE_FUNCTIONS)); -static DUCKDB: LazyLock> = LazyLock::new(|| { - let mut sink: CatalogNames = CatalogNames::default(); - polyglot_sql_function_catalogs::register_enabled_catalogs(&mut sink); - sink.names -}); -static SNOWFLAKE_INDEX: [OnceLock; PROBE_ARITIES] = - [const { OnceLock::new() }; PROBE_ARITIES]; -static DUCKDB_INDEX: [OnceLock; PROBE_ARITIES] = - [const { OnceLock::new() }; PROBE_ARITIES]; +static SPELLINGS: LazyLock>> = + LazyLock::new(|| spellings(FUNCTION_SPELLINGS)); /// One call site whose authored built-in spelling the dialect does not accept. #[derive(Debug, PartialEq, Eq)] @@ -95,21 +74,6 @@ struct Scan { path_colon: bool, } -/// How the parser represents one probe call. -#[derive(Clone, Debug, PartialEq, Eq, Hash)] -enum Signature { - /// A generic function node with this upper-case name. - Named(String), - /// A typed built-in node, identified by its expression variant. - Typed(Discriminant), -} - -/// Upper-case DuckDB names from the SQL library's introspected function catalogue. -#[derive(Default)] -struct CatalogNames { - names: FxHashSet, -} - /// Incremental byte-to-character position, so locating every call stays linear in the SQL. pub(crate) struct Cursor { byte: usize, @@ -118,29 +82,6 @@ pub(crate) struct Cursor { pub(crate) column: usize, } -impl CatalogSink for CatalogNames { - fn set_dialect_name_case(&mut self, _dialect: &'static str, _name_case: FunctionNameCase) {} - - fn set_function_name_case( - &mut self, - _dialect: &'static str, - _function_name: &str, - _name_case: FunctionNameCase, - ) { - } - - fn register( - &mut self, - dialect: &'static str, - function_name: &str, - _signatures: Vec, - ) { - if dialect == DUCKDB_CATALOG && is_identifier(function_name) { - self.names.insert(function_name.to_ascii_uppercase()); - } - } -} - impl Cursor { pub(crate) fn new() -> Self { Self { @@ -163,79 +104,84 @@ impl Cursor { } self.byte = byte; } + + /// `error` located at the byte range `start..end` of `sql`, with its character span. + pub(crate) fn locate( + &mut self, + sql: &str, + (start, end): (usize, usize), + error: ValidationError, + ) -> ValidationError { + self.advance(sql, start); + let first: usize = self.characters; + let last: usize = first + sql[start..end].chars().count(); + error + .with_location(self.line, self.column) + .with_span(Some(first), Some(last)) + } } -impl FunctionProbes { - /// Return the accepted spelling for an unaccepted call-site name the parser treats as built-in. - fn suggestion(&self, dialect: DialectType, upper: &str, arity: usize) -> Option { - let key: ProbeKey = (dialect, upper.to_owned(), arity.min(PROBE_ARITIES - 1)); - if let Some(cached) = self - .suggestions - .lock() - .unwrap_or_else(PoisonError::into_inner) - .get(&key) +/// Read `dialect SPELLING arguments ACCEPTED` rows; `#` starts a comment. +fn spellings(source: &'static str) -> FxHashMap<&'static str, Vec> { + let mut rows: FxHashMap<&'static str, Vec> = FxHashMap::default(); + for line in source.lines().map(str::trim) { + let mut fields = line.split_whitespace(); + if let (Some(dialect), Some(spelling), Some(arity), Some(accepted)) = + (fields.next(), fields.next(), fields.next(), fields.next()) + && !dialect.starts_with('#') + && let Ok(arity) = arity.parse::() { - return cached.clone(); + rows.entry(spelling) + .or_default() + .push((dialect, arity, accepted)); } - let found: Option = closest_spelling(dialect, upper, key.2); - self.suggestions - .lock() - .unwrap_or_else(PoisonError::into_inner) - .insert(key, found.clone()); - found } + rows } -fn accepted_names(source: &str) -> FxHashSet { - source - .lines() - .map(str::trim) - .filter(|line| !line.is_empty() && !line.starts_with('#')) - .map(str::to_ascii_uppercase) - .collect() +/// Return the accepted spelling for `upper` called with `arity` arguments in `dialect`. +fn accepted_spelling(dialect: &str, upper: &str, arity: usize) -> Option<&'static str> { + SPELLINGS + .get(upper)? + .iter() + .find_map(|(row_dialect, row_arity, accepted)| { + (*row_dialect == dialect && *row_arity == arity).then_some(*accepted) + }) } -fn accepted(dialect: DialectType) -> Option<&'static FxHashSet> { +/// The analysis dialect name the spelling table is keyed on, for the legacy validation path. +fn dialect_name(dialect: DialectType) -> Option<&'static str> { match dialect { - DialectType::Snowflake => Some(&SNOWFLAKE), - DialectType::DuckDB => Some(&DUCKDB), + DialectType::Snowflake => Some(SNOWFLAKE_DIALECT), + DialectType::DuckDB => Some(DUCKDB_DIALECT), _ => None, } } /// Return located B101 diagnostics, with character spans, for unsupported built-in spellings. -pub(crate) fn unsupported_function_errors( - sql: &str, - dialect: DialectType, - probes: &FunctionProbes, -) -> Vec { +pub(crate) fn unsupported_function_errors(sql: &str, dialect: DialectType) -> Vec { let mut cursor: Cursor = Cursor::new(); - unsupported_calls(sql, dialect, probes) + let Some(name) = dialect_name(dialect) else { + return Vec::new(); + }; + unsupported_calls(sql, name) .into_iter() .map(|call| { let message: String = format!( "Unknown function '{}' for dialect {dialect:?}; did you mean {}?", call.name, call.suggestion ); - cursor.advance(sql, call.start); - let start: usize = cursor.characters; - let end: usize = start + sql[call.start..call.end].chars().count(); - ValidationError::error(message, UNKNOWN_FUNCTION) - .with_location(cursor.line, cursor.column) - .with_span(Some(start), Some(end)) + cursor.locate( + sql, + (call.start, call.end), + ValidationError::error(message, UNKNOWN_FUNCTION), + ) }) .collect() } -/// Return call sites in `sql` whose built-in spelling `dialect` does not accept. -pub(crate) fn unsupported_calls( - sql: &str, - dialect: DialectType, - probes: &FunctionProbes, -) -> Vec { - let Some(names) = accepted(dialect) else { - return Vec::new(); - }; +/// Return call sites in `sql` whose built-in spelling the analysis `dialect` does not accept. +pub(crate) fn unsupported_calls(sql: &str, dialect: &str) -> Vec { let bytes: &[u8] = sql.as_bytes(); let mut calls: Vec = Vec::new(); let mut scan: Scan = Scan::new(); @@ -277,18 +223,15 @@ pub(crate) fn unsupported_calls( let open: usize = skip_trivia(bytes, index); if bytes.get(open) == Some(&b'(') && scan.call_position() - && !name.starts_with("__") - && !names.contains(&upper) - && !KEYWORDS.contains(upper.as_str()) && let Some((arity, close)) = arguments(bytes, open) && !opens_cte_body(bytes, close + 1) - && let Some(suggestion) = probes.suggestion(dialect, &upper, arity) + && let Some(suggestion) = accepted_spelling(dialect, &upper, arity) { calls.push(UnsupportedCall { name: name.to_owned(), start, end: index, - suggestion, + suggestion: suggestion.to_owned(), }); } scan.word(upper); @@ -416,13 +359,6 @@ pub(crate) fn is_word_byte(byte: u8) -> bool { byte.is_ascii_alphanumeric() || byte == b'_' || byte == b'$' } -fn is_identifier(name: &str) -> bool { - name.bytes() - .next() - .is_some_and(|byte| byte.is_ascii_alphabetic() || byte == b'_') - && name.bytes().all(is_word_byte) -} - /// Return the top-level argument count and closing index of the call opening at `open`. fn arguments(sql: &[u8], open: usize) -> Option<(usize, usize)> { let mut depth: usize = 0; @@ -463,76 +399,3 @@ fn arguments(sql: &[u8], open: usize) -> Option<(usize, usize)> { } None } - -/// Return the closest accepted spelling the parser maps to the same built-in node as `upper`. -fn closest_spelling(dialect: DialectType, upper: &str, arity: usize) -> Option { - let signature: Signature = parsed_signature(dialect, upper, arity)?; - if signature == Signature::Named(upper.to_owned()) { - return None; - } - spelling_index(dialect, arity)? - .get(&signature)? - .iter() - .min_by_key(|accepted| (edit_distance(upper, accepted), (*accepted).clone())) - .cloned() -} - -fn spelling_index(dialect: DialectType, arity: usize) -> Option<&'static SpellingIndex> { - let (names, indexes) = match dialect { - DialectType::Snowflake => (&*SNOWFLAKE, &SNOWFLAKE_INDEX), - DialectType::DuckDB => (&*DUCKDB, &DUCKDB_INDEX), - _ => return None, - }; - Some(indexes.get(arity)?.get_or_init(|| { - let mut index: SpellingIndex = FxHashMap::default(); - for name in names { - if let Some(signature) = parsed_signature(dialect, name, arity) { - index.entry(signature).or_default().push(name.clone()); - } - } - index - })) -} - -fn parsed_signature(dialect: DialectType, upper: &str, arity: usize) -> Option { - let arguments: Vec = (1..=arity).map(|index| format!("a{index}")).collect(); - let sql: String = format!("{PROBE_PREFIX}{upper}({})", arguments.join(", ")); - let Ok(statements) = Dialect::get(dialect).parse(&sql) else { - return None; - }; - let [Expression::Select(select)] = statements.as_slice() else { - return None; - }; - let [expression] = select.expressions.as_slice() else { - return None; - }; - match expression { - Expression::Function(function) => { - Some(Signature::Named(function.name.to_ascii_uppercase())) - } - Expression::AggregateFunction(function) => { - Some(Signature::Named(function.name.to_ascii_uppercase())) - } - Expression::Column(_) - | Expression::Identifier(_) - | Expression::Tuple(_) - | Expression::Paren(_) => None, - other => Some(Signature::Typed(std::mem::discriminant(other))), - } -} - -fn edit_distance(left: &str, right: &str) -> usize { - let right: Vec = right.chars().collect(); - let mut previous: Vec = (0..=right.len()).collect(); - for (row, left_char) in left.chars().enumerate() { - let mut current: Vec = vec![row + 1; right.len() + 1]; - for (column, right_char) in right.iter().enumerate() { - let substitution: usize = previous[column] + usize::from(left_char != *right_char); - current[column + 1] = substitution - .min(previous[column + 1] + 1) - .min(current[column] + 1); - } - previous = current; - } - previous[right.len()] -} diff --git a/crates/sqlbuild-analysis/src/semantic_validation/_helpers/function_spellings.txt b/crates/sqlbuild-analysis/src/semantic_validation/_helpers/function_spellings.txt new file mode 100644 index 0000000000..06b81043cd --- /dev/null +++ b/crates/sqlbuild-analysis/src/semantic_validation/_helpers/function_spellings.txt @@ -0,0 +1,48 @@ +# Built-in spellings SQLBuild's compile-time name check (B101) rejects, with the spelling the +# dialect accepts for the same function. A call is rejected only when its spelling and argument +# count appear here, so user-defined and unknown functions are never rejected. +# +# Columns: analysis dialect, upper-case spelling, argument count, accepted spelling. +# Every Snowflake spelling is absent from Snowflake's function reference; every DuckDB spelling +# is unknown to DuckDB 2.0.0 with its core extensions. Add a row only with that evidence. +snowflake CEILING 1 CEIL +snowflake CEILING 2 CEIL +snowflake CHARACTER_LENGTH 1 LENGTH +snowflake CHAR_LENGTH 1 LENGTH +snowflake COUNTIF 1 COUNT_IF +snowflake DATEPART 2 DATE_PART +snowflake DAY_OF_MONTH 1 DAYOFMONTH +snowflake DAY_OF_WEEK 1 DAYOFWEEK +snowflake DAY_OF_YEAR 1 DAYOFYEAR +snowflake ENDS_WITH 2 ENDSWITH +snowflake IF 2 IFF +snowflake IF 3 IFF +snowflake IIF 2 IFF +snowflake IIF 3 IFF +snowflake INSTR 2 POSITION +snowflake INSTR 3 POSITION +snowflake LCASE 1 LOWER +snowflake LOCATE 2 POSITION +snowflake LOCATE 3 POSITION +snowflake RAND 1 RANDOM +snowflake STARTS_WITH 2 STARTSWITH +snowflake STRPOS 2 POSITION +snowflake STRPOS 3 POSITION +snowflake TEXTLEN 1 LEN +snowflake TOTYPENAME 1 TYPEOF +snowflake UCASE 1 UPPER +snowflake WEEK_OF_YEAR 1 WEEKOFYEAR +duckdb DAY_OF_MONTH 1 DAYOFMONTH +duckdb DAY_OF_WEEK 1 DAYOFWEEK +duckdb DAY_OF_YEAR 1 DAYOFYEAR +duckdb ENDSWITH 2 ENDS_WITH +duckdb EVERY 1 BOOL_AND +duckdb LOCATE 2 INSTR +duckdb LOCATE 3 INSTR +duckdb LOGICAL_AND 1 BOOL_AND +duckdb LOGICAL_OR 1 BOOL_OR +duckdb RAND 1 RANDOM +duckdb STARTSWITH 2 STARTS_WITH +duckdb TEXTLEN 1 LEN +duckdb TOTYPENAME 1 TYPEOF +duckdb WEEK_OF_YEAR 1 WEEKOFYEAR diff --git a/crates/sqlbuild-analysis/src/semantic_validation/_helpers/snowflake_functions.txt b/crates/sqlbuild-analysis/src/semantic_validation/_helpers/snowflake_functions.txt deleted file mode 100644 index 6147565f61..0000000000 --- a/crates/sqlbuild-analysis/src/semantic_validation/_helpers/snowflake_functions.txt +++ /dev/null @@ -1,993 +0,0 @@ -# Snowflake built-in function spellings accepted by SQLBuild's compile-time name check (B101). -# -# Source: Snowflake's public "All functions (alphabetical)" reference, including every listed -# alias (for example SUBSTR and SUBSTRING, DATEDIFF, TIMEDIFF and TIMESTAMPDIFF, TRY_TO_DECIMAL, -# TRY_TO_NUMBER and TRY_TO_NUMERIC, STARTSWITH, ENDSWITH, CONTAINS, IFF), with wildcard families -# such as TO_TIMESTAMP_* and YEAR*/DAY*/WEEK* expanded, plus SYS_CONNECT_BY_PATH from the -# CONNECT BY reference. Names are case-insensitive; one name per line; `#` starts a comment. -# -# A spelling absent from this list is rejected only when the SQL parser maps it to the same -# built-in node as a listed spelling, so user-defined and unknown functions are never rejected. -# Add a missing Snowflake built-in here rather than suppressing the diagnostic. -ABS -ACCEPTED_VALUES -ACOS -ACOSH -ADD_MONTHS -AGENT_RUN -AGG -AI_AGG -AI_CLASSIFY -AI_COMPLETE -AI_COUNT_TOKENS -AI_EMBED -AI_EXTRACT -AI_FILTER -AI_MULTI_EMBED -AI_PARSE_DOCUMENT -AI_REDACT -AI_SENTIMENT -AI_SIMILARITY -AI_SUMMARIZE -AI_SUMMARIZE_AGG -AI_TRANSCRIBE -AI_TRANSLATE -ALERT_HISTORY -ALL_USER_NAMES -ANY_VALUE -APPLICATION_CALLBACK_HISTORY -APPLICATION_CONFIGURATION_VALUE_HISTORY -APPLICATION_JSON -APPLICATION_SPECIFICATION_STATUS_HISTORY -APPROXIMATE_JACCARD_INDEX -APPROXIMATE_SIMILARITY -APPROX_COUNT_DISTINCT -APPROX_PERCENTILE -APPROX_PERCENTILE_ACCUMULATE -APPROX_PERCENTILE_COMBINE -APPROX_PERCENTILE_ESTIMATE -APPROX_TOP_K -APPROX_TOP_K_ACCUMULATE -APPROX_TOP_K_COMBINE -APPROX_TOP_K_ESTIMATE -ARRAYS_OVERLAP -ARRAYS_TO_OBJECT -ARRAYS_ZIP -ARRAY_AGG -ARRAY_APPEND -ARRAY_CAT -ARRAY_COMPACT -ARRAY_CONSTRUCT -ARRAY_CONSTRUCT_COMPACT -ARRAY_CONSTRUCT_STRUCTURED -ARRAY_CONTAINS -ARRAY_DISTINCT -ARRAY_EXCEPT -ARRAY_FLATTEN -ARRAY_GENERATE_RANGE -ARRAY_INSERT -ARRAY_INTERSECTION -ARRAY_MAX -ARRAY_MIN -ARRAY_POSITION -ARRAY_PREPEND -ARRAY_REMOVE -ARRAY_REMOVE_AT -ARRAY_REVERSE -ARRAY_SIZE -ARRAY_SLICE -ARRAY_SORT -ARRAY_TO_STRING -ARRAY_UNION_AGG -ARRAY_UNIQUE_AGG -ASCII -ASIN -ASINH -AS_ARRAY -AS_BINARY -AS_BOOLEAN -AS_CHAR -AS_DATE -AS_DECIMAL -AS_DOUBLE -AS_INTEGER -AS_NUMBER -AS_OBJECT -AS_REAL -AS_TIME -AS_TIMESTAMP_LTZ -AS_TIMESTAMP_NTZ -AS_TIMESTAMP_TZ -AS_VARCHAR -ATAN -ATAN2 -ATANH -AUTOMATIC_CLUSTERING_HISTORY -AUTO_REFRESH_REGISTRATION_HISTORY -AVAILABLE_LISTINGS -AVAILABLE_LISTING_REFRESH_HISTORY -AVG -BASE64_DECODE_BINARY -BASE64_DECODE_STRING -BASE64_ENCODE -BETWEEN -BIND_VALUES -BITAND -BITAND_AGG -BITMAP_ABSOLUTE_POSITION -BITMAP_AND -BITMAP_AND_AGG -BITMAP_BIT_POSITION -BITMAP_BUCKET_NUMBER -BITMAP_CONSTRUCT_AGG -BITMAP_COUNT -BITMAP_OR -BITMAP_OR_AGG -BITMAP_TO_ARRAY -BITNOT -BITOR -BITOR_AGG -BITSHIFTLEFT -BITSHIFTRIGHT -BITXOR -BITXOR_AGG -BIT_LENGTH -BLANK_COUNT -BLANK_PERCENT -BOOLAND -BOOLAND_AGG -BOOLNOT -BOOLOR -BOOLOR_AGG -BOOLXOR -BOOLXOR_AGG -BUILD_SCOPED_FILE_URL -BUILD_STAGE_FILE_URL -CASE -CAST -CBRT -CEIL -CHAR -CHARINDEX -CHECK_JSON -CHECK_XML -CHR -CLASSIFY_TEXT -COALESCE -COLLATE -COLLATION -COMPLETE -COMPLETE_TASK_GRAPHS -COMPRESS -CONCAT -CONCAT_WS -CONDITIONAL_CHANGE_EVENT -CONDITIONAL_TRUE_EVENT -CONTAINS -CONVERT_TIMEZONE -COPY_HISTORY -CORR -CORTEX_SEARCH_BATCH -CORTEX_SEARCH_DATA_SCAN -CORTEX_SEARCH_REFRESH_HISTORY -COS -COSH -COT -COUNT -COUNT_IF -COUNT_TOKENS -COVAR_POP -COVAR_SAMP -CUME_DIST -CUMULATIVE_PRIVACY_LOSSES -CURRENT_ACCOUNT -CURRENT_ACCOUNT_NAME -CURRENT_AVAILABLE_ROLES -CURRENT_CLIENT -CURRENT_DATABASE -CURRENT_DATE -CURRENT_IP_ADDRESS -CURRENT_ORGANIZATION_NAME -CURRENT_ORGANIZATION_USER -CURRENT_REGION -CURRENT_ROLE -CURRENT_ROLE_TYPE -CURRENT_SCHEMA -CURRENT_SCHEMAS -CURRENT_SECONDARY_ROLES -CURRENT_SESSION -CURRENT_STATEMENT -CURRENT_TASK_GRAPHS -CURRENT_TIME -CURRENT_TIMESTAMP -CURRENT_TRANSACTION -CURRENT_USER -CURRENT_VERSION -CURRENT_WAREHOUSE -DATABASE_REFRESH_HISTORY -DATABASE_REFRESH_PROGRESS -DATABASE_REFRESH_PROGRESS_BY_JOB -DATABASE_REPLICATION_USAGE_HISTORY -DATABASE_STORAGE_USAGE_HISTORY -DATASKETCHES_HLL -DATASKETCHES_HLL_ACCUMULATE -DATASKETCHES_HLL_COMBINE -DATASKETCHES_HLL_ESTIMATE -DATA_AGENT_RUN -DATA_METRIC_FUNCTION_EXPECTATIONS -DATA_METRIC_FUNCTION_REFERENCES -DATA_METRIC_SCHEDULED_TIME -DATA_QUALITY_MONITORING_EXPECTATION_STATUS -DATA_QUALITY_MONITORING_RESULTS -DATA_TRANSFER_HISTORY -DATE -DATEADD -DATEDIFF -DATE_FROM_PARTS -DATE_PART -DATE_TRUNC -DAY -DAYNAME -DAYOFMONTH -DAYOFWEEK -DAYOFWEEKISO -DAYOFYEAR -DBT_PROJECT_EXECUTION_HISTORY -DECODE -DECOMPRESS_BINARY -DECOMPRESS_STRING -DECRYPT -DECRYPT_RAW -DEGREES -DENSE_RANK -DIV0 -DIV0NULL -DP_INTERVAL_HIGH -DP_INTERVAL_LOW -DUPLICATE_COUNT -DYNAMIC_TABLES -DYNAMIC_TABLE_GRAPH_HISTORY -DYNAMIC_TABLE_REFRESH_HISTORY -EDITDISTANCE -EMAIL_INTEGRATION_CONFIG -EMBED_TEXT_1024 -EMBED_TEXT_768 -ENCRYPT -ENCRYPT_RAW -ENDSWITH -ENTITY_SENTIMENT -EQUAL_NULL -ESTIMATE_REMAINING_DP_AGGREGATES -EXECUTE_AI_EVALUATION -EXP -EXPLAIN_JSON -EXTERNAL_FUNCTIONS_HISTORY -EXTERNAL_TABLE_FILES -EXTERNAL_TABLE_FILE_REGISTRATION_HISTORY -EXTRACT -EXTRACT_ANSWER -EXTRACT_SEMANTIC_CATEGORIES -EXTREME_OUTLIER_COUNT -EXTREME_OUTLIER_IQR_COUNT -EXTREME_OUTLIER_IQR_PERCENT -EXTREME_OUTLIER_PERCENT -EXTREME_OUTLIER_ZSCORE_COUNT -EXTREME_OUTLIER_ZSCORE_PERCENT -FACTORIAL -FILTER -FINETUNE -FIRST_VALUE -FLATTEN -FLOOR -FL_GET_CONTENT_TYPE -FL_GET_ETAG -FL_GET_FILE_TYPE -FL_GET_LAST_MODIFIED -FL_GET_RELATIVE_PATH -FL_GET_SCOPED_FILE_URL -FL_GET_SIZE -FL_GET_STAGE -FL_GET_STAGE_FILE_URL -FL_IS_AUDIO -FL_IS_COMPRESSED -FL_IS_DOCUMENT -FL_IS_IMAGE -FL_IS_VIDEO -FRESHNESS -GENERATE_COLUMN_DESCRIPTION -GENERATE_POSTGRES_ACCESS_TOKEN_FOR_USER -GENERATOR -GET -GETBIT -GETDATE -GETVARIABLE -GET_ABSOLUTE_PATH -GET_AI_EVALUATION_DATA -GET_AI_OBSERVABILITY_LOGS -GET_AI_RECORD_TRACE -GET_ANACONDA_PACKAGES_REPODATA -GET_CONDITION_QUERY_UUID -GET_CONFIGURATION_VALUE -GET_CONTACTS -GET_DDL -GET_IGNORE_CASE -GET_JOB_HISTORY -GET_LINEAGE -GET_OBJECT_REFERENCES -GET_PATH -GET_PRESIGNED_URL -GET_PYTHON_PROFILER_OUTPUT -GET_QUERY_OPERATOR_STATS -GET_RELATIVE_PATH -GET_STAGE_LOCATION -GREATEST -GREATEST_IGNORE_NULLS -GROUPING -GROUPING_ID -H3_CELL_TO_BOUNDARY -H3_CELL_TO_CHILDREN -H3_CELL_TO_CHILDREN_STRING -H3_CELL_TO_PARENT -H3_CELL_TO_POINT -H3_COMPACT_CELLS -H3_COMPACT_CELLS_STRINGS -H3_COVERAGE -H3_COVERAGE_STRINGS -H3_GET_RESOLUTION -H3_GRID_DISK -H3_GRID_DISTANCE -H3_GRID_PATH -H3_INT_TO_STRING -H3_IS_PENTAGON -H3_IS_VALID_CELL -H3_LATLNG_TO_CELL -H3_LATLNG_TO_CELL_STRING -H3_POINT_TO_CELL -H3_POINT_TO_CELL_STRING -H3_POLYGON_TO_CELLS -H3_POLYGON_TO_CELLS_STRINGS -H3_STRING_TO_INT -H3_TRY_COVERAGE -H3_TRY_COVERAGE_STRINGS -H3_TRY_GRID_DISTANCE -H3_TRY_GRID_PATH -H3_TRY_POLYGON_TO_CELLS -H3_TRY_POLYGON_TO_CELLS_STRINGS -H3_UNCOMPACT_CELLS -H3_UNCOMPACT_CELLS_STRINGS -HASH -HASH_AGG -HAVERSINE -HEX_DECODE_BINARY -HEX_DECODE_STRING -HEX_ENCODE -HLL -HLL_ACCUMULATE -HLL_COMBINE -HLL_ESTIMATE -HLL_EXPORT -HLL_IMPORT -HOUR -ICEBERG_TABLE_FILES -ICEBERG_TABLE_SNAPSHOT_REFRESH_HISTORY -IFF -IFNULL -ILIKE -IN -INFER_SCHEMA -INITCAP -INSERT -INTEGRATION -INTERPOLATE_BFILL -INTERPOLATE_FFILL -INTERPOLATE_LINEAR -INVOKER_ROLE -INVOKER_SHARE -IS_APPLICATION_ROLE_ACTIVATED -IS_APPLICATION_ROLE_IN_SESSION -IS_ARRAY -IS_BINARY -IS_BOOLEAN -IS_CHAR -IS_CONFIGURATION_SET -IS_DATABASE_ROLE_ACTIVATED -IS_DATABASE_ROLE_IN_SESSION -IS_DATE -IS_DATE_VALUE -IS_DECIMAL -IS_DOUBLE -IS_GRANTED_TO_INVOKER_ROLE -IS_GROUP_ACTIVATED -IS_GROUP_IMPORTED -IS_INSTANCE_ROLE_IN_SESSION -IS_INTEGER -IS_NULL_VALUE -IS_OBJECT -IS_ORGANIZATION_USER -IS_ORGANIZATION_USER_GROUP -IS_ORGANIZATION_USER_GROUP_IN_SESSION -IS_REAL -IS_ROLE_ACTIVATED -IS_ROLE_IN_SESSION -IS_TIME -IS_TIMESTAMP_LTZ -IS_TIMESTAMP_NTZ -IS_TIMESTAMP_TZ -IS_USER_IMPORTED -IS_VARCHAR -JAROWINKLER_SIMILARITY -JSON_EXTRACT_PATH_TEXT -KURTOSIS -LAG -LAST_DAY -LAST_QUERY_ID -LAST_SUCCESSFUL_SCHEDULED_TIME -LAST_TRANSACTION -LAST_VALUE -LEAD -LEAST -LEAST_IGNORE_NULLS -LEFT -LEN -LENGTH -LIKE -LISTAGG -LISTING_REFRESH_HISTORY -LN -LOCALTIME -LOCALTIMESTAMP -LOG -LOGIN_HISTORY -LOGIN_HISTORY_BY_USER -LOWER -LPAD -LTRIM -MAP_CAT -MAP_CONSTRUCT -MAP_CONTAINS_KEY -MAP_DELETE -MAP_ENTRIES -MAP_INSERT -MAP_KEYS -MAP_PICK -MAP_SIZE -MATERIALIZED_VIEW_REFRESH_HISTORY -MAX -MAX_BY -MD5 -MD5_BINARY -MD5_HEX -MD5_NUMBER_LOWER64 -MD5_NUMBER_UPPER64 -MEDIAN -MIN -MINHASH -MINHASH_COMBINE -MINUTE -MIN_BY -MOD -MODE -MODEL_MONITOR_DRIFT_METRIC -MODEL_MONITOR_PERFORMANCE_METRIC -MODEL_MONITOR_STAT_METRIC -MONTH -MONTHNAME -MONTHS_BETWEEN -NETWORK_RULE_REFERENCES -NEXT_DAY -NORMAL -NOTIFICATION_HISTORY -NTH_VALUE -NTILE -NULLIF -NULLIFZERO -NULL_COUNT -NULL_PERCENT -NVL -NVL2 -OBJECT_AGG -OBJECT_CONSTRUCT -OBJECT_CONSTRUCT_KEEP_NULL -OBJECT_CONSTRUCT_KEEP_NULL_STRUCTURED -OBJECT_DELETE -OBJECT_INSERT -OBJECT_KEYS -OBJECT_PICK -OCTET_LENGTH -ONLINE_FEATURE_TABLE_REFRESH_HISTORY -OUTLIER_COUNT -OUTLIER_IQR_COUNT -OUTLIER_IQR_PERCENT -OUTLIER_PERCENT -OUTLIER_ZSCORE_COUNT -OUTLIER_ZSCORE_PERCENT -PARSE_DOCUMENT -PARSE_IP -PARSE_JSON -PARSE_URL -PARSE_XML -PERCENTILE_CONT -PERCENTILE_DISC -PERCENT_RANK -PI -PIPE_USAGE_HISTORY -POLICY_CONTEXT -POLICY_REFERENCES -POSITION -POW -POWER -PREVIOUS_DAY -PROMPT -QUARTER -QUERY_ACCELERATION_HISTORY -QUERY_HISTORY -RADIANS -RANDOM -RANDSTR -RANK -RATIO_TO_REPORT -REDUCE -REGEXP -REGEXP_COUNT -REGEXP_INSTR -REGEXP_LIKE -REGEXP_REPLACE -REGEXP_SUBSTR -REGEXP_SUBSTR_ALL -REGR_AVGX -REGR_AVGY -REGR_COUNT -REGR_INTERCEPT -REGR_R2 -REGR_SLOPE -REGR_SXX -REGR_SXY -REGR_SYY -REGR_VALX -REGR_VALY -REPEAT -REPLACE -REPLICATION_GROUP_DANGLING_REFERENCES -REPLICATION_GROUP_REFRESH_HISTORY -REPLICATION_GROUP_REFRESH_HISTORY_ALL -REPLICATION_GROUP_REFRESH_PROGRESS -REPLICATION_GROUP_REFRESH_PROGRESS_ALL -REPLICATION_GROUP_REFRESH_PROGRESS_BY_JOB -REPLICATION_GROUP_USAGE_HISTORY -REPLICATION_USAGE_HISTORY -REST_EVENT_HISTORY -RESULT_SCAN -REVERSE -RIGHT -RLIKE -ROUND -ROW_COUNT -ROW_NUMBER -RPAD -RTRIM -RTRIMMED_LENGTH -SANITIZE_WEBHOOK_CONTENT -SCHEDULED_TIME -SEARCH -SEARCH_IP -SEARCH_OPTIMIZATION_HISTORY -SEARCH_PREVIEW -SECOND -SENTIMENT -SEQ1 -SEQ2 -SEQ4 -SEQ8 -SERVERLESS_ALERT_HISTORY -SERVERLESS_TASK_HISTORY -SET_SYS_CONTEXT -SHA1 -SHA1_BINARY -SHA1_HEX -SHA2 -SHA2_BINARY -SHA2_HEX -SHOW_PYTHON_PACKAGES_DEPENDENCIES -SIGN -SIN -SINH -SKEW -SOUNDEX -SOUNDEX_P123 -SPACE -SPLIT -SPLIT_PART -SPLIT_TEXT_MARKDOWN_HEADER -SPLIT_TEXT_RECURSIVE_CHARACTER -SPLIT_TO_TABLE -SQRT -SQUARE -STAGE_DIRECTORY_FILE_REGISTRATION_HISTORY -STAGE_STORAGE_USAGE_HISTORY -STARTSWITH -STDDEV -STDDEV_POP -STDDEV_SAMP -STORAGE_LIFECYCLE_POLICY_HISTORY -STRIP_NULL_VALUE -STRTOK -STRTOK_SPLIT_TO_TABLE -STRTOK_TO_ARRAY -ST_AREA -ST_ASBINARY -ST_ASEWKB -ST_ASEWKT -ST_ASGEOJSON -ST_ASTEXT -ST_ASWKB -ST_ASWKT -ST_AZIMUTH -ST_BUFFER -ST_CENTROID -ST_COLLECT -ST_CONTAINS -ST_COVEREDBY -ST_COVERS -ST_DIFFERENCE -ST_DIMENSION -ST_DISJOINT -ST_DISTANCE -ST_DWITHIN -ST_ENDPOINT -ST_ENVELOPE -ST_GEOGFROMGEOHASH -ST_GEOGPOINTFROMGEOHASH -ST_GEOGRAPHYFROMWKB -ST_GEOGRAPHYFROMWKT -ST_GEOHASH -ST_GEOMETRYFROMWKB -ST_GEOMETRYFROMWKT -ST_GEOMFROMGEOHASH -ST_GEOMPOINT -ST_GEOMPOINTFROMGEOHASH -ST_HAUSDORFFDISTANCE -ST_INTERPOLATE -ST_INTERSECTION -ST_INTERSECTION_AGG -ST_INTERSECTS -ST_ISVALID -ST_LENGTH -ST_MAKEGEOMPOINT -ST_MAKELINE -ST_MAKEPOINT -ST_MAKEPOLYGON -ST_MAKEPOLYGONORIENTED -ST_NPOINTS -ST_NUMPOINTS -ST_PERIMETER -ST_POINT -ST_POINTN -ST_POLYGON -ST_SETSRID -ST_SIMPLIFY -ST_SRID -ST_STARTPOINT -ST_SYMDIFFERENCE -ST_TRANSFORM -ST_UNION -ST_UNION_AGG -ST_WITHIN -ST_X -ST_XMAX -ST_XMIN -ST_Y -ST_YMAX -ST_YMIN -SUBSTR -SUBSTRING -SUM -SYSDATE -SYSTEM$ABORT_SESSION -SYSTEM$ABORT_TRANSACTION -SYSTEM$ACTIVATE_CMK_INFO -SYSTEM$ACTIVATE_CMK_INFO_POSTGRES -SYSTEM$ADD_EVENT -SYSTEM$ADD_REFERENCE -SYSTEM$ALLOWLIST -SYSTEM$ALLOWLIST_PRIVATELINK -SYSTEM$APPLICATION_GET_LOG_LEVEL -SYSTEM$APPLICATION_GET_METRIC_LEVEL -SYSTEM$APPLICATION_GET_TRACE_LEVEL -SYSTEM$APP_COMPATIBILITY_CHECK -SYSTEM$AUTHORIZE_PRIVATELINK -SYSTEM$AUTHORIZE_STAGE_PRIVATELINK_ACCESS -SYSTEM$AUTO_REFRESH_STATUS -SYSTEM$BEGIN_DEBUG_APPLICATION -SYSTEM$BEHAVIOR_CHANGE_BUNDLE_STATUS -SYSTEM$BLOCK_INTERNAL_STAGES_PUBLIC_ACCESS -SYSTEM$BLOCK_INTERNAL_STAGES_PUBLIC_ACCESS_WITH_EXCEPTION -SYSTEM$CANCEL_ALL_QUERIES -SYSTEM$CANCEL_QUERY -SYSTEM$CATALOG_LINK_STATUS -SYSTEM$CKE_HASH_FUNCTION -SYSTEM$CLEANUP_DATABASE_ROLE_GRANTS -SYSTEM$CLIENT_VERSION_INFO -SYSTEM$CLIENT_VULNERABILITY_INFO -SYSTEM$CLUSTERING_DEPTH -SYSTEM$CLUSTERING_INFORMATION -SYSTEM$COMMIT_MOVE_ORGANIZATION_ACCOUNT -SYSTEM$CONVERT_PIPES_SQS_TO_SNS -SYSTEM$CREATE_BILLING_EVENT -SYSTEM$CREATE_BILLING_EVENTS -SYSTEM$CURRENT_USER_TASK_NAME -SYSTEM$DATABASE_REFRESH_PROGRESS -SYSTEM$DATA_METRIC_SCAN -SYSTEM$DEACTIVATE_CMK_INFO -SYSTEM$DECODE_PAT -SYSTEM$DEPROVISION_PRIVATELINK_ENDPOINT -SYSTEM$DEPROVISION_PRIVATELINK_ENDPOINT_TSS -SYSTEM$DEREGISTER_CMK_INFO -SYSTEM$DEREGISTER_CMK_INFO_POSTGRES -SYSTEM$DESC_ICEBERG_ACCESS_IDENTITY -SYSTEM$DISABLE_BEHAVIOR_CHANGE_BUNDLE -SYSTEM$DISABLE_DATABASE_REPLICATION -SYSTEM$DISABLE_GLOBAL_DATA_SHARING_FOR_ACCOUNT -SYSTEM$DISABLE_PREVIEW_ACCESS -SYSTEM$DISABLE_PRIVATELINK_ACCESS_ONLY -SYSTEM$ENABLE_BEHAVIOR_CHANGE_BUNDLE -SYSTEM$ENABLE_GLOBAL_DATA_SHARING_FOR_ACCOUNT -SYSTEM$ENABLE_PREVIEW_ACCESS -SYSTEM$ENCODE_CKE_PRIMARY_KEY -SYSTEM$END_DEBUG_APPLICATION -SYSTEM$ENFORCE_PRIVATELINK_ACCESS_ONLY -SYSTEM$ESTIMATE_AUTOMATIC_CLUSTERING_COSTS -SYSTEM$ESTIMATE_QUERY_ACCELERATION -SYSTEM$ESTIMATE_SEARCH_OPTIMIZATION_COSTS -SYSTEM$EVALUATE_DATA_QUALITY_EXPECTATIONS -SYSTEM$EVALUATE_DATA_QUALITY_EXPECTATIONS_PERSIST_RESULT -SYSTEM$EXPLAIN_JSON_TO_TEXT -SYSTEM$EXPLAIN_PLAN_JSON -SYSTEM$EXPORT_TDS_FROM_SEMANTIC_VIEW -SYSTEM$EXTERNAL_TABLE_PIPE_STATUS -SYSTEM$FINISH_OAUTH_FLOW -SYSTEM$GENERATE_SAML_CSR -SYSTEM$GENERATE_SCIM_ACCESS_TOKEN -SYSTEM$GET_ALL_DEFAULT_COLUMNS_OVERRIDES -SYSTEM$GET_ALL_REFERENCES -SYSTEM$GET_AWS_SNS_IAM_POLICY -SYSTEM$GET_CATALOG_LINKED_DATABASE_CONFIG -SYSTEM$GET_CLASSIFICATION_RESULT -SYSTEM$GET_CMK_AKV_CONSENT_URL -SYSTEM$GET_CMK_CONFIG -SYSTEM$GET_CMK_CONFIG_POSTGRES -SYSTEM$GET_CMK_INFO -SYSTEM$GET_CMK_INFO_POSTGRES -SYSTEM$GET_CMK_KMS_KEY_POLICY -SYSTEM$GET_COMPUTE_POOL_PENDING_MAINTENANCE -SYSTEM$GET_DBT_LOG -SYSTEM$GET_DEBUG_STATUS -SYSTEM$GET_DEFAULT_COLUMNS_OVERRIDE_FOR_SHOW_COMMAND -SYSTEM$GET_DEFAULT_COLUMNS_OVERRIDE_FOR_SYSTEM_OBJECT -SYSTEM$GET_DIRECTORY_TABLE_STATUS -SYSTEM$GET_GCP_KMS_CMK_GRANT_ACCESS_CMD -SYSTEM$GET_HASH_FOR_APPLICATION -SYSTEM$GET_ICEBERG_TABLE_INFORMATION -SYSTEM$GET_INSTANCE_FAMILY_PLACEMENT_GROUPS -SYSTEM$GET_LOGIN_FAILURE_DETAILS -SYSTEM$GET_PREDECESSOR_RETURN_VALUE -SYSTEM$GET_PREVIEW_ACCESS_STATUS -SYSTEM$GET_PRIVATELINK -SYSTEM$GET_PRIVATELINK_AUTHORIZED_ENDPOINTS -SYSTEM$GET_PRIVATELINK_CONFIG -SYSTEM$GET_PRIVATELINK_ENDPOINTS_INFO -SYSTEM$GET_PRIVATELINK_ENDPOINT_REGISTRATIONS -SYSTEM$GET_PURCHASE_ATTRIBUTES -SYSTEM$GET_REFERENCED_OBJECT_ID_HASH -SYSTEM$GET_RESULTSET_STATUS -SYSTEM$GET_SERVICE_DNS_DOMAIN -SYSTEM$GET_SERVICE_LOGS -SYSTEM$GET_SNOWFLAKE_EGRESS_IP_RANGES -SYSTEM$GET_SNOWFLAKE_PLATFORM_INFO -SYSTEM$GET_STAGE_PRIVATELINK_AUTHORIZED_ENDPOINTS -SYSTEM$GET_TAG -SYSTEM$GET_TAG_ALLOWED_VALUES -SYSTEM$GET_TAG_ON_CURRENT_COLUMN -SYSTEM$GET_TAG_ON_CURRENT_TABLE -SYSTEM$GET_TASK_GRAPH_CONFIG -SYSTEM$GLOBAL_ACCOUNT_SET_PARAMETER -SYSTEM$HOLD_PRIVILEGE_ON_ACCOUNT -SYSTEM$INITIATE_MOVE_ORGANIZATION_ACCOUNT -SYSTEM$INTERNAL_STAGES_PUBLIC_ACCESS_STATUS -SYSTEM$IS_APPLICATION_ALL_MANDATORY_TELEMETRY_EVENT_DEFINITIONS_ENABLED -SYSTEM$IS_APPLICATION_AUTHORIZED_FOR_TELEMETRY_EVENT_SHARING -SYSTEM$IS_APPLICATION_INSTALLED_FROM_SAME_ACCOUNT -SYSTEM$IS_APPLICATION_SHARING_EVENTS_WITH_PROVIDER -SYSTEM$IS_GLOBAL_DATA_SHARING_ENABLED_FOR_ACCOUNT -SYSTEM$IS_LISTING_PURCHASED -SYSTEM$IS_LISTING_TRIAL -SYSTEM$LAST_CHANGE_COMMIT_TIME -SYSTEM$LINK_ACCOUNT_OBJECTS_BY_NAME -SYSTEM$LINK_ORGANIZATION_USER -SYSTEM$LINK_ORGANIZATION_USER_GROUP -SYSTEM$LIST_APPLICATION_RESTRICTED_FEATURES -SYSTEM$LIST_ICEBERG_TABLES_FROM_CATALOG -SYSTEM$LIST_NAMESPACES_FROM_CATALOG -SYSTEM$LOCATE_DBT_ARCHIVE -SYSTEM$LOCATE_DBT_ARTIFACTS -SYSTEM$LOG -SYSTEM$MIGRATE_SAML_IDP_REGISTRATION -SYSTEM$OPT_IN_INTERNAL_STAGE_NETWORK_LOGS -SYSTEM$OPT_OUT_INTERNAL_STAGE_NETWORK_LOGS -SYSTEM$OPT_OUT_MALICIOUS_IP_PROTECTION_BY_CATEGORY -SYSTEM$PIPE_FORCE_RESUME -SYSTEM$PIPE_REBINDING_WITH_NOTIFICATION_CHANNEL -SYSTEM$PIPE_STATUS -SYSTEM$PROVISION_PRIVATELINK_ENDPOINT -SYSTEM$PROVISION_PRIVATELINK_ENDPOINT_TSS -SYSTEM$QUERY_REFERENCE -SYSTEM$READ_OSI_YAML_FROM_SEMANTIC_VIEW -SYSTEM$READ_YAML_FROM_SEMANTIC_VIEW -SYSTEM$REFERENCE -SYSTEM$REGISTER_CMK_INFO -SYSTEM$REGISTER_CMK_INFO_POSTGRES -SYSTEM$REGISTER_PRIVATELINK_ENDPOINT -SYSTEM$REMOVE_ALL_REFERENCES -SYSTEM$REMOVE_REFERENCE -SYSTEM$REPORT_HEALTH_STATUS -SYSTEM$RESOLVE_PYTHON_PACKAGES -SYSTEM$RESTORE_PRIVATELINK_ENDPOINT -SYSTEM$RESTORE_PRIVATELINK_ENDPOINT_TSS -SYSTEM$REVOKE_PRIVATELINK -SYSTEM$REVOKE_STAGE_PRIVATELINK_ACCESS -SYSTEM$SCHEDULE_ASYNC_REPLICATION_GROUP_REFRESH -SYSTEM$SEND_NOTIFICATIONS_TO_CATALOG -SYSTEM$SET_APPLICATION_RESTRICTED_FEATURE_ACCESS -SYSTEM$SET_CATALOG_INTEGRATION -SYSTEM$SET_DEFAULT_COLUMNS_OVERRIDE_FOR_SHOW_COMMAND -SYSTEM$SET_DEFAULT_COLUMNS_OVERRIDE_FOR_SYSTEM_OBJECT -SYSTEM$SET_EVENT_SHARING_ACCOUNT_FOR_REGION -SYSTEM$SET_PRIVATELINK_ENDPOINT_HOSTNAME -SYSTEM$SET_REFERENCE -SYSTEM$SET_RETURN_VALUE -SYSTEM$SET_ROW_TIMESTAMP_ON_ALL_SUPPORTED_TABLES -SYSTEM$SET_SPAN_ATTRIBUTES -SYSTEM$SHOW_ACTIVE_BEHAVIOR_CHANGE_BUNDLES -SYSTEM$SHOW_BUDGETS_FOR_RESOURCE -SYSTEM$SHOW_BUDGETS_IN_ACCOUNT -SYSTEM$SHOW_EVENT_SHARING_ACCOUNTS -SYSTEM$SHOW_MOVE_ORGANIZATION_ACCOUNT_STATUS -SYSTEM$SHOW_OAUTH_CLIENT_SECRETS -SYSTEM$SHOW_SENSITIVE_DATA_MONITORED_ENTITIES -SYSTEM$SNOWPIPE_STREAMING_UPDATE_CHANNEL_OFFSET_TOKEN -SYSTEM$START_OAUTH_FLOW -SYSTEM$START_USER_EMAIL_VERIFICATION -SYSTEM$STREAM_BACKLOG -SYSTEM$STREAM_GET_TABLE_TIMESTAMP -SYSTEM$STREAM_HAS_DATA -SYSTEM$SUPPORTED_DBT_VERSIONS -SYSTEM$TASK_DEPENDENTS_ENABLE -SYSTEM$TASK_RUNTIME_INFO -SYSTEM$TRIGGER_LISTING_REFRESH -SYSTEM$TYPEOF -SYSTEM$UNBLOCK_INTERNAL_STAGES_PUBLIC_ACCESS -SYSTEM$UNLINK_ORGANIZATION_USER -SYSTEM$UNLINK_ORGANIZATION_USER_GROUP -SYSTEM$UNREGISTER_PRIVATELINK_ENDPOINT -SYSTEM$UNSET_DEFAULT_COLUMNS_OVERRIDE_FOR_SHOW_COMMAND -SYSTEM$UNSET_DEFAULT_COLUMNS_OVERRIDE_FOR_SYSTEM_OBJECT -SYSTEM$UNSET_EVENT_SHARING_ACCOUNT_FOR_REGION -SYSTEM$USER_TASK_CANCEL_ONGOING_EXECUTIONS -SYSTEM$VALIDATE_STORAGE_INTEGRATION -SYSTEM$VERIFY_CATALOG_INTEGRATION -SYSTEM$VERIFY_CMK_INFO -SYSTEM$VERIFY_CMK_INFO_POSTGRES -SYSTEM$VERIFY_EXTERNAL_OAUTH_TOKEN -SYSTEM$VERIFY_EXTERNAL_VOLUME -SYSTEM$WAIT -SYSTEM$WAIT_FOR_SERVICES -SYSTIMESTAMP -SYS_CONNECT_BY_PATH -SYS_CONTEXT -TAG_REFERENCES -TAG_REFERENCES_ALL_COLUMNS -TAG_REFERENCES_WITH_LINEAGE -TAN -TANH -TASK_DEPENDENTS -TASK_HISTORY -TEXT_HTML -TEXT_PLAIN -TIME -TIMEADD -TIMEDIFF -TIMESTAMPADD -TIMESTAMPDIFF -TIMESTAMP_FROM_PARTS -TIME_FROM_PARTS -TIME_SLICE -TO_ARRAY -TO_BINARY -TO_BOOLEAN -TO_CHAR -TO_DATE -TO_DECFLOAT -TO_DECIMAL -TO_DOUBLE -TO_FILE -TO_GEOGRAPHY -TO_GEOMETRY -TO_JSON -TO_NUMBER -TO_NUMERIC -TO_OBJECT -TO_QUERY -TO_TIME -TO_TIMESTAMP -TO_TIMESTAMP_LTZ -TO_TIMESTAMP_NTZ -TO_TIMESTAMP_TZ -TO_UUID -TO_VARCHAR -TO_VARIANT -TO_XML -TRANSFORM -TRANSLATE -TRIM -TRUNC -TRUNCATE -TRY_BASE64_DECODE_BINARY -TRY_BASE64_DECODE_STRING -TRY_CAST -TRY_COMPLETE -TRY_DECRYPT -TRY_DECRYPT_RAW -TRY_HEX_DECODE_BINARY -TRY_HEX_DECODE_STRING -TRY_PARSE_JSON -TRY_TO_BINARY -TRY_TO_BOOLEAN -TRY_TO_DATE -TRY_TO_DECFLOAT -TRY_TO_DECIMAL -TRY_TO_DOUBLE -TRY_TO_FILE -TRY_TO_GEOGRAPHY -TRY_TO_GEOMETRY -TRY_TO_NUMBER -TRY_TO_NUMERIC -TRY_TO_TIME -TRY_TO_TIMESTAMP -TRY_TO_TIMESTAMP_LTZ -TRY_TO_TIMESTAMP_NTZ -TRY_TO_TIMESTAMP_TZ -TRY_TO_UUID -TYPEOF -UNICODE -UNIFORM -UNIQUE_COUNT -UPPER -UUID_STRING -VALIDATE -VALIDATE_PIPE_LOAD -VARIANCE -VARIANCE_POP -VARIANCE_SAMP -VAR_POP -VAR_SAMP -VECTOR_AVG -VECTOR_COSINE_SIMILARITY -VECTOR_INNER_PRODUCT -VECTOR_L1_DISTANCE -VECTOR_L2_DISTANCE -VECTOR_MAX -VECTOR_MIN -VECTOR_NORMALIZE -VECTOR_SUM -VECTOR_TRUNCATE -WAREHOUSE_LOAD_HISTORY -WAREHOUSE_METERING_HISTORY -WEEK -WEEKISO -WEEKOFYEAR -WIDTH_BUCKET -XMLGET -YEAR -YEAROFWEEK -YEAROFWEEKISO -ZEROIFNULL -ZIPF diff --git a/crates/sqlbuild-analysis/src/semantic_validation/_helpers/sqlite_temporal_casts.rs b/crates/sqlbuild-analysis/src/semantic_validation/_helpers/sqlite_temporal_casts.rs index c3a37312e8..fe304fd8b0 100644 --- a/crates/sqlbuild-analysis/src/semantic_validation/_helpers/sqlite_temporal_casts.rs +++ b/crates/sqlbuild-analysis/src/semantic_validation/_helpers/sqlite_temporal_casts.rs @@ -35,12 +35,11 @@ pub(crate) fn sqlite_temporal_cast_warnings( '2026-01-01 10:00:00' becomes 2026", cast.type_name ); - cursor.advance(sql, cast.start); - let start: usize = cursor.characters; - let end: usize = start + sql[cast.start..cast.end].chars().count(); - ValidationError::warning(message, SQLITE_TEMPORAL_CAST_CODE) - .with_location(cursor.line, cursor.column) - .with_span(Some(start), Some(end)) + cursor.locate( + sql, + (cast.start, cast.end), + ValidationError::warning(message, SQLITE_TEMPORAL_CAST_CODE), + ) }) .collect() } diff --git a/crates/sqlbuild-analysis/src/semantic_validation/_helpers/truth_tests.rs b/crates/sqlbuild-analysis/src/semantic_validation/_helpers/truth_tests.rs index 896b12f20d..350eac4856 100644 --- a/crates/sqlbuild-analysis/src/semantic_validation/_helpers/truth_tests.rs +++ b/crates/sqlbuild-analysis/src/semantic_validation/_helpers/truth_tests.rs @@ -37,12 +37,11 @@ pub(crate) fn unsupported_truth_test_errors( spelling(&test), rewrite(dialect, &test) ); - cursor.advance(sql, test.start); - let start: usize = cursor.characters; - let end: usize = start + sql[test.start..test.end].chars().count(); - ValidationError::error(message, UNSUPPORTED_TRUTH_TEST_CODE) - .with_location(cursor.line, cursor.column) - .with_span(Some(start), Some(end)) + cursor.locate( + sql, + (test.start, test.end), + ValidationError::error(message, UNSUPPORTED_TRUTH_TEST_CODE), + ) }) .collect() } diff --git a/crates/sqlbuild-analysis/src/semantic_validation/main/map_diagnostics.rs b/crates/sqlbuild-analysis/src/semantic_validation/main/map_diagnostics.rs index 73e179774e..75bfd83774 100644 --- a/crates/sqlbuild-analysis/src/semantic_validation/main/map_diagnostics.rs +++ b/crates/sqlbuild-analysis/src/semantic_validation/main/map_diagnostics.rs @@ -1,11 +1,9 @@ -use crate::semantic_validation::models::FunctionProbes; use polyglot_sql::{DialectType, ValidationResult}; pub(crate) fn map_diagnostics( sql: &str, dialect: DialectType, result: ValidationResult, - probes: &FunctionProbes, ) -> Result { - crate::semantic_validation::_helpers::diagnostics::map_diagnostics(sql, dialect, result, probes) + crate::semantic_validation::_helpers::diagnostics::map_diagnostics(sql, dialect, result) } diff --git a/crates/sqlbuild-analysis/src/semantic_validation/models.rs b/crates/sqlbuild-analysis/src/semantic_validation/models.rs index a24345c77a..a6f184a32c 100644 --- a/crates/sqlbuild-analysis/src/semantic_validation/models.rs +++ b/crates/sqlbuild-analysis/src/semantic_validation/models.rs @@ -1,6 +1,6 @@ //! Compile-owned native catalog, mapping state and boundary requests. -use crate::semantic_validation::types::{PreparedCompactAnalysis, ProbeKey, Relations}; +use crate::semantic_validation::types::{PreparedCompactAnalysis, Relations}; use polyglot_sql::validation::SchemaTable; use polyglot_sql::{DialectType, SchemaValidationOptions}; use rayon::ThreadPool; @@ -65,12 +65,6 @@ pub struct PositionInput { pub passes: Vec>, } -/// Memoized call-site function-name probes, owned by the catalog that validates a compile. -#[derive(Debug, Default)] -pub(crate) struct FunctionProbes { - pub(crate) suggestions: Mutex>>, -} - pub struct ProjectCatalog { pub(crate) dialect: DialectType, pub(crate) options: SchemaValidationOptions, @@ -78,7 +72,6 @@ pub struct ProjectCatalog { pub(super) tables: HashMap, pub(super) overrides: Vec>, pub(super) analysis_tables: HashMap, - pub(crate) function_probes: Arc, /// One analysis pool per compile catalog, built on first use and shared by its views. pub(super) analysis_pool: Arc>>>, /// Analysis batches running on the pool; preparation only borrows the pool while none run. diff --git a/crates/sqlbuild-analysis/src/semantic_validation/tests/test_function_names.rs b/crates/sqlbuild-analysis/src/semantic_validation/tests/test_function_names.rs index 11698e22b1..9003dcfa2a 100644 --- a/crates/sqlbuild-analysis/src/semantic_validation/tests/test_function_names.rs +++ b/crates/sqlbuild-analysis/src/semantic_validation/tests/test_function_names.rs @@ -1,6 +1,5 @@ use crate::semantic_validation::_helpers::diagnostics::map_diagnostics; use crate::semantic_validation::_helpers::function_names::unsupported_calls; -use crate::semantic_validation::models::FunctionProbes; use crate::semantic_validation::tests::test_types::{ LocatedFunctionDiagnosticTestCase, UnsupportedFunctionTestCase, }; @@ -11,25 +10,25 @@ fn given_unsupported_builtin_spelling_when_scanning_then_reports_dialect_spellin let test_cases = [ UnsupportedFunctionTestCase { description: "Snowflake STARTS_WITH suggests STARTSWITH", - dialect: DialectType::Snowflake, + dialect: "snowflake", sql: "SELECT STARTS_WITH(product_name, 'a') AS flagged FROM products", expected_calls: &[("STARTS_WITH", "STARTSWITH")], }, UnsupportedFunctionTestCase { description: "matching is case-insensitive and keeps the authored spelling", - dialect: DialectType::Snowflake, + dialect: "snowflake", sql: "select ends_with(product_name, 'a'), lcase(product_name) from products", expected_calls: &[("ends_with", "ENDSWITH"), ("lcase", "LOWER")], }, UnsupportedFunctionTestCase { description: "conditional and length aliases suggest the Snowflake spelling", - dialect: DialectType::Snowflake, + dialect: "snowflake", sql: "SELECT IF(quantity > 0, 1, 0), CHAR_LENGTH(product_name) FROM products", expected_calls: &[("IF", "IFF"), ("CHAR_LENGTH", "LENGTH")], }, UnsupportedFunctionTestCase { description: "calls inside windows, lambdas and table functions are checked", - dialect: DialectType::Snowflake, + dialect: "snowflake", sql: "SELECT SUM(quantity) OVER (PARTITION BY UCASE(category)), \ FILTER(tags, tag -> STARTS_WITH(tag, 'a')) \ FROM products, TABLE(FLATTEN(input => ARRAY_CONSTRUCT(LCASE(sku))))", @@ -41,26 +40,26 @@ fn given_unsupported_builtin_spelling_when_scanning_then_reports_dialect_spellin }, UnsupportedFunctionTestCase { description: "an object literal value after a string key is checked", - dialect: DialectType::Snowflake, + dialect: "snowflake", sql: "SELECT {'featured': STARTS_WITH(product_name, 'a')} FROM products", expected_calls: &[("STARTS_WITH", "STARTSWITH")], }, UnsupportedFunctionTestCase { description: "a MATCH_RECOGNIZE DEFINE condition after AS is checked", - dialect: DialectType::Snowflake, + dialect: "snowflake", sql: "SELECT * FROM products MATCH_RECOGNIZE (ORDER BY sku PATTERN (featured) \ DEFINE featured AS STARTS_WITH(product_name, 'a'))", expected_calls: &[("STARTS_WITH", "STARTSWITH")], }, UnsupportedFunctionTestCase { description: "comments between the name and its parenthesis are skipped", - dialect: DialectType::Snowflake, + dialect: "snowflake", sql: "SELECT LCASE /* lower */ (sku), UCASE -- upper\n (sku) FROM products", expected_calls: &[("LCASE", "LOWER"), ("UCASE", "UPPER")], }, UnsupportedFunctionTestCase { description: "operand FROM and select lists inside a FROM subquery are checked", - dialect: DialectType::Snowflake, + dialect: "snowflake", sql: "SELECT TRIM(BOTH 'x' FROM LCASE(sku)), sku IS DISTINCT FROM UCASE(sku) \ FROM (SELECT sku, LCASE(sku) AS lowered FROM products) AS listed, catalog \ WHERE sku IN (1, 2) AND LCASE(sku) = 'a'", @@ -73,19 +72,19 @@ fn given_unsupported_builtin_spelling_when_scanning_then_reports_dialect_spellin }, UnsupportedFunctionTestCase { description: "a call followed by AS NOT without MATERIALIZED is still checked", - dialect: DialectType::DuckDB, + dialect: "duckdb", sql: "SELECT STARTSWITH(product_name, 'a') AS not_featured FROM products", expected_calls: &[("STARTSWITH", "STARTS_WITH")], }, UnsupportedFunctionTestCase { description: "DuckDB STARTSWITH suggests the catalogue spelling", - dialect: DialectType::DuckDB, + dialect: "duckdb", sql: "SELECT STARTSWITH(product_name, 'a') FROM products", expected_calls: &[("STARTSWITH", "STARTS_WITH")], }, ]; for test_case in &test_cases { - let calls = unsupported_calls(test_case.sql, test_case.dialect, &FunctionProbes::default()); + let calls = unsupported_calls(test_case.sql, test_case.dialect); let observed: Vec<(&str, &str)> = calls .iter() .map(|call| { @@ -109,7 +108,7 @@ fn given_supported_or_non_builtin_call_when_scanning_then_reports_nothing() { let test_cases = [ UnsupportedFunctionTestCase { description: "listed Snowflake synonyms are accepted", - dialect: DialectType::Snowflake, + dialect: "snowflake", sql: "SELECT STARTSWITH(a, 'x'), ENDSWITH(a, 'x'), CONTAINS(a, 'x'), SUBSTR(a, 1, 2), \ SUBSTRING(a, 1, 2), TIMESTAMPDIFF(day, b, c), DATEDIFF(day, b, c), \ TRY_TO_DECIMAL(a, 10, 2), TRY_TO_NUMBER(a), IFF(d, 1, 0), LEN(a), \ @@ -119,14 +118,14 @@ fn given_supported_or_non_builtin_call_when_scanning_then_reports_nothing() { }, UnsupportedFunctionTestCase { description: "quoted identifiers, literals and comments are not call sites", - dialect: DialectType::Snowflake, + dialect: "snowflake", sql: "SELECT \"STARTS_WITH\"(a, 'x'), 'STARTS_WITH(a, b)', $$LCASE(a)$$ \ -- STARTS_WITH(a, 'x')\n /* LCASE(a) */ FROM products", expected_calls: &[], }, UnsupportedFunctionTestCase { description: "methods, qualified functions, sentinels and macros are not checked", - dialect: DialectType::Snowflake, + dialect: "snowflake", sql: "SELECT tools.starts_with(a, 'x'), analytics.helpers.lcase(a), \ __sqlbuild_udf_starts_with(a, 'x'), @py.starts_with(a), payload:lcase(a) \ FROM __ref(\"products\")", @@ -134,13 +133,13 @@ fn given_supported_or_non_builtin_call_when_scanning_then_reports_nothing() { }, UnsupportedFunctionTestCase { description: "unknown and user-defined function names are never flagged", - dialect: DialectType::Snowflake, + dialect: "snowflake", sql: "SELECT normalize_sku(a), DATE_ADD(day, 1, b), my_udf() FROM products", expected_calls: &[], }, UnsupportedFunctionTestCase { description: "type positions and keywords before parentheses are not calls", - dialect: DialectType::Snowflake, + dialect: "snowflake", sql: "WITH ranked (sku, position_rank) AS (SELECT DISTINCT (a), \ ROW_NUMBER() OVER (ORDER BY b) FROM products WHERE NOT (a IN (1, 2)) \ AND EXISTS (SELECT 1)) \ @@ -149,14 +148,14 @@ fn given_supported_or_non_builtin_call_when_scanning_then_reports_nothing() { }, UnsupportedFunctionTestCase { description: "CTE column lists are not calls", - dialect: DialectType::Snowflake, + dialect: "snowflake", sql: "WITH listed AS (SELECT 1 AS dow), day_of_week (dow) AS (SELECT 1), \ lcase(x) AS (SELECT 2) SELECT dow FROM day_of_week", expected_calls: &[], }, UnsupportedFunctionTestCase { description: "materialized CTE column lists are not calls", - dialect: DialectType::DuckDB, + dialect: "duckdb", sql: "WITH day_of_week (d) AS MATERIALIZED (SELECT 1), \ day_of_month (d) AS /* hint */ NOT -- keep\n MATERIALIZED (SELECT 2) \ SELECT * FROM day_of_week", @@ -164,7 +163,7 @@ fn given_supported_or_non_builtin_call_when_scanning_then_reports_nothing() { }, UnsupportedFunctionTestCase { description: "derived-table, table-function and table aliases are not calls", - dialect: DialectType::Snowflake, + dialect: "snowflake", sql: "SELECT x FROM (VALUES (1)) day_of_week (x), products lcase (y), \ TABLE(FLATTEN(input => tags)) ucase (z), day_of_month (w) \ JOIN char_length (v) ON TRUE", @@ -172,27 +171,27 @@ fn given_supported_or_non_builtin_call_when_scanning_then_reports_nothing() { }, UnsupportedFunctionTestCase { description: "INSERT and CREATE column lists are not calls", - dialect: DialectType::Snowflake, + dialect: "snowflake", sql: "CREATE TABLE day_of_week (dow INTEGER); CREATE VIEW lcase (x) AS SELECT 1; \ INSERT INTO ucase (x) SELECT 1; CREATE TABLE IF NOT EXISTS char_length (x INT)", expected_calls: &[], }, UnsupportedFunctionTestCase { description: "dialects without a spelling table are not checked", - dialect: DialectType::PostgreSQL, + dialect: "postgres", sql: "SELECT STARTS_WITH(a, 'x'), LCASE(a) FROM products", expected_calls: &[], }, UnsupportedFunctionTestCase { description: "DuckDB catalogue spellings are accepted", - dialect: DialectType::DuckDB, + dialect: "duckdb", sql: "SELECT starts_with(a, 'x'), prefix(a, 'x'), lcase(a), substr(a, 1, 2) \ FROM products", expected_calls: &[], }, ]; for test_case in &test_cases { - let calls = unsupported_calls(test_case.sql, test_case.dialect, &FunctionProbes::default()); + let calls = unsupported_calls(test_case.sql, test_case.dialect); let observed: Vec<(&str, &str)> = calls .iter() .map(|call| (call.name.as_str(), call.suggestion.as_str())) @@ -225,7 +224,6 @@ fn given_unsupported_spelling_when_mapping_diagnostics_then_returns_located_b101 valid: true, errors: Vec::new(), }, - &FunctionProbes::default(), )?; let [error] = result.errors.as_slice() else { return Err(format!("{}: {:?}", test_case.description, result.errors)); diff --git a/crates/sqlbuild-analysis/src/semantic_validation/tests/test_truth_tests.rs b/crates/sqlbuild-analysis/src/semantic_validation/tests/test_truth_tests.rs index f9ba73f68f..6b9865dd9b 100644 --- a/crates/sqlbuild-analysis/src/semantic_validation/tests/test_truth_tests.rs +++ b/crates/sqlbuild-analysis/src/semantic_validation/tests/test_truth_tests.rs @@ -1,6 +1,5 @@ use crate::semantic_validation::_helpers::diagnostics::map_diagnostics; use crate::semantic_validation::_helpers::truth_tests::{rejects_truth_tests, truth_tests}; -use crate::semantic_validation::models::FunctionProbes; use crate::semantic_validation::tests::test_types::{ LocatedTruthTestDiagnosticTestCase, TruthTestDialectTestCase, TruthTestScanTestCase, }; @@ -91,7 +90,6 @@ fn given_dialect_when_checking_truth_tests_then_only_rejecting_warehouses_report valid: true, errors: Vec::new(), }, - &FunctionProbes::default(), ) .expect(test_case.description); assert_eq!( @@ -162,7 +160,6 @@ fn given_rejected_truth_test_when_mapping_diagnostics_then_returns_located_b103( valid: true, errors: Vec::new(), }, - &FunctionProbes::default(), )?; let [error] = result.errors.as_slice() else { return Err(format!("{}: {:?}", test_case.description, result.errors)); diff --git a/crates/sqlbuild-analysis/src/semantic_validation/tests/test_types.rs b/crates/sqlbuild-analysis/src/semantic_validation/tests/test_types.rs index 201a5dc6fc..7fcc899e20 100644 --- a/crates/sqlbuild-analysis/src/semantic_validation/tests/test_types.rs +++ b/crates/sqlbuild-analysis/src/semantic_validation/tests/test_types.rs @@ -12,7 +12,7 @@ pub(crate) struct SemanticValidationBatchTestCase { pub(crate) struct UnsupportedFunctionTestCase { pub(crate) description: &'static str, - pub(crate) dialect: polyglot_sql::DialectType, + pub(crate) dialect: &'static str, pub(crate) sql: &'static str, pub(crate) expected_calls: &'static [(&'static str, &'static str)], } diff --git a/crates/sqlbuild-analysis/src/semantic_validation/types.rs b/crates/sqlbuild-analysis/src/semantic_validation/types.rs index 8ab94dd7a8..02480b2f00 100644 --- a/crates/sqlbuild-analysis/src/semantic_validation/types.rs +++ b/crates/sqlbuild-analysis/src/semantic_validation/types.rs @@ -5,7 +5,6 @@ pub type Relations = HashMap; pub type BindingRequest = (String, Vec<(String, bool)>, Relations); /// One analysis SQL with its relation stubs and placeholder defaults. pub type NormalizationRequest = (String, HashMap, HashMap); -pub(crate) type ProbeKey = (polyglot_sql::DialectType, String, usize); /// Compact analysis whose catalog schemas are resolved; it runs with an options-only catalog. pub(crate) type PreparedCompactAnalysis = Box Result + Send>;