@@ -104,6 +104,16 @@ static TupleTableSlot *ogrIterateForeignScan(ForeignScanState *node);
104104static void ogrReScanForeignScan (ForeignScanState * node );
105105static void ogrEndForeignScan (ForeignScanState * node );
106106
107+ static void strTableColumnLaunder (char * str );
108+
109+ #if PG_VERSION_NUM >= 90500
110+ /*
111+ * Require PostgreSQL >= 9.5
112+ */
113+ static List * ogrImportForeignSchema (ImportForeignSchemaStmt * stmt ,
114+ Oid serverOid );
115+ #endif
116+
107117/*
108118 * Helper functions
109119 */
@@ -115,6 +125,8 @@ static void ogr_fdw_exit(int code, Datum arg);
115125/* Global to hold GEOMETRYOID */
116126Oid GEOMETRYOID = InvalidOid ;
117127
128+ #define STR_MAX_LEN 256
129+
118130
119131void
120132_PG_init (void )
@@ -180,6 +192,12 @@ ogr_fdw_handler(PG_FUNCTION_ARGS)
180192 fdwroutine -> IterateForeignScan = ogrIterateForeignScan ;
181193 fdwroutine -> ReScanForeignScan = ogrReScanForeignScan ;
182194 fdwroutine -> EndForeignScan = ogrEndForeignScan ;
195+
196+ #if PG_VERSION_NUM >= 90500
197+ /* PostgreSQL 9.5+
198+ Support functions for IMPORT FOREIGN SCHEMA */
199+ fdwroutine -> ImportForeignSchema = ogrImportForeignSchema ;
200+ #endif
183201
184202 PG_RETURN_POINTER (fdwroutine );
185203}
@@ -1265,3 +1283,288 @@ ogrEndForeignScan(ForeignScanState *node)
12651283
12661284
12671285#endif /* PostgreSQL 9.3 version check */
1286+
1287+ #if PG_VERSION_NUM >= 90500
1288+ /*
1289+ * PostgreSQL 9.5 or above. Import a foreign schema
1290+ */
1291+ static List *
1292+ ogrImportForeignSchema (ImportForeignSchemaStmt * stmt , Oid serverOid )
1293+ {
1294+ List * commands = NIL ;
1295+ ForeignServer * server ;
1296+
1297+ List * options ;
1298+ ListCell * cell ;
1299+ char * sGeomType ;
1300+ bool check_schema = false;
1301+ bool launder_column_names = true;
1302+ bool launder_table_names = true;
1303+ StringInfoData buf ;
1304+ OgrConnection ogr ;
1305+ int i ;
1306+ int j ;
1307+ int k ;
1308+ char layer_name [STR_MAX_LEN ];
1309+ char table_name [STR_MAX_LEN ];
1310+ ListCell * lc ;
1311+ bool include_item = false;
1312+ OGRDataSourceH ogr_ds = NULL ;
1313+ OGRSFDriverH ogr_dr = NULL ;
1314+ OGRFeatureDefnH ogr_fd = NULL ;
1315+ OGRLayerH ogr_lyr = NULL ;
1316+
1317+ /** check table prefix if remote_schema asked for is not ogr_all **/
1318+ check_schema = !( strcmp (stmt -> remote_schema , "ogr_all" ) == 0 );
1319+
1320+ elog (NOTICE , "Check schema %d %s" , check_schema , stmt -> remote_schema );
1321+ if ( GEOMETRYOID == BYTEAOID ){ /* postgis is not in search path */
1322+ sGeomType = "bytea" ;
1323+ }
1324+ else {
1325+ sGeomType = "geometry" ;
1326+ }
1327+
1328+ #if GDAL_VERSION_MAJOR >= 2 || GDAL_VERSION_MINOR >= 11
1329+ int geom_field_count ;
1330+ #endif
1331+ /* Null all values */
1332+ memset (& ogr , 0 , sizeof (OgrConnection ));
1333+
1334+ /*
1335+ * Get connection to the foreign server. Connection manager will
1336+ * establish new connection if necessary.
1337+ */
1338+ server = GetForeignServer (serverOid );
1339+
1340+ /* Read server druver and data source connection string
1341+ */
1342+ options = NIL ;
1343+ options = list_concat (options , server -> options );
1344+ foreach (cell , options )
1345+ {
1346+ DefElem * def = (DefElem * ) lfirst (cell );
1347+ if (strcmp (def -> defname , OPT_SOURCE ) == 0 )
1348+ ogr .ds_str = defGetString (def );
1349+ if (strcmp (def -> defname , OPT_DRIVER ) == 0 )
1350+ ogr .dr_str = defGetString (def );
1351+ }
1352+
1353+ /* Parse statement laundering options */
1354+ foreach (lc , stmt -> options )
1355+ {
1356+ DefElem * def = (DefElem * ) lfirst (lc );
1357+
1358+ if (strcmp (def -> defname , "launder_column_names" ) == 0 )
1359+ launder_column_names = defGetBoolean (def );
1360+ else if (strcmp (def -> defname , "launder_table_names" ) == 0 )
1361+ launder_table_names = defGetBoolean (def );
1362+ else
1363+ ereport (ERROR ,
1364+ (errcode (ERRCODE_FDW_INVALID_OPTION_NAME ),
1365+ errmsg ("invalid option \"%s\"" , def -> defname )));
1366+ }
1367+
1368+ OGRRegisterAll ();
1369+ ogr_ds = OGROpen (ogr .ds_str , FALSE, & ogr_dr );
1370+
1371+
1372+ /* Create workspace for strings */
1373+ initStringInfo (& buf );
1374+
1375+ for ( i = 0 ; i < OGR_DS_GetLayerCount (ogr_ds ); i ++ )
1376+ {
1377+ include_item = false;
1378+ ogr_lyr = OGR_DS_GetLayer (ogr_ds , i );
1379+ /* we have a table */
1380+ if ( ogr_lyr )
1381+ {
1382+ /* layer name is never laundered, since it's link back to foreign data */
1383+ strncpy (layer_name , OGR_L_GetName (ogr_lyr ), STR_MAX_LEN );
1384+
1385+ /* We need to compare against created table names
1386+ * because postgres does an extra check on create foriegn table
1387+ * and removes statements not in limit
1388+ */
1389+ /* having this as separate variable since we may choose to launder it */
1390+ strncpy (table_name , OGR_L_GetName (ogr_lyr ), STR_MAX_LEN );
1391+ if (launder_table_names ){
1392+ strTableColumnLaunder (table_name );
1393+ }
1394+
1395+ /* only include if layer prefix starts with remote schema
1396+ or remote schema is ogr_all */
1397+ include_item = (!check_schema ||
1398+ ( strncmp (layer_name , stmt -> remote_schema , strlen (stmt -> remote_schema ) ) == 0 ) );
1399+ /* Apply restrictions for LIMIT TO and EXCEPT */
1400+ if (include_item && ( stmt -> list_type == FDW_IMPORT_SCHEMA_LIMIT_TO ||
1401+ stmt -> list_type == FDW_IMPORT_SCHEMA_EXCEPT ) )
1402+ {
1403+ /* Check if current table is in list of except/include tables */
1404+ /* default state is only true if type is EXCEPT */
1405+ include_item = ( stmt -> list_type == FDW_IMPORT_SCHEMA_EXCEPT );
1406+ foreach (lc , stmt -> table_list )
1407+ {
1408+ RangeVar * rv = (RangeVar * ) lfirst (lc );
1409+ if ( strcmp (rv -> relname , table_name ) == 0 ){
1410+ //elog(NOTICE, "MATCH layer %s, table %s", layer_name, rv->relname );
1411+ /* bit is true on match only if limit to */
1412+ include_item = ( stmt -> list_type == FDW_IMPORT_SCHEMA_LIMIT_TO );
1413+ break ;
1414+ }
1415+
1416+ }
1417+ }
1418+ }
1419+
1420+ if (include_item ){
1421+ resetStringInfo (& buf );
1422+
1423+ if (launder_table_names ){
1424+ strTableColumnLaunder (table_name );
1425+ }
1426+ ogr_fd = OGR_L_GetLayerDefn (ogr_lyr );
1427+ if ( !ogr_fd )
1428+ {
1429+ /** TODO raise error **/
1430+ elog (NOTICE , "Error in layer def load %s" , layer_name );
1431+ }
1432+
1433+ appendStringInfo (& buf , "CREATE FOREIGN TABLE %s (\n" ,
1434+ quote_identifier (table_name ));
1435+ appendStringInfo (& buf , " fid integer" );
1436+
1437+ #if GDAL_VERSION_MAJOR >= 2 || GDAL_VERSION_MINOR >= 11
1438+ geom_field_count = OGR_FD_GetGeomFieldCount (ogr_fd );
1439+ if ( geom_field_count == 1 )
1440+ {
1441+ appendStringInfo (& buf , " ,geom %s" , sGeomType );
1442+ }
1443+ else
1444+ {
1445+ for ( j = 0 ; j < geom_field_count ; j ++ )
1446+ {
1447+ appendStringInfo (& buf , " ,geom%d %s" , j + 1 , sGeomType );
1448+ }
1449+ }
1450+ #else
1451+ if ( OGR_L_GetGeomType (ogr_lyr ) != wkbNone )
1452+ appendStringInfo (& buf , " ,geom %s" , sGeomType );
1453+ #endif
1454+
1455+ for ( k = 0 ; k < OGR_FD_GetFieldCount (ogr_fd ); k ++ )
1456+ {
1457+ char field_name [STR_MAX_LEN ];
1458+ OGRFieldDefnH ogr_fld = OGR_FD_GetFieldDefn (ogr_fd , k );
1459+ strncpy (field_name , OGR_Fld_GetNameRef (ogr_fld ), STR_MAX_LEN );
1460+ if (launder_column_names ){
1461+ strTableColumnLaunder (field_name );
1462+ }
1463+ appendStringInfo (& buf , " , %s " , quote_identifier (field_name ));
1464+ switch ( OGR_Fld_GetType (ogr_fld ) )
1465+ {
1466+ case OFTInteger :
1467+ #if GDAL_VERSION_MAJOR >= 2
1468+ if ( OGR_Fld_GetSubType (ogr_fld ) == OFSTBoolean )
1469+ appendStringInfoString (& buf ,"boolean" );
1470+ else
1471+ #endif
1472+ appendStringInfoString (& buf ,"integer" );
1473+ break ;
1474+ case OFTReal :
1475+ appendStringInfoString (& buf ,"real" );
1476+ break ;
1477+ case OFTString :
1478+ appendStringInfoString (& buf ,"varchar" );
1479+ break ;
1480+ case OFTBinary :
1481+ appendStringInfoString (& buf ,"bytea" );
1482+ break ;
1483+ case OFTDate :
1484+ appendStringInfoString (& buf ,"date" );
1485+ break ;
1486+ case OFTTime :
1487+ appendStringInfoString (& buf ,"time" );
1488+ break ;
1489+ case OFTDateTime :
1490+ appendStringInfoString (& buf ,"timestamp" );
1491+ break ;
1492+ case OFTIntegerList :
1493+ appendStringInfoString (& buf ,"integer[]" );
1494+ break ;
1495+ case OFTRealList :
1496+ appendStringInfoString (& buf ,"real[]" );
1497+ break ;
1498+ case OFTStringList :
1499+ appendStringInfoString (& buf ,"varchar[]" );
1500+ break ;
1501+
1502+ #if GDAL_VERSION_MAJOR >= 2
1503+ case OFTInteger64 :
1504+ appendStringInfoString (& buf ,"bigint" );
1505+ break ;
1506+ #endif
1507+ default :
1508+ elog (NOTICE , "Unsupported GDAL type '%s'" , OGR_GetFieldTypeName (OGR_Fld_GetType (ogr_fld )) );
1509+ //CPLError(CE_Failure, CPLE_AppDefined, "Unsupported GDAL type '%s'", OGR_GetFieldTypeName(OGR_Fld_GetType(ogr_fld)));
1510+ //return OGRERR_FAILURE;
1511+ }
1512+ }
1513+
1514+ /*
1515+ * Add server name and layer-level options. We specify remote
1516+ * layer name as option
1517+ */
1518+ appendStringInfo (& buf , "\n) SERVER %s\nOPTIONS (" ,
1519+ quote_identifier (server -> servername ));
1520+
1521+ appendStringInfoString (& buf , "layer " );
1522+ ogrDeparseStringLiteral (& buf , layer_name );
1523+
1524+ appendStringInfoString (& buf , ");" );
1525+
1526+ commands = lappend (commands , pstrdup (buf .data ));
1527+ }
1528+ }
1529+ OGR_DS_Destroy (ogr_ds );
1530+ elog (NOTICE , "Number of tables to be created %d" , list_length (commands ) );
1531+ //elog(NOTICE, "The nth item %s", list_nth(commands,0) );
1532+
1533+ /* Clean up */
1534+ pfree (buf .data );
1535+ /** returns list of create foreign table statements to run **/
1536+ return commands ;
1537+ }
1538+ #endif /*end import foreign schema **/
1539+
1540+ static void strTableColumnLaunder (char * str )
1541+ {
1542+ int i , j = 0 ;
1543+ for (i = 0 ; str [i ]; i ++ )
1544+ {
1545+ char c = tolower (str [i ]);
1546+
1547+ /* First character is a numeral, prefix with 'n' */
1548+ if ( i == 0 && (c >= 48 && c <= 57 ) )
1549+ {
1550+ str [j ++ ] = 'n' ;
1551+ }
1552+
1553+ /* Replace non-safe characters w/ _ */
1554+ if ( (c >= 48 && c <= 57 ) || /* 0-9 */
1555+ (c >= 65 && c <= 90 ) || /* A-Z */
1556+ (c >= 97 && c <= 122 ) /* a-z */ )
1557+ {
1558+ /* Good character, do nothing */
1559+ }
1560+ else
1561+ {
1562+ c = '_' ;
1563+ }
1564+ str [j ++ ] = c ;
1565+
1566+ /* Avoid mucking with data beyond the end of our stack-allocated strings */
1567+ if ( j >= STR_MAX_LEN )
1568+ j = STR_MAX_LEN - 1 ;
1569+ }
1570+ }
0 commit comments