Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 18 additions & 18 deletions src/ast/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -96,24 +96,24 @@ pub use self::dml::{
pub use self::operator::{BinaryOperator, UnaryOperator};
pub use self::query::{
AfterMatchSkip, ConnectByKind, Cte, CteAsMaterialized, Distinct, EmptyMatchesMode,
ExceptSelectItem, ExcludeSelectItem, ExprWithAlias, ExprWithAliasAndOrderBy, Fetch, ForClause,
ForJson, ForXml, FormatClause, GroupByExpr, GroupByWithModifier, IdentWithAlias,
IlikeSelectItem, InputFormatClause, Interpolate, InterpolateExpr, Join, JoinConstraint,
JoinOperator, JsonTableColumn, JsonTableColumnErrorHandling, JsonTableNamedColumn,
JsonTableNestedColumn, LateralView, LimitClause, LockClause, LockType, MatchRecognizePattern,
MatchRecognizeSymbol, Measure, NamedWindowDefinition, NamedWindowExpr, NonBlock, Offset,
OffsetRows, OpenJsonTableColumn, OrderBy, OrderByExpr, OrderByKind, OrderByOptions,
OrderBySort, PipeOperator, PivotValueSource, ProjectionSelect, Query, RenameSelectItem,
RepetitionQuantifier, ReplaceSelectElement, ReplaceSelectItem, RowsPerMatch, Select,
SelectFlavor, SelectInto, SelectItem, SelectItemQualifiedWildcardKind, SelectModifiers,
SetExpr, SetOperator, SetQuantifier, Setting, SymbolDefinition, Table, TableAlias,
TableAliasColumnDef, TableFactor, TableFunctionArgs, TableIndexHintForClause,
TableIndexHintType, TableIndexHints, TableIndexType, TableSample, TableSampleBucket,
TableSampleKind, TableSampleMethod, TableSampleModifier, TableSampleQuantity, TableSampleSeed,
TableSampleSeedModifier, TableSampleUnit, TableVersion, TableWithJoins, Top, TopQuantity,
UpdateTableFromKind, ValueTableMode, Values, WildcardAdditionalOptions, With, WithFill,
XmlNamespaceDefinition, XmlPassingArgument, XmlPassingClause, XmlTableColumn,
XmlTableColumnOption,
ExceptSelectItem, ExcludeSelectItem, ExplicitTable, ExprWithAlias, ExprWithAliasAndOrderBy,
Fetch, ForClause, ForJson, ForXml, FormatClause, GroupByExpr, GroupByWithModifier,
IdentWithAlias, IlikeSelectItem, InheritanceModifier, InputFormatClause, Interpolate,
InterpolateExpr, Join, JoinConstraint, JoinOperator, JsonTableColumn,
JsonTableColumnErrorHandling, JsonTableNamedColumn, JsonTableNestedColumn, LateralView,
LimitClause, LockClause, LockType, MatchRecognizePattern, MatchRecognizeSymbol, Measure,
NamedWindowDefinition, NamedWindowExpr, NonBlock, Offset, OffsetRows, OpenJsonTableColumn,
OrderBy, OrderByExpr, OrderByKind, OrderByOptions, OrderBySort, PipeOperator, PivotValueSource,
ProjectionSelect, Query, RenameSelectItem, RepetitionQuantifier, ReplaceSelectElement,
ReplaceSelectItem, RowsPerMatch, Select, SelectFlavor, SelectInto, SelectItem,
SelectItemQualifiedWildcardKind, SelectModifiers, SetExpr, SetOperator, SetQuantifier, Setting,
SymbolDefinition, TableAlias, TableAliasColumnDef, TableFactor, TableFunctionArgs,
TableIndexHintForClause, TableIndexHintType, TableIndexHints, TableIndexType, TableSample,
TableSampleBucket, TableSampleKind, TableSampleMethod, TableSampleModifier,
TableSampleQuantity, TableSampleSeed, TableSampleSeedModifier, TableSampleUnit, TableVersion,
TableWithJoins, Top, TopQuantity, UpdateTableFromKind, ValueTableMode, Values,
WildcardAdditionalOptions, With, WithFill, XmlNamespaceDefinition, XmlPassingArgument,
XmlPassingClause, XmlTableColumn, XmlTableColumnOption,
};

pub use self::trigger::{
Expand Down
55 changes: 38 additions & 17 deletions src/ast/query.rs
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,7 @@ pub enum SetExpr {
/// `MERGE` statement
Merge(Statement),
/// `TABLE` command
Table(Box<Table>),
Table(Box<ExplicitTable>),
}

impl SetExpr {
Expand Down Expand Up @@ -293,28 +293,49 @@ impl fmt::Display for SetQuantifier {
}
}

/// SQL:2016 `<explicit table>`: `TABLE <name>`, shorthand for `SELECT * FROM <name>`.
/// Postgres extends with `ONLY` and trailing `*`; see [`InheritanceModifier`].
///
/// <https://jakewheat.github.io/sql-overview/sql-2016-foundation-grammar.html#explicit-table>
/// <https://www.postgresql.org/docs/current/sql-select.html#SQL-TABLE>
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
/// A [`TABLE` command]( https://www.postgresql.org/docs/current/sql-select.html#SQL-TABLE)
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
/// A (possibly schema-qualified) table reference used in `FROM` clauses.
pub struct Table {
/// Optional table name (absent for e.g. `TABLE` command without argument).
pub table_name: Option<String>,
/// Optional schema/catalog name qualifying the table.
pub schema_name: Option<String>,
pub struct ExplicitTable {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hmm not sure if there's a need to rename Table (its not clear to me what we gain by doing so)?

/// The (possibly schema-qualified) table name.
pub name: ObjectName,
/// Postgres inheritance modifier (`ONLY` or trailing `*`), if present.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
/// Postgres inheritance modifier (`ONLY` or trailing `*`), if present.
/// Inheritance modifier.

the rest of the comment is duplicated, the pg part we can drop since other dialects can support it either now or in the future

pub inheritance: InheritanceModifier,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this should be an Option instead of introducing a custom None variant in the enum

}

/// Postgres inheritance-hierarchy modifier for table references.
///
/// Controls whether a query against a table includes rows from descendant
/// tables (inheritance children or partitions). See
/// <https://www.postgresql.org/docs/current/ddl-inherit.html>.
Comment on lines +311 to +315

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
/// Postgres inheritance-hierarchy modifier for table references.
///
/// Controls whether a query against a table includes rows from descendant
/// tables (inheritance children or partitions). See
/// <https://www.postgresql.org/docs/current/ddl-inherit.html>.
/// Inheritance-hierarchy modifier for table references.
///
/// Controls whether a query against a table includes rows from descendant
/// tables (inheritance children or partitions).
/// [Postgres]: https://www.postgresql.org/docs/current/ddl-inherit.html.

#[derive(Debug, Copy, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub enum InheritanceModifier {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
pub enum InheritanceModifier {
pub enum TableInheritanceModifier {

/// No modifier — default behavior (descendants included).
None,
/// `ONLY` prefix — exclude rows from descendant tables.
Only,
/// Trailing `*` — explicitly include descendant rows. Same effect as
/// `None`, preserved as a distinct variant so round-tripping echoes
/// what the user wrote.
IncludeDescendants,
}

impl fmt::Display for Table {
impl fmt::Display for ExplicitTable {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
if let Some(ref table_name) = self.table_name {
if let Some(ref schema_name) = self.schema_name {
write!(f, "TABLE {}.{}", schema_name, table_name,)?;
} else {
write!(f, "TABLE {}", table_name)?;
}
} else {
write!(f, "TABLE")?;
f.write_str("TABLE ")?;
if self.inheritance == InheritanceModifier::Only {
f.write_str("ONLY ")?;
}
write!(f, "{}", self.name)?;
if self.inheritance == InheritanceModifier::IncludeDescendants {
f.write_str(" *")?;
}
Ok(())
}
Expand Down
5 changes: 5 additions & 0 deletions src/dialect/ansi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,4 +39,9 @@ impl Dialect for AnsiDialect {
fn supports_nested_comments(&self) -> bool {
true
}

/// SQL:2016 `<explicit table>`.
fn supports_table_command(&self) -> bool {
true
}
}
8 changes: 8 additions & 0 deletions src/dialect/generic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -317,6 +317,14 @@ impl Dialect for GenericDialect {
true
}

fn supports_table_command(&self) -> bool {
true
}

fn supports_explicit_table_inheritance_modifiers(&self) -> bool {
true
}

fn supports_aliased_function_args(&self) -> bool {
true
}
Expand Down
23 changes: 23 additions & 0 deletions src/dialect/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1425,6 +1425,29 @@ pub trait Dialect: Debug + Any {
fn supports_array_typedef_with_brackets(&self) -> bool {
false
}

/// Returns true if the dialect supports the `TABLE` command
/// (SQL:2016 `<explicit table>`). See [`ExplicitTable`].
fn supports_table_command(&self) -> bool {
false
}

/// Returns true if the dialect supports Postgres inheritance modifiers
/// (`ONLY` prefix and trailing `*`) on the `TABLE` command.
/// See [`InheritanceModifier`].
fn supports_explicit_table_inheritance_modifiers(&self) -> bool {
false
}

/// Returns the maximum number of dot-separated parts allowed in a
/// table name for the `TABLE` command. For example, `2` means only
/// `schema.table` is accepted; `3` would allow `catalog.schema.table`.
///
/// Returns `None` if the dialect does not restrict the number of parts.
fn table_command_max_name_parts(&self) -> Option<usize> {
None
Comment on lines +1429 to +1448

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should be able to drop these dialect methods, the latter two are a bit too specific I think, and in general since we're introducing an entirely new statement it should be fine to have the parser always accept the statement without it conflicting with other dialects

}

/// Returns true if the dialect supports geometric types.
///
/// Postgres: <https://www.postgresql.org/docs/9.5/functions-geometry.html>
Expand Down
9 changes: 9 additions & 0 deletions src/dialect/mysql.rs
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,15 @@ impl Dialect for MySqlDialect {
true
}

/// See: <https://dev.mysql.com/doc/refman/8.0/en/table.html>
fn supports_table_command(&self) -> bool {
true
}

fn table_command_max_name_parts(&self) -> Option<usize> {
Some(2)
}

fn supports_left_associative_joins_without_parens(&self) -> bool {
false
}
Expand Down
12 changes: 12 additions & 0 deletions src/dialect/postgresql.rs
Original file line number Diff line number Diff line change
Expand Up @@ -343,4 +343,16 @@ impl Dialect for PostgreSqlDialect {
fn supports_comment_optimizer_hint(&self) -> bool {
true
}

fn supports_table_command(&self) -> bool {
true
}

fn supports_explicit_table_inheritance_modifiers(&self) -> bool {
true
}

fn table_command_max_name_parts(&self) -> Option<usize> {
Some(2)
}
}
81 changes: 38 additions & 43 deletions src/parser/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -565,7 +565,7 @@ impl<'a> Parser<'a> {
}

/// Convenience method to parse a string with one or more SQL
/// statements into produce an Abstract Syntax Tree (AST).
/// statements to produce an Abstract Syntax Tree (AST).
///
/// Example
/// ```
Expand Down Expand Up @@ -660,6 +660,10 @@ impl<'a> Parser<'a> {
self.prev_token();
self.parse_query().map(Into::into)
}
Keyword::TABLE if self.dialect.supports_table_command() => {
self.prev_token();
self.parse_query().map(Into::into)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

which statement type is being returned?

}
Keyword::TRUNCATE => self.parse_truncate().map(Into::into),
Keyword::ATTACH => {
if dialect_of!(self is DuckDbDialect) {
Expand Down Expand Up @@ -14975,8 +14979,8 @@ impl<'a> Parser<'a> {
} else if self.parse_keyword(Keyword::VALUE) {
let is_mysql = dialect_of!(self is MySqlDialect);
SetExpr::Values(self.parse_values(is_mysql, true)?)
} else if self.parse_keyword(Keyword::TABLE) {
SetExpr::Table(Box::new(self.parse_as_table()?))
} else if self.dialect.supports_table_command() && self.parse_keyword(Keyword::TABLE) {
SetExpr::Table(Box::new(self.parse_explicit_table()?))
} else {
return self.expected_ref(
"SELECT, VALUES, or a subquery in the query body",
Expand Down Expand Up @@ -15480,49 +15484,40 @@ impl<'a> Parser<'a> {
Ok(clauses)
}

/// Parse `CREATE TABLE x AS TABLE y`
pub fn parse_as_table(&mut self) -> Result<Table, ParserError> {
let token1 = self.next_token();
let token2 = self.next_token();
let token3 = self.next_token();
/// Parse the body of a TABLE query expression.
/// Called after the `TABLE` keyword has been consumed.
pub fn parse_explicit_table(&mut self) -> Result<ExplicitTable, ParserError> {
let allow_inheritance = self.dialect.supports_explicit_table_inheritance_modifiers();

let table_name;
let schema_name;
if token2 == Token::Period {
match token1.token {
Token::Word(w) => {
schema_name = w.value;
}
_ => {
return self.expected("Schema name", token1);
}
}
match token3.token {
Token::Word(w) => {
table_name = w.value;
}
_ => {
return self.expected("Table name", token3);
}
}
Ok(Table {
table_name: Some(table_name),
schema_name: Some(schema_name),
})
} else {
match token1.token {
Token::Word(w) => {
table_name = w.value;
}
_ => {
return self.expected("Table name", token1);
}
let has_only = allow_inheritance && self.parse_keyword(Keyword::ONLY);
let parenthesized = has_only && self.consume_token(&Token::LParen);

let name = self.parse_object_name(true)?;

if let Some(max) = self.dialect.table_command_max_name_parts() {
if name.0.len() > max {
return self.expected_ref(
"a table name (optionally schema-qualified)",
self.peek_token_ref(),
);
}
Ok(Table {
table_name: Some(table_name),
schema_name: None,
})
}

if parenthesized {
self.expect_token(&Token::RParen)?;
}

let has_star = allow_inheritance && !has_only && self.consume_token(&Token::Mul);

let inheritance = if has_only {
InheritanceModifier::Only
} else if has_star {
InheritanceModifier::IncludeDescendants
} else {
InheritanceModifier::None
};

Ok(ExplicitTable { name, inheritance })
}

/// Parse a `SET ROLE` statement. Expects SET to be consumed already.
Expand Down
Loading