mirror of
https://github.com/apache/datafusion-sqlparser-rs.git
synced 2025-07-07 17:04:59 +00:00
2208 lines
72 KiB
Rust
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");
|
|
}
|
|
}
|