[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