Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,7 @@ public List<JdbcColumnHandle> getColumns(ConnectorSession session, JdbcTableHand
while (resultSet.next()) {
JdbcTypeHandle typeHandle = new JdbcTypeHandle(
resultSet.getInt("DATA_TYPE"),
resultSet.getString("TYPE_NAME"),
resultSet.getInt("COLUMN_SIZE"),
resultSet.getInt("DECIMAL_DIGITS"));
Optional<ReadMapping> columnMapping = toPrestoType(session, typeHandle);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@
import static com.facebook.presto.spi.type.Decimals.readBigDecimal;
import static com.facebook.presto.spi.type.DoubleType.DOUBLE;
import static com.facebook.presto.spi.type.IntegerType.INTEGER;
import static com.facebook.presto.spi.type.JsonType.JSON;
import static com.facebook.presto.spi.type.RealType.REAL;
import static com.facebook.presto.spi.type.SmallintType.SMALLINT;
import static com.facebook.presto.spi.type.TinyintType.TINYINT;
Expand Down Expand Up @@ -150,6 +151,9 @@ else if (type instanceof DecimalType) {
else if (isVarcharType(type) || isCharType(type)) {
statement.setString(parameter, type.getSlice(block, position).toStringUtf8());
}
else if (JSON.equals(type)) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We don't need the type instance to be in the SPI. Instead, compare like this:

type.getTypeSignature().getBase().equals(StandardTypes.JSON)

statement.setString(parameter, type.getSlice(block, position).toStringUtf8());
}
else if (VARBINARY.equals(type)) {
statement.setBytes(parameter, type.getSlice(block, position).getBytes());
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,16 +23,19 @@
public final class JdbcTypeHandle
{
private final int jdbcType;
private final String jdbcTypeName;
private final int columnSize;
private final int decimalDigits;

@JsonCreator
public JdbcTypeHandle(
@JsonProperty("jdbcType") int jdbcType,
@JsonProperty("jdbcTypeName") String jdbcTypeName,
@JsonProperty("columnSize") int columnSize,
@JsonProperty("decimalDigits") int decimalDigits)
{
this.jdbcType = jdbcType;
this.jdbcTypeName = jdbcTypeName;
this.columnSize = columnSize;
this.decimalDigits = decimalDigits;
}
Expand All @@ -43,6 +46,12 @@ public int getJdbcType()
return jdbcType;
}

@JsonProperty
public String getJdbcTypeName()
{
return jdbcTypeName;
}

@JsonProperty
public int getColumnSize()
{
Expand All @@ -58,7 +67,7 @@ public int getDecimalDigits()
@Override
public int hashCode()
{
return Objects.hash(jdbcType, columnSize, decimalDigits);
return Objects.hash(jdbcType, jdbcTypeName, columnSize, decimalDigits);
}

@Override
Expand All @@ -72,6 +81,7 @@ public boolean equals(Object o)
}
JdbcTypeHandle that = (JdbcTypeHandle) o;
return jdbcType == that.jdbcType &&
jdbcTypeName.equals(that.jdbcTypeName) &&
columnSize == that.columnSize &&
decimalDigits == that.decimalDigits;
}
Expand All @@ -81,6 +91,7 @@ public String toString()
{
return toStringHelper(this)
.add("jdbcType", jdbcType)
.add("jdbcTypeName", jdbcTypeName)
.add("columnSize", columnSize)
.add("decimalDigits", decimalDigits)
.toString();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,20 +19,20 @@ public final class TestingJdbcTypeHandle
{
private TestingJdbcTypeHandle() {}

public static final JdbcTypeHandle JDBC_BOOLEAN = new JdbcTypeHandle(Types.BOOLEAN, 1, 0);
public static final JdbcTypeHandle JDBC_BOOLEAN = new JdbcTypeHandle(Types.BOOLEAN, "", 1, 0);

public static final JdbcTypeHandle JDBC_SMALLINT = new JdbcTypeHandle(Types.SMALLINT, 1, 0);
public static final JdbcTypeHandle JDBC_TINYINT = new JdbcTypeHandle(Types.TINYINT, 2, 0);
public static final JdbcTypeHandle JDBC_INTEGER = new JdbcTypeHandle(Types.INTEGER, 4, 0);
public static final JdbcTypeHandle JDBC_BIGINT = new JdbcTypeHandle(Types.BIGINT, 8, 0);
public static final JdbcTypeHandle JDBC_SMALLINT = new JdbcTypeHandle(Types.SMALLINT, "", 1, 0);
public static final JdbcTypeHandle JDBC_TINYINT = new JdbcTypeHandle(Types.TINYINT, "", 2, 0);
public static final JdbcTypeHandle JDBC_INTEGER = new JdbcTypeHandle(Types.INTEGER, "", 4, 0);
public static final JdbcTypeHandle JDBC_BIGINT = new JdbcTypeHandle(Types.BIGINT, "", 8, 0);

public static final JdbcTypeHandle JDBC_REAL = new JdbcTypeHandle(Types.REAL, 8, 0);
public static final JdbcTypeHandle JDBC_DOUBLE = new JdbcTypeHandle(Types.DOUBLE, 8, 0);
public static final JdbcTypeHandle JDBC_REAL = new JdbcTypeHandle(Types.REAL, "", 8, 0);
public static final JdbcTypeHandle JDBC_DOUBLE = new JdbcTypeHandle(Types.DOUBLE, "", 8, 0);

public static final JdbcTypeHandle JDBC_CHAR = new JdbcTypeHandle(Types.CHAR, 10, 0);
public static final JdbcTypeHandle JDBC_VARCHAR = new JdbcTypeHandle(Types.VARCHAR, 10, 0);
public static final JdbcTypeHandle JDBC_CHAR = new JdbcTypeHandle(Types.CHAR, "", 10, 0);
public static final JdbcTypeHandle JDBC_VARCHAR = new JdbcTypeHandle(Types.VARCHAR, "", 10, 0);

public static final JdbcTypeHandle JDBC_DATE = new JdbcTypeHandle(Types.DATE, 8, 0);
public static final JdbcTypeHandle JDBC_TIME = new JdbcTypeHandle(Types.TIME, 4, 0);
public static final JdbcTypeHandle JDBC_TIMESTAMP = new JdbcTypeHandle(Types.TIMESTAMP, 8, 0);
public static final JdbcTypeHandle JDBC_DATE = new JdbcTypeHandle(Types.DATE, "", 8, 0);
public static final JdbcTypeHandle JDBC_TIME = new JdbcTypeHandle(Types.TIME, "", 4, 0);
public static final JdbcTypeHandle JDBC_TIMESTAMP = new JdbcTypeHandle(Types.TIMESTAMP, "", 8, 0);
}
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@
import static com.facebook.presto.spi.type.RealType.REAL;
import static com.facebook.presto.spi.type.SmallintType.SMALLINT;
import static com.facebook.presto.spi.type.StandardTypes.ARRAY;
import static com.facebook.presto.spi.type.StandardTypes.JSON;
import static com.facebook.presto.spi.type.StandardTypes.MAP;
import static com.facebook.presto.spi.type.TimeType.TIME;
import static com.facebook.presto.spi.type.TimeWithTimeZoneType.TIME_WITH_TIME_ZONE;
Expand Down Expand Up @@ -261,6 +262,9 @@ else if (DOUBLE.equals(type)) {
else if (BOOLEAN.equals(type)) {
type.writeBoolean(blockBuilder, (Boolean) value);
}
else if (JSON.equals(type)) {
type.writeSlice(blockBuilder, Slices.utf8Slice((String) value));
}
else if (type instanceof VarcharType) {
type.writeSlice(blockBuilder, Slices.utf8Slice((String) value));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,90 +13,11 @@
*/
package com.facebook.presto.type;

import com.facebook.presto.spi.ConnectorSession;
import com.facebook.presto.spi.block.Block;
import com.facebook.presto.spi.block.BlockBuilder;
import com.facebook.presto.spi.type.AbstractVariableWidthType;
import com.facebook.presto.spi.type.StandardTypes;
import com.facebook.presto.spi.type.TypeSignature;
import io.airlift.slice.Slice;
import io.airlift.slice.Slices;

/**
* The stack representation for JSON objects must have the keys in natural sorted order.
*/
public class JsonType
extends AbstractVariableWidthType
extends com.facebook.presto.spi.type.JsonType
{
public static final JsonType JSON = new JsonType();

public JsonType()
{
super(new TypeSignature(StandardTypes.JSON), Slice.class);
}

@Override
public boolean isComparable()
{
return true;
}

@Override
public boolean equalTo(Block leftBlock, int leftPosition, Block rightBlock, int rightPosition)
{
Slice leftValue = leftBlock.getSlice(leftPosition, 0, leftBlock.getSliceLength(leftPosition));
Slice rightValue = rightBlock.getSlice(rightPosition, 0, rightBlock.getSliceLength(rightPosition));
return leftValue.equals(rightValue);
}

@Override
public long hash(Block block, int position)
{
return block.hash(position, 0, block.getSliceLength(position));
}

@Override
public Object getObjectValue(ConnectorSession session, Block block, int position)
{
if (block.isNull(position)) {
return null;
}

return block.getSlice(position, 0, block.getSliceLength(position)).toStringUtf8();
}

@Override
public void appendTo(Block block, int position, BlockBuilder blockBuilder)
{
if (block.isNull(position)) {
blockBuilder.appendNull();
}
else {
block.writeBytesTo(position, 0, block.getSliceLength(position), blockBuilder);
blockBuilder.closeEntry();
}
}

@Override
public Slice getSlice(Block block, int position)
{
return block.getSlice(position, 0, block.getSliceLength(position));
}

public void writeString(BlockBuilder blockBuilder, String value)
{
writeSlice(blockBuilder, Slices.utf8Slice(value));
}

@Override
public void writeSlice(BlockBuilder blockBuilder, Slice value)
{
writeSlice(blockBuilder, value, 0, value.length());
}

@Override
public void writeSlice(BlockBuilder blockBuilder, Slice value, int offset, int length)
{
blockBuilder.writeBytes(value, offset, length).closeEntry();
}
public static final com.facebook.presto.spi.type.JsonType JSON = com.facebook.presto.spi.type.JsonType.JSON;
}
21 changes: 15 additions & 6 deletions presto-postgresql/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,21 @@
<artifactId>javax.inject</artifactId>
</dependency>

<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
</dependency>

<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
</dependency>

<dependency>
<groupId>io.airlift</groupId>
<artifactId>json</artifactId>
</dependency>

<!-- Presto SPI -->
<dependency>
<groupId>com.facebook.presto</groupId>
Expand Down Expand Up @@ -91,12 +106,6 @@
<scope>test</scope>
</dependency>

<dependency>
<groupId>io.airlift</groupId>
<artifactId>json</artifactId>
<scope>test</scope>
</dependency>

<dependency>
<groupId>com.facebook.presto</groupId>
<artifactId>presto-main</artifactId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,18 +18,42 @@
import com.facebook.presto.plugin.jdbc.DriverConnectionFactory;
import com.facebook.presto.plugin.jdbc.JdbcConnectorId;
import com.facebook.presto.plugin.jdbc.JdbcOutputTableHandle;
import com.facebook.presto.plugin.jdbc.JdbcTypeHandle;
import com.facebook.presto.plugin.jdbc.ReadMapping;
import com.facebook.presto.spi.ConnectorSession;
import com.facebook.presto.spi.PrestoException;
import com.facebook.presto.spi.type.Type;
import com.fasterxml.jackson.core.JsonFactory;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.ObjectMapper;
import io.airlift.json.ObjectMapperProvider;
import io.airlift.slice.DynamicSliceOutput;
import io.airlift.slice.Slice;
import io.airlift.slice.SliceOutput;
import org.postgresql.Driver;

import javax.inject.Inject;

import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Types;
import java.util.Optional;

import static com.facebook.presto.plugin.jdbc.ReadMapping.sliceReadMapping;
import static com.facebook.presto.spi.StandardErrorCode.INVALID_FUNCTION_ARGUMENT;
import static com.facebook.presto.spi.type.JsonType.JSON;
import static com.facebook.presto.spi.type.VarbinaryType.VARBINARY;
import static com.fasterxml.jackson.core.JsonFactory.Feature.CANONICALIZE_FIELD_NAMES;
import static com.fasterxml.jackson.databind.SerializationFeature.ORDER_MAP_ENTRIES_BY_KEYS;
import static io.airlift.slice.Slices.utf8Slice;
import static java.lang.String.format;
import static java.nio.charset.StandardCharsets.UTF_8;

public class PostgreSqlClient
extends BaseJdbcClient
Expand Down Expand Up @@ -87,7 +111,58 @@ protected String toSqlType(Type type)
if (VARBINARY.equals(type)) {
return "bytea";
}
if (JSON.equals(type)) {
return "jsonb";
}

return super.toSqlType(type);
}

@Override
public Optional<ReadMapping> toPrestoType(ConnectorSession session, JdbcTypeHandle typeHandle)
{
if (typeHandle.getJdbcType() == Types.OTHER) {
switch (typeHandle.getJdbcTypeName()) {
case "jsonb":
case "json":
return Optional.of(jsonReadMapping());
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will predicate-pushdown work correctly with json? We definitely need some tests for that.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you please elaborate about this and add some references to similar tests ?

}

return super.toPrestoType(session, typeHandle);
}

private static ReadMapping jsonReadMapping()
{
return sliceReadMapping(JSON, (resultSet, columnIndex) -> jsonParse(utf8Slice(resultSet.getString(columnIndex))));
}
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@findepi
Any idea how can I use com.facebook.presto.operator.scalar.JsonFunctions#jsonParse from here without the need to duplicate code ?
Same for com.facebook.presto.type.JsonType that is needed by ReadMapping


private static final JsonFactory JSON_FACTORY = new JsonFactory()
.disable(CANONICALIZE_FIELD_NAMES);

private static final ObjectMapper SORTED_MAPPER = new ObjectMapperProvider().get().configure(ORDER_MAP_ENTRIES_BY_KEYS, true);

public static Slice jsonParse(Slice slice)
{
try (JsonParser parser = createJsonParser(JSON_FACTORY, slice)) {
byte[] in = slice.getBytes();
SliceOutput dynamicSliceOutput = new DynamicSliceOutput(in.length);
SORTED_MAPPER.writeValue((OutputStream) dynamicSliceOutput, SORTED_MAPPER.readValue(parser, Object.class));
// nextToken() returns null if the input is parsed correctly,
// but will throw an exception if there are trailing characters.
parser.nextToken();
return dynamicSliceOutput.slice();
}
catch (Exception e) {
throw new PrestoException(INVALID_FUNCTION_ARGUMENT, format("Cannot convert '%s' to JSON", slice.toStringUtf8()));
}
}

public static JsonParser createJsonParser(JsonFactory factory, Slice json)
throws IOException
{
// Jackson tries to detect the character encoding automatically when using InputStream
// so we pass an InputStreamReader instead.
return factory.createParser(new InputStreamReader(json.getInput(), UTF_8));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ public static QueryRunner createPostgreSqlQueryRunner(TestingPostgreSqlServer se
queryRunner.createCatalog("tpch", "tpch");

Map<String, String> properties = ImmutableMap.<String, String>builder()
.put("connection-url", server.getJdbcUrl())
.put("connection-url", server.getJdbcUrl() + "&stringtype=unspecified")
.put("allow-drop-table", "true")
.build();

Expand Down
Loading