| // Licensed to the Apache Software Foundation (ASF) under one | |
| // or more contributor license agreements. See the NOTICE file | |
| // distributed with this work for additional information | |
| // regarding copyright ownership. The ASF licenses this file | |
| // to you under the Apache License, Version 2.0 (the | |
| // "License"); you may not use this file except in compliance | |
| // with the License. You may obtain a copy of the License at | |
| // | |
| // http://www.apache.org/licenses/LICENSE-2.0 | |
| // | |
| // Unless required by applicable law or agreed to in writing, | |
| // software distributed under the License is distributed on an | |
| // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | |
| // KIND, either express or implied. See the License for the | |
| // specific language governing permissions and limitations | |
| // under the License. | |
| use crate::{ | |
| ast::{ | |
| ddl::AlterSchema, query::SelectItemQualifiedWildcardKind, AlterSchemaOperation, AlterTable, | |
| ColumnOptions, CreateOperator, CreateOperatorClass, CreateOperatorFamily, CreateView, | |
| ExportData, Owner, TypedString, | |
| }, | |
| tokenizer::TokenWithSpan, | |
| }; | |
| use core::iter; | |
| use crate::tokenizer::Span; | |
| use super::{ | |
| comments, dcl::SecondaryRoles, value::ValueWithSpan, AccessExpr, AlterColumnOperation, | |
| AlterIndexOperation, AlterTableOperation, Analyze, Array, Assignment, AssignmentTarget, | |
| AttachedToken, BeginEndStatements, CaseStatement, CloseCursor, ClusteredIndex, ColumnDef, | |
| ColumnOption, ColumnOptionDef, ConditionalStatementBlock, ConditionalStatements, | |
| ConflictTarget, ConnectByKind, ConstraintCharacteristics, CopySource, CreateIndex, CreateTable, | |
| CreateTableOptions, Cte, Delete, DoUpdate, ExceptSelectItem, ExcludeConstraintElement, | |
| ExcludeSelectItem, Expr, ExprWithAlias, Fetch, ForValues, FromTable, Function, FunctionArg, | |
| FunctionArgExpr, FunctionArgumentClause, FunctionArgumentList, FunctionArguments, GroupByExpr, | |
| HavingBound, IfStatement, IlikeSelectItem, IndexColumn, Insert, Interpolate, InterpolateExpr, | |
| Join, JoinConstraint, JoinOperator, JsonPath, JsonPathElem, LateralView, LimitClause, | |
| MatchRecognizePattern, Measure, Merge, MergeAction, MergeClause, MergeInsertExpr, | |
| MergeInsertKind, MergeUpdateExpr, MergeUpdateKind, NamedParenthesizedList, | |
| NamedWindowDefinition, ObjectName, ObjectNamePart, Offset, OnConflict, OnConflictAction, | |
| OnInsert, OpenStatement, OrderBy, OrderByExpr, OrderByKind, OutputClause, Parens, Partition, | |
| PartitionBoundValue, PivotValueSource, ProjectionSelect, Query, RaiseStatement, | |
| RaiseStatementValue, ReferentialAction, RenameSelectItem, ReplaceSelectElement, | |
| ReplaceSelectItem, Select, SelectInto, SelectItem, SetExpr, SqlOption, Statement, Subscript, | |
| SymbolDefinition, TableAlias, TableAliasColumnDef, TableConstraint, TableFactor, TableObject, | |
| TableOptionsClustered, TableWithJoins, Update, UpdateTableFromKind, Use, Values, ViewColumnDef, | |
| WhileStatement, WildcardAdditionalOptions, With, WithFill, | |
| }; | |
| /// Given an iterator of spans, return the [Span::union] of all spans. | |
| fn union_spans<I: Iterator<Item = Span>>(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 TokenWithSpan { | |
| fn span(&self) -> Span { | |
| self.span | |
| } | |
| } | |
| impl<T> Spanned for Parens<T> { | |
| fn span(&self) -> Span { | |
| self.opening_token.0.span.union(&self.closing_token.0.span) | |
| } | |
| } | |
| impl Spanned for Query { | |
| fn span(&self) -> Span { | |
| let Query { | |
| with, | |
| body, | |
| order_by, | |
| limit_clause, | |
| fetch, | |
| locks: _, // todo | |
| for_clause: _, // todo, mssql specific | |
| settings: _, // todo, clickhouse specific | |
| format_clause: _, // todo, clickhouse specific | |
| pipe_operators: _, // todo bigquery 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_clause.as_ref().map(|i| i.span())) | |
| .chain(fetch.as_ref().map(|i| i.span())), | |
| ) | |
| } | |
| } | |
| impl Spanned for LimitClause { | |
| fn span(&self) -> Span { | |
| match self { | |
| LimitClause::LimitOffset { | |
| limit, | |
| offset, | |
| limit_by, | |
| } => union_spans( | |
| limit | |
| .iter() | |
| .map(|i| i.span()) | |
| .chain(offset.as_ref().map(|i| i.span())) | |
| .chain(limit_by.iter().map(|i| i.span())), | |
| ), | |
| LimitClause::OffsetCommaLimit { offset, limit } => offset.span().union(&limit.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(), | |
| SetExpr::Delete(statement) => statement.span(), | |
| SetExpr::Merge(statement) => statement.span(), | |
| } | |
| } | |
| } | |
| impl Spanned for Values { | |
| fn span(&self) -> Span { | |
| let Values { | |
| explicit_row: _, // bool, | |
| value_keyword: _, | |
| rows, | |
| } = self; | |
| match &rows[..] { | |
| [] => Span::empty(), | |
| [f] => f.span(), | |
| [f, .., l] => f.span().union(&l.span()), | |
| } | |
| } | |
| } | |
| /// # partial span | |
| /// | |
| /// Missing spans: | |
| /// - [Statement::CopyIntoSnowflake] | |
| /// - [Statement::CreateSecret] | |
| /// - [Statement::CreateRole] | |
| /// - [Statement::AlterType] | |
| /// - [Statement::AlterOperator] | |
| /// - [Statement::AlterRole] | |
| /// - [Statement::AttachDatabase] | |
| /// - [Statement::AttachDuckDBDatabase] | |
| /// - [Statement::DetachDuckDBDatabase] | |
| /// - [Statement::Drop] | |
| /// - [Statement::DropFunction] | |
| /// - [Statement::DropProcedure] | |
| /// - [Statement::DropSecret] | |
| /// - [Statement::Declare] | |
| /// - [Statement::CreateExtension] | |
| /// - [Statement::CreateCollation] | |
| /// - [Statement::AlterCollation] | |
| /// - [Statement::Fetch] | |
| /// - [Statement::Flush] | |
| /// - [Statement::Discard] | |
| /// - [Statement::Set] | |
| /// - [Statement::ShowFunctions] | |
| /// - [Statement::ShowVariable] | |
| /// - [Statement::ShowStatus] | |
| /// - [Statement::ShowVariables] | |
| /// - [Statement::ShowCreate] | |
| /// - [Statement::ShowColumns] | |
| /// - [Statement::ShowTables] | |
| /// - [Statement::ShowCollation] | |
| /// - [Statement::StartTransaction] | |
| /// - [Statement::Comment] | |
| /// - [Statement::Commit] | |
| /// - [Statement::Rollback] | |
| /// - [Statement::CreateSchema] | |
| /// - [Statement::CreateDatabase] | |
| /// - [Statement::CreateFunction] | |
| /// - [Statement::CreateTrigger] | |
| /// - [Statement::DropTrigger] | |
| /// - [Statement::CreateProcedure] | |
| /// - [Statement::CreateMacro] | |
| /// - [Statement::CreateStage] | |
| /// - [Statement::CreateFileFormat] | |
| /// - [Statement::Assert] | |
| /// - [Statement::Grant] | |
| /// - [Statement::Revoke] | |
| /// - [Statement::Deallocate] | |
| /// - [Statement::Execute] | |
| /// - [Statement::Prepare] | |
| /// - [Statement::Kill] | |
| /// - [Statement::ExplainTable] | |
| /// - [Statement::Explain] | |
| /// - [Statement::Savepoint] | |
| /// - [Statement::ReleaseSavepoint] | |
| /// - [Statement::Cache] | |
| /// - [Statement::UNCache] | |
| /// - [Statement::CreateSequence] | |
| /// - [Statement::CreateType] | |
| /// - [Statement::Pragma] | |
| /// - [Statement::Lock] | |
| /// - [Statement::LockTables] | |
| /// - [Statement::UnlockTables] | |
| /// - [Statement::Unload] | |
| /// - [Statement::OptimizeTable] | |
| impl Spanned for Statement { | |
| fn span(&self) -> Span { | |
| match self { | |
| Statement::Analyze(analyze) => analyze.span(), | |
| Statement::Truncate(truncate) => truncate.span(), | |
| Statement::Msck(msck) => msck.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::Case(stmt) => stmt.span(), | |
| Statement::If(stmt) => stmt.span(), | |
| Statement::While(stmt) => stmt.span(), | |
| Statement::Raise(stmt) => stmt.span(), | |
| Statement::Call(function) => function.span(), | |
| Statement::Copy { | |
| source, | |
| to: _, | |
| target: _, | |
| options: _, | |
| legacy_options: _, | |
| values: _, | |
| } => source.span(), | |
| Statement::CopyIntoSnowflake { | |
| into: _, | |
| into_columns: _, | |
| from_obj: _, | |
| from_obj_alias: _, | |
| stage_params: _, | |
| from_transformations: _, | |
| files: _, | |
| pattern: _, | |
| file_format: _, | |
| copy_options: _, | |
| validation_mode: _, | |
| kind: _, | |
| from_query: _, | |
| partition: _, | |
| } => Span::empty(), | |
| Statement::Open(open) => open.span(), | |
| Statement::Close { cursor } => match cursor { | |
| CloseCursor::All => Span::empty(), | |
| CloseCursor::Specific { name } => name.span, | |
| }, | |
| Statement::Update(update) => update.span(), | |
| Statement::Delete(delete) => delete.span(), | |
| Statement::CreateView(create_view) => create_view.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(create_role) => create_role.span(), | |
| Statement::CreateExtension(create_extension) => create_extension.span(), | |
| Statement::CreateCollation(create_collation) => create_collation.span(), | |
| Statement::DropExtension(drop_extension) => drop_extension.span(), | |
| Statement::DropOperator(drop_operator) => drop_operator.span(), | |
| Statement::DropOperatorFamily(drop_operator_family) => drop_operator_family.span(), | |
| Statement::DropOperatorClass(drop_operator_class) => drop_operator_class.span(), | |
| Statement::CreateSecret { .. } => Span::empty(), | |
| Statement::CreateServer { .. } => Span::empty(), | |
| Statement::CreateForeignTable(stmt) => stmt.span(), | |
| Statement::CreateConnector { .. } => Span::empty(), | |
| Statement::CreateOperator(create_operator) => create_operator.span(), | |
| Statement::CreateOperatorFamily(create_operator_family) => { | |
| create_operator_family.span() | |
| } | |
| Statement::CreateOperatorClass(create_operator_class) => create_operator_class.span(), | |
| Statement::CreateTextSearch(create_text_search) => create_text_search.span(), | |
| Statement::AlterTable(alter_table) => alter_table.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::AlterFunction { .. } => Span::empty(), | |
| Statement::AlterType { .. } => Span::empty(), | |
| Statement::AlterCollation { .. } => Span::empty(), | |
| Statement::AlterOperator { .. } => Span::empty(), | |
| Statement::AlterOperatorFamily { .. } => Span::empty(), | |
| Statement::AlterOperatorClass { .. } => Span::empty(), | |
| Statement::AlterTextSearch { .. } => Span::empty(), | |
| Statement::AlterRole { .. } => Span::empty(), | |
| Statement::AlterSession { .. } => Span::empty(), | |
| Statement::AttachDatabase { .. } => Span::empty(), | |
| Statement::AttachDuckDBDatabase { .. } => Span::empty(), | |
| Statement::DetachDuckDBDatabase { .. } => Span::empty(), | |
| Statement::Drop { .. } => Span::empty(), | |
| Statement::DropFunction(drop_function) => drop_function.span(), | |
| Statement::DropDomain { .. } => Span::empty(), | |
| Statement::DropProcedure { .. } => Span::empty(), | |
| Statement::DropSecret { .. } => Span::empty(), | |
| Statement::Declare { .. } => Span::empty(), | |
| Statement::Fetch { .. } => Span::empty(), | |
| Statement::Flush { .. } => Span::empty(), | |
| Statement::Discard { .. } => Span::empty(), | |
| Statement::Set(_) => 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::ShowCharset { .. } => Span::empty(), | |
| Statement::Use(u) => u.span(), | |
| Statement::StartTransaction { .. } => 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::CreateDomain { .. } => Span::empty(), | |
| Statement::CreateTrigger { .. } => Span::empty(), | |
| Statement::DropTrigger { .. } => Span::empty(), | |
| Statement::CreateProcedure { .. } => Span::empty(), | |
| Statement::CreateMacro { .. } => Span::empty(), | |
| Statement::CreateStage { .. } => Span::empty(), | |
| Statement::CreateFileFormat { .. } => Span::empty(), | |
| Statement::CreateWarehouse(..) => Span::empty(), | |
| Statement::Assert { .. } => Span::empty(), | |
| Statement::Grant { .. } => Span::empty(), | |
| Statement::Deny { .. } => 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(merge) => merge.span(), | |
| Statement::Cache { .. } => Span::empty(), | |
| Statement::UNCache { .. } => Span::empty(), | |
| Statement::CreateSequence { .. } => Span::empty(), | |
| Statement::CreateType { .. } => Span::empty(), | |
| Statement::Pragma { .. } => Span::empty(), | |
| Statement::Lock(_) => 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::AlterConnector { .. } => Span::empty(), | |
| Statement::DropPolicy { .. } => Span::empty(), | |
| Statement::DropConnector { .. } => Span::empty(), | |
| Statement::ShowCatalogs { .. } => Span::empty(), | |
| Statement::ShowDatabases { .. } => Span::empty(), | |
| Statement::ShowProcessList { .. } => Span::empty(), | |
| Statement::ShowSchemas { .. } => Span::empty(), | |
| Statement::ShowObjects { .. } => Span::empty(), | |
| Statement::ShowViews { .. } => Span::empty(), | |
| Statement::LISTEN { .. } => Span::empty(), | |
| Statement::NOTIFY { .. } => Span::empty(), | |
| Statement::LoadData { .. } => Span::empty(), | |
| Statement::UNLISTEN { .. } => Span::empty(), | |
| Statement::RenameTable { .. } => Span::empty(), | |
| Statement::RaisError { .. } => Span::empty(), | |
| Statement::Throw(_) => Span::empty(), | |
| Statement::Print { .. } => Span::empty(), | |
| Statement::WaitFor(_) => Span::empty(), | |
| Statement::Return { .. } => Span::empty(), | |
| Statement::List(..) | Statement::Put { .. } | Statement::Remove(..) => Span::empty(), | |
| Statement::ExportData(ExportData { | |
| options, | |
| query, | |
| connection, | |
| }) => union_spans( | |
| options | |
| .iter() | |
| .map(|i| i.span()) | |
| .chain(core::iter::once(query.span())) | |
| .chain(connection.iter().map(|i| i.span())), | |
| ), | |
| Statement::CreateUser(..) => Span::empty(), | |
| Statement::AlterSchema(s) => s.span(), | |
| Statement::Vacuum(..) => Span::empty(), | |
| Statement::AlterUser(..) => Span::empty(), | |
| Statement::Reset(..) => 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::Role(object_name) => object_name.span(), | |
| Use::SecondaryRoles(secondary_roles) => { | |
| if let SecondaryRoles::List(roles) = secondary_roles { | |
| return union_spans(roles.iter().map(|i| i.span)); | |
| } | |
| Span::empty() | |
| } | |
| 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 | |
| unlogged: _, // bool | |
| external: _, // bool | |
| global: _, // bool | |
| dynamic: _, // bool | |
| if_not_exists: _, // bool | |
| transient: _, // bool | |
| volatile: _, // bool | |
| iceberg: _, // bool, Snowflake specific | |
| snapshot: _, // bool, BigQuery specific | |
| name, | |
| columns, | |
| constraints, | |
| hive_distribution: _, // hive specific | |
| hive_formats: _, // hive specific | |
| file_format: _, // enum | |
| location: _, // string, no span | |
| query, | |
| without_rowid: _, // bool | |
| like: _, | |
| clone, | |
| comment: _, // todo, no span | |
| on_commit: _, | |
| 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 | |
| inherits: _, // todo, PostgreSQL specific | |
| partition_of, | |
| for_values, | |
| 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_storage_lifecycle_policy: _, // todo, Snowflake specific | |
| with_tags: _, // todo, Snowflake specific | |
| external_volume: _, // todo, Snowflake specific | |
| with_connection: _, // todo, BigQuery external table connection | |
| base_location: _, // todo, Snowflake specific | |
| catalog: _, // todo, Snowflake specific | |
| catalog_sync: _, // todo, Snowflake specific | |
| storage_serialization_policy: _, | |
| table_options, | |
| target_lag: _, | |
| warehouse: _, | |
| version: _, | |
| refresh_mode: _, | |
| initialize: _, | |
| require_user: _, | |
| diststyle: _, | |
| distkey: _, | |
| sortkey: _, | |
| backup: _, | |
| multiset: _, | |
| fallback: _, | |
| with_data: _, | |
| } = self; | |
| union_spans( | |
| core::iter::once(name.span()) | |
| .chain(core::iter::once(table_options.span())) | |
| .chain(columns.iter().map(|i| i.span())) | |
| .chain(constraints.iter().map(|i| i.span())) | |
| .chain(query.iter().map(|i| i.span())) | |
| .chain(clone.iter().map(|i| i.span())) | |
| .chain(partition_of.iter().map(|i| i.span())) | |
| .chain(for_values.iter().map(|i| i.span())), | |
| ) | |
| } | |
| } | |
| impl Spanned for ColumnDef { | |
| fn span(&self) -> Span { | |
| let ColumnDef { | |
| name, | |
| data_type: _, // enum | |
| options, | |
| } = self; | |
| union_spans(core::iter::once(name.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(constraint) => constraint.span(), | |
| TableConstraint::PrimaryKey(constraint) => constraint.span(), | |
| TableConstraint::ForeignKey(constraint) => constraint.span(), | |
| TableConstraint::Check(constraint) => constraint.span(), | |
| TableConstraint::Index(constraint) => constraint.span(), | |
| TableConstraint::FulltextOrSpatial(constraint) => constraint.span(), | |
| TableConstraint::PrimaryKeyUsingIndex(constraint) | |
| | TableConstraint::UniqueUsingIndex(constraint) => constraint.span(), | |
| TableConstraint::Exclude(constraint) => constraint.span(), | |
| } | |
| } | |
| } | |
| impl Spanned for PartitionBoundValue { | |
| fn span(&self) -> Span { | |
| match self { | |
| PartitionBoundValue::Expr(expr) => expr.span(), | |
| // MINVALUE and MAXVALUE are keywords without tracked spans | |
| PartitionBoundValue::MinValue => Span::empty(), | |
| PartitionBoundValue::MaxValue => Span::empty(), | |
| } | |
| } | |
| } | |
| impl Spanned for ForValues { | |
| fn span(&self) -> Span { | |
| match self { | |
| ForValues::In(exprs) => union_spans(exprs.iter().map(|e| e.span())), | |
| ForValues::From { from, to } => union_spans( | |
| from.iter() | |
| .map(|v| v.span()) | |
| .chain(to.iter().map(|v| v.span())), | |
| ), | |
| // WITH (MODULUS n, REMAINDER r) - u64 values have no spans | |
| ForValues::With { .. } => Span::empty(), | |
| ForValues::Default => Span::empty(), | |
| } | |
| } | |
| } | |
| impl Spanned for CreateIndex { | |
| fn span(&self) -> Span { | |
| let CreateIndex { | |
| name, | |
| table_name, | |
| using: _, | |
| columns, | |
| unique: _, // bool | |
| concurrently: _, // bool | |
| r#async: _, // bool | |
| if_not_exists: _, // bool | |
| include, | |
| nulls_distinct: _, // bool | |
| with, | |
| predicate, | |
| index_options: _, | |
| alter_options, | |
| } = self; | |
| union_spans( | |
| name.iter() | |
| .map(|i| i.span()) | |
| .chain(core::iter::once(table_name.span())) | |
| .chain(columns.iter().map(|i| i.column.span())) | |
| .chain(include.iter().map(|i| i.span)) | |
| .chain(with.iter().map(|i| i.span())) | |
| .chain(predicate.iter().map(|i| i.span())) | |
| .chain(alter_options.iter().map(|i| i.span())), | |
| ) | |
| } | |
| } | |
| impl Spanned for IndexColumn { | |
| fn span(&self) -> Span { | |
| self.column.span() | |
| } | |
| } | |
| impl Spanned for ExcludeConstraintElement { | |
| fn span(&self) -> Span { | |
| self.column.span() | |
| } | |
| } | |
| impl Spanned for CaseStatement { | |
| fn span(&self) -> Span { | |
| let CaseStatement { | |
| case_token: AttachedToken(start), | |
| match_expr: _, | |
| when_blocks: _, | |
| else_block: _, | |
| end_case_token: AttachedToken(end), | |
| } = self; | |
| union_spans([start.span, end.span].into_iter()) | |
| } | |
| } | |
| impl Spanned for IfStatement { | |
| fn span(&self) -> Span { | |
| let IfStatement { | |
| if_block, | |
| elseif_blocks, | |
| else_block, | |
| end_token, | |
| } = self; | |
| union_spans( | |
| iter::once(if_block.span()) | |
| .chain(elseif_blocks.iter().map(|b| b.span())) | |
| .chain(else_block.as_ref().map(|b| b.span())) | |
| .chain(end_token.as_ref().map(|AttachedToken(t)| t.span)), | |
| ) | |
| } | |
| } | |
| impl Spanned for WhileStatement { | |
| fn span(&self) -> Span { | |
| let WhileStatement { while_block } = self; | |
| while_block.span() | |
| } | |
| } | |
| impl Spanned for ConditionalStatements { | |
| fn span(&self) -> Span { | |
| match self { | |
| ConditionalStatements::Sequence { statements } => { | |
| union_spans(statements.iter().map(|s| s.span())) | |
| } | |
| ConditionalStatements::BeginEnd(bes) => bes.span(), | |
| } | |
| } | |
| } | |
| impl Spanned for ConditionalStatementBlock { | |
| fn span(&self) -> Span { | |
| let ConditionalStatementBlock { | |
| start_token: AttachedToken(start_token), | |
| condition, | |
| then_token, | |
| conditional_statements, | |
| } = self; | |
| union_spans( | |
| iter::once(start_token.span) | |
| .chain(condition.as_ref().map(|c| c.span())) | |
| .chain(then_token.as_ref().map(|AttachedToken(t)| t.span)) | |
| .chain(iter::once(conditional_statements.span())), | |
| ) | |
| } | |
| } | |
| impl Spanned for RaiseStatement { | |
| fn span(&self) -> Span { | |
| let RaiseStatement { value } = self; | |
| union_spans(value.iter().map(|value| value.span())) | |
| } | |
| } | |
| impl Spanned for RaiseStatementValue { | |
| fn span(&self) -> Span { | |
| match self { | |
| RaiseStatementValue::UsingMessage(expr) => expr.span(), | |
| RaiseStatementValue::Expr(expr) => expr.span(), | |
| } | |
| } | |
| } | |
| /// # partial span | |
| /// | |
| /// Missing spans: | |
| /// - [ColumnOption::Null] | |
| /// - [ColumnOption::NotNull] | |
| /// - [ColumnOption::Comment] | |
| /// - [ColumnOption::PrimaryKey] | |
| /// - [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::PrimaryKey(constraint) => constraint.span(), | |
| ColumnOption::Unique(constraint) => constraint.span(), | |
| ColumnOption::Check(constraint) => constraint.span(), | |
| ColumnOption::ForeignKey(constraint) => constraint.span(), | |
| ColumnOption::DialectSpecific(_) => Span::empty(), | |
| ColumnOption::CharacterSet(object_name) => object_name.span(), | |
| ColumnOption::Collation(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(), | |
| ColumnOption::Srid(..) => Span::empty(), | |
| ColumnOption::Invisible => 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() | |
| } | |
| } | |
| impl Spanned for Analyze { | |
| fn span(&self) -> Span { | |
| union_spans( | |
| self.table_name | |
| .iter() | |
| .map(|t| t.span()) | |
| .chain( | |
| self.partitions | |
| .iter() | |
| .flat_map(|i| i.iter().map(|k| k.span())), | |
| ) | |
| .chain(self.columns.iter().map(|i| i.span)), | |
| ) | |
| } | |
| } | |
| /// # 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, | |
| had_set: _, | |
| } => 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 { | |
| delete_token, | |
| optimizer_hints: _, | |
| tables, | |
| from, | |
| using, | |
| selection, | |
| returning, | |
| output, | |
| order_by, | |
| limit, | |
| } = self; | |
| union_spans( | |
| core::iter::once(delete_token.0.span).chain( | |
| 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(output.iter().map(|i| i.span())) | |
| .chain(order_by.iter().map(|i| i.span())) | |
| .chain(limit.iter().map(|i| i.span())), | |
| ), | |
| ) | |
| } | |
| } | |
| impl Spanned for Update { | |
| fn span(&self) -> Span { | |
| let Update { | |
| update_token, | |
| optimizer_hints: _, | |
| table, | |
| assignments, | |
| from, | |
| selection, | |
| returning, | |
| output, | |
| or: _, | |
| order_by, | |
| limit, | |
| } = self; | |
| union_spans( | |
| core::iter::once(table.span()) | |
| .chain(core::iter::once(update_token.0.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()))) | |
| .chain(output.iter().map(|i| i.span())) | |
| .chain(order_by.iter().map(|i| i.span())) | |
| .chain(limit.iter().map(|i| i.span())), | |
| ) | |
| } | |
| } | |
| impl Spanned for Merge { | |
| fn span(&self) -> Span { | |
| union_spans( | |
| [self.merge_token.0.span, self.on.span()] | |
| .into_iter() | |
| .chain(self.clauses.iter().map(Spanned::span)) | |
| .chain(self.output.iter().map(Spanned::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; | |
| name.span.union_opt(&options.as_ref().map(|o| o.span())) | |
| } | |
| } | |
| impl Spanned for ColumnOptions { | |
| fn span(&self) -> Span { | |
| union_spans(self.as_slice().iter().map(|i| i.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())), | |
| ), | |
| SqlOption::TableSpace(_) => Span::empty(), | |
| SqlOption::Comment(_) => Span::empty(), | |
| SqlOption::NamedParenthesizedList(NamedParenthesizedList { | |
| key: name, | |
| name: value, | |
| values, | |
| }) => union_spans(core::iter::once(name.span).chain(values.iter().map(|i| i.span))) | |
| .union_opt(&value.as_ref().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.as_slice().iter().map(|i| i.span())) | |
| } | |
| CreateTableOptions::Plain(vec) => union_spans(vec.iter().map(|i| i.span())), | |
| CreateTableOptions::TableProperties(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 { | |
| constraint, | |
| not_valid: _, | |
| } => 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, | |
| drop_behavior: _, | |
| } => name.span, | |
| AlterTableOperation::DropColumn { | |
| has_column_keyword: _, | |
| column_names, | |
| if_exists: _, | |
| drop_behavior: _, | |
| } => union_spans(column_names.iter().map(|i| i.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::DropForeignKey { name, .. } => name.span, | |
| AlterTableOperation::DropIndex { name } => name.span, | |
| 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::ForceRowLevelSecurity => Span::empty(), | |
| AlterTableOperation::NoForceRowLevelSecurity => 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::ModifyOrderBy { order_by } => { | |
| union_spans(order_by.iter().map(|e| e.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::SetLogged => Span::empty(), | |
| AlterTableOperation::SetUnlogged => Span::empty(), | |
| AlterTableOperation::OwnerTo { .. } => Span::empty(), | |
| AlterTableOperation::ClusterBy { exprs } => union_spans(exprs.iter().map(|e| e.span())), | |
| AlterTableOperation::DropClusteringKey => Span::empty(), | |
| AlterTableOperation::AlterSortKey { .. } => Span::empty(), | |
| AlterTableOperation::SuspendRecluster => Span::empty(), | |
| AlterTableOperation::ResumeRecluster => Span::empty(), | |
| AlterTableOperation::Refresh { .. } => Span::empty(), | |
| AlterTableOperation::Suspend => Span::empty(), | |
| AlterTableOperation::Resume => Span::empty(), | |
| AlterTableOperation::Algorithm { .. } => Span::empty(), | |
| AlterTableOperation::AutoIncrement { value, .. } => value.span(), | |
| AlterTableOperation::Lock { .. } => Span::empty(), | |
| AlterTableOperation::ReplicaIdentity { .. } => Span::empty(), | |
| AlterTableOperation::ValidateConstraint { name } => name.span, | |
| AlterTableOperation::SetOptionsParens { options } => { | |
| union_spans(options.iter().map(|i| i.span())) | |
| } | |
| } | |
| } | |
| } | |
| 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())), | |
| ) | |
| } | |
| } | |
| /// # partial span | |
| /// | |
| /// Missing spans: | |
| /// - [OrderByKind::All] | |
| impl Spanned for OrderBy { | |
| fn span(&self) -> Span { | |
| match &self.kind { | |
| OrderByKind::All(_) => Span::empty(), | |
| OrderByKind::Expressions(exprs) => union_spans( | |
| exprs | |
| .iter() | |
| .map(|i| i.span()) | |
| .chain(self.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 { | |
| insert_token, | |
| optimizer_hints: _, | |
| or: _, // enum, sqlite specific | |
| ignore: _, // bool | |
| into: _, // bool | |
| table, | |
| table_alias, | |
| columns, | |
| by_name: _, // bool | |
| overwrite: _, // bool | |
| source, | |
| partitioned, | |
| after_columns, | |
| has_table_keyword: _, // bool | |
| on, | |
| returning, | |
| output, | |
| replace_into: _, // bool | |
| priority: _, // todo, mysql specific | |
| insert_alias: _, // todo, mysql specific | |
| assignments, | |
| settings: _, // todo, clickhouse specific | |
| format_clause: _, // todo, clickhouse specific | |
| multi_table_insert_type: _, // snowflake multi-table insert | |
| multi_table_into_clauses: _, // snowflake multi-table insert | |
| multi_table_when_clauses: _, // snowflake multi-table insert | |
| multi_table_else_clause: _, // snowflake multi-table insert | |
| } = self; | |
| union_spans( | |
| core::iter::once(insert_token.0.span) | |
| .chain(core::iter::once(table.span())) | |
| .chain(table_alias.iter().map(|k| k.alias.span)) | |
| .chain(columns.iter().map(|i| i.span())) | |
| .chain(source.as_ref().map(|q| q.span())) | |
| .chain(assignments.iter().map(|i| i.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()))) | |
| .chain(output.iter().map(|i| i.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 <expr>` reports as `<expr>::span`. | |
| /// | |
| /// Missing spans: | |
| /// - [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::CompoundFieldAccess { root, access_chain } => { | |
| union_spans(iter::once(root.span()).chain(access_chain.iter().map(|i| i.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::IsJson { | |
| expr, | |
| kind: _, | |
| unique_keys: _, | |
| negated: _, | |
| } => 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::RLike { .. } => Span::empty(), | |
| Expr::IsNormalized { | |
| expr, | |
| form: _, | |
| negated: _, | |
| } => expr.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(TypedString { value, .. }) => value.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::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: _, | |
| shorthand: _, | |
| } => 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::Prefixed { value, .. } => value.span(), | |
| Expr::Case { | |
| case_token, | |
| end_token, | |
| operand, | |
| conditions, | |
| else_result, | |
| } => union_spans( | |
| iter::once(case_token.0.span) | |
| .chain( | |
| operand | |
| .as_ref() | |
| .map(|i| i.span()) | |
| .into_iter() | |
| .chain(conditions.iter().flat_map(|case_when| { | |
| [case_when.condition.span(), case_when.result.span()] | |
| })) | |
| .chain(else_result.as_ref().map(|i| i.span())), | |
| ) | |
| .chain(iter::once(end_token.0.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::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::MemberOf(member_of) => member_of.value.span().union(&member_of.array.span()), | |
| } | |
| } | |
| } | |
| 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 AccessExpr { | |
| fn span(&self) -> Span { | |
| match self { | |
| AccessExpr::Dot(ident) => ident.span(), | |
| AccessExpr::Subscript(subscript) => subscript.span(), | |
| } | |
| } | |
| } | |
| impl Spanned for ObjectName { | |
| fn span(&self) -> Span { | |
| let ObjectName(segments) = self; | |
| union_spans(segments.iter().map(|i| i.span())) | |
| } | |
| } | |
| impl Spanned for ObjectNamePart { | |
| fn span(&self) -> Span { | |
| match self { | |
| ObjectNamePart::Identifier(ident) => ident.span, | |
| ObjectNamePart::Function(func) => func | |
| .name | |
| .span | |
| .union(&union_spans(func.args.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, | |
| uses_odbc_syntax: _, | |
| 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::Where(expr) => expr.span(), | |
| 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(), | |
| FunctionArgumentClause::JsonReturningClause(_) => 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(), | |
| JsonPathElem::ColonBracket { key } => key.span(), | |
| } | |
| } | |
| } | |
| impl Spanned for SelectItemQualifiedWildcardKind { | |
| fn span(&self) -> Span { | |
| match self { | |
| SelectItemQualifiedWildcardKind::ObjectName(object_name) => object_name.span(), | |
| SelectItemQualifiedWildcardKind::Expr(expr) => expr.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::ExprWithAliases { expr, aliases } => { | |
| union_spans(iter::once(expr.span()).chain(aliases.iter().map(|i| i.span))) | |
| } | |
| SelectItem::QualifiedWildcard(kind, wildcard_additional_options) => union_spans( | |
| [kind.span()] | |
| .into_iter() | |
| .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, | |
| opt_alias, | |
| } = 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())) | |
| .chain(opt_alias.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(name) => name.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: _, | |
| sample: _, | |
| index_hints: _, | |
| } => 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, | |
| sample: _, | |
| } => 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, | |
| with_ordinality: _, | |
| 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::XmlTable { .. } => 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, | |
| null_inclusion: _, | |
| 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(|ilist| ilist.span())) | |
| .chain(alias.as_ref().map(|alias| alias.span())), | |
| ), | |
| TableFactor::UnpivotExpr { | |
| expression, | |
| value_alias, | |
| attribute_alias, | |
| } => union_spans( | |
| core::iter::once(expression.span()) | |
| .chain(core::iter::once(value_alias.span)) | |
| .chain(attribute_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::SemanticView { | |
| name, | |
| dimensions, | |
| metrics, | |
| facts, | |
| where_clause, | |
| alias, | |
| } => union_spans( | |
| name.0 | |
| .iter() | |
| .map(|i| i.span()) | |
| .chain(dimensions.iter().map(|d| d.span())) | |
| .chain(metrics.iter().map(|m| m.span())) | |
| .chain(facts.iter().map(|f| f.span())) | |
| .chain(where_clause.as_ref().map(|e| e.span())) | |
| .chain(alias.as_ref().map(|a| a.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, | |
| options: _, | |
| 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] | |
| /// - [FunctionArgExpr::WildcardWithOptions] | |
| 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(), | |
| FunctionArgExpr::WildcardWithOptions(_) => Span::empty(), | |
| } | |
| } | |
| } | |
| impl Spanned for TableAlias { | |
| fn span(&self) -> Span { | |
| let TableAlias { | |
| explicit: _, | |
| name, | |
| columns, | |
| at, | |
| } = self; | |
| union_spans( | |
| core::iter::once(name.span) | |
| .chain(columns.iter().map(Spanned::span)) | |
| .chain(at.iter().map(|at| at.span)), | |
| ) | |
| } | |
| } | |
| impl Spanned for TableAliasColumnDef { | |
| fn span(&self) -> Span { | |
| let TableAliasColumnDef { name, data_type: _ } = self; | |
| name.span | |
| } | |
| } | |
| impl Spanned for ValueWithSpan { | |
| fn span(&self) -> Span { | |
| self.span | |
| } | |
| } | |
| 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::Join(join_constraint) => join_constraint.span(), | |
| JoinOperator::Inner(join_constraint) => join_constraint.span(), | |
| JoinOperator::Left(join_constraint) => join_constraint.span(), | |
| JoinOperator::LeftOuter(join_constraint) => join_constraint.span(), | |
| JoinOperator::Right(join_constraint) => join_constraint.span(), | |
| JoinOperator::RightOuter(join_constraint) => join_constraint.span(), | |
| JoinOperator::FullOuter(join_constraint) => join_constraint.span(), | |
| JoinOperator::CrossJoin(join_constraint) => join_constraint.span(), | |
| 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(), | |
| JoinOperator::StraightJoin(join_constraint) => join_constraint.span(), | |
| JoinOperator::ArrayJoin => Span::empty(), | |
| JoinOperator::LeftArrayJoin => Span::empty(), | |
| JoinOperator::InnerArrayJoin => Span::empty(), | |
| } | |
| } | |
| } | |
| /// # 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, | |
| optimizer_hints: _, | |
| distinct: _, // todo | |
| select_modifiers: _, | |
| top: _, // todo, mysql specific | |
| projection, | |
| exclude: _, | |
| 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: _, | |
| flavor: _, | |
| } = 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(connect_by.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())), | |
| ) | |
| } | |
| } | |
| impl Spanned for ConnectByKind { | |
| fn span(&self) -> Span { | |
| match self { | |
| ConnectByKind::ConnectBy { | |
| connect_token, | |
| nocycle: _, | |
| relationships, | |
| } => union_spans( | |
| core::iter::once(connect_token.0.span()) | |
| .chain(relationships.last().iter().map(|item| item.span())), | |
| ), | |
| ConnectByKind::StartWith { | |
| start_token, | |
| condition, | |
| } => union_spans([start_token.0.span(), condition.span()].into_iter()), | |
| } | |
| } | |
| } | |
| 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 | |
| targets, | |
| } = self; | |
| union_spans(targets.iter().map(|t| t.span())) | |
| } | |
| } | |
| impl Spanned for UpdateTableFromKind { | |
| fn span(&self) -> Span { | |
| let from = match self { | |
| UpdateTableFromKind::BeforeSet(from) => from, | |
| UpdateTableFromKind::AfterSet(from) => from, | |
| }; | |
| union_spans(from.iter().map(|t| t.span())) | |
| } | |
| } | |
| impl Spanned for TableObject { | |
| fn span(&self) -> Span { | |
| match self { | |
| TableObject::TableName(ObjectName(segments)) => { | |
| union_spans(segments.iter().map(|i| i.span())) | |
| } | |
| TableObject::TableFunction(func) => func.span(), | |
| TableObject::TableQuery(query) => query.span(), | |
| } | |
| } | |
| } | |
| impl Spanned for BeginEndStatements { | |
| fn span(&self) -> Span { | |
| let BeginEndStatements { | |
| begin_token, | |
| statements, | |
| end_token, | |
| } = self; | |
| union_spans( | |
| core::iter::once(begin_token.0.span) | |
| .chain(statements.iter().map(|i| i.span())) | |
| .chain(core::iter::once(end_token.0.span)), | |
| ) | |
| } | |
| } | |
| impl Spanned for OpenStatement { | |
| fn span(&self) -> Span { | |
| let OpenStatement { cursor_name } = self; | |
| cursor_name.span | |
| } | |
| } | |
| impl Spanned for AlterSchemaOperation { | |
| fn span(&self) -> Span { | |
| match self { | |
| AlterSchemaOperation::SetDefaultCollate { collate } => collate.span(), | |
| AlterSchemaOperation::AddReplica { replica, options } => union_spans( | |
| core::iter::once(replica.span) | |
| .chain(options.iter().flat_map(|i| i.iter().map(|i| i.span()))), | |
| ), | |
| AlterSchemaOperation::DropReplica { replica } => replica.span, | |
| AlterSchemaOperation::SetOptionsParens { options } => { | |
| union_spans(options.iter().map(|i| i.span())) | |
| } | |
| AlterSchemaOperation::Rename { name } => name.span(), | |
| AlterSchemaOperation::OwnerTo { owner } => { | |
| if let Owner::Ident(ident) = owner { | |
| ident.span | |
| } else { | |
| Span::empty() | |
| } | |
| } | |
| } | |
| } | |
| } | |
| impl Spanned for AlterSchema { | |
| fn span(&self) -> Span { | |
| union_spans( | |
| core::iter::once(self.name.span()).chain(self.operations.iter().map(|i| i.span())), | |
| ) | |
| } | |
| } | |
| impl Spanned for CreateView { | |
| fn span(&self) -> Span { | |
| union_spans( | |
| core::iter::once(self.name.span()) | |
| .chain(self.columns.iter().map(|i| i.span())) | |
| .chain(core::iter::once(self.query.span())) | |
| .chain(core::iter::once(self.options.span())) | |
| .chain(self.cluster_by.iter().map(|i| i.span)) | |
| .chain(self.to.iter().map(|i| i.span())), | |
| ) | |
| } | |
| } | |
| impl Spanned for AlterTable { | |
| fn span(&self) -> Span { | |
| union_spans( | |
| core::iter::once(self.name.span()) | |
| .chain(self.operations.iter().map(|i| i.span())) | |
| .chain(self.on_cluster.iter().map(|i| i.span)) | |
| .chain(core::iter::once(self.end_token.0.span)), | |
| ) | |
| } | |
| } | |
| impl Spanned for CreateOperator { | |
| fn span(&self) -> Span { | |
| Span::empty() | |
| } | |
| } | |
| impl Spanned for CreateOperatorFamily { | |
| fn span(&self) -> Span { | |
| Span::empty() | |
| } | |
| } | |
| impl Spanned for CreateOperatorClass { | |
| fn span(&self) -> Span { | |
| Span::empty() | |
| } | |
| } | |
| impl Spanned for MergeClause { | |
| fn span(&self) -> Span { | |
| union_spans([self.when_token.0.span, self.action.span()].into_iter()) | |
| } | |
| } | |
| impl Spanned for MergeAction { | |
| fn span(&self) -> Span { | |
| match self { | |
| MergeAction::Insert(expr) => expr.span(), | |
| MergeAction::Update(expr) => expr.span(), | |
| MergeAction::Delete { delete_token } => delete_token.0.span, | |
| MergeAction::DoNothing { | |
| do_token, | |
| nothing_token, | |
| } => do_token.0.span.union(¬hing_token.0.span), | |
| } | |
| } | |
| } | |
| impl Spanned for MergeInsertExpr { | |
| fn span(&self) -> Span { | |
| union_spans( | |
| [ | |
| self.insert_token.0.span, | |
| self.kind_token.0.span, | |
| match self.kind { | |
| MergeInsertKind::Values(ref values) => values.span(), | |
| MergeInsertKind::Row | MergeInsertKind::Wildcard => Span::empty(), | |
| }, | |
| ] | |
| .into_iter() | |
| .chain(self.insert_predicate.iter().map(Spanned::span)) | |
| .chain(self.columns.iter().map(|i| i.span())), | |
| ) | |
| } | |
| } | |
| impl Spanned for MergeUpdateExpr { | |
| fn span(&self) -> Span { | |
| let kind_span = match &self.kind { | |
| MergeUpdateKind::Set(assignments) => union_spans(assignments.iter().map(Spanned::span)), | |
| MergeUpdateKind::Wildcard => Span::empty(), | |
| }; | |
| union_spans( | |
| core::iter::once(self.update_token.0.span) | |
| .chain(core::iter::once(kind_span)) | |
| .chain(self.update_predicate.iter().map(Spanned::span)) | |
| .chain(self.delete_predicate.iter().map(Spanned::span)), | |
| ) | |
| } | |
| } | |
| impl Spanned for OutputClause { | |
| fn span(&self) -> Span { | |
| match self { | |
| OutputClause::Output { | |
| output_token, | |
| select_items, | |
| into_table, | |
| } => union_spans( | |
| core::iter::once(output_token.0.span) | |
| .chain(into_table.iter().map(Spanned::span)) | |
| .chain(select_items.iter().map(Spanned::span)), | |
| ), | |
| OutputClause::Returning { | |
| returning_token, | |
| select_items, | |
| } => union_spans( | |
| core::iter::once(returning_token.0.span) | |
| .chain(select_items.iter().map(Spanned::span)), | |
| ), | |
| } | |
| } | |
| } | |
| impl Spanned for comments::CommentWithSpan { | |
| fn span(&self) -> Span { | |
| self.span | |
| } | |
| } | |
| #[cfg(test)] | |
| pub mod tests { | |
| use crate::ast::Value; | |
| use crate::dialect::{Dialect, GenericDialect, SnowflakeDialect}; | |
| use crate::parser::Parser; | |
| use crate::tokenizer::{Location, 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"); | |
| } | |
| #[test] | |
| fn test_case_expr_span() { | |
| let dialect = &GenericDialect; | |
| let mut test = SpanTest::new(dialect, "CASE 1 WHEN 2 THEN 3 ELSE 4 END"); | |
| let expr = test.0.parse_expr().unwrap(); | |
| let expr_span = expr.span(); | |
| assert_eq!( | |
| test.get_source(expr_span), | |
| "CASE 1 WHEN 2 THEN 3 ELSE 4 END" | |
| ); | |
| } | |
| #[test] | |
| fn test_placeholder_span() { | |
| let sql = "\nSELECT\n :fooBar"; | |
| let r = Parser::parse_sql(&GenericDialect, sql).unwrap(); | |
| assert_eq!(1, r.len()); | |
| match &r[0] { | |
| Statement::Query(q) => { | |
| let col = &q.body.as_select().unwrap().projection[0]; | |
| match col { | |
| SelectItem::UnnamedExpr(Expr::Value(ValueWithSpan { | |
| value: Value::Placeholder(s), | |
| span, | |
| })) => { | |
| assert_eq!(":fooBar", s); | |
| assert_eq!(&Span::new((3, 3).into(), (3, 10).into()), span); | |
| } | |
| _ => panic!("expected unnamed expression; got {col:?}"), | |
| } | |
| } | |
| stmt => panic!("expected query; got {stmt:?}"), | |
| } | |
| } | |
| #[test] | |
| fn test_alter_table_multiline_span() { | |
| let sql = r#"-- foo | |
| ALTER TABLE users | |
| ADD COLUMN foo | |
| varchar; -- hi there"#; | |
| let r = Parser::parse_sql(&crate::dialect::PostgreSqlDialect {}, sql).unwrap(); | |
| assert_eq!(1, r.len()); | |
| let stmt_span = r[0].span(); | |
| assert_eq!(stmt_span.start, (2, 13).into()); | |
| assert_eq!(stmt_span.end, (4, 11).into()); | |
| } | |
| #[test] | |
| fn test_update_statement_span() { | |
| let sql = r#"-- foo | |
| UPDATE foo | |
| /* bar */ | |
| SET bar = 3 | |
| WHERE quux > 42 ; | |
| "#; | |
| let r = Parser::parse_sql(&crate::dialect::GenericDialect, sql).unwrap(); | |
| assert_eq!(1, r.len()); | |
| let stmt_span = r[0].span(); | |
| assert_eq!(stmt_span.start, (2, 7).into()); | |
| assert_eq!(stmt_span.end, (5, 17).into()); | |
| } | |
| #[test] | |
| fn test_insert_statement_span() { | |
| let sql = r#" | |
| /* foo */ INSERT INTO FOO (X, Y, Z) | |
| SELECT 1, 2, 3 | |
| FROM DUAL | |
| ;"#; | |
| let r = Parser::parse_sql(&crate::dialect::GenericDialect, sql).unwrap(); | |
| assert_eq!(1, r.len()); | |
| let stmt_span = r[0].span(); | |
| assert_eq!(stmt_span.start, (2, 11).into()); | |
| assert_eq!(stmt_span.end, (4, 12).into()); | |
| } | |
| #[test] | |
| fn test_replace_statement_span() { | |
| let sql = r#" | |
| /* foo */ REPLACE INTO | |
| cities(name,population) | |
| SELECT | |
| name, | |
| population | |
| FROM | |
| cities | |
| WHERE id = 1 | |
| ;"#; | |
| let r = Parser::parse_sql(&crate::dialect::GenericDialect, sql).unwrap(); | |
| assert_eq!(1, r.len()); | |
| dbg!(&r[0]); | |
| let stmt_span = r[0].span(); | |
| assert_eq!(stmt_span.start, (2, 11).into()); | |
| assert_eq!(stmt_span.end, (9, 13).into()); | |
| } | |
| #[test] | |
| fn test_delete_statement_span() { | |
| let sql = r#"-- foo | |
| DELETE /* quux */ | |
| FROM foo | |
| WHERE foo.x = 42 | |
| ;"#; | |
| let r = Parser::parse_sql(&crate::dialect::GenericDialect, sql).unwrap(); | |
| assert_eq!(1, r.len()); | |
| let stmt_span = r[0].span(); | |
| assert_eq!(stmt_span.start, (2, 7).into()); | |
| assert_eq!(stmt_span.end, (4, 24).into()); | |
| } | |
| #[test] | |
| fn test_merge_statement_spans() { | |
| let sql = r#" | |
| -- plain merge statement; no RETURNING, no OUTPUT | |
| MERGE INTO target_table USING source_table | |
| ON target_table.id = source_table.oooid | |
| /* an inline comment */ WHEN NOT MATCHED THEN | |
| INSERT (ID, description) | |
| VALUES (source_table.id, source_table.description) | |
| -- another one | |
| WHEN MATCHED AND target_table.x = 'X' THEN | |
| UPDATE SET target_table.description = source_table.description | |
| WHEN MATCHED AND target_table.x != 'X' THEN DELETE | |
| WHEN NOT MATCHED AND 1 THEN INSERT (product, quantity) ROW | |
| WHEN MATCHED THEN DO NOTHING | |
| "#; | |
| let r = Parser::parse_sql(&crate::dialect::GenericDialect, sql).unwrap(); | |
| assert_eq!(1, r.len()); | |
| // ~ assert the span of the whole statement | |
| let stmt_span = r[0].span(); | |
| assert_eq!(stmt_span.start, (4, 9).into()); | |
| assert_eq!(stmt_span.end, (17, 37).into()); | |
| // ~ individual tokens within the statement | |
| let Statement::Merge(Merge { | |
| merge_token, | |
| optimizer_hints: _, | |
| into: _, | |
| table: _, | |
| source: _, | |
| on: _, | |
| clauses, | |
| output, | |
| }) = &r[0] | |
| else { | |
| panic!("not a MERGE statement"); | |
| }; | |
| assert_eq!( | |
| merge_token.0.span, | |
| Span::new(Location::new(4, 9), Location::new(4, 14)) | |
| ); | |
| assert_eq!(clauses.len(), 5); | |
| // ~ the INSERT clause's TOKENs | |
| assert_eq!( | |
| clauses[0].when_token.0.span, | |
| Span::new(Location::new(7, 33), Location::new(7, 37)) | |
| ); | |
| if let MergeAction::Insert(MergeInsertExpr { | |
| insert_token, | |
| kind_token, | |
| .. | |
| }) = &clauses[0].action | |
| { | |
| assert_eq!( | |
| insert_token.0.span, | |
| Span::new(Location::new(8, 13), Location::new(8, 19)) | |
| ); | |
| assert_eq!( | |
| kind_token.0.span, | |
| Span::new(Location::new(9, 16), Location::new(9, 22)) | |
| ); | |
| } else { | |
| panic!("not a MERGE INSERT clause"); | |
| } | |
| // ~ the UPDATE token(s) | |
| assert_eq!( | |
| clauses[1].when_token.0.span, | |
| Span::new(Location::new(12, 17), Location::new(12, 21)) | |
| ); | |
| if let MergeAction::Update(MergeUpdateExpr { | |
| update_token, | |
| kind: _, | |
| update_predicate: _, | |
| delete_predicate: _, | |
| }) = &clauses[1].action | |
| { | |
| assert_eq!( | |
| update_token.0.span, | |
| Span::new(Location::new(13, 13), Location::new(13, 19)) | |
| ); | |
| } else { | |
| panic!("not a MERGE UPDATE clause"); | |
| } | |
| // the DELETE token(s) | |
| assert_eq!( | |
| clauses[2].when_token.0.span, | |
| Span::new(Location::new(15, 15), Location::new(15, 19)) | |
| ); | |
| if let MergeAction::Delete { delete_token } = &clauses[2].action { | |
| assert_eq!( | |
| delete_token.0.span, | |
| Span::new(Location::new(15, 61), Location::new(15, 67)) | |
| ); | |
| } else { | |
| panic!("not a MERGE DELETE clause"); | |
| } | |
| // ~ an INSERT clause's ROW token | |
| assert_eq!( | |
| clauses[3].when_token.0.span, | |
| Span::new(Location::new(16, 9), Location::new(16, 13)) | |
| ); | |
| if let MergeAction::Insert(MergeInsertExpr { | |
| insert_token, | |
| kind_token, | |
| .. | |
| }) = &clauses[3].action | |
| { | |
| assert_eq!( | |
| insert_token.0.span, | |
| Span::new(Location::new(16, 37), Location::new(16, 43)) | |
| ); | |
| assert_eq!( | |
| kind_token.0.span, | |
| Span::new(Location::new(16, 64), Location::new(16, 67)) | |
| ); | |
| } else { | |
| panic!("not a MERGE INSERT clause"); | |
| } | |
| assert_eq!( | |
| clauses[4].when_token.0.span, | |
| Span::new(Location::new(17, 9), Location::new(17, 13)) | |
| ); | |
| if let MergeAction::DoNothing { | |
| do_token, | |
| nothing_token, | |
| } = &clauses[4].action | |
| { | |
| assert_eq!( | |
| do_token.0.span, | |
| Span::new(Location::new(17, 27), Location::new(17, 29)) | |
| ); | |
| assert_eq!( | |
| nothing_token.0.span, | |
| Span::new(Location::new(17, 30), Location::new(17, 37)) | |
| ); | |
| assert_eq!( | |
| clauses[4].action.span(), | |
| Span::new(Location::new(17, 27), Location::new(17, 37)) | |
| ); | |
| } else { | |
| panic!("not a MERGE DO NOTHING clause"); | |
| } | |
| assert!(output.is_none()); | |
| } | |
| #[test] | |
| fn test_merge_statement_spans_with_returning() { | |
| let sql = r#" | |
| MERGE INTO wines AS w | |
| USING wine_stock_changes AS s | |
| ON s.winename = w.winename | |
| WHEN NOT MATCHED AND s.stock_delta > 0 THEN INSERT VALUES (s.winename, s.stock_delta) | |
| WHEN MATCHED AND w.stock + s.stock_delta > 0 THEN UPDATE SET stock = w.stock + s.stock_delta | |
| WHEN MATCHED THEN DELETE | |
| RETURNING merge_action(), w.* | |
| "#; | |
| let r = Parser::parse_sql(&crate::dialect::GenericDialect, sql).unwrap(); | |
| assert_eq!(1, r.len()); | |
| // ~ assert the span of the whole statement | |
| let stmt_span = r[0].span(); | |
| assert_eq!( | |
| stmt_span, | |
| Span::new(Location::new(2, 5), Location::new(8, 34)) | |
| ); | |
| // ~ individual tokens within the statement | |
| if let Statement::Merge(Merge { output, .. }) = &r[0] { | |
| if let Some(OutputClause::Returning { | |
| returning_token, .. | |
| }) = output | |
| { | |
| assert_eq!( | |
| returning_token.0.span, | |
| Span::new(Location::new(8, 5), Location::new(8, 14)) | |
| ); | |
| } else { | |
| panic!("unexpected MERGE output clause"); | |
| } | |
| } else { | |
| panic!("not a MERGE statement"); | |
| }; | |
| } | |
| #[test] | |
| fn test_merge_statement_spans_with_output() { | |
| let sql = r#"MERGE INTO a USING b ON a.id = b.id | |
| WHEN MATCHED THEN DELETE | |
| OUTPUT inserted.*"#; | |
| let r = Parser::parse_sql(&crate::dialect::GenericDialect, sql).unwrap(); | |
| assert_eq!(1, r.len()); | |
| // ~ assert the span of the whole statement | |
| let stmt_span = r[0].span(); | |
| assert_eq!( | |
| stmt_span, | |
| Span::new(Location::new(1, 1), Location::new(3, 32)) | |
| ); | |
| // ~ individual tokens within the statement | |
| if let Statement::Merge(Merge { output, .. }) = &r[0] { | |
| if let Some(OutputClause::Output { output_token, .. }) = output { | |
| assert_eq!( | |
| output_token.0.span, | |
| Span::new(Location::new(3, 15), Location::new(3, 21)) | |
| ); | |
| } else { | |
| panic!("unexpected MERGE output clause"); | |
| } | |
| } else { | |
| panic!("not a MERGE statement"); | |
| }; | |
| } | |
| #[test] | |
| fn test_merge_statement_spans_with_update_predicates() { | |
| let sql = r#" | |
| MERGE INTO a USING b ON a.id = b.id | |
| WHEN MATCHED THEN | |
| UPDATE set a.x = a.x + b.x | |
| WHERE b.x != 2 | |
| DELETE WHERE a.x <> 3"#; | |
| let r = Parser::parse_sql(&crate::dialect::GenericDialect, sql).unwrap(); | |
| assert_eq!(1, r.len()); | |
| // ~ assert the span of the whole statement | |
| let stmt_span = r[0].span(); | |
| assert_eq!( | |
| stmt_span, | |
| Span::new(Location::new(2, 8), Location::new(6, 36)) | |
| ); | |
| } | |
| #[test] | |
| fn test_merge_statement_spans_with_insert_predicate() { | |
| let sql = r#" | |
| MERGE INTO a USING b ON a.id = b.id | |
| WHEN NOT MATCHED THEN | |
| INSERT VALUES (b.x, b.y) WHERE b.x != 2 | |
| -- qed | |
| "#; | |
| let r = Parser::parse_sql(&crate::dialect::GenericDialect, sql).unwrap(); | |
| assert_eq!(1, r.len()); | |
| // ~ assert the span of the whole statement | |
| let stmt_span = r[0].span(); | |
| assert_eq!( | |
| stmt_span, | |
| Span::new(Location::new(2, 8), Location::new(4, 52)) | |
| ); | |
| } | |
| #[test] | |
| fn test_create_foreign_table_option_spans() { | |
| let dialect = &crate::dialect::PostgreSqlDialect {}; | |
| let sql = r#"CREATE FOREIGN TABLE ft (a INT) SERVER s OPTIONS ("schema_name" 'public')"#; | |
| let mut test = SpanTest::new(dialect, sql); | |
| let options = match test.0.parse_statement().unwrap() { | |
| Statement::CreateForeignTable(stmt) => stmt.options.unwrap(), | |
| stmt => panic!("expected CREATE FOREIGN TABLE, got {stmt:?}"), | |
| }; | |
| assert_eq!(test.get_source(options[0].key.span), r#""schema_name""#); | |
| assert_eq!(test.get_source(options[0].value.span), "'public'"); | |
| } | |
| #[test] | |
| fn test_alter_table_modify_order_by_span() { | |
| let dialect = &crate::dialect::ClickHouseDialect {}; | |
| let sql = "ALTER TABLE t MODIFY ORDER BY (a, b.c)"; | |
| let test = SpanTest::new(dialect, sql); | |
| let r = Parser::parse_sql(dialect, sql).unwrap(); | |
| match &r[0] { | |
| Statement::AlterTable(alter) => { | |
| let op_span = alter.operations[0].span(); | |
| assert_eq!(test.get_source(op_span), "a, b.c"); | |
| } | |
| stmt => panic!("expected ALTER TABLE; got {stmt:?}"), | |
| } | |
| } | |
| } |