Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Expose ogr_fdw_info functions via SQL #260

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 37 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -426,6 +426,43 @@ ALTER SERVER myserver_latin1
);
```

### FDW Reflection

To view all GDAL table names available from a FDW server (the SQL equivalent of `ogr_fdw_info -s {datasource}`):

```sql
SELECT ogr_fdw_layers('myserver');
```
```
ogr_fdw_layers
----------------
Cities
Countries

(2 rows)
```

To retrieve the `CREATE FOREIGN TABLE` SQL for a particular OGR layer as the SQL equivalent of `ogr_fdw_info -s {datasource} -l {layer}`, use the `ogr_fdw_table_sql(server_name, ogr_layer_name, table_name=NULL, launder_column_names=TRUE, launder_table_name=TRUE)` function. By default the table name will reflect the OGR layer name. `launder_column_names` and `launder_table_name` have the same meaning as described in [Mixed Case and Special Characters](#mixed-case-and-special-characters).

```sql
SELECT ogr_fdw_table_sql('myserver', 'pt_two');
```
```
ogr_fdw_table_sql
---------------------------------
CREATE FOREIGN TABLE "pt_two" (
fid integer,
"geom" geometry(Point, 4326),
"name" varchar,
"age" integer,
"height" real,
"birthdate" date
) SERVER "myserver"
OPTIONS (layer 'pt_two');

(1 row)
```

### Utility Functions

To view the current FDW and GDAL version.
Expand Down
9 changes: 9 additions & 0 deletions input/import.source
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,11 @@ set client_min_messages=NOTICE;

CREATE SCHEMA imp1;

SELECT ogr_fdw_layers('myserver');

SELECT ogr_fdw_table_sql('myserver', '2launder');
SELECT ogr_fdw_table_sql('myserver', '2launder', NULL, FALSE, FALSE);

IMPORT FOREIGN SCHEMA ogr_all
LIMIT TO (n2launder)
FROM SERVER myserver
Expand All @@ -23,6 +28,8 @@ SELECT * FROM imp1.n2launder WHERE fid = 0;

CREATE SCHEMA imp2;

SELECT ogr_fdw_table_sql('myserver', 'natural', 'naturally');

IMPORT FOREIGN SCHEMA ogr_all
LIMIT TO ("natural")
FROM SERVER myserver
Expand All @@ -47,6 +54,8 @@ CREATE SERVER svr_test_apost

CREATE SCHEMA imp3;

SELECT ogr_fdw_table_sql('svr_test_apost', 'no_geom_apost');

IMPORT FOREIGN SCHEMA ogr_all
LIMIT TO (no_geom_apost)
FROM SERVER svr_test_apost
Expand Down
14 changes: 14 additions & 0 deletions ogr_fdw--1.1.sql
Original file line number Diff line number Diff line change
Expand Up @@ -30,3 +30,17 @@ CREATE OR REPLACE FUNCTION ogr_fdw_drivers()
LANGUAGE 'c'
IMMUTABLE STRICT
PARALLEL SAFE;

CREATE OR REPLACE FUNCTION ogr_fdw_table_sql(server_name text, layer_name text, table_name text DEFAULT NULL, launder_column_names boolean DEFAULT TRUE, launder_table_name boolean DEFAULT TRUE)
RETURNS text
AS 'MODULE_PATHNAME', 'ogr_fdw_table_sql'
LANGUAGE 'c'
STABLE
PARALLEL SAFE;

CREATE OR REPLACE FUNCTION ogr_fdw_layers(server_name text)
RETURNS TABLE (layer_name text)
AS 'MODULE_PATHNAME', 'ogr_fdw_layers'
LANGUAGE 'c'
IMMUTABLE STRICT
PARALLEL SAFE;
4 changes: 2 additions & 2 deletions ogr_fdw.c
Original file line number Diff line number Diff line change
Expand Up @@ -507,7 +507,7 @@ ogrEreportError(const char* errstr)
* Make sure the datasource is cleaned up when we're done
* with a connection.
*/
static void
void
ogrFinishConnection(OgrConnection* ogr)
{
elog(DEBUG3, "%s: entered function", __func__);
Expand All @@ -525,7 +525,7 @@ ogrFinishConnection(OgrConnection* ogr)
ogr->ds = NULL;
}

static OgrConnection
OgrConnection
ogrGetConnectionFromServer(Oid foreignserverid, OgrUpdateable updateable)
{
ForeignServer* server;
Expand Down
3 changes: 3 additions & 0 deletions ogr_fdw.h
Original file line number Diff line number Diff line change
Expand Up @@ -208,4 +208,7 @@ bool ogrDeparse(StringInfo buf, PlannerInfo* root, RelOptInfo* foreignrel, List*
Oid ogrGetGeometryOid(void);
OGRErr pgDatumToOgrGeometry (Datum pg_geometry, Oid pgsendfunc, OGRGeometryH* ogr_geometry);

OgrConnection ogrGetConnectionFromServer(Oid foreignserverid, OgrUpdateable updateable);
void ogrFinishConnection(OgrConnection* ogr);

#endif /* _OGR_FDW_H */
108 changes: 108 additions & 0 deletions ogr_fdw_func.c
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@

#include "ogr_fdw.h"
#include "ogr_fdw_gdal.h"
#include "ogr_fdw_common.h"

#include <postgres.h>
#include <fmgr.h>
Expand Down Expand Up @@ -85,4 +86,111 @@ Datum ogr_fdw_version(PG_FUNCTION_ARGS)
PG_RETURN_TEXT_P(cstring_to_text(ver_str));
}

/**
*/
Datum ogr_fdw_table_sql(PG_FUNCTION_ARGS);
PG_FUNCTION_INFO_V1(ogr_fdw_table_sql);
Datum ogr_fdw_table_sql(PG_FUNCTION_ARGS)
{
char *serverName;
char *layerName;
char *tableName = NULL;
bool launderColumnNames;
bool launderTableName;

ForeignServer* server;
OgrConnection ogr;
OGRErr err;
OGRLayerH ogrLayer;
stringbuffer_t buf;

// because we accept NULL for tableName, we need to make the function
// non-STRICT and check all the arguments for NULL
if (PG_ARGISNULL(0) || PG_ARGISNULL(1) || PG_ARGISNULL(3) || PG_ARGISNULL(4))
{
PG_RETURN_NULL();
}
if (!PG_ARGISNULL(2))
{
tableName = text_to_cstring(PG_GETARG_TEXT_PP(2));
}

serverName = text_to_cstring(PG_GETARG_TEXT_PP(0));
layerName = text_to_cstring(PG_GETARG_TEXT_PP(1));
launderColumnNames = PG_GETARG_BOOL(3);
launderTableName = PG_GETARG_BOOL(4);

server = GetForeignServerByName(serverName, false);
ogr = ogrGetConnectionFromServer(server->serverid, OGR_UPDATEABLE_FALSE);

ogrLayer = GDALDatasetGetLayerByName(ogr.ds, layerName);
if (ogrLayer)
{
stringbuffer_init(&buf);
err = ogrLayerToSQL(ogrLayer,
serverName,
launderTableName,
launderColumnNames,
tableName,
ogrGetGeometryOid() != BYTEAOID,
&buf);

if (err != OGRERR_NONE)
{
ereport(ERROR,
(errcode(ERRCODE_FDW_ERROR),
errmsg("Cannot generate SQL for: %s", layerName),
errhint("GDAL Error %d: %s", CPLGetLastErrorNo(), CPLGetLastErrorMsg())));
}

ogrFinishConnection(&ogr);

PG_RETURN_TEXT_P(cstring_to_text(stringbuffer_getstring(&buf)));
}
else
{
ereport(ERROR,
(errcode(ERRCODE_FDW_TABLE_NOT_FOUND),
errmsg("Unable to find OGR Layer: %s", layerName)));
}
}


/**
*/
Datum ogr_fdw_layers(PG_FUNCTION_ARGS);
PG_FUNCTION_INFO_V1(ogr_fdw_layers);
Datum ogr_fdw_layers(PG_FUNCTION_ARGS)
{
ForeignServer* server = GetForeignServerByName(text_to_cstring(PG_GETARG_TEXT_PP(0)), false);
OgrConnection ogr = ogrGetConnectionFromServer(server->serverid, OGR_UPDATEABLE_FALSE);

ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
InitMaterializedSRF(fcinfo, MAT_SRF_USE_EXPECTED_DESC);

for (int i = 0; i < GDALDatasetGetLayerCount(ogr.ds); i++)
{
OGRLayerH ogrLayer = GDALDatasetGetLayer(ogr.ds, i);

if (!ogrLayer)
{
ereport(WARNING,
(errcode(ERRCODE_FDW_ERROR),
errmsg("Skipping OGR layer %d, unable to read layer", i),
errhint("GDAL Error %d: %s", CPLGetLastErrorNo(), CPLGetLastErrorMsg())));
continue;
}

Datum values[1];
bool nulls[1];

memset(values, 0, sizeof(values));
memset(nulls, 0, sizeof(nulls));

values[0] = CStringGetTextDatum(OGR_L_GetName(ogrLayer));
tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
}
ogrFinishConnection(&ogr);

return (Datum) 0;
}
65 changes: 65 additions & 0 deletions output/import.source
Original file line number Diff line number Diff line change
@@ -1,6 +1,46 @@
set client_min_messages=NOTICE;
------------------------------------------------
CREATE SCHEMA imp1;
SELECT ogr_fdw_layers('myserver');
ogr_fdw_layers
----------------
enc
pt_two
2launder
poly
natural
(5 rows)

SELECT ogr_fdw_table_sql('myserver', '2launder');
ogr_fdw_table_sql
----------------------------------------------------
CREATE FOREIGN TABLE n2launder ( +
fid bigint, +
geom bytea, +
n2ame varchar(50) OPTIONS (column_name '2ame'), +
age integer, +
height double precision, +
b_rthdate date OPTIONS (column_name 'b-rthdate')+
) SERVER myserver +
OPTIONS (layer '2launder'); +

(1 row)

SELECT ogr_fdw_table_sql('myserver', '2launder', NULL, FALSE, FALSE);
ogr_fdw_table_sql
-----------------------------------
CREATE FOREIGN TABLE "2launder" (+
fid bigint, +
geom bytea, +
"2ame" varchar(50), +
age integer, +
"Height" double precision, +
"b-rthdate" date +
) SERVER myserver +
OPTIONS (layer '2launder'); +

(1 row)

IMPORT FOREIGN SCHEMA ogr_all
LIMIT TO (n2launder)
FROM SERVER myserver
Expand Down Expand Up @@ -31,6 +71,18 @@ SELECT * FROM imp1.n2launder WHERE fid = 0;

------------------------------------------------
CREATE SCHEMA imp2;
SELECT ogr_fdw_table_sql('myserver', 'natural', 'naturally');
ogr_fdw_table_sql
----------------------------------
CREATE FOREIGN TABLE naturally (+
fid bigint, +
id double precision, +
"natural" varchar(4) +
) SERVER myserver +
OPTIONS (layer 'natural'); +

(1 row)

IMPORT FOREIGN SCHEMA ogr_all
LIMIT TO ("natural")
FROM SERVER myserver
Expand Down Expand Up @@ -64,6 +116,19 @@ CREATE SERVER svr_test_apost
datasource '@abs_srcdir@/data/no_geom_apost.csv',
format 'CSV' );
CREATE SCHEMA imp3;
SELECT ogr_fdw_table_sql('svr_test_apost', 'no_geom_apost');
ogr_fdw_table_sql
------------------------------------------------------------------
CREATE FOREIGN TABLE no_geom_apost ( +
fid bigint, +
name varchar, +
age varchar, +
person_s_value varchar OPTIONS (column_name 'person''s value')+
) SERVER svr_test_apost +
OPTIONS (layer 'no_geom_apost'); +

(1 row)

IMPORT FOREIGN SCHEMA ogr_all
LIMIT TO (no_geom_apost)
FROM SERVER svr_test_apost
Expand Down
Loading