Skip to content

Commit 4f0d345

Browse files
Mariamalmesferpratyakshsharmaagrawalreetika
authored andcommitted
Add read support for geometry and geography data types in postgres connector
Co-authored-by: pratyakshsharma <[email protected]>[email protected]> Co-authored-by: agrawalreetika <[email protected]>
1 parent 9e3dccf commit 4f0d345

File tree

10 files changed

+228
-45
lines changed

10 files changed

+228
-45
lines changed

presto-base-jdbc/pom.xml

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,17 @@
110110
<artifactId>jmxutils</artifactId>
111111
</dependency>
112112

113+
<dependency>
114+
<groupId>com.esri.geometry</groupId>
115+
<artifactId>esri-geometry-api</artifactId>
116+
</dependency>
117+
118+
<dependency>
119+
<groupId>com.facebook.presto</groupId>
120+
<artifactId>presto-geospatial-toolkit</artifactId>
121+
<scope>provided</scope>
122+
</dependency>
123+
113124
<!-- for testing -->
114125

115126
<dependency>
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
/*
2+
* Licensed under the Apache License, Version 2.0 (the "License");
3+
* you may not use this file except in compliance with the License.
4+
* You may obtain a copy of the License at
5+
*
6+
* http://www.apache.org/licenses/LICENSE-2.0
7+
*
8+
* Unless required by applicable law or agreed to in writing, software
9+
* distributed under the License is distributed on an "AS IS" BASIS,
10+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11+
* See the License for the specific language governing permissions and
12+
* limitations under the License.
13+
*/
14+
package com.facebook.presto.plugin.jdbc;
15+
16+
import com.esri.core.geometry.ogc.OGCGeometry;
17+
import com.facebook.presto.spi.PrestoException;
18+
import io.airlift.slice.Slice;
19+
20+
import static com.esri.core.geometry.ogc.OGCGeometry.fromBinary;
21+
import static com.facebook.presto.geospatial.GeometryUtils.wktFromJtsGeometry;
22+
import static com.facebook.presto.geospatial.serde.EsriGeometrySerde.serialize;
23+
import static com.facebook.presto.geospatial.serde.JtsGeometrySerde.deserialize;
24+
import static com.facebook.presto.spi.StandardErrorCode.INVALID_FUNCTION_ARGUMENT;
25+
import static io.airlift.slice.Slices.utf8Slice;
26+
import static java.util.Objects.requireNonNull;
27+
28+
public class GeometryUtils
29+
{
30+
private GeometryUtils() {}
31+
32+
public static Slice getAsText(Slice input)
33+
{
34+
return utf8Slice(wktFromJtsGeometry(deserialize(input)));
35+
}
36+
37+
public static Slice stGeomFromBinary(Slice input)
38+
{
39+
requireNonNull(input, "input is null");
40+
OGCGeometry geometry;
41+
try {
42+
geometry = fromBinary(input.toByteBuffer().slice());
43+
}
44+
catch (IllegalArgumentException | IndexOutOfBoundsException e) {
45+
throw new PrestoException(INVALID_FUNCTION_ARGUMENT, "Invalid Well-Known Binary (WKB)", e);
46+
}
47+
geometry.setSpatialReference(null);
48+
return serialize(geometry);
49+
}
50+
}

presto-base-jdbc/src/main/java/com/facebook/presto/plugin/jdbc/mapping/StandardColumnMappings.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,8 +61,11 @@
6161
import static com.facebook.presto.common.type.UuidType.UUID;
6262
import static com.facebook.presto.common.type.UuidType.prestoUuidToJavaUuid;
6363
import static com.facebook.presto.common.type.VarbinaryType.VARBINARY;
64+
import static com.facebook.presto.common.type.VarcharType.VARCHAR;
6465
import static com.facebook.presto.common.type.VarcharType.createUnboundedVarcharType;
6566
import static com.facebook.presto.common.type.VarcharType.createVarcharType;
67+
import static com.facebook.presto.plugin.jdbc.GeometryUtils.getAsText;
68+
import static com.facebook.presto.plugin.jdbc.GeometryUtils.stGeomFromBinary;
6669
import static com.facebook.presto.plugin.jdbc.mapping.ReadMapping.createBooleanReadMapping;
6770
import static com.facebook.presto.plugin.jdbc.mapping.ReadMapping.createDoubleReadMapping;
6871
import static com.facebook.presto.plugin.jdbc.mapping.ReadMapping.createLongReadMapping;
@@ -445,4 +448,9 @@ else if (type instanceof UuidType) {
445448
}
446449
return Optional.empty();
447450
}
451+
public static ReadMapping geometryReadMapping()
452+
{
453+
return createSliceReadMapping(VARCHAR,
454+
(resultSet, columnIndex) -> getAsText(stGeomFromBinary(wrappedBuffer(resultSet.getBytes(columnIndex)))));
455+
}
448456
}

presto-docs/src/main/sphinx/connector/postgresql.rst

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -145,7 +145,10 @@ The connector maps PostgreSQL types to the corresponding PrestoDB types:
145145
- ``JSON``
146146
* - ``JSONB``
147147
- ``JSON``
148-
148+
* - ``GEOMETRY``
149+
- ``VARCHAR``
150+
* - ``GEOGRAPHY``
151+
- ``VARCHAR``
149152
No other types are supported.
150153

151154
PrestoDB to PostgreSQL type mapping

presto-mysql/pom.xml

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -77,11 +77,6 @@
7777
<scope>provided</scope>
7878
</dependency>
7979

80-
<dependency>
81-
<groupId>com.facebook.presto</groupId>
82-
<artifactId>presto-geospatial-toolkit</artifactId>
83-
</dependency>
84-
8580
<dependency>
8681
<groupId>com.facebook.drift</groupId>
8782
<artifactId>drift-api</artifactId>

presto-mysql/src/main/java/com/facebook/presto/plugin/mysql/MySqlClient.java

Lines changed: 1 addition & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@
1313
*/
1414
package com.facebook.presto.plugin.mysql;
1515

16-
import com.esri.core.geometry.ogc.OGCGeometry;
1716
import com.facebook.presto.common.type.TimestampType;
1817
import com.facebook.presto.common.type.Type;
1918
import com.facebook.presto.common.type.VarcharType;
@@ -36,7 +35,6 @@
3635
import com.google.common.collect.ImmutableSet;
3736
import com.mysql.cj.jdbc.JdbcStatement;
3837
import com.mysql.jdbc.Driver;
39-
import io.airlift.slice.Slice;
4038

4139
import javax.inject.Inject;
4240

@@ -52,31 +50,22 @@
5250
import java.util.Optional;
5351
import java.util.Properties;
5452

55-
import static com.esri.core.geometry.ogc.OGCGeometry.fromBinary;
5653
import static com.facebook.presto.common.type.RealType.REAL;
5754
import static com.facebook.presto.common.type.StandardTypes.GEOMETRY;
5855
import static com.facebook.presto.common.type.TimeWithTimeZoneType.TIME_WITH_TIME_ZONE;
5956
import static com.facebook.presto.common.type.TimestampWithTimeZoneType.TIMESTAMP_WITH_TIME_ZONE;
6057
import static com.facebook.presto.common.type.VarbinaryType.VARBINARY;
61-
import static com.facebook.presto.common.type.VarcharType.VARCHAR;
6258
import static com.facebook.presto.common.type.Varchars.isVarcharType;
63-
import static com.facebook.presto.geospatial.GeometryUtils.wktFromJtsGeometry;
64-
import static com.facebook.presto.geospatial.serde.EsriGeometrySerde.serialize;
65-
import static com.facebook.presto.geospatial.serde.JtsGeometrySerde.deserialize;
6659
import static com.facebook.presto.plugin.jdbc.DriverConnectionFactory.basicConnectionProperties;
6760
import static com.facebook.presto.plugin.jdbc.JdbcErrorCode.JDBC_ERROR;
6861
import static com.facebook.presto.plugin.jdbc.QueryBuilder.quote;
69-
import static com.facebook.presto.plugin.jdbc.mapping.ReadMapping.createSliceReadMapping;
62+
import static com.facebook.presto.plugin.jdbc.mapping.StandardColumnMappings.geometryReadMapping;
7063
import static com.facebook.presto.spi.StandardErrorCode.ALREADY_EXISTS;
71-
import static com.facebook.presto.spi.StandardErrorCode.INVALID_FUNCTION_ARGUMENT;
7264
import static com.facebook.presto.spi.StandardErrorCode.NOT_SUPPORTED;
7365
import static com.google.common.collect.ImmutableMap.toImmutableMap;
7466
import static com.google.common.util.concurrent.MoreExecutors.directExecutor;
75-
import static io.airlift.slice.Slices.utf8Slice;
76-
import static io.airlift.slice.Slices.wrappedBuffer;
7767
import static java.lang.String.format;
7868
import static java.util.Locale.ENGLISH;
79-
import static java.util.Objects.requireNonNull;
8069
import static java.util.function.Function.identity;
8170

8271
public class MySqlClient
@@ -253,31 +242,6 @@ public Optional<ReadMapping> toPrestoType(ConnectorSession session, JdbcTypeHand
253242
return super.toPrestoType(session, typeHandle);
254243
}
255244

256-
protected static ReadMapping geometryReadMapping()
257-
{
258-
return createSliceReadMapping(VARCHAR,
259-
(resultSet, columnIndex) -> getAsText(stGeomFromBinary(wrappedBuffer(resultSet.getBytes(columnIndex)))));
260-
}
261-
262-
protected static Slice getAsText(Slice input)
263-
{
264-
return utf8Slice(wktFromJtsGeometry(deserialize(input)));
265-
}
266-
267-
private static Slice stGeomFromBinary(Slice input)
268-
{
269-
requireNonNull(input, "input is null");
270-
OGCGeometry geometry;
271-
try {
272-
geometry = fromBinary(input.toByteBuffer().slice());
273-
}
274-
catch (IllegalArgumentException | IndexOutOfBoundsException e) {
275-
throw new PrestoException(INVALID_FUNCTION_ARGUMENT, "Invalid Well-Known Binary (WKB)", e);
276-
}
277-
geometry.setSpatialReference(null);
278-
return serialize(geometry);
279-
}
280-
281245
@Override
282246
public void createTable(ConnectorSession session, ConnectorTableMetadata tableMetadata)
283247
{

presto-mysql/src/test/java/com/facebook/presto/plugin/mysql/TestMySqlClient.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,8 @@
2727
import java.sql.SQLException;
2828

2929
import static com.facebook.presto.geospatial.GeoFunctions.stGeomFromBinary;
30-
import static com.facebook.presto.plugin.mysql.MySqlClient.geometryReadMapping;
31-
import static com.facebook.presto.plugin.mysql.MySqlClient.getAsText;
30+
import static com.facebook.presto.plugin.jdbc.GeometryUtils.getAsText;
31+
import static com.facebook.presto.plugin.jdbc.mapping.StandardColumnMappings.geometryReadMapping;
3232
import static org.testng.Assert.assertEquals;
3333
import static org.testng.Assert.fail;
3434

presto-postgresql/pom.xml

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,24 @@
100100
<scope>provided</scope>
101101
</dependency>
102102

103+
<dependency>
104+
<groupId>com.esri.geometry</groupId>
105+
<artifactId>esri-geometry-api</artifactId>
106+
</dependency>
107+
108+
<dependency>
109+
<groupId>org.openjdk.jol</groupId>
110+
<artifactId>jol-core</artifactId>
111+
<scope>provided</scope>
112+
</dependency>
113+
103114
<!-- for testing -->
115+
<dependency>
116+
<groupId>com.h2database</groupId>
117+
<artifactId>h2</artifactId>
118+
<scope>test</scope>
119+
</dependency>
120+
104121
<dependency>
105122
<groupId>com.facebook.presto</groupId>
106123
<artifactId>presto-testng-services</artifactId>

presto-postgresql/src/main/java/com/facebook/presto/plugin/postgresql/PostgreSqlClient.java

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,12 @@
2121
import com.facebook.presto.plugin.jdbc.BaseJdbcClient;
2222
import com.facebook.presto.plugin.jdbc.BaseJdbcConfig;
2323
import com.facebook.presto.plugin.jdbc.DriverConnectionFactory;
24+
import com.facebook.presto.plugin.jdbc.JdbcColumnHandle;
2425
import com.facebook.presto.plugin.jdbc.JdbcConnectorId;
2526
import com.facebook.presto.plugin.jdbc.JdbcIdentity;
27+
import com.facebook.presto.plugin.jdbc.JdbcSplit;
2628
import com.facebook.presto.plugin.jdbc.JdbcTypeHandle;
29+
import com.facebook.presto.plugin.jdbc.QueryBuilder;
2730
import com.facebook.presto.plugin.jdbc.mapping.ReadMapping;
2831
import com.facebook.presto.spi.ConnectorSession;
2932
import com.facebook.presto.spi.ConnectorTableMetadata;
@@ -48,22 +51,30 @@
4851
import java.sql.PreparedStatement;
4952
import java.sql.ResultSet;
5053
import java.sql.SQLException;
54+
import java.util.List;
55+
import java.util.Map;
5156
import java.util.Optional;
5257
import java.util.UUID;
5358

59+
import static com.facebook.presto.common.type.StandardTypes.GEOMETRY;
60+
import static com.facebook.presto.common.type.StandardTypes.SPHERICAL_GEOGRAPHY;
5461
import static com.facebook.presto.common.type.VarbinaryType.VARBINARY;
5562
import static com.facebook.presto.plugin.jdbc.JdbcErrorCode.JDBC_ERROR;
63+
import static com.facebook.presto.plugin.jdbc.QueryBuilder.quote;
5664
import static com.facebook.presto.plugin.jdbc.mapping.ReadMapping.createSliceReadMapping;
65+
import static com.facebook.presto.plugin.jdbc.mapping.StandardColumnMappings.geometryReadMapping;
5766
import static com.facebook.presto.spi.StandardErrorCode.ALREADY_EXISTS;
5867
import static com.facebook.presto.spi.StandardErrorCode.INVALID_FUNCTION_ARGUMENT;
5968
import static com.fasterxml.jackson.core.JsonFactory.Feature.CANONICALIZE_FIELD_NAMES;
6069
import static com.fasterxml.jackson.databind.SerializationFeature.ORDER_MAP_ENTRIES_BY_KEYS;
70+
import static com.google.common.collect.ImmutableMap.toImmutableMap;
6171
import static io.airlift.slice.Slices.utf8Slice;
6272
import static io.airlift.slice.Slices.wrappedLongArray;
6373
import static java.lang.Long.reverseBytes;
6474
import static java.lang.String.format;
6575
import static java.nio.charset.StandardCharsets.UTF_8;
6676
import static java.util.Locale.ENGLISH;
77+
import static java.util.function.Function.identity;
6778

6879
public class PostgreSqlClient
6980
extends BaseJdbcClient
@@ -116,16 +127,44 @@ protected String toSqlType(Type type)
116127
return super.toSqlType(type);
117128
}
118129

130+
@Override
131+
public PreparedStatement buildSql(ConnectorSession session, Connection connection, JdbcSplit split, List<JdbcColumnHandle> columnHandles) throws SQLException
132+
{
133+
Map<String, String> columnExpressions = columnHandles.stream()
134+
.filter(handle -> handle.getJdbcTypeHandle().getJdbcTypeName().equalsIgnoreCase(GEOMETRY))
135+
.map(JdbcColumnHandle::getColumnName)
136+
.collect(toImmutableMap(
137+
identity(),
138+
columnName -> "ST_AsBinary(" + quote(identifierQuote, columnName) + ")"));
139+
140+
return new QueryBuilder(identifierQuote).buildSql(
141+
this,
142+
session,
143+
connection,
144+
split.getCatalogName(),
145+
split.getSchemaName(),
146+
split.getTableName(),
147+
columnHandles,
148+
columnExpressions,
149+
split.getTupleDomain(),
150+
split.getAdditionalPredicate());
151+
}
152+
119153
@Override
120154
public Optional<ReadMapping> toPrestoType(ConnectorSession session, JdbcTypeHandle typeHandle)
121155
{
156+
String typeName = typeHandle.getJdbcTypeName();
157+
122158
if (typeHandle.getJdbcTypeName().equals("jsonb") || typeHandle.getJdbcTypeName().equals("json")) {
123159
return Optional.of(jsonReadMapping());
124160
}
125161

126162
else if (typeHandle.getJdbcTypeName().equals("uuid")) {
127163
return Optional.of(uuidReadMapping());
128164
}
165+
else if (typeName.equalsIgnoreCase(GEOMETRY) || typeName.equalsIgnoreCase(SPHERICAL_GEOGRAPHY)) {
166+
return Optional.of(geometryReadMapping());
167+
}
129168
return super.toPrestoType(session, typeHandle);
130169
}
131170

0 commit comments

Comments
 (0)