Skip to content
Commits on Source (4)
......@@ -2,7 +2,7 @@
"name": "ogr_fdw",
"abstract": "OGR foreign data wrapper",
"description": "OGR FDW allows you to connect to any OGR supported data source.",
"version": "1.0",
"version": "1.0.9",
"maintainer": [
"Paul Ramsey <pramsey@cleverelephant.ca>"
],
......@@ -20,12 +20,12 @@
}
},
"provides": {
"http": {
"ogr_fdw": {
"file": "ogr_fdw--1.0.sql",
"docfile": "README.md",
"version": "1.0",
"version": "1.0.9",
"abstract": "OGR FDW wrapper"
},
}
},
"resources": {
"homepage": "https://github.com/pramsey/pgsql-ogr-fdw/",
......@@ -44,8 +44,10 @@
"url": "http://pgxn.org/meta/spec.txt"
},
"tags": [
"http",
"curl",
"web"
"ogr",
"gdal",
"gis",
"fdw",
"postgis"
]
}
......@@ -16,6 +16,15 @@ This implementation currently has the following limitations:
* **OGR connections every time** Rather than pooling OGR connections, each query makes (and disposes of) two new ones, which seems to be the largest performance drag at the moment for restricted (small) queries.
* **All columns are retrieved every time.** PostgreSQL foreign data wrappers don't require all columns all the time, and some efficiencies can be gained by only requesting the columns needed to fulfill a query. This would be a minimal efficiency improvement, but can be removed given some development time, since the OGR API supports returning a subset of columns.
## Download
* Windows
* Via [Stackbuilder](https://www.postgresql.org/download/windows/) (part of PostGIS Bundle)
* Linux
* [Arch Linux](https://aur.archlinux.org/packages/pgsql-ogr-fdw/)
* [Ubuntu](https://launchpad.net/ubuntu/+source/pgsql-ogr-fdw)
* [Red Hat](https://yum.postgresql.org/news-packagelist.php)
* OSX
## Basic Operation
In order to access geometry data from OGR, the PostGIS extension has to be installed: if it is not installed, geometry will be represented as bytea columns, with well-known binary (WKB) values.
......
pgsql-ogr-fdw (1.0.9-1) unstable; urgency=medium
* Team upload.
* New upstream release.
-- Bas Couwenberg <sebastic@debian.org> Tue, 05 Nov 2019 05:53:30 +0100
pgsql-ogr-fdw (1.0.8-2) unstable; urgency=medium
* Team upload.
......
......@@ -569,6 +569,10 @@ ogrGetConnectionFromServer(Oid foreignserverid, OgrUpdateable updateable)
/* Connect! */
err = ogrGetDataSource(&ogr, updateable);
if (err == OGRERR_FAILURE)
{
elog(ERROR, "ogrGetDataSource failed");
}
return ogr;
}
......@@ -770,6 +774,10 @@ ogr_fdw_validator(PG_FUNCTION_ARGS)
ogr.open_options = open_options;
err = ogrGetDataSource(&ogr, updateable);
if (err == OGRERR_FAILURE)
{
elog(ERROR, "ogrGetDataSource failed");
}
if (ogr.ds)
{
GDALClose(ogr.ds);
......
......@@ -31,7 +31,8 @@ typedef struct OgrDeparseCtx
static bool ogrDeparseExpr(Expr* node, OgrDeparseCtx* context);
// static void ogrDeparseOpExpr(OpExpr* node, OgrDeparseCtx *context);
static void setStringInfoLength(StringInfo str, int len)
static void
setStringInfoLength(StringInfo str, int len)
{
str->len = len;
str->data[len] = '\0';
......@@ -49,10 +50,14 @@ ogrStringFromDatum(Datum datum, Oid type)
if (type == BOOLOID)
{
if (datum)
{
return "1=1";
}
else
{
return "1=0";
}
}
/* get the type's output function */
tuple = SearchSysCache1(TYPEOID, ObjectIdGetDatum(type));
......@@ -87,7 +92,9 @@ ogrStringFromDatum(Datum datum, Oid type)
/* Don't return a zero length string, return an empty string */
if (str[0] == '\0')
{
return "''";
}
/* wrap string with ' */
appendStringInfoChar(&result, '\'');
......@@ -95,7 +102,9 @@ ogrStringFromDatum(Datum datum, Oid type)
{
/* Escape single quotes as doubled '' */
if (*p == '\'')
{
appendStringInfoChar(&result, '\'');
}
appendStringInfoChar(&result, *p);
}
appendStringInfoChar(&result, '\'');
......@@ -165,10 +174,14 @@ ogrDeparseConst(Const* constant, OgrDeparseCtx *context)
if (err != OGRERR_NONE)
{
if (! context->geom)
{
context->geom = ogrgeom;
}
else
{
elog(WARNING, "got two geometries in OGR FDW query, only using the first");
}
}
/*
* geometry doesn't play a role in the deparsed SQL
*/
......@@ -210,11 +223,15 @@ ogrIsLegalVarName(const char *varname)
/* First char must be a-zA-Z */
if (i == 0 && !((c >= 97 && c <= 122) || (c >= 65 && c <= 90)))
{
return false;
}
/* All other chars must be 0-9a-zA-Z_ */
if (!((c >= 97 && c <= 122) || (c >= 65 && c <= 90) || (c >= 48 && c <= 59) || (c == 96)))
{
return false;
}
}
return true;
......@@ -249,8 +266,10 @@ ogrDeparseVar(Var *node, OgrDeparseCtx *context)
{
fldname = OGR_L_GetFIDColumn(lyr);
if (! fldname || strlen(fldname) == 0)
{
fldname = "fid";
}
}
else if (table->cols[i].ogrvariant == OGR_FIELD)
{
OGRFeatureDefnH fd = OGR_L_GetLayerDefn(lyr);
......@@ -261,9 +280,13 @@ ogrDeparseVar(Var *node, OgrDeparseCtx *context)
if (fldname)
{
if (ogrIsLegalVarName(fldname))
{
appendStringInfoString(buf, fldname);
}
else
{
appendStringInfo(buf, "\"%s\"", fldname);
}
done = true;
}
......@@ -281,7 +304,8 @@ ogrDeparseVar(Var *node, OgrDeparseCtx *context)
return true;
}
static int ogrOperatorCmpFunc(const void * a, const void * b)
static int
ogrOperatorCmpFunc(const void* a, const void* b)
{
return strcasecmp(*(const char**)a, *(const char**)b);
}
......@@ -296,10 +320,14 @@ ogrOperatorIsSupported(const char *opname)
elog(DEBUG3, "ogrOperatorIsSupported got operator '%s'", opname);
if (bsearch(&opname, ogrOperators, 10, sizeof(char*), ogrOperatorCmpFunc))
{
return true;
}
else
{
return false;
}
}
static bool
......@@ -316,7 +344,9 @@ ogrDeparseOpExpr(OpExpr* node, OgrDeparseCtx *context)
/* Retrieve information about the operator from system catalog. */
tuple = SearchSysCache1(OPEROID, ObjectIdGetDatum(node->opno));
if (!HeapTupleIsValid(tuple))
{
elog(ERROR, "cache lookup failed for operator %u", node->opno);
}
form = (Form_pg_operator) GETSTRUCT(tuple);
oprkind = form->oprkind;
opname = NameStr(form->oprname);
......@@ -382,7 +412,9 @@ ogrDeparseOpExpr(OpExpr* node, OgrDeparseCtx *context)
/* All OGR string comparisons are case insensitive, so we just */
/* use 'ILIKE' all the time. */
if (streq(opname, "~~") || streq(opname, "~~*"))
{
opname = "ILIKE";
}
/* Operator symbol */
appendStringInfoString(buf, opname);
......@@ -439,7 +471,9 @@ ogrDeparseBoolExpr(BoolExpr *node, OgrDeparseCtx *context)
/* Connect expressions and parenthesize each condition */
if (! first)
{
appendStringInfo(buf, " %s ", op);
}
/* Unparse the expression, if possible */
result = ogrDeparseExpr((Expr*) lfirst(lc), context);
......@@ -447,21 +481,29 @@ ogrDeparseBoolExpr(BoolExpr *node, OgrDeparseCtx *context)
/* We can backtrack just this term for AND expressions */
if (boolop == AND_EXPR && ! result)
{
setStringInfoLength(buf, len_save_part);
}
/* We have to drop the whole thing if we can't get every part of an OR expression */
if (boolop == OR_EXPR && ! result)
{
break;
}
/* Don't flip the "first" bit until we get a good expression */
if (first && result)
{
first = false;
}
}
appendStringInfoChar(buf, ')');
/* We have to drop the whole thing if we can't get every part of an OR expression */
if (boolop == OR_EXPR && ! result)
{
setStringInfoLength(buf, len_save_all);
}
return result_total > 0;
}
......@@ -471,7 +513,9 @@ static bool
ogrDeparseRelabelType(RelabelType* node, OgrDeparseCtx* context)
{
if (node->relabelformat != COERCE_IMPLICIT_CAST)
{
elog(WARNING, "Received a non-implicit relabel expression but did not handle it");
}
return ogrDeparseExpr(node->arg, context);
}
......@@ -484,9 +528,13 @@ ogrDeparseNullTest(NullTest *node, OgrDeparseCtx *context)
appendStringInfoChar(buf, '(');
ogrDeparseExpr(node->arg, context);
if (node->nulltesttype == IS_NULL)
{
appendStringInfoString(buf, " IS NULL)");
}
else
{
appendStringInfoString(buf, " IS NOT NULL)");
}
return true;
}
......@@ -495,7 +543,9 @@ static bool
ogrDeparseExpr(Expr* node, OgrDeparseCtx* context)
{
if (node == NULL)
{
return false;
}
switch (nodeTag(node))
{
......@@ -554,7 +604,9 @@ ogrDeparse(StringInfo buf, PlannerInfo *root, RelOptInfo *foreignrel, List *expr
/* initialize result list to empty */
if (params)
{
*params = NIL;
}
/* Set up context struct for recursion */
context.buf = buf;
......@@ -591,8 +643,10 @@ ogrDeparse(StringInfo buf, PlannerInfo *root, RelOptInfo *foreignrel, List *expr
/* Don't flip the "first" bit until we get a good expression */
if (first && result)
{
first = false;
}
}
return true;
}
......
......@@ -59,13 +59,20 @@ formats()
createable = GDALDatasetTestCapability(ogr_dr, ODrCCreateDataSource);
#endif
/* Skip raster data sources */
if ( ! vector ) continue;
if (! vector)
{
continue;
}
/* Report sources w/ create capability as r/w */
if (createable)
{
tmpl = " -> \"%s\" (read/write)\n";
}
else
{
tmpl = " -> \"%s\" (readonly)\n";
}
printf(tmpl, GDALGetDriverShortName(ogr_dr));
}
......@@ -93,10 +100,14 @@ main (int argc, char **argv)
/* If no options are specified, display usage */
if (argc == 1)
{
usage();
}
while ((ch = getopt(argc, argv, "h?s:l:f")) != -1) {
switch (ch) {
while ((ch = getopt(argc, argv, "h?s:l:f")) != -1)
{
switch (ch)
{
case 's':
source = optarg;
break;
......@@ -202,7 +213,9 @@ ogrGenerateSQL(const char *source, const char *layer)
}
if (! ogr_dr)
{
ogr_dr = GDALGetDatasetDriver(ogr_ds);
}
/* There should be a nicer way to do this */
strcpy(server_name, "myserver");
......
......@@ -32,7 +32,10 @@ stringbuffer_init_with_size(stringbuffer_t *s, size_t size)
void
stringbuffer_release(stringbuffer_t* s)
{
if ( s->str_start ) free(s->str_start);
if (s->str_start)
{
free(s->str_start);
}
}
void
......@@ -61,7 +64,10 @@ void
stringbuffer_destroy(stringbuffer_t* s)
{
stringbuffer_release(s);
if ( s ) free(s);
if (s)
{
free(s);
}
}
/**
......@@ -88,7 +94,9 @@ stringbuffer_makeroom(stringbuffer_t *s, size_t size_to_add)
size_t required_size = current_size + size_to_add;
while (capacity < required_size)
{
capacity *= 2;
}
if (capacity > s->capacity)
{
......@@ -105,7 +113,9 @@ char
stringbuffer_lastchar(stringbuffer_t* s)
{
if (s->str_end == s->str_start)
{
return 0;
}
return * (s->str_end - 1);
}
......@@ -211,7 +221,8 @@ stringbuffer_avprintf(stringbuffer_t *s, const char *fmt, va_list ap)
/* Propogate errors up */
if (len < 0)
#if defined(__MINGW64_VERSION_MAJOR)
len = _vscprintf(fmt, ap2);/**Assume windows flaky vsnprintf that returns -1 if initial buffer to small and add more space **/
len = _vscprintf(fmt,
ap2); /**Assume windows flaky vsnprintf that returns -1 if initial buffer to small and add more space **/
#else
return len;
#endif
......@@ -228,9 +239,15 @@ stringbuffer_avprintf(stringbuffer_t *s, const char *fmt, va_list ap)
len = vsnprintf(s->str_end, maxlen, fmt, ap);
/* Printing error? Error! */
if ( len < 0 ) return len;
if (len < 0)
{
return len;
}
/* Too long still? Error! */
if ( len >= maxlen ) return -1;
if (len >= maxlen)
{
return -1;
}
}
/* Move end pointer forward and return. */
......@@ -303,7 +320,9 @@ stringbuffer_trim_trailing_zeroes(stringbuffer_t *s)
int dist;
if (s->str_end - s->str_start < 2)
{
return 0;
}
/* Roll backwards to find the decimal for this number */
while (ptr > s->str_start)
......@@ -315,14 +334,20 @@ stringbuffer_trim_trailing_zeroes(stringbuffer_t *s)
break;
}
if ((*ptr >= '0') && (*ptr <= '9'))
{
continue;
}
else
{
break;
}
}
/* No decimal? Nothing to trim! */
if (! decimal_ptr)
{
return 0;
}
ptr = s->str_end;
......@@ -331,20 +356,28 @@ stringbuffer_trim_trailing_zeroes(stringbuffer_t *s)
{
ptr--;
if (*ptr == '0')
{
continue;
}
else
{
break;
}
}
/* Huh, we get anywhere. Must not have trimmed anything. */
if (ptr == s->str_end)
{
return 0;
}
/* If we stopped at the decimal, we want to null that out.
It we stopped on a numeral, we want to preserve that, so push the
pointer forward one space. */
if (*ptr != '.')
{
ptr++;
}
/* Add null terminator re-set the end of the stringbuffer. */
*ptr = '\0';
......