use core::iter; use crate::tokenizer::Span; use super::{ AlterColumnOperation, AlterIndexOperation, AlterTableOperation, Array, Assignment, AssignmentTarget, CloseCursor, ClusteredIndex, ColumnDef, ColumnOption, ColumnOptionDef, ConflictTarget, ConnectBy, ConstraintCharacteristics, CopySource, CreateIndex, CreateTable, CreateTableOptions, Cte, Delete, DoUpdate, ExceptSelectItem, ExcludeSelectItem, Expr, ExprWithAlias, Fetch, FromTable, Function, FunctionArg, FunctionArgExpr, FunctionArgumentClause, FunctionArgumentList, FunctionArguments, GroupByExpr, HavingBound, IlikeSelectItem, Insert, Interpolate, InterpolateExpr, Join, JoinConstraint, JoinOperator, JsonPath, JsonPathElem, LateralView, MatchRecognizePattern, Measure, NamedWindowDefinition, ObjectName, Offset, OnConflict, OnConflictAction, OnInsert, OrderBy, OrderByExpr, Partition, PivotValueSource, ProjectionSelect, Query, ReferentialAction, RenameSelectItem, ReplaceSelectElement, ReplaceSelectItem, Select, SelectInto, SelectItem, SetExpr, SqlOption, Statement, Subscript, SymbolDefinition, TableAlias, TableAliasColumnDef, TableConstraint, TableFactor, TableOptionsClustered, TableWithJoins, Use, Value, Values, ViewColumnDef, WildcardAdditionalOptions, With, WithFill, }; /// Given an iterator of spans, return the [Span::union] of all spans. fn union_spans>(iter: I) -> Span { Span::union_iter(iter) } /// Trait for AST nodes that have a source location information. /// /// # Notes: /// /// Source [`Span`] are not yet complete. They may be missing: /// /// 1. keywords or other tokens /// 2. span information entirely, in which case they return [`Span::empty()`]. /// /// Note Some impl blocks (rendered below) are annotated with which nodes are /// missing spans. See [this ticket] for additional information and status. /// /// [this ticket]: https://github.com/apache/datafusion-sqlparser-rs/issues/1548 /// /// # Example /// ``` /// # use sqlparser::parser::{Parser, ParserError}; /// # use sqlparser::ast::Spanned; /// # use sqlparser::dialect::GenericDialect; /// # use sqlparser::tokenizer::Location; /// # fn main() -> Result<(), ParserError> { /// let dialect = GenericDialect {}; /// let sql = r#"SELECT * /// FROM table_1"#; /// let statements = Parser::new(&dialect) /// .try_with_sql(sql)? /// .parse_statements()?; /// // Get the span of the first statement (SELECT) /// let span = statements[0].span(); /// // statement starts at line 1, column 1 (1 based, not 0 based) /// assert_eq!(span.start, Location::new(1, 1)); /// // statement ends on line 2, column 15 /// assert_eq!(span.end, Location::new(2, 15)); /// # Ok(()) /// # } /// ``` /// pub trait Spanned { /// Return the [`Span`] (the minimum and maximum [`Location`]) for this AST /// node, by recursively combining the spans of its children. /// /// [`Location`]: crate::tokenizer::Location fn span(&self) -> Span; } impl Spanned for Query { fn span(&self) -> Span { let Query { with, body, order_by, limit, limit_by, offset, fetch, locks: _, // todo for_clause: _, // todo, mssql specific settings: _, // todo, clickhouse specific format_clause: _, // todo, clickhouse specific } = self; union_spans( with.iter() .map(|i| i.span()) .chain(core::iter::once(body.span())) .chain(order_by.as_ref().map(|i| i.span())) .chain(limit.as_ref().map(|i| i.span())) .chain(limit_by.iter().map(|i| i.span())) .chain(offset.as_ref().map(|i| i.span())) .chain(fetch.as_ref().map(|i| i.span())), ) } } impl Spanned for Offset { fn span(&self) -> Span { let Offset { value, rows: _, // enum } = self; value.span() } } impl Spanned for Fetch { fn span(&self) -> Span { let Fetch { with_ties: _, // bool percent: _, // bool quantity, } = self; quantity.as_ref().map_or(Span::empty(), |i| i.span()) } } impl Spanned for With { fn span(&self) -> Span { let With { with_token, recursive: _, // bool cte_tables, } = self; union_spans( core::iter::once(with_token.0.span).chain(cte_tables.iter().map(|item| item.span())), ) } } impl Spanned for Cte { fn span(&self) -> Span { let Cte { alias, query, from, materialized: _, // enum closing_paren_token, } = self; union_spans( core::iter::once(alias.span()) .chain(core::iter::once(query.span())) .chain(from.iter().map(|item| item.span)) .chain(core::iter::once(closing_paren_token.0.span)), ) } } /// # partial span /// /// [SetExpr::Table] is not implemented. impl Spanned for SetExpr { fn span(&self) -> Span { match self { SetExpr::Select(select) => select.span(), SetExpr::Query(query) => query.span(), SetExpr::SetOperation { op: _, set_quantifier: _, left, right, } => left.span().union(&right.span()), SetExpr::Values(values) => values.span(), SetExpr::Insert(statement) => statement.span(), SetExpr::Table(_) => Span::empty(), SetExpr::Update(statement) => statement.span(), } } } impl Spanned for Values { fn span(&self) -> Span { let Values { explicit_row: _, // bool, rows, } = self; union_spans( rows.iter() .map(|row| union_spans(row.iter().map(|expr| expr.span()))), ) } } /// # partial span /// /// Missing spans: /// - [Statement::CopyIntoSnowflake] /// - [Statement::CreateSecret] /// - [Statement::CreateRole] /// - [Statement::AlterRole] /// - [Statement::AttachDatabase] /// - [Statement::AttachDuckDBDatabase] /// - [Statement::DetachDuckDBDatabase] /// - [Statement::Drop] /// - [Statement::DropFunction] /// - [Statement::DropProcedure] /// - [Statement::DropSecret] /// - [Statement::Declare] /// - [Statement::CreateExtension] /// - [Statement::Fetch] /// - [Statement::Flush] /// - [Statement::Discard] /// - [Statement::SetRole] /// - [Statement::SetVariable] /// - [Statement::SetTimeZone] /// - [Statement::SetNames] /// - [Statement::SetNamesDefault] /// - [Statement::ShowFunctions] /// - [Statement::ShowVariable] /// - [Statement::ShowStatus] /// - [Statement::ShowVariables] /// - [Statement::ShowCreate] /// - [Statement::ShowColumns] /// - [Statement::ShowTables] /// - [Statement::ShowCollation] /// - [Statement::StartTransaction] /// - [Statement::SetTransaction] /// - [Statement::Comment] /// - [Statement::Commit] /// - [Statement::Rollback] /// - [Statement::CreateSchema] /// - [Statement::CreateDatabase] /// - [Statement::CreateFunction] /// - [Statement::CreateTrigger] /// - [Statement::DropTrigger] /// - [Statement::CreateProcedure] /// - [Statement::CreateMacro] /// - [Statement::CreateStage] /// - [Statement::Assert] /// - [Statement::Grant] /// - [Statement::Revoke] /// - [Statement::Deallocate] /// - [Statement::Execute] /// - [Statement::Prepare] /// - [Statement::Kill] /// - [Statement::ExplainTable] /// - [Statement::Explain] /// - [Statement::Savepoint] /// - [Statement::ReleaseSavepoint] /// - [Statement::Merge] /// - [Statement::Cache] /// - [Statement::UNCache] /// - [Statement::CreateSequence] /// - [Statement::CreateType] /// - [Statement::Pragma] /// - [Statement::LockTables] /// - [Statement::UnlockTables] /// - [Statement::Unload] /// - [Statement::OptimizeTable] impl Spanned for Statement { fn span(&self) -> Span { match self { Statement::Analyze { table_name, partitions, for_columns: _, columns, cache_metadata: _, noscan: _, compute_statistics: _, } => union_spans( core::iter::once(table_name.span()) .chain(partitions.iter().flat_map(|i| i.iter().map(|k| k.span()))) .chain(columns.iter().map(|i| i.span)), ), Statement::Truncate { table_names, partitions, table: _, only: _, identity: _, cascade: _, on_cluster: _, } => union_spans( table_names .iter() .map(|i| i.name.span()) .chain(partitions.iter().flat_map(|i| i.iter().map(|k| k.span()))), ), Statement::Msck { table_name, repair: _, partition_action: _, } => table_name.span(), Statement::Query(query) => query.span(), Statement::Insert(insert) => insert.span(), Statement::Install { extension_name } => extension_name.span, Statement::Load { extension_name } => extension_name.span, Statement::Directory { overwrite: _, local: _, path: _, file_format: _, source, } => source.span(), Statement::Call(function) => function.span(), Statement::Copy { source, to: _, target: _, options: _, legacy_options: _, values: _, } => source.span(), Statement::CopyIntoSnowflake { into: _, from_stage: _, from_stage_alias: _, stage_params: _, from_transformations: _, files: _, pattern: _, file_format: _, copy_options: _, validation_mode: _, } => Span::empty(), Statement::Close { cursor } => match cursor { CloseCursor::All => Span::empty(), CloseCursor::Specific { name } => name.span, }, Statement::Update { table, assignments, from, selection, returning, or: _, } => union_spans( core::iter::once(table.span()) .chain(assignments.iter().map(|i| i.span())) .chain(from.iter().map(|i| i.span())) .chain(selection.iter().map(|i| i.span())) .chain(returning.iter().flat_map(|i| i.iter().map(|k| k.span()))), ), Statement::Delete(delete) => delete.span(), Statement::CreateView { or_replace: _, materialized: _, name, columns, query, options, cluster_by, comment: _, with_no_schema_binding: _, if_not_exists: _, temporary: _, to, } => union_spans( core::iter::once(name.span()) .chain(columns.iter().map(|i| i.span())) .chain(core::iter::once(query.span())) .chain(core::iter::once(options.span())) .chain(cluster_by.iter().map(|i| i.span)) .chain(to.iter().map(|i| i.span())), ), Statement::CreateTable(create_table) => create_table.span(), Statement::CreateVirtualTable { name, if_not_exists: _, module_name, module_args, } => union_spans( core::iter::once(name.span()) .chain(core::iter::once(module_name.span)) .chain(module_args.iter().map(|i| i.span)), ), Statement::CreateIndex(create_index) => create_index.span(), Statement::CreateRole { .. } => Span::empty(), Statement::CreateSecret { .. } => Span::empty(), Statement::AlterTable { name, if_exists: _, only: _, operations, location: _, on_cluster, } => union_spans( core::iter::once(name.span()) .chain(operations.iter().map(|i| i.span())) .chain(on_cluster.iter().map(|i| i.span)), ), Statement::AlterIndex { name, operation } => name.span().union(&operation.span()), Statement::AlterView { name, columns, query, with_options, } => union_spans( core::iter::once(name.span()) .chain(columns.iter().map(|i| i.span)) .chain(core::iter::once(query.span())) .chain(with_options.iter().map(|i| i.span())), ), // These statements need to be implemented Statement::AlterRole { .. } => Span::empty(), Statement::AttachDatabase { .. } => Span::empty(), Statement::AttachDuckDBDatabase { .. } => Span::empty(), Statement::DetachDuckDBDatabase { .. } => Span::empty(), Statement::Drop { .. } => Span::empty(), Statement::DropFunction { .. } => Span::empty(), Statement::DropProcedure { .. } => Span::empty(), Statement::DropSecret { .. } => Span::empty(), Statement::Declare { .. } => Span::empty(), Statement::CreateExtension { .. } => Span::empty(), Statement::Fetch { .. } => Span::empty(), Statement::Flush { .. } => Span::empty(), Statement::Discard { .. } => Span::empty(), Statement::SetRole { .. } => Span::empty(), Statement::SetVariable { .. } => Span::empty(), Statement::SetTimeZone { .. } => Span::empty(), Statement::SetNames { .. } => Span::empty(), Statement::SetNamesDefault {} => Span::empty(), Statement::ShowFunctions { .. } => Span::empty(), Statement::ShowVariable { .. } => Span::empty(), Statement::ShowStatus { .. } => Span::empty(), Statement::ShowVariables { .. } => Span::empty(), Statement::ShowCreate { .. } => Span::empty(), Statement::ShowColumns { .. } => Span::empty(), Statement::ShowTables { .. } => Span::empty(), Statement::ShowCollation { .. } => Span::empty(), Statement::Use(u) => u.span(), Statement::StartTransaction { .. } => Span::empty(), Statement::SetTransaction { .. } => Span::empty(), Statement::Comment { .. } => Span::empty(), Statement::Commit { .. } => Span::empty(), Statement::Rollback { .. } => Span::empty(), Statement::CreateSchema { .. } => Span::empty(), Statement::CreateDatabase { .. } => Span::empty(), Statement::CreateFunction { .. } => Span::empty(), Statement::CreateTrigger { .. } => Span::empty(), Statement::DropTrigger { .. } => Span::empty(), Statement::CreateProcedure { .. } => Span::empty(), Statement::CreateMacro { .. } => Span::empty(), Statement::CreateStage { .. } => Span::empty(), Statement::Assert { .. } => Span::empty(), Statement::Grant { .. } => Span::empty(), Statement::Revoke { .. } => Span::empty(), Statement::Deallocate { .. } => Span::empty(), Statement::Execute { .. } => Span::empty(), Statement::Prepare { .. } => Span::empty(), Statement::Kill { .. } => Span::empty(), Statement::ExplainTable { .. } => Span::empty(), Statement::Explain { .. } => Span::empty(), Statement::Savepoint { .. } => Span::empty(), Statement::ReleaseSavepoint { .. } => Span::empty(), Statement::Merge { .. } => Span::empty(), Statement::Cache { .. } => Span::empty(), Statement::UNCache { .. } => Span::empty(), Statement::CreateSequence { .. } => Span::empty(), Statement::CreateType { .. } => Span::empty(), Statement::Pragma { .. } => Span::empty(), Statement::LockTables { .. } => Span::empty(), Statement::UnlockTables => Span::empty(), Statement::Unload { .. } => Span::empty(), Statement::OptimizeTable { .. } => Span::empty(), Statement::CreatePolicy { .. } => Span::empty(), Statement::AlterPolicy { .. } => Span::empty(), Statement::DropPolicy { .. } => Span::empty(), Statement::ShowDatabases { .. } => Span::empty(), Statement::ShowSchemas { .. } => Span::empty(), Statement::ShowViews { .. } => Span::empty(), Statement::LISTEN { .. } => Span::empty(), Statement::NOTIFY { .. } => Span::empty(), Statement::LoadData { .. } => Span::empty(), Statement::UNLISTEN { .. } => Span::empty(), } } } impl Spanned for Use { fn span(&self) -> Span { match self { Use::Catalog(object_name) => object_name.span(), Use::Schema(object_name) => object_name.span(), Use::Database(object_name) => object_name.span(), Use::Warehouse(object_name) => object_name.span(), Use::Object(object_name) => object_name.span(), Use::Default => Span::empty(), } } } impl Spanned for CreateTable { fn span(&self) -> Span { let CreateTable { or_replace: _, // bool temporary: _, // bool external: _, // bool global: _, // bool if_not_exists: _, // bool transient: _, // bool volatile: _, // bool name, columns, constraints, hive_distribution: _, // hive specific hive_formats: _, // hive specific table_properties, with_options, file_format: _, // enum location: _, // string, no span query, without_rowid: _, // bool like, clone, engine: _, // todo comment: _, // todo, no span auto_increment_offset: _, // u32, no span default_charset: _, // string, no span collation: _, // string, no span on_commit: _, // enum on_cluster: _, // todo, clickhouse specific primary_key: _, // todo, clickhouse specific order_by: _, // todo, clickhouse specific partition_by: _, // todo, BigQuery specific cluster_by: _, // todo, BigQuery specific clustered_by: _, // todo, Hive specific options: _, // todo, BigQuery specific strict: _, // bool copy_grants: _, // bool enable_schema_evolution: _, // bool change_tracking: _, // bool data_retention_time_in_days: _, // u64, no span max_data_extension_time_in_days: _, // u64, no span default_ddl_collation: _, // string, no span with_aggregation_policy: _, // todo, Snowflake specific with_row_access_policy: _, // todo, Snowflake specific with_tags: _, // todo, Snowflake specific } = self; union_spans( core::iter::once(name.span()) .chain(columns.iter().map(|i| i.span())) .chain(constraints.iter().map(|i| i.span())) .chain(table_properties.iter().map(|i| i.span())) .chain(with_options.iter().map(|i| i.span())) .chain(query.iter().map(|i| i.span())) .chain(like.iter().map(|i| i.span())) .chain(clone.iter().map(|i| i.span())), ) } } impl Spanned for ColumnDef { fn span(&self) -> Span { let ColumnDef { name, data_type: _, // enum collation, options, } = self; union_spans( core::iter::once(name.span) .chain(collation.iter().map(|i| i.span())) .chain(options.iter().map(|i| i.span())), ) } } impl Spanned for ColumnOptionDef { fn span(&self) -> Span { let ColumnOptionDef { name, option } = self; option.span().union_opt(&name.as_ref().map(|i| i.span)) } } impl Spanned for TableConstraint { fn span(&self) -> Span { match self { TableConstraint::Unique { name, index_name, index_type_display: _, index_type: _, columns, index_options: _, characteristics, } => union_spans( name.iter() .map(|i| i.span) .chain(index_name.iter().map(|i| i.span)) .chain(columns.iter().map(|i| i.span)) .chain(characteristics.iter().map(|i| i.span())), ), TableConstraint::PrimaryKey { name, index_name, index_type: _, columns, index_options: _, characteristics, } => union_spans( name.iter() .map(|i| i.span) .chain(index_name.iter().map(|i| i.span)) .chain(columns.iter().map(|i| i.span)) .chain(characteristics.iter().map(|i| i.span())), ), TableConstraint::ForeignKey { name, columns, foreign_table, referred_columns, on_delete, on_update, characteristics, } => union_spans( name.iter() .map(|i| i.span) .chain(columns.iter().map(|i| i.span)) .chain(core::iter::once(foreign_table.span())) .chain(referred_columns.iter().map(|i| i.span)) .chain(on_delete.iter().map(|i| i.span())) .chain(on_update.iter().map(|i| i.span())) .chain(characteristics.iter().map(|i| i.span())), ), TableConstraint::Check { name, expr } => { expr.span().union_opt(&name.as_ref().map(|i| i.span)) } TableConstraint::Index { display_as_key: _, name, index_type: _, columns, } => union_spans( name.iter() .map(|i| i.span) .chain(columns.iter().map(|i| i.span)), ), TableConstraint::FulltextOrSpatial { fulltext: _, index_type_display: _, opt_index_name, columns, } => union_spans( opt_index_name .iter() .map(|i| i.span) .chain(columns.iter().map(|i| i.span)), ), } } } impl Spanned for CreateIndex { fn span(&self) -> Span { let CreateIndex { name, table_name, using, columns, unique: _, // bool concurrently: _, // bool if_not_exists: _, // bool include, nulls_distinct: _, // bool with, predicate, } = self; union_spans( name.iter() .map(|i| i.span()) .chain(core::iter::once(table_name.span())) .chain(using.iter().map(|i| i.span)) .chain(columns.iter().map(|i| i.span())) .chain(include.iter().map(|i| i.span)) .chain(with.iter().map(|i| i.span())) .chain(predicate.iter().map(|i| i.span())), ) } } /// # partial span /// /// Missing spans: /// - [ColumnOption::Null] /// - [ColumnOption::NotNull] /// - [ColumnOption::Comment] /// - [ColumnOption::Unique]ยจ /// - [ColumnOption::DialectSpecific] /// - [ColumnOption::Generated] impl Spanned for ColumnOption { fn span(&self) -> Span { match self { ColumnOption::Null => Span::empty(), ColumnOption::NotNull => Span::empty(), ColumnOption::Default(expr) => expr.span(), ColumnOption::Materialized(expr) => expr.span(), ColumnOption::Ephemeral(expr) => expr.as_ref().map_or(Span::empty(), |e| e.span()), ColumnOption::Alias(expr) => expr.span(), ColumnOption::Unique { .. } => Span::empty(), ColumnOption::ForeignKey { foreign_table, referred_columns, on_delete, on_update, characteristics, } => union_spans( core::iter::once(foreign_table.span()) .chain(referred_columns.iter().map(|i| i.span)) .chain(on_delete.iter().map(|i| i.span())) .chain(on_update.iter().map(|i| i.span())) .chain(characteristics.iter().map(|i| i.span())), ), ColumnOption::Check(expr) => expr.span(), ColumnOption::DialectSpecific(_) => Span::empty(), ColumnOption::CharacterSet(object_name) => object_name.span(), ColumnOption::Comment(_) => Span::empty(), ColumnOption::OnUpdate(expr) => expr.span(), ColumnOption::Generated { .. } => Span::empty(), ColumnOption::Options(vec) => union_spans(vec.iter().map(|i| i.span())), ColumnOption::Identity(..) => Span::empty(), ColumnOption::OnConflict(..) => Span::empty(), ColumnOption::Policy(..) => Span::empty(), ColumnOption::Tags(..) => Span::empty(), } } } /// # missing span impl Spanned for ReferentialAction { fn span(&self) -> Span { Span::empty() } } /// # missing span impl Spanned for ConstraintCharacteristics { fn span(&self) -> Span { let ConstraintCharacteristics { deferrable: _, // bool initially: _, // enum enforced: _, // bool } = self; Span::empty() } } /// # partial span /// /// Missing spans: /// - [AlterColumnOperation::SetNotNull] /// - [AlterColumnOperation::DropNotNull] /// - [AlterColumnOperation::DropDefault] /// - [AlterColumnOperation::AddGenerated] impl Spanned for AlterColumnOperation { fn span(&self) -> Span { match self { AlterColumnOperation::SetNotNull => Span::empty(), AlterColumnOperation::DropNotNull => Span::empty(), AlterColumnOperation::SetDefault { value } => value.span(), AlterColumnOperation::DropDefault => Span::empty(), AlterColumnOperation::SetDataType { data_type: _, using, } => using.as_ref().map_or(Span::empty(), |u| u.span()), AlterColumnOperation::AddGenerated { .. } => Span::empty(), } } } impl Spanned for CopySource { fn span(&self) -> Span { match self { CopySource::Table { table_name, columns, } => union_spans( core::iter::once(table_name.span()).chain(columns.iter().map(|i| i.span)), ), CopySource::Query(query) => query.span(), } } } impl Spanned for Delete { fn span(&self) -> Span { let Delete { tables, from, using, selection, returning, order_by, limit, } = self; union_spans( tables .iter() .map(|i| i.span()) .chain(core::iter::once(from.span())) .chain( using .iter() .map(|u| union_spans(u.iter().map(|i| i.span()))), ) .chain(selection.iter().map(|i| i.span())) .chain(returning.iter().flat_map(|i| i.iter().map(|k| k.span()))) .chain(order_by.iter().map(|i| i.span())) .chain(limit.iter().map(|i| i.span())), ) } } impl Spanned for FromTable { fn span(&self) -> Span { match self { FromTable::WithFromKeyword(vec) => union_spans(vec.iter().map(|i| i.span())), FromTable::WithoutKeyword(vec) => union_spans(vec.iter().map(|i| i.span())), } } } impl Spanned for ViewColumnDef { fn span(&self) -> Span { let ViewColumnDef { name, data_type: _, // todo, DataType options, } = self; union_spans( core::iter::once(name.span) .chain(options.iter().flat_map(|i| i.iter().map(|k| k.span()))), ) } } impl Spanned for SqlOption { fn span(&self) -> Span { match self { SqlOption::Clustered(table_options_clustered) => table_options_clustered.span(), SqlOption::Ident(ident) => ident.span, SqlOption::KeyValue { key, value } => key.span.union(&value.span()), SqlOption::Partition { column_name, range_direction: _, for_values, } => union_spans( core::iter::once(column_name.span).chain(for_values.iter().map(|i| i.span())), ), } } } /// # partial span /// /// Missing spans: /// - [TableOptionsClustered::ColumnstoreIndex] impl Spanned for TableOptionsClustered { fn span(&self) -> Span { match self { TableOptionsClustered::ColumnstoreIndex => Span::empty(), TableOptionsClustered::ColumnstoreIndexOrder(vec) => { union_spans(vec.iter().map(|i| i.span)) } TableOptionsClustered::Index(vec) => union_spans(vec.iter().map(|i| i.span())), } } } impl Spanned for ClusteredIndex { fn span(&self) -> Span { let ClusteredIndex { name, asc: _, // bool } = self; name.span } } impl Spanned for CreateTableOptions { fn span(&self) -> Span { match self { CreateTableOptions::None => Span::empty(), CreateTableOptions::With(vec) => union_spans(vec.iter().map(|i| i.span())), CreateTableOptions::Options(vec) => union_spans(vec.iter().map(|i| i.span())), } } } /// # partial span /// /// Missing spans: /// - [AlterTableOperation::OwnerTo] impl Spanned for AlterTableOperation { fn span(&self) -> Span { match self { AlterTableOperation::AddConstraint(table_constraint) => table_constraint.span(), AlterTableOperation::AddColumn { column_keyword: _, if_not_exists: _, column_def, column_position: _, } => column_def.span(), AlterTableOperation::AddProjection { if_not_exists: _, name, select, } => name.span.union(&select.span()), AlterTableOperation::DropProjection { if_exists: _, name } => name.span, AlterTableOperation::MaterializeProjection { if_exists: _, name, partition, } => name.span.union_opt(&partition.as_ref().map(|i| i.span)), AlterTableOperation::ClearProjection { if_exists: _, name, partition, } => name.span.union_opt(&partition.as_ref().map(|i| i.span)), AlterTableOperation::DisableRowLevelSecurity => Span::empty(), AlterTableOperation::DisableRule { name } => name.span, AlterTableOperation::DisableTrigger { name } => name.span, AlterTableOperation::DropConstraint { if_exists: _, name, cascade: _, } => name.span, AlterTableOperation::DropColumn { column_name, if_exists: _, cascade: _, } => column_name.span, AlterTableOperation::AttachPartition { partition } => partition.span(), AlterTableOperation::DetachPartition { partition } => partition.span(), AlterTableOperation::FreezePartition { partition, with_name, } => partition .span() .union_opt(&with_name.as_ref().map(|n| n.span)), AlterTableOperation::UnfreezePartition { partition, with_name, } => partition .span() .union_opt(&with_name.as_ref().map(|n| n.span)), AlterTableOperation::DropPrimaryKey => Span::empty(), AlterTableOperation::EnableAlwaysRule { name } => name.span, AlterTableOperation::EnableAlwaysTrigger { name } => name.span, AlterTableOperation::EnableReplicaRule { name } => name.span, AlterTableOperation::EnableReplicaTrigger { name } => name.span, AlterTableOperation::EnableRowLevelSecurity => Span::empty(), AlterTableOperation::EnableRule { name } => name.span, AlterTableOperation::EnableTrigger { name } => name.span, AlterTableOperation::RenamePartitions { old_partitions, new_partitions, } => union_spans( old_partitions .iter() .map(|i| i.span()) .chain(new_partitions.iter().map(|i| i.span())), ), AlterTableOperation::AddPartitions { if_not_exists: _, new_partitions, } => union_spans(new_partitions.iter().map(|i| i.span())), AlterTableOperation::DropPartitions { partitions, if_exists: _, } => union_spans(partitions.iter().map(|i| i.span())), AlterTableOperation::RenameColumn { old_column_name, new_column_name, } => old_column_name.span.union(&new_column_name.span), AlterTableOperation::RenameTable { table_name } => table_name.span(), AlterTableOperation::ChangeColumn { old_name, new_name, data_type: _, options, column_position: _, } => union_spans( core::iter::once(old_name.span) .chain(core::iter::once(new_name.span)) .chain(options.iter().map(|i| i.span())), ), AlterTableOperation::ModifyColumn { col_name, data_type: _, options, column_position: _, } => { union_spans(core::iter::once(col_name.span).chain(options.iter().map(|i| i.span()))) } AlterTableOperation::RenameConstraint { old_name, new_name } => { old_name.span.union(&new_name.span) } AlterTableOperation::AlterColumn { column_name, op } => { column_name.span.union(&op.span()) } AlterTableOperation::SwapWith { table_name } => table_name.span(), AlterTableOperation::SetTblProperties { table_properties } => { union_spans(table_properties.iter().map(|i| i.span())) } AlterTableOperation::OwnerTo { .. } => Span::empty(), } } } impl Spanned for Partition { fn span(&self) -> Span { match self { Partition::Identifier(ident) => ident.span, Partition::Expr(expr) => expr.span(), Partition::Part(expr) => expr.span(), Partition::Partitions(vec) => union_spans(vec.iter().map(|i| i.span())), } } } impl Spanned for ProjectionSelect { fn span(&self) -> Span { let ProjectionSelect { projection, order_by, group_by, } = self; union_spans( projection .iter() .map(|i| i.span()) .chain(order_by.iter().map(|i| i.span())) .chain(group_by.iter().map(|i| i.span())), ) } } impl Spanned for OrderBy { fn span(&self) -> Span { let OrderBy { exprs, interpolate } = self; union_spans( exprs .iter() .map(|i| i.span()) .chain(interpolate.iter().map(|i| i.span())), ) } } /// # partial span /// /// Missing spans: /// - [GroupByExpr::All] impl Spanned for GroupByExpr { fn span(&self) -> Span { match self { GroupByExpr::All(_) => Span::empty(), GroupByExpr::Expressions(exprs, _modifiers) => { union_spans(exprs.iter().map(|i| i.span())) } } } } impl Spanned for Interpolate { fn span(&self) -> Span { let Interpolate { exprs } = self; union_spans(exprs.iter().flat_map(|i| i.iter().map(|e| e.span()))) } } impl Spanned for InterpolateExpr { fn span(&self) -> Span { let InterpolateExpr { column, expr } = self; column.span.union_opt(&expr.as_ref().map(|e| e.span())) } } impl Spanned for AlterIndexOperation { fn span(&self) -> Span { match self { AlterIndexOperation::RenameIndex { index_name } => index_name.span(), } } } /// # partial span /// /// Missing spans:ever /// - [Insert::insert_alias] impl Spanned for Insert { fn span(&self) -> Span { let Insert { or: _, // enum, sqlite specific ignore: _, // bool into: _, // bool table_name, table_alias, columns, overwrite: _, // bool source, partitioned, after_columns, table: _, // bool on, returning, replace_into: _, // bool priority: _, // todo, mysql specific insert_alias: _, // todo, mysql specific } = self; union_spans( core::iter::once(table_name.span()) .chain(table_alias.as_ref().map(|i| i.span)) .chain(columns.iter().map(|i| i.span)) .chain(source.as_ref().map(|q| q.span())) .chain(partitioned.iter().flat_map(|i| i.iter().map(|k| k.span()))) .chain(after_columns.iter().map(|i| i.span)) .chain(on.as_ref().map(|i| i.span())) .chain(returning.iter().flat_map(|i| i.iter().map(|k| k.span()))), ) } } impl Spanned for OnInsert { fn span(&self) -> Span { match self { OnInsert::DuplicateKeyUpdate(vec) => union_spans(vec.iter().map(|i| i.span())), OnInsert::OnConflict(on_conflict) => on_conflict.span(), } } } impl Spanned for OnConflict { fn span(&self) -> Span { let OnConflict { conflict_target, action, } = self; action .span() .union_opt(&conflict_target.as_ref().map(|i| i.span())) } } impl Spanned for ConflictTarget { fn span(&self) -> Span { match self { ConflictTarget::Columns(vec) => union_spans(vec.iter().map(|i| i.span)), ConflictTarget::OnConstraint(object_name) => object_name.span(), } } } /// # partial span /// /// Missing spans: /// - [OnConflictAction::DoNothing] impl Spanned for OnConflictAction { fn span(&self) -> Span { match self { OnConflictAction::DoNothing => Span::empty(), OnConflictAction::DoUpdate(do_update) => do_update.span(), } } } impl Spanned for DoUpdate { fn span(&self) -> Span { let DoUpdate { assignments, selection, } = self; union_spans( assignments .iter() .map(|i| i.span()) .chain(selection.iter().map(|i| i.span())), ) } } impl Spanned for Assignment { fn span(&self) -> Span { let Assignment { target, value } = self; target.span().union(&value.span()) } } impl Spanned for AssignmentTarget { fn span(&self) -> Span { match self { AssignmentTarget::ColumnName(object_name) => object_name.span(), AssignmentTarget::Tuple(vec) => union_spans(vec.iter().map(|i| i.span())), } } } /// # partial span /// /// Most expressions are missing keywords in their spans. /// f.e. `IS NULL ` reports as `::span`. /// /// Missing spans: /// - [Expr::TypedString] /// - [Expr::MatchAgainst] # MySQL specific /// - [Expr::RLike] # MySQL specific /// - [Expr::Struct] # BigQuery specific /// - [Expr::Named] # BigQuery specific /// - [Expr::Dictionary] # DuckDB specific /// - [Expr::Map] # DuckDB specific /// - [Expr::Lambda] impl Spanned for Expr { fn span(&self) -> Span { match self { Expr::Identifier(ident) => ident.span, Expr::CompoundIdentifier(vec) => union_spans(vec.iter().map(|i| i.span)), Expr::CompositeAccess { expr, key } => expr.span().union(&key.span), Expr::IsFalse(expr) => expr.span(), Expr::IsNotFalse(expr) => expr.span(), Expr::IsTrue(expr) => expr.span(), Expr::IsNotTrue(expr) => expr.span(), Expr::IsNull(expr) => expr.span(), Expr::IsNotNull(expr) => expr.span(), Expr::IsUnknown(expr) => expr.span(), Expr::IsNotUnknown(expr) => expr.span(), Expr::IsDistinctFrom(lhs, rhs) => lhs.span().union(&rhs.span()), Expr::IsNotDistinctFrom(lhs, rhs) => lhs.span().union(&rhs.span()), Expr::InList { expr, list, negated: _, } => union_spans( core::iter::once(expr.span()).chain(list.iter().map(|item| item.span())), ), Expr::InSubquery { expr, subquery, negated: _, } => expr.span().union(&subquery.span()), Expr::InUnnest { expr, array_expr, negated: _, } => expr.span().union(&array_expr.span()), Expr::Between { expr, negated: _, low, high, } => expr.span().union(&low.span()).union(&high.span()), Expr::BinaryOp { left, op: _, right } => left.span().union(&right.span()), Expr::Like { negated: _, expr, pattern, escape_char: _, any: _, } => expr.span().union(&pattern.span()), Expr::ILike { negated: _, expr, pattern, escape_char: _, any: _, } => expr.span().union(&pattern.span()), Expr::SimilarTo { negated: _, expr, pattern, escape_char: _, } => expr.span().union(&pattern.span()), Expr::Ceil { expr, field: _ } => expr.span(), Expr::Floor { expr, field: _ } => expr.span(), Expr::Position { expr, r#in } => expr.span().union(&r#in.span()), Expr::Overlay { expr, overlay_what, overlay_from, overlay_for, } => expr .span() .union(&overlay_what.span()) .union(&overlay_from.span()) .union_opt(&overlay_for.as_ref().map(|i| i.span())), Expr::Collate { expr, collation } => expr .span() .union(&union_spans(collation.0.iter().map(|i| i.span))), Expr::Nested(expr) => expr.span(), Expr::Value(value) => value.span(), Expr::TypedString { .. } => Span::empty(), Expr::MapAccess { column, keys } => column .span() .union(&union_spans(keys.iter().map(|i| i.key.span()))), Expr::Function(function) => function.span(), Expr::GroupingSets(vec) => { union_spans(vec.iter().flat_map(|i| i.iter().map(|k| k.span()))) } Expr::Cube(vec) => union_spans(vec.iter().flat_map(|i| i.iter().map(|k| k.span()))), Expr::Rollup(vec) => union_spans(vec.iter().flat_map(|i| i.iter().map(|k| k.span()))), Expr::Tuple(vec) => union_spans(vec.iter().map(|i| i.span())), Expr::Array(array) => array.span(), Expr::MatchAgainst { .. } => Span::empty(), Expr::JsonAccess { value, path } => value.span().union(&path.span()), Expr::RLike { .. } => Span::empty(), Expr::AnyOp { left, compare_op: _, right, is_some: _, } => left.span().union(&right.span()), Expr::AllOp { left, compare_op: _, right, } => left.span().union(&right.span()), Expr::UnaryOp { op: _, expr } => expr.span(), Expr::Convert { expr, data_type: _, charset, target_before_value: _, styles, is_try: _, } => union_spans( core::iter::once(expr.span()) .chain(charset.as_ref().map(|i| i.span())) .chain(styles.iter().map(|i| i.span())), ), Expr::Cast { kind: _, expr, data_type: _, format: _, } => expr.span(), Expr::AtTimeZone { timestamp, time_zone, } => timestamp.span().union(&time_zone.span()), Expr::Extract { field: _, syntax: _, expr, } => expr.span(), Expr::Substring { expr, substring_from, substring_for, special: _, } => union_spans( core::iter::once(expr.span()) .chain(substring_from.as_ref().map(|i| i.span())) .chain(substring_for.as_ref().map(|i| i.span())), ), Expr::Trim { expr, trim_where: _, trim_what, trim_characters, } => union_spans( core::iter::once(expr.span()) .chain(trim_what.as_ref().map(|i| i.span())) .chain( trim_characters .as_ref() .map(|items| union_spans(items.iter().map(|i| i.span()))), ), ), Expr::IntroducedString { value, .. } => value.span(), Expr::Case { operand, conditions, results, else_result, } => union_spans( operand .as_ref() .map(|i| i.span()) .into_iter() .chain(conditions.iter().map(|i| i.span())) .chain(results.iter().map(|i| i.span())) .chain(else_result.as_ref().map(|i| i.span())), ), Expr::Exists { subquery, .. } => subquery.span(), Expr::Subquery(query) => query.span(), Expr::Struct { .. } => Span::empty(), Expr::Named { .. } => Span::empty(), Expr::Dictionary(_) => Span::empty(), Expr::Map(_) => Span::empty(), Expr::Subscript { expr, subscript } => expr.span().union(&subscript.span()), Expr::Interval(interval) => interval.value.span(), Expr::Wildcard(token) => token.0.span, Expr::QualifiedWildcard(object_name, token) => union_spans( object_name .0 .iter() .map(|i| i.span) .chain(iter::once(token.0.span)), ), Expr::OuterJoin(expr) => expr.span(), Expr::Prior(expr) => expr.span(), Expr::Lambda(_) => Span::empty(), Expr::Method(_) => Span::empty(), } } } impl Spanned for Subscript { fn span(&self) -> Span { match self { Subscript::Index { index } => index.span(), Subscript::Slice { lower_bound, upper_bound, stride, } => union_spans( [ lower_bound.as_ref().map(|i| i.span()), upper_bound.as_ref().map(|i| i.span()), stride.as_ref().map(|i| i.span()), ] .into_iter() .flatten(), ), } } } impl Spanned for ObjectName { fn span(&self) -> Span { let ObjectName(segments) = self; union_spans(segments.iter().map(|i| i.span)) } } impl Spanned for Array { fn span(&self) -> Span { let Array { elem, named: _, // bool } = self; union_spans(elem.iter().map(|i| i.span())) } } impl Spanned for Function { fn span(&self) -> Span { let Function { name, parameters, args, filter, null_treatment: _, // enum over: _, // todo within_group, } = self; union_spans( name.0 .iter() .map(|i| i.span) .chain(iter::once(args.span())) .chain(iter::once(parameters.span())) .chain(filter.iter().map(|i| i.span())) .chain(within_group.iter().map(|i| i.span())), ) } } /// # partial span /// /// The span of [FunctionArguments::None] is empty. impl Spanned for FunctionArguments { fn span(&self) -> Span { match self { FunctionArguments::None => Span::empty(), FunctionArguments::Subquery(query) => query.span(), FunctionArguments::List(list) => list.span(), } } } impl Spanned for FunctionArgumentList { fn span(&self) -> Span { let FunctionArgumentList { duplicate_treatment: _, // enum args, clauses, } = self; union_spans( // # todo: duplicate-treatment span args.iter() .map(|i| i.span()) .chain(clauses.iter().map(|i| i.span())), ) } } impl Spanned for FunctionArgumentClause { fn span(&self) -> Span { match self { FunctionArgumentClause::IgnoreOrRespectNulls(_) => Span::empty(), FunctionArgumentClause::OrderBy(vec) => union_spans(vec.iter().map(|i| i.expr.span())), FunctionArgumentClause::Limit(expr) => expr.span(), FunctionArgumentClause::OnOverflow(_) => Span::empty(), FunctionArgumentClause::Having(HavingBound(_kind, expr)) => expr.span(), FunctionArgumentClause::Separator(value) => value.span(), FunctionArgumentClause::JsonNullClause(_) => Span::empty(), } } } /// # partial span /// /// see Spanned impl for JsonPathElem for more information impl Spanned for JsonPath { fn span(&self) -> Span { let JsonPath { path } = self; union_spans(path.iter().map(|i| i.span())) } } /// # partial span /// /// Missing spans: /// - [JsonPathElem::Dot] impl Spanned for JsonPathElem { fn span(&self) -> Span { match self { JsonPathElem::Dot { .. } => Span::empty(), JsonPathElem::Bracket { key } => key.span(), } } } impl Spanned for SelectItem { fn span(&self) -> Span { match self { SelectItem::UnnamedExpr(expr) => expr.span(), SelectItem::ExprWithAlias { expr, alias } => expr.span().union(&alias.span), SelectItem::QualifiedWildcard(object_name, wildcard_additional_options) => union_spans( object_name .0 .iter() .map(|i| i.span) .chain(iter::once(wildcard_additional_options.span())), ), SelectItem::Wildcard(wildcard_additional_options) => wildcard_additional_options.span(), } } } impl Spanned for WildcardAdditionalOptions { fn span(&self) -> Span { let WildcardAdditionalOptions { wildcard_token, opt_ilike, opt_exclude, opt_except, opt_replace, opt_rename, } = self; union_spans( core::iter::once(wildcard_token.0.span) .chain(opt_ilike.as_ref().map(|i| i.span())) .chain(opt_exclude.as_ref().map(|i| i.span())) .chain(opt_rename.as_ref().map(|i| i.span())) .chain(opt_replace.as_ref().map(|i| i.span())) .chain(opt_except.as_ref().map(|i| i.span())), ) } } /// # missing span impl Spanned for IlikeSelectItem { fn span(&self) -> Span { Span::empty() } } impl Spanned for ExcludeSelectItem { fn span(&self) -> Span { match self { ExcludeSelectItem::Single(ident) => ident.span, ExcludeSelectItem::Multiple(vec) => union_spans(vec.iter().map(|i| i.span)), } } } impl Spanned for RenameSelectItem { fn span(&self) -> Span { match self { RenameSelectItem::Single(ident) => ident.ident.span.union(&ident.alias.span), RenameSelectItem::Multiple(vec) => { union_spans(vec.iter().map(|i| i.ident.span.union(&i.alias.span))) } } } } impl Spanned for ExceptSelectItem { fn span(&self) -> Span { let ExceptSelectItem { first_element, additional_elements, } = self; union_spans( iter::once(first_element.span).chain(additional_elements.iter().map(|i| i.span)), ) } } impl Spanned for ReplaceSelectItem { fn span(&self) -> Span { let ReplaceSelectItem { items } = self; union_spans(items.iter().map(|i| i.span())) } } impl Spanned for ReplaceSelectElement { fn span(&self) -> Span { let ReplaceSelectElement { expr, column_name, as_keyword: _, // bool } = self; expr.span().union(&column_name.span) } } /// # partial span /// /// Missing spans: /// - [TableFactor::JsonTable] impl Spanned for TableFactor { fn span(&self) -> Span { match self { TableFactor::Table { name, alias, args: _, with_hints: _, version: _, with_ordinality: _, partitions: _, json_path: _, } => union_spans( name.0 .iter() .map(|i| i.span) .chain(alias.as_ref().map(|alias| { union_spans( iter::once(alias.name.span) .chain(alias.columns.iter().map(|i| i.span())), ) })), ), TableFactor::Derived { lateral: _, subquery, alias, } => subquery .span() .union_opt(&alias.as_ref().map(|alias| alias.span())), TableFactor::TableFunction { expr, alias } => expr .span() .union_opt(&alias.as_ref().map(|alias| alias.span())), TableFactor::UNNEST { alias, with_offset: _, with_offset_alias, array_exprs, with_ordinality: _, } => union_spans( alias .iter() .map(|i| i.span()) .chain(array_exprs.iter().map(|i| i.span())) .chain(with_offset_alias.as_ref().map(|i| i.span)), ), TableFactor::NestedJoin { table_with_joins, alias, } => table_with_joins .span() .union_opt(&alias.as_ref().map(|alias| alias.span())), TableFactor::Function { lateral: _, name, args, alias, } => union_spans( name.0 .iter() .map(|i| i.span) .chain(args.iter().map(|i| i.span())) .chain(alias.as_ref().map(|alias| alias.span())), ), TableFactor::JsonTable { .. } => Span::empty(), TableFactor::Pivot { table, aggregate_functions, value_column, value_source, default_on_null, alias, } => union_spans( core::iter::once(table.span()) .chain(aggregate_functions.iter().map(|i| i.span())) .chain(value_column.iter().map(|i| i.span)) .chain(core::iter::once(value_source.span())) .chain(default_on_null.as_ref().map(|i| i.span())) .chain(alias.as_ref().map(|i| i.span())), ), TableFactor::Unpivot { table, value, name, columns, alias, } => union_spans( core::iter::once(table.span()) .chain(core::iter::once(value.span)) .chain(core::iter::once(name.span)) .chain(columns.iter().map(|i| i.span)) .chain(alias.as_ref().map(|alias| alias.span())), ), TableFactor::MatchRecognize { table, partition_by, order_by, measures, rows_per_match: _, after_match_skip: _, pattern, symbols, alias, } => union_spans( core::iter::once(table.span()) .chain(partition_by.iter().map(|i| i.span())) .chain(order_by.iter().map(|i| i.span())) .chain(measures.iter().map(|i| i.span())) .chain(core::iter::once(pattern.span())) .chain(symbols.iter().map(|i| i.span())) .chain(alias.as_ref().map(|i| i.span())), ), TableFactor::OpenJsonTable { .. } => Span::empty(), } } } impl Spanned for PivotValueSource { fn span(&self) -> Span { match self { PivotValueSource::List(vec) => union_spans(vec.iter().map(|i| i.span())), PivotValueSource::Any(vec) => union_spans(vec.iter().map(|i| i.span())), PivotValueSource::Subquery(query) => query.span(), } } } impl Spanned for ExprWithAlias { fn span(&self) -> Span { let ExprWithAlias { expr, alias } = self; expr.span().union_opt(&alias.as_ref().map(|i| i.span)) } } /// # missing span impl Spanned for MatchRecognizePattern { fn span(&self) -> Span { Span::empty() } } impl Spanned for SymbolDefinition { fn span(&self) -> Span { let SymbolDefinition { symbol, definition } = self; symbol.span.union(&definition.span()) } } impl Spanned for Measure { fn span(&self) -> Span { let Measure { expr, alias } = self; expr.span().union(&alias.span) } } impl Spanned for OrderByExpr { fn span(&self) -> Span { let OrderByExpr { expr, asc: _, // bool nulls_first: _, // bool with_fill, } = self; expr.span().union_opt(&with_fill.as_ref().map(|f| f.span())) } } impl Spanned for WithFill { fn span(&self) -> Span { let WithFill { from, to, step } = self; union_spans( from.iter() .map(|f| f.span()) .chain(to.iter().map(|t| t.span())) .chain(step.iter().map(|s| s.span())), ) } } impl Spanned for FunctionArg { fn span(&self) -> Span { match self { FunctionArg::Named { name, arg, operator: _, } => name.span.union(&arg.span()), FunctionArg::Unnamed(arg) => arg.span(), FunctionArg::ExprNamed { name, arg, operator: _, } => name.span().union(&arg.span()), } } } /// # partial span /// /// Missing spans: /// - [FunctionArgExpr::Wildcard] impl Spanned for FunctionArgExpr { fn span(&self) -> Span { match self { FunctionArgExpr::Expr(expr) => expr.span(), FunctionArgExpr::QualifiedWildcard(object_name) => { union_spans(object_name.0.iter().map(|i| i.span)) } FunctionArgExpr::Wildcard => Span::empty(), } } } impl Spanned for TableAlias { fn span(&self) -> Span { let TableAlias { name, columns } = self; union_spans(iter::once(name.span).chain(columns.iter().map(|i| i.span()))) } } impl Spanned for TableAliasColumnDef { fn span(&self) -> Span { let TableAliasColumnDef { name, data_type: _ } = self; name.span } } /// # missing span /// /// The span of a `Value` is currently not implemented, as doing so /// requires a breaking changes, which may be done in a future release. impl Spanned for Value { fn span(&self) -> Span { Span::empty() // # todo: Value needs to store spans before this is possible } } impl Spanned for Join { fn span(&self) -> Span { let Join { relation, global: _, // bool join_operator, } = self; relation.span().union(&join_operator.span()) } } /// # partial span /// /// Missing spans: /// - [JoinOperator::CrossJoin] /// - [JoinOperator::CrossApply] /// - [JoinOperator::OuterApply] impl Spanned for JoinOperator { fn span(&self) -> Span { match self { JoinOperator::Inner(join_constraint) => join_constraint.span(), JoinOperator::LeftOuter(join_constraint) => join_constraint.span(), JoinOperator::RightOuter(join_constraint) => join_constraint.span(), JoinOperator::FullOuter(join_constraint) => join_constraint.span(), JoinOperator::CrossJoin => Span::empty(), JoinOperator::LeftSemi(join_constraint) => join_constraint.span(), JoinOperator::RightSemi(join_constraint) => join_constraint.span(), JoinOperator::LeftAnti(join_constraint) => join_constraint.span(), JoinOperator::RightAnti(join_constraint) => join_constraint.span(), JoinOperator::CrossApply => Span::empty(), JoinOperator::OuterApply => Span::empty(), JoinOperator::AsOf { match_condition, constraint, } => match_condition.span().union(&constraint.span()), JoinOperator::Anti(join_constraint) => join_constraint.span(), JoinOperator::Semi(join_constraint) => join_constraint.span(), } } } /// # partial span /// /// Missing spans: /// - [JoinConstraint::Natural] /// - [JoinConstraint::None] impl Spanned for JoinConstraint { fn span(&self) -> Span { match self { JoinConstraint::On(expr) => expr.span(), JoinConstraint::Using(vec) => union_spans(vec.iter().map(|i| i.span)), JoinConstraint::Natural => Span::empty(), JoinConstraint::None => Span::empty(), } } } impl Spanned for TableWithJoins { fn span(&self) -> Span { let TableWithJoins { relation, joins } = self; union_spans(core::iter::once(relation.span()).chain(joins.iter().map(|item| item.span()))) } } impl Spanned for Select { fn span(&self) -> Span { let Select { select_token, distinct: _, // todo top: _, // todo, mysql specific projection, into, from, lateral_views, prewhere, selection, group_by, cluster_by, distribute_by, sort_by, having, named_window, qualify, window_before_qualify: _, // bool value_table_mode: _, // todo, BigQuery specific connect_by, top_before_distinct: _, } = self; union_spans( core::iter::once(select_token.0.span) .chain(projection.iter().map(|item| item.span())) .chain(into.iter().map(|item| item.span())) .chain(from.iter().map(|item| item.span())) .chain(lateral_views.iter().map(|item| item.span())) .chain(prewhere.iter().map(|item| item.span())) .chain(selection.iter().map(|item| item.span())) .chain(core::iter::once(group_by.span())) .chain(cluster_by.iter().map(|item| item.span())) .chain(distribute_by.iter().map(|item| item.span())) .chain(sort_by.iter().map(|item| item.span())) .chain(having.iter().map(|item| item.span())) .chain(named_window.iter().map(|item| item.span())) .chain(qualify.iter().map(|item| item.span())) .chain(connect_by.iter().map(|item| item.span())), ) } } impl Spanned for ConnectBy { fn span(&self) -> Span { let ConnectBy { condition, relationships, } = self; union_spans( core::iter::once(condition.span()).chain(relationships.iter().map(|item| item.span())), ) } } impl Spanned for NamedWindowDefinition { fn span(&self) -> Span { let NamedWindowDefinition( ident, _, // todo: NamedWindowExpr ) = self; ident.span } } impl Spanned for LateralView { fn span(&self) -> Span { let LateralView { lateral_view, lateral_view_name, lateral_col_alias, outer: _, // bool } = self; union_spans( core::iter::once(lateral_view.span()) .chain(core::iter::once(lateral_view_name.span())) .chain(lateral_col_alias.iter().map(|i| i.span)), ) } } impl Spanned for SelectInto { fn span(&self) -> Span { let SelectInto { temporary: _, // bool unlogged: _, // bool table: _, // bool name, } = self; name.span() } } #[cfg(test)] pub mod tests { use crate::dialect::{Dialect, GenericDialect, SnowflakeDialect}; use crate::parser::Parser; use crate::tokenizer::Span; use super::*; struct SpanTest<'a>(Parser<'a>, &'a str); impl<'a> SpanTest<'a> { fn new(dialect: &'a dyn Dialect, sql: &'a str) -> Self { Self(Parser::new(dialect).try_with_sql(sql).unwrap(), sql) } // get the subsection of the source string that corresponds to the span // only works on single-line strings fn get_source(&self, span: Span) -> &'a str { // lines in spans are 1-indexed &self.1[(span.start.column as usize - 1)..(span.end.column - 1) as usize] } } #[test] fn test_join() { let dialect = &GenericDialect; let mut test = SpanTest::new( dialect, "SELECT id, name FROM users LEFT JOIN companies ON users.company_id = companies.id", ); let query = test.0.parse_select().unwrap(); let select_span = query.span(); assert_eq!( test.get_source(select_span), "SELECT id, name FROM users LEFT JOIN companies ON users.company_id = companies.id" ); let join_span = query.from[0].joins[0].span(); // 'LEFT JOIN' missing assert_eq!( test.get_source(join_span), "companies ON users.company_id = companies.id" ); } #[test] pub fn test_union() { let dialect = &GenericDialect; let mut test = SpanTest::new( dialect, "SELECT a FROM postgres.public.source UNION SELECT a FROM postgres.public.source", ); let query = test.0.parse_query().unwrap(); let select_span = query.span(); assert_eq!( test.get_source(select_span), "SELECT a FROM postgres.public.source UNION SELECT a FROM postgres.public.source" ); } #[test] pub fn test_subquery() { let dialect = &GenericDialect; let mut test = SpanTest::new( dialect, "SELECT a FROM (SELECT a FROM postgres.public.source) AS b", ); let query = test.0.parse_select().unwrap(); let select_span = query.span(); assert_eq!( test.get_source(select_span), "SELECT a FROM (SELECT a FROM postgres.public.source) AS b" ); let subquery_span = query.from[0].span(); // left paren missing assert_eq!( test.get_source(subquery_span), "SELECT a FROM postgres.public.source) AS b" ); } #[test] pub fn test_cte() { let dialect = &GenericDialect; let mut test = SpanTest::new(dialect, "WITH cte_outer AS (SELECT a FROM postgres.public.source), cte_ignored AS (SELECT a FROM cte_outer), cte_inner AS (SELECT a FROM cte_outer) SELECT a FROM cte_inner"); let query = test.0.parse_query().unwrap(); let select_span = query.span(); assert_eq!(test.get_source(select_span), "WITH cte_outer AS (SELECT a FROM postgres.public.source), cte_ignored AS (SELECT a FROM cte_outer), cte_inner AS (SELECT a FROM cte_outer) SELECT a FROM cte_inner"); } #[test] pub fn test_snowflake_lateral_flatten() { let dialect = &SnowflakeDialect; let mut test = SpanTest::new(dialect, "SELECT FLATTENED.VALUE:field::TEXT AS FIELD FROM SNOWFLAKE.SCHEMA.SOURCE AS S, LATERAL FLATTEN(INPUT => S.JSON_ARRAY) AS FLATTENED"); let query = test.0.parse_select().unwrap(); let select_span = query.span(); assert_eq!(test.get_source(select_span), "SELECT FLATTENED.VALUE:field::TEXT AS FIELD FROM SNOWFLAKE.SCHEMA.SOURCE AS S, LATERAL FLATTEN(INPUT => S.JSON_ARRAY) AS FLATTENED"); } #[test] pub fn test_wildcard_from_cte() { let dialect = &GenericDialect; let mut test = SpanTest::new( dialect, "WITH cte AS (SELECT a FROM postgres.public.source) SELECT cte.* FROM cte", ); let query = test.0.parse_query().unwrap(); let cte_span = query.clone().with.unwrap().cte_tables[0].span(); let cte_query_span = query.clone().with.unwrap().cte_tables[0].query.span(); let body_span = query.body.span(); // the WITH keyboard is part of the query assert_eq!( test.get_source(cte_span), "cte AS (SELECT a FROM postgres.public.source)" ); assert_eq!( test.get_source(cte_query_span), "SELECT a FROM postgres.public.source" ); assert_eq!(test.get_source(body_span), "SELECT cte.* FROM cte"); } }