diff --git a/pom.xml b/pom.xml index 416bbc56e7d3c..988c21d943330 100644 --- a/pom.xml +++ b/pom.xml @@ -712,7 +712,7 @@ mysql mysql-connector-java - 5.1.44 + 5.1.47 @@ -878,7 +878,7 @@ io.airlift testing-mysql-server - 5.7.22-1 + 8.0.12-2 diff --git a/presto-base-jdbc/src/main/java/io/prestosql/plugin/jdbc/BaseJdbcClient.java b/presto-base-jdbc/src/main/java/io/prestosql/plugin/jdbc/BaseJdbcClient.java index 4db5a7244e0de..a0f85b14b03e7 100644 --- a/presto-base-jdbc/src/main/java/io/prestosql/plugin/jdbc/BaseJdbcClient.java +++ b/presto-base-jdbc/src/main/java/io/prestosql/plugin/jdbc/BaseJdbcClient.java @@ -80,6 +80,7 @@ import static io.prestosql.spi.type.Varchars.isVarcharType; import static java.lang.String.format; import static java.lang.String.join; +import static java.sql.DatabaseMetaData.columnNoNulls; import static java.util.Collections.nCopies; import static java.util.Locale.ENGLISH; import static java.util.Objects.requireNonNull; @@ -209,7 +210,8 @@ public List getColumns(ConnectorSession session, JdbcTableHand // skip unsupported column types if (columnMapping.isPresent()) { String columnName = resultSet.getString("COLUMN_NAME"); - columns.add(new JdbcColumnHandle(columnName, typeHandle, columnMapping.get().getType())); + boolean nullable = (resultSet.getInt("NULLABLE") != columnNoNulls); + columns.add(new JdbcColumnHandle(columnName, typeHandle, columnMapping.get().getType(), nullable)); } } if (columns.isEmpty()) { @@ -274,6 +276,17 @@ public PreparedStatement buildSql(ConnectorSession session, Connection connectio split.getAdditionalPredicate()); } + @Override + public void createTable(ConnectorSession session, ConnectorTableMetadata tableMetadata) + { + try { + createTable(session, tableMetadata, tableMetadata.getTable().getTableName()); + } + catch (SQLException e) { + throw new PrestoException(JDBC_ERROR, e); + } + } + @Override public JdbcOutputTableHandle beginCreateTable(ConnectorSession session, ConnectorTableMetadata tableMetadata) { @@ -287,6 +300,17 @@ public JdbcOutputTableHandle beginInsertTable(ConnectorSession session, Connecto } private JdbcOutputTableHandle beginWriteTable(ConnectorSession session, ConnectorTableMetadata tableMetadata) + { + try { + return createTable(session, tableMetadata, generateTemporaryTableName()); + } + catch (SQLException e) { + throw new PrestoException(JDBC_ERROR, e); + } + } + + protected JdbcOutputTableHandle createTable(ConnectorSession session, ConnectorTableMetadata tableMetadata, String tableName) + throws SQLException { SchemaTableName schemaTableName = tableMetadata.getTable(); String schema = schemaTableName.getSchemaName(); @@ -302,11 +326,10 @@ private JdbcOutputTableHandle beginWriteTable(ConnectorSession session, Connecto if (uppercase) { schema = schema.toUpperCase(ENGLISH); table = table.toUpperCase(ENGLISH); + tableName = tableName.toUpperCase(ENGLISH); } String catalog = connection.getCatalog(); - String temporaryName = generateTemporaryTableName(); - ImmutableList.Builder columnNames = ImmutableList.builder(); ImmutableList.Builder columnTypes = ImmutableList.builder(); ImmutableList.Builder columnList = ImmutableList.builder(); @@ -318,12 +341,12 @@ private JdbcOutputTableHandle beginWriteTable(ConnectorSession session, Connecto columnNames.add(columnName); columnTypes.add(column.getType()); // TODO in INSERT case, we should reuse original column type and, ideally, constraints (then JdbcPageSink must get writer from toPrestoType()) - columnList.add(format("%s %s", quoted(columnName), toWriteMapping(session, column.getType()).getDataType())); + columnList.add(getColumnSql(session, column, columnName)); } String sql = format( "CREATE TABLE %s (%s)", - quoted(catalog, schema, temporaryName), + quoted(catalog, schema, tableName), join(", ", columnList.build())); execute(connection, sql); @@ -333,11 +356,20 @@ private JdbcOutputTableHandle beginWriteTable(ConnectorSession session, Connecto table, columnNames.build(), columnTypes.build(), - temporaryName); + tableName); } - catch (SQLException e) { - throw new PrestoException(JDBC_ERROR, e); + } + + private String getColumnSql(ConnectorSession session, ColumnMetadata column, String columnName) + { + StringBuilder sb = new StringBuilder() + .append(quoted(columnName)) + .append(" ") + .append(toWriteMapping(session, column.getType()).getDataType()); + if (!column.isNullable()) { + sb.append(" NOT NULL"); } + return sb.toString(); } protected String generateTemporaryTableName() @@ -348,12 +380,33 @@ protected String generateTemporaryTableName() @Override public void commitCreateTable(JdbcIdentity identity, JdbcOutputTableHandle handle) { - String sql = format( - "ALTER TABLE %s RENAME TO %s", - quoted(handle.getCatalogName(), handle.getSchemaName(), handle.getTemporaryTableName()), - quoted(handle.getCatalogName(), handle.getSchemaName(), handle.getTableName())); + renameTable( + identity, + handle.getCatalogName(), + handle.getSchemaName(), + handle.getTemporaryTableName(), + new SchemaTableName(handle.getSchemaName(), handle.getTableName())); + } - try (Connection connection = getConnection(identity, handle)) { + @Override + public void renameTable(JdbcIdentity identity, JdbcTableHandle handle, SchemaTableName newTableName) + { + renameTable(identity, handle.getCatalogName(), handle.getSchemaName(), handle.getTableName(), newTableName); + } + + protected void renameTable(JdbcIdentity identity, String catalogName, String schemaName, String tableName, SchemaTableName newTable) + { + try (Connection connection = connectionFactory.openConnection(identity)) { + String newSchemaName = newTable.getSchemaName(); + String newTableName = newTable.getTableName(); + if (connection.getMetaData().storesUpperCaseIdentifiers()) { + newSchemaName = newSchemaName.toUpperCase(ENGLISH); + newTableName = newTableName.toUpperCase(ENGLISH); + } + String sql = format( + "ALTER TABLE %s RENAME TO %s", + quoted(catalogName, schemaName, tableName), + quoted(catalogName, newSchemaName, newTableName)); execute(connection, sql); } catch (SQLException e) { @@ -384,6 +437,59 @@ public void finishInsertTable(JdbcIdentity identity, JdbcOutputTableHandle handl } } + @Override + public void addColumn(ConnectorSession session, JdbcTableHandle handle, ColumnMetadata column) + { + try (Connection connection = connectionFactory.openConnection(JdbcIdentity.from(session))) { + String columnName = column.getName(); + if (connection.getMetaData().storesUpperCaseIdentifiers()) { + columnName = columnName.toUpperCase(ENGLISH); + } + String sql = format( + "ALTER TABLE %s ADD %s", + quoted(handle.getCatalogName(), handle.getSchemaName(), handle.getTableName()), + getColumnSql(session, column, columnName)); + execute(connection, sql); + } + catch (SQLException e) { + throw new PrestoException(JDBC_ERROR, e); + } + } + + @Override + public void renameColumn(JdbcIdentity identity, JdbcTableHandle handle, JdbcColumnHandle jdbcColumn, String newColumnName) + { + try (Connection connection = connectionFactory.openConnection(identity)) { + if (connection.getMetaData().storesUpperCaseIdentifiers()) { + newColumnName = newColumnName.toUpperCase(ENGLISH); + } + String sql = format( + "ALTER TABLE %s RENAME COLUMN %s TO %s", + quoted(handle.getCatalogName(), handle.getSchemaName(), handle.getTableName()), + jdbcColumn.getColumnName(), + newColumnName); + execute(connection, sql); + } + catch (SQLException e) { + throw new PrestoException(JDBC_ERROR, e); + } + } + + @Override + public void dropColumn(JdbcIdentity identity, JdbcTableHandle handle, JdbcColumnHandle column) + { + try (Connection connection = connectionFactory.openConnection(identity)) { + String sql = format( + "ALTER TABLE %s DROP COLUMN %s", + quoted(handle.getCatalogName(), handle.getSchemaName(), handle.getTableName()), + column.getColumnName()); + execute(connection, sql); + } + catch (SQLException e) { + throw new PrestoException(JDBC_ERROR, e); + } + } + @Override public void dropTable(JdbcIdentity identity, JdbcTableHandle handle) { diff --git a/presto-base-jdbc/src/main/java/io/prestosql/plugin/jdbc/JdbcClient.java b/presto-base-jdbc/src/main/java/io/prestosql/plugin/jdbc/JdbcClient.java index 5f605fa299de6..a9f7fc77e0aef 100644 --- a/presto-base-jdbc/src/main/java/io/prestosql/plugin/jdbc/JdbcClient.java +++ b/presto-base-jdbc/src/main/java/io/prestosql/plugin/jdbc/JdbcClient.java @@ -14,6 +14,7 @@ package io.prestosql.plugin.jdbc; import io.prestosql.spi.connector.ColumnHandle; +import io.prestosql.spi.connector.ColumnMetadata; import io.prestosql.spi.connector.ConnectorSession; import io.prestosql.spi.connector.ConnectorSplitSource; import io.prestosql.spi.connector.ConnectorTableMetadata; @@ -62,6 +63,16 @@ default void abortReadConnection(Connection connection) PreparedStatement buildSql(ConnectorSession session, Connection connection, JdbcSplit split, List columnHandles) throws SQLException; + void addColumn(ConnectorSession session, JdbcTableHandle handle, ColumnMetadata column); + + void dropColumn(JdbcIdentity identity, JdbcTableHandle handle, JdbcColumnHandle column); + + void renameColumn(JdbcIdentity identity, JdbcTableHandle handle, JdbcColumnHandle jdbcColumn, String newColumnName); + + void renameTable(JdbcIdentity identity, JdbcTableHandle handle, SchemaTableName newTableName); + + void createTable(ConnectorSession session, ConnectorTableMetadata tableMetadata); + JdbcOutputTableHandle beginCreateTable(ConnectorSession session, ConnectorTableMetadata tableMetadata); void commitCreateTable(JdbcIdentity identity, JdbcOutputTableHandle handle); diff --git a/presto-base-jdbc/src/main/java/io/prestosql/plugin/jdbc/JdbcColumnHandle.java b/presto-base-jdbc/src/main/java/io/prestosql/plugin/jdbc/JdbcColumnHandle.java index 0bd43f8e4133c..86b9f587f89aa 100644 --- a/presto-base-jdbc/src/main/java/io/prestosql/plugin/jdbc/JdbcColumnHandle.java +++ b/presto-base-jdbc/src/main/java/io/prestosql/plugin/jdbc/JdbcColumnHandle.java @@ -22,6 +22,7 @@ import java.util.Objects; import static com.google.common.base.MoreObjects.toStringHelper; +import static java.util.Collections.emptyMap; import static java.util.Objects.requireNonNull; public final class JdbcColumnHandle @@ -30,16 +31,19 @@ public final class JdbcColumnHandle private final String columnName; private final JdbcTypeHandle jdbcTypeHandle; private final Type columnType; + private final boolean nullable; @JsonCreator public JdbcColumnHandle( @JsonProperty("columnName") String columnName, @JsonProperty("jdbcTypeHandle") JdbcTypeHandle jdbcTypeHandle, - @JsonProperty("columnType") Type columnType) + @JsonProperty("columnType") Type columnType, + @JsonProperty("nullable") boolean nullable) { this.columnName = requireNonNull(columnName, "columnName is null"); this.jdbcTypeHandle = requireNonNull(jdbcTypeHandle, "jdbcTypeHandle is null"); this.columnType = requireNonNull(columnType, "columnType is null"); + this.nullable = nullable; } @JsonProperty @@ -60,9 +64,15 @@ public Type getColumnType() return columnType; } + @JsonProperty + public boolean isNullable() + { + return nullable; + } + public ColumnMetadata getColumnMetadata() { - return new ColumnMetadata(columnName, columnType); + return new ColumnMetadata(columnName, columnType, nullable, null, null, false, emptyMap()); } @Override @@ -91,6 +101,7 @@ public String toString() .add("columnName", columnName) .add("jdbcTypeHandle", jdbcTypeHandle) .add("columnType", columnType) + .add("nullable", nullable) .toString(); } } diff --git a/presto-base-jdbc/src/main/java/io/prestosql/plugin/jdbc/JdbcConnector.java b/presto-base-jdbc/src/main/java/io/prestosql/plugin/jdbc/JdbcConnector.java index 84803a0b3914f..54067c6bf880b 100644 --- a/presto-base-jdbc/src/main/java/io/prestosql/plugin/jdbc/JdbcConnector.java +++ b/presto-base-jdbc/src/main/java/io/prestosql/plugin/jdbc/JdbcConnector.java @@ -18,6 +18,7 @@ import io.airlift.log.Logger; import io.prestosql.spi.connector.Connector; import io.prestosql.spi.connector.ConnectorAccessControl; +import io.prestosql.spi.connector.ConnectorCapabilities; import io.prestosql.spi.connector.ConnectorMetadata; import io.prestosql.spi.connector.ConnectorPageSinkProvider; import io.prestosql.spi.connector.ConnectorRecordSetProvider; @@ -34,6 +35,8 @@ import java.util.concurrent.ConcurrentMap; import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.collect.Sets.immutableEnumSet; +import static io.prestosql.spi.connector.ConnectorCapabilities.NOT_NULL_COLUMN_CONSTRAINT; import static io.prestosql.spi.transaction.IsolationLevel.READ_COMMITTED; import static io.prestosql.spi.transaction.IsolationLevel.checkConnectorSupports; import static java.util.Objects.requireNonNull; @@ -149,4 +152,10 @@ public final void shutdown() log.error(e, "Error shutting down connector"); } } + + @Override + public Set getCapabilities() + { + return immutableEnumSet(NOT_NULL_COLUMN_CONSTRAINT); + } } diff --git a/presto-base-jdbc/src/main/java/io/prestosql/plugin/jdbc/JdbcMetadata.java b/presto-base-jdbc/src/main/java/io/prestosql/plugin/jdbc/JdbcMetadata.java index c06dda4750a7d..a6a545cd97eea 100644 --- a/presto-base-jdbc/src/main/java/io/prestosql/plugin/jdbc/JdbcMetadata.java +++ b/presto-base-jdbc/src/main/java/io/prestosql/plugin/jdbc/JdbcMetadata.java @@ -168,6 +168,12 @@ public ConnectorOutputTableHandle beginCreateTable(ConnectorSession session, Con return handle; } + @Override + public void createTable(ConnectorSession session, ConnectorTableMetadata tableMetadata, boolean ignoreExisting) + { + jdbcClient.createTable(session, tableMetadata); + } + @Override public Optional finishCreateTable(ConnectorSession session, ConnectorOutputTableHandle tableHandle, Collection fragments, Collection computedStatistics) { @@ -208,6 +214,36 @@ public Optional finishInsert(ConnectorSession session, return Optional.empty(); } + @Override + public void addColumn(ConnectorSession session, ConnectorTableHandle table, ColumnMetadata columnMetadata) + { + JdbcTableHandle tableHandle = (JdbcTableHandle) table; + jdbcClient.addColumn(session, tableHandle, columnMetadata); + } + + @Override + public void dropColumn(ConnectorSession session, ConnectorTableHandle table, ColumnHandle column) + { + JdbcTableHandle tableHandle = (JdbcTableHandle) table; + JdbcColumnHandle columnHandle = (JdbcColumnHandle) column; + jdbcClient.dropColumn(JdbcIdentity.from(session), tableHandle, columnHandle); + } + + @Override + public void renameColumn(ConnectorSession session, ConnectorTableHandle table, ColumnHandle column, String target) + { + JdbcTableHandle tableHandle = (JdbcTableHandle) table; + JdbcColumnHandle columnHandle = (JdbcColumnHandle) column; + jdbcClient.renameColumn(JdbcIdentity.from(session), tableHandle, columnHandle, target); + } + + @Override + public void renameTable(ConnectorSession session, ConnectorTableHandle table, SchemaTableName newTableName) + { + JdbcTableHandle tableHandle = (JdbcTableHandle) table; + jdbcClient.renameTable(JdbcIdentity.from(session), tableHandle, newTableName); + } + @Override public TableStatistics getTableStatistics(ConnectorSession session, ConnectorTableHandle tableHandle, Constraint constraint) { diff --git a/presto-base-jdbc/src/test/java/io/prestosql/plugin/jdbc/TestJdbcClient.java b/presto-base-jdbc/src/test/java/io/prestosql/plugin/jdbc/TestJdbcClient.java index 6118e37910180..9bab0b0448d29 100644 --- a/presto-base-jdbc/src/test/java/io/prestosql/plugin/jdbc/TestJdbcClient.java +++ b/presto-base-jdbc/src/test/java/io/prestosql/plugin/jdbc/TestJdbcClient.java @@ -83,9 +83,9 @@ public void testMetadata() assertEquals(table.get().getTableName(), "NUMBERS"); assertEquals(table.get().getSchemaTableName(), schemaTableName); assertEquals(jdbcClient.getColumns(session, table.orElse(null)), ImmutableList.of( - new JdbcColumnHandle("TEXT", JDBC_VARCHAR, VARCHAR), - new JdbcColumnHandle("TEXT_SHORT", JDBC_VARCHAR, createVarcharType(32)), - new JdbcColumnHandle("VALUE", JDBC_BIGINT, BIGINT))); + new JdbcColumnHandle("TEXT", JDBC_VARCHAR, VARCHAR, true), + new JdbcColumnHandle("TEXT_SHORT", JDBC_VARCHAR, createVarcharType(32), true), + new JdbcColumnHandle("VALUE", JDBC_BIGINT, BIGINT, true))); } @Test @@ -95,8 +95,8 @@ public void testMetadataWithSchemaPattern() Optional table = jdbcClient.getTableHandle(JdbcIdentity.from(session), schemaTableName); assertTrue(table.isPresent(), "table is missing"); assertEquals(jdbcClient.getColumns(session, table.get()), ImmutableList.of( - new JdbcColumnHandle("TE_T", JDBC_VARCHAR, VARCHAR), - new JdbcColumnHandle("VA%UE", JDBC_BIGINT, BIGINT))); + new JdbcColumnHandle("TE_T", JDBC_VARCHAR, VARCHAR, true), + new JdbcColumnHandle("VA%UE", JDBC_BIGINT, BIGINT, true))); } @Test @@ -106,9 +106,9 @@ public void testMetadataWithFloatAndDoubleCol() Optional table = jdbcClient.getTableHandle(JdbcIdentity.from(session), schemaTableName); assertTrue(table.isPresent(), "table is missing"); assertEquals(jdbcClient.getColumns(session, table.get()), ImmutableList.of( - new JdbcColumnHandle("COL1", JDBC_BIGINT, BIGINT), - new JdbcColumnHandle("COL2", JDBC_DOUBLE, DOUBLE), - new JdbcColumnHandle("COL3", JDBC_DOUBLE, DOUBLE), - new JdbcColumnHandle("COL4", JDBC_REAL, REAL))); + new JdbcColumnHandle("COL1", JDBC_BIGINT, BIGINT, true), + new JdbcColumnHandle("COL2", JDBC_DOUBLE, DOUBLE, true), + new JdbcColumnHandle("COL3", JDBC_DOUBLE, DOUBLE, true), + new JdbcColumnHandle("COL4", JDBC_REAL, REAL, true))); } } diff --git a/presto-base-jdbc/src/test/java/io/prestosql/plugin/jdbc/TestJdbcColumnHandle.java b/presto-base-jdbc/src/test/java/io/prestosql/plugin/jdbc/TestJdbcColumnHandle.java index a557cfb40c41a..b87002a2875bf 100644 --- a/presto-base-jdbc/src/test/java/io/prestosql/plugin/jdbc/TestJdbcColumnHandle.java +++ b/presto-base-jdbc/src/test/java/io/prestosql/plugin/jdbc/TestJdbcColumnHandle.java @@ -28,7 +28,7 @@ public class TestJdbcColumnHandle @Test public void testJsonRoundTrip() { - assertJsonRoundTrip(COLUMN_CODEC, new JdbcColumnHandle("columnName", JDBC_VARCHAR, VARCHAR)); + assertJsonRoundTrip(COLUMN_CODEC, new JdbcColumnHandle("columnName", JDBC_VARCHAR, VARCHAR, true)); } @Test @@ -36,15 +36,15 @@ public void testEquivalence() { EquivalenceTester.equivalenceTester() .addEquivalentGroup( - new JdbcColumnHandle("columnName", JDBC_VARCHAR, VARCHAR), - new JdbcColumnHandle("columnName", JDBC_VARCHAR, VARCHAR), - new JdbcColumnHandle("columnName", JDBC_BIGINT, BIGINT), - new JdbcColumnHandle("columnName", JDBC_VARCHAR, VARCHAR)) + new JdbcColumnHandle("columnName", JDBC_VARCHAR, VARCHAR, true), + new JdbcColumnHandle("columnName", JDBC_VARCHAR, VARCHAR, true), + new JdbcColumnHandle("columnName", JDBC_BIGINT, BIGINT, true), + new JdbcColumnHandle("columnName", JDBC_VARCHAR, VARCHAR, true)) .addEquivalentGroup( - new JdbcColumnHandle("columnNameX", JDBC_VARCHAR, VARCHAR), - new JdbcColumnHandle("columnNameX", JDBC_VARCHAR, VARCHAR), - new JdbcColumnHandle("columnNameX", JDBC_BIGINT, BIGINT), - new JdbcColumnHandle("columnNameX", JDBC_VARCHAR, VARCHAR)) + new JdbcColumnHandle("columnNameX", JDBC_VARCHAR, VARCHAR, true), + new JdbcColumnHandle("columnNameX", JDBC_VARCHAR, VARCHAR, true), + new JdbcColumnHandle("columnNameX", JDBC_BIGINT, BIGINT, true), + new JdbcColumnHandle("columnNameX", JDBC_VARCHAR, VARCHAR, true)) .check(); } } diff --git a/presto-base-jdbc/src/test/java/io/prestosql/plugin/jdbc/TestJdbcMetadata.java b/presto-base-jdbc/src/test/java/io/prestosql/plugin/jdbc/TestJdbcMetadata.java index 4693e129219a3..88d1425846d6c 100644 --- a/presto-base-jdbc/src/test/java/io/prestosql/plugin/jdbc/TestJdbcMetadata.java +++ b/presto-base-jdbc/src/test/java/io/prestosql/plugin/jdbc/TestJdbcMetadata.java @@ -35,6 +35,7 @@ import static io.prestosql.spi.type.VarcharType.VARCHAR; import static io.prestosql.spi.type.VarcharType.createVarcharType; import static io.prestosql.testing.TestingConnectorSession.SESSION; +import static java.util.Collections.emptyMap; import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertNull; import static org.testng.Assert.assertTrue; @@ -84,9 +85,9 @@ public void testGetColumnHandles() { // known table assertEquals(metadata.getColumnHandles(SESSION, tableHandle), ImmutableMap.of( - "text", new JdbcColumnHandle("TEXT", JDBC_VARCHAR, VARCHAR), - "text_short", new JdbcColumnHandle("TEXT_SHORT", JDBC_VARCHAR, createVarcharType(32)), - "value", new JdbcColumnHandle("VALUE", JDBC_BIGINT, BIGINT))); + "text", new JdbcColumnHandle("TEXT", JDBC_VARCHAR, VARCHAR, true), + "text_short", new JdbcColumnHandle("TEXT_SHORT", JDBC_VARCHAR, createVarcharType(32), true), + "value", new JdbcColumnHandle("VALUE", JDBC_BIGINT, BIGINT, true))); // unknown table unknownTableColumnHandle(new JdbcTableHandle(new SchemaTableName("unknown", "unknown"), "unknown", "unknown", "unknown")); @@ -110,7 +111,7 @@ public void getTableMetadata() ConnectorTableMetadata tableMetadata = metadata.getTableMetadata(SESSION, tableHandle); assertEquals(tableMetadata.getTable(), new SchemaTableName("example", "numbers")); assertEquals(tableMetadata.getColumns(), ImmutableList.of( - new ColumnMetadata("text", VARCHAR), + new ColumnMetadata("text", VARCHAR, false, null, null, false, emptyMap()), // primary key is not null in H2 new ColumnMetadata("text_short", createVarcharType(32)), new ColumnMetadata("value", BIGINT))); @@ -119,7 +120,7 @@ public void getTableMetadata() ConnectorTableMetadata specialTableMetadata = metadata.getTableMetadata(SESSION, specialTableHandle); assertEquals(specialTableMetadata.getTable(), new SchemaTableName("exa_ple", "num_ers")); assertEquals(specialTableMetadata.getColumns(), ImmutableList.of( - new ColumnMetadata("te_t", VARCHAR), + new ColumnMetadata("te_t", VARCHAR, false, null, null, false, emptyMap()), // primary key is not null in H2 new ColumnMetadata("va%ue", BIGINT))); // unknown tables should produce null @@ -171,19 +172,42 @@ public void testListTables() public void getColumnMetadata() { assertEquals( - metadata.getColumnMetadata(SESSION, tableHandle, new JdbcColumnHandle("text", JDBC_VARCHAR, VARCHAR)), + metadata.getColumnMetadata(SESSION, tableHandle, new JdbcColumnHandle("text", JDBC_VARCHAR, VARCHAR, true)), new ColumnMetadata("text", VARCHAR)); } - @Test(expectedExceptions = PrestoException.class) - public void testCreateTable() + @Test + public void testCreateAndAlterTable() { - metadata.createTable( - SESSION, - new ConnectorTableMetadata( - new SchemaTableName("example", "foo"), - ImmutableList.of(new ColumnMetadata("text", VARCHAR))), - false); + SchemaTableName table = new SchemaTableName("example", "foo"); + metadata.createTable(SESSION, new ConnectorTableMetadata(table, ImmutableList.of(new ColumnMetadata("text", VARCHAR))), false); + + JdbcTableHandle handle = metadata.getTableHandle(SESSION, table); + + ConnectorTableMetadata layout = metadata.getTableMetadata(SESSION, handle); + assertEquals(layout.getTable(), table); + assertEquals(layout.getColumns().size(), 1); + assertEquals(layout.getColumns().get(0), new ColumnMetadata("text", VARCHAR)); + + metadata.addColumn(SESSION, handle, new ColumnMetadata("x", VARCHAR)); + layout = metadata.getTableMetadata(SESSION, handle); + assertEquals(layout.getColumns().size(), 2); + assertEquals(layout.getColumns().get(0), new ColumnMetadata("text", VARCHAR)); + assertEquals(layout.getColumns().get(1), new ColumnMetadata("x", VARCHAR)); + + JdbcColumnHandle columnHandle = new JdbcColumnHandle("x", JDBC_VARCHAR, VARCHAR, true); + metadata.dropColumn(SESSION, handle, columnHandle); + layout = metadata.getTableMetadata(SESSION, handle); + assertEquals(layout.getColumns().size(), 1); + assertEquals(layout.getColumns().get(0), new ColumnMetadata("text", VARCHAR)); + + SchemaTableName newTableName = new SchemaTableName("example", "bar"); + metadata.renameTable(SESSION, handle, newTableName); + handle = metadata.getTableHandle(SESSION, newTableName); + layout = metadata.getTableMetadata(SESSION, handle); + assertEquals(layout.getTable(), newTableName); + assertEquals(layout.getColumns().size(), 1); + assertEquals(layout.getColumns().get(0), new ColumnMetadata("text", VARCHAR)); } @Test diff --git a/presto-base-jdbc/src/test/java/io/prestosql/plugin/jdbc/TestJdbcQueryBuilder.java b/presto-base-jdbc/src/test/java/io/prestosql/plugin/jdbc/TestJdbcQueryBuilder.java index a58b3e64c0b89..38594ac909d43 100644 --- a/presto-base-jdbc/src/test/java/io/prestosql/plugin/jdbc/TestJdbcQueryBuilder.java +++ b/presto-base-jdbc/src/test/java/io/prestosql/plugin/jdbc/TestJdbcQueryBuilder.java @@ -87,18 +87,18 @@ public void setup() CharType charType = CharType.createCharType(0); columns = ImmutableList.of( - new JdbcColumnHandle("col_0", JDBC_BIGINT, BIGINT), - new JdbcColumnHandle("col_1", JDBC_DOUBLE, DOUBLE), - new JdbcColumnHandle("col_2", JDBC_BOOLEAN, BOOLEAN), - new JdbcColumnHandle("col_3", JDBC_VARCHAR, VARCHAR), - new JdbcColumnHandle("col_4", JDBC_DATE, DATE), - new JdbcColumnHandle("col_5", JDBC_TIME, TIME), - new JdbcColumnHandle("col_6", JDBC_TIMESTAMP, TIMESTAMP), - new JdbcColumnHandle("col_7", JDBC_TINYINT, TINYINT), - new JdbcColumnHandle("col_8", JDBC_SMALLINT, SMALLINT), - new JdbcColumnHandle("col_9", JDBC_INTEGER, INTEGER), - new JdbcColumnHandle("col_10", JDBC_REAL, REAL), - new JdbcColumnHandle("col_11", JDBC_CHAR, charType)); + new JdbcColumnHandle("col_0", JDBC_BIGINT, BIGINT, true), + new JdbcColumnHandle("col_1", JDBC_DOUBLE, DOUBLE, true), + new JdbcColumnHandle("col_2", JDBC_BOOLEAN, BOOLEAN, true), + new JdbcColumnHandle("col_3", JDBC_VARCHAR, VARCHAR, true), + new JdbcColumnHandle("col_4", JDBC_DATE, DATE, true), + new JdbcColumnHandle("col_5", JDBC_TIME, TIME, true), + new JdbcColumnHandle("col_6", JDBC_TIMESTAMP, TIMESTAMP, true), + new JdbcColumnHandle("col_7", JDBC_TINYINT, TINYINT, true), + new JdbcColumnHandle("col_8", JDBC_SMALLINT, SMALLINT, true), + new JdbcColumnHandle("col_9", JDBC_INTEGER, INTEGER, true), + new JdbcColumnHandle("col_10", JDBC_REAL, REAL, true), + new JdbcColumnHandle("col_11", JDBC_CHAR, charType, true)); Connection connection = database.getConnection(); try (PreparedStatement preparedStatement = connection.prepareStatement("create table \"test_table\" (" + "" + diff --git a/presto-base-jdbc/src/test/java/io/prestosql/plugin/jdbc/TestJdbcRecordSet.java b/presto-base-jdbc/src/test/java/io/prestosql/plugin/jdbc/TestJdbcRecordSet.java index 3c86d48dab8ca..8547432c17a0f 100644 --- a/presto-base-jdbc/src/test/java/io/prestosql/plugin/jdbc/TestJdbcRecordSet.java +++ b/presto-base-jdbc/src/test/java/io/prestosql/plugin/jdbc/TestJdbcRecordSet.java @@ -65,20 +65,20 @@ public void tearDown() public void testGetColumnTypes() { RecordSet recordSet = new JdbcRecordSet(jdbcClient, session, split, ImmutableList.of( - new JdbcColumnHandle("text", JDBC_VARCHAR, VARCHAR), - new JdbcColumnHandle("text_short", JDBC_VARCHAR, createVarcharType(32)), - new JdbcColumnHandle("value", JDBC_BIGINT, BIGINT))); + new JdbcColumnHandle("text", JDBC_VARCHAR, VARCHAR, true), + new JdbcColumnHandle("text_short", JDBC_VARCHAR, createVarcharType(32), true), + new JdbcColumnHandle("value", JDBC_BIGINT, BIGINT, true))); assertEquals(recordSet.getColumnTypes(), ImmutableList.of(VARCHAR, createVarcharType(32), BIGINT)); recordSet = new JdbcRecordSet(jdbcClient, session, split, ImmutableList.of( - new JdbcColumnHandle("value", JDBC_BIGINT, BIGINT), - new JdbcColumnHandle("text", JDBC_VARCHAR, VARCHAR))); + new JdbcColumnHandle("value", JDBC_BIGINT, BIGINT, true), + new JdbcColumnHandle("text", JDBC_VARCHAR, VARCHAR, true))); assertEquals(recordSet.getColumnTypes(), ImmutableList.of(BIGINT, VARCHAR)); recordSet = new JdbcRecordSet(jdbcClient, session, split, ImmutableList.of( - new JdbcColumnHandle("value", JDBC_BIGINT, BIGINT), - new JdbcColumnHandle("value", JDBC_BIGINT, BIGINT), - new JdbcColumnHandle("text", JDBC_VARCHAR, VARCHAR))); + new JdbcColumnHandle("value", JDBC_BIGINT, BIGINT, true), + new JdbcColumnHandle("value", JDBC_BIGINT, BIGINT, true), + new JdbcColumnHandle("text", JDBC_VARCHAR, VARCHAR, true))); assertEquals(recordSet.getColumnTypes(), ImmutableList.of(BIGINT, BIGINT, VARCHAR)); recordSet = new JdbcRecordSet(jdbcClient, session, split, ImmutableList.of()); diff --git a/presto-main/src/main/java/io/prestosql/execution/AddColumnTask.java b/presto-main/src/main/java/io/prestosql/execution/AddColumnTask.java index 99140999d2e58..3035d14c76034 100644 --- a/presto-main/src/main/java/io/prestosql/execution/AddColumnTask.java +++ b/presto-main/src/main/java/io/prestosql/execution/AddColumnTask.java @@ -37,10 +37,12 @@ import static com.google.common.util.concurrent.Futures.immediateFuture; import static io.prestosql.metadata.MetadataUtil.createQualifiedObjectName; import static io.prestosql.spi.StandardErrorCode.NOT_FOUND; +import static io.prestosql.spi.connector.ConnectorCapabilities.NOT_NULL_COLUMN_CONSTRAINT; import static io.prestosql.spi.type.TypeSignature.parseTypeSignature; import static io.prestosql.sql.NodeUtils.mapFromProperties; import static io.prestosql.sql.analyzer.SemanticErrorCode.COLUMN_ALREADY_EXISTS; import static io.prestosql.sql.analyzer.SemanticErrorCode.MISSING_TABLE; +import static io.prestosql.sql.analyzer.SemanticErrorCode.NOT_SUPPORTED; import static io.prestosql.sql.analyzer.SemanticErrorCode.TYPE_MISMATCH; import static io.prestosql.type.UnknownType.UNKNOWN; import static java.util.Locale.ENGLISH; @@ -85,6 +87,9 @@ public ListenableFuture execute(AddColumn statement, TransactionManager trans if (columnHandles.containsKey(element.getName().getValue().toLowerCase(ENGLISH))) { throw new SemanticException(COLUMN_ALREADY_EXISTS, statement, "Column '%s' already exists", element.getName()); } + if (!element.isNullable() && !metadata.getConnectorCapabilities(session, connectorId).contains(NOT_NULL_COLUMN_CONSTRAINT)) { + throw new SemanticException(NOT_SUPPORTED, element, "Catalog '%s' does not support NOT NULL for column '%s'", connectorId.getCatalogName(), element.getName()); + } Map sqlProperties = mapFromProperties(element.getProperties()); Map columnProperties = metadata.getColumnPropertyManager().getProperties( @@ -95,7 +100,13 @@ public ListenableFuture execute(AddColumn statement, TransactionManager trans metadata, parameters); - ColumnMetadata column = new ColumnMetadata(element.getName().getValue(), type, element.getComment().orElse(null), null, false, columnProperties); + ColumnMetadata column = new ColumnMetadata( + element.getName().getValue(), + type, + element.isNullable(), element.getComment().orElse(null), + null, + false, + columnProperties); metadata.addColumn(session, tableHandle.get(), column); diff --git a/presto-main/src/main/java/io/prestosql/execution/CreateTableTask.java b/presto-main/src/main/java/io/prestosql/execution/CreateTableTask.java index a1ed0945c0216..c7871748ceb04 100644 --- a/presto-main/src/main/java/io/prestosql/execution/CreateTableTask.java +++ b/presto-main/src/main/java/io/prestosql/execution/CreateTableTask.java @@ -50,6 +50,7 @@ import static io.prestosql.spi.StandardErrorCode.ALREADY_EXISTS; import static io.prestosql.spi.StandardErrorCode.GENERIC_INTERNAL_ERROR; import static io.prestosql.spi.StandardErrorCode.NOT_FOUND; +import static io.prestosql.spi.connector.ConnectorCapabilities.NOT_NULL_COLUMN_CONSTRAINT; import static io.prestosql.spi.type.TypeSignature.parseTypeSignature; import static io.prestosql.sql.NodeUtils.mapFromProperties; import static io.prestosql.sql.analyzer.SemanticErrorCode.DUPLICATE_COLUMN_NAME; @@ -118,6 +119,9 @@ public ListenableFuture internalExecute(CreateTable statement, Metadata metad if (columns.containsKey(name)) { throw new SemanticException(DUPLICATE_COLUMN_NAME, column, "Column name '%s' specified more than once", column.getName()); } + if (!column.isNullable() && !metadata.getConnectorCapabilities(session, connectorId).contains(NOT_NULL_COLUMN_CONSTRAINT)) { + throw new SemanticException(NOT_SUPPORTED, column, "Catalog '%s' does not support non-null column for column name '%s'", connectorId.getCatalogName(), column.getName()); + } Map sqlProperties = mapFromProperties(column.getProperties()); Map columnProperties = metadata.getColumnPropertyManager().getProperties( @@ -128,7 +132,13 @@ public ListenableFuture internalExecute(CreateTable statement, Metadata metad metadata, parameters); - columns.put(name, new ColumnMetadata(name, type, column.getComment().orElse(null), null, false, columnProperties)); + columns.put(name, new ColumnMetadata( + name, + type, + column.isNullable(), column.getComment().orElse(null), + null, + false, + columnProperties)); } else if (element instanceof LikeClause) { LikeClause likeClause = (LikeClause) element; diff --git a/presto-main/src/main/java/io/prestosql/metadata/CatalogMetadata.java b/presto-main/src/main/java/io/prestosql/metadata/CatalogMetadata.java index 8fdce46e8e875..f2bbb5640488f 100644 --- a/presto-main/src/main/java/io/prestosql/metadata/CatalogMetadata.java +++ b/presto-main/src/main/java/io/prestosql/metadata/CatalogMetadata.java @@ -16,12 +16,15 @@ import com.google.common.collect.ImmutableList; import io.prestosql.Session; import io.prestosql.connector.ConnectorId; +import io.prestosql.spi.connector.ConnectorCapabilities; import io.prestosql.spi.connector.ConnectorMetadata; import io.prestosql.spi.connector.ConnectorTransactionHandle; import java.util.List; +import java.util.Set; import static com.google.common.base.MoreObjects.toStringHelper; +import static com.google.common.collect.Sets.immutableEnumSet; import static java.util.Objects.requireNonNull; public class CatalogMetadata @@ -39,6 +42,7 @@ public class CatalogMetadata private final ConnectorId systemTablesId; private final ConnectorMetadata systemTables; private final ConnectorTransactionHandle systemTablesTransactionHandle; + private final Set connectorCapabilities; public CatalogMetadata( ConnectorId connectorId, @@ -49,7 +53,8 @@ public CatalogMetadata( ConnectorTransactionHandle informationSchemaTransactionHandle, ConnectorId systemTablesId, ConnectorMetadata systemTables, - ConnectorTransactionHandle systemTablesTransactionHandle) + ConnectorTransactionHandle systemTablesTransactionHandle, + Set connectorCapabilities) { this.connectorId = requireNonNull(connectorId, "connectorId is null"); this.metadata = requireNonNull(metadata, "metadata is null"); @@ -60,6 +65,7 @@ public CatalogMetadata( this.systemTablesId = requireNonNull(systemTablesId, "systemTablesId is null"); this.systemTables = requireNonNull(systemTables, "systemTables is null"); this.systemTablesTransactionHandle = requireNonNull(systemTablesTransactionHandle, "systemTablesTransactionHandle is null"); + this.connectorCapabilities = immutableEnumSet(requireNonNull(connectorCapabilities, "connectorCapabilities is null")); } public ConnectorId getConnectorId() @@ -118,6 +124,11 @@ public List listConnectorIds() return ImmutableList.of(informationSchemaId, systemTablesId, connectorId); } + public Set getConnectorCapabilities() + { + return connectorCapabilities; + } + @Override public String toString() { diff --git a/presto-main/src/main/java/io/prestosql/metadata/Metadata.java b/presto-main/src/main/java/io/prestosql/metadata/Metadata.java index 4083461483168..f7c67f4381728 100644 --- a/presto-main/src/main/java/io/prestosql/metadata/Metadata.java +++ b/presto-main/src/main/java/io/prestosql/metadata/Metadata.java @@ -21,6 +21,7 @@ import io.prestosql.spi.connector.CatalogSchemaName; import io.prestosql.spi.connector.ColumnHandle; import io.prestosql.spi.connector.ColumnMetadata; +import io.prestosql.spi.connector.ConnectorCapabilities; import io.prestosql.spi.connector.ConnectorOutputMetadata; import io.prestosql.spi.connector.ConnectorTableMetadata; import io.prestosql.spi.connector.Constraint; @@ -382,6 +383,8 @@ public interface Metadata AnalyzePropertyManager getAnalyzePropertyManager(); + Set getConnectorCapabilities(Session session, ConnectorId catalogName); + @Deprecated boolean usesLegacyTableLayouts(Session session, TableHandle table); } diff --git a/presto-main/src/main/java/io/prestosql/metadata/MetadataManager.java b/presto-main/src/main/java/io/prestosql/metadata/MetadataManager.java index 79662f0e0f7ce..504f85f18671d 100644 --- a/presto-main/src/main/java/io/prestosql/metadata/MetadataManager.java +++ b/presto-main/src/main/java/io/prestosql/metadata/MetadataManager.java @@ -33,6 +33,7 @@ import io.prestosql.spi.connector.CatalogSchemaName; import io.prestosql.spi.connector.ColumnHandle; import io.prestosql.spi.connector.ColumnMetadata; +import io.prestosql.spi.connector.ConnectorCapabilities; import io.prestosql.spi.connector.ConnectorInsertTableHandle; import io.prestosql.spi.connector.ConnectorMetadata; import io.prestosql.spi.connector.ConnectorOutputMetadata; @@ -1164,6 +1165,12 @@ public AnalyzePropertyManager getAnalyzePropertyManager() return analyzePropertyManager; } + @Override + public Set getConnectorCapabilities(Session session, ConnectorId connectorId) + { + return getCatalogMetadata(session, connectorId).getConnectorCapabilities(); + } + @Override public boolean usesLegacyTableLayouts(Session session, TableHandle table) { diff --git a/presto-main/src/main/java/io/prestosql/sql/rewrite/ShowQueriesRewrite.java b/presto-main/src/main/java/io/prestosql/sql/rewrite/ShowQueriesRewrite.java index d35b381c8ca95..42755cc2dee79 100644 --- a/presto-main/src/main/java/io/prestosql/sql/rewrite/ShowQueriesRewrite.java +++ b/presto-main/src/main/java/io/prestosql/sql/rewrite/ShowQueriesRewrite.java @@ -457,7 +457,7 @@ protected Node visitShowCreate(ShowCreate node, Void context) .filter(column -> !column.isHidden()) .map(column -> { List propertyNodes = buildProperties(objectName, Optional.of(column.getName()), INVALID_COLUMN_PROPERTY, column.getProperties(), allColumnProperties); - return new ColumnDefinition(new Identifier(column.getName()), column.getType().getDisplayName(), propertyNodes, Optional.ofNullable(column.getComment())); + return new ColumnDefinition(new Identifier(column.getName()), column.getType().getDisplayName(), column.isNullable(), propertyNodes, Optional.ofNullable(column.getComment())); }) .collect(toImmutableList()); diff --git a/presto-main/src/main/java/io/prestosql/transaction/InMemoryTransactionManager.java b/presto-main/src/main/java/io/prestosql/transaction/InMemoryTransactionManager.java index 51d4165b4a9fd..7423ef45a4a58 100644 --- a/presto-main/src/main/java/io/prestosql/transaction/InMemoryTransactionManager.java +++ b/presto-main/src/main/java/io/prestosql/transaction/InMemoryTransactionManager.java @@ -412,15 +412,23 @@ private synchronized CatalogMetadata getTransactionCatalogMetadata(ConnectorId c if (catalogMetadata == null) { Catalog catalog = catalogsByConnectorId.get(connectorId); verify(catalog != null, "Unknown connectorId: %s", connectorId); + Connector connector = catalog.getConnector(connectorId); ConnectorTransactionMetadata metadata = createConnectorTransactionMetadata(catalog.getConnectorId(), catalog); ConnectorTransactionMetadata informationSchema = createConnectorTransactionMetadata(catalog.getInformationSchemaId(), catalog); ConnectorTransactionMetadata systemTables = createConnectorTransactionMetadata(catalog.getSystemTablesId(), catalog); catalogMetadata = new CatalogMetadata( - metadata.getConnectorId(), metadata.getConnectorMetadata(), metadata.getTransactionHandle(), - informationSchema.getConnectorId(), informationSchema.getConnectorMetadata(), informationSchema.getTransactionHandle(), - systemTables.getConnectorId(), systemTables.getConnectorMetadata(), systemTables.getTransactionHandle()); + metadata.getConnectorId(), + metadata.getConnectorMetadata(), + metadata.getTransactionHandle(), + informationSchema.getConnectorId(), + informationSchema.getConnectorMetadata(), + informationSchema.getTransactionHandle(), + systemTables.getConnectorId(), + systemTables.getConnectorMetadata(), + systemTables.getTransactionHandle(), + connector.getCapabilities()); this.catalogMetadata.put(catalog.getConnectorId(), catalogMetadata); this.catalogMetadata.put(catalog.getInformationSchemaId(), catalogMetadata); diff --git a/presto-main/src/test/java/io/prestosql/execution/TestCreateTableTask.java b/presto-main/src/test/java/io/prestosql/execution/TestCreateTableTask.java index 84c6c583f0df1..cac1d493f9a31 100644 --- a/presto-main/src/test/java/io/prestosql/execution/TestCreateTableTask.java +++ b/presto-main/src/test/java/io/prestosql/execution/TestCreateTableTask.java @@ -27,31 +27,42 @@ import io.prestosql.security.AllowAllAccessControl; import io.prestosql.spi.PrestoException; import io.prestosql.spi.connector.ColumnHandle; +import io.prestosql.spi.connector.ColumnMetadata; +import io.prestosql.spi.connector.ConnectorCapabilities; import io.prestosql.spi.connector.ConnectorTableMetadata; import io.prestosql.spi.type.Type; import io.prestosql.spi.type.TypeManager; import io.prestosql.spi.type.TypeSignature; +import io.prestosql.sql.analyzer.SemanticException; import io.prestosql.sql.tree.ColumnDefinition; import io.prestosql.sql.tree.CreateTable; import io.prestosql.sql.tree.QualifiedName; +import io.prestosql.sql.tree.TableElement; import io.prestosql.transaction.TransactionManager; import io.prestosql.type.TypeRegistry; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; +import java.util.List; import java.util.Optional; -import java.util.concurrent.atomic.AtomicInteger; +import java.util.Set; +import java.util.concurrent.CopyOnWriteArrayList; +import static com.google.common.collect.Sets.immutableEnumSet; import static io.airlift.concurrent.MoreFutures.getFutureValue; import static io.prestosql.spi.StandardErrorCode.ALREADY_EXISTS; +import static io.prestosql.spi.connector.ConnectorCapabilities.NOT_NULL_COLUMN_CONSTRAINT; import static io.prestosql.spi.session.PropertyMetadata.stringProperty; import static io.prestosql.sql.QueryUtil.identifier; import static io.prestosql.testing.TestingSession.createBogusTestingCatalog; import static io.prestosql.testing.TestingSession.testSessionBuilder; import static io.prestosql.transaction.InMemoryTransactionManager.createTestTransactionManager; import static java.util.Collections.emptyList; +import static java.util.Collections.emptySet; +import static java.util.Locale.ENGLISH; import static java.util.Objects.requireNonNull; import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertFalse; import static org.testng.Assert.assertTrue; import static org.testng.Assert.fail; @@ -87,14 +98,15 @@ public void setUp() metadata = new MockMetadata(typeManager, tablePropertyManager, columnPropertyManager, - testCatalog.getConnectorId()); + testCatalog.getConnectorId(), + emptySet()); } @Test public void testCreateTableNotExistsTrue() { CreateTable statement = new CreateTable(QualifiedName.of("test_table"), - ImmutableList.of(new ColumnDefinition(identifier("a"), "BIGINT", emptyList(), Optional.empty())), + ImmutableList.of(new ColumnDefinition(identifier("a"), "BIGINT", true, emptyList(), Optional.empty())), true, ImmutableList.of(), Optional.empty()); @@ -107,7 +119,7 @@ public void testCreateTableNotExistsTrue() public void testCreateTableNotExistsFalse() { CreateTable statement = new CreateTable(QualifiedName.of("test_table"), - ImmutableList.of(new ColumnDefinition(identifier("a"), "BIGINT", emptyList(), Optional.empty())), + ImmutableList.of(new ColumnDefinition(identifier("a"), "BIGINT", true, emptyList(), Optional.empty())), false, ImmutableList.of(), Optional.empty()); @@ -125,6 +137,51 @@ public void testCreateTableNotExistsFalse() assertEquals(metadata.getCreateTableCallCount(), 1); } + @Test + public void testCreateWithNotNullColumns() + { + metadata.setConnectorCapabilities(NOT_NULL_COLUMN_CONSTRAINT); + List inputColumns = ImmutableList.of( + new ColumnDefinition(identifier("a"), "DATE", true, emptyList(), Optional.empty()), + new ColumnDefinition(identifier("b"), "VARCHAR", false, emptyList(), Optional.empty()), + new ColumnDefinition(identifier("c"), "VARBINARY", false, emptyList(), Optional.empty())); + CreateTable statement = new CreateTable(QualifiedName.of("test_table"), inputColumns, true, ImmutableList.of(), Optional.empty()); + + getFutureValue(new CreateTableTask().internalExecute(statement, metadata, new AllowAllAccessControl(), testSession, emptyList())); + assertEquals(metadata.getCreateTableCallCount(), 1); + List columns = metadata.getReceivedTableMetadata().get(0).getColumns(); + assertEquals(columns.size(), 3); + + assertEquals(columns.get(0).getName(), "a"); + assertEquals(columns.get(0).getType().getDisplayName().toUpperCase(ENGLISH), "DATE"); + assertTrue(columns.get(0).isNullable()); + + assertEquals(columns.get(1).getName(), "b"); + assertEquals(columns.get(1).getType().getDisplayName().toUpperCase(ENGLISH), "VARCHAR"); + assertFalse(columns.get(1).isNullable()); + + assertEquals(columns.get(2).getName(), "c"); + assertEquals(columns.get(2).getType().getDisplayName().toUpperCase(ENGLISH), "VARBINARY"); + assertFalse(columns.get(2).isNullable()); + } + + @Test(expectedExceptions = SemanticException.class, expectedExceptionsMessageRegExp = ".*does not support non-null column for column name 'b'") + public void testCreateWithUnsupportedConnectorThrowsWhenNotNull() + { + List inputColumns = ImmutableList.of( + new ColumnDefinition(identifier("a"), "DATE", true, emptyList(), Optional.empty()), + new ColumnDefinition(identifier("b"), "VARCHAR", false, emptyList(), Optional.empty()), + new ColumnDefinition(identifier("c"), "VARBINARY", false, emptyList(), Optional.empty())); + CreateTable statement = new CreateTable( + QualifiedName.of("test_table"), + inputColumns, + true, + ImmutableList.of(), + Optional.empty()); + + getFutureValue(new CreateTableTask().internalExecute(statement, metadata, new AllowAllAccessControl(), testSession, emptyList())); + } + private static class MockMetadata extends AbstractMockMetadata { @@ -132,24 +189,27 @@ private static class MockMetadata private final TablePropertyManager tablePropertyManager; private final ColumnPropertyManager columnPropertyManager; private final ConnectorId catalogHandle; - private AtomicInteger createTableCallCount = new AtomicInteger(); + private final List tables = new CopyOnWriteArrayList<>(); + private Set connectorCapabilities; public MockMetadata( TypeManager typeManager, TablePropertyManager tablePropertyManager, ColumnPropertyManager columnPropertyManager, - ConnectorId catalogHandle) + ConnectorId catalogHandle, + Set connectorCapabilities) { this.typeManager = requireNonNull(typeManager, "typeManager is null"); this.tablePropertyManager = requireNonNull(tablePropertyManager, "tablePropertyManager is null"); this.columnPropertyManager = requireNonNull(columnPropertyManager, "columnPropertyManager is null"); this.catalogHandle = requireNonNull(catalogHandle, "catalogHandle is null"); + this.connectorCapabilities = requireNonNull(immutableEnumSet(connectorCapabilities), "connectorCapabilities is null"); } @Override public void createTable(Session session, String catalogName, ConnectorTableMetadata tableMetadata, boolean ignoreExisting) { - createTableCallCount.incrementAndGet(); + tables.add(tableMetadata); if (!ignoreExisting) { throw new PrestoException(ALREADY_EXISTS, "Table already exists"); } @@ -190,7 +250,12 @@ public Optional getTableHandle(Session session, QualifiedObjectName public int getCreateTableCallCount() { - return createTableCallCount.get(); + return tables.size(); + } + + public List getReceivedTableMetadata() + { + return tables; } @Override @@ -198,5 +263,16 @@ public void dropColumn(Session session, TableHandle tableHandle, ColumnHandle co { throw new UnsupportedOperationException(); } + + @Override + public Set getConnectorCapabilities(Session session, ConnectorId catalogName) + { + return connectorCapabilities; + } + + public void setConnectorCapabilities(ConnectorCapabilities... connectorCapabilities) + { + this.connectorCapabilities = immutableEnumSet(ImmutableList.copyOf(connectorCapabilities)); + } } } diff --git a/presto-main/src/test/java/io/prestosql/metadata/AbstractMockMetadata.java b/presto-main/src/test/java/io/prestosql/metadata/AbstractMockMetadata.java index 433ed0119c760..70b094aad5d01 100644 --- a/presto-main/src/test/java/io/prestosql/metadata/AbstractMockMetadata.java +++ b/presto-main/src/test/java/io/prestosql/metadata/AbstractMockMetadata.java @@ -20,6 +20,7 @@ import io.prestosql.spi.connector.CatalogSchemaName; import io.prestosql.spi.connector.ColumnHandle; import io.prestosql.spi.connector.ColumnMetadata; +import io.prestosql.spi.connector.ConnectorCapabilities; import io.prestosql.spi.connector.ConnectorOutputMetadata; import io.prestosql.spi.connector.ConnectorTableMetadata; import io.prestosql.spi.connector.Constraint; @@ -509,6 +510,12 @@ public boolean catalogExists(Session session, String catalogName) throw new UnsupportedOperationException(); } + @Override + public Set getConnectorCapabilities(Session session, ConnectorId catalogName) + { + throw new UnsupportedOperationException(); + } + @Override public boolean usesLegacyTableLayouts(Session session, TableHandle table) { diff --git a/presto-mysql/pom.xml b/presto-mysql/pom.xml index 89c75cb55d792..c2f5177797e2e 100644 --- a/presto-mysql/pom.xml +++ b/presto-mysql/pom.xml @@ -125,6 +125,12 @@ testing-mysql-server test + + + org.jetbrains + annotations + test + diff --git a/presto-mysql/src/main/java/io/prestosql/plugin/mysql/MySqlClient.java b/presto-mysql/src/main/java/io/prestosql/plugin/mysql/MySqlClient.java index 057b1277a9dff..c535a388fd784 100644 --- a/presto-mysql/src/main/java/io/prestosql/plugin/mysql/MySqlClient.java +++ b/presto-mysql/src/main/java/io/prestosql/plugin/mysql/MySqlClient.java @@ -20,10 +20,13 @@ import io.prestosql.plugin.jdbc.BaseJdbcConfig; import io.prestosql.plugin.jdbc.ConnectionFactory; import io.prestosql.plugin.jdbc.DriverConnectionFactory; +import io.prestosql.plugin.jdbc.JdbcColumnHandle; import io.prestosql.plugin.jdbc.JdbcIdentity; +import io.prestosql.plugin.jdbc.JdbcTableHandle; import io.prestosql.plugin.jdbc.WriteMapping; import io.prestosql.spi.PrestoException; import io.prestosql.spi.connector.ConnectorSession; +import io.prestosql.spi.connector.ConnectorTableMetadata; import io.prestosql.spi.connector.SchemaTableName; import io.prestosql.spi.type.Type; import io.prestosql.spi.type.VarcharType; @@ -40,11 +43,15 @@ import java.util.Set; import static com.google.common.util.concurrent.MoreExecutors.directExecutor; +import static com.mysql.jdbc.SQLError.SQL_STATE_ER_TABLE_EXISTS_ERROR; +import static com.mysql.jdbc.SQLError.SQL_STATE_SYNTAX_ERROR; import static io.prestosql.plugin.jdbc.DriverConnectionFactory.basicConnectionProperties; +import static io.prestosql.plugin.jdbc.JdbcErrorCode.JDBC_ERROR; import static io.prestosql.plugin.jdbc.StandardColumnMappings.realWriteFunction; import static io.prestosql.plugin.jdbc.StandardColumnMappings.timestampWriteFunction; import static io.prestosql.plugin.jdbc.StandardColumnMappings.varbinaryWriteFunction; import static io.prestosql.plugin.jdbc.StandardColumnMappings.varcharWriteFunction; +import static io.prestosql.spi.StandardErrorCode.ALREADY_EXISTS; import static io.prestosql.spi.StandardErrorCode.NOT_SUPPORTED; import static io.prestosql.spi.type.RealType.REAL; import static io.prestosql.spi.type.TimeWithTimeZoneType.TIME_WITH_TIME_ZONE; @@ -52,6 +59,7 @@ import static io.prestosql.spi.type.TimestampWithTimeZoneType.TIMESTAMP_WITH_TIME_ZONE; import static io.prestosql.spi.type.VarbinaryType.VARBINARY; import static io.prestosql.spi.type.Varchars.isVarcharType; +import static java.lang.String.format; import static java.util.Locale.ENGLISH; public class MySqlClient @@ -187,4 +195,48 @@ else if (varcharType.getBoundedLength() <= 16777215) { return super.toWriteMapping(session, type); } + + @Override + public void createTable(ConnectorSession session, ConnectorTableMetadata tableMetadata) + { + try { + createTable(session, tableMetadata, tableMetadata.getTable().getTableName()); + } + catch (SQLException e) { + boolean exists = SQL_STATE_ER_TABLE_EXISTS_ERROR.equals(e.getSQLState()); + throw new PrestoException(exists ? ALREADY_EXISTS : JDBC_ERROR, e); + } + } + + @Override + public void renameColumn(JdbcIdentity identity, JdbcTableHandle handle, JdbcColumnHandle jdbcColumn, String newColumnName) + { + try (Connection connection = connectionFactory.openConnection(identity)) { + DatabaseMetaData metadata = connection.getMetaData(); + if (metadata.storesUpperCaseIdentifiers()) { + newColumnName = newColumnName.toUpperCase(ENGLISH); + } + String sql = format( + "ALTER TABLE %s RENAME COLUMN %s TO %s", + quoted(handle.getCatalogName(), handle.getSchemaName(), handle.getTableName()), + quoted(jdbcColumn.getColumnName()), + quoted(newColumnName)); + execute(connection, sql); + } + catch (SQLException e) { + // MySQL versions earlier than 8 do not support the above RENAME COLUMN syntax + if (SQL_STATE_SYNTAX_ERROR.equals(e.getSQLState())) { + throw new PrestoException(NOT_SUPPORTED, format("Rename column not supported in catalog: '%s'", handle.getCatalogName()), e); + } + throw new PrestoException(JDBC_ERROR, e); + } + } + + @Override + public void renameTable(JdbcIdentity identity, JdbcTableHandle handle, SchemaTableName newTableName) + { + // MySQL doesn't support specifying the catalog name in a rename. By setting the + // catalogName parameter to null, it will be omitted in the ALTER TABLE statement. + renameTable(identity, null, handle.getSchemaName(), handle.getTableName(), newTableName); + } } diff --git a/presto-mysql/src/test/java/io/prestosql/plugin/mysql/TestMySqlDistributedQueries.java b/presto-mysql/src/test/java/io/prestosql/plugin/mysql/TestMySqlDistributedQueries.java index 571a6b20399cc..ba389834960d3 100644 --- a/presto-mysql/src/test/java/io/prestosql/plugin/mysql/TestMySqlDistributedQueries.java +++ b/presto-mysql/src/test/java/io/prestosql/plugin/mysql/TestMySqlDistributedQueries.java @@ -16,7 +16,7 @@ import io.airlift.testing.mysql.TestingMySqlServer; import io.airlift.tpch.TpchTable; import io.prestosql.testing.MaterializedResult; -import io.prestosql.tests.AbstractTestQueries; +import io.prestosql.tests.AbstractTestDistributedQueries; import org.testng.annotations.AfterClass; import org.testng.annotations.Test; @@ -27,7 +27,7 @@ @Test public class TestMySqlDistributedQueries - extends AbstractTestQueries + extends AbstractTestDistributedQueries { private final TestingMySqlServer mysqlServer; @@ -49,6 +49,12 @@ public final void destroy() mysqlServer.close(); } + @Override + protected boolean supportsViews() + { + return false; + } + @Override public void testShowColumns() { @@ -81,5 +87,18 @@ public void testDescribeOutputNamedAndUnnamed() // this connector uses a non-canonical type for varchar columns in tpch } + @Override + public void testInsert() + { + // Test not supported due to lack of support for array types. + // See TestMySqlIntegrationSmokeTest for insertion tests. + } + + @Override + public void testDelete() + { + // delete is not supported + } + // MySQL specific tests should normally go in TestMySqlIntegrationSmokeTest } diff --git a/presto-mysql/src/test/java/io/prestosql/plugin/mysql/TestMySqlIntegrationSmokeTest.java b/presto-mysql/src/test/java/io/prestosql/plugin/mysql/TestMySqlIntegrationSmokeTest.java index aa9be73b3a9db..c7a7f02a3f5e1 100644 --- a/presto-mysql/src/test/java/io/prestosql/plugin/mysql/TestMySqlIntegrationSmokeTest.java +++ b/presto-mysql/src/test/java/io/prestosql/plugin/mysql/TestMySqlIntegrationSmokeTest.java @@ -18,6 +18,7 @@ import io.prestosql.testing.MaterializedResult; import io.prestosql.testing.MaterializedRow; import io.prestosql.tests.AbstractTestIntegrationSmokeTest; +import org.intellij.lang.annotations.Language; import org.testng.annotations.AfterClass; import org.testng.annotations.Test; @@ -32,6 +33,7 @@ import static io.prestosql.spi.type.VarcharType.VARCHAR; import static io.prestosql.testing.TestingSession.testSessionBuilder; import static io.prestosql.testing.assertions.Assert.assertEquals; +import static java.lang.String.format; import static org.testng.Assert.assertFalse; import static org.testng.Assert.assertTrue; @@ -167,6 +169,44 @@ public void testCharTrailingSpace() assertUpdate("DROP TABLE char_trailing_space"); } + @Test + public void testInsertIntoNotNullColumn() + { + @Language("SQL") String createTableSql = format("" + + "CREATE TABLE %s.tpch.test_insert_not_null (\n" + + " column_a date,\n" + + " column_b date NOT NULL\n" + + ")", + getSession().getCatalog().get()); + assertUpdate(createTableSql); + assertEquals(computeScalar("SHOW CREATE TABLE test_insert_not_null"), createTableSql); + + assertQueryFails("INSERT INTO test_insert_not_null (column_a) VALUES (date '2012-12-31')", "Column 'column_b' cannot be null"); + assertQueryFails("INSERT INTO test_insert_not_null (column_a, column_b) VALUES (date '2012-12-31', null)", "Column 'column_b' cannot be null"); + + assertUpdate("ALTER TABLE test_insert_not_null ADD COLUMN column_c BIGINT NOT NULL"); + + createTableSql = format("" + + "CREATE TABLE %s.tpch.test_insert_not_null (\n" + + " column_a date,\n" + + " column_b date NOT NULL,\n" + + " column_c bigint NOT NULL\n" + + ")", + getSession().getCatalog().get()); + assertEquals(computeScalar("SHOW CREATE TABLE test_insert_not_null"), createTableSql); + + assertQueryFails("INSERT INTO test_insert_not_null (column_b) VALUES (date '2012-12-31')", "Column 'column_c' cannot be null"); + assertQueryFails("INSERT INTO test_insert_not_null (column_b, column_c) VALUES (date '2012-12-31', null)", "Column 'column_c' cannot be null"); + + assertUpdate("INSERT INTO test_insert_not_null (column_b, column_c) VALUES (date '2012-12-31', 1)", 1); + assertUpdate("INSERT INTO test_insert_not_null (column_a, column_b, column_c) VALUES (date '2013-01-01', date '2013-01-02', 2)", 1); + assertQuery( + "SELECT * FROM test_insert_not_null", + "VALUES (NULL, CAST('2012-12-31' AS DATE), 1), (CAST('2013-01-01' AS DATE), CAST('2013-01-02' AS DATE), 2)"); + + assertUpdate("DROP TABLE test_insert_not_null"); + } + private void execute(String sql) throws SQLException { diff --git a/presto-mysql/src/test/java/io/prestosql/plugin/mysql/TestMySqlTypeMapping.java b/presto-mysql/src/test/java/io/prestosql/plugin/mysql/TestMySqlTypeMapping.java index 909310da38fe2..a3f75efa37a36 100644 --- a/presto-mysql/src/test/java/io/prestosql/plugin/mysql/TestMySqlTypeMapping.java +++ b/presto-mysql/src/test/java/io/prestosql/plugin/mysql/TestMySqlTypeMapping.java @@ -117,7 +117,7 @@ public void testMySqlCreatedParameterizedVarchar() .addRoundTrip(stringDataType("mediumtext", createVarcharType(16777215)), "c") .addRoundTrip(stringDataType("longtext", createUnboundedVarcharType()), "d") .addRoundTrip(varcharDataType(32), "e") - .addRoundTrip(varcharDataType(20000), "f") + .addRoundTrip(varcharDataType(15000), "f") .execute(getQueryRunner(), mysqlCreateAndInsert("tpch.mysql_test_parameterized_varchar")); } diff --git a/presto-parser/src/main/antlr4/io/prestosql/sql/parser/SqlBase.g4 b/presto-parser/src/main/antlr4/io/prestosql/sql/parser/SqlBase.g4 index 5e4f5164cc735..df3c8957f6ecf 100644 --- a/presto-parser/src/main/antlr4/io/prestosql/sql/parser/SqlBase.g4 +++ b/presto-parser/src/main/antlr4/io/prestosql/sql/parser/SqlBase.g4 @@ -130,7 +130,7 @@ tableElement ; columnDefinition - : identifier type (COMMENT string)? (WITH properties)? + : identifier type (NOT NULL)? (COMMENT string)? (WITH properties)? ; likeClause diff --git a/presto-parser/src/main/java/io/prestosql/sql/SqlFormatter.java b/presto-parser/src/main/java/io/prestosql/sql/SqlFormatter.java index d123c5c3cd2ee..0e02358450a3e 100644 --- a/presto-parser/src/main/java/io/prestosql/sql/SqlFormatter.java +++ b/presto-parser/src/main/java/io/prestosql/sql/SqlFormatter.java @@ -871,11 +871,16 @@ private static String formatName(QualifiedName name) private String formatColumnDefinition(ColumnDefinition column) { - return formatExpression(column.getName(), parameters) + " " + column.getType() + - column.getComment() - .map(comment -> " COMMENT " + formatStringLiteral(comment)) - .orElse("") + - formatPropertiesSingleLine(column.getProperties()); + StringBuilder sb = new StringBuilder() + .append(formatExpression(column.getName(), parameters)) + .append(" ").append(column.getType()); + if (!column.isNullable()) { + sb.append(" NOT NULL"); + } + column.getComment().ifPresent(comment -> + sb.append(" COMMENT ").append(formatStringLiteral(comment))); + sb.append(formatPropertiesSingleLine(column.getProperties())); + return sb.toString(); } private static String formatGrantor(GrantorSpecification grantor) diff --git a/presto-parser/src/main/java/io/prestosql/sql/parser/AstBuilder.java b/presto-parser/src/main/java/io/prestosql/sql/parser/AstBuilder.java index 969db5f2cda2e..ccf8edd060e55 100644 --- a/presto-parser/src/main/java/io/prestosql/sql/parser/AstBuilder.java +++ b/presto-parser/src/main/java/io/prestosql/sql/parser/AstBuilder.java @@ -1562,10 +1562,13 @@ public Node visitColumnDefinition(SqlBaseParser.ColumnDefinitionContext context) properties = visit(context.properties().property(), Property.class); } + boolean nullable = context.NOT() == null; + return new ColumnDefinition( getLocation(context), (Identifier) visit(context.identifier()), getType(context.type()), + nullable, properties, comment); } diff --git a/presto-parser/src/main/java/io/prestosql/sql/testing/TreeAssertions.java b/presto-parser/src/main/java/io/prestosql/sql/testing/TreeAssertions.java index 61d98cdf49804..0c7804ee963a0 100644 --- a/presto-parser/src/main/java/io/prestosql/sql/testing/TreeAssertions.java +++ b/presto-parser/src/main/java/io/prestosql/sql/testing/TreeAssertions.java @@ -63,9 +63,8 @@ private static Statement parseFormatted(SqlParser sqlParser, ParsingOptions pars return sqlParser.createStatement(sql, parsingOptions); } catch (ParsingException e) { - throw new AssertionError(format( - "failed to parse formatted SQL: %s\nerror: %s\ntree: %s", - sql, e.getMessage(), tree)); + String message = format("failed to parse formatted SQL: %s\nerror: %s\ntree: %s", sql, e.getMessage(), tree); + throw new AssertionError(message, e); } } diff --git a/presto-parser/src/main/java/io/prestosql/sql/tree/ColumnDefinition.java b/presto-parser/src/main/java/io/prestosql/sql/tree/ColumnDefinition.java index 5586be7bbd60b..06cc43f5d9339 100644 --- a/presto-parser/src/main/java/io/prestosql/sql/tree/ColumnDefinition.java +++ b/presto-parser/src/main/java/io/prestosql/sql/tree/ColumnDefinition.java @@ -27,24 +27,26 @@ public final class ColumnDefinition { private final Identifier name; private final String type; + private final boolean nullable; private final List properties; private final Optional comment; - public ColumnDefinition(Identifier name, String type, List properties, Optional comment) + public ColumnDefinition(Identifier name, String type, boolean nullable, List properties, Optional comment) { - this(Optional.empty(), name, type, properties, comment); + this(Optional.empty(), name, type, nullable, properties, comment); } - public ColumnDefinition(NodeLocation location, Identifier name, String type, List properties, Optional comment) + public ColumnDefinition(NodeLocation location, Identifier name, String type, boolean nullable, List properties, Optional comment) { - this(Optional.of(location), name, type, properties, comment); + this(Optional.of(location), name, type, nullable, properties, comment); } - private ColumnDefinition(Optional location, Identifier name, String type, List properties, Optional comment) + private ColumnDefinition(Optional location, Identifier name, String type, boolean nullable, List properties, Optional comment) { super(location); this.name = requireNonNull(name, "name is null"); this.type = requireNonNull(type, "type is null"); + this.nullable = nullable; this.properties = requireNonNull(properties, "properties is null"); this.comment = requireNonNull(comment, "comment is null"); } @@ -59,6 +61,11 @@ public String getType() return type; } + public boolean isNullable() + { + return nullable; + } + public List getProperties() { return properties; @@ -93,6 +100,7 @@ public boolean equals(Object obj) ColumnDefinition o = (ColumnDefinition) obj; return Objects.equals(this.name, o.name) && Objects.equals(this.type, o.type) && + this.nullable == o.nullable && Objects.equals(properties, o.properties) && Objects.equals(this.comment, o.comment); } @@ -100,7 +108,7 @@ public boolean equals(Object obj) @Override public int hashCode() { - return Objects.hash(name, type, properties, comment); + return Objects.hash(name, type, properties, comment, nullable); } @Override @@ -109,6 +117,7 @@ public String toString() return toStringHelper(this) .add("name", name) .add("type", type) + .add("nullable", nullable) .add("properties", properties) .add("comment", comment) .toString(); diff --git a/presto-parser/src/test/java/io/prestosql/sql/parser/TestSqlParser.java b/presto-parser/src/test/java/io/prestosql/sql/parser/TestSqlParser.java index f1a2c6faa39f9..716499d5b8188 100644 --- a/presto-parser/src/test/java/io/prestosql/sql/parser/TestSqlParser.java +++ b/presto-parser/src/test/java/io/prestosql/sql/parser/TestSqlParser.java @@ -1085,15 +1085,15 @@ public void testCreateTable() assertStatement("CREATE TABLE foo (a VARCHAR, b BIGINT COMMENT 'hello world', c IPADDRESS)", new CreateTable(QualifiedName.of("foo"), ImmutableList.of( - new ColumnDefinition(identifier("a"), "VARCHAR", emptyList(), Optional.empty()), - new ColumnDefinition(identifier("b"), "BIGINT", emptyList(), Optional.of("hello world")), - new ColumnDefinition(identifier("c"), "IPADDRESS", emptyList(), Optional.empty())), + new ColumnDefinition(identifier("a"), "VARCHAR", true, emptyList(), Optional.empty()), + new ColumnDefinition(identifier("b"), "BIGINT", true, emptyList(), Optional.of("hello world")), + new ColumnDefinition(identifier("c"), "IPADDRESS", true, emptyList(), Optional.empty())), false, ImmutableList.of(), Optional.empty())); assertStatement("CREATE TABLE IF NOT EXISTS bar (c TIMESTAMP)", new CreateTable(QualifiedName.of("bar"), - ImmutableList.of(new ColumnDefinition(identifier("c"), "TIMESTAMP", emptyList(), Optional.empty())), + ImmutableList.of(new ColumnDefinition(identifier("c"), "TIMESTAMP", true, emptyList(), Optional.empty())), true, ImmutableList.of(), Optional.empty())); @@ -1101,7 +1101,7 @@ public void testCreateTable() // with properties assertStatement("CREATE TABLE IF NOT EXISTS bar (c TIMESTAMP WITH (nullable = true, compression = 'LZ4'))", new CreateTable(QualifiedName.of("bar"), - ImmutableList.of(new ColumnDefinition(identifier("c"), "TIMESTAMP", ImmutableList.of( + ImmutableList.of(new ColumnDefinition(identifier("c"), "TIMESTAMP", true, ImmutableList.of( new Property(new Identifier("nullable"), BooleanLiteral.TRUE_LITERAL), new Property(new Identifier("compression"), new StringLiteral("LZ4")) ), Optional.empty())), @@ -1121,7 +1121,7 @@ public void testCreateTable() assertStatement("CREATE TABLE IF NOT EXISTS bar (c TIMESTAMP, LIKE like_table)", new CreateTable(QualifiedName.of("bar"), ImmutableList.of( - new ColumnDefinition(identifier("c"), "TIMESTAMP", emptyList(), Optional.empty()), + new ColumnDefinition(identifier("c"), "TIMESTAMP", true, emptyList(), Optional.empty()), new LikeClause(QualifiedName.of("like_table"), Optional.empty())), true, @@ -1130,10 +1130,10 @@ public void testCreateTable() assertStatement("CREATE TABLE IF NOT EXISTS bar (c TIMESTAMP, LIKE like_table, d DATE)", new CreateTable(QualifiedName.of("bar"), ImmutableList.of( - new ColumnDefinition(identifier("c"), "TIMESTAMP", emptyList(), Optional.empty()), + new ColumnDefinition(identifier("c"), "TIMESTAMP", true, emptyList(), Optional.empty()), new LikeClause(QualifiedName.of("like_table"), Optional.empty()), - new ColumnDefinition(identifier("d"), "DATE", emptyList(), Optional.empty())), + new ColumnDefinition(identifier("d"), "DATE", true, emptyList(), Optional.empty())), true, ImmutableList.of(), Optional.empty())); @@ -1148,7 +1148,7 @@ public void testCreateTable() assertStatement("CREATE TABLE IF NOT EXISTS bar (c TIMESTAMP, LIKE like_table EXCLUDING PROPERTIES)", new CreateTable(QualifiedName.of("bar"), ImmutableList.of( - new ColumnDefinition(identifier("c"), "TIMESTAMP", emptyList(), Optional.empty()), + new ColumnDefinition(identifier("c"), "TIMESTAMP", true, emptyList(), Optional.empty()), new LikeClause(QualifiedName.of("like_table"), Optional.of(LikeClause.PropertiesOption.EXCLUDING))), true, @@ -1157,7 +1157,7 @@ public void testCreateTable() assertStatement("CREATE TABLE IF NOT EXISTS bar (c TIMESTAMP, LIKE like_table EXCLUDING PROPERTIES) COMMENT 'test'", new CreateTable(QualifiedName.of("bar"), ImmutableList.of( - new ColumnDefinition(identifier("c"), "TIMESTAMP", emptyList(), Optional.empty()), + new ColumnDefinition(identifier("c"), "TIMESTAMP", true, emptyList(), Optional.empty()), new LikeClause(QualifiedName.of("like_table"), Optional.of(LikeClause.PropertiesOption.EXCLUDING))), true, @@ -1165,6 +1165,27 @@ public void testCreateTable() Optional.of("test"))); } + @Test + public void testCreateTableWithNotNull() + { + assertStatement( + "CREATE TABLE foo (" + + "a VARCHAR NOT NULL COMMENT 'column a', " + + "b BIGINT COMMENT 'hello world', " + + "c IPADDRESS, " + + "d DATE NOT NULL)", + new CreateTable( + QualifiedName.of("foo"), + ImmutableList.of( + new ColumnDefinition(identifier("a"), "VARCHAR", false, emptyList(), Optional.of("column a")), + new ColumnDefinition(identifier("b"), "BIGINT", true, emptyList(), Optional.of("hello world")), + new ColumnDefinition(identifier("c"), "IPADDRESS", true, emptyList(), Optional.empty()), + new ColumnDefinition(identifier("d"), "DATE", false, emptyList(), Optional.empty())), + false, + ImmutableList.of(), + Optional.empty())); + } + @Test public void testCreateTableAsSelect() { @@ -1386,7 +1407,9 @@ public void testAnalyze() public void testAddColumn() { assertStatement("ALTER TABLE foo.t ADD COLUMN c bigint", new AddColumn(QualifiedName.of("foo", "t"), - new ColumnDefinition(identifier("c"), "bigint", emptyList(), Optional.empty()))); + new ColumnDefinition(identifier("c"), "bigint", true, emptyList(), Optional.empty()))); + assertStatement("ALTER TABLE foo.t ADD COLUMN d double NOT NULL", new AddColumn(QualifiedName.of("foo", "t"), + new ColumnDefinition(identifier("d"), "double", false, emptyList(), Optional.empty()))); } @Test diff --git a/presto-postgresql/pom.xml b/presto-postgresql/pom.xml index 8b9bc100da392..f78b66de32265 100644 --- a/presto-postgresql/pom.xml +++ b/presto-postgresql/pom.xml @@ -135,6 +135,12 @@ testing-postgresql-server test + + + org.jetbrains + annotations + test + diff --git a/presto-postgresql/src/main/java/io/prestosql/plugin/postgresql/PostgreSqlClient.java b/presto-postgresql/src/main/java/io/prestosql/plugin/postgresql/PostgreSqlClient.java index 02f664b2818b7..d54fae9e3bd21 100644 --- a/presto-postgresql/src/main/java/io/prestosql/plugin/postgresql/PostgreSqlClient.java +++ b/presto-postgresql/src/main/java/io/prestosql/plugin/postgresql/PostgreSqlClient.java @@ -25,12 +25,13 @@ import io.prestosql.plugin.jdbc.ColumnMapping; import io.prestosql.plugin.jdbc.DriverConnectionFactory; import io.prestosql.plugin.jdbc.JdbcIdentity; -import io.prestosql.plugin.jdbc.JdbcOutputTableHandle; import io.prestosql.plugin.jdbc.JdbcTypeHandle; import io.prestosql.plugin.jdbc.SliceWriteFunction; import io.prestosql.plugin.jdbc.WriteMapping; import io.prestosql.spi.PrestoException; import io.prestosql.spi.connector.ConnectorSession; +import io.prestosql.spi.connector.ConnectorTableMetadata; +import io.prestosql.spi.connector.SchemaTableName; import io.prestosql.spi.type.StandardTypes; import io.prestosql.spi.type.Type; import io.prestosql.spi.type.TypeManager; @@ -54,8 +55,11 @@ import static com.fasterxml.jackson.databind.SerializationFeature.ORDER_MAP_ENTRIES_BY_KEYS; import static io.airlift.slice.Slices.utf8Slice; import static io.prestosql.plugin.jdbc.ColumnMapping.DISABLE_PUSHDOWN; +import static io.prestosql.plugin.jdbc.JdbcErrorCode.JDBC_ERROR; import static io.prestosql.plugin.jdbc.StandardColumnMappings.varbinaryWriteFunction; +import static io.prestosql.spi.StandardErrorCode.ALREADY_EXISTS; import static io.prestosql.spi.StandardErrorCode.INVALID_FUNCTION_ARGUMENT; +import static io.prestosql.spi.StandardErrorCode.NOT_SUPPORTED; import static io.prestosql.spi.type.VarbinaryType.VARBINARY; import static java.lang.String.format; import static java.nio.charset.StandardCharsets.UTF_8; @@ -63,6 +67,8 @@ public class PostgreSqlClient extends BaseJdbcClient { + private static final String DUPLICATE_TABLE_SQLSTATE = "42P07"; + protected final Type jsonType; @Inject @@ -73,19 +79,34 @@ public PostgreSqlClient(BaseJdbcConfig config, TypeManager typeManager) } @Override - public void commitCreateTable(JdbcIdentity identity, JdbcOutputTableHandle handle) + public void createTable(ConnectorSession session, ConnectorTableMetadata tableMetadata) + { + try { + createTable(session, tableMetadata, tableMetadata.getTable().getTableName()); + } + catch (SQLException e) { + boolean exists = DUPLICATE_TABLE_SQLSTATE.equals(e.getSQLState()); + throw new PrestoException(exists ? ALREADY_EXISTS : JDBC_ERROR, e); + } + } + + @Override + protected void renameTable(JdbcIdentity identity, String catalogName, String schemaName, String tableName, SchemaTableName newTable) { - // PostgreSQL does not allow qualifying the target of a rename + if (!schemaName.equals(newTable.getSchemaName())) { + throw new PrestoException(NOT_SUPPORTED, "Table rename across schemas is not supported in PostgreSQL"); + } + String sql = format( "ALTER TABLE %s RENAME TO %s", - quoted(handle.getCatalogName(), handle.getSchemaName(), handle.getTemporaryTableName()), - quoted(handle.getTableName())); + quoted(catalogName, schemaName, tableName), + quoted(newTable.getTableName())); - try (Connection connection = getConnection(identity, handle)) { + try (Connection connection = connectionFactory.openConnection(identity)) { execute(connection, sql); } catch (SQLException e) { - throw new RuntimeException(e); + throw new PrestoException(JDBC_ERROR, e); } } diff --git a/presto-postgresql/src/test/java/io/prestosql/plugin/postgresql/TestPostgreSqlDistributedQueries.java b/presto-postgresql/src/test/java/io/prestosql/plugin/postgresql/TestPostgreSqlDistributedQueries.java index 87adf5fb308b9..0db2829df4dd7 100644 --- a/presto-postgresql/src/test/java/io/prestosql/plugin/postgresql/TestPostgreSqlDistributedQueries.java +++ b/presto-postgresql/src/test/java/io/prestosql/plugin/postgresql/TestPostgreSqlDistributedQueries.java @@ -15,7 +15,7 @@ import io.airlift.testing.postgresql.TestingPostgreSqlServer; import io.airlift.tpch.TpchTable; -import io.prestosql.tests.AbstractTestQueries; +import io.prestosql.tests.AbstractTestDistributedQueries; import org.testng.annotations.AfterClass; import org.testng.annotations.Test; @@ -25,7 +25,7 @@ @Test public class TestPostgreSqlDistributedQueries - extends AbstractTestQueries + extends AbstractTestDistributedQueries { private final TestingPostgreSqlServer postgreSqlServer; @@ -48,5 +48,24 @@ public final void destroy() postgreSqlServer.close(); } + @Override + protected boolean supportsViews() + { + return false; + } + + @Override + public void testInsert() + { + // Test not supported due to lack of support for array types. + // See TestPostgreSqlIntegrationSmokeTest for insertion tests. + } + + @Override + public void testDelete() + { + // delete is not supported + } + // PostgreSQL specific tests should normally go in TestPostgreSqlIntegrationSmokeTest } diff --git a/presto-postgresql/src/test/java/io/prestosql/plugin/postgresql/TestPostgreSqlIntegrationSmokeTest.java b/presto-postgresql/src/test/java/io/prestosql/plugin/postgresql/TestPostgreSqlIntegrationSmokeTest.java index 3c08419f3c080..ad1a0525f5ebb 100644 --- a/presto-postgresql/src/test/java/io/prestosql/plugin/postgresql/TestPostgreSqlIntegrationSmokeTest.java +++ b/presto-postgresql/src/test/java/io/prestosql/plugin/postgresql/TestPostgreSqlIntegrationSmokeTest.java @@ -15,6 +15,7 @@ import io.airlift.testing.postgresql.TestingPostgreSqlServer; import io.prestosql.tests.AbstractTestIntegrationSmokeTest; +import org.intellij.lang.annotations.Language; import org.testng.annotations.AfterClass; import org.testng.annotations.Test; @@ -184,6 +185,44 @@ public void testCharTrailingSpace() assertUpdate("DROP TABLE char_trailing_space"); } + @Test + public void testInsertIntoNotNullColumn() + { + @Language("SQL") String createTableSql = format("" + + "CREATE TABLE %s.tpch.test_insert_not_null (\n" + + " column_a date,\n" + + " column_b date NOT NULL\n" + + ")", + getSession().getCatalog().get()); + assertUpdate(createTableSql); + assertEquals(computeScalar("SHOW CREATE TABLE test_insert_not_null"), createTableSql); + + assertQueryFails("INSERT INTO test_insert_not_null (column_a) VALUES (date '2012-12-31')", "(?s).*null value in column \"column_b\" violates not-null constraint.*"); + assertQueryFails("INSERT INTO test_insert_not_null (column_a, column_b) VALUES (date '2012-12-31', null)", "(?s).*null value in column \"column_b\" violates not-null constraint.*"); + + assertUpdate("ALTER TABLE test_insert_not_null ADD COLUMN column_c BIGINT NOT NULL"); + + createTableSql = format("" + + "CREATE TABLE %s.tpch.test_insert_not_null (\n" + + " column_a date,\n" + + " column_b date NOT NULL,\n" + + " column_c bigint NOT NULL\n" + + ")", + getSession().getCatalog().get()); + assertEquals(computeScalar("SHOW CREATE TABLE test_insert_not_null"), createTableSql); + + assertQueryFails("INSERT INTO test_insert_not_null (column_b) VALUES (date '2012-12-31')", "(?s).*null value in column \"column_c\" violates not-null constraint.*"); + assertQueryFails("INSERT INTO test_insert_not_null (column_b, column_c) VALUES (date '2012-12-31', null)", "(?s).*null value in column \"column_c\" violates not-null constraint.*"); + + assertUpdate("INSERT INTO test_insert_not_null (column_b, column_c) VALUES (date '2012-12-31', 1)", 1); + assertUpdate("INSERT INTO test_insert_not_null (column_a, column_b, column_c) VALUES (date '2013-01-01', date '2013-01-02', 2)", 1); + assertQuery( + "SELECT * FROM test_insert_not_null", + "VALUES (NULL, CAST('2012-12-31' AS DATE), 1), (CAST('2013-01-01' AS DATE), CAST('2013-01-02' AS DATE), 2)"); + + assertUpdate("DROP TABLE test_insert_not_null"); + } + private AutoCloseable withSchema(String schema) throws Exception { diff --git a/presto-redshift/src/main/java/io/prestosql/plugin/redshift/RedshiftClient.java b/presto-redshift/src/main/java/io/prestosql/plugin/redshift/RedshiftClient.java index 268fe314a5c27..1b912c0a19738 100644 --- a/presto-redshift/src/main/java/io/prestosql/plugin/redshift/RedshiftClient.java +++ b/presto-redshift/src/main/java/io/prestosql/plugin/redshift/RedshiftClient.java @@ -17,8 +17,8 @@ import io.prestosql.plugin.jdbc.BaseJdbcConfig; import io.prestosql.plugin.jdbc.DriverConnectionFactory; import io.prestosql.plugin.jdbc.JdbcIdentity; -import io.prestosql.plugin.jdbc.JdbcOutputTableHandle; import io.prestosql.spi.PrestoException; +import io.prestosql.spi.connector.SchemaTableName; import org.postgresql.Driver; import javax.inject.Inject; @@ -28,6 +28,7 @@ import java.sql.SQLException; import static io.prestosql.plugin.jdbc.JdbcErrorCode.JDBC_ERROR; +import static io.prestosql.spi.StandardErrorCode.NOT_SUPPORTED; import static java.lang.String.format; public class RedshiftClient @@ -40,15 +41,18 @@ public RedshiftClient(BaseJdbcConfig config) } @Override - public void commitCreateTable(JdbcIdentity identity, JdbcOutputTableHandle handle) + protected void renameTable(JdbcIdentity identity, String catalogName, String schemaName, String tableName, SchemaTableName newTable) { - // Redshift does not allow qualifying the target of a rename + if (!schemaName.equals(newTable.getSchemaName())) { + throw new PrestoException(NOT_SUPPORTED, "Table rename across schemas is not supported in Redshift"); + } + String sql = format( "ALTER TABLE %s RENAME TO %s", - quoted(handle.getCatalogName(), handle.getSchemaName(), handle.getTemporaryTableName()), - quoted(handle.getTableName())); + quoted(catalogName, schemaName, tableName), + quoted(newTable.getTableName())); - try (Connection connection = getConnection(identity, handle)) { + try (Connection connection = connectionFactory.openConnection(identity)) { execute(connection, sql); } catch (SQLException e) { diff --git a/presto-spi/src/main/java/io/prestosql/spi/connector/ColumnMetadata.java b/presto-spi/src/main/java/io/prestosql/spi/connector/ColumnMetadata.java index 82ed91f8aa7d5..6a1c64f5bd5aa 100644 --- a/presto-spi/src/main/java/io/prestosql/spi/connector/ColumnMetadata.java +++ b/presto-spi/src/main/java/io/prestosql/spi/connector/ColumnMetadata.java @@ -29,6 +29,7 @@ public class ColumnMetadata { private final String name; private final Type type; + private final boolean nullable; private final String comment; private final String extraInfo; private final boolean hidden; @@ -36,20 +37,25 @@ public class ColumnMetadata public ColumnMetadata(String name, Type type) { - this(name, type, null, null, false, emptyMap()); + this(name, type, true, null, null, false, emptyMap()); } public ColumnMetadata(String name, Type type, String comment, boolean hidden) { - this(name, type, comment, null, hidden, emptyMap()); + this(name, type, true, comment, null, hidden, emptyMap()); } public ColumnMetadata(String name, Type type, String comment, String extraInfo, boolean hidden) { - this(name, type, comment, extraInfo, hidden, emptyMap()); + this(name, type, true, comment, extraInfo, hidden, emptyMap()); } public ColumnMetadata(String name, Type type, String comment, String extraInfo, boolean hidden, Map properties) + { + this(name, type, true, comment, extraInfo, hidden, properties); + } + + public ColumnMetadata(String name, Type type, boolean nullable, String comment, String extraInfo, boolean hidden, Map properties) { checkNotEmpty(name, "name"); requireNonNull(type, "type is null"); @@ -61,6 +67,7 @@ public ColumnMetadata(String name, Type type, String comment, String extraInfo, this.extraInfo = extraInfo; this.hidden = hidden; this.properties = properties.isEmpty() ? emptyMap() : unmodifiableMap(new LinkedHashMap<>(properties)); + this.nullable = nullable; } public String getName() @@ -73,6 +80,11 @@ public Type getType() return type; } + public boolean isNullable() + { + return nullable; + } + public String getComment() { return comment; @@ -99,6 +111,7 @@ public String toString() StringBuilder sb = new StringBuilder("ColumnMetadata{"); sb.append("name='").append(name).append('\''); sb.append(", type=").append(type); + sb.append(", ").append(nullable ? "nullable" : "nonnull"); if (comment != null) { sb.append(", comment='").append(comment).append('\''); } @@ -118,7 +131,7 @@ public String toString() @Override public int hashCode() { - return Objects.hash(name, type, comment, extraInfo, hidden); + return Objects.hash(name, type, nullable, comment, extraInfo, hidden); } @Override @@ -133,6 +146,7 @@ public boolean equals(Object obj) ColumnMetadata other = (ColumnMetadata) obj; return Objects.equals(this.name, other.name) && Objects.equals(this.type, other.type) && + Objects.equals(this.nullable, other.nullable) && Objects.equals(this.comment, other.comment) && Objects.equals(this.extraInfo, other.extraInfo) && Objects.equals(this.hidden, other.hidden); diff --git a/presto-spi/src/main/java/io/prestosql/spi/connector/Connector.java b/presto-spi/src/main/java/io/prestosql/spi/connector/Connector.java index b346805218941..8188a53b9d93f 100644 --- a/presto-spi/src/main/java/io/prestosql/spi/connector/Connector.java +++ b/presto-spi/src/main/java/io/prestosql/spi/connector/Connector.java @@ -172,4 +172,9 @@ default boolean isSingleStatementWritesOnly() * have been returned from the connector. */ default void shutdown() {} + + default Set getCapabilities() + { + return emptySet(); + } } diff --git a/presto-spi/src/main/java/io/prestosql/spi/connector/ConnectorCapabilities.java b/presto-spi/src/main/java/io/prestosql/spi/connector/ConnectorCapabilities.java new file mode 100644 index 0000000000000..18dc308fd392e --- /dev/null +++ b/presto-spi/src/main/java/io/prestosql/spi/connector/ConnectorCapabilities.java @@ -0,0 +1,19 @@ +/* + * 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 io.prestosql.spi.connector; + +public enum ConnectorCapabilities +{ + NOT_NULL_COLUMN_CONSTRAINT, +} diff --git a/presto-sqlserver/src/main/java/io/prestosql/plugin/sqlserver/SqlServerClient.java b/presto-sqlserver/src/main/java/io/prestosql/plugin/sqlserver/SqlServerClient.java index 1c5eb3d486a2f..7ca3822b338e5 100644 --- a/presto-sqlserver/src/main/java/io/prestosql/plugin/sqlserver/SqlServerClient.java +++ b/presto-sqlserver/src/main/java/io/prestosql/plugin/sqlserver/SqlServerClient.java @@ -13,13 +13,16 @@ */ package io.prestosql.plugin.sqlserver; +import com.google.common.base.Joiner; import com.microsoft.sqlserver.jdbc.SQLServerDriver; import io.prestosql.plugin.jdbc.BaseJdbcClient; import io.prestosql.plugin.jdbc.BaseJdbcConfig; import io.prestosql.plugin.jdbc.DriverConnectionFactory; +import io.prestosql.plugin.jdbc.JdbcColumnHandle; import io.prestosql.plugin.jdbc.JdbcIdentity; -import io.prestosql.plugin.jdbc.JdbcOutputTableHandle; +import io.prestosql.plugin.jdbc.JdbcTableHandle; import io.prestosql.spi.PrestoException; +import io.prestosql.spi.connector.SchemaTableName; import javax.inject.Inject; @@ -32,6 +35,8 @@ public class SqlServerClient extends BaseJdbcClient { + private static final Joiner DOT_JOINER = Joiner.on("."); + @Inject public SqlServerClient(BaseJdbcConfig config) { @@ -39,14 +44,28 @@ public SqlServerClient(BaseJdbcConfig config) } @Override - public void commitCreateTable(JdbcIdentity identity, JdbcOutputTableHandle handle) + protected void renameTable(JdbcIdentity identity, String catalogName, String schemaName, String tableName, SchemaTableName newTable) { String sql = format( "sp_rename %s, %s", - singleQuote(handle.getCatalogName(), handle.getSchemaName(), handle.getTemporaryTableName()), - singleQuote(handle.getTableName())); + singleQuote(catalogName, schemaName, tableName), + singleQuote(newTable.getTableName())); + try (Connection connection = connectionFactory.openConnection(identity)) { + execute(connection, sql); + } + catch (SQLException e) { + throw new PrestoException(JDBC_ERROR, e); + } + } - try (Connection connection = getConnection(identity, handle)) { + @Override + public void renameColumn(JdbcIdentity identity, JdbcTableHandle handle, JdbcColumnHandle jdbcColumn, String newColumnName) + { + try (Connection connection = connectionFactory.openConnection(identity)) { + String sql = format( + "sp_rename %s, %s, 'COLUMN'", + singleQuote(handle.getCatalogName(), handle.getSchemaName(), handle.getTableName(), jdbcColumn.getColumnName()), + singleQuote(newColumnName)); execute(connection, sql); } catch (SQLException e) { @@ -54,9 +73,9 @@ public void commitCreateTable(JdbcIdentity identity, JdbcOutputTableHandle handl } } - private static String singleQuote(String catalog, String schema, String table) + private static String singleQuote(String... objects) { - return singleQuote(catalog + "." + schema + "." + table); + return singleQuote(DOT_JOINER.join(objects)); } private static String singleQuote(String literal) diff --git a/presto-tests/src/main/java/io/prestosql/tests/H2QueryRunner.java b/presto-tests/src/main/java/io/prestosql/tests/H2QueryRunner.java index 3fe8fac44d0e1..dee93a9fb64cb 100644 --- a/presto-tests/src/main/java/io/prestosql/tests/H2QueryRunner.java +++ b/presto-tests/src/main/java/io/prestosql/tests/H2QueryRunner.java @@ -196,7 +196,7 @@ public MaterializedRow map(ResultSet resultSet, StatementContext context) throws SQLException { int count = resultSet.getMetaData().getColumnCount(); - checkArgument(types.size() == count, "type does not match result"); + checkArgument(types.size() == count, "expected types count (%s) does not match actual column count (%s)", types.size(), count); List row = new ArrayList<>(count); for (int i = 1; i <= count; i++) { Type type = types.get(i - 1); diff --git a/presto-tpch/src/main/java/io/prestosql/plugin/tpch/TpchMetadata.java b/presto-tpch/src/main/java/io/prestosql/plugin/tpch/TpchMetadata.java index cb6c796a10731..937853ef6243c 100644 --- a/presto-tpch/src/main/java/io/prestosql/plugin/tpch/TpchMetadata.java +++ b/presto-tpch/src/main/java/io/prestosql/plugin/tpch/TpchMetadata.java @@ -284,7 +284,7 @@ private static ConnectorTableMetadata getTableMetadata(String schemaName, TpchTa { ImmutableList.Builder columns = ImmutableList.builder(); for (TpchColumn column : tpchTable.getColumns()) { - columns.add(new ColumnMetadata(columnNaming.getName(column), getPrestoType(column))); + columns.add(new ColumnMetadata(columnNaming.getName(column), getPrestoType(column), false, null, null, false, emptyMap())); } columns.add(new ColumnMetadata(ROW_NUMBER_COLUMN_NAME, BIGINT, null, true));