Skip to content
Merged
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
4 changes: 2 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@

[package]
name = "pgmold-sqlparser"
description = "Fork of sqlparser with additional PostgreSQL features (PARTITION OF, SECURITY DEFINER/INVOKER, SET params, EXCLUDE, TEXT SEARCH, AGGREGATE, FOREIGN TABLE/FDW, PUBLICATION, SUBSCRIPTION, ALTER DOMAIN/TRIGGER/EXTENSION, CAST, CONVERSION, LANGUAGE, RULE, STATISTICS, ACCESS METHOD, EVENT TRIGGER, TRANSFORM, SECURITY LABEL, USER MAPPING, TABLESPACE, GRANT ON TYPE/DOMAIN, COMMENT ON TRIGGER/AGGREGATE/POLICY, ALTER TYPE OWNER/SCHEMA/ATTRIBUTE, ALTER DEFAULT PRIVILEGES)"
version = "0.62.0"
description = "Fork of sqlparser with additional PostgreSQL features (PARTITION OF, SECURITY DEFINER/INVOKER, SET params, EXCLUDE, TEXT SEARCH, AGGREGATE, FOREIGN TABLE/FDW, PUBLICATION, SUBSCRIPTION, ALTER DOMAIN/TRIGGER/EXTENSION, CAST, CONVERSION, LANGUAGE, RULE, STATISTICS, ACCESS METHOD, EVENT TRIGGER, TRANSFORM, SECURITY LABEL, USER MAPPING, TABLESPACE, GRANT ON TYPE/DOMAIN, COMMENT ON TRIGGER/AGGREGATE/POLICY/CONSTRAINT/OPERATOR/RULE, ALTER TYPE OWNER/SCHEMA/ATTRIBUTE, ALTER DEFAULT PRIVILEGES)"
version = "0.63.0"
authors = ["Filipe Guerreiro <filipe.m.guerreiro@gmail.com>"]
homepage = "https://github.com/fmguerreiro/datafusion-sqlparser-rs"
documentation = "https://docs.rs/pgmold-sqlparser/"
Expand Down
32 changes: 32 additions & 0 deletions changelog/0.63.0.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<!--
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
-->

# pgmold-sqlparser 0.63.0 Changelog

Fork-only release. Covers fork-side changes since 0.62.0; no upstream sync.

**Breaking changes:**

- PostgreSQL: `Statement::Comment` gains two fields, `operator_args: Option<CommentOperatorArgs>` (operand types for `COMMENT ON OPERATOR`, each side `Option<DataType>` to model `NONE` for unary operators) and `on_domain: bool` (set when `COMMENT ON CONSTRAINT … ON DOMAIN <domain>` is parsed). Existing destructures must be updated.

**Other:**

- PostgreSQL: `COMMENT ON CONSTRAINT name ON [DOMAIN] target IS …` is now parsed into `CommentObject::Constraint` with `table_name` carrying the relation/domain and `on_domain` distinguishing the two forms.
- PostgreSQL: `COMMENT ON OPERATOR name (left, right) IS …` is now parsed into `CommentObject::Operator` with `operator_args` carrying the operand signature; `NONE` is preserved as `None` for prefix/postfix unary operators.
- PostgreSQL: `COMMENT ON RULE name ON target IS …` is now parsed into `CommentObject::Rule` with `table_name` carrying the target relation.
69 changes: 65 additions & 4 deletions src/ast/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2467,6 +2467,8 @@ pub enum CommentObject {
Collation,
/// A table column.
Column,
/// A table or domain constraint.
Constraint,
/// A database.
Database,
/// A domain.
Expand All @@ -2479,12 +2481,16 @@ pub enum CommentObject {
Index,
/// A materialized view.
MaterializedView,
/// A user-defined operator.
Operator,
/// A row-level security policy.
Policy,
/// A procedure.
Procedure,
/// A role.
Role,
/// A query rewrite rule.
Rule,
/// A schema.
Schema,
/// A sequence.
Expand All @@ -2507,15 +2513,18 @@ impl CommentObject {
CommentObject::Aggregate => "AGGREGATE",
CommentObject::Collation => "COLLATION",
CommentObject::Column => "COLUMN",
CommentObject::Constraint => "CONSTRAINT",
CommentObject::Database => "DATABASE",
CommentObject::Domain => "DOMAIN",
CommentObject::Extension => "EXTENSION",
CommentObject::Function => "FUNCTION",
CommentObject::Index => "INDEX",
CommentObject::MaterializedView => "MATERIALIZED VIEW",
CommentObject::Operator => "OPERATOR",
CommentObject::Policy => "POLICY",
CommentObject::Procedure => "PROCEDURE",
CommentObject::Role => "ROLE",
CommentObject::Rule => "RULE",
CommentObject::Schema => "SCHEMA",
CommentObject::Sequence => "SEQUENCE",
CommentObject::Table => "TABLE",
Expand All @@ -2535,14 +2544,17 @@ impl CommentObject {
Keyword::AGGREGATE => CommentObject::Aggregate,
Keyword::COLLATION => CommentObject::Collation,
Keyword::COLUMN => CommentObject::Column,
Keyword::CONSTRAINT => CommentObject::Constraint,
Keyword::DATABASE => CommentObject::Database,
Keyword::DOMAIN => CommentObject::Domain,
Keyword::EXTENSION => CommentObject::Extension,
Keyword::FUNCTION => CommentObject::Function,
Keyword::INDEX => CommentObject::Index,
Keyword::OPERATOR => CommentObject::Operator,
Keyword::POLICY => CommentObject::Policy,
Keyword::PROCEDURE => CommentObject::Procedure,
Keyword::ROLE => CommentObject::Role,
Keyword::RULE => CommentObject::Rule,
Keyword::SCHEMA => CommentObject::Schema,
Keyword::SEQUENCE => CommentObject::Sequence,
Keyword::TABLE => CommentObject::Table,
Expand All @@ -2561,6 +2573,38 @@ impl fmt::Display for CommentObject {
}
}

/// Operand types for `COMMENT ON OPERATOR name (left, right)`.
///
/// Each side may be `NONE` for prefix or postfix unary operators, mirroring
/// the syntax allowed by `DROP OPERATOR` / `ALTER OPERATOR`. Both sides are
/// optional independently because PostgreSQL accepts unary operators in
/// either direction.
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub struct CommentOperatorArgs {
/// Left-hand operand type, or `None` for `NONE` (prefix operator).
pub left: Option<DataType>,
/// Right-hand operand type, or `None` for `NONE` (postfix operator).
pub right: Option<DataType>,
}

impl fmt::Display for CommentOperatorArgs {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let write_side = |opt: &Option<DataType>, f: &mut fmt::Formatter| -> fmt::Result {
match opt {
Some(dt) => write!(f, "{dt}"),
None => f.write_str("NONE"),
}
};
f.write_str("(")?;
write_side(&self.left, f)?;
f.write_str(", ")?;
write_side(&self.right, f)?;
f.write_str(")")
}
}

#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
Expand Down Expand Up @@ -4461,10 +4505,21 @@ pub enum Statement {
/// while `None` means no parameter list was provided. Used for
/// `FUNCTION`, `PROCEDURE`, and `AGGREGATE` targets.
arguments: Option<Vec<DataType>>,
/// Partner table for objects scoped to a table, i.e. the
/// `ON <table>` tail in `COMMENT ON TRIGGER t ON tbl IS '…'` or
/// `COMMENT ON POLICY p ON tbl IS '…'`.
/// Operand signature for `COMMENT ON OPERATOR name (left, right)`.
/// Always `Some(_)` for `Operator`, `None` for every other variant.
/// Modeled separately from `arguments` because operator slots may be
/// `NONE` (unary operators), which `Vec<DataType>` cannot express.
operator_args: Option<CommentOperatorArgs>,
/// Partner relation for objects scoped to a relation, i.e. the
/// `ON <table>` (or `ON DOMAIN <domain>`) tail in
/// `COMMENT ON TRIGGER t ON tbl IS '…'`,
/// `COMMENT ON POLICY p ON tbl IS '…'`,
/// `COMMENT ON RULE r ON tbl IS '…'`, or
/// `COMMENT ON CONSTRAINT c ON [DOMAIN] tbl IS '…'`.
table_name: Option<ObjectName>,
/// `true` when the relation tail used the `ON DOMAIN <domain>` form.
/// Only meaningful for `Constraint`; always `false` otherwise.
on_domain: bool,
/// Optional comment text (None to remove comment).
comment: Option<String>,
/// An optional `IF EXISTS` clause. (Non-standard.)
Expand Down Expand Up @@ -6252,7 +6307,9 @@ impl fmt::Display for Statement {
object_type,
object_name,
arguments,
operator_args,
table_name,
on_domain,
comment,
if_exists,
} => {
Expand All @@ -6264,8 +6321,12 @@ impl fmt::Display for Statement {
if let Some(args) = arguments {
write!(f, "({})", display_comma_separated(args))?;
}
if let Some(operator_args) = operator_args {
write!(f, "{operator_args}")?;
}
if let Some(table_name) = table_name {
write!(f, " ON {table_name}")?;
let prefix = if *on_domain { " ON DOMAIN " } else { " ON " };
write!(f, "{prefix}{table_name}")?;
}
write!(f, " IS ")?;
if let Some(c) = comment {
Expand Down
54 changes: 38 additions & 16 deletions src/parser/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -917,7 +917,11 @@ impl<'a> Parser<'a> {
},
None => return self.expected("comment object_type", token),
};
let object_name = self.parse_object_name(false)?;
let object_name = if object_type == CommentObject::Operator {
self.parse_operator_name()?
} else {
self.parse_object_name(false)?
};

let arguments = match object_type {
CommentObject::Function | CommentObject::Procedure | CommentObject::Aggregate => {
Expand All @@ -939,12 +943,28 @@ impl<'a> Parser<'a> {
));
}

let table_name = match object_type {
CommentObject::Trigger | CommentObject::Policy => {
let operator_args = if object_type == CommentObject::Operator {
self.expect_token(&Token::LParen)?;
let left = self.parse_operator_arg_type_or_none()?;
self.expect_token(&Token::Comma)?;
let right = self.parse_operator_arg_type_or_none()?;
self.expect_token(&Token::RParen)?;
Some(CommentOperatorArgs { left, right })
} else {
None
};

let (table_name, on_domain) = match object_type {
CommentObject::Trigger | CommentObject::Policy | CommentObject::Rule => {
self.expect_keyword_is(Keyword::ON)?;
Some(self.parse_object_name(false)?)
(Some(self.parse_object_name(false)?), false)
}
_ => None,
CommentObject::Constraint => {
self.expect_keyword_is(Keyword::ON)?;
let on_domain = self.parse_keyword(Keyword::DOMAIN);
(Some(self.parse_object_name(false)?), on_domain)
}
_ => (None, false),
};

self.expect_keyword_is(Keyword::IS)?;
Expand All @@ -957,7 +977,9 @@ impl<'a> Parser<'a> {
object_type,
object_name,
arguments,
operator_args,
table_name,
on_domain,
comment,
if_exists,
})
Expand Down Expand Up @@ -8532,19 +8554,9 @@ impl<'a> Parser<'a> {
fn parse_drop_operator_signature(&mut self) -> Result<DropOperatorSignature, ParserError> {
let name = self.parse_operator_name()?;
self.expect_token(&Token::LParen)?;

// Parse left operand type (or NONE for prefix operators)
let left_type = if self.parse_keyword(Keyword::NONE) {
None
} else {
Some(self.parse_data_type()?)
};

let left_type = self.parse_operator_arg_type_or_none()?;
self.expect_token(&Token::Comma)?;

// Parse right operand type (always required)
let right_type = self.parse_data_type()?;

self.expect_token(&Token::RParen)?;

Ok(DropOperatorSignature {
Expand All @@ -8554,6 +8566,16 @@ impl<'a> Parser<'a> {
})
}

/// Parse one slot of a PostgreSQL operator signature: a `DataType` or the
/// keyword `NONE`. Used by `DROP OPERATOR` and `COMMENT ON OPERATOR`.
fn parse_operator_arg_type_or_none(&mut self) -> Result<Option<DataType>, ParserError> {
if self.parse_keyword(Keyword::NONE) {
Ok(None)
} else {
Ok(Some(self.parse_data_type()?))
}
}

/// Parse a [Statement::DropOperatorFamily]
///
/// [PostgreSQL Documentation](https://www.postgresql.org/docs/current/sql-dropopfamily.html)
Expand Down
3 changes: 3 additions & 0 deletions tests/sqlparser_common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15368,6 +15368,9 @@ fn parse_comments() {
}

// https://www.postgresql.org/docs/current/sql-comment.html
// CONSTRAINT, OPERATOR, RULE require object-specific tails handled in
// their own tests; this table only covers `COMMENT ON <KEYWORD> name IS '…'`
// shapes that share the simple object_name + comment structure.
let object_types = [
("COLLATION", CommentObject::Collation),
("COLUMN", CommentObject::Column),
Expand Down
Loading
Loading