[SCM] PostGIS branch master updated. 3.6.0rc2-488-gd606f127f
git at osgeo.org
git at osgeo.org
Wed May 27 15:18:44 PDT 2026
This is an automated email from the git hooks/post-receive script. It was
generated because a ref change was pushed to the repository containing
the project "PostGIS".
The branch, master has been updated
via d606f127ffd1dc6ae1a7e415a1b293f8cbaa57c7 (commit)
via a12dd5a9e5952e1bfefb32dd891a43f781bd5e42 (commit)
via 82f8e34b61f0f2b29bba3b408e81015d226573ec (commit)
from c90277228b4fd73680502f7756721fbbf00749c2 (commit)
Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.
- Log -----------------------------------------------------------------
commit d606f127ffd1dc6ae1a7e415a1b293f8cbaa57c7
Merge: c90277228 a12dd5a9e
Author: Paul Ramsey <pramsey at cleverelephant.ca>
Date: Wed May 27 15:18:31 2026 -0700
Merge branch 'master-flatbuf'
commit a12dd5a9e5952e1bfefb32dd891a43f781bd5e42
Author: Paul Ramsey <pramsey at cleverelephant.ca>
Date: Wed May 27 21:53:39 2026 +0000
Add tests for flatbuffer schema checking
diff --git a/regress/core/flatgeobuf.sql b/regress/core/flatgeobuf.sql
index df46eca1e..7fb731aa1 100644
--- a/regress/core/flatgeobuf.sql
+++ b/regress/core/flatgeobuf.sql
@@ -158,6 +158,38 @@ select 'E1', id, bool_1, ST_AsText(geom), bool_2 from ST_FromFlatGeobuf(null::fl
) q)
);
+select '--- Type mismatch detection ---';
+
+-- Setup: a long (bigint) column and a text column in separate tables
+select ST_FromFlatGeobufToTable('public', 'flatgeobuf_mm_long', (select ST_AsFlatGeobuf(q) fgb from (select
+ null::geometry, null::bigint as val) q));
+select ST_FromFlatGeobufToTable('public', 'flatgeobuf_mm_text', (select ST_AsFlatGeobuf(q) fgb from (select
+ null::geometry, null::text as val) q));
+select ST_FromFlatGeobufToTable('public', 'flatgeobuf_mm_twocols', (select ST_AsFlatGeobuf(q) fgb from (select
+ null::geometry, null::bigint as val1, null::bigint as val2) q));
+
+-- Type mismatch: file has bigint (long), target expects text
+select 'MM1' from ST_FromFlatGeobuf(null::flatgeobuf_mm_text, (
+ select ST_AsFlatGeobuf(q) fgb from (select null::geometry, 42::bigint as val) q));
+
+-- Count mismatch: file has 2 property columns, target type has 1
+select 'MM2' from ST_FromFlatGeobuf(null::flatgeobuf_mm_long, (
+ select ST_AsFlatGeobuf(q) fgb from (select null::geometry, 42::bigint as val1, 43::bigint as val2) q));
+
+select '--- Numeric widening ---';
+
+-- integer (int32 in file) widened to bigint in target
+select ST_FromFlatGeobufToTable('public', 'flatgeobuf_widen_i', (select ST_AsFlatGeobuf(q) fgb from (select
+ null::geometry, null::bigint as val) q));
+select 'W1', id, val from ST_FromFlatGeobuf(null::flatgeobuf_widen_i, (
+ select ST_AsFlatGeobuf(q) fgb from (select null::geometry, 42::integer as val) q));
+
+-- real (float4 in file) widened to double precision in target
+select ST_FromFlatGeobufToTable('public', 'flatgeobuf_widen_f', (select ST_AsFlatGeobuf(q) fgb from (select
+ null::geometry, null::double precision as val) q));
+select 'W2', id, val from ST_FromFlatGeobuf(null::flatgeobuf_widen_f, (
+ select ST_AsFlatGeobuf(q) fgb from (select null::geometry, 1.5::real as val) q));
+
select '--- Quoted identifiers ---';
-- Verify that special characters in column names are properly quoted
@@ -181,3 +213,8 @@ drop table if exists public.flatgeobuf_t1;
drop table if exists public.flatgeobuf_a1;
drop table if exists public.flatgeobuf_e1;
drop table if exists public.flatgeobuf_qi;
+drop table if exists public.flatgeobuf_mm_long;
+drop table if exists public.flatgeobuf_mm_text;
+drop table if exists public.flatgeobuf_mm_twocols;
+drop table if exists public.flatgeobuf_widen_i;
+drop table if exists public.flatgeobuf_widen_f;
diff --git a/regress/core/flatgeobuf_expected b/regress/core/flatgeobuf_expected
index c93738ff9..ea7e022cc 100644
--- a/regress/core/flatgeobuf_expected
+++ b/regress/core/flatgeobuf_expected
@@ -24,6 +24,12 @@ ERROR: mixed geometry type is not supported
A1|0||t|1|2|3|4|1.2|1.3|2016-06-23 03:44:52.134125+00|hello
--- Exotic roundtrips ---
E1|0|t|POINT(1.1 2.1)|f
+--- Type mismatch detection ---
+ERROR: flatgeobuf: column "val" type mismatch: file type "Long" is not compatible with PostgreSQL type text
+ERROR: flatgeobuf: column count mismatch: file has 2 columns, target type has 1
+--- Numeric widening ---
+W1|0|42
+W2|0|1.5
--- Quoted identifiers ---
QI1
QI2
commit 82f8e34b61f0f2b29bba3b408e81015d226573ec
Author: Paul Ramsey <pramsey at cleverelephant.ca>
Date: Wed May 27 21:52:25 2026 +0000
Check that user-provided flatbuffer schema matches
The flatbuffer import maps an incoming file to a provided
table schema, and the consistency of those two schemes
needs to be checked before data are copied across.
diff --git a/postgis/flatgeobuf.c b/postgis/flatgeobuf.c
index ae3f8fb53..c984557dd 100644
--- a/postgis/flatgeobuf.c
+++ b/postgis/flatgeobuf.c
@@ -287,8 +287,6 @@ static void decode_properties(struct flatgeobuf_decode_ctx *ctx, Datum *values,
POSTGIS_DEBUGF(3, "flatgeobuf: decode_properties from byte array with length %d at offset %d", size, offset);
- // TODO: init isnull
-
if (size > 0 && size < (sizeof(uint16_t) + sizeof(uint8_t)))
elog(ERROR, "flatgeobuf: decode_properties: Unexpected properties data size %d", size);
while (offset + 1 < size) {
@@ -389,7 +387,10 @@ static void decode_properties(struct flatgeobuf_decode_ctx *ctx, Datum *values,
if (offset + sizeof(float) > size)
elog(ERROR, "flatgeobuf: decode_properties: Invalid size for float value");
memcpy(&value, data + offset, sizeof(float));
- values[ci] = Float4GetDatum(value);
+ if (getBaseType(TupleDescAttr(ctx->tupdesc, ci)->atttypid) == FLOAT8OID)
+ values[ci] = Float8GetDatum((double) value);
+ else
+ values[ci] = Float4GetDatum(value);
offset += sizeof(float);
break;
}
@@ -467,7 +468,9 @@ void flatgeobuf_decode_row(struct flatgeobuf_decode_ctx *ctx)
uint32_t natts = ctx->tupdesc->natts;
Datum *values = palloc0(natts * sizeof(Datum));
- bool *isnull = palloc0(natts * sizeof(bool));
+ bool *isnull = palloc(natts * sizeof(bool));
+ for (uint32_t j = 0; j < natts; j++) isnull[j] = true;
+ isnull[0] = false;
values[0] = Int32GetDatum(ctx->fid);
@@ -476,6 +479,7 @@ void flatgeobuf_decode_row(struct flatgeobuf_decode_ctx *ctx)
if (ctx->ctx->lwgeom != NULL) {
values[1] = PointerGetDatum(geometry_serialize(ctx->ctx->lwgeom));
+ isnull[1] = false;
} else {
POSTGIS_DEBUG(3, "geometry is null");
isnull[1] = true;
diff --git a/postgis/lwgeom_in_flatgeobuf.c b/postgis/lwgeom_in_flatgeobuf.c
index 97ed26b16..8fc2e6efb 100644
--- a/postgis/lwgeom_in_flatgeobuf.c
+++ b/postgis/lwgeom_in_flatgeobuf.c
@@ -67,6 +67,60 @@ static char *get_pgtype(uint8_t column_type) {
elog(ERROR, "unknown column_type %d", column_type);
}
+static const char *
+flatgeobuf_type_name(uint8_t fgb_type)
+{
+ /* Names match FlatGeobuf::EnumNamesColumnType() in header_generated.h */
+ static const char * const names[] = {
+ "Byte", "UByte", "Bool", "Short", "UShort",
+ "Int", "UInt", "Long", "ULong",
+ "Float", "Double", "String", "Json", "DateTime", "Binary"
+ };
+ if (fgb_type >= sizeof(names) / sizeof(names[0]))
+ return "unknown";
+ return names[fgb_type];
+}
+
+static bool
+flatgeobuf_type_compatible(uint8_t fgb_type, Oid pgtype)
+{
+ switch (fgb_type)
+ {
+ case flatgeobuf_column_type_bool:
+ return pgtype == BOOLOID;
+ /* small integer types: allow widening into larger signed ints */
+ case flatgeobuf_column_type_byte:
+ case flatgeobuf_column_type_ubyte:
+ case flatgeobuf_column_type_short:
+ case flatgeobuf_column_type_ushort:
+ return pgtype == INT2OID || pgtype == INT4OID || pgtype == INT8OID;
+ /* int32: allow widening to bigint */
+ case flatgeobuf_column_type_int:
+ return pgtype == INT4OID || pgtype == INT8OID;
+ /* uint32 max exceeds INT4, must land in bigint */
+ case flatgeobuf_column_type_uint:
+ return pgtype == INT8OID;
+ case flatgeobuf_column_type_long:
+ case flatgeobuf_column_type_ulong:
+ return pgtype == INT8OID;
+ /* float: allow widening to double (explicit conversion handled in decode) */
+ case flatgeobuf_column_type_float:
+ return pgtype == FLOAT4OID || pgtype == FLOAT8OID;
+ case flatgeobuf_column_type_double:
+ return pgtype == FLOAT8OID;
+ case flatgeobuf_column_type_string:
+ return pgtype == TEXTOID || pgtype == VARCHAROID;
+ case flatgeobuf_column_type_datetime:
+ return pgtype == DATEOID || pgtype == TIMEOID ||
+ pgtype == TIMESTAMPOID || pgtype == TIMESTAMPTZOID;
+ case flatgeobuf_column_type_json:
+ return pgtype == JSONBOID;
+ case flatgeobuf_column_type_binary:
+ return pgtype == BYTEAOID;
+ }
+ return false;
+}
+
PG_FUNCTION_INFO_V1(pgis_tablefromflatgeobuf);
Datum pgis_tablefromflatgeobuf(PG_FUNCTION_ARGS)
{
@@ -191,7 +245,29 @@ Datum pgis_fromflatgeobuf(PG_FUNCTION_ARGS)
SRF_RETURN_DONE(funcctx);
}
- // TODO: get table and verify structure against header
+ /* Validate that the file schema matches the caller-supplied composite type.
+ * tupdesc positions 0 (fid) and 1 (geom) are fixed; file columns start at 2. */
+ if (ctx->ctx->columns_size != (uint32_t)(tupdesc->natts - 2))
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("flatgeobuf: column count mismatch: "
+ "file has %u columns, target type has %d",
+ ctx->ctx->columns_size, tupdesc->natts - 2)));
+
+ for (uint16_t col_i = 0; col_i < ctx->ctx->columns_size; col_i++)
+ {
+ flatgeobuf_column *col = ctx->ctx->columns[col_i];
+ Oid pgtype = getBaseType(TupleDescAttr(tupdesc, col_i + 2)->atttypid);
+ if (!flatgeobuf_type_compatible(col->type, pgtype))
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("flatgeobuf: column \"%s\" type mismatch: "
+ "file type \"%s\" is not compatible with PostgreSQL type %s",
+ col->name,
+ flatgeobuf_type_name(col->type),
+ format_type_be(pgtype))));
+ }
+
MemoryContextSwitchTo(oldcontext);
}
-----------------------------------------------------------------------
Summary of changes:
postgis/flatgeobuf.c | 12 ++++---
postgis/lwgeom_in_flatgeobuf.c | 78 +++++++++++++++++++++++++++++++++++++++-
regress/core/flatgeobuf.sql | 37 +++++++++++++++++++
regress/core/flatgeobuf_expected | 6 ++++
4 files changed, 128 insertions(+), 5 deletions(-)
hooks/post-receive
--
PostGIS
More information about the postgis-tickets
mailing list