Skip to content

Commit 7e64216

Browse files
Mariamalmesferpratyakshsharmaagrawalreetika
authored andcommitted
Add support for geometry and geography in postgres connector data type in postgres connector
Co-authored-by: pratyakshsharma <pratyaksh13@gmail.com>3@gmail.com> Co-authored-by: agrawalreetika <agrawal.reetika786@gmail.com>
1 parent 21a37b8 commit 7e64216

10 files changed

Lines changed: 228 additions & 45 deletions

File tree

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/StandardReadMappings.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,8 +41,11 @@
4141
import static com.facebook.presto.common.type.TimestampType.TIMESTAMP;
4242
import static com.facebook.presto.common.type.TinyintType.TINYINT;
4343
import static com.facebook.presto.common.type.VarbinaryType.VARBINARY;
44+
import static com.facebook.presto.common.type.VarcharType.VARCHAR;
4445
import static com.facebook.presto.common.type.VarcharType.createUnboundedVarcharType;
4546
import static com.facebook.presto.common.type.VarcharType.createVarcharType;
47+
import static com.facebook.presto.plugin.jdbc.GeometryUtils.getAsText;
48+
import static com.facebook.presto.plugin.jdbc.GeometryUtils.stGeomFromBinary;
4649
import static com.facebook.presto.plugin.jdbc.ReadMapping.longReadMapping;
4750
import static com.facebook.presto.plugin.jdbc.ReadMapping.sliceReadMapping;
4851
import static io.airlift.slice.Slices.utf8Slice;
@@ -234,4 +237,9 @@ public static Optional<ReadMapping> jdbcTypeToPrestoType(JdbcTypeHandle type)
234237
}
235238
return Optional.empty();
236239
}
240+
public static ReadMapping geometryReadMapping()
241+
{
242+
return sliceReadMapping(VARCHAR,
243+
(resultSet, columnIndex) -> getAsText(stGeomFromBinary(wrappedBuffer(resultSet.getBytes(columnIndex)))));
244+
}
237245
}

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

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,10 @@ The connector maps PostgreSQL types to the corresponding PrestoDB types:
141141
- ``JSON``
142142
* - ``JSONB``
143143
- ``JSON``
144-
144+
* - ``GEOMETRY``
145+
- ``VARCHAR``
146+
* - ``GEOGRAPHY``
147+
- ``VARCHAR``
145148
No other types are supported.
146149

147150
PrestoDB to PostgreSQL type mapping

presto-mysql/pom.xml

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

79-
<dependency>
80-
<groupId>com.facebook.presto</groupId>
81-
<artifactId>presto-geospatial-toolkit</artifactId>
82-
</dependency>
83-
8479
<dependency>
8580
<groupId>com.facebook.drift</groupId>
8681
<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.ReadMapping.sliceReadMapping;
62+
import static com.facebook.presto.plugin.jdbc.StandardReadMappings.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 sliceReadMapping(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.StandardReadMappings.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
@@ -99,7 +99,24 @@
9999
<scope>provided</scope>
100100
</dependency>
101101

102+
<dependency>
103+
<groupId>com.esri.geometry</groupId>
104+
<artifactId>esri-geometry-api</artifactId>
105+
</dependency>
106+
107+
<dependency>
108+
<groupId>org.openjdk.jol</groupId>
109+
<artifactId>jol-core</artifactId>
110+
<scope>provided</scope>
111+
</dependency>
112+
102113
<!-- for testing -->
114+
<dependency>
115+
<groupId>com.h2database</groupId>
116+
<artifactId>h2</artifactId>
117+
<scope>test</scope>
118+
</dependency>
119+
103120
<dependency>
104121
<groupId>com.facebook.presto</groupId>
105122
<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.ReadMapping;
2831
import com.facebook.presto.spi.ConnectorSession;
2932
import com.facebook.presto.spi.ConnectorTableMetadata;
@@ -48,20 +51,28 @@
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;
64+
import static com.facebook.presto.plugin.jdbc.StandardReadMappings.geometryReadMapping;
5665
import static com.facebook.presto.spi.StandardErrorCode.ALREADY_EXISTS;
5766
import static com.facebook.presto.spi.StandardErrorCode.INVALID_FUNCTION_ARGUMENT;
5867
import static com.fasterxml.jackson.core.JsonFactory.Feature.CANONICALIZE_FIELD_NAMES;
5968
import static com.fasterxml.jackson.databind.SerializationFeature.ORDER_MAP_ENTRIES_BY_KEYS;
69+
import static com.google.common.collect.ImmutableMap.toImmutableMap;
6070
import static io.airlift.slice.Slices.utf8Slice;
6171
import static io.airlift.slice.Slices.wrappedLongArray;
6272
import static java.lang.Long.reverseBytes;
6373
import static java.lang.String.format;
6474
import static java.nio.charset.StandardCharsets.UTF_8;
75+
import static java.util.function.Function.identity;
6576

6677
public class PostgreSqlClient
6778
extends BaseJdbcClient
@@ -114,16 +125,44 @@ protected String toSqlType(Type type)
114125
return super.toSqlType(type);
115126
}
116127

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

124160
else if (typeHandle.getJdbcTypeName().equals("uuid")) {
125161
return Optional.of(uuidColumnMapping());
126162
}
163+
else if (typeName.equalsIgnoreCase(GEOMETRY) || typeName.equalsIgnoreCase(SPHERICAL_GEOGRAPHY)) {
164+
return Optional.of(geometryReadMapping());
165+
}
127166
return super.toPrestoType(session, typeHandle);
128167
}
129168

0 commit comments

Comments
 (0)