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