You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@calcite.apache.org by dm...@apache.org on 2023/03/23 07:56:00 UTC

[calcite] branch main updated: [CALCITE-5403] Babel parser should parse PostgreSQL's SET, RESET, BEGIN, SHOW, ROLLBACK, COMMIT commands

This is an automated email from the ASF dual-hosted git repository.

dmsysolyatin pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/calcite.git


The following commit(s) were added to refs/heads/main by this push:
     new 62024984f0 [CALCITE-5403] Babel parser should parse PostgreSQL's SET, RESET, BEGIN, SHOW, ROLLBACK, COMMIT commands
62024984f0 is described below

commit 62024984f0d4058c57899ac3d0cac674fac6fb18
Author: dssysolyatin <dm...@gmail.com>
AuthorDate: Mon Nov 28 20:06:59 2022 +0200

    [CALCITE-5403] Babel parser should parse PostgreSQL's SET, RESET, BEGIN, SHOW, ROLLBACK, COMMIT commands
---
 babel/src/main/codegen/config.fmpp                 |  26 ++
 .../codegen/includes/parserPostgresqlImpls.ftl     | 264 +++++++++++++++++++++
 .../calcite/sql/babel/postgresql/AndChain.java     |  36 +++
 .../calcite/sql/babel/postgresql/SqlBegin.java     |  89 +++++++
 .../calcite/sql/babel/postgresql/SqlCommit.java    |  71 ++++++
 .../calcite/sql/babel/postgresql/SqlDiscard.java   |  70 ++++++
 .../calcite/sql/babel/postgresql/SqlRollback.java  |  71 ++++++
 .../calcite/sql/babel/postgresql/SqlShow.java      |  74 ++++++
 .../calcite/sql/babel/postgresql/package-info.java |  21 ++
 .../org/apache/calcite/test/BabelParserTest.java   |  80 +++++++
 10 files changed, 802 insertions(+)

diff --git a/babel/src/main/codegen/config.fmpp b/babel/src/main/codegen/config.fmpp
index eb0d5f1ef6..a61dee55e2 100644
--- a/babel/src/main/codegen/config.fmpp
+++ b/babel/src/main/codegen/config.fmpp
@@ -30,14 +30,25 @@ data: {
       "org.apache.calcite.sql.SqlCreate",
       "org.apache.calcite.sql.babel.SqlBabelCreateTable",
       "org.apache.calcite.sql.babel.TableCollectionType",
+      "org.apache.calcite.sql.babel.postgresql.AndChain",
+      "org.apache.calcite.sql.babel.postgresql.SqlBegin",
+      "org.apache.calcite.sql.babel.postgresql.SqlCommit",
+      "org.apache.calcite.sql.babel.postgresql.SqlDiscard",
+      "org.apache.calcite.sql.babel.postgresql.SqlRollback",
+      "org.apache.calcite.sql.babel.postgresql.SqlShow",
       "org.apache.calcite.sql.ddl.SqlDdlNodes",
     ]
 
     # List of new keywords. Example: "DATABASES", "TABLES". If the keyword is
     # not a reserved keyword, add it to the 'nonReservedKeywords' section.
     keywords: [
+      "DISCARD"
       "IF"
+      "PLANS"
+      "SEED"
       "SEMI"
+      "SEQUENCES"
+      "TEMP"
       "VOLATILE"
     ]
 
@@ -45,8 +56,13 @@ data: {
     # items in this list become non-reserved
     nonReservedKeywordsToAdd: [
       # not in core, added in babel
+      "DISCARD"
       "IF"
+      "PLANS"
+      "SEED"
       "SEMI"
+      "SEQUENCES"
+      "TEMP"
 
       # The following keywords are reserved in core Calcite,
       # are reserved in some version of SQL,
@@ -544,6 +560,15 @@ data: {
     createStatementParserMethods: [
       "SqlCreateTable"
     ]
+    
+    statementParserMethods: [
+      "PostgresqlSqlShow()",
+      "PostgresqlSqlSetOption()",
+      "PostgresqlSqlBegin()",
+      "PostgresqlSqlDiscard()",
+      "PostgresqlSqlCommit()",
+      "PostgresqlSqlRollback()"
+    ]
 
     # Binary operators tokens.
     # Example: "< INFIX_CAST: \"::\" >".
@@ -566,6 +591,7 @@ data: {
     # Example: "parserImpls.ftl".
     implementationFiles: [
       "parserImpls.ftl"
+      "parserPostgresqlImpls.ftl"
     ]
 
     includePosixOperators: true
diff --git a/babel/src/main/codegen/includes/parserPostgresqlImpls.ftl b/babel/src/main/codegen/includes/parserPostgresqlImpls.ftl
new file mode 100644
index 0000000000..fee70a58d7
--- /dev/null
+++ b/babel/src/main/codegen/includes/parserPostgresqlImpls.ftl
@@ -0,0 +1,264 @@
+<#--
+// 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.
+-->
+
+/** SHOW (<TRANSACTION ISOLATION LEVEL> | name) */
+SqlNode PostgresqlSqlShow() :
+{
+    final SqlIdentifier parameter;
+    final Span s;
+}
+{
+    <SHOW> { s = span(); }
+  (
+      parameter = PostgresqlTransactionIsolationLevel()
+|
+      <IDENTIFIER> {
+          parameter = new SqlIdentifier(token.image.toLowerCase(Locale.ROOT), getPos());
+      }
+  ) { return SqlShow.OPERATOR.createCall(null, s.end(this), parameter); }
+}
+
+SqlIdentifier PostgresqlTransactionIsolationLevel() :
+{
+    final Span s;
+}
+{
+  { s = span(); } <TRANSACTION> <ISOLATION> <LEVEL> {
+    return new SqlIdentifier("transaction_isolation", s.end(this));
+  }
+}
+
+/**
+ * SET [ SESSION | LOCAL ] configuration_parameter { TO | = } { value | 'value' | DEFAULT }
+ * SET [ SESSION | LOCAL ] TIME ZONE { value | 'value' | LOCAL | DEFAULT }
+ * SET [ SESSION | LOCAL ] (SCHEMA | NAMES | SEED) value
+ * value - Values can be specified as string constants, identifiers, numbers, or comma-separated lists of these
+ */
+SqlNode PostgresqlSqlSetOption() :
+{
+    SqlIdentifier name;
+    final SqlNode val;
+    String scope = null;
+    Span s;
+}
+{
+    { s = span(); }
+    (
+        <SET> {
+            s.add(this);
+        }
+        [ scope = PostgresqlOptionScope() ]
+        (
+          <TIME> <ZONE> { name = new SqlIdentifier("timezone", getPos()); }
+          (
+            val = Default()
+          |
+            <LOCAL> { val = SqlLiteral.createCharString("LOCAL", getPos()); }
+          |
+            val = Literal()
+          )
+        |
+          (
+              <SCHEMA> { name = new SqlIdentifier("search_path", getPos()); }
+            |
+              <NAMES> { name = new SqlIdentifier("client_encoding", getPos()); }
+            |
+              <SEED> { name = new SqlIdentifier("seed", getPos()); }
+            |
+              name = CompoundIdentifier()
+              ( <EQ> | <TO> )
+          )
+          val = PostgresqlSqlOptionValues()
+        ){
+          return new SqlSetOption(s.end(val), scope, name, val);
+        }
+
+    |
+        <RESET> {
+            s.add(this);
+        }
+        (
+            name = CompoundIdentifier()
+        |
+            <ALL> {
+                name = new SqlIdentifier(token.image.toUpperCase(Locale.ROOT),
+                    getPos());
+            }
+        )
+        {
+            return new SqlSetOption(s.end(name), scope, name, null);
+        }
+    )
+}
+
+String PostgresqlOptionScope() :
+{
+}
+{
+    ( <LOCAL> | <SESSION> ) { return token.image.toUpperCase(Locale.ROOT); }
+}
+
+SqlNode PostgresqlSqlOptionValues():
+{
+  final List<SqlNode> list;
+  Span s;
+  SqlNode e;
+}
+{
+   e = PostgresqlSqlOptionValue() { s = span(); list = startList(e); }
+   ( <COMMA> e = PostgresqlSqlOptionValue() { list.add(e); } )*
+   { 
+      return list.size() > 1 ? new SqlNodeList(list, s.end(this)) : e; 
+   }
+}
+
+SqlNode PostgresqlSqlOptionValue():
+{
+  final SqlNode val;
+}
+{
+  (
+      val = Default()
+  |
+      val = Literal()
+  |
+      val = SimpleIdentifier()
+  ) {
+    return val;
+  }
+}
+
+/** DISCARD { ALL | PLANS | SEQUENCES | TEMPORARY | TEMP } */
+SqlNode PostgresqlSqlDiscard() :
+{
+  final Span s;
+}
+{
+  { s = span(); }
+   <DISCARD> ( <ALL> | <PLANS> | <SEQUENCES> | <TEMPORARY> | <TEMP> ) {
+      return SqlDiscard.OPERATOR.createCall(s.end(this), new SqlIdentifier(
+         token.image.toUpperCase(Locale.ROOT), getPos()));
+   }
+}
+
+/**
+ * BEGIN [ WORK | TRANSACTION ] [ transaction_mode [, ...] ]
+ * where transaction_mode is one of:
+ * ISOLATION LEVEL { SERIALIZABLE | REPEATABLE READ | READ COMMITTED | READ UNCOMMITTED }
+ * READ WRITE | READ ONLY
+ * [ NOT ] DEFERRABLE
+ */
+SqlNode PostgresqlSqlBegin() :
+{
+  final Span s;
+  SqlNodeList transactionModeList = SqlNodeList.EMPTY;
+}
+{
+  { s = span(); }
+  <BEGIN> [ ( <WORK> | <TRANSACTION> ) ] [transactionModeList = PostgresqlSqlBeginTransactionModeList()] {
+    return SqlBegin.OPERATOR.createCall(s.end(this), (SqlNode) transactionModeList);
+  }
+}
+
+SqlNodeList PostgresqlSqlBeginTransactionModeList():
+{
+  final List<SqlNode> list;
+  Span s;
+  SqlNode e;
+}
+{
+  { s = span(); }
+  e = PostgresqlSqlBeginTransactionMode() { s = span(); list = startList(e); }
+  ( <COMMA> e = PostgresqlSqlBeginTransactionMode() { list.add(e); } )*
+  { return new SqlNodeList(list, s.end(this)); }
+}
+
+SqlNode PostgresqlSqlBeginTransactionMode():
+{
+  final Span s;
+  SqlBegin.TransactionMode m;
+}
+{
+    { s = span(); }
+(   
+    LOOKAHEAD(2)
+    <READ> 
+    (
+      <WRITE> { m = SqlBegin.TransactionMode.READ_WRITE; } 
+    | 
+      <ONLY>  { m = SqlBegin.TransactionMode.READ_ONLY; }
+    )
+|
+    <DEFERRABLE> { m = SqlBegin.TransactionMode.DEFERRABLE; }
+|
+    <NOT> <DEFERRABLE> { m = SqlBegin.TransactionMode.NOT_DEFERRABLE; }
+|
+    <ISOLATION> <LEVEL> 
+    (
+        <SERIALIZABLE> { m = SqlBegin.TransactionMode.ISOLATION_LEVEL_SERIALIZABLE; }
+    | 
+        <REPEATABLE> <READ> { m = SqlBegin.TransactionMode.ISOLATION_LEVEL_REPEATABLE_READ; }
+    | 
+        <READ> 
+        ( 
+          <COMMITTED> { m = SqlBegin.TransactionMode.ISOLATION_LEVEL_READ_COMMITTED; } 
+        | 
+          <UNCOMMITTED>  { m = SqlBegin.TransactionMode.ISOLATION_LEVEL_READ_UNCOMMITTED; }
+        ) 
+    )
+) {
+    return m.symbol(s.end(this));
+  }
+}
+
+/** COMMIT [ WORK | TRANSACTION ] [ AND [ NO ] CHAIN ] */
+SqlNode PostgresqlSqlCommit():
+{
+  final Span s;
+  AndChain chain = AndChain.AND_NO_CHAIN;
+}
+{
+  { s = span(); }
+  <COMMIT> [ <WORK> | <TRANSACTION> ] [ 
+  (
+    <AND> <CHAIN> { chain = AndChain.AND_CHAIN; }
+  |
+    <AND> <NO> <CHAIN>
+  )] {
+    final SqlParserPos pos = s.end(this); 
+    return SqlCommit.OPERATOR.createCall(pos, chain.symbol(pos));
+  }
+}
+
+/** ROLLBACK [ WORK | TRANSACTION ] [ AND [ NO ] CHAIN ] */
+SqlNode PostgresqlSqlRollback():
+{
+  final Span s;
+  AndChain chain = AndChain.AND_NO_CHAIN;
+}
+{
+  { s = span(); }
+  <ROLLBACK> [ <WORK> | <TRANSACTION> ] [ 
+  (
+    <AND> <CHAIN> { chain = AndChain.AND_CHAIN; }
+  |
+    <AND> <NO> <CHAIN>
+  )] {
+    final SqlParserPos pos = s.end(this); 
+    return SqlRollback.OPERATOR.createCall(pos, chain.symbol(pos));
+  }
+}
diff --git a/babel/src/main/java/org/apache/calcite/sql/babel/postgresql/AndChain.java b/babel/src/main/java/org/apache/calcite/sql/babel/postgresql/AndChain.java
new file mode 100644
index 0000000000..f9129b5a04
--- /dev/null
+++ b/babel/src/main/java/org/apache/calcite/sql/babel/postgresql/AndChain.java
@@ -0,0 +1,36 @@
+/*
+ * 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.
+ */
+package org.apache.calcite.sql.babel.postgresql;
+
+import org.apache.calcite.sql.Symbolizable;
+
+/**
+ * Defines the keywords that can occur immediately after the "ROLLBACK" or "COMMIT" keywords.
+ *
+ * @see SqlCommit
+ * @see SqlRollback
+ * @see <a href="https://www.postgresql.org/docs/current/sql-commit.html">COMMIT specification</a>
+ * @see <a href="https://www.postgresql.org/docs/current/sql-rollback.html">ROLLBACK specification</a>
+ */
+public enum AndChain implements Symbolizable {
+  AND_CHAIN,
+  AND_NO_CHAIN;
+
+  @Override public String toString() {
+    return super.toString().replace("_", " ");
+  }
+}
diff --git a/babel/src/main/java/org/apache/calcite/sql/babel/postgresql/SqlBegin.java b/babel/src/main/java/org/apache/calcite/sql/babel/postgresql/SqlBegin.java
new file mode 100644
index 0000000000..bfd275c3ef
--- /dev/null
+++ b/babel/src/main/java/org/apache/calcite/sql/babel/postgresql/SqlBegin.java
@@ -0,0 +1,89 @@
+/*
+ * 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.
+ */
+package org.apache.calcite.sql.babel.postgresql;
+
+import org.apache.calcite.sql.SqlCall;
+import org.apache.calcite.sql.SqlKind;
+import org.apache.calcite.sql.SqlLiteral;
+import org.apache.calcite.sql.SqlNode;
+import org.apache.calcite.sql.SqlNodeList;
+import org.apache.calcite.sql.SqlOperator;
+import org.apache.calcite.sql.SqlSpecialOperator;
+import org.apache.calcite.sql.SqlWriter;
+import org.apache.calcite.sql.Symbolizable;
+import org.apache.calcite.sql.parser.SqlParserPos;
+import org.apache.calcite.sql.type.ReturnTypes;
+
+import com.google.common.collect.ImmutableList;
+
+import org.checkerframework.checker.nullness.qual.Nullable;
+
+import java.util.List;
+
+/**
+ * Parse tree node representing a {@code BEGIN} clause.
+ * @see <a href="https://www.postgresql.org/docs/current/sql-begin.html">BEGIN specification</a>
+ */
+public class SqlBegin extends SqlCall {
+  public static final SqlSpecialOperator OPERATOR =
+      new SqlSpecialOperator("BEGIN", SqlKind.OTHER_FUNCTION, 32, false, ReturnTypes.BOOLEAN, null,
+          null) {
+        @Override public SqlCall createCall(@Nullable final SqlLiteral functionQualifier,
+            final SqlParserPos pos,
+            final @Nullable SqlNode... operands) {
+          return new SqlBegin(pos, (SqlNodeList) operands[0]);
+        }
+      };
+
+  private final SqlNodeList transactionModeList;
+
+  protected SqlBegin(final SqlParserPos pos, final SqlNodeList transactionModeList) {
+    super(pos);
+    this.transactionModeList = transactionModeList;
+  }
+
+  @Override public SqlOperator getOperator() {
+    return OPERATOR;
+  }
+
+  @Override public List<SqlNode> getOperandList() {
+    return ImmutableList.of(transactionModeList);
+  }
+
+  @Override public void unparse(final SqlWriter writer, final int leftPrec, final int rightPrec) {
+    writer.keyword("BEGIN");
+    transactionModeList.unparse(writer, -1, -1);
+  }
+
+  /**
+   * Transaction mode.
+   */
+  public enum TransactionMode implements Symbolizable {
+    READ_WRITE,
+    READ_ONLY,
+    DEFERRABLE,
+    NOT_DEFERRABLE,
+    ISOLATION_LEVEL_SERIALIZABLE,
+    ISOLATION_LEVEL_REPEATABLE_READ,
+    ISOLATION_LEVEL_READ_COMMITTED,
+    ISOLATION_LEVEL_READ_UNCOMMITTED;
+
+    @Override public String toString() {
+      return super.toString().replace("_", " ");
+    }
+  }
+}
diff --git a/babel/src/main/java/org/apache/calcite/sql/babel/postgresql/SqlCommit.java b/babel/src/main/java/org/apache/calcite/sql/babel/postgresql/SqlCommit.java
new file mode 100644
index 0000000000..8b9491803f
--- /dev/null
+++ b/babel/src/main/java/org/apache/calcite/sql/babel/postgresql/SqlCommit.java
@@ -0,0 +1,71 @@
+/*
+ * 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.
+ */
+package org.apache.calcite.sql.babel.postgresql;
+
+import org.apache.calcite.sql.SqlCall;
+import org.apache.calcite.sql.SqlKind;
+import org.apache.calcite.sql.SqlLiteral;
+import org.apache.calcite.sql.SqlNode;
+import org.apache.calcite.sql.SqlOperator;
+import org.apache.calcite.sql.SqlSpecialOperator;
+import org.apache.calcite.sql.SqlWriter;
+import org.apache.calcite.sql.parser.SqlParserPos;
+import org.apache.calcite.sql.type.ReturnTypes;
+
+import com.google.common.collect.ImmutableList;
+
+import org.checkerframework.checker.nullness.qual.Nullable;
+
+import java.util.List;
+
+/**
+ * Parse tree node representing a {@code COMMIT} clause.
+ * @see <a href="https://www.postgresql.org/docs/current/sql-commit.html">COMMIT specification</a>
+ */
+public class SqlCommit extends SqlCall {
+
+  public static final SqlSpecialOperator OPERATOR =
+      new SqlSpecialOperator("COMMIT", SqlKind.OTHER_FUNCTION, 32, false, ReturnTypes.BOOLEAN, null,
+          null) {
+        @Override public SqlCall createCall(@Nullable final SqlLiteral functionQualifier,
+            final SqlParserPos pos,
+            final @Nullable SqlNode... operands) {
+          return new SqlCommit(pos, (SqlLiteral) operands[0]);
+        }
+      };
+  private final SqlLiteral chain;
+
+  protected SqlCommit(final SqlParserPos pos, final SqlLiteral chain) {
+    super(pos);
+    this.chain = chain;
+  }
+
+  @Override public SqlOperator getOperator() {
+    return OPERATOR;
+  }
+
+  @Override public List<SqlNode> getOperandList() {
+    return ImmutableList.of(this.chain);
+  }
+
+  @Override public void unparse(final SqlWriter writer, final int leftPrec, final int rightPrec) {
+    writer.keyword("COMMIT");
+    if (this.chain.symbolValue(AndChain.class) == AndChain.AND_CHAIN) {
+      writer.literal("AND CHAIN");
+    }
+  }
+}
diff --git a/babel/src/main/java/org/apache/calcite/sql/babel/postgresql/SqlDiscard.java b/babel/src/main/java/org/apache/calcite/sql/babel/postgresql/SqlDiscard.java
new file mode 100644
index 0000000000..78adbd8574
--- /dev/null
+++ b/babel/src/main/java/org/apache/calcite/sql/babel/postgresql/SqlDiscard.java
@@ -0,0 +1,70 @@
+/*
+ * 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.
+ */
+package org.apache.calcite.sql.babel.postgresql;
+
+import org.apache.calcite.sql.SqlCall;
+import org.apache.calcite.sql.SqlIdentifier;
+import org.apache.calcite.sql.SqlKind;
+import org.apache.calcite.sql.SqlLiteral;
+import org.apache.calcite.sql.SqlNode;
+import org.apache.calcite.sql.SqlOperator;
+import org.apache.calcite.sql.SqlSpecialOperator;
+import org.apache.calcite.sql.SqlWriter;
+import org.apache.calcite.sql.parser.SqlParserPos;
+import org.apache.calcite.sql.type.ReturnTypes;
+
+import com.google.common.collect.ImmutableList;
+
+import org.checkerframework.checker.nullness.qual.Nullable;
+
+import java.util.List;
+
+/**
+ * Parse tree node representing a {@code DISCARD} clause.
+ * @see <a href="https://www.postgresql.org/docs/current/sql-discard.html">DISCARD specification</a>
+ */
+public class SqlDiscard extends SqlCall {
+  public static final SqlSpecialOperator OPERATOR =
+      new SqlSpecialOperator("DISCARD", SqlKind.OTHER_FUNCTION, 32, false, ReturnTypes.BOOLEAN,
+          null, null) {
+        @Override public SqlCall createCall(@Nullable final SqlLiteral functionQualifier,
+            final SqlParserPos pos,
+            final @Nullable SqlNode... operands) {
+          return new SqlDiscard(pos, (SqlIdentifier) operands[0]);
+        }
+      };
+
+  private final SqlIdentifier subcommand;
+
+  public SqlDiscard(final SqlParserPos pos, final SqlIdentifier subcommand) {
+    super(pos);
+    this.subcommand = subcommand;
+  }
+
+  @Override public SqlOperator getOperator() {
+    return OPERATOR;
+  }
+
+  @Override public List<SqlNode> getOperandList() {
+    return ImmutableList.of(subcommand);
+  }
+
+  @Override public void unparse(final SqlWriter writer, final int leftPrec, final int rightPrec) {
+    writer.keyword("DISCARD");
+    writer.literal(subcommand.toString());
+  }
+}
diff --git a/babel/src/main/java/org/apache/calcite/sql/babel/postgresql/SqlRollback.java b/babel/src/main/java/org/apache/calcite/sql/babel/postgresql/SqlRollback.java
new file mode 100644
index 0000000000..c400f0c843
--- /dev/null
+++ b/babel/src/main/java/org/apache/calcite/sql/babel/postgresql/SqlRollback.java
@@ -0,0 +1,71 @@
+/*
+ * 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.
+ */
+package org.apache.calcite.sql.babel.postgresql;
+
+import org.apache.calcite.sql.SqlCall;
+import org.apache.calcite.sql.SqlKind;
+import org.apache.calcite.sql.SqlLiteral;
+import org.apache.calcite.sql.SqlNode;
+import org.apache.calcite.sql.SqlOperator;
+import org.apache.calcite.sql.SqlSpecialOperator;
+import org.apache.calcite.sql.SqlWriter;
+import org.apache.calcite.sql.parser.SqlParserPos;
+import org.apache.calcite.sql.type.ReturnTypes;
+
+import com.google.common.collect.ImmutableList;
+
+import org.checkerframework.checker.nullness.qual.Nullable;
+
+import java.util.List;
+
+/**
+ * Parse tree node representing a {@code ROLLBACK} clause.
+ * @see <a href="https://www.postgresql.org/docs/current/sql-rollback.html">ROLLBACK specification</a>
+ */
+public class SqlRollback extends SqlCall {
+
+  public static final SqlSpecialOperator OPERATOR =
+      new SqlSpecialOperator("ROLLBACK", SqlKind.OTHER_FUNCTION, 32, false, ReturnTypes.BOOLEAN,
+          null, null) {
+        @Override public SqlCall createCall(@Nullable final SqlLiteral functionQualifier,
+            final SqlParserPos pos,
+            final @Nullable SqlNode... operands) {
+          return new SqlRollback(pos, (SqlLiteral) operands[0]);
+        }
+      };
+  private final SqlLiteral chain;
+
+  protected SqlRollback(final SqlParserPos pos, final SqlLiteral chain) {
+    super(pos);
+    this.chain = chain;
+  }
+
+  @Override public SqlOperator getOperator() {
+    return OPERATOR;
+  }
+
+  @Override public List<SqlNode> getOperandList() {
+    return ImmutableList.of(this.chain);
+  }
+
+  @Override public void unparse(final SqlWriter writer, final int leftPrec, final int rightPrec) {
+    writer.keyword("ROLLBACK");
+    if (this.chain.symbolValue(AndChain.class) == AndChain.AND_CHAIN) {
+      writer.literal("AND CHAIN");
+    }
+  }
+}
diff --git a/babel/src/main/java/org/apache/calcite/sql/babel/postgresql/SqlShow.java b/babel/src/main/java/org/apache/calcite/sql/babel/postgresql/SqlShow.java
new file mode 100644
index 0000000000..6e81b01cb2
--- /dev/null
+++ b/babel/src/main/java/org/apache/calcite/sql/babel/postgresql/SqlShow.java
@@ -0,0 +1,74 @@
+/*
+ * 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.
+ */
+package org.apache.calcite.sql.babel.postgresql;
+
+import org.apache.calcite.sql.SqlCall;
+import org.apache.calcite.sql.SqlIdentifier;
+import org.apache.calcite.sql.SqlKind;
+import org.apache.calcite.sql.SqlLiteral;
+import org.apache.calcite.sql.SqlNode;
+import org.apache.calcite.sql.SqlOperator;
+import org.apache.calcite.sql.SqlSpecialOperator;
+import org.apache.calcite.sql.SqlWriter;
+import org.apache.calcite.sql.parser.SqlParserPos;
+import org.apache.calcite.sql.type.ReturnTypes;
+
+import com.google.common.collect.ImmutableList;
+
+import org.checkerframework.checker.nullness.qual.Nullable;
+
+import java.util.List;
+
+/**
+ * Parse tree node representing a {@code SHOW} clause.
+ * @see <a href="https://www.postgresql.org/docs/current/sql-show.html">SHOW specification</a>
+ */
+public class SqlShow extends SqlCall {
+  public static final SqlSpecialOperator OPERATOR =
+      new SqlSpecialOperator("SHOW", SqlKind.OTHER_FUNCTION, 32, false, ReturnTypes.VARCHAR_2000,
+          null, null) {
+    @Override public SqlCall createCall(@Nullable final SqlLiteral functionQualifier,
+        final SqlParserPos pos,
+        final @Nullable SqlNode... operands) {
+      return new SqlShow(pos, (SqlIdentifier) operands[0]);
+    }
+  };
+
+  private final SqlIdentifier name;
+
+  protected SqlShow(final SqlParserPos pos, SqlIdentifier name) {
+    super(pos);
+    this.name = name;
+  }
+
+  @Override public SqlOperator getOperator() {
+    return OPERATOR;
+  }
+
+  @Override public List<SqlNode> getOperandList() {
+    return ImmutableList.of(name);
+  }
+
+  @Override public void unparse(final SqlWriter writer, final int leftPrec, final int rightPrec) {
+    writer.keyword("SHOW");
+    writer.identifier(name.getSimple(), false);
+  }
+
+  public SqlIdentifier getName() {
+    return name;
+  }
+}
diff --git a/babel/src/main/java/org/apache/calcite/sql/babel/postgresql/package-info.java b/babel/src/main/java/org/apache/calcite/sql/babel/postgresql/package-info.java
new file mode 100644
index 0000000000..2362f6f463
--- /dev/null
+++ b/babel/src/main/java/org/apache/calcite/sql/babel/postgresql/package-info.java
@@ -0,0 +1,21 @@
+/*
+ * 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.
+ */
+
+/**
+ * Parse tree for PostgreSQL extensions used by the Babel parser.
+ */
+package org.apache.calcite.sql.babel.postgresql;
diff --git a/babel/src/test/java/org/apache/calcite/test/BabelParserTest.java b/babel/src/test/java/org/apache/calcite/test/BabelParserTest.java
index 9cccc5e448..310929592a 100644
--- a/babel/src/test/java/org/apache/calcite/test/BabelParserTest.java
+++ b/babel/src/test/java/org/apache/calcite/test/BabelParserTest.java
@@ -18,6 +18,7 @@ package org.apache.calcite.test;
 
 import org.apache.calcite.sql.SqlDialect;
 import org.apache.calcite.sql.dialect.MysqlSqlDialect;
+import org.apache.calcite.sql.dialect.PostgresqlSqlDialect;
 import org.apache.calcite.sql.parser.SqlAbstractParserImpl;
 import org.apache.calcite.sql.parser.SqlParser;
 import org.apache.calcite.sql.parser.SqlParserFixture;
@@ -333,6 +334,85 @@ class BabelParserTest extends SqlParserTest {
         .ok("SELECT (ARRAY['a', 'b'])");
   }
 
+  @Test void testPostgresqlShow() {
+    SqlParserFixture f = fixture().withDialect(PostgresqlSqlDialect.DEFAULT);
+    f.sql("SHOW autovacuum")
+        .ok("SHOW \"autovacuum\"");
+    f.sql("SHOW TRANSACTION ISOLATION LEVEL")
+        .ok("SHOW \"transaction_isolation\"");
+  }
+
+  @Test void testPostgresqlSetOption() {
+    SqlParserFixture f = fixture().withDialect(PostgresqlSqlDialect.DEFAULT);
+    f.sql("SET SESSION autovacuum = true")
+        .ok("ALTER SESSION SET \"autovacuum\" = TRUE");
+    f.sql("SET SESSION autovacuum = DEFAULT")
+        .ok("ALTER SESSION SET \"autovacuum\" = DEFAULT");
+    f.sql("SET LOCAL autovacuum TO 'DEFAULT'")
+        .ok("ALTER LOCAL SET \"autovacuum\" = 'DEFAULT'");
+
+    f.sql("SET SESSION TIME ZONE DEFAULT")
+        .ok("ALTER SESSION SET \"timezone\" = DEFAULT");
+    f.sql("SET SESSION TIME ZONE LOCAL")
+        .ok("ALTER SESSION SET \"timezone\" = 'LOCAL'");
+    f.sql("SET TIME ZONE 'PST8PDT'")
+        .ok("SET \"timezone\" = 'PST8PDT'");
+    f.sql("SET TIME ZONE INTERVAL '-08:00' HOUR TO MINUTE")
+        .ok("SET \"timezone\" = INTERVAL '-08:00' HOUR TO MINUTE");
+
+    f.sql("SET search_path = public,public,\"$user\"")
+        .ok("SET \"search_path\" = \"public\", \"public\", \"$user\"");
+    f.sql("SET SCHEMA public,public,\"$user\"")
+        .ok("SET \"search_path\" = \"public\", \"public\", \"$user\"");
+    f.sql("SET NAMES iso_8859_15_to_utf8")
+        .ok("SET \"client_encoding\" = \"iso_8859_15_to_utf8\"");
+  }
+
+  @Test void testPostgresqlBegin() {
+    SqlParserFixture f = fixture().withDialect(PostgresqlSqlDialect.DEFAULT);
+    f.sql("BEGIN").same();
+    f.sql("BEGIN READ ONLY").same();
+    f.sql("BEGIN TRANSACTION READ WRITE")
+        .ok("BEGIN READ WRITE");
+    f.sql("BEGIN WORK ISOLATION LEVEL SERIALIZABLE")
+        .ok("BEGIN ISOLATION LEVEL SERIALIZABLE");
+    f.sql("BEGIN ISOLATION LEVEL SERIALIZABLE, READ ONLY, DEFERRABLE").same();
+    f.sql("BEGIN ISOLATION LEVEL SERIALIZABLE, READ WRITE, NOT DEFERRABLE").same();
+  }
+
+  @Test void testPostgresqlCommit() {
+    SqlParserFixture f = fixture().withDialect(PostgresqlSqlDialect.DEFAULT);
+    f.sql("COMMIT").same();
+    f.sql("COMMIT WORK")
+        .ok("COMMIT");
+    f.sql("COMMIT TRANSACTION")
+        .ok("COMMIT");
+    f.sql("COMMIT AND NO CHAIN")
+        .ok("COMMIT");
+    f.sql("COMMIT AND CHAIN").same();
+  }
+
+  @Test void testPostgresqlRollback() {
+    SqlParserFixture f = fixture().withDialect(PostgresqlSqlDialect.DEFAULT);
+    f.sql("ROLLBACK").same();
+    f.sql("ROLLBACK WORK")
+        .ok("ROLLBACK");
+    f.sql("ROLLBACK TRANSACTION")
+        .ok("ROLLBACK");
+    f.sql("ROLLBACK AND NO CHAIN")
+        .ok("ROLLBACK");
+    f.sql("ROLLBACK AND CHAIN").same();
+  }
+
+  @Test void testPostgresqlDiscard() {
+    SqlParserFixture f = fixture().withDialect(PostgresqlSqlDialect.DEFAULT);
+    f.sql("DISCARD ALL").same();
+    f.sql("DISCARD PLANS").same();
+    f.sql("DISCARD SEQUENCES").same();
+    f.sql("DISCARD TEMPORARY").same();
+    f.sql("DISCARD TEMP").same();
+  }
+
   /** Similar to {@link #testHoist()} but using custom parser. */
   @Test void testHoistMySql() {
     // SQL contains back-ticks, which require MySQL's quoting,