Skip to content

Commit

Permalink
Merge pull request #291 from Mytherin/postgis
Browse files Browse the repository at this point in the history
Working PostGIS integration by converting Postgres Geometry to WKB_BLOB
  • Loading branch information
Mytherin authored Jan 29, 2025
2 parents aeaf8b5 + 2a2d308 commit dbc8db0
Show file tree
Hide file tree
Showing 3 changed files with 110 additions and 1 deletion.
11 changes: 10 additions & 1 deletion src/postgres_copy_to.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -260,15 +260,24 @@ void CastBlobToPostgres(ClientContext &context, Vector &input, Vector &result, i
}
}

void CastGeometryToPostgres(ClientContext &context, Vector &input, Vector &result, idx_t size) {
VectorOperations::Cast(context, input, result, size);
}

void CastToPostgresVarchar(ClientContext &context, Vector &input, Vector &result, idx_t size) {
switch (input.GetType().id()) {
auto &type = input.GetType();
switch (type.id()) {
case LogicalTypeId::LIST:
CastListToPostgresArray(context, input, result, size);
break;
case LogicalTypeId::STRUCT:
CastStructToPostgres(context, input, result, size);
break;
case LogicalTypeId::BLOB:
if (type.HasAlias() && StringUtil::CIEquals(type.GetAlias(), "wkb_blob")) {
CastGeometryToPostgres(context, input, result, size);
break;
}
CastBlobToPostgres(context, input, result, size);
break;
default:
Expand Down
14 changes: 14 additions & 0 deletions src/postgres_utils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@ PGconn *PostgresUtils::PGConnect(const string &dsn) {

string PostgresUtils::TypeToString(const LogicalType &input) {
if (input.HasAlias()) {
if (StringUtil::CIEquals(input.GetAlias(), "wkb_blob")) {
return "GEOMETRY";
}
return input.GetAlias();
}
switch (input.id()) {
Expand All @@ -47,13 +50,22 @@ string PostgresUtils::TypeToString(const LogicalType &input) {
}
}

LogicalType GetGeometryType() {
auto blob_type = LogicalType(LogicalTypeId::BLOB);
blob_type.SetAlias("WKB_BLOB");
return blob_type;
}

LogicalType PostgresUtils::RemoveAlias(const LogicalType &type) {
if (!type.HasAlias()) {
return type;
}
if (StringUtil::CIEquals(type.GetAlias(), "json")) {
return type;
}
if (StringUtil::CIEquals(type.GetAlias(), "geometry")) {
return GetGeometryType();
}
switch (type.id()) {
case LogicalTypeId::STRUCT: {
auto child_types = StructType::GetChildTypes(type);
Expand Down Expand Up @@ -144,6 +156,8 @@ LogicalType PostgresUtils::TypeToLogicalType(optional_ptr<PostgresTransaction> t
} else if (pgtypename == "jsonb") {
postgres_type.info = PostgresTypeAnnotation::JSONB;
return LogicalType::VARCHAR;
} else if (pgtypename == "geometry") {
return GetGeometryType();
} else if (pgtypename == "date") {
return LogicalType::DATE;
} else if (pgtypename == "bytea") {
Expand Down
86 changes: 86 additions & 0 deletions test/sql/storage/attach_postgis.test
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
# name: test/sql/storage/attach_postgis.test
# description: Test PostGIS integration
# group: [storage]

require postgres_scanner

require-env POSTGRES_TEST_DATABASE_AVAILABLE

# e.g. export SPATIAL_EXTENSION='~/Programs/duckdb-spatial/build/debug/extension/spatial/spatial.duckdb_extension'
require-env SPATIAL_EXTENSION

statement ok
PRAGMA enable_verification

statement ok
ATTACH 'dbname=postgres' AS s (TYPE POSTGRES);

# make sure PostGIS is installed
statement ok
SELECT * FROM postgres_query(s, 'SELECT PostGIS_Version()')

# spatial not loaded yet
statement error
CREATE OR REPLACE TABLE s.my_points(geom GEOMETRY);
----
spatial

statement ok
LOAD '${SPATIAL_EXTENSION}'

# create a table
statement ok
CREATE OR REPLACE TABLE s.my_points(geom GEOMETRY);

# insert data
statement ok
INSERT INTO s.my_points VALUES (ST_Point(1,1));

# try binary copy
query I
SELECT geom::VARCHAR FROM s.my_points;
----
POINT (1 1)

# text copy
statement ok
SET pg_use_binary_copy=false;

statement ok
INSERT INTO s.my_points VALUES (ST_Point(2,2));

query I
SELECT geom::VARCHAR FROM s.my_points;
----
POINT (1 1)
POINT (2 2)

# make sure Postgres itself can read the values
query I
SELECT * FROM postgres_query(s, 'SELECT ST_AsText(geom) FROM my_points')
----
POINT(1 1)
POINT(2 2)

# re-attach
statement ok
DETACH s

statement ok
ATTACH 'dbname=postgres' AS s (TYPE POSTGRES);

query I
SELECT geom::VARCHAR FROM s.my_points;
----
POINT (1 1)
POINT (2 2)

# update
statement ok
UPDATE s.my_points SET geom=ST_Point(ST_X(geom::GEOMETRY) + 10, ST_Y(geom::GEOMETRY) + 10)

query I
SELECT geom::VARCHAR FROM s.my_points;
----
POINT (11 11)
POINT (12 12)

0 comments on commit dbc8db0

Please sign in to comment.