[Git][debian-gis-team/pgsql-ogr-fdw][upstream] New upstream version 1.1.5
Bas Couwenberg (@sebastic)
gitlab at salsa.debian.org
Fri Jun 28 06:16:19 BST 2024
Bas Couwenberg pushed to branch upstream at Debian GIS Project / pgsql-ogr-fdw
Commits:
669ced5e by Bas Couwenberg at 2024-06-28T06:12:57+02:00
New upstream version 1.1.5
- - - - -
10 changed files:
- .github/workflows/ci.yml
- Makefile
- data/no_geom_apost.csv
- input/import.source
- input/pgsql.source
- ogr_fdw.c
- ogr_fdw.h
- ogr_fdw_common.c
- output/import.source
- output/pgsql.source
Changes:
=====================================
.github/workflows/ci.yml
=====================================
@@ -20,12 +20,13 @@ jobs:
- { PGVER: 14 }
- { PGVER: 15 }
- { PGVER: 16 }
+ - { PGVER: 17 }
runs-on: ubuntu-latest
steps:
- name: 'Check Out'
- uses: actions/checkout at v3
+ uses: actions/checkout at v4
- name: 'Raise Priority for apt.postgresql.org'
run: |
=====================================
Makefile
=====================================
@@ -28,13 +28,16 @@ REGRESS_OPTS = --encoding=UTF8
PG_CPPFLAGS += $(GDAL_CFLAGS)
LIBS += $(GDAL_LIBS)
SHLIB_LINK := $(LIBS)
+# For MacOS
+#SHLIB_LINK += -rpath /usr/local/lib
PGXS := $(shell $(PG_CONFIG) --pgxs)
include $(PGXS)
-PG_VERSION_NUM = $(shell awk '/PG_VERSION_NUM/ { print $$3 }' $(shell $(PG_CONFIG) --includedir-server)/pg_config.h)
+PG_VERSION_NUM = $(shell pg_config --version | cut -f2 -d' ' | awk -F. '{printf "%d%04d", $$1, $$2}')
HAS_IMPORT_SCHEMA = $(shell [ $(PG_VERSION_NUM) -ge 90500 ] && echo yes)
+
# order matters, file first, import last
REGRESS = file pgsql
ifeq ($(HAS_IMPORT_SCHEMA),yes)
=====================================
data/no_geom_apost.csv
=====================================
@@ -3,3 +3,4 @@ Peter,34,10.2
John,77,3.4
Paul,45,19.2
Matthew,35,18.2
+"",44,34.3
=====================================
input/import.source
=====================================
@@ -60,6 +60,8 @@ FROM information_schema._pg_foreign_table_columns AS fc
WHERE fc.nspname = 'imp3' and fc.relname = 'no_geom_apost'
ORDER BY c.ordinal_position;
-SELECT * FROM imp3.no_geom_apost;
+SELECT name,age FROM imp3.no_geom_apost WHERE name = 'Paul';
+SELECT name,age FROM imp3.no_geom_apost WHERE name IS NULL;
+SELECT name,age FROM imp3.no_geom_apost WHERE name = '';
------------------------------------------------
=====================================
input/pgsql.source
=====================================
@@ -19,12 +19,10 @@ CREATE TABLE bytea_local (
----------------------------------------------------------------------
-- Populate local table
-INSERT INTO bytea_local (name, geom, age, size, value, num, dt, tm, dttm, varch, yn)
- VALUES ('Jim', '14232'::bytea, 23, 1, 4.3, 5.5, '2010-10-10'::date, '13:23:21'::time, '2010-10-10 13:23:21'::timestamp, 'this', 'y' );
-INSERT INTO bytea_local (name, geom, age, size, value, num, dt, tm, dttm, varch, yn)
- VALUES ('Marvin', '55555'::bytea, 34, 2, 5.4, 10.13, '2011-11-11'::date, '15:21:45'::time, '2011-11-11 15:21:45'::timestamp, 'that', 'n' );
-INSERT INTO bytea_local (name, geom, age, size, value, num, dt, tm, dttm, varch, yn)
- VALUES (NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
+INSERT INTO bytea_local (name, geom, age, size, value, num, dt, tm, dttm, varch, yn) VALUES
+ ('Jim', '14232'::bytea, 23, 1, 4.3, 5.5, '2010-10-10'::date, '13:23:21'::time, '2010-10-10 13:23:21'::timestamp, 'this', 'y' ),
+ ('Marvin', '55555'::bytea, 34, 2, 5.4, 10.13, '2011-11-11'::date, '15:21:45'::time, '2011-11-11 15:21:45'::timestamp, 'that', 'n' ),
+ (NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
----------------------------------------------------------------------
-- Create remote table
@@ -134,4 +132,48 @@ SELECT a.fid, a.name, b.name
FROM bytea_local a
JOIN bytea_fdw b
USING (fid);
-
+
+----------------------------------------------------------------------
+-- Populate local array table
+
+SET client_min_messages = NOTICE;
+
+CREATE TABLE array_local (
+ fid integer primary key,
+ geom bytea,
+ txt text[],
+ int int2[],
+ flt float4[],
+ b boolean[]
+);
+
+INSERT INTO array_local (fid,txt, int, flt, b) VALUES
+ (1, ARRAY['Jim'], ARRAY[1,2,3], ARRAY[3.4,5.6,7.8], ARRAY[true,false]),
+ (2, ARRAY['Jim',NULL,'Joe'], ARRAY[1,3,NULL,4], ARRAY[4.5,NULL,3.4], ARRAY[false,NULL]),
+ (3, NULL, NULL, NULL, NULL);
+
+----------------------------------------------------------------------
+-- Create remote array table
+
+CREATE FOREIGN TABLE array_fdw (
+ fid bigint,
+ geom bytea,
+ txt text[],
+ int int4[],
+ flt float8[],
+ b boolean[]
+) SERVER pgserver OPTIONS (layer 'array_local');
+
+SELECT fid, txt, int, flt, b FROM array_fdw;
+
+----------------------------------------------------------------------
+-- Update remote array table
+
+UPDATE array_fdw SET
+ txt = ARRAY['newJim', 'newJoe'],
+ int = ARRAY[-2, -1, 0, 1, 2],
+ flt = ARRAY[-0.1, 0.0, 0.1]
+WHERE fid = 3;
+
+SELECT txt, int, flt FROM array_fdw WHERE fid = 3;
+
=====================================
ogr_fdw.c
=====================================
@@ -952,8 +952,11 @@ ogrGetForeignPaths(PlannerInfo* root,
planstate->startup_cost,
planstate->total_cost,
NIL, /* no pathkeys */
- NULL, /* no outer rel either */
+ NULL, /* no lateral_relids */
NULL /* no extra plan */
+#if PG_VERSION_NUM >= 170000
+ , NIL /* no fdw_restrictinfo list */
+#endif
#if PG_VERSION_NUM >= 90500
, NIL /* no fdw_private list */
#endif
@@ -1092,175 +1095,137 @@ ogrGetForeignPlan(PlannerInfo* root,
}
-static void
-pgCanConvertToOgr(Oid pg_type, OGRFieldType ogr_type, const char* colname, const char* tblname)
+static bool
+pgCanConvertToOgr(Oid pg_type, OGRFieldType ogr_type)
{
- if (pg_type == BOOLOID && ogr_type == OFTInteger)
- {
- return;
- }
- else if (pg_type == INT2OID && ogr_type == OFTInteger)
- {
- return;
- }
- else if (pg_type == INT4OID && ogr_type == OFTInteger)
- {
- return;
- }
- else if (pg_type == INT8OID)
+ struct PgOgrMap
{
+ Oid pg;
+ OGRFieldType ogr;
+ };
+
+ static struct PgOgrMap data[] = {
+ {BOOLOID, OFTInteger}, /* 16 */
+ {BYTEAOID, OFTBinary}, /* 17 */
+ {CHAROID, OFTString}, /* 18 */
+ {NAMEOID, OFTString}, /* 19 */
#if GDAL_VERSION_MAJOR >= 2
- if (ogr_type == OFTInteger64)
- {
- return;
- }
+ {INT8OID, OFTInteger64}, /* 20 */
#else
- if (ogr_type == OFTInteger)
- {
- return;
- }
+ {INT8OID, OFTInteger}, /* 20 */
#endif
+ {INT2OID, OFTInteger}, /* 21 */
+ {INT4OID, OFTInteger}, /* 23 */
+ {TEXTOID, OFTString}, /* 25 */
+ {FLOAT4OID, OFTReal}, /* 700 */
+ {FLOAT8OID, OFTReal}, /* 701 */
+ {BOOLARRAYOID, OFTIntegerList}, /* 1000 */
+ {CHARARRAYOID, OFTStringList}, /* 1002 */
+ {NAMEARRAYOID, OFTStringList}, /* 1003 */
+ {INT2ARRAYOID, OFTIntegerList}, /* 1005 */
+ {INT4ARRAYOID, OFTIntegerList}, /* 1007 */
+ {TEXTARRAYOID, OFTStringList}, /* 1009 */
+ {VARCHARARRAYOID, OFTStringList}, /* 1015 */
+#if GDAL_VERSION_MAJOR >= 2
+ {INT8ARRAYOID, OFTInteger64List}, /* 1016 */
+#endif
+ {FLOAT4ARRAYOID, OFTRealList}, /* 1021 */
+ {FLOAT8ARRAYOID, OFTRealList}, /* 1022 */
+ {BPCHAROID, OFTString}, /* 1042 */
+ {VARCHAROID, OFTString}, /* 1043 */
+ {DATEOID, OFTDate}, /* 1082 */
+ {TIMEOID, OFTTime}, /* 1083 */
+ {TIMESTAMPOID, OFTDateTime}, /* 1114 */
+ {NUMERICOID, OFTReal}, /* 1700 */
+ {0, 0}};
+
+ struct PgOgrMap* map = data;
+ while (map->pg)
+ {
+ if (ogr_type == map->ogr) return true;
+ else map++;
}
- else if (pg_type == NUMERICOID && ogr_type == OFTReal)
- {
- return;
- }
- else if (pg_type == FLOAT4OID && ogr_type == OFTReal)
- {
- return;
- }
- else if (pg_type == FLOAT8OID && ogr_type == OFTReal)
- {
- return;
- }
- else if (pg_type == TEXTOID && ogr_type == OFTString)
- {
- return;
- }
- else if (pg_type == VARCHAROID && ogr_type == OFTString)
- {
- return;
- }
- else if (pg_type == CHAROID && ogr_type == OFTString)
- {
- return;
- }
- else if (pg_type == BPCHAROID && ogr_type == OFTString)
- {
- return;
- }
- else if (pg_type == NAMEOID && ogr_type == OFTString)
- {
- return;
- }
- else if (pg_type == BYTEAOID && ogr_type == OFTBinary)
- {
- return;
- }
- else if (pg_type == DATEOID && ogr_type == OFTDate)
- {
- return;
- }
- else if (pg_type == TIMEOID && ogr_type == OFTTime)
- {
- return;
- }
- else if (pg_type == TIMESTAMPOID && ogr_type == OFTDateTime)
- {
- return;
- }
+ return false;
- ereport(ERROR, (
- errcode(ERRCODE_FDW_INVALID_DATA_TYPE),
- errmsg("column \"%s\" of foreign table \"%s\" converts \"%s\" to OGR \"%s\"",
- colname, tblname,
- format_type_be(pg_type), OGR_GetFieldTypeName(ogr_type))
- ));
}
+
static void
-ogrCanConvertToPg(OGRFieldType ogr_type, Oid pg_type, const char* colname, const char* tblname)
+pgCheckConvertToOgr(Oid pg_type, OGRFieldType ogr_type, const char *colname, const char *tblname)
{
- switch (ogr_type)
- {
- case OFTInteger:
- if (pg_type == BOOLOID || pg_type == INT4OID || pg_type == INT8OID ||
- pg_type == NUMERICOID || pg_type == FLOAT4OID ||
- pg_type == FLOAT8OID || pg_type == TEXTOID || pg_type == VARCHAROID)
- {
- return;
- }
- break;
-
- case OFTReal:
- if (pg_type == NUMERICOID || pg_type == FLOAT4OID || pg_type == FLOAT8OID ||
- pg_type == TEXTOID || pg_type == VARCHAROID)
- {
- return;
- }
- break;
-
- case OFTBinary:
- if (pg_type == BYTEAOID)
- {
- return;
- }
- break;
+ if (pgCanConvertToOgr(pg_type, ogr_type))
+ return;
- case OFTString:
- if (pg_type == TEXTOID || pg_type == VARCHAROID || pg_type == CHAROID || pg_type == BPCHAROID)
- {
- return;
- }
- break;
+ ereport(ERROR, (
+ errcode(ERRCODE_FDW_INVALID_DATA_TYPE),
+ errmsg("column \"%s\" of foreign table \"%s\" converts \"%s\" to OGR \"%s\"",
+ colname, tblname,
+ format_type_be(pg_type), OGR_GetFieldTypeName(ogr_type))
+ ));
+}
- case OFTDate:
- if (pg_type == DATEOID || pg_type == TIMESTAMPOID || pg_type == TEXTOID || pg_type == VARCHAROID)
- {
- return;
- }
- break;
- case OFTTime:
- if (pg_type == TIMEOID || pg_type == TEXTOID || pg_type == VARCHAROID)
+static bool
+ogrCanConvertToPg(OGRFieldType ogr_type, Oid pg_type)
+{
+ struct OgrPgMap
+ {
+ OGRFieldType ogr;
+ Oid pg[16];
+ };
+
+ static struct OgrPgMap data[] =
+ {
+ {OFTInteger, {BOOLOID, INT4OID, INT8OID, NUMERICOID, FLOAT4OID, FLOAT8OID, TEXTOID, VARCHAROID, 0}},
+ {OFTReal, {NUMERICOID, FLOAT4OID, FLOAT8OID, TEXTOID, VARCHAROID, 0}},
+ {OFTBinary, {BYTEAOID, 0}},
+ {OFTString, {TEXTOID, VARCHAROID, CHAROID, BPCHAROID, 0}},
+ {OFTDate, {DATEOID, TIMESTAMPOID, TEXTOID, VARCHAROID, 0}},
+ {OFTTime, {TIMEOID, TEXTOID, VARCHAROID, 0}},
+ {OFTDateTime, {TIMESTAMPOID, TEXTOID, VARCHAROID, 0}},
+ #if GDAL_VERSION_MAJOR >= 2
+ {OFTInteger64, {INT8OID, NUMERICOID, FLOAT8OID, TEXTOID, VARCHAROID, 0}},
+ {OFTInteger64List, {INT8ARRAYOID, FLOAT8ARRAYOID, TEXTARRAYOID, VARCHARARRAYOID, 0}},
+ #endif
+ {OFTRealList, {FLOAT4ARRAYOID, FLOAT8ARRAYOID, TEXTARRAYOID, VARCHARARRAYOID, 0}},
+ {OFTStringList, {TEXTARRAYOID, VARCHARARRAYOID, NAMEARRAYOID, CHARARRAYOID, 0}},
+ {OFTIntegerList, {BOOLARRAYOID, INT2ARRAYOID, INT4ARRAYOID, INT8ARRAYOID, TEXTARRAYOID, VARCHARARRAYOID, 0}},
+ {256, {0}} /* Zero terminate list */
+ };
+
+ struct OgrPgMap* map = data;
+ while (map->ogr <= OFTMaxType)
+ {
+ if (ogr_type == map->ogr)
{
- return;
+ Oid *typ = map->pg;
+ while (*typ)
+ {
+ if (pg_type == *typ) return true;
+ else typ++;
+ }
}
- break;
+ map++;
+ }
- case OFTDateTime:
- if (pg_type == TIMESTAMPOID || pg_type == TEXTOID || pg_type == VARCHAROID)
- {
- return;
- }
- break;
+ return false;
+}
-#if GDAL_VERSION_MAJOR >= 2
- case OFTInteger64:
- if (pg_type == INT8OID || pg_type == NUMERICOID || pg_type == FLOAT8OID ||
- pg_type == TEXTOID || pg_type == VARCHAROID)
- {
- return;
- }
- break;
-#endif
+static void
+ogrCheckConvertToPg(OGRFieldType ogr_type, Oid pg_type, const char *colname, const char *tblname)
+{
+ if (ogrCanConvertToPg(ogr_type, pg_type))
+ return;
- case OFTWideString:
- case OFTIntegerList:
-#if GDAL_VERSION_MAJOR >= 2
- case OFTInteger64List:
-#endif
- case OFTRealList:
- case OFTStringList:
- case OFTWideStringList:
+ if (ogr_type == OFTWideString || ogr_type == OFTWideStringList)
{
ereport(ERROR, (
errcode(ERRCODE_FDW_INVALID_DATA_TYPE),
- errmsg("column \"%s\" of foreign table \"%s\" uses an OGR array, currently unsupported", colname, tblname)
+ errmsg("column \"%s\" of foreign table \"%s\" uses an OGR OFTWideString, deprecated", colname, tblname)
));
- break;
- }
+ return;
}
+
ereport(ERROR, (
errcode(ERRCODE_FDW_INVALID_DATA_TYPE),
errmsg("column \"%s\" of foreign table \"%s\" converts OGR \"%s\" to \"%s\"",
@@ -1269,6 +1234,7 @@ ogrCanConvertToPg(OGRFieldType ogr_type, Oid pg_type, const char* colname, const
));
}
+
#ifdef OGR_FDW_HEXWKB
static char* hexchr = "0123456789ABCDEF";
@@ -1432,17 +1398,27 @@ ogrReadColumnData(OgrFdwState* state)
continue;
}
+ /* Check for array type */
+ col.pgelmtype = get_element_type(col.pgtype);
+ if (col.pgelmtype)
+ {
+ /* Extra type info needed to form the array */
+ col.pgisarray = true;
+ }
+ else
+ col.pgelmtype = col.pgtype;
+
/* Find the appropriate conversion functions */
- getTypeInputInfo(col.pgtype, &col.pginputfunc, &col.pginputioparam);
- getTypeBinaryInputInfo(col.pgtype, &col.pgrecvfunc, &col.pgrecvioparam);
- getTypeOutputInfo(col.pgtype, &col.pgoutputfunc, &col.pgoutputvarlena);
- getTypeBinaryOutputInfo(col.pgtype, &col.pgsendfunc, &col.pgsendvarlena);
+ getTypeInputInfo(col.pgelmtype, &col.pginputfunc, &col.pginputioparam);
+ getTypeBinaryInputInfo(col.pgelmtype, &col.pgrecvfunc, &col.pgrecvioparam);
+ getTypeOutputInfo(col.pgelmtype, &col.pgoutputfunc, &col.pgoutputvarlena);
+ getTypeBinaryOutputInfo(col.pgelmtype, &col.pgsendfunc, &col.pgsendvarlena);
/* Get the PgSQL column name */
#if PG_VERSION_NUM >= 110000
- col.pgname = get_attname(rel->rd_id, att_tuple->attnum, false);
+ col.pgname = pstrdup(get_attname(rel->rd_id, att_tuple->attnum, false));
#else
- col.pgname = get_attname(rel->rd_id, att_tuple->attnum);
+ col.pgname = pstrdup(get_attname(rel->rd_id, att_tuple->attnum));
#endif
/* Handle FID first */
@@ -1503,7 +1479,7 @@ ogrReadColumnData(OgrFdwState* state)
OGRFieldType fldtype = OGR_Fld_GetType(fld);
/* Error if types mismatched when column names match */
- ogrCanConvertToPg(fldtype, col.pgtype, col.pgname, tblname);
+ ogrCheckConvertToPg(fldtype, col.pgtype, col.pgname, tblname);
col.ogrvariant = OGR_FIELD;
col.ogrfldnum = found_entry->fldnum;
@@ -1683,18 +1659,38 @@ ogrBeginForeignScan(ForeignScanState* node, int eflags)
* each column in the foreign table.
*/
static Datum
-pgDatumFromCString(const char* cstr, Oid pgtype, int pgtypmod, Oid pginputfunc)
+pgDatumFromCString(const char* cstr, const OgrFdwColumn *col, int char_encoding, bool *is_null)
{
- Datum value;
- Datum cdata = CStringGetDatum(cstr);
+ size_t cstr_len = cstr ? strlen(cstr) : 0;
+ Datum value = (Datum) 0;
+ char *cstr_decoded;
+
+ /* Zero length implies NULL for all non-strings */
+ if (cstr_len == 0 && (col->ogrfldtype != OFTString && col->ogrfldtype != OFTStringList))
+ {
+ *is_null = true;
+ return value;
+ }
+
+ cstr_decoded = char_encoding ?
+ pg_any_to_server(cstr, cstr_len, char_encoding) :
+ pstrdup(cstr);
+
+ value = OidFunctionCall3(col->pginputfunc,
+ CStringGetDatum(cstr_decoded),
+ ObjectIdGetDatum(InvalidOid),
+ Int32GetDatum(col->pgtypmod));
- value = OidFunctionCall3(pginputfunc, cdata,
- ObjectIdGetDatum(InvalidOid),
- Int32GetDatum(pgtypmod));
+ /* Free cstr_decoded if it is a copy */
+ if (cstr != cstr_decoded)
+ pfree(cstr_decoded);
+ is_null = false;
return value;
}
+
+
static inline void
ogrNullSlot(Datum* values, bool* nulls, int i)
{
@@ -1726,6 +1722,9 @@ ogrFeatureToSlot(const OGRFeatureH feat, TupleTableSlot* slot, const OgrFdwExecS
TupleDesc tupdesc = slot->tts_tupleDescriptor;
int have_typmod_funcs = (execstate->setsridfunc && execstate->typmodsridfunc);
+#define CSTR_SZ 256
+ char cstr[CSTR_SZ];
+
/* Check our assumption that slot and setup data match */
if (tbl->ncols != tupdesc->natts)
{
@@ -1739,8 +1738,6 @@ ogrFeatureToSlot(const OGRFeatureH feat, TupleTableSlot* slot, const OgrFdwExecS
OgrFdwColumn col = tbl->cols[i];
const char* pgname = col.pgname;
Oid pgtype = col.pgtype;
- int pgtypmod = col.pgtypmod;
- Oid pginputfunc = col.pginputfunc;
int ogrfldnum = col.ogrfldnum;
OGRFieldType ogrfldtype = col.ogrfldtype;
OgrColumnVariant ogrvariant = col.ogrvariant;
@@ -1757,6 +1754,7 @@ ogrFeatureToSlot(const OGRFeatureH feat, TupleTableSlot* slot, const OgrFdwExecS
if (ogrvariant == OGR_FID)
{
GIntBig fid = OGR_F_GetFID(feat);
+ bool is_null = false;
if (fid == OGRNullFID)
{
@@ -1767,8 +1765,8 @@ ogrFeatureToSlot(const OGRFeatureH feat, TupleTableSlot* slot, const OgrFdwExecS
char fidstr[256];
snprintf(fidstr, 256, OGR_FDW_FRMT_INT64, OGR_FDW_CAST_INT64(fid));
- nulls[i] = false;
- values[i] = pgDatumFromCString(fidstr, pgtype, pgtypmod, pginputfunc);
+ values[i] = pgDatumFromCString(fidstr, &col, execstate->ogr.char_encoding, &is_null);
+ nulls[i] = is_null;
}
}
else if (ogrvariant == OGR_GEOMETRY)
@@ -1890,118 +1888,175 @@ ogrFeatureToSlot(const OGRFeatureH feat, TupleTableSlot* slot, const OgrFdwExecS
#endif
/* Ensure that the OGR data type fits the destination Pg column */
- ogrCanConvertToPg(ogrfldtype, pgtype, pgname, tbl->tblname);
+ ogrCheckConvertToPg(ogrfldtype, pgtype, pgname, tbl->tblname);
/* Only convert non-null fields */
- if (field_not_null)
+ if (!field_not_null)
{
- switch (ogrfldtype)
- {
- case OFTBinary:
- {
- /*
- * Convert binary fields to bytea directly
- */
- int bufsize;
- GByte* buf = OGR_F_GetFieldAsBinary(feat, ogrfldnum, &bufsize);
- int varsize = bufsize + VARHDRSZ;
- bytea* varlena = palloc(varsize);
- memcpy(VARDATA(varlena), buf, bufsize);
- SET_VARSIZE(varlena, varsize);
- nulls[i] = false;
- values[i] = PointerGetDatum(varlena);
- break;
- }
- case OFTInteger:
- case OFTReal:
- case OFTString:
+ ogrNullSlot(values, nulls, i);
+ continue;
+ }
+
+ switch (ogrfldtype)
+ {
+ case OFTBinary:
+ {
+ /*
+ * Convert binary fields to bytea directly
+ */
+ int bufsize;
+ GByte* buf = OGR_F_GetFieldAsBinary(feat, ogrfldnum, &bufsize);
+ int varsize = bufsize + VARHDRSZ;
+ bytea* varlena = palloc(varsize);
+ memcpy(VARDATA(varlena), buf, bufsize);
+ SET_VARSIZE(varlena, varsize);
+ nulls[i] = false;
+ values[i] = PointerGetDatum(varlena);
+ break;
+ }
+ case OFTInteger:
+ case OFTReal:
+ case OFTString:
#if GDAL_VERSION_MAJOR >= 2
- case OFTInteger64:
+ case OFTInteger64:
#endif
+ {
+ /*
+ * Convert numbers and strings via a string representation.
+ * Handling numbers directly would be faster, but require a lot of extra code.
+ * For now, we go via text.
+ */
+ const char* cstr_in = OGR_F_GetFieldAsString(feat, ogrfldnum);
+ bool is_null = false;
+ values[i] = pgDatumFromCString(cstr_in, &col, execstate->ogr.char_encoding, &is_null);
+ nulls[i] = is_null;
+ break;
+ }
+ case OFTDate:
+ case OFTTime:
+ case OFTDateTime:
+ {
+ /*
+ * OGR date/times have a weird access method, so we use that to pull
+ * out the raw data and turn it into a string for PgSQL's (very
+ * sophisticated) date/time parsing routines to handle.
+ */
+ int year, month, day, hour, minute, second, tz;
+ bool is_null = false;
+ OGR_F_GetFieldAsDateTime(feat, ogrfldnum,
+ &year, &month, &day,
+ &hour, &minute, &second, &tz);
+
+ if (ogrfldtype == OFTDate)
{
- /*
- * Convert numbers and strings via a string representation.
- * Handling numbers directly would be faster, but require a lot of extra code.
- * For now, we go via text.
- */
- const char* cstr_in = OGR_F_GetFieldAsString(feat, ogrfldnum);
- size_t cstr_len = cstr_in ? strlen(cstr_in) : 0;
- if (cstr_in && (cstr_len > 0 || ogrfldtype == OFTString))
- {
- char* cstr_decoded;
- if (execstate->ogr.char_encoding)
- {
- cstr_decoded = pg_any_to_server(cstr_in, cstr_len, execstate->ogr.char_encoding);
- }
- else
- {
- cstr_decoded = pstrdup(cstr_in);
- }
- nulls[i] = false;
- values[i] = pgDatumFromCString(cstr_decoded, pgtype, pgtypmod, pginputfunc);
- /* Free cstr_decoded if it is a copy */
- if (cstr_in != cstr_decoded)
- pfree(cstr_decoded);
- }
- else
- {
- ogrNullSlot(values, nulls, i);
- }
- break;
+ snprintf(cstr, CSTR_SZ, "%d-%02d-%02d", year, month, day);
}
- case OFTDate:
- case OFTTime:
- case OFTDateTime:
+ else if (ogrfldtype == OFTTime)
{
- /*
- * OGR date/times have a weird access method, so we use that to pull
- * out the raw data and turn it into a string for PgSQL's (very
- * sophisticated) date/time parsing routines to handle.
- */
- int year, month, day, hour, minute, second, tz;
- char cstr[256];
-
- OGR_F_GetFieldAsDateTime(feat, ogrfldnum,
- &year, &month, &day,
- &hour, &minute, &second, &tz);
-
- if (ogrfldtype == OFTDate)
- {
- snprintf(cstr, 256, "%d-%02d-%02d", year, month, day);
- }
- else if (ogrfldtype == OFTTime)
- {
- snprintf(cstr, 256, "%02d:%02d:%02d", hour, minute, second);
- }
- else
- {
- snprintf(cstr, 256, "%d-%02d-%02d %02d:%02d:%02d", year, month, day, hour, minute, second);
- }
- nulls[i] = false;
- values[i] = pgDatumFromCString(cstr, pgtype, pgtypmod, pginputfunc);
- break;
+ snprintf(cstr, CSTR_SZ, "%02d:%02d:%02d", hour, minute, second);
+ }
+ else
+ {
+#if (GDAL_VERSION_NUM >= GDAL_COMPUTE_VERSION(3,7,0))
+ const char* tsstr = OGR_F_GetFieldAsISO8601DateTime(feat, ogrfldnum, NULL);
+ strncpy(cstr, tsstr, CSTR_SZ);
+#else
+ snprintf(cstr, CSTR_SZ, "%d-%02d-%02d %02d:%02d:%02d", year, month, day, hour, minute, second);
+#endif
+ }
+ values[i] = pgDatumFromCString(cstr, &col, PG_SQL_ASCII, &is_null);
+ nulls[i] = is_null;
+ break;
+ }
+
+#if GDAL_VERSION_MAJOR >= 2
+ case OFTInteger64List:
+ {
+ int ilist_size;
+ const int64 *ilist = (int64*)OGR_F_GetFieldAsInteger64List(feat, ogrfldnum, &ilist_size);
+ ArrayBuildState *abs = initArrayResult(col.pgelmtype, CurrentMemoryContext, false);
+ for (uint32 i = 0; i < ilist_size; i++)
+ {
+ bool is_null = false;
+ snprintf(cstr, CSTR_SZ, "%ld", ilist[i]);
+ abs = accumArrayResult(abs,
+ pgDatumFromCString(cstr, &col, execstate->ogr.char_encoding, &is_null),
+ is_null,
+ col.pgelmtype,
+ CurrentMemoryContext);
}
- case OFTIntegerList:
- case OFTRealList:
- case OFTStringList:
+ values[i] = makeArrayResult(abs, CurrentMemoryContext);
+ nulls[i] = false;
+ break;
+ }
+#endif
+
+ case OFTIntegerList:
+ {
+ int ilist_size;
+ const int *ilist = OGR_F_GetFieldAsIntegerList(feat, ogrfldnum, &ilist_size);
+ ArrayBuildState *abs = initArrayResult(col.pgelmtype, CurrentMemoryContext, false);
+ for (uint32 i = 0; i < ilist_size; i++)
{
- /* TODO, map these OGR array types into PgSQL arrays (fun!) */
- elog(ERROR, "unsupported OGR array type \"%s\"", OGR_GetFieldTypeName(ogrfldtype));
- break;
+ bool is_null = false;
+ snprintf(cstr, CSTR_SZ, "%d", ilist[i]);
+ abs = accumArrayResult(abs,
+ pgDatumFromCString(cstr, &col, execstate->ogr.char_encoding, &is_null),
+ is_null,
+ col.pgelmtype,
+ CurrentMemoryContext);
}
- default:
+ values[i] = makeArrayResult(abs, CurrentMemoryContext);
+ nulls[i] = false;
+ break;
+ }
+
+ case OFTRealList:
+ {
+ int rlist_size;
+ const double *rlist = OGR_F_GetFieldAsDoubleList(feat, ogrfldnum, &rlist_size);
+ ArrayBuildState *abs = initArrayResult(col.pgelmtype, CurrentMemoryContext, false);
+ for (uint32 i = 0; i < rlist_size; i++)
{
- elog(ERROR, "unsupported OGR type \"%s\"", OGR_GetFieldTypeName(ogrfldtype));
- break;
+ bool is_null = false;
+ snprintf(cstr, CSTR_SZ, "%g", rlist[i]);
+ abs = accumArrayResult(abs,
+ pgDatumFromCString(cstr, &col, execstate->ogr.char_encoding, &is_null),
+ is_null,
+ col.pgelmtype,
+ CurrentMemoryContext);
}
+ values[i] = makeArrayResult(abs, CurrentMemoryContext);
+ nulls[i] = false;
+ break;
+ }
+ case OFTStringList:
+ {
+ ArrayBuildState *abs = initArrayResult(col.pgelmtype, CurrentMemoryContext, false);
+ char **cstrs = OGR_F_GetFieldAsStringList(feat, ogrfldnum);
+ while (*cstrs)
+ {
+ bool is_null = false;
+ abs = accumArrayResult(abs,
+ pgDatumFromCString(*cstrs, &col, execstate->ogr.char_encoding, &is_null),
+ is_null,
+ col.pgelmtype,
+ CurrentMemoryContext);
+ cstrs++;
}
+ values[i] = makeArrayResult(abs, CurrentMemoryContext);
+ nulls[i] = false;
+ break;
}
- else
+ default:
{
- ogrNullSlot(values, nulls, i);
+ elog(ERROR, "unsupported OGR type \"%s\"", OGR_GetFieldTypeName(ogrfldtype));
+ break;
}
+
+ } /* !switch */
}
/* Fill in unmatched columns with NULL */
else if (ogrvariant == OGR_UNMATCHED)
@@ -2182,7 +2237,7 @@ ogrSlotToFeature(const TupleTableSlot* slot, OGRFeatureH feat, const OgrFdwTable
else if (ogrvariant == OGR_FIELD)
{
/* Ensure that the OGR data type fits the destination Pg column */
- pgCanConvertToOgr(pgtype, ogrfldtype, pgname, tbl->tblname);
+ pgCheckConvertToOgr(pgtype, ogrfldtype, pgname, tbl->tblname);
/* Skip NULL case */
if (nulls[i])
@@ -2325,6 +2380,70 @@ ogrSlotToFeature(const TupleTableSlot* slot, OGRFeatureH feat, const OgrFdwTable
break;
}
+ case BOOLARRAYOID:
+ case INT2ARRAYOID:
+ case INT4ARRAYOID:
+ {
+ ArrayType *arr = DatumGetArrayTypeP(values[i]);
+ size_t sz = ArrayGetNItems(ARR_NDIM(arr), ARR_DIMS(arr));
+ Datum d;
+ bool isnull;
+ int *ints = palloc(sizeof(int) * sz);
+ int num_ints = 0;
+ ArrayIterator it = array_create_iterator(arr, 0, NULL);
+ while (array_iterate(it, &d, &isnull))
+ {
+ if (isnull) continue;
+ ints[num_ints++] = DatumGetInt32(d);
+ }
+ OGR_F_SetFieldIntegerList(feat, ogrfldnum, num_ints, ints);
+ pfree(ints);
+ break;
+ }
+
+ case CHARARRAYOID:
+ case NAMEARRAYOID:
+ case TEXTARRAYOID:
+ case VARCHARARRAYOID:
+ {
+ ArrayType *arr = DatumGetArrayTypeP(values[i]);
+ Datum d;
+ bool isnull;
+ char** papszList = NULL;
+ ArrayIterator it = array_create_iterator(arr, 0, NULL);
+ while (array_iterate(it, &d, &isnull))
+ {
+ char *cstr;
+ if (isnull) continue;
+ cstr = text_to_cstring(DatumGetTextP(d));
+ papszList = CSLAddString(papszList, cstr);
+ pfree(cstr);
+ }
+ OGR_F_SetFieldStringList(feat, ogrfldnum, papszList);
+ CSLDestroy(papszList);
+ break;
+ }
+
+ case FLOAT4ARRAYOID:
+ case FLOAT8ARRAYOID:
+ {
+ ArrayType *arr = DatumGetArrayTypeP(values[i]);
+ size_t sz = ArrayGetNItems(ARR_NDIM(arr), ARR_DIMS(arr));
+ Datum d;
+ bool isnull;
+ double *floats = palloc(sizeof(double) * sz);
+ int num_floats = 0;
+ ArrayIterator it = array_create_iterator(arr, 0, NULL);
+ while (array_iterate(it, &d, &isnull))
+ {
+ if (isnull) continue;
+ floats[num_floats++] = DatumGetFloat8(d);
+ }
+ OGR_F_SetFieldDoubleList(feat, ogrfldnum, num_floats, floats);
+ pfree(floats);
+ break;
+ }
+
/* TODO: array types for string, integer, float */
default:
{
=====================================
ogr_fdw.h
=====================================
@@ -98,22 +98,25 @@ typedef enum {
typedef struct OgrFdwColumn
{
/* PgSQL metadata */
- int pgattnum; /* PostgreSQL attribute number */
- int pgattisdropped; /* PostgreSQL attribute dropped? */
- char* pgname; /* PostgreSQL column name */
- Oid pgtype; /* PostgreSQL data type */
- int pgtypmod; /* PostgreSQL type modifier */
-
- /* For reading */
- Oid pginputfunc; /* PostgreSQL function to convert cstring to type */
+ int pgattnum; /* PgSQL attribute number */
+ int pgattisdropped; /* PgSQL attribute dropped? */
+ char* pgname; /* PgSQL column name */
+ Oid pgtype; /* PgQL data type */
+ int pgtypmod; /* PgSQL type modifier */
+
+ bool pgisarray;
+ Oid pgelmtype; /* If column is array then this is nonzero */
+
+ /* For reading. If array, for array element type. */
+ Oid pginputfunc; /* PgSQL convert cstring to type */
Oid pginputioparam;
- Oid pgrecvfunc; /* PostgreSQL function to convert binary to type */
+ Oid pgrecvfunc; /* PgSQL convert binary to type */
Oid pgrecvioparam;
- /* For writing */
- Oid pgoutputfunc; /* PostgreSQL function to convert type to cstring */
+ /* For writing. If array, for array element type. */
+ Oid pgoutputfunc; /* PgSQL convert type to cstring */
bool pgoutputvarlena;
- Oid pgsendfunc; /* PostgreSQL function to convert type to binary */
+ Oid pgsendfunc; /* PgSQL convert type to binary */
bool pgsendvarlena;
/* OGR metadata */
=====================================
ogr_fdw_common.c
=====================================
@@ -141,6 +141,9 @@ ogrTypeToPgType(OGRFieldDefnH ogr_fld, char *pgtype, size_t width)
case OFTInteger64:
snprintf(pgtype, width, "bigint");
break;
+ case OFTInteger64List:
+ snprintf(pgtype, width, "bigint[]");
+ break;
#endif
default:
CPLError(CE_Failure, CPLE_AssertionFailed,
=====================================
output/import.source
=====================================
@@ -84,13 +84,21 @@ ORDER BY c.ordinal_position;
person_s_value | character varying | {"column_name=person's value"}
(4 rows)
-SELECT * FROM imp3.no_geom_apost;
- fid | name | age | person_s_value
------+---------+-----+----------------
- 1 | Peter | 34 | 10.2
- 2 | John | 77 | 3.4
- 3 | Paul | 45 | 19.2
- 4 | Matthew | 35 | 18.2
-(4 rows)
+SELECT name,age FROM imp3.no_geom_apost WHERE name = 'Paul';
+ name | age
+------+-----
+ Paul | 45
+(1 row)
+
+SELECT name,age FROM imp3.no_geom_apost WHERE name IS NULL;
+ name | age
+------+-----
+(0 rows)
+
+SELECT name,age FROM imp3.no_geom_apost WHERE name = '';
+ name | age
+------+-----
+ | 44
+(1 row)
------------------------------------------------
=====================================
output/pgsql.source
=====================================
@@ -16,12 +16,10 @@ CREATE TABLE bytea_local (
);
----------------------------------------------------------------------
-- Populate local table
-INSERT INTO bytea_local (name, geom, age, size, value, num, dt, tm, dttm, varch, yn)
- VALUES ('Jim', '14232'::bytea, 23, 1, 4.3, 5.5, '2010-10-10'::date, '13:23:21'::time, '2010-10-10 13:23:21'::timestamp, 'this', 'y' );
-INSERT INTO bytea_local (name, geom, age, size, value, num, dt, tm, dttm, varch, yn)
- VALUES ('Marvin', '55555'::bytea, 34, 2, 5.4, 10.13, '2011-11-11'::date, '15:21:45'::time, '2011-11-11 15:21:45'::timestamp, 'that', 'n' );
-INSERT INTO bytea_local (name, geom, age, size, value, num, dt, tm, dttm, varch, yn)
- VALUES (NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
+INSERT INTO bytea_local (name, geom, age, size, value, num, dt, tm, dttm, varch, yn) VALUES
+ ('Jim', '14232'::bytea, 23, 1, 4.3, 5.5, '2010-10-10'::date, '13:23:21'::time, '2010-10-10 13:23:21'::timestamp, 'this', 'y' ),
+ ('Marvin', '55555'::bytea, 34, 2, 5.4, 10.13, '2011-11-11'::date, '15:21:45'::time, '2011-11-11 15:21:45'::timestamp, 'that', 'n' ),
+ (NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
----------------------------------------------------------------------
-- Create remote table
CREATE SERVER pgserver
@@ -191,4 +189,49 @@ SELECT a.fid, a.name, b.name
3 | |
(3 rows)
-
+----------------------------------------------------------------------
+-- Populate local array table
+SET client_min_messages = NOTICE;
+CREATE TABLE array_local (
+ fid integer primary key,
+ geom bytea,
+ txt text[],
+ int int2[],
+ flt float4[],
+ b boolean[]
+);
+INSERT INTO array_local (fid,txt, int, flt, b) VALUES
+ (1, ARRAY['Jim'], ARRAY[1,2,3], ARRAY[3.4,5.6,7.8], ARRAY[true,false]),
+ (2, ARRAY['Jim',NULL,'Joe'], ARRAY[1,3,NULL,4], ARRAY[4.5,NULL,3.4], ARRAY[false,NULL]),
+ (3, NULL, NULL, NULL, NULL);
+----------------------------------------------------------------------
+-- Create remote array table
+CREATE FOREIGN TABLE array_fdw (
+ fid bigint,
+ geom bytea,
+ txt text[],
+ int int4[],
+ flt float8[],
+ b boolean[]
+) SERVER pgserver OPTIONS (layer 'array_local');
+SELECT fid, txt, int, flt, b FROM array_fdw;
+ fid | txt | int | flt | b
+-----+--------------+-----------+---------------+-------
+ 1 | {Jim} | {1,2,3} | {3.4,5.6,7.8} | {t,f}
+ 2 | {Jim,"",Joe} | {1,3,0,4} | {4.5,0,3.4} | {f,f}
+ 3 | | | |
+(3 rows)
+
+----------------------------------------------------------------------
+-- Update remote array table
+UPDATE array_fdw SET
+ txt = ARRAY['newJim', 'newJoe'],
+ int = ARRAY[-2, -1, 0, 1, 2],
+ flt = ARRAY[-0.1, 0.0, 0.1]
+WHERE fid = 3;
+SELECT txt, int, flt FROM array_fdw WHERE fid = 3;
+ txt | int | flt
+-----------------+---------------+--------------
+ {newJim,newJoe} | {-2,-1,0,1,2} | {-0.1,0,0.1}
+(1 row)
+
View it on GitLab: https://salsa.debian.org/debian-gis-team/pgsql-ogr-fdw/-/commit/669ced5ef2ea500ec535d768ba5c697ac41cad72
--
This project does not include diff previews in email notifications.
View it on GitLab: https://salsa.debian.org/debian-gis-team/pgsql-ogr-fdw/-/commit/669ced5ef2ea500ec535d768ba5c697ac41cad72
You're receiving this email because of your account on salsa.debian.org.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://alioth-lists.debian.net/pipermail/pkg-grass-devel/attachments/20240628/a69cdb4f/attachment-0001.htm>
More information about the Pkg-grass-devel
mailing list