datafusion-sqlparse/src/ast/spans.rs
Andrew Lamb 4ab3ab9147
Update comments / docs for Spanned (#1549)
Co-authored-by: Ifeanyi Ubah <ify1992@yahoo.com>
2024-11-30 08:08:55 -05:00

2208 lines
72 KiB
Rust

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