1 /*-------------------------------------------------------------------------
2  *
3  * ogr_fdw.c
4  *		  foreign-data wrapper for GIS data access.
5  *
6  * Copyright (c) 2014-2015, Paul Ramsey <pramsey@cleverelephant.ca>
7  *
8  *-------------------------------------------------------------------------
9  */
10 
11 /*
12  * System
13  */
14 #include <sys/stat.h>
15 #include <unistd.h>
16 
17 #include "postgres.h"
18 
19 /*
20  * Require PostgreSQL >= 9.3
21  */
22 #if PG_VERSION_NUM < 90300
23 #error "OGR FDW requires PostgreSQL version 9.3 or higher"
24 
25 #else
26 
27 /*
28  * Definition of stringToQualifiedNameList
29  */
30 #if PG_VERSION_NUM >= 100000
31 #include "utils/regproc.h"
32 #endif
33 
34 /*
35  * Local structures
36  */
37 #include "ogr_fdw.h"
38 
39 PG_MODULE_MAGIC;
40 
41 /*
42  * Describes the valid options for objects that use this wrapper.
43  */
44 struct OgrFdwOption
45 {
46 	const char* optname;
47 	Oid optcontext;     /* Oid of catalog in which option may appear */
48 	bool optrequired;   /* Flag mandatory options */
49 	bool optfound;      /* Flag whether options was specified by user */
50 };
51 
52 #define OPT_DRIVER "format"
53 #define OPT_SOURCE "datasource"
54 #define OPT_LAYER "layer"
55 #define OPT_COLUMN "column_name"
56 #define OPT_CONFIG_OPTIONS "config_options"
57 #define OPT_OPEN_OPTIONS "open_options"
58 #define OPT_UPDATEABLE "updateable"
59 #define OPT_CHAR_ENCODING "character_encoding"
60 
61 #define OGR_FDW_FRMT_INT64	 "%lld"
62 #define OGR_FDW_CAST_INT64(x)	 (long long)(x)
63 
64 /*
65  * Valid options for ogr_fdw.
66  * ForeignDataWrapperRelationId (no options)
67  * ForeignServerRelationId (CREATE SERVER options)
68  * UserMappingRelationId (CREATE USER MAPPING options)
69  * ForeignTableRelationId (CREATE FOREIGN TABLE options)
70  *
71  * {optname, optcontext, optrequired, optfound}
72  */
73 static struct OgrFdwOption valid_options[] =
74 {
75 
76 	/* OGR column mapping */
77 	{OPT_COLUMN, AttributeRelationId, false, false},
78 
79 	/* OGR datasource options */
80 	{OPT_SOURCE, ForeignServerRelationId, true, false},
81 	{OPT_DRIVER, ForeignServerRelationId, false, false},
82 	{OPT_UPDATEABLE, ForeignServerRelationId, false, false},
83 	{OPT_CONFIG_OPTIONS, ForeignServerRelationId, false, false},
84 	{OPT_CHAR_ENCODING, ForeignServerRelationId, false, false},
85 #if GDAL_VERSION_MAJOR >= 2
86 	{OPT_OPEN_OPTIONS, ForeignServerRelationId, false, false},
87 #endif
88 	/* OGR layer options */
89 	{OPT_LAYER, ForeignTableRelationId, true, false},
90 	{OPT_UPDATEABLE, ForeignTableRelationId, false, false},
91 
92 	/* EOList marker */
93 	{NULL, InvalidOid, false, false}
94 };
95 
96 /*
97  * SQL functions
98  */
99 extern Datum ogr_fdw_handler(PG_FUNCTION_ARGS);
100 extern Datum ogr_fdw_validator(PG_FUNCTION_ARGS);
101 
102 PG_FUNCTION_INFO_V1(ogr_fdw_handler);
103 PG_FUNCTION_INFO_V1(ogr_fdw_validator);
104 void _PG_init(void);
105 
106 /*
107  * FDW query callback routines
108  */
109 static void ogrGetForeignRelSize(PlannerInfo* root,
110                                  RelOptInfo* baserel,
111                                  Oid foreigntableid);
112 static void ogrGetForeignPaths(PlannerInfo* root,
113                                RelOptInfo* baserel,
114                                Oid foreigntableid);
115 static ForeignScan* ogrGetForeignPlan(PlannerInfo* root,
116                                       RelOptInfo* baserel,
117                                       Oid foreigntableid,
118                                       ForeignPath* best_path,
119                                       List* tlist,
120                                       List* scan_clauses
121 #if PG_VERSION_NUM >= 90500
122                                       , Plan* outer_plan
123 #endif
124                                      );
125 static void ogrBeginForeignScan(ForeignScanState* node, int eflags);
126 static TupleTableSlot* ogrIterateForeignScan(ForeignScanState* node);
127 static void ogrReScanForeignScan(ForeignScanState* node);
128 static void ogrEndForeignScan(ForeignScanState* node);
129 
130 /*
131  * FDW modify callback routines
132  */
133 static void ogrAddForeignUpdateTargets(Query* parsetree,
134                                        RangeTblEntry* target_rte,
135                                        Relation target_relation);
136 static void ogrBeginForeignModify(ModifyTableState* mtstate,
137                                   ResultRelInfo* rinfo,
138                                   List* fdw_private,
139                                   int subplan_index,
140                                   int eflags);
141 static TupleTableSlot* ogrExecForeignInsert(EState* estate,
142         ResultRelInfo* rinfo,
143         TupleTableSlot* slot,
144         TupleTableSlot* planSlot);
145 static TupleTableSlot* ogrExecForeignUpdate(EState* estate,
146         ResultRelInfo* rinfo,
147         TupleTableSlot* slot,
148         TupleTableSlot* planSlot);
149 static TupleTableSlot* ogrExecForeignDelete(EState* estate,
150         ResultRelInfo* rinfo,
151         TupleTableSlot* slot,
152         TupleTableSlot* planSlot);
153 static void ogrEndForeignModify(EState* estate,
154                                 ResultRelInfo* rinfo);
155 static int ogrIsForeignRelUpdatable(Relation rel);
156 
157 
158 #if PG_VERSION_NUM >= 90500
159 static List* ogrImportForeignSchema(ImportForeignSchemaStmt* stmt, Oid serverOid);
160 #endif
161 
162 /*
163  * Helper functions
164  */
165 static OgrConnection ogrGetConnectionFromTable(Oid foreigntableid, OgrUpdateable updateable);
166 static void ogr_fdw_exit(int code, Datum arg);
167 static void ogrReadColumnData(OgrFdwState* state);
168 
169 /* Global to hold GEOMETRYOID */
170 Oid GEOMETRYOID = InvalidOid;
171 
172 
173 #if GDAL_VERSION_NUM >= GDAL_COMPUTE_VERSION(2,1,0)
174 
175 const char* const gdalErrorTypes[] =
176 {
177 	"None",
178 	"AppDefined",
179 	"OutOfMemory",
180 	"FileIO",
181 	"OpenFailed",
182 	"IllegalArg",
183 	"NotSupported",
184 	"AssertionFailed",
185 	"NoWriteAccess",
186 	"UserInterrupt",
187 	"ObjectNull",
188 	"HttpResponse",
189 	"AWSBucketNotFound",
190 	"AWSObjectNotFound",
191 	"AWSAccessDenied",
192 	"AWSInvalidCredentials",
193 	"AWSSignatureDoesNotMatch"
194 };
195 
196 /* In theory this function should be declared "static void CPL_STDCALL" */
197 /* since this is the official signature of error handler callbacks. */
198 /* That would be needed if both GDAL and ogr_fdw were compiled with Visual */
199 /* Studio, but with non-Visual Studio compilers, the macro expands to empty, */
200 /* so if both GDAL and ogr_fdw are compiled with gcc things are fine. In case */
201 /* of mixes, crashes may occur but there is no clean fix... So let this as a note */
202 /* in case of future issue... */
203 static void
ogrErrorHandler(CPLErr eErrClass,int err_no,const char * msg)204 ogrErrorHandler(CPLErr eErrClass, int err_no, const char* msg)
205 {
206 	const char* gdalErrType = "unknown type";
207 	if (err_no >= 0 && err_no <
208 	        (int)sizeof(gdalErrorTypes) / sizeof(gdalErrorTypes[0]))
209 	{
210 		gdalErrType = gdalErrorTypes[err_no];
211 	}
212 	switch (eErrClass)
213 	{
214 	case CE_None:
215 		elog(NOTICE, "GDAL %s [%d] %s", gdalErrType, err_no, msg);
216 		break;
217 	case CE_Debug:
218 		elog(DEBUG2, "GDAL %s [%d] %s", gdalErrType, err_no, msg);
219 		break;
220 	case CE_Warning:
221 		elog(WARNING, "GDAL %s [%d] %s", gdalErrType, err_no, msg);
222 		break;
223 	case CE_Failure:
224 	case CE_Fatal:
225 	default:
226 		elog(ERROR, "GDAL %s [%d] %s", gdalErrType, err_no, msg);
227 		break;
228 	}
229 	return;
230 }
231 
232 #endif /* GDAL 2.1.0+ */
233 
234 void
_PG_init(void)235 _PG_init(void)
236 {
237 	on_proc_exit(&ogr_fdw_exit, PointerGetDatum(NULL));
238 
239 #if GDAL_VERSION_NUM >= GDAL_COMPUTE_VERSION(2,1,0)
240 	/* Hook up the GDAL error handlers to PgSQL elog() */
241 	CPLSetErrorHandler(ogrErrorHandler);
242 	CPLSetCurrentErrorHandlerCatchDebug(true);
243 #endif
244 }
245 
246 /*
247  * ogr_fdw_exit: Exit callback function.
248  */
249 static void
ogr_fdw_exit(int code,Datum arg)250 ogr_fdw_exit(int code, Datum arg)
251 {
252 	OGRCleanupAll();
253 }
254 
255 /*
256  * Function to get the geometry OID if required
257  */
258 Oid
ogrGetGeometryOid(void)259 ogrGetGeometryOid(void)
260 {
261 	if (GEOMETRYOID == InvalidOid)
262 	{
263 		Oid typoid = TypenameGetTypid("geometry");
264 		if (OidIsValid(typoid) && get_typisdefined(typoid))
265 		{
266 			GEOMETRYOID = typoid;
267 		}
268 		else
269 		{
270 			GEOMETRYOID = BYTEAOID;
271 		}
272 	}
273 
274 	return GEOMETRYOID;
275 }
276 
277 /*
278  * Foreign-data wrapper handler function: return a struct with pointers
279  * to my callback routines.
280  */
281 Datum
ogr_fdw_handler(PG_FUNCTION_ARGS)282 ogr_fdw_handler(PG_FUNCTION_ARGS)
283 {
284 	FdwRoutine* fdwroutine = makeNode(FdwRoutine);
285 
286 	/* Read support */
287 	fdwroutine->GetForeignRelSize = ogrGetForeignRelSize;
288 	fdwroutine->GetForeignPaths = ogrGetForeignPaths;
289 	fdwroutine->GetForeignPlan = ogrGetForeignPlan;
290 	fdwroutine->BeginForeignScan = ogrBeginForeignScan;
291 	fdwroutine->IterateForeignScan = ogrIterateForeignScan;
292 	fdwroutine->ReScanForeignScan = ogrReScanForeignScan;
293 	fdwroutine->EndForeignScan = ogrEndForeignScan;
294 
295 	/* Write support */
296 	fdwroutine->AddForeignUpdateTargets = ogrAddForeignUpdateTargets;
297 	fdwroutine->BeginForeignModify = ogrBeginForeignModify;
298 	fdwroutine->ExecForeignInsert = ogrExecForeignInsert;
299 	fdwroutine->ExecForeignUpdate = ogrExecForeignUpdate;
300 	fdwroutine->ExecForeignDelete = ogrExecForeignDelete;
301 	fdwroutine->EndForeignModify = ogrEndForeignModify;
302 	fdwroutine->IsForeignRelUpdatable = ogrIsForeignRelUpdatable;
303 
304 #if PG_VERSION_NUM >= 90500
305 	/*  Support functions for IMPORT FOREIGN SCHEMA */
306 	fdwroutine->ImportForeignSchema = ogrImportForeignSchema;
307 #endif
308 
309 	PG_RETURN_POINTER(fdwroutine);
310 }
311 
312 /*
313 * When attempting a soft open (allowing for failure and retry),
314 * we might need to call the opening
315 * routines twice, so all the opening machinery is placed here
316 * for convenient re-calling.
317 */
318 static OGRErr
ogrGetDataSourceAttempt(OgrConnection * ogr,bool bUpdateable,char ** open_option_list)319 ogrGetDataSourceAttempt(OgrConnection* ogr, bool bUpdateable, char** open_option_list)
320 {
321 	GDALDriverH ogr_dr = NULL;
322 #if GDAL_VERSION_MAJOR >= 2
323 	unsigned int open_flags = GDAL_OF_VECTOR;
324 
325 	if (bUpdateable)
326 	{
327 		open_flags |= GDAL_OF_UPDATE;
328 	}
329 	else
330 	{
331 		open_flags |= GDAL_OF_READONLY;
332 	}
333 #endif
334 
335 	if (ogr->dr_str)
336 	{
337 		ogr_dr = GDALGetDriverByName(ogr->dr_str);
338 		if (!ogr_dr)
339 		{
340 			ereport(ERROR,
341 			        (errcode(ERRCODE_FDW_UNABLE_TO_ESTABLISH_CONNECTION),
342 			         errmsg("unable to find format \"%s\"", ogr->dr_str),
343 			         errhint("See the formats list at http://www.gdal.org/ogr_formats.html")));
344 		}
345 #if GDAL_VERSION_MAJOR < 2
346 		ogr->ds = OGR_Dr_Open(ogr_dr, ogr->ds_str, bUpdateable);
347 #else
348 		{
349 			char** driver_list = CSLAddString(NULL, ogr->dr_str);
350 			ogr->ds = GDALOpenEx(ogr->ds_str,                  /* file/data source */
351 			                     open_flags,                            /* open flags */
352 			                     (const char* const*)driver_list,       /* driver */
353 			                     (const char* const*)open_option_list,  /* open options */
354 			                     NULL);                                 /* sibling files */
355 			CSLDestroy(driver_list);
356 		}
357 #endif
358 	}
359 	/* No driver, try a blind open... */
360 	else
361 	{
362 #if GDAL_VERSION_MAJOR < 2
363 		ogr->ds = OGROpen(ogr->ds_str, bUpdateable, &ogr_dr);
364 #else
365 		ogr->ds = GDALOpenEx(ogr->ds_str,
366 		                     open_flags,
367 		                     NULL,
368 		                     (const char* const*)open_option_list,
369 		                     NULL);
370 #endif
371 	}
372 	return ogr->ds ? OGRERR_NONE : OGRERR_FAILURE;
373 }
374 
375 /*
376  * Given a connection string and (optional) driver string, try to connect
377  * with appropriate error handling and reporting. Used in query startup,
378  * and in FDW options validation.
379  */
380 static OGRErr
ogrGetDataSource(OgrConnection * ogr,OgrUpdateable updateable)381 ogrGetDataSource(OgrConnection* ogr, OgrUpdateable updateable)
382 {
383 	char** open_option_list = NULL;
384 	bool bUpdateable = (updateable == OGR_UPDATEABLE_TRUE || updateable == OGR_UPDATEABLE_TRY);
385 	OGRErr err;
386 
387 	/* Set the GDAL config options into the environment */
388 	if (ogr->config_options)
389 	{
390 		char** option_iter;
391 		char** option_list = CSLTokenizeString(ogr->config_options);
392 
393 		for (option_iter = option_list; option_iter && *option_iter; option_iter++)
394 		{
395 			char* key;
396 			const char* value;
397 			value = CPLParseNameValue(*option_iter, &key);
398 			if (!(key && value))
399 			{
400 				elog(ERROR, "bad config option string '%s'", ogr->config_options);
401 			}
402 
403 			elog(DEBUG1, "GDAL config option '%s' set to '%s'", key, value);
404 			CPLSetConfigOption(key, value);
405 			CPLFree(key);
406 		}
407 		CSLDestroy(option_list);
408 	}
409 
410 	/* Parse the GDAL layer open options */
411 	if (ogr->open_options)
412 	{
413 		open_option_list = CSLTokenizeString(ogr->open_options);
414 	}
415 
416 	/* Cannot search for drivers if they aren't registered, */
417 	/* but don't do registration if we already have drivers loaded */
418 	if (GDALGetDriverCount() <= 0)
419 	{
420 		GDALAllRegister();
421 	}
422 
423 	/* First attempt at connection */
424 	err = ogrGetDataSourceAttempt(ogr, bUpdateable, open_option_list);
425 
426 	/* Failed on soft updateable attempt, try and fall back to readonly */
427 	if ((!ogr->ds) && updateable == OGR_UPDATEABLE_TRY)
428 	{
429 		err = ogrGetDataSourceAttempt(ogr, false, open_option_list);
430 		/* Succeeded with readonly connection */
431 		if (ogr->ds)
432 		{
433 			ogr->ds_updateable = ogr->lyr_updateable = OGR_UPDATEABLE_FALSE;
434 		}
435 	}
436 
437 	/* Open failed, provide error hint if OGR gives us one. */
438 	if (!ogr->ds)
439 	{
440 		const char* ogrerrmsg = CPLGetLastErrorMsg();
441 		if (ogrerrmsg && !streq(ogrerrmsg, ""))
442 		{
443 			ereport(ERROR,
444 			        (errcode(ERRCODE_FDW_UNABLE_TO_ESTABLISH_CONNECTION),
445 			         errmsg("unable to connect to data source \"%s\"", ogr->ds_str),
446 			         errhint("%s", ogrerrmsg)));
447 		}
448 		else
449 		{
450 			ereport(ERROR,
451 			        (errcode(ERRCODE_FDW_UNABLE_TO_ESTABLISH_CONNECTION),
452 			         errmsg("unable to connect to data source \"%s\"", ogr->ds_str)));
453 		}
454 	}
455 
456 	CSLDestroy(open_option_list);
457 
458 	return err;
459 }
460 
461 static bool
ogrCanReallyCountFast(const OgrConnection * con)462 ogrCanReallyCountFast(const OgrConnection* con)
463 {
464 	GDALDriverH dr = GDALGetDatasetDriver(con->ds);
465 	const char* dr_str = GDALGetDriverShortName(dr);
466 
467 	if (streq(dr_str, "ESRI Shapefile") ||
468 	    streq(dr_str, "FileGDB") ||
469 	    streq(dr_str, "OpenFileGDB"))
470 	{
471 		return true;
472 	}
473 	return false;
474 }
475 
476 static void
ogrEreportError(const char * errstr)477 ogrEreportError(const char* errstr)
478 {
479 	const char* ogrerrmsg = CPLGetLastErrorMsg();
480 	if (ogrerrmsg && !streq(ogrerrmsg, ""))
481 	{
482 		ereport(ERROR,
483 		        (errcode(ERRCODE_FDW_ERROR),
484 		         errmsg("%s", errstr),
485 		         errhint("%s", ogrerrmsg)));
486 	}
487 	else
488 	{
489 		ereport(ERROR,
490 		        (errcode(ERRCODE_FDW_ERROR),
491 		         errmsg("%s", errstr)));
492 	}
493 }
494 
495 /*
496  * Make sure the datasource is cleaned up when we're done
497  * with a connection.
498  */
499 static void
ogrFinishConnection(OgrConnection * ogr)500 ogrFinishConnection(OgrConnection* ogr)
501 {
502 	if (ogr->lyr && OGR_L_SyncToDisk(ogr->lyr) != OGRERR_NONE)
503 	{
504 		elog(NOTICE, "failed to flush writes to OGR data source");
505 	}
506 
507 	if (ogr->ds)
508 	{
509 		GDALClose(ogr->ds);
510 	}
511 
512 	ogr->ds = NULL;
513 }
514 
515 static OgrConnection
ogrGetConnectionFromServer(Oid foreignserverid,OgrUpdateable updateable)516 ogrGetConnectionFromServer(Oid foreignserverid, OgrUpdateable updateable)
517 {
518 	ForeignServer* server;
519 	OgrConnection ogr;
520 	ListCell* cell;
521 	OGRErr err;
522 
523 	/* Null all values */
524 	memset(&ogr, 0, sizeof(OgrConnection));
525 	ogr.ds_updateable = OGR_UPDATEABLE_UNSET;
526 	ogr.lyr_updateable = OGR_UPDATEABLE_UNSET;
527 
528 	server = GetForeignServer(foreignserverid);
529 
530 	foreach (cell, server->options)
531 	{
532 		DefElem* def = (DefElem*) lfirst(cell);
533 		if (streq(def->defname, OPT_SOURCE))
534 		{
535 			ogr.ds_str = defGetString(def);
536 		}
537 		if (streq(def->defname, OPT_DRIVER))
538 		{
539 			ogr.dr_str = defGetString(def);
540 		}
541 		if (streq(def->defname, OPT_CONFIG_OPTIONS))
542 		{
543 			ogr.config_options = defGetString(def);
544 		}
545 		if (streq(def->defname, OPT_OPEN_OPTIONS))
546 		{
547 			ogr.open_options = defGetString(def);
548 		}
549 		if (streq(def->defname, OPT_CHAR_ENCODING))
550 		{
551 			ogr.char_encoding = pg_char_to_encoding(defGetString(def));
552 		}
553 		if (streq(def->defname, OPT_UPDATEABLE))
554 		{
555 			if (defGetBoolean(def))
556 			{
557 				ogr.ds_updateable = OGR_UPDATEABLE_TRUE;
558 			}
559 			else
560 			{
561 				ogr.ds_updateable = OGR_UPDATEABLE_FALSE;
562 				/* Over-ride the open mode to favour user-defined mode */
563 				updateable = OGR_UPDATEABLE_FALSE;
564 			}
565 		}
566 	}
567 
568 	if (!ogr.ds_str)
569 	{
570 		elog(ERROR, "FDW table '%s' option is missing", OPT_SOURCE);
571 	}
572 
573 	/*
574 	 * TODO: Connections happen twice for each query, having a
575 	 * connection pool will certainly make things faster.
576 	 */
577 
578 	/*  Connect! */
579 	err = ogrGetDataSource(&ogr, updateable);
580 	if (err == OGRERR_FAILURE)
581 	{
582 		elog(ERROR, "ogrGetDataSource failed");
583 	}
584 	return ogr;
585 }
586 
587 
588 /*
589  * Read the options (data source connection from server and
590  * layer name from table) from a foreign table and use them
591  * to connect to an OGR layer. Return a connection object that
592  * has handles for both the datasource and layer.
593  */
594 static OgrConnection
ogrGetConnectionFromTable(Oid foreigntableid,OgrUpdateable updateable)595 ogrGetConnectionFromTable(Oid foreigntableid, OgrUpdateable updateable)
596 {
597 	ForeignTable* table;
598 	/* UserMapping *mapping; */
599 	/* ForeignDataWrapper *wrapper; */
600 	ListCell* cell;
601 	OgrConnection ogr;
602 
603 	/* Gather all data for the foreign table. */
604 	table = GetForeignTable(foreigntableid);
605 	/* mapping = GetUserMapping(GetUserId(), table->serverid); */
606 
607 	ogr = ogrGetConnectionFromServer(table->serverid, updateable);
608 
609 	foreach (cell, table->options)
610 	{
611 		DefElem* def = (DefElem*) lfirst(cell);
612 		if (streq(def->defname, OPT_LAYER))
613 		{
614 			ogr.lyr_str = defGetString(def);
615 		}
616 		if (streq(def->defname, OPT_UPDATEABLE))
617 		{
618 			if (defGetBoolean(def))
619 			{
620 				if (ogr.ds_updateable == OGR_UPDATEABLE_FALSE)
621 				{
622 					ereport(ERROR, (
623 					            errcode(ERRCODE_FDW_ERROR),
624 					            errmsg("data source \"%s\" is not updateable", ogr.ds_str),
625 					            errhint("cannot set table '%s' option to true", OPT_UPDATEABLE)
626 					        ));
627 				}
628 				ogr.lyr_updateable = OGR_UPDATEABLE_TRUE;
629 			}
630 			else
631 			{
632 				ogr.lyr_updateable = OGR_UPDATEABLE_FALSE;
633 			}
634 		}
635 	}
636 
637 	if (!ogr.lyr_str)
638 	{
639 		elog(ERROR, "FDW table '%s' option is missing", OPT_LAYER);
640 	}
641 
642 	/* Does the layer exist in the data source? */
643 	ogr.lyr = GDALDatasetGetLayerByName(ogr.ds, ogr.lyr_str);
644 	if (!ogr.lyr)
645 	{
646 		const char* ogrerr = CPLGetLastErrorMsg();
647 		ereport(ERROR, (
648 		    errcode(ERRCODE_FDW_TABLE_NOT_FOUND),
649 		    errmsg("unable to connect to %s to \"%s\"", OPT_LAYER, ogr.lyr_str),
650 		    (ogrerr && ! streq(ogrerr, ""))
651 		        ? errhint("%s", ogrerr)
652 		        : errhint("Does the layer exist?")
653 		    ));
654 	}
655 
656 	if (OGR_L_TestCapability(ogr.lyr, OLCStringsAsUTF8))
657 	{
658 		ogr.char_encoding = PG_UTF8;
659 	}
660 
661 	return ogr;
662 }
663 
664 
665 /*
666  * Validate the options given to a FOREIGN DATA WRAPPER, SERVER,
667  * USER MAPPING or FOREIGN TABLE that uses ogr_fdw.
668  *
669  * Raise an ERROR if the option or its value is considered invalid.
670  */
671 Datum
ogr_fdw_validator(PG_FUNCTION_ARGS)672 ogr_fdw_validator(PG_FUNCTION_ARGS)
673 {
674 	List* options_list = untransformRelOptions(PG_GETARG_DATUM(0));
675 	Oid catalog = PG_GETARG_OID(1);
676 	ListCell* cell;
677 	struct OgrFdwOption* opt;
678 	const char* source = NULL, *driver = NULL;
679 	const char* config_options = NULL, *open_options = NULL;
680 	OgrUpdateable updateable = OGR_UPDATEABLE_FALSE;
681 
682 	/* Initialize found state to not found */
683 	for (opt = valid_options; opt->optname; opt++)
684 	{
685 		opt->optfound = false;
686 	}
687 
688 	/*
689 	 * Check that only options supported by ogr_fdw, and allowed for the
690 	 * current object type, are given.
691 	 */
692 	foreach (cell, options_list)
693 	{
694 		DefElem* def = (DefElem*) lfirst(cell);
695 		bool optfound = false;
696 
697 		for (opt = valid_options; opt->optname; opt++)
698 		{
699 			if (catalog == opt->optcontext && streq(opt->optname, def->defname))
700 			{
701 				/* Mark that this user option was found */
702 				opt->optfound = optfound = true;
703 
704 				/* Store some options for testing later */
705 				if (streq(opt->optname, OPT_SOURCE))
706 				{
707 					source = defGetString(def);
708 				}
709 				if (streq(opt->optname, OPT_DRIVER))
710 				{
711 					driver = defGetString(def);
712 				}
713 				if (streq(opt->optname, OPT_CONFIG_OPTIONS))
714 				{
715 					config_options = defGetString(def);
716 				}
717 				if (streq(opt->optname, OPT_OPEN_OPTIONS))
718 				{
719 					open_options = defGetString(def);
720 				}
721 				if (streq(opt->optname, OPT_UPDATEABLE))
722 				{
723 					if (defGetBoolean(def))
724 					{
725 						updateable = OGR_UPDATEABLE_TRY;
726 					}
727 				}
728 
729 				break;
730 			}
731 		}
732 
733 		if (!optfound)
734 		{
735 			/*
736 			 * Unknown option specified, complain about it. Provide a hint
737 			 * with list of valid options for the object.
738 			 */
739 			const struct OgrFdwOption* opt;
740 			StringInfoData buf;
741 
742 			initStringInfo(&buf);
743 			for (opt = valid_options; opt->optname; opt++)
744 			{
745 				if (catalog == opt->optcontext)
746 					appendStringInfo(&buf, "%s%s", (buf.len > 0) ? ", " : "",
747 					                 opt->optname);
748 			}
749 
750 			ereport(ERROR, (
751 			    errcode(ERRCODE_FDW_INVALID_OPTION_NAME),
752 			    errmsg("invalid option \"%s\"", def->defname),
753 			    buf.len > 0
754 			        ? errhint("Valid options in this context are: %s", buf.data)
755 			        : errhint("There are no valid options in this context.")));
756 		}
757 	}
758 
759 	/* Check that all the mandatory options were found */
760 	for (opt = valid_options; opt->optname; opt++)
761 	{
762 		/* Required option for this catalog type is missing? */
763 		if (catalog == opt->optcontext && opt->optrequired && ! opt->optfound)
764 		{
765 			ereport(ERROR, (
766 			    errcode(ERRCODE_FDW_DYNAMIC_PARAMETER_VALUE_NEEDED),
767 			    errmsg("required option \"%s\" is missing", opt->optname)));
768 		}
769 	}
770 
771 	/* Make sure server connection can actually be established */
772 	if (catalog == ForeignServerRelationId && source)
773 	{
774 		OgrConnection ogr;
775 		OGRErr err;
776 
777 		ogr.ds_str = source;
778 		ogr.dr_str = driver;
779 		ogr.config_options = config_options;
780 		ogr.open_options = open_options;
781 
782 		err = ogrGetDataSource(&ogr, updateable);
783 		if (err == OGRERR_FAILURE)
784 		{
785 			elog(ERROR, "ogrGetDataSource failed");
786 		}
787 		if (ogr.ds)
788 		{
789 			GDALClose(ogr.ds);
790 		}
791 	}
792 
793 	PG_RETURN_VOID();
794 }
795 
796 /*
797  * Initialize an OgrFdwPlanState on the heap.
798  */
799 static OgrFdwState*
getOgrFdwState(Oid foreigntableid,OgrFdwStateType state_type)800 getOgrFdwState(Oid foreigntableid, OgrFdwStateType state_type)
801 {
802 	OgrFdwState* state;
803 	size_t size;
804 	OgrUpdateable updateable = OGR_UPDATEABLE_FALSE;
805 
806 	switch (state_type)
807 	{
808 	case OGR_PLAN_STATE:
809 		size = sizeof(OgrFdwPlanState);
810 		updateable = OGR_UPDATEABLE_FALSE;
811 		break;
812 	case OGR_EXEC_STATE:
813 		size = sizeof(OgrFdwExecState);
814 		updateable = OGR_UPDATEABLE_FALSE;
815 		break;
816 	case OGR_MODIFY_STATE:
817 		updateable = OGR_UPDATEABLE_TRUE;
818 		size = sizeof(OgrFdwModifyState);
819 		break;
820 	default:
821 		elog(ERROR, "invalid state type");
822 	}
823 
824 	state = palloc0(size);
825 	state->type = state_type;
826 
827 	/*  Connect! */
828 	state->ogr = ogrGetConnectionFromTable(foreigntableid, updateable);
829 	state->foreigntableid = foreigntableid;
830 
831 	return state;
832 }
833 
834 
835 
836 /*
837  * ogrGetForeignRelSize
838  *		Obtain relation size estimates for a foreign table
839  */
840 static void
ogrGetForeignRelSize(PlannerInfo * root,RelOptInfo * baserel,Oid foreigntableid)841 ogrGetForeignRelSize(PlannerInfo* root,
842                      RelOptInfo* baserel,
843                      Oid foreigntableid)
844 {
845 	/* Initialize the OGR connection */
846 	OgrFdwState* state = (OgrFdwState*)getOgrFdwState(foreigntableid, OGR_PLAN_STATE);
847 	OgrFdwPlanState* planstate = (OgrFdwPlanState*)state;
848 	List* scan_clauses = baserel->baserestrictinfo;
849 
850 	/* Set to NULL to clear the restriction clauses in OGR */
851 	OGR_L_SetIgnoredFields(planstate->ogr.lyr, NULL);
852 	OGR_L_SetSpatialFilter(planstate->ogr.lyr, NULL);
853 	OGR_L_SetAttributeFilter(planstate->ogr.lyr, NULL);
854 
855 	/*
856 	* The estimate number of rows returned must actually use restrictions.
857 	* Since OGR can't really give us a fast count with restrictions on
858 	* (usually involves a scan) and restrictions in the baserel mean we
859 	* must punt row count estimates.
860 	*/
861 
862 	/* TODO: calculate the row width based on the attribute types of the OGR table */
863 
864 	/*
865 	* OGR asks drivers to honestly state if they can provide a fast
866 	* row count, but too many drivers lie. We are only listing drivers
867 	* we trust in ogrCanReallyCountFast()
868 	*/
869 
870 	/* If we can quickly figure how many rows this layer has, then do so */
871 	if (scan_clauses == NIL &&
872 	        OGR_L_TestCapability(planstate->ogr.lyr, OLCFastFeatureCount) == TRUE &&
873 	        ogrCanReallyCountFast(&(planstate->ogr)))
874 	{
875 		/* Count rows, but don't force a slow count */
876 		int rows = OGR_L_GetFeatureCount(planstate->ogr.lyr, false);
877 		/* Only use row count if return is valid (>0) */
878 		if (rows >= 0)
879 		{
880 			planstate->nrows = rows;
881 			baserel->rows = rows;
882 		}
883 	}
884 
885 	/* Save connection state for next calls */
886 	baserel->fdw_private = (void*) planstate;
887 
888 	return;
889 }
890 
891 
892 
893 /*
894  * ogrGetForeignPaths
895  *		Create possible access paths for a scan on the foreign table
896  *
897  *		Currently there is only one
898  *		possible access path, which simply returns all records in the order in
899  *		the data file.
900  */
901 static void
ogrGetForeignPaths(PlannerInfo * root,RelOptInfo * baserel,Oid foreigntableid)902 ogrGetForeignPaths(PlannerInfo* root,
903                    RelOptInfo* baserel,
904                    Oid foreigntableid)
905 {
906 	OgrFdwPlanState* planstate = (OgrFdwPlanState*)(baserel->fdw_private);
907 
908 	/* TODO: replace this with something that looks at the OGRDriver and */
909 	/* makes a determination based on that? Better: add connection caching */
910 	/* so that slow startup doesn't matter so much */
911 	planstate->startup_cost = 25;
912 
913 	/* TODO: more research on what the total cost is supposed to mean, */
914 	/* relative to the startup cost? */
915 	planstate->total_cost = planstate->startup_cost + baserel->rows;
916 
917 	/* Built the (one) path we are providing. Providing fancy paths is */
918 	/* really only possible with back-ends that can properly provide */
919 	/* explain info on how they complete the query, not for something as */
920 	/* obtuse as OGR. (So far, have only seen it w/ the postgres_fdw */
921 	add_path(baserel,
922 	         (Path*) create_foreignscan_path(root, baserel,
923 #if PG_VERSION_NUM >= 90600
924 	                 NULL, /* PathTarget */
925 #endif
926 	                 baserel->rows,
927 	                 planstate->startup_cost,
928 	                 planstate->total_cost,
929 	                 NIL,     /* no pathkeys */
930 	                 NULL,    /* no outer rel either */
931 	                 NULL  /* no extra plan */
932 #if PG_VERSION_NUM >= 90500
933 	                 , NIL /* no fdw_private list */
934 #endif
935 	                                        )
936 	        );   /* no fdw_private data */
937 }
938 
939 
940 /*
941  * Convert an OgrFdwSpatialFilter into a List so it can
942  * be safely passed through the fdw_private list.
943  */
944 static List*
ogrSpatialFilterToList(const OgrFdwSpatialFilter * spatial_filter)945 ogrSpatialFilterToList(const OgrFdwSpatialFilter* spatial_filter)
946 {
947 	List *l = NIL;
948 	if (spatial_filter)
949 	{
950 		l = lappend(l, makeInteger(spatial_filter->ogrfldnum));
951 		l = lappend(l, makeFloat(psprintf("%.17g", spatial_filter->minx)));
952 		l = lappend(l, makeFloat(psprintf("%.17g", spatial_filter->miny)));
953 		l = lappend(l, makeFloat(psprintf("%.17g", spatial_filter->maxx)));
954 		l = lappend(l, makeFloat(psprintf("%.17g", spatial_filter->maxy)));
955 	}
956 	return l;
957 }
958 
959 /*
960  * Convert the List form back into an OgrFdwSpatialFilter
961  * after passing through fdw_private.
962  */
963 static OgrFdwSpatialFilter*
ogrSpatialFilterFromList(const List * lst)964 ogrSpatialFilterFromList(const List* lst)
965 {
966 	OgrFdwSpatialFilter* spatial_filter;
967 
968 	if (lst == NIL)
969 		return NULL;
970 
971 	Assert(list_length(lst) == 5);
972 
973 	spatial_filter = palloc(sizeof(OgrFdwSpatialFilter));
974 	spatial_filter->ogrfldnum = intVal(linitial(lst));
975 	spatial_filter->minx = floatVal(lsecond(lst));
976 	spatial_filter->miny = floatVal(lthird(lst));
977 	spatial_filter->maxx = floatVal(lfourth(lst));
978 	spatial_filter->maxy = floatVal(list_nth(lst, 4)); /* list_nth counts from zero */
979 	return spatial_filter;
980 }
981 
982 /*
983  * fileGetForeignPlan
984  *		Create a ForeignScan plan node for scanning the foreign table
985  */
986 static ForeignScan*
ogrGetForeignPlan(PlannerInfo * root,RelOptInfo * baserel,Oid foreigntableid,ForeignPath * best_path,List * tlist,List * scan_clauses,Plan * outer_plan)987 ogrGetForeignPlan(PlannerInfo* root,
988                   RelOptInfo* baserel,
989                   Oid foreigntableid,
990                   ForeignPath* best_path,
991                   List* tlist,
992                   List* scan_clauses
993 #if PG_VERSION_NUM >= 90500
994                   , Plan* outer_plan
995 #endif
996                  )
997 {
998 	Index scan_relid = baserel->relid;
999 	bool sql_generated;
1000 	StringInfoData sql;
1001 	List* params_list = NULL;
1002 	List* fdw_private;
1003 	OgrFdwPlanState* planstate = (OgrFdwPlanState*)(baserel->fdw_private);
1004 	OgrFdwState* state = (OgrFdwState*)(baserel->fdw_private);
1005 	OgrFdwSpatialFilter* spatial_filter = NULL;
1006 	char* attribute_filter = NULL;
1007 
1008 	/* Add in column mapping data to build SQL with the right OGR column names */
1009 	ogrReadColumnData(state);
1010 
1011 	/*
1012 	 * TODO: Review the columns requested (via params_list) and only pull those back, using
1013 	 * OGR_L_SetIgnoredFields. This is less important than pushing restrictions
1014 	 * down to OGR via OGR_L_SetAttributeFilter (done) and (TODO) OGR_L_SetSpatialFilter.
1015 	 */
1016 	initStringInfo(&sql);
1017 	sql_generated = ogrDeparse(&sql, root, baserel, scan_clauses, state, &params_list, &spatial_filter);
1018 
1019 	/* Extract the OGR SQL from the StringInfoData */
1020 	if (sql_generated && sql.len > 0)
1021 		attribute_filter = sql.data;
1022 
1023 	/* Log filters at debug level one as necessary */
1024 	if (attribute_filter)
1025 		elog(DEBUG1, "OGR SQL: %s", attribute_filter);
1026 	if (spatial_filter)
1027 		elog(DEBUG1, "OGR spatial filter (%g %g, %g %g)",
1028 		             spatial_filter->minx, spatial_filter->miny,
1029 		             spatial_filter->maxx, spatial_filter->maxy);
1030 	/*
1031 	 * Here we strip RestrictInfo
1032 	 * nodes from the clauses and ignore pseudoconstants (which will be
1033 	 * handled elsewhere).
1034 	 * Some FDW implementations (mysql_fdw) just pass this full list on to the
1035 	 * make_foreignscan function. postgres_fdw carefully separates local and remote
1036 	 * clauses and only passes the local ones to make_foreignscan, so this
1037 	 * is probably best practice, though re-applying the clauses is probably
1038 	 * the least of our performance worries with this fdw. For now, we just
1039 	 * pass them all to make_foreignscan, see no evil, etc.
1040 	 */
1041 	scan_clauses = extract_actual_clauses(scan_clauses, false);
1042 
1043 	/* Pack the data we want to pass to the execution stage into a List. */
1044 	/* The members of this list must by copyable by PgSQL, which means */
1045 	/* they need to be Lists themselves, or Value nodes, otherwise when */
1046 	/* the plan gets copied the copy might fail. */
1047 	fdw_private = list_make3(makeString(attribute_filter), params_list, ogrSpatialFilterToList(spatial_filter));
1048 
1049 	/* Clean up our connection */
1050 	ogrFinishConnection(&(planstate->ogr));
1051 
1052 	/* Create the ForeignScan node */
1053 	return make_foreignscan(tlist,
1054 	                        scan_clauses,
1055 	                        scan_relid,
1056 	                        NIL,	/* no expressions to evaluate */
1057 	                        fdw_private
1058 #if PG_VERSION_NUM >= 90500
1059 	                        , NIL /* no scan_tlist */
1060 	                        , NIL  /* no remote quals */
1061 	                        , outer_plan
1062 #endif
1063 	                       );
1064 
1065 
1066 }
1067 
1068 static void
pgCanConvertToOgr(Oid pg_type,OGRFieldType ogr_type,const char * colname,const char * tblname)1069 pgCanConvertToOgr(Oid pg_type, OGRFieldType ogr_type, const char* colname, const char* tblname)
1070 {
1071 	if (pg_type == BOOLOID && ogr_type == OFTInteger)
1072 	{
1073 		return;
1074 	}
1075 	else if (pg_type == INT2OID && ogr_type == OFTInteger)
1076 	{
1077 		return;
1078 	}
1079 	else if (pg_type == INT4OID && ogr_type == OFTInteger)
1080 	{
1081 		return;
1082 	}
1083 	else if (pg_type == INT8OID)
1084 	{
1085 #if GDAL_VERSION_MAJOR >= 2
1086 		if (ogr_type == OFTInteger64)
1087 		{
1088 			return;
1089 		}
1090 #else
1091 		if (ogr_type == OFTInteger)
1092 		{
1093 			return;
1094 		}
1095 #endif
1096 	}
1097 	else if (pg_type == NUMERICOID && ogr_type == OFTReal)
1098 	{
1099 		return;
1100 	}
1101 	else if (pg_type == FLOAT4OID && ogr_type == OFTReal)
1102 	{
1103 		return;
1104 	}
1105 	else if (pg_type == FLOAT8OID && ogr_type == OFTReal)
1106 	{
1107 		return;
1108 	}
1109 	else if (pg_type == TEXTOID && ogr_type == OFTString)
1110 	{
1111 		return;
1112 	}
1113 	else if (pg_type == VARCHAROID && ogr_type == OFTString)
1114 	{
1115 		return;
1116 	}
1117 	else if (pg_type == CHAROID && ogr_type == OFTString)
1118 	{
1119 		return;
1120 	}
1121 	else if (pg_type == BPCHAROID && ogr_type == OFTString)
1122 	{
1123 		return;
1124 	}
1125 	else if (pg_type == NAMEOID && ogr_type == OFTString)
1126 	{
1127 		return;
1128 	}
1129 	else if (pg_type == BYTEAOID && ogr_type == OFTBinary)
1130 	{
1131 		return;
1132 	}
1133 	else if (pg_type == DATEOID && ogr_type == OFTDate)
1134 	{
1135 		return;
1136 	}
1137 	else if (pg_type == TIMEOID && ogr_type == OFTTime)
1138 	{
1139 		return;
1140 	}
1141 	else if (pg_type == TIMESTAMPOID && ogr_type == OFTDateTime)
1142 	{
1143 		return;
1144 	}
1145 
1146 	ereport(ERROR, (
1147 	            errcode(ERRCODE_FDW_INVALID_DATA_TYPE),
1148 	            errmsg("column \"%s\" of foreign table \"%s\" converts \"%s\" to OGR \"%s\"",
1149 	                   colname, tblname,
1150 	                   format_type_be(pg_type), OGR_GetFieldTypeName(ogr_type))
1151 	        ));
1152 }
1153 
1154 static void
ogrCanConvertToPg(OGRFieldType ogr_type,Oid pg_type,const char * colname,const char * tblname)1155 ogrCanConvertToPg(OGRFieldType ogr_type, Oid pg_type, const char* colname, const char* tblname)
1156 {
1157 	switch (ogr_type)
1158 	{
1159 	case OFTInteger:
1160 		if (pg_type == BOOLOID ||  pg_type == INT4OID || pg_type == INT8OID ||
1161 		    pg_type == NUMERICOID || pg_type == FLOAT4OID ||
1162 		    pg_type == FLOAT8OID || pg_type == TEXTOID || pg_type == VARCHAROID)
1163 		{
1164 			return;
1165 		}
1166 		break;
1167 
1168 	case OFTReal:
1169 		if (pg_type == NUMERICOID || pg_type == FLOAT4OID || pg_type == FLOAT8OID ||
1170 		    pg_type == TEXTOID || pg_type == VARCHAROID)
1171 		{
1172 			return;
1173 		}
1174 		break;
1175 
1176 	case OFTBinary:
1177 		if (pg_type == BYTEAOID)
1178 		{
1179 			return;
1180 		}
1181 		break;
1182 
1183 	case OFTString:
1184 		if (pg_type == TEXTOID || pg_type == VARCHAROID || pg_type == CHAROID || pg_type == BPCHAROID)
1185 		{
1186 			return;
1187 		}
1188 		break;
1189 
1190 	case OFTDate:
1191 		if (pg_type == DATEOID || pg_type == TIMESTAMPOID || pg_type == TEXTOID || pg_type == VARCHAROID)
1192 		{
1193 			return;
1194 		}
1195 		break;
1196 
1197 	case OFTTime:
1198 		if (pg_type == TIMEOID || pg_type == TEXTOID || pg_type == VARCHAROID)
1199 		{
1200 			return;
1201 		}
1202 		break;
1203 
1204 	case OFTDateTime:
1205 		if (pg_type == TIMESTAMPOID || pg_type == TEXTOID || pg_type == VARCHAROID)
1206 		{
1207 			return;
1208 		}
1209 		break;
1210 
1211 #if GDAL_VERSION_MAJOR >= 2
1212 	case OFTInteger64:
1213 		if (pg_type == INT8OID || pg_type == NUMERICOID || pg_type == FLOAT8OID ||
1214 		    pg_type == TEXTOID || pg_type == VARCHAROID)
1215 		{
1216 			return;
1217 		}
1218 		break;
1219 #endif
1220 
1221 	case OFTWideString:
1222 	case OFTIntegerList:
1223 #if GDAL_VERSION_MAJOR >= 2
1224 	case OFTInteger64List:
1225 #endif
1226 	case OFTRealList:
1227 	case OFTStringList:
1228 	case OFTWideStringList:
1229 	{
1230 		ereport(ERROR, (
1231 		    errcode(ERRCODE_FDW_INVALID_DATA_TYPE),
1232 		    errmsg("column \"%s\" of foreign table \"%s\" uses an OGR array, currently unsupported", colname, tblname)
1233 		    ));
1234 		break;
1235 	}
1236 	}
1237 	ereport(ERROR, (
1238 	    errcode(ERRCODE_FDW_INVALID_DATA_TYPE),
1239 	    errmsg("column \"%s\" of foreign table \"%s\" converts OGR \"%s\" to \"%s\"",
1240 	           colname, tblname,
1241 	           OGR_GetFieldTypeName(ogr_type), format_type_be(pg_type))
1242 	    ));
1243 }
1244 
1245 #ifdef OGR_FDW_HEXWKB
1246 
1247 static char* hexchr = "0123456789ABCDEF";
1248 
1249 static char*
ogrBytesToHex(unsigned char * bytes,size_t size)1250 ogrBytesToHex(unsigned char* bytes, size_t size)
1251 {
1252 	char* hex;
1253 	int i;
1254 	if (! bytes || ! size)
1255 	{
1256 		elog(ERROR, "ogrBytesToHex: invalid input");
1257 		return NULL;
1258 	}
1259 	hex = palloc(size * 2 + 1);
1260 	hex[2 * size] = '\0';
1261 	for (i = 0; i < size; i++)
1262 	{
1263 		/* Top four bits to 0-F */
1264 		hex[2 * i] = hexchr[bytes[i] >> 4];
1265 		/* Bottom four bits to 0-F */
1266 		hex[2 * i + 1] = hexchr[bytes[i] & 0x0F];
1267 	}
1268 	return hex;
1269 }
1270 
1271 #endif
1272 
1273 static void
freeOgrFdwTable(OgrFdwTable * table)1274 freeOgrFdwTable(OgrFdwTable* table)
1275 {
1276 	if (table)
1277 	{
1278 		if (table->tblname)
1279 		{
1280 			pfree(table->tblname);
1281 		}
1282 		if (table->cols)
1283 		{
1284 			pfree(table->cols);
1285 		}
1286 		pfree(table);
1287 	}
1288 }
1289 
1290 typedef struct
1291 {
1292 	char* fldname;
1293 	int fldnum;
1294 } OgrFieldEntry;
1295 
1296 static int
ogrFieldEntryCmpFunc(const void * a,const void * b)1297 ogrFieldEntryCmpFunc(const void* a, const void* b)
1298 {
1299 	const char* a_name = ((OgrFieldEntry*)a)->fldname;
1300 	const char* b_name = ((OgrFieldEntry*)b)->fldname;
1301 
1302 	return strcasecmp(a_name, b_name);
1303 }
1304 
1305 
1306 /*
1307  * The execstate holds a foreign table relation id and an OGR connection,
1308  * this function finds all the OGR fields that match up to columns in the
1309  * foreign table definition, using columns name match and data type consistency
1310  * as the criteria for making a match.
1311  * The results of the matching are stored in the execstate before the function
1312  * returns.
1313  */
1314 static void
ogrReadColumnData(OgrFdwState * state)1315 ogrReadColumnData(OgrFdwState* state)
1316 {
1317 	Relation rel;
1318 	TupleDesc tupdesc;
1319 	int i;
1320 	OgrFdwTable* tbl;
1321 	OGRFeatureDefnH dfn;
1322 	int ogr_ncols;
1323 	int fid_count = 0;
1324 	int geom_count = 0;
1325 	int ogr_geom_count = 0;
1326 	int field_count = 0;
1327 	OgrFieldEntry* ogr_fields;
1328 	int ogr_fields_count = 0;
1329 	char* tblname = get_rel_name(state->foreigntableid);
1330 
1331 	/* Blow away any existing table in the state */
1332 	if (state->table)
1333 	{
1334 		freeOgrFdwTable(state->table);
1335 		state->table = NULL;
1336 	}
1337 
1338 	/* Fresh table */
1339 	tbl = palloc0(sizeof(OgrFdwTable));
1340 
1341 	/* One column for each PgSQL foreign table column */
1342 #if PG_VERSION_NUM < 120000
1343 	rel = heap_open(state->foreigntableid, NoLock);
1344 #else
1345 	rel = table_open(state->foreigntableid, NoLock);
1346 #endif /* PG_VERSION_NUM */
1347 
1348 	tupdesc = rel->rd_att;
1349 	state->tupdesc = tupdesc;
1350 	tbl->ncols = tupdesc->natts;
1351 	tbl->cols = palloc0(tbl->ncols * sizeof(OgrFdwColumn));
1352 	tbl->tblname = pstrdup(tblname);
1353 
1354 	/* Get OGR metadata ready */
1355 	dfn = OGR_L_GetLayerDefn(state->ogr.lyr);
1356 	ogr_ncols = OGR_FD_GetFieldCount(dfn);
1357 #if (GDAL_VERSION_NUM >= GDAL_COMPUTE_VERSION(1,11,0))
1358 	ogr_geom_count = OGR_FD_GetGeomFieldCount(dfn);
1359 #else
1360 	ogr_geom_count = (OGR_FD_GetGeomType(dfn) != wkbNone) ? 1 : 0;
1361 #endif
1362 
1363 
1364 	/* Prepare sorted list of OGR column names */
1365 	/* TODO: change this to a hash table, to avoid repeated strcmp */
1366 	/* We will search both the original and laundered OGR field names for matches */
1367 	ogr_fields_count = 2 * ogr_ncols;
1368 	ogr_fields = palloc0(ogr_fields_count * sizeof(OgrFieldEntry));
1369 	for (i = 0; i < ogr_ncols; i++)
1370 	{
1371 		char* fldname = pstrdup(OGR_Fld_GetNameRef(OGR_FD_GetFieldDefn(dfn, i)));
1372 		char* fldname_laundered = palloc(STR_MAX_LEN);
1373 		strncpy(fldname_laundered, fldname, STR_MAX_LEN);
1374 		ogrStringLaunder(fldname_laundered);
1375 		ogr_fields[2 * i].fldname = fldname;
1376 		ogr_fields[2 * i].fldnum = i;
1377 		ogr_fields[2 * i + 1].fldname = fldname_laundered;
1378 		ogr_fields[2 * i + 1].fldnum = i;
1379 	}
1380 	qsort(ogr_fields, ogr_fields_count, sizeof(OgrFieldEntry), ogrFieldEntryCmpFunc);
1381 
1382 
1383 	/* loop through foreign table columns */
1384 	for (i = 0; i < tbl->ncols; i++)
1385 	{
1386 		List* options;
1387 		ListCell* lc;
1388 		OgrFieldEntry* found_entry;
1389 		OgrFieldEntry entry;
1390 
1391 #if PG_VERSION_NUM >= 110000
1392 		Form_pg_attribute att_tuple = &tupdesc->attrs[i];
1393 #else
1394 		Form_pg_attribute att_tuple = tupdesc->attrs[i];
1395 #endif
1396 		OgrFdwColumn col = tbl->cols[i];
1397 		col.pgattnum = att_tuple->attnum;
1398 		col.pgtype = att_tuple->atttypid;
1399 		col.pgtypmod = att_tuple->atttypmod;
1400 		col.pgattisdropped = att_tuple->attisdropped;
1401 
1402 		/* Skip filling in any further metadata about dropped columns */
1403 		if (col.pgattisdropped)
1404 		{
1405 			continue;
1406 		}
1407 
1408 		/* Find the appropriate conversion functions */
1409 		getTypeInputInfo(col.pgtype, &col.pginputfunc, &col.pginputioparam);
1410 		getTypeBinaryInputInfo(col.pgtype, &col.pgrecvfunc, &col.pgrecvioparam);
1411 		getTypeOutputInfo(col.pgtype, &col.pgoutputfunc, &col.pgoutputvarlena);
1412 		getTypeBinaryOutputInfo(col.pgtype, &col.pgsendfunc, &col.pgsendvarlena);
1413 
1414 		/* Get the PgSQL column name */
1415 #if PG_VERSION_NUM >= 110000
1416 		col.pgname = get_attname(rel->rd_id, att_tuple->attnum, false);
1417 #else
1418 		col.pgname = get_attname(rel->rd_id, att_tuple->attnum);
1419 #endif
1420 
1421 		/* Handle FID first */
1422 		if (strcaseeq(col.pgname, "fid") &&
1423 		        (col.pgtype == INT4OID || col.pgtype == INT8OID))
1424 		{
1425 			if (fid_count >= 1)
1426 			{
1427 				elog(ERROR, "FDW table '%s' includes more than one FID column", tblname);
1428 			}
1429 
1430 			col.ogrvariant = OGR_FID;
1431 			col.ogrfldnum = fid_count++;
1432 			tbl->cols[i] = col;
1433 			continue;
1434 		}
1435 
1436 		/* If the OGR source has geometries, can we match them to Pg columns? */
1437 		/* We'll match to the first ones we find, irrespective of name */
1438 		if (geom_count < ogr_geom_count && col.pgtype == ogrGetGeometryOid())
1439 		{
1440 			col.ogrvariant = OGR_GEOMETRY;
1441 			col.ogrfldtype = OFTBinary;
1442 			col.ogrfldnum = geom_count++;
1443 			tbl->cols[i] = col;
1444 			continue;
1445 		}
1446 
1447 		/* Now we search for matches in the OGR fields */
1448 
1449 		/* By default, search for the PgSQL column name */
1450 		entry.fldname = col.pgname;
1451 		entry.fldnum = 0;
1452 
1453 		/*
1454 		 * But, if there is a 'column_name' option for this column, we
1455 		 * want to search for *that* in the OGR layer.
1456 		 */
1457 		options = GetForeignColumnOptions(state->foreigntableid, i + 1);
1458 		foreach (lc, options)
1459 		{
1460 			DefElem*    def = (DefElem*) lfirst(lc);
1461 
1462 			if (streq(def->defname, OPT_COLUMN))
1463 			{
1464 				entry.fldname = defGetString(def);
1465 				break;
1466 			}
1467 		}
1468 
1469 		/* Search PgSQL column name in the OGR column name list */
1470 		found_entry = bsearch(&entry, ogr_fields, ogr_fields_count, sizeof(OgrFieldEntry), ogrFieldEntryCmpFunc);
1471 
1472 		/* Column name matched, so save this entry, if the types are consistent */
1473 		if (found_entry)
1474 		{
1475 			OGRFieldDefnH fld = OGR_FD_GetFieldDefn(dfn, found_entry->fldnum);
1476 			OGRFieldType fldtype = OGR_Fld_GetType(fld);
1477 
1478 			/* Error if types mismatched when column names match */
1479 			ogrCanConvertToPg(fldtype, col.pgtype, col.pgname, tblname);
1480 
1481 			col.ogrvariant = OGR_FIELD;
1482 			col.ogrfldnum = found_entry->fldnum;
1483 			col.ogrfldtype = fldtype;
1484 			field_count++;
1485 		}
1486 		else
1487 		{
1488 			col.ogrvariant = OGR_UNMATCHED;
1489 		}
1490 		tbl->cols[i] = col;
1491 	}
1492 
1493 	elog(DEBUG2, "ogrReadColumnData matched %d FID, %d GEOM, %d FIELDS out of %d PGSQL COLUMNS", fid_count, geom_count,
1494 	     field_count, tbl->ncols);
1495 
1496 	/* Clean up */
1497 
1498 	state->table = tbl;
1499 	for (i = 0; i < 2 * ogr_ncols; i++)
1500 	{
1501 		if (ogr_fields[i].fldname)
1502 		{
1503 			pfree(ogr_fields[i].fldname);
1504 		}
1505 	}
1506 	pfree(ogr_fields);
1507 #if PG_VERSION_NUM < 120000
1508 	heap_close(rel, NoLock);
1509 #else
1510 	table_close(rel, NoLock);
1511 #endif /* PG_VERSION_NUM */
1512 
1513 
1514 	return;
1515 }
1516 
1517 /*
1518  * ogrLookupGeometryFunctionOid
1519  *
1520  * Find the procedure Oids of useful functions so we can call
1521  * them later.
1522  */
1523 static Oid
ogrLookupGeometryFunctionOid(const char * proname)1524 ogrLookupGeometryFunctionOid(const char* proname)
1525 {
1526 	List* names;
1527 	FuncCandidateList clist;
1528 
1529 	/* This only works if PostGIS is installed */
1530 	if (ogrGetGeometryOid() == InvalidOid || ogrGetGeometryOid() == BYTEAOID)
1531 	{
1532 		return InvalidOid;
1533 	}
1534 
1535 	names = stringToQualifiedNameList(proname);
1536 #if PG_VERSION_NUM < 90400
1537 	clist = FuncnameGetCandidates(names, -1, NIL, false, false);
1538 #else
1539 	clist = FuncnameGetCandidates(names, -1, NIL, false, false, false);
1540 #endif
1541 	if (streq(proname, "st_setsrid"))
1542 	{
1543 		do
1544 		{
1545 			int i;
1546 			for (i = 0; i < clist->nargs; i++)
1547 			{
1548 				if (clist->args[i] == ogrGetGeometryOid())
1549 				{
1550 					return clist->oid;
1551 				}
1552 			}
1553 		}
1554 		while ((clist = clist->next));
1555 	}
1556 	else if (streq(proname, "postgis_typmod_srid"))
1557 	{
1558 		return clist->oid;
1559 	}
1560 
1561 	return InvalidOid;
1562 }
1563 
1564 /*
1565  * ogrBeginForeignScan
1566  */
1567 static void
ogrBeginForeignScan(ForeignScanState * node,int eflags)1568 ogrBeginForeignScan(ForeignScanState* node, int eflags)
1569 {
1570 	OgrFdwState* state;
1571 	OgrFdwExecState* execstate;
1572 	OgrFdwSpatialFilter* spatial_filter;
1573 	Oid foreigntableid = RelationGetRelid(node->ss.ss_currentRelation);
1574 	ForeignScan* fsplan = (ForeignScan*)node->ss.ps.plan;
1575 
1576 	/* Do nothing in EXPLAIN (no ANALYZE) case */
1577 	if (eflags & EXEC_FLAG_EXPLAIN_ONLY)
1578 		return;
1579 
1580 	/* Initialize OGR connection */
1581 	state = getOgrFdwState(foreigntableid, OGR_EXEC_STATE);
1582 	execstate = (OgrFdwExecState*)state;
1583 
1584 	/* Read the OGR layer definition and PgSQL foreign table definitions */
1585 	ogrReadColumnData(state);
1586 
1587 	/* Collect the procedure Oids for PostGIS functions we might need */
1588 	execstate->setsridfunc = ogrLookupGeometryFunctionOid("st_setsrid");
1589 	execstate->typmodsridfunc = ogrLookupGeometryFunctionOid("postgis_typmod_srid");
1590 
1591 	/* Get OGR SQL generated by the deparse step during the planner function. */
1592 	execstate->sql = (char*) strVal(list_nth(fsplan->fdw_private, 0));
1593 
1594 	/* TODO: Use the parse step attribute list to restrict requested columns */
1595 	// execstate->retrieved_attrs = (List *) list_nth(fsplan->fdw_private, 1);
1596 
1597 	/* Get spatial filter generated by the deparse step. */
1598 	spatial_filter = ogrSpatialFilterFromList(list_nth(fsplan->fdw_private, 2));
1599 
1600 	if (spatial_filter)
1601 	{
1602 		OGR_L_SetSpatialFilterRectEx(execstate->ogr.lyr,
1603 		                             spatial_filter->ogrfldnum,
1604 		                             spatial_filter->minx,
1605 		                             spatial_filter->miny,
1606 		                             spatial_filter->maxx,
1607 		                             spatial_filter->maxy
1608 		                             );
1609 	}
1610 
1611 	if (execstate->sql && strlen(execstate->sql) > 0)
1612 	{
1613 		OGRErr err = OGR_L_SetAttributeFilter(execstate->ogr.lyr, execstate->sql);
1614 		if (err != OGRERR_NONE)
1615 		{
1616 			const char* ogrerr = CPLGetLastErrorMsg();
1617 
1618 			if (ogrerr && ! streq(ogrerr, ""))
1619 			{
1620 				ereport(NOTICE,
1621 				        (errcode(ERRCODE_FDW_ERROR),
1622 				         errmsg("unable to set OGR SQL '%s' on layer", execstate->sql),
1623 				         errhint("%s", ogrerr)));
1624 			}
1625 			else
1626 			{
1627 				ereport(NOTICE,
1628 				        (errcode(ERRCODE_FDW_ERROR),
1629 				         errmsg("unable to set OGR SQL '%s' on layer", execstate->sql)));
1630 			}
1631 		}
1632 	}
1633 	else
1634 	{
1635 		OGR_L_SetAttributeFilter(execstate->ogr.lyr, NULL);
1636 	}
1637 
1638 	/* Save the state for the next call */
1639 	node->fdw_state = (void*) execstate;
1640 
1641 	return;
1642 }
1643 
1644 /*
1645  * Rather than explicitly try and form PgSQL datums, use the type
1646  * input functions, that accept cstring representations, and convert
1647  * to the input format. We have to lookup the right input function for
1648  * each column in the foreign table.
1649  */
1650 static Datum
pgDatumFromCString(const char * cstr,Oid pgtype,int pgtypmod,Oid pginputfunc)1651 pgDatumFromCString(const char* cstr, Oid pgtype, int pgtypmod, Oid pginputfunc)
1652 {
1653 	Datum value;
1654 	Datum cdata = CStringGetDatum(cstr);
1655 
1656 	value = OidFunctionCall3(pginputfunc, cdata,
1657 	                         ObjectIdGetDatum(InvalidOid),
1658 	                         Int32GetDatum(pgtypmod));
1659 
1660 	return value;
1661 }
1662 
1663 static inline void
ogrNullSlot(Datum * values,bool * nulls,int i)1664 ogrNullSlot(Datum* values, bool* nulls, int i)
1665 {
1666 	values[i] = PointerGetDatum(NULL);
1667 	nulls[i] = true;
1668 }
1669 
1670 /*
1671 * The ogrIterateForeignScan is getting a new TupleTableSlot to handle
1672 * for each iteration. Each slot contains an entry for every column in
1673 * in the foreign table, that has to be filled out, either with a value
1674 * or a NULL for columns that either have been deleted or were not requested
1675 * in the query.
1676 *
1677 * The tupledescriptor tells us about the types of each slot.
1678 * For now we assume our slot has exactly the same number of
1679 * records and equivalent types to our OGR layer, and that our
1680 * foreign table's first two columns are an integer primary key
1681 * using int8 as the type, and then a geometry using bytea as
1682 * the type, then everything else.
1683 */
1684 static OGRErr
ogrFeatureToSlot(const OGRFeatureH feat,TupleTableSlot * slot,const OgrFdwExecState * execstate)1685 ogrFeatureToSlot(const OGRFeatureH feat, TupleTableSlot* slot, const OgrFdwExecState* execstate)
1686 {
1687 	const OgrFdwTable* tbl = execstate->table;
1688 	int i;
1689 	Datum* values = slot->tts_values;
1690 	bool* nulls = slot->tts_isnull;
1691 	TupleDesc tupdesc = slot->tts_tupleDescriptor;
1692 	int have_typmod_funcs = (execstate->setsridfunc && execstate->typmodsridfunc);
1693 
1694 	/* Check our assumption that slot and setup data match */
1695 	if (tbl->ncols != tupdesc->natts)
1696 	{
1697 		elog(ERROR, "FDW metadata table and exec table have mismatching number of columns");
1698 		return OGRERR_FAILURE;
1699 	}
1700 
1701 	/* For each pgtable column, get a value from OGR */
1702 	for (i = 0; i < tbl->ncols; i++)
1703 	{
1704 		OgrFdwColumn col = tbl->cols[i];
1705 		const char* pgname = col.pgname;
1706 		Oid pgtype = col.pgtype;
1707 		int pgtypmod = col.pgtypmod;
1708 		Oid pginputfunc = col.pginputfunc;
1709 		int ogrfldnum = col.ogrfldnum;
1710 		OGRFieldType ogrfldtype = col.ogrfldtype;
1711 		OgrColumnVariant ogrvariant = col.ogrvariant;
1712 
1713 		/*
1714 		 * Fill in dropped attributes with NULL
1715 		 */
1716 		if (col.pgattisdropped)
1717 		{
1718 			ogrNullSlot(values, nulls, i);
1719 			continue;
1720 		}
1721 
1722 		if (ogrvariant == OGR_FID)
1723 		{
1724 			GIntBig fid = OGR_F_GetFID(feat);
1725 
1726 			if (fid == OGRNullFID)
1727 			{
1728 				ogrNullSlot(values, nulls, i);
1729 			}
1730 			else
1731 			{
1732 				char fidstr[256];
1733 				snprintf(fidstr, 256, OGR_FDW_FRMT_INT64, OGR_FDW_CAST_INT64(fid));
1734 
1735 				nulls[i] = false;
1736 				values[i] = pgDatumFromCString(fidstr, pgtype, pgtypmod, pginputfunc);
1737 			}
1738 		}
1739 		else if (ogrvariant == OGR_GEOMETRY)
1740 		{
1741 			int wkbsize;
1742 			int varsize;
1743 			bytea* varlena;
1744 			unsigned char* wkb;
1745 			OGRErr err;
1746 
1747 #if (GDAL_VERSION_NUM >= GDAL_COMPUTE_VERSION(1,11,0))
1748 			OGRGeometryH geom = OGR_F_GetGeomFieldRef(feat, ogrfldnum);
1749 #else
1750 			OGRGeometryH geom = OGR_F_GetGeometryRef(feat);
1751 #endif
1752 
1753 			/* No geometry ? NULL */
1754 			if (! geom)
1755 			{
1756 				/* No geometry column, so make the output null */
1757 				ogrNullSlot(values, nulls, i);
1758 				continue;
1759 			}
1760 
1761 			/*
1762 			 * Start by generating standard PgSQL variable length byte
1763 			 * buffer, with WKB filled into the data area.
1764 			 */
1765 			wkbsize = OGR_G_WkbSize(geom);
1766 			varsize = wkbsize + VARHDRSZ;
1767 			varlena = palloc(varsize);
1768 			wkb = (unsigned char*)VARDATA(varlena);
1769 			err = OGR_G_ExportToWkb(geom, wkbNDR, wkb);
1770 			SET_VARSIZE(varlena, varsize);
1771 
1772 			/* Couldn't create WKB from OGR geometry? error */
1773 			if (err != OGRERR_NONE)
1774 			{
1775 				return err;
1776 			}
1777 
1778 			if (pgtype == BYTEAOID)
1779 			{
1780 				/*
1781 				 * Nothing special to do for bytea, just send the varlena data through!
1782 				 */
1783 				nulls[i] = false;
1784 				values[i] = PointerGetDatum(varlena);
1785 			}
1786 			else if (pgtype == ogrGetGeometryOid())
1787 			{
1788 				/*
1789 				 * For geometry we need to convert the varlena WKB data into a serialized
1790 				 * geometry (aka "gserialized"). For that, we can use the type's "recv" function
1791 				 * which takes in WKB and spits out serialized form, or the "input" function
1792 				 * that takes in HEXWKB. The "input" function is more lax about geometry
1793 				 * structure errors (unclosed polys, etc).
1794 				 */
1795 #ifdef OGR_FDW_HEXWKB
1796 				char* hexwkb = ogrBytesToHex(wkb, wkbsize);
1797 				/*
1798 				 * Use the input function to convert the WKB from OGR into
1799 				 * a PostGIS internal format.
1800 				 */
1801 				nulls[i] = false;
1802 				values[i] = OidFunctionCall1(col.pginputfunc, PointerGetDatum(hexwkb));
1803 				pfree(hexwkb);
1804 #else
1805 				/*
1806 				 * The "recv" function expects to receive a StringInfo pointer
1807 				 * on the first argument, so we form one of those ourselves by
1808 				 * hand. Rather than copy into a fresh buffer, we'll just use the
1809 				 * existing varlena buffer and point to the data area.
1810 				 *
1811 				 * The "recv" function tests for basic geometry validity,
1812 				 * things like polygon closure, etc. So don't feed it junk.
1813 				 */
1814 				StringInfoData strinfo;
1815 				strinfo.data = (char*)wkb;
1816 				strinfo.len = wkbsize;
1817 				strinfo.maxlen = strinfo.len;
1818 				strinfo.cursor = 0;
1819 
1820 				/*
1821 				 * Use the recv function to convert the WKB from OGR into
1822 				 * a PostGIS internal format.
1823 				 */
1824 				nulls[i] = false;
1825 				values[i] = OidFunctionCall1(col.pgrecvfunc, PointerGetDatum(&strinfo));
1826 #endif
1827 
1828 				/*
1829 				 * Apply the typmod restriction to the incoming geometry, so it's
1830 				 * not really a restriction anymore, it's more like a requirement.
1831 				 *
1832 				 * TODO: In the case where the OGR input actually *knows* what SRID
1833 				 * it is, we should actually apply *that* and let the restriction run
1834 				 * its usual course.
1835 				 */
1836 				if (have_typmod_funcs && col.pgtypmod >= 0)
1837 				{
1838 					Datum srid = OidFunctionCall1(execstate->typmodsridfunc, Int32GetDatum(col.pgtypmod));
1839 					values[i] = OidFunctionCall2(execstate->setsridfunc, values[i], srid);
1840 				}
1841 			}
1842 			else
1843 			{
1844 				elog(NOTICE, "conversion to geometry called with column type not equal to bytea or geometry");
1845 				ogrNullSlot(values, nulls, i);
1846 			}
1847 
1848 		}
1849 		else if (ogrvariant == OGR_FIELD)
1850 		{
1851 #if (GDAL_VERSION_NUM >= GDAL_COMPUTE_VERSION(2,2,0))
1852 			int field_not_null = OGR_F_IsFieldSet(feat, ogrfldnum) && ! OGR_F_IsFieldNull(feat, ogrfldnum);
1853 #else
1854 			int field_not_null = OGR_F_IsFieldSet(feat, ogrfldnum);
1855 #endif
1856 
1857 			/* Ensure that the OGR data type fits the destination Pg column */
1858 			ogrCanConvertToPg(ogrfldtype, pgtype, pgname, tbl->tblname);
1859 
1860 			/* Only convert non-null fields */
1861 			if (field_not_null)
1862 			{
1863 				switch (ogrfldtype)
1864 				{
1865 				case OFTBinary:
1866 				{
1867 					/*
1868 					 * Convert binary fields to bytea directly
1869 					 */
1870 					int bufsize;
1871 					GByte* buf = OGR_F_GetFieldAsBinary(feat, ogrfldnum, &bufsize);
1872 					int varsize = bufsize + VARHDRSZ;
1873 					bytea* varlena = palloc(varsize);
1874 					memcpy(VARDATA(varlena), buf, bufsize);
1875 					SET_VARSIZE(varlena, varsize);
1876 					nulls[i] = false;
1877 					values[i] = PointerGetDatum(varlena);
1878 					break;
1879 				}
1880 				case OFTInteger:
1881 				case OFTReal:
1882 				case OFTString:
1883 #if GDAL_VERSION_MAJOR >= 2
1884 				case OFTInteger64:
1885 #endif
1886 				{
1887 					/*
1888 					 * Convert numbers and strings via a string representation.
1889 					 * Handling numbers directly would be faster, but require a lot of extra code.
1890 					 * For now, we go via text.
1891 					 */
1892 					const char* cstr_in = OGR_F_GetFieldAsString(feat, ogrfldnum);
1893 					size_t cstr_len = cstr_in ? strlen(cstr_in) : 0;
1894 					if (cstr_in && cstr_len > 0)
1895 					{
1896 						char* cstr_decoded;
1897 						if (execstate->ogr.char_encoding)
1898 						{
1899 							cstr_decoded = pg_any_to_server(cstr_in, cstr_len, execstate->ogr.char_encoding);
1900 						}
1901 						else
1902 						{
1903 							cstr_decoded = pstrdup(cstr_in);
1904 						}
1905 						nulls[i] = false;
1906 						values[i] = pgDatumFromCString(cstr_decoded, pgtype, pgtypmod, pginputfunc);
1907 						/* Free cstr_decoded if it is a copy */
1908 						if (cstr_in != cstr_decoded)
1909 							pfree(cstr_decoded);
1910 					}
1911 					else
1912 					{
1913 						ogrNullSlot(values, nulls, i);
1914 					}
1915 					break;
1916 				}
1917 				case OFTDate:
1918 				case OFTTime:
1919 				case OFTDateTime:
1920 				{
1921 					/*
1922 					 * OGR date/times have a weird access method, so we use that to pull
1923 					 * out the raw data and turn it into a string for PgSQL's (very
1924 					 * sophisticated) date/time parsing routines to handle.
1925 					 */
1926 					int year, month, day, hour, minute, second, tz;
1927 					char cstr[256];
1928 
1929 					OGR_F_GetFieldAsDateTime(feat, ogrfldnum,
1930 					                         &year, &month, &day,
1931 					                         &hour, &minute, &second, &tz);
1932 
1933 					if (ogrfldtype == OFTDate)
1934 					{
1935 						snprintf(cstr, 256, "%d-%02d-%02d", year, month, day);
1936 					}
1937 					else if (ogrfldtype == OFTTime)
1938 					{
1939 						snprintf(cstr, 256, "%02d:%02d:%02d", hour, minute, second);
1940 					}
1941 					else
1942 					{
1943 						snprintf(cstr, 256, "%d-%02d-%02d %02d:%02d:%02d", year, month, day, hour, minute, second);
1944 					}
1945 					nulls[i] = false;
1946 					values[i] = pgDatumFromCString(cstr, pgtype, pgtypmod, pginputfunc);
1947 					break;
1948 
1949 				}
1950 				case OFTIntegerList:
1951 				case OFTRealList:
1952 				case OFTStringList:
1953 				{
1954 					/* TODO, map these OGR array types into PgSQL arrays (fun!) */
1955 					elog(ERROR, "unsupported OGR array type \"%s\"", OGR_GetFieldTypeName(ogrfldtype));
1956 					break;
1957 				}
1958 				default:
1959 				{
1960 					elog(ERROR, "unsupported OGR type \"%s\"", OGR_GetFieldTypeName(ogrfldtype));
1961 					break;
1962 				}
1963 
1964 				}
1965 			}
1966 			else
1967 			{
1968 				ogrNullSlot(values, nulls, i);
1969 			}
1970 		}
1971 		/* Fill in unmatched columns with NULL */
1972 		else if (ogrvariant == OGR_UNMATCHED)
1973 		{
1974 			ogrNullSlot(values, nulls, i);
1975 		}
1976 		else
1977 		{
1978 			elog(ERROR, "OGR FDW unsupported column variant in \"%s\", %d", pgname, ogrvariant);
1979 			return OGRERR_FAILURE;
1980 		}
1981 
1982 	}
1983 
1984 	/* done! */
1985 	return OGRERR_NONE;
1986 }
1987 
1988 static void
ogrStaticText(char * text,const char * str)1989 ogrStaticText(char* text, const char* str)
1990 {
1991 	size_t len = strlen(str);
1992 	memcpy(VARDATA(text), str, len);
1993 	SET_VARSIZE(text, len + VARHDRSZ);
1994 	return;
1995 }
1996 
1997 /*
1998  * EWKB includes a flag that indicates an SRID embedded in the
1999  * binary. The EWKB has an endian byte, four bytes of type information
2000  * and then 4 bytes of optional SRID information. If that info is
2001  * there, we want to over-write it, and remove the SRID flag, to
2002  * generate more "standard" WKB for OGR to consume.
2003  */
2004 static size_t
ogrEwkbStripSrid(unsigned char * wkb,size_t wkbsize)2005 ogrEwkbStripSrid(unsigned char* wkb, size_t wkbsize)
2006 {
2007 	unsigned int type = 0;
2008 	int has_srid = 0;
2009 	size_t newwkbsize = wkbsize;
2010 	memcpy(&type, wkb + 1, 4);
2011 	/* has_z = type & 0x80000000; */
2012 	/* has_m = type & 0x40000000; */
2013 	has_srid = type & 0x20000000;
2014 
2015 	/* Flatten SRID flag away */
2016 	type &= 0xDFFFFFFF;
2017 	memcpy(wkb + 1, &type, 4);
2018 
2019 	/* If there was an SRID number embedded, overwrite it */
2020 	if (has_srid)
2021 	{
2022 		newwkbsize -= 4; /* no space for SRID number needed */
2023 		memmove(wkb + 5, wkb + 9, newwkbsize - 5);
2024 	}
2025 
2026 	return newwkbsize;
2027 }
2028 
2029 OGRErr
pgDatumToOgrGeometry(Datum pg_geometry,Oid pgsendfunc,OGRGeometryH * ogr_geometry)2030 pgDatumToOgrGeometry (Datum pg_geometry, Oid pgsendfunc, OGRGeometryH* ogr_geometry)
2031 {
2032 	OGRErr err;
2033 	bytea* wkb_bytea = DatumGetByteaP(OidFunctionCall1(pgsendfunc, pg_geometry));
2034 	unsigned char* wkb = (unsigned char*)VARDATA_ANY(wkb_bytea);
2035 	size_t wkbsize = VARSIZE_ANY_EXHDR(wkb_bytea);
2036 	wkbsize = ogrEwkbStripSrid(wkb, wkbsize);
2037 	err = OGR_G_CreateFromWkb(wkb, NULL, ogr_geometry, wkbsize);
2038 	if (wkb_bytea)
2039 		pfree(wkb_bytea);
2040 	return err;
2041 }
2042 
2043 static OGRErr
ogrSlotToFeature(const TupleTableSlot * slot,OGRFeatureH feat,const OgrFdwTable * tbl)2044 ogrSlotToFeature(const TupleTableSlot* slot, OGRFeatureH feat, const OgrFdwTable* tbl)
2045 {
2046 	int i;
2047 	Datum* values = slot->tts_values;
2048 	bool* nulls = slot->tts_isnull;
2049 	TupleDesc tupdesc = slot->tts_tupleDescriptor;
2050 
2051 	int year, month, day, hour, minute, second;
2052 
2053 	/* Prepare date-time part tokens for use later */
2054 	char txtyear[STR_MAX_LEN];
2055 	char txtmonth[STR_MAX_LEN];
2056 	char txtday[STR_MAX_LEN];
2057 	char txthour[STR_MAX_LEN];
2058 	char txtminute[STR_MAX_LEN];
2059 	char txtsecond[STR_MAX_LEN];
2060 
2061 	ogrStaticText(txtyear, "year");
2062 	ogrStaticText(txtmonth, "month");
2063 	ogrStaticText(txtday, "day");
2064 	ogrStaticText(txthour, "hour");
2065 	ogrStaticText(txtminute, "minute");
2066 	ogrStaticText(txtsecond, "second");
2067 
2068 	/* Check our assumption that slot and setup data match */
2069 	if (tbl->ncols != tupdesc->natts)
2070 	{
2071 		elog(ERROR, "FDW metadata table and slot table have mismatching number of columns");
2072 		return OGRERR_FAILURE;
2073 	}
2074 
2075 	/* For each pgtable column, set a value on the feature OGR */
2076 	for (i = 0; i < tbl->ncols; i++)
2077 	{
2078 		OgrFdwColumn col = tbl->cols[i];
2079 		const char* pgname = col.pgname;
2080 		Oid pgtype = col.pgtype;
2081 		Oid pgoutputfunc = col.pgoutputfunc;
2082 		int ogrfldnum = col.ogrfldnum;
2083 		OGRFieldType ogrfldtype = col.ogrfldtype;
2084 		OgrColumnVariant ogrvariant = col.ogrvariant;
2085 
2086 		/* Skip dropped attributes */
2087 		if (col.pgattisdropped)
2088 		{
2089 			continue;
2090 		}
2091 
2092 		/* Skip the FID, we have to treat it as immutable anyways */
2093 		if (ogrvariant == OGR_FID)
2094 		{
2095 			if (nulls[i])
2096 			{
2097 				OGR_F_SetFID(feat, OGRNullFID);
2098 			}
2099 			else
2100 			{
2101 				if (pgtype == INT4OID)
2102 				{
2103 					int32 val = DatumGetInt32(values[i]);
2104 					OGR_F_SetFID(feat, val);
2105 				}
2106 				else if (pgtype == INT8OID)
2107 				{
2108 					int64 val = DatumGetInt64(values[i]);
2109 					OGR_F_SetFID(feat, val);
2110 				}
2111 				else
2112 				{
2113 					elog(ERROR, "unable to handle non-integer fid");
2114 				}
2115 			}
2116 			continue;
2117 		}
2118 
2119 		/* TODO: For updates, we should only set the fields that are */
2120 		/*       in the target list, and flag the others as unchanged */
2121 		if (ogrvariant == OGR_GEOMETRY)
2122 		{
2123 			OGRErr err;
2124 			if (nulls[i])
2125 			{
2126 #if (GDAL_VERSION_NUM >= GDAL_COMPUTE_VERSION(1,11,0))
2127 				err = OGR_F_SetGeomFieldDirectly(feat, ogrfldnum, NULL);
2128 #else
2129 				err = OGR_F_SetGeometryDirectly(feat, NULL);
2130 #endif
2131 				continue;
2132 			}
2133 			else
2134 			{
2135 				OGRGeometryH geom;
2136 				err = pgDatumToOgrGeometry (values[i], col.pgsendfunc, &geom);
2137 				if (err != OGRERR_NONE)
2138 					return err;
2139 
2140 #if (GDAL_VERSION_NUM >= GDAL_COMPUTE_VERSION(1,11,0))
2141 				err = OGR_F_SetGeomFieldDirectly(feat, ogrfldnum, geom);
2142 #else
2143 				err = OGR_F_SetGeometryDirectly(feat, geom);
2144 #endif
2145 			}
2146 		}
2147 		else if (ogrvariant == OGR_FIELD)
2148 		{
2149 			/* Ensure that the OGR data type fits the destination Pg column */
2150 			pgCanConvertToOgr(pgtype, ogrfldtype, pgname, tbl->tblname);
2151 
2152 			/* Skip NULL case */
2153 			if (nulls[i])
2154 			{
2155 				OGR_F_UnsetField(feat, ogrfldnum);
2156 				continue;
2157 			}
2158 
2159 			switch (pgtype)
2160 			{
2161 			case BOOLOID:
2162 			{
2163 				int8 val = DatumGetBool(values[i]);
2164 				OGR_F_SetFieldInteger(feat, ogrfldnum, val);
2165 				break;
2166 			}
2167 			case INT2OID:
2168 			{
2169 				int16 val = DatumGetInt16(values[i]);
2170 				OGR_F_SetFieldInteger(feat, ogrfldnum, val);
2171 				break;
2172 			}
2173 			case INT4OID:
2174 			{
2175 				int32 val = DatumGetInt32(values[i]);
2176 				OGR_F_SetFieldInteger(feat, ogrfldnum, val);
2177 				break;
2178 			}
2179 			case INT8OID:
2180 			{
2181 				int64 val = DatumGetInt64(values[i]);
2182 #if GDAL_VERSION_MAJOR >= 2
2183 				OGR_F_SetFieldInteger64(feat, ogrfldnum, val);
2184 #else
2185 				if (val < INT_MAX)
2186 				{
2187 					OGR_F_SetFieldInteger(feat, ogrfldnum, (int32)val);
2188 				}
2189 				else
2190 				{
2191 					elog(ERROR, "unable to coerce int64 into int32 OGR field");
2192 				}
2193 #endif
2194 				break;
2195 
2196 			}
2197 
2198 			case NUMERICOID:
2199 			{
2200 				Datum d;
2201 				float8 f;
2202 
2203 				/* Convert to string */
2204 				d = OidFunctionCall1(pgoutputfunc, values[i]);
2205 				/* Convert back to float8 */
2206 				f = DatumGetFloat8(DirectFunctionCall1(float8in, d));
2207 
2208 				OGR_F_SetFieldDouble(feat, ogrfldnum, f);
2209 				break;
2210 			}
2211 			case FLOAT4OID:
2212 			{
2213 				OGR_F_SetFieldDouble(feat, ogrfldnum, DatumGetFloat4(values[i]));
2214 				break;
2215 			}
2216 			case FLOAT8OID:
2217 			{
2218 				OGR_F_SetFieldDouble(feat, ogrfldnum, DatumGetFloat8(values[i]));
2219 				break;
2220 			}
2221 
2222 			case TEXTOID:
2223 			case VARCHAROID:
2224 			case NAMEOID:
2225 			case BPCHAROID: /* char(n) */
2226 			{
2227 				bytea* varlena = (bytea*)DatumGetPointer(values[i]);
2228 				size_t varsize = VARSIZE_ANY_EXHDR(varlena);
2229 				char* str = palloc0(varsize + 1);
2230 				memcpy(str, VARDATA_ANY(varlena), varsize);
2231 				OGR_F_SetFieldString(feat, ogrfldnum, str);
2232 				pfree(str);
2233 				break;
2234 			}
2235 
2236 			case CHAROID: /* char */
2237 			{
2238 				char str[2];
2239 				str[0] = DatumGetChar(values[i]);
2240 				str[1] = '\0';
2241 				OGR_F_SetFieldString(feat, ogrfldnum, str);
2242 				break;
2243 			}
2244 
2245 			case BYTEAOID:
2246 			{
2247 				bytea* varlena = PG_DETOAST_DATUM(values[i]);
2248 				size_t varsize = VARSIZE_ANY_EXHDR(varlena);
2249 				OGR_F_SetFieldBinary(feat, ogrfldnum, varsize, (GByte*)VARDATA_ANY(varlena));
2250 				break;
2251 			}
2252 
2253 			case DATEOID:
2254 			{
2255 				/* Convert date to timestamp */
2256 				Datum d = DirectFunctionCall1(date_timestamp, values[i]);
2257 
2258 				/* Read out the parts */
2259 				year = lround(DatumGetFloat8(DirectFunctionCall2(timestamp_part, PointerGetDatum(txtyear), d)));
2260 				month = lround(DatumGetFloat8(DirectFunctionCall2(timestamp_part, PointerGetDatum(txtmonth), d)));
2261 				day = lround(DatumGetFloat8(DirectFunctionCall2(timestamp_part, PointerGetDatum(txtday), d)));
2262 				OGR_F_SetFieldDateTime(feat, ogrfldnum, year, month, day, 0, 0, 0, 0);
2263 				break;
2264 			}
2265 
2266 			/* TODO: handle time zones explicitly */
2267 			case TIMEOID:
2268 			case TIMETZOID:
2269 			{
2270 				/* Read the parts of the time */
2271 				hour = lround(DatumGetFloat8(DirectFunctionCall2(time_part, PointerGetDatum(txthour), values[i])));
2272 				minute = lround(DatumGetFloat8(DirectFunctionCall2(time_part, PointerGetDatum(txtminute), values[i])));
2273 				second = lround(DatumGetFloat8(DirectFunctionCall2(time_part, PointerGetDatum(txtsecond), values[i])));
2274 				OGR_F_SetFieldDateTime(feat, ogrfldnum, 0, 0, 0, hour, minute, second, 0);
2275 				break;
2276 			}
2277 
2278 
2279 			case TIMESTAMPOID:
2280 			case TIMESTAMPTZOID:
2281 			{
2282 				Datum d = values[i];
2283 				year = lround(DatumGetFloat8(DirectFunctionCall2(timestamp_part, PointerGetDatum(txtyear), d)));
2284 				month = lround(DatumGetFloat8(DirectFunctionCall2(timestamp_part, PointerGetDatum(txtmonth), d)));
2285 				day = lround(DatumGetFloat8(DirectFunctionCall2(timestamp_part, PointerGetDatum(txtday), d)));
2286 				hour = lround(DatumGetFloat8(DirectFunctionCall2(timestamp_part, PointerGetDatum(txthour), d)));
2287 				minute = lround(DatumGetFloat8(DirectFunctionCall2(timestamp_part, PointerGetDatum(txtminute), d)));
2288 				second = lround(DatumGetFloat8(DirectFunctionCall2(timestamp_part, PointerGetDatum(txtsecond), d)));
2289 				OGR_F_SetFieldDateTime(feat, ogrfldnum, year, month, day, hour, minute, second, 0);
2290 				break;
2291 			}
2292 
2293 			/* TODO: array types for string, integer, float */
2294 			default:
2295 			{
2296 				elog(ERROR, "OGR FDW unsupported PgSQL column type in \"%s\", %d", pgname, pgtype);
2297 				return OGRERR_FAILURE;
2298 			}
2299 			}
2300 		}
2301 		/* Fill in unmatched columns with NULL */
2302 		else if (ogrvariant == OGR_UNMATCHED)
2303 		{
2304 			OGR_F_UnsetField(feat, ogrfldnum);
2305 		}
2306 		else
2307 		{
2308 			elog(ERROR, "OGR FDW unsupported column variant in \"%s\", %d", pgname, ogrvariant);
2309 			return OGRERR_FAILURE;
2310 		}
2311 
2312 	}
2313 
2314 	/* done! */
2315 	return OGRERR_NONE;
2316 }
2317 
2318 /*
2319  * ogrIterateForeignScan
2320  *		Read next record from OGR and store it into the
2321  *		ScanTupleSlot as a virtual tuple
2322  */
2323 static TupleTableSlot*
ogrIterateForeignScan(ForeignScanState * node)2324 ogrIterateForeignScan(ForeignScanState* node)
2325 {
2326 	OgrFdwExecState* execstate = (OgrFdwExecState*) node->fdw_state;
2327 	TupleTableSlot* slot = node->ss.ss_ScanTupleSlot;
2328 	OGRFeatureH feat;
2329 
2330 	/*
2331 	 * Clear the slot. If it gets through w/o being filled up, that means
2332 	 * we're all done.
2333 	 */
2334 	ExecClearTuple(slot);
2335 
2336 	/*
2337 	 * First time through, reset reading. Then keep reading until
2338 	 * we run out of records, then return a cleared (NULL) slot, to
2339 	 * notify the core we're done.
2340 	 */
2341 	if (execstate->rownum == 0)
2342 	{
2343 		OGR_L_ResetReading(execstate->ogr.lyr);
2344 	}
2345 
2346 	/* If we rectreive a feature from OGR, copy it over into the slot */
2347 	feat = OGR_L_GetNextFeature(execstate->ogr.lyr);
2348 	if (feat)
2349 	{
2350 		/* convert result to arrays of values and null indicators */
2351 		if (OGRERR_NONE != ogrFeatureToSlot(feat, slot, execstate))
2352 		{
2353 			ogrEreportError("failure reading OGR data source");
2354 		}
2355 
2356 		/* store the virtual tuple */
2357 		ExecStoreVirtualTuple(slot);
2358 
2359 		/* increment row count */
2360 		execstate->rownum++;
2361 
2362 		/* Release OGR feature object */
2363 		OGR_F_Destroy(feat);
2364 	}
2365 
2366 	return slot;
2367 }
2368 
2369 /*
2370  * ogrReScanForeignScan
2371  *		Rescan table, possibly with new parameters
2372  */
2373 static void
ogrReScanForeignScan(ForeignScanState * node)2374 ogrReScanForeignScan(ForeignScanState* node)
2375 {
2376 	OgrFdwExecState* execstate = (OgrFdwExecState*) node->fdw_state;
2377 
2378 	OGR_L_ResetReading(execstate->ogr.lyr);
2379 	execstate->rownum = 0;
2380 
2381 	return;
2382 }
2383 
2384 /*
2385  * ogrEndForeignScan
2386  *		Finish scanning foreign table and dispose objects used for this scan
2387  */
2388 static void
ogrEndForeignScan(ForeignScanState * node)2389 ogrEndForeignScan(ForeignScanState* node)
2390 {
2391 	OgrFdwExecState* execstate = (OgrFdwExecState*) node->fdw_state;
2392 	if (execstate)
2393 	{
2394 		elog(DEBUG2, "OGR FDW processed %d rows from OGR", execstate->rownum);
2395 		ogrFinishConnection(&(execstate->ogr));
2396 	}
2397 
2398 	return;
2399 }
2400 
2401 /* ======================================================== */
2402 /* WRITE SUPPORT */
2403 /* ======================================================== */
2404 
2405 /* if the scanning functions above respected the targetlist,
2406 we would only be getting back the SET target=foo columns in the slots below,
2407 so we would need to add the "fid" to all targetlists (and also disallow fid changing
2408 perhaps).
2409 
2410 since we always pull complete tables in the scan functions, the
2411 slots below are basically full tables, in fact they include (?) one entry
2412 for each OGR column, even when the table does not include the column,
2413 just nulling out the entries that are not in the table definition
2414 
2415 it might be better to update the scan code to properly manage target lists
2416 first, and then come back here and do things properly
2417 
2418 we will need a ogrSlotToFeature to feed into the OGR_L_SetFeature and
2419 OGR_L_CreateFeature functions. Also will use OGR_L_DeleteFeature and
2420 fid value
2421 
2422 in ogrGetForeignPlan we get a tlist that includes just the attributes we
2423 are interested in, can use that to pare down the request perhaps
2424 */
2425 
2426 static int
ogrGetFidColumn(const TupleDesc td)2427 ogrGetFidColumn(const TupleDesc td)
2428 {
2429 	int i;
2430 	for (i = 0; i < td->natts; i++)
2431 	{
2432 #if PG_VERSION_NUM >= 110000
2433 		NameData attname = td->attrs[i].attname;
2434 		Oid atttypeid = td->attrs[i].atttypid;
2435 #else
2436 		NameData attname = td->attrs[i]->attname;
2437 		Oid atttypeid = td->attrs[i]->atttypid;
2438 #endif
2439 		if ((atttypeid == INT4OID || atttypeid == INT8OID) &&
2440 		        strcaseeq("fid", attname.data))
2441 		{
2442 			return i;
2443 		}
2444 	}
2445 	return -1;
2446 }
2447 
2448 /*
2449  * ogrAddForeignUpdateTargets
2450  *
2451  * For now we no-op this callback, as we are making the presence of
2452  * "fid" in the FDW table definition a requirement for any update.
2453  * It might be possible to add nonexisting "junk" columns? In which case
2454  * there could always be a virtual fid travelling with the queries,
2455  * and the FDW table itself wouldn't need such a column?
2456  */
2457 static void
ogrAddForeignUpdateTargets(Query * parsetree,RangeTblEntry * target_rte,Relation target_relation)2458 ogrAddForeignUpdateTargets(Query* parsetree,
2459                            RangeTblEntry* target_rte,
2460                            Relation target_relation)
2461 {
2462 	ListCell* cell;
2463 	Form_pg_attribute att;
2464 	Var* var;
2465 	TargetEntry* tle;
2466 	TupleDesc tupdesc = target_relation->rd_att;
2467 	int fid_column = ogrGetFidColumn(tupdesc);
2468 
2469 	elog(DEBUG2, "ogrAddForeignUpdateTargets");
2470 
2471 	if (fid_column < 0)
2472 	{
2473 		elog(ERROR, "table '%s' does not have a 'fid' column", RelationGetRelationName(target_relation));
2474 	}
2475 
2476 #if PG_VERSION_NUM >= 110000
2477 	att = &tupdesc->attrs[fid_column];
2478 #else
2479 	att = tupdesc->attrs[fid_column];
2480 #endif
2481 	/* Make a Var representing the desired value */
2482 	var = makeVar(parsetree->resultRelation,
2483 	              att->attnum,
2484 	              att->atttypid,
2485 	              att->atttypmod,
2486 	              att->attcollation,
2487 	              0);
2488 
2489 	/* Wrap it in a resjunk TLE with the right name ... */
2490 	tle = makeTargetEntry((Expr*)var,
2491 	                      list_length(parsetree->targetList) + 1,
2492 	                      pstrdup(NameStr(att->attname)),
2493 	                      true);
2494 
2495 	parsetree->targetList = lappend(parsetree->targetList, tle);
2496 
2497 	foreach (cell, parsetree->targetList)
2498 	{
2499 		TargetEntry* target = (TargetEntry*) lfirst(cell);
2500 		elog(DEBUG4, "parsetree->targetList %s:%d", target->resname, target->resno);
2501 	}
2502 
2503 	return;
2504 
2505 }
2506 
2507 /*
2508  * ogrBeginForeignModify
2509  * For now the only thing we'll do here is set up the connection
2510  * and pass that on to the next functions.
2511  */
2512 static void
ogrBeginForeignModify(ModifyTableState * mtstate,ResultRelInfo * rinfo,List * fdw_private,int subplan_index,int eflags)2513 ogrBeginForeignModify(ModifyTableState* mtstate,
2514                       ResultRelInfo* rinfo,
2515                       List* fdw_private,
2516                       int subplan_index,
2517                       int eflags)
2518 {
2519 	Oid foreigntableid;
2520 	OgrFdwState* state;
2521 
2522 	elog(DEBUG2, "ogrBeginForeignModify");
2523 
2524 	foreigntableid = RelationGetRelid(rinfo->ri_RelationDesc);
2525 	state = getOgrFdwState(foreigntableid, OGR_MODIFY_STATE);
2526 
2527 	/* Read the OGR layer definition and PgSQL foreign table definitions */
2528 	ogrReadColumnData(state);
2529 
2530 	/* Save OGR connection, etc, for later */
2531 	rinfo->ri_FdwState = state;
2532 	return;
2533 }
2534 
2535 /*
2536  * ogrExecForeignUpdate
2537  * Find out what the fid is, get the OGR feature for that FID,
2538  * and then update the values on that feature.
2539  */
2540 static TupleTableSlot*
ogrExecForeignUpdate(EState * estate,ResultRelInfo * rinfo,TupleTableSlot * slot,TupleTableSlot * planSlot)2541 ogrExecForeignUpdate(EState* estate,
2542                      ResultRelInfo* rinfo,
2543                      TupleTableSlot* slot,
2544                      TupleTableSlot* planSlot)
2545 {
2546 	OgrFdwModifyState* modstate = rinfo->ri_FdwState;
2547 	TupleDesc td = slot->tts_tupleDescriptor;
2548 	Relation rel = rinfo->ri_RelationDesc;
2549 	Oid foreigntableid = RelationGetRelid(rel);
2550 	int fid_column;
2551 	Oid fid_type;
2552 	Datum fid_datum;
2553 	int64 fid;
2554 	OGRFeatureH feat;
2555 	OGRErr err;
2556 
2557 	/* Is there a fid column? */
2558 	fid_column = ogrGetFidColumn(td);
2559 	if (fid_column < 0)
2560 	{
2561 		elog(ERROR, "cannot find 'fid' column in table '%s'", get_rel_name(foreigntableid));
2562 	}
2563 
2564 #if PG_VERSION_NUM >= 120000
2565 	slot_getallattrs(slot);
2566 #endif
2567 
2568 	/* What is the value of the FID for this record? */
2569 	fid_datum = slot->tts_values[fid_column];
2570 #if PG_VERSION_NUM >= 110000
2571 	fid_type = td->attrs[fid_column].atttypid;
2572 #else
2573 	fid_type = td->attrs[fid_column]->atttypid;
2574 #endif
2575 	if (fid_type == INT8OID)
2576 	{
2577 		fid = DatumGetInt64(fid_datum);
2578 	}
2579 	else
2580 	{
2581 		fid = DatumGetInt32(fid_datum);
2582 	}
2583 
2584 	elog(DEBUG2, "ogrExecForeignUpdate fid=" OGR_FDW_FRMT_INT64, OGR_FDW_CAST_INT64(fid));
2585 
2586 	/* Get the OGR feature for this fid */
2587 	feat = OGR_L_GetFeature(modstate->ogr.lyr, fid);
2588 
2589 	/* If we found a feature, then copy data from the slot onto the feature */
2590 	/* and then back into the layer */
2591 	if (! feat)
2592 	{
2593 		ogrEreportError("failure reading OGR feature");
2594 	}
2595 
2596 	err = ogrSlotToFeature(slot, feat, modstate->table);
2597 	if (err != OGRERR_NONE)
2598 	{
2599 		ogrEreportError("failure populating OGR feature");
2600 	}
2601 
2602 	err = OGR_L_SetFeature(modstate->ogr.lyr, feat);
2603 	if (err != OGRERR_NONE)
2604 	{
2605 		ogrEreportError("failure writing back OGR feature");
2606 	}
2607 
2608 	OGR_F_Destroy(feat);
2609 
2610 	/* TODO: slot handling? what happens with RETURNING clauses? */
2611 
2612 	return slot;
2613 }
2614 
2615 // typedef struct TupleTableSlot
2616 // {
2617 //     NodeTag     type;
2618 //     bool        tts_isempty;    /* true = slot is empty */
2619 //     bool        tts_shouldFree; /* should pfree tts_tuple? */
2620 //     bool        tts_shouldFreeMin;      /* should pfree tts_mintuple? */
2621 //     bool        tts_slow;       /* saved state for slot_deform_tuple */
2622 //     HeapTuple   tts_tuple;      /* physical tuple, or NULL if virtual */
2623 //     TupleDesc   tts_tupleDescriptor;    /* slot's tuple descriptor */
2624 //     MemoryContext tts_mcxt;     /* slot itself is in this context */
2625 //     Buffer      tts_buffer;     /* tuple's buffer, or InvalidBuffer */
2626 //     int         tts_nvalid;     /* # of valid values in tts_values */
2627 //     Datum      *tts_values;     /* current per-attribute values */
2628 //     bool       *tts_isnull;     /* current per-attribute isnull flags */
2629 //     MinimalTuple tts_mintuple;  /* minimal tuple, or NULL if none */
2630 //     HeapTupleData tts_minhdr;   /* workspace for minimal-tuple-only case */
2631 //     long        tts_off;        /* saved state for slot_deform_tuple */
2632 // } TupleTableSlot;
2633 
2634 // typedef struct tupleDesc
2635 // {
2636 //     int         natts;          /* number of attributes in the tuple */
2637 //     Form_pg_attribute *attrs;
2638 //     /* attrs[N] is a pointer to the description of Attribute Number N+1 */
2639 //     TupleConstr *constr;        /* constraints, or NULL if none */
2640 //     Oid         tdtypeid;       /* composite type ID for tuple type */
2641 //     int32       tdtypmod;       /* typmod for tuple type */
2642 //     bool        tdhasoid;       /* tuple has oid attribute in its header */
2643 //     int         tdrefcount;     /* reference count, or -1 if not counting */
2644 // }   *TupleDesc;
2645 //
2646 
2647 // typedef struct ResultRelInfo
2648 // {
2649 //     NodeTag     type;
2650 //     Index       ri_RangeTableIndex;
2651 //     Relation    ri_RelationDesc;
2652 //     int         ri_NumIndices;
2653 //     RelationPtr ri_IndexRelationDescs;
2654 //     IndexInfo **ri_IndexRelationInfo;
2655 //     TriggerDesc *ri_TrigDesc;
2656 //     FmgrInfo   *ri_TrigFunctions;
2657 //     List      **ri_TrigWhenExprs;
2658 //     Instrumentation *ri_TrigInstrument;
2659 //     struct FdwRoutine *ri_FdwRoutine;
2660 //     void       *ri_FdwState;
2661 //     List       *ri_WithCheckOptions;
2662 //     List       *ri_WithCheckOptionExprs;
2663 //     List      **ri_ConstraintExprs;
2664 //     JunkFilter *ri_junkFilter;
2665 //     ProjectionInfo *ri_projectReturning;
2666 //     ProjectionInfo *ri_onConflictSetProj;
2667 //     List       *ri_onConflictSetWhere;
2668 // } ResultRelInfo;
2669 
2670 // typedef struct TargetEntry
2671 // 	{
2672 // 	    Expr        xpr;
2673 // 	    Expr       *expr;           /* expression to evaluate */
2674 // 	    AttrNumber  resno;          /* attribute number (see notes above) */
2675 // 	    char       *resname;        /* name of the column (could be NULL) */
2676 // 	    Index       ressortgroupref;/* nonzero if referenced by a sort/group
2677 // 	                                 * clause */
2678 // 	    Oid         resorigtbl;     /* OID of column's source table */
2679 // 	    AttrNumber  resorigcol;     /* column's number in source table */
2680 // 	    bool        resjunk;        /* set to true to eliminate the attribute from
2681 // 	                                 * final target list */
2682 // 	} TargetEntry;
2683 
2684 // TargetEntry *
2685 // makeTargetEntry(Expr *expr,
2686 //                 AttrNumber resno,
2687 //                 char *resname,
2688 //                 bool resjunk)
2689 
2690 // Var *
2691 // makeVar(Index varno,
2692 //         AttrNumber varattno,
2693 //         Oid vartype,
2694 //         int32 vartypmod,
2695 //         Oid varcollid,
2696 //         Index varlevelsup)
2697 
2698 // typedef struct Var
2699 // {
2700 //     Expr        xpr;
2701 //     Index       varno;          /* index of this var's relation in the range
2702 //                                  * table, or INNER_VAR/OUTER_VAR/INDEX_VAR */
2703 //     AttrNumber  varattno;       /* attribute number of this var, or zero for
2704 //                                  * all */
2705 //     Oid         vartype;        /* pg_type OID for the type of this var */
2706 //     int32       vartypmod;      /* pg_attribute typmod value */
2707 //     Oid         varcollid;      /* OID of collation, or InvalidOid if none */
2708 //     Index       varlevelsup;    /* for subquery variables referencing outer
2709 //                                  * relations; 0 in a normal var, >0 means N
2710 //                                  * levels up */
2711 //     Index       varnoold;       /* original value of varno, for debugging */
2712 //     AttrNumber  varoattno;      /* original value of varattno */
2713 //     int         location;       /* token location, or -1 if unknown */
2714 // } Var;
2715 
2716 static TupleTableSlot*
ogrExecForeignInsert(EState * estate,ResultRelInfo * rinfo,TupleTableSlot * slot,TupleTableSlot * planSlot)2717 ogrExecForeignInsert(EState* estate,
2718                      ResultRelInfo* rinfo,
2719                      TupleTableSlot* slot,
2720                      TupleTableSlot* planSlot)
2721 {
2722 	OgrFdwModifyState* modstate = rinfo->ri_FdwState;
2723 	OGRFeatureDefnH ogr_fd = OGR_L_GetLayerDefn(modstate->ogr.lyr);
2724 	OGRFeatureH feat = OGR_F_Create(ogr_fd);
2725 	int fid_column;
2726 	OGRErr err;
2727 	GIntBig fid;
2728 
2729 	elog(DEBUG2, "ogrExecForeignInsert");
2730 
2731 #if PG_VERSION_NUM >= 120000
2732 	/*
2733 	* PgSQL 12 passes an unpopulated slot to us, and for now
2734 	* we force it to populate itself and then read directly
2735 	* from it. For future, using the slot_getattr() infra
2736 	* would be cleaner, but version dependent.
2737 	*/
2738 	slot_getallattrs(slot);
2739 #endif
2740 
2741 	/* Copy the data from the slot onto the feature */
2742 	if (!feat)
2743 	{
2744 		ogrEreportError("failure creating OGR feature");
2745 	}
2746 
2747 	err = ogrSlotToFeature(slot, feat, modstate->table);
2748 	if (err != OGRERR_NONE)
2749 	{
2750 		ogrEreportError("failure populating OGR feature");
2751 	}
2752 
2753 	err = OGR_L_CreateFeature(modstate->ogr.lyr, feat);
2754 	if (err != OGRERR_NONE)
2755 	{
2756 		ogrEreportError("failure writing OGR feature");
2757 	}
2758 
2759 	fid = OGR_F_GetFID(feat);
2760 	OGR_F_Destroy(feat);
2761 
2762 	/* Update the FID for RETURNING slot */
2763 	fid_column = ogrGetFidColumn(slot->tts_tupleDescriptor);
2764 	if (fid_column >= 0)
2765 	{
2766 		slot->tts_values[fid_column] = Int64GetDatum(fid);
2767 		slot->tts_isnull[fid_column] = false;
2768 		slot->tts_nvalid++;
2769 	}
2770 
2771 	return slot;
2772 }
2773 
2774 
2775 
2776 static TupleTableSlot*
ogrExecForeignDelete(EState * estate,ResultRelInfo * rinfo,TupleTableSlot * slot,TupleTableSlot * planSlot)2777 ogrExecForeignDelete(EState* estate,
2778                      ResultRelInfo* rinfo,
2779                      TupleTableSlot* slot,
2780                      TupleTableSlot* planSlot)
2781 {
2782 	OgrFdwModifyState* modstate = rinfo->ri_FdwState;
2783 	TupleDesc td = planSlot->tts_tupleDescriptor;
2784 	Relation rel = rinfo->ri_RelationDesc;
2785 	Oid foreigntableid = RelationGetRelid(rel);
2786 	int fid_column;
2787 	Oid fid_type;
2788 	Datum fid_datum;
2789 	int64 fid;
2790 	OGRErr err;
2791 
2792 	/* Is there a fid column? */
2793 	fid_column = ogrGetFidColumn(td);
2794 	if (fid_column < 0)
2795 	{
2796 		elog(ERROR, "cannot find 'fid' column in table '%s'", get_rel_name(foreigntableid));
2797 	}
2798 
2799 #if PG_VERSION_NUM >= 120000
2800 	slot_getallattrs(planSlot);
2801 #endif
2802 
2803 	/* What is the value of the FID for this record? */
2804 	fid_datum = planSlot->tts_values[fid_column];
2805 #if PG_VERSION_NUM >= 110000
2806 	fid_type = td->attrs[fid_column].atttypid;
2807 #else
2808 	fid_type = td->attrs[fid_column]->atttypid;
2809 #endif
2810 
2811 	if (fid_type == INT8OID)
2812 	{
2813 		fid = DatumGetInt64(fid_datum);
2814 	}
2815 	else
2816 	{
2817 		fid = DatumGetInt32(fid_datum);
2818 	}
2819 
2820 	elog(DEBUG2, "ogrExecForeignDelete fid=" OGR_FDW_FRMT_INT64, OGR_FDW_CAST_INT64(fid));
2821 
2822 	/* Delete the OGR feature for this fid */
2823 	err = OGR_L_DeleteFeature(modstate->ogr.lyr, fid);
2824 
2825 	if (err != OGRERR_NONE)
2826 	{
2827 		return NULL;
2828 	}
2829 	else
2830 	{
2831 		return slot;
2832 	}
2833 }
2834 
2835 static void
ogrEndForeignModify(EState * estate,ResultRelInfo * rinfo)2836 ogrEndForeignModify(EState* estate, ResultRelInfo* rinfo)
2837 {
2838 	OgrFdwModifyState* modstate = rinfo->ri_FdwState;
2839 
2840 	elog(DEBUG2, "ogrEndForeignModify");
2841 
2842 	ogrFinishConnection(&(modstate->ogr));
2843 
2844 	return;
2845 }
2846 
2847 static int
ogrIsForeignRelUpdatable(Relation rel)2848 ogrIsForeignRelUpdatable(Relation rel)
2849 {
2850 	const int readonly = 0;
2851 	int foreign_rel_updateable = 0;
2852 	TupleDesc td = RelationGetDescr(rel);
2853 	OgrConnection ogr;
2854 	Oid foreigntableid = RelationGetRelid(rel);
2855 
2856 	elog(DEBUG2, "ogrIsForeignRelUpdatable");
2857 
2858 	/* Before we say "yes"... */
2859 	/*  Does the foreign relation have a "fid" column? */
2860 	/* Is that column an integer? */
2861 	if (ogrGetFidColumn(td) < 0)
2862 	{
2863 		elog(NOTICE, "no \"fid\" column in foreign table '%s'", get_rel_name(foreigntableid));
2864 		return readonly;
2865 	}
2866 
2867 	/*   Is it backed by a writable OGR driver? */
2868 	/*   Can we open the relation in read/write mode? */
2869 	ogr = ogrGetConnectionFromTable(foreigntableid, OGR_UPDATEABLE_TRY);
2870 
2871 	/* Something in the open process set the readonly flags */
2872 	/* Perhaps user has manually set the foreign table option to readonly */
2873 	if (ogr.ds_updateable == OGR_UPDATEABLE_FALSE ||
2874 	        ogr.lyr_updateable == OGR_UPDATEABLE_FALSE)
2875 	{
2876 		return readonly;
2877 	}
2878 
2879 	/* No data source or layer objects? Readonly */
2880 	if (!(ogr.ds && ogr.lyr))
2881 	{
2882 		return readonly;
2883 	}
2884 
2885 	if (OGR_L_TestCapability(ogr.lyr, OLCRandomWrite))
2886 	{
2887 		foreign_rel_updateable |= (1 << CMD_UPDATE);
2888 	}
2889 
2890 	if (OGR_L_TestCapability(ogr.lyr, OLCSequentialWrite))
2891 	{
2892 		foreign_rel_updateable |= (1 << CMD_INSERT);
2893 	}
2894 
2895 	if (OGR_L_TestCapability(ogr.lyr, OLCDeleteFeature))
2896 	{
2897 		foreign_rel_updateable |= (1 << CMD_DELETE);
2898 	}
2899 
2900 	ogrFinishConnection(&ogr);
2901 
2902 	return foreign_rel_updateable;
2903 }
2904 
2905 #if PG_VERSION_NUM >= 90500
2906 
2907 /*
2908  * PostgreSQL 9.5 or above.  Import a foreign schema
2909  */
2910 static List*
ogrImportForeignSchema(ImportForeignSchemaStmt * stmt,Oid serverOid)2911 ogrImportForeignSchema(ImportForeignSchemaStmt* stmt, Oid serverOid)
2912 {
2913 	List* commands = NIL;
2914 	ForeignServer* server;
2915 	ListCell* lc;
2916 	bool import_all = false;
2917 	bool launder_column_names, launder_table_names;
2918 	OgrConnection ogr;
2919 	int i;
2920 	char layer_name[STR_MAX_LEN];
2921 	char table_name[STR_MAX_LEN];
2922 
2923 	/* Are we importing all layers in the OGR datasource? */
2924 	import_all = streq(stmt->remote_schema, "ogr_all");
2925 
2926 	/* Make connection to server */
2927 	server = GetForeignServer(serverOid);
2928 	ogr = ogrGetConnectionFromServer(serverOid, OGR_UPDATEABLE_FALSE);
2929 
2930 	/* Launder by default */
2931 	launder_column_names = launder_table_names = true;
2932 
2933 	/* Read user-provided statement laundering options */
2934 	foreach (lc, stmt->options)
2935 	{
2936 		DefElem* def = (DefElem*) lfirst(lc);
2937 
2938 		if (streq(def->defname, "launder_column_names"))
2939 		{
2940 			launder_column_names = defGetBoolean(def);
2941 		}
2942 		else if (streq(def->defname, "launder_table_names"))
2943 		{
2944 			launder_table_names = defGetBoolean(def);
2945 		}
2946 		else
2947 			ereport(ERROR,
2948 			        (errcode(ERRCODE_FDW_INVALID_OPTION_NAME),
2949 			         errmsg("invalid option \"%s\"", def->defname)));
2950 	}
2951 
2952 	for (i = 0; i < GDALDatasetGetLayerCount(ogr.ds); i++)
2953 	{
2954 		bool import_layer = false;
2955 		OGRLayerH ogr_lyr = GDALDatasetGetLayer(ogr.ds, i);
2956 
2957 		if (! ogr_lyr)
2958 		{
2959 			elog(DEBUG1, "Skipping OGR layer %d, unable to read layer", i);
2960 			continue;
2961 		}
2962 
2963 		/* Layer name is never laundered, since it's the link back to OGR */
2964 		strncpy(layer_name, OGR_L_GetName(ogr_lyr), STR_MAX_LEN);
2965 
2966 		/*
2967 		* We need to compare against created table names
2968 		* because PgSQL does an extra check on CREATE FOREIGN TABLE
2969 		*/
2970 		strncpy(table_name, layer_name, STR_MAX_LEN);
2971 		if (launder_table_names)
2972 		{
2973 			ogrStringLaunder(table_name);
2974 		}
2975 
2976 		/*
2977 		* Only include if we are importing "ogr_all" or
2978 		* the layer prefix starts with the remote schema
2979 		*/
2980 		import_layer = import_all ||
2981 		               (strncmp(layer_name, stmt->remote_schema, strlen(stmt->remote_schema)) == 0);
2982 
2983 		/* Apply restrictions for LIMIT TO and EXCEPT */
2984 		if (import_layer && (
2985 		            stmt->list_type == FDW_IMPORT_SCHEMA_LIMIT_TO ||
2986 		            stmt->list_type == FDW_IMPORT_SCHEMA_EXCEPT))
2987 		{
2988 			/* Limited list? Assume we are taking no items */
2989 			if (stmt->list_type == FDW_IMPORT_SCHEMA_LIMIT_TO)
2990 			{
2991 				import_layer = false;
2992 			}
2993 
2994 			/* Check the list for our items */
2995 			foreach (lc, stmt->table_list)
2996 			{
2997 				RangeVar* rv = (RangeVar*) lfirst(lc);
2998 				/* Found one! */
2999 				if (streq(rv->relname, table_name))
3000 				{
3001 					if (stmt->list_type == FDW_IMPORT_SCHEMA_LIMIT_TO)
3002 					{
3003 						import_layer = true;
3004 					}
3005 					else
3006 					{
3007 						import_layer = false;
3008 					}
3009 
3010 					break;
3011 				}
3012 
3013 			}
3014 		}
3015 
3016 		if (import_layer)
3017 		{
3018 			OGRErr err;
3019 			stringbuffer_t buf;
3020 			stringbuffer_init(&buf);
3021 
3022 			err = ogrLayerToSQL(ogr_lyr,
3023 			                    server->servername,
3024 			                    launder_table_names,
3025 			                    launder_column_names,
3026 			                    NULL,
3027 			                    ogrGetGeometryOid() != BYTEAOID,
3028 			                    &buf
3029 			                   );
3030 
3031 			if (err != OGRERR_NONE)
3032 			{
3033 				elog(ERROR, "unable to generate IMPORT SQL for '%s'", table_name);
3034 			}
3035 
3036 			commands = lappend(commands, pstrdup(stringbuffer_getstring(&buf)));
3037 			stringbuffer_release(&buf);
3038 		}
3039 	}
3040 
3041 	elog(NOTICE, "Number of tables to be created %d", list_length(commands));
3042 
3043 	ogrFinishConnection(&ogr);
3044 
3045 	return commands;
3046 }
3047 
3048 #endif /* PostgreSQL 9.5+ for ogrImportForeignSchema */
3049 
3050 
3051 
3052 #endif /* PostgreSQL 9.3+ version check */
3053 
3054