From c1776520a41bcfb2fc3d9e6db269d410d25a10ee Mon Sep 17 00:00:00 2001 From: Vignesh Manickavasagam <69285843+gs-manvig@users.noreply.github.com> Date: Tue, 26 Nov 2024 04:27:15 -0500 Subject: [PATCH] [Legend SQL] Support second format of transaction isolation query (#3274) * Support second format of transaction isolation query * Add support for pg_type system schema --- .../engine/postgres/ExecutionDispatcher.java | 5 +- .../legend/engine/postgres/SystemSchemas.java | 3 +- .../txn/TxnIsolationPreparedStatement.java | 143 ++++++++++++++++++ .../txn/TxnIsolationSessionHandler.java | 35 +++++ .../handler/txn/TxnIsolationStatement.java | 79 ++++++++++ .../postgres/ExecutionDispatcherTest.java | 16 +- .../engine/postgres/PostgresServerTest.java | 57 +++++++ 7 files changed, 334 insertions(+), 4 deletions(-) create mode 100644 legend-engine-xts-sql/legend-engine-xt-sql-postgres-server/src/main/java/org/finos/legend/engine/postgres/handler/txn/TxnIsolationPreparedStatement.java create mode 100644 legend-engine-xts-sql/legend-engine-xt-sql-postgres-server/src/main/java/org/finos/legend/engine/postgres/handler/txn/TxnIsolationSessionHandler.java create mode 100644 legend-engine-xts-sql/legend-engine-xt-sql-postgres-server/src/main/java/org/finos/legend/engine/postgres/handler/txn/TxnIsolationStatement.java diff --git a/legend-engine-xts-sql/legend-engine-xt-sql-postgres-server/src/main/java/org/finos/legend/engine/postgres/ExecutionDispatcher.java b/legend-engine-xts-sql/legend-engine-xt-sql-postgres-server/src/main/java/org/finos/legend/engine/postgres/ExecutionDispatcher.java index e9b4652945a..49cd7086e22 100644 --- a/legend-engine-xts-sql/legend-engine-xt-sql-postgres-server/src/main/java/org/finos/legend/engine/postgres/ExecutionDispatcher.java +++ b/legend-engine-xts-sql/legend-engine-xt-sql-postgres-server/src/main/java/org/finos/legend/engine/postgres/ExecutionDispatcher.java @@ -20,12 +20,14 @@ import org.finos.legend.engine.language.sql.grammar.from.antlr4.SqlBaseParserBaseVisitor; import org.finos.legend.engine.postgres.handler.SessionHandler; import org.finos.legend.engine.postgres.handler.empty.EmptySessionHandler; +import org.finos.legend.engine.postgres.handler.txn.TxnIsolationSessionHandler; import org.finos.legend.engine.protocol.sql.metamodel.QualifiedName; public class ExecutionDispatcher extends SqlBaseParserBaseVisitor { private static final TableNameExtractor EXTRACTOR = new TableNameExtractor(); private static final SessionHandler EMPTY_SESSION_HANDLER = new EmptySessionHandler(); + private static final SessionHandler TXN_ISOLATION_HANDLER = new TxnIsolationSessionHandler(); private final SessionHandler dataSessionHandler; private final SessionHandler metaDataSessionHandler; @@ -51,8 +53,7 @@ public SessionHandler visitSet(SqlBaseParser.SetContext ctx) @Override public SessionHandler visitShowTransaction(SqlBaseParser.ShowTransactionContext ctx) { - // TODO: Handle show transaction instead of routing to metadata session handler - return metaDataSessionHandler; + return TXN_ISOLATION_HANDLER; } /** diff --git a/legend-engine-xts-sql/legend-engine-xt-sql-postgres-server/src/main/java/org/finos/legend/engine/postgres/SystemSchemas.java b/legend-engine-xts-sql/legend-engine-xt-sql-postgres-server/src/main/java/org/finos/legend/engine/postgres/SystemSchemas.java index f3398de17b3..dee503ffc6d 100644 --- a/legend-engine-xts-sql/legend-engine-xt-sql-postgres-server/src/main/java/org/finos/legend/engine/postgres/SystemSchemas.java +++ b/legend-engine-xts-sql/legend-engine-xt-sql-postgres-server/src/main/java/org/finos/legend/engine/postgres/SystemSchemas.java @@ -21,7 +21,8 @@ public enum SystemSchemas { INFORMATION_SCHEMA("information_schema"), - PG_CATALOG("pg_catalog"); + PG_CATALOG("pg_catalog"), + PG_TYPE("pg_type"); private final String schemaName; diff --git a/legend-engine-xts-sql/legend-engine-xt-sql-postgres-server/src/main/java/org/finos/legend/engine/postgres/handler/txn/TxnIsolationPreparedStatement.java b/legend-engine-xts-sql/legend-engine-xt-sql-postgres-server/src/main/java/org/finos/legend/engine/postgres/handler/txn/TxnIsolationPreparedStatement.java new file mode 100644 index 00000000000..eadd4937ed9 --- /dev/null +++ b/legend-engine-xts-sql/legend-engine-xt-sql-postgres-server/src/main/java/org/finos/legend/engine/postgres/handler/txn/TxnIsolationPreparedStatement.java @@ -0,0 +1,143 @@ +// Copyright 2023 Goldman Sachs +// +// Licensed 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.finos.legend.engine.postgres.handler.txn; + +import org.finos.legend.engine.postgres.handler.PostgresPreparedStatement; +import org.finos.legend.engine.postgres.handler.PostgresResultSet; +import org.finos.legend.engine.postgres.handler.PostgresResultSetMetaData; + +import java.sql.ParameterMetaData; +import java.sql.Types; + +public class TxnIsolationPreparedStatement implements PostgresPreparedStatement +{ + private boolean isComplete = false; + static final String READ_COMMITTED = "read committed"; + static final PostgresResultSetMetaData TXN_ISOLATION_RS_META_DATA = new PostgresResultSetMetaData() + { + @Override + public int getColumnCount() + { + return 1; + } + + @Override + public String getColumnName(int i) + { + return "transaction_isolation"; + } + + @Override + public int getColumnType(int i) + { + return Types.VARCHAR; + } + + @Override + public int getScale(int i) + { + return 0; + } + }; + + @Override + public void setObject(int i, Object o) + { + // empty statement doesn't require objects to be set + } + + @Override + public PostgresResultSetMetaData getMetaData() + { + return TXN_ISOLATION_RS_META_DATA; + } + + @Override + public ParameterMetaData getParameterMetaData() + { + return null; + } + + @Override + public void close() throws Exception + { + // txn isolation handler doesn't require closure + } + + @Override + public void setMaxRows(int maxRows) + { + // txn isolation handler doesn't require max rows + } + + @Override + public int getMaxRows() + { + return 0; + } + + @Override + public boolean isExecuted() + { + return true; + } + + @Override + public boolean execute() + { + return true; + } + + @Override + public PostgresResultSet getResultSet() + { + return new PostgresResultSet() + { + @Override + public PostgresResultSetMetaData getMetaData() + { + return TXN_ISOLATION_RS_META_DATA; + } + + @Override + public Object getObject(int i) + { + return READ_COMMITTED; + } + + @Override + public boolean next() + { + if (!isComplete) + { + isComplete = true; + return true; + } + else + { + return false; + } + } + + @Override + public void close() + { + // txn isolation result set doesn't require closure + } + }; + } + +} diff --git a/legend-engine-xts-sql/legend-engine-xt-sql-postgres-server/src/main/java/org/finos/legend/engine/postgres/handler/txn/TxnIsolationSessionHandler.java b/legend-engine-xts-sql/legend-engine-xt-sql-postgres-server/src/main/java/org/finos/legend/engine/postgres/handler/txn/TxnIsolationSessionHandler.java new file mode 100644 index 00000000000..d5e68cf0ed4 --- /dev/null +++ b/legend-engine-xts-sql/legend-engine-xt-sql-postgres-server/src/main/java/org/finos/legend/engine/postgres/handler/txn/TxnIsolationSessionHandler.java @@ -0,0 +1,35 @@ +// Copyright 2023 Goldman Sachs +// +// Licensed 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.finos.legend.engine.postgres.handler.txn; + +import org.finos.legend.engine.postgres.handler.PostgresPreparedStatement; +import org.finos.legend.engine.postgres.handler.PostgresStatement; +import org.finos.legend.engine.postgres.handler.SessionHandler; + +public class TxnIsolationSessionHandler implements SessionHandler +{ + @Override + public PostgresPreparedStatement prepareStatement(String query) + { + return new TxnIsolationPreparedStatement(); + } + + @Override + public PostgresStatement createStatement() + { + return new TxnIsolationStatement(); + } +} diff --git a/legend-engine-xts-sql/legend-engine-xt-sql-postgres-server/src/main/java/org/finos/legend/engine/postgres/handler/txn/TxnIsolationStatement.java b/legend-engine-xts-sql/legend-engine-xt-sql-postgres-server/src/main/java/org/finos/legend/engine/postgres/handler/txn/TxnIsolationStatement.java new file mode 100644 index 00000000000..d7d4b02effe --- /dev/null +++ b/legend-engine-xts-sql/legend-engine-xt-sql-postgres-server/src/main/java/org/finos/legend/engine/postgres/handler/txn/TxnIsolationStatement.java @@ -0,0 +1,79 @@ +// Copyright 2023 Goldman Sachs +// +// Licensed 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.finos.legend.engine.postgres.handler.txn; + +import org.finos.legend.engine.postgres.handler.PostgresResultSet; +import org.finos.legend.engine.postgres.handler.PostgresResultSetMetaData; +import org.finos.legend.engine.postgres.handler.PostgresStatement; + +import static org.finos.legend.engine.postgres.handler.txn.TxnIsolationPreparedStatement.READ_COMMITTED; +import static org.finos.legend.engine.postgres.handler.txn.TxnIsolationPreparedStatement.TXN_ISOLATION_RS_META_DATA; + +public class TxnIsolationStatement implements PostgresStatement +{ + private boolean isComplete = false; + + @Override + public boolean execute(String query) + { + return true; + } + + @Override + public PostgresResultSet getResultSet() + { + return new PostgresResultSet() + { + @Override + public PostgresResultSetMetaData getMetaData() + { + return TXN_ISOLATION_RS_META_DATA; + } + + @Override + public Object getObject(int i) + { + return READ_COMMITTED; + } + + @Override + public boolean next() + { + if (!isComplete) + { + isComplete = true; + return true; + } + else + { + return false; + } + } + + @Override + public void close() + { + // txn isolation result set doesn't require closure + } + }; + } + + @Override + public void close() throws Exception + { + // txn isolation handler doesn't require closure + } +} diff --git a/legend-engine-xts-sql/legend-engine-xt-sql-postgres-server/src/test/java/org/finos/legend/engine/postgres/ExecutionDispatcherTest.java b/legend-engine-xts-sql/legend-engine-xt-sql-postgres-server/src/test/java/org/finos/legend/engine/postgres/ExecutionDispatcherTest.java index 9bd4fd91a75..7010b5f59eb 100644 --- a/legend-engine-xts-sql/legend-engine-xt-sql-postgres-server/src/test/java/org/finos/legend/engine/postgres/ExecutionDispatcherTest.java +++ b/legend-engine-xts-sql/legend-engine-xt-sql-postgres-server/src/test/java/org/finos/legend/engine/postgres/ExecutionDispatcherTest.java @@ -21,6 +21,7 @@ import org.finos.legend.engine.postgres.handler.PostgresStatement; import org.finos.legend.engine.postgres.handler.SessionHandler; import org.finos.legend.engine.postgres.handler.empty.EmptySessionHandler; +import org.finos.legend.engine.postgres.handler.txn.TxnIsolationSessionHandler; import org.junit.Assert; import org.junit.Test; @@ -42,6 +43,12 @@ public void testSelectInformationSchema() assertMetadataSessionHandler("SELECT * FROM information_schema.TABLES"); } + @Test + public void testSelectPgType() + { + assertMetadataSessionHandler("SELECT oid, typbasetype FROM pg_type"); + } + @Test public void testSelectPgCatalog() { @@ -79,7 +86,8 @@ public void testSelectTableName() @Test public void testShowTxnLevel() { - assertMetadataSessionHandler("SHOW TRANSACTION ISOLATION LEVEL"); + assertTxnIsoSessionHandler("SHOW TRANSACTION ISOLATION LEVEL"); + assertTxnIsoSessionHandler("SHOW transaction_isolation"); } private static void assertEmptySessionHandler(String query) @@ -94,6 +102,12 @@ private static void assertMetadataSessionHandler(String query) Assert.assertSame(metadataSessionHandler, sessionHandler); } + private static void assertTxnIsoSessionHandler(String query) + { + SessionHandler sessionHandler = getSessionHandler(query); + Assert.assertTrue(sessionHandler instanceof TxnIsolationSessionHandler); + } + private static void assertDataSessionHandler(String query) { SessionHandler sessionHandler = getSessionHandler(query); diff --git a/legend-engine-xts-sql/legend-engine-xt-sql-postgres-server/src/test/java/org/finos/legend/engine/postgres/PostgresServerTest.java b/legend-engine-xts-sql/legend-engine-xt-sql-postgres-server/src/test/java/org/finos/legend/engine/postgres/PostgresServerTest.java index 501b376ef7f..4c67bdd5d2b 100644 --- a/legend-engine-xts-sql/legend-engine-xt-sql-postgres-server/src/test/java/org/finos/legend/engine/postgres/PostgresServerTest.java +++ b/legend-engine-xts-sql/legend-engine-xt-sql-postgres-server/src/test/java/org/finos/legend/engine/postgres/PostgresServerTest.java @@ -21,6 +21,7 @@ import org.finos.legend.engine.language.pure.compiler.toPureGraph.PureModel; import org.finos.legend.engine.postgres.auth.AnonymousIdentityProvider; import org.finos.legend.engine.postgres.auth.NoPasswordAuthenticationMethod; +import org.finos.legend.engine.postgres.config.OpenTelemetryConfig; import org.finos.legend.engine.postgres.config.ServerConfig; import org.finos.legend.engine.postgres.handler.legend.LegendExecutionService; import org.finos.legend.engine.postgres.handler.legend.LegendSessionFactory; @@ -43,6 +44,7 @@ import java.sql.ResultSet; import java.sql.ResultSetMetaData; import java.sql.SQLException; +import java.sql.Statement; import java.util.Properties; public class PostgresServerTest @@ -356,6 +358,9 @@ public void testSelectWithoutTable() throws SQLException } } + /** + * This query format is used by the postgres jdbc driver + */ @Test public void testShowTxn() throws SQLException { @@ -375,6 +380,39 @@ public void testShowTxn() throws SQLException } } + /** + * This query format is used by the postgres odbc driver + */ + @Test + public void testShowTxnOdbc() throws SQLException + { + String sql = "SHOW transaction_isolation"; + try ( + Connection connection = DriverManager.getConnection("jdbc:postgresql://127.0.0.1:" + testPostgresServer.getLocalAddress().getPort() + "/postgres", + "dummy", "dummy"); + PreparedStatement preparedStatement = connection.prepareStatement(sql); + ResultSet psResultSet = preparedStatement.executeQuery(); + Statement statement = connection.createStatement() + ) + { + int psRows = 0; + while (psResultSet.next()) + { + psRows++; + } + Assert.assertEquals(1, psRows); + + Assert.assertTrue(statement.execute(sql)); + ResultSet statementResultSet = statement.getResultSet(); + int statementRows = 0; + while (statementResultSet.next()) + { + statementRows++; + } + Assert.assertEquals(1, statementRows); + } + } + @Test public void testHikariConnection() throws SQLException { @@ -435,6 +473,25 @@ public void testPgCatalog() throws SQLException } } + @Test + public void testPgType() throws SQLException + { + try ( + Connection connection = DriverManager.getConnection("jdbc:postgresql://127.0.0.1:" + testPostgresServer.getLocalAddress().getPort() + "/postgres", + "dummy", "dummy"); + PreparedStatement statement = connection.prepareStatement("SELECT oid, typbasetype FROM pg_type"); + ResultSet resultSet = statement.executeQuery() + ) + { + int rows = 0; + while (resultSet.next()) + { + rows++; + } + Assert.assertEquals(24, rows); + } + } + @Test public void testConnectionIsValid() throws SQLException {