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

Added callback routines for PostgreSQL >= 9.2 #3

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
109 changes: 6 additions & 103 deletions README
Original file line number Diff line number Diff line change
@@ -1,104 +1,7 @@
ODBC FDW (beta) for PostgreSQL 9.1+
===================================

This PostgreSQL extension implements a Foreign Data Wrapper (FDW) for
remote databases using Open Database Connectivity(ODBC): http://msdn.microsoft.com/en-us/library/ms714562(v=VS.85).aspx


Building
--------

To build the code, you need to have one of the ODBC driver managers installed
on your computer.

A list of driver managers is available here: http://en.wikipedia.org/wiki/Open_Database_Connectivity

Once that's done, the extension can be built with:

PATH=/usr/local/pgsql91/bin/:$PATH make USE_PGXS=1 make
sudo PATH=/usr/local/pgsql91/bin/:$PATH make USE_PGXS=1 install

(assuming you have PostgreSQL 9.1 in /usr/local/pgsql91).

I've tested on Mac OS X 10.6 and Windows 7. More testings on other platforms are coming soon.

TODO
-----------

I will test against other platforms with different driver managers and more foreign DBMS's.

Usage
-----

The following parameters can be set on ODBC foreign server:

dsn: The Database Source Name for the foreign database system you're connecting to.
Default: <none>

The following parameter can be set on a ODBC foreign table:

database: The name of the database to query.
Default: <none>

schema: The schema of the database to query.
Default: <none>

table: The name of the table to query.
Default: <none>

sql_query: Optional: User defined SQL statement for querying the foreign table.
Default: <none>

sql_count: Optional: User defined SQL statement for counting number of records in the foreign table.
Default: <none>

<column name>: The column mapping to remote table columns.
If there is no column mapping specified, default is the origninal column name.

The following parameter can be set on a user mapping for a ODBC
foreign server:

username: The username to authenticate to the foreign server with.
Default: <none>

password: The password to authenticate to the foreign server with.
Default: <none>


Example
-------


CREATE SERVER odbc_server
FOREIGN DATA WRAPPER odbc_fdw
OPTIONS (dsn 'test');

CREATE FOREIGN TABLE odbc_table (
db_id integer,
db_name varchar(255),
db_desc text,
db_users float4,
db_createdtime timestamp
)
SERVER odbc_server
OPTIONS (
database 'myplace',
schema 'test',
table 'dblist',
sql_query 'select description,id,name,created_datetime,sd,users from `test`.`dblist`',
sql_count 'select count(id) from `test`.`dblist`',
db_id 'id',
db_name 'name',
db_desc 'description',
db_users 'users',
db_createdtime 'created_datetime'
);

CREATE USER MAPPING FOR postgres
SERVER odbc_server
OPTIONS (username 'root', password '');


--
Zheng Yang
[email protected]
************************************************
* Note: this fork was for some private purpose
* and had some significant issues.
* Pls. check out https://github.com/CartoDB/odbc_fdw
* instead!
************************************************
170 changes: 167 additions & 3 deletions odbc_fdw.c
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
* This software is released under the PostgreSQL Licence.
*
* Author: Zheng Yang <[email protected]>
* Updated to 9.2+ by Gunnar "Nick" Bluth <[email protected]>
* based on tds_fdw code from Geoff Montee
*
* IDENTIFICATION
* odbc_fdw/odbc_fdw.c
Expand Down Expand Up @@ -36,6 +38,19 @@
#include "utils/relcache.h"
#include "storage/lock.h"
#include "miscadmin.h"
#include "mb/pg_wchar.h"
#include "optimizer/cost.h"
#include "storage/fd.h"
#include "utils/array.h"
#include "utils/builtins.h"
#include "utils/rel.h"

#if (PG_VERSION_NUM >= 90200)
#include "optimizer/pathnode.h"
#include "optimizer/restrictinfo.h"
#include "optimizer/planmain.h"
#endif


#include <stdio.h>
#include <sql.h>
Expand Down Expand Up @@ -109,12 +124,25 @@ PG_FUNCTION_INFO_V1(odbc_fdw_validator);
/*
* FDW callback routines
*/
static FdwPlan *odbcPlanForeignScan(Oid foreigntableid, PlannerInfo *root, RelOptInfo *baserel);
static void odbcExplainForeignScan(ForeignScanState *node, ExplainState *es);
static void odbcBeginForeignScan(ForeignScanState *node, int eflags);
static TupleTableSlot *odbcIterateForeignScan(ForeignScanState *node);
static void odbcReScanForeignScan(ForeignScanState *node);
static void odbcEndForeignScan(ForeignScanState *node);
/* routines for 9.2.0+ */
#if (PG_VERSION_NUM >= 90200)
static void odbcGetForeignRelSize(PlannerInfo *root, RelOptInfo *baserel, Oid foreigntableid);
static void odbcEstimateCosts(PlannerInfo *root, RelOptInfo *baserel, Cost *startup_cost, Cost *total_cost, Oid foreigntableid);
static void odbcGetForeignPaths(PlannerInfo *root, RelOptInfo *baserel, Oid foreigntableid);
static bool odbcAnalyzeForeignTable(Relation relation, AcquireSampleRowsFunc *func, BlockNumber *totalpages);
static ForeignScan* odbcGetForeignPlan(PlannerInfo *root, RelOptInfo *baserel, Oid foreigntableid, ForeignPath *best_path, List *tlist, List *scan_clauses);
/* routines for versions older than 9.2.0 */
#else
static FdwPlan *odbcPlanForeignScan(Oid foreigntableid, PlannerInfo *root, RelOptInfo *baserel);
#endif




/*
* helper functions
Expand All @@ -125,8 +153,15 @@ Datum
odbc_fdw_handler(PG_FUNCTION_ARGS)
{
FdwRoutine *fdwroutine = makeNode(FdwRoutine);

/* FIXME */
#if (PG_VERSION_NUM >= 90200)
fdwroutine->GetForeignRelSize = odbcGetForeignRelSize;
fdwroutine->GetForeignPaths = odbcGetForeignPaths;
fdwroutine->AnalyzeForeignTable = odbcAnalyzeForeignTable;
fdwroutine->GetForeignPlan = odbcGetForeignPlan;
#else
fdwroutine->PlanForeignScan = odbcPlanForeignScan;
#endif
fdwroutine->ExplainForeignScan = odbcExplainForeignScan;
fdwroutine->BeginForeignScan = odbcBeginForeignScan;
fdwroutine->IterateForeignScan = odbcIterateForeignScan;
Expand Down Expand Up @@ -701,6 +736,134 @@ odbcIsValidOption(const char *option, Oid context)
}


/* routines for 9.2.0+ */
#if (PG_VERSION_NUM >= 90200)

static void odbcGetForeignRelSize(PlannerInfo *root, RelOptInfo *baserel, Oid foreigntableid)
{
unsigned int table_size = 0;
char *svr_dsn = NULL;
char *svr_database = NULL;
char *svr_schema = NULL;
char *svr_table = NULL;
char *sql_query = NULL;
char *sql_count = NULL;
char *username = NULL;
char *password = NULL;
List *col_mapping_list;

#ifdef DEBUG
elog(NOTICE, "odbcGetForeignRelSize");
#endif

/* Fetch the foreign table options */
odbcGetOptions(foreigntableid, &svr_dsn, &svr_database, &svr_schema, &svr_table, &sql_query,
&sql_count, &username, &password, &col_mapping_list);

odbcGetTableSize(svr_dsn, svr_database, svr_schema, svr_table, username, password, sql_count, &table_size);

baserel->rows = table_size;
baserel->tuples = baserel->rows;
}

static void odbcEstimateCosts(PlannerInfo *root, RelOptInfo *baserel, Cost *startup_cost, Cost *total_cost, Oid foreigntableid)
{
unsigned int table_size = 0;
char *svr_dsn = NULL;
char *svr_database = NULL;
char *svr_schema = NULL;
char *svr_table = NULL;
char *sql_query = NULL;
char *sql_count = NULL;
char *username = NULL;
char *password = NULL;
List *col_mapping_list;

#ifdef DEBUG
elog(NOTICE, "odbcEstimateCosts");
#endif

/* Fetch the foreign table options */
odbcGetOptions(foreigntableid, &svr_dsn, &svr_database, &svr_schema, &svr_table, &sql_query,
&sql_count, &username, &password, &col_mapping_list);

odbcGetTableSize(svr_dsn, svr_database, svr_schema, svr_table, username, password, sql_count, &table_size);

*startup_cost = 25;

*total_cost = baserel->rows + *startup_cost;

#ifdef DEBUG
ereport(NOTICE,
(errmsg("----> finishing odbcEstimateCosts")
));
#endif
}

static void odbcGetForeignPaths(PlannerInfo *root, RelOptInfo *baserel, Oid foreigntableid)
{
Cost startup_cost;
Cost total_cost;

#ifdef DEBUG
ereport(NOTICE,
(errmsg("----> starting odbcGetForeignPaths")
));
#endif

odbcEstimateCosts(root, baserel, &startup_cost, &total_cost, foreigntableid);

add_path(baserel,
(Path *) create_foreignscan_path(root, baserel, baserel->rows, startup_cost, total_cost,
NIL, NULL, NIL));

#ifdef DEBUG
ereport(NOTICE,
(errmsg("----> finishing odbcGetForeignPaths")
));
#endif
}

static bool odbcAnalyzeForeignTable(Relation relation, AcquireSampleRowsFunc *func, BlockNumber *totalpages)
{
#ifdef DEBUG
ereport(NOTICE,
(errmsg("----> starting odbcAnalyzeForeignTable")
));
#endif

#ifdef DEBUG
ereport(NOTICE,
(errmsg("----> finishing odbcAnalyzeForeignTable")
));
#endif

return false;
}

static ForeignScan* odbcGetForeignPlan(PlannerInfo *root, RelOptInfo *baserel,
Oid foreigntableid, ForeignPath *best_path, List *tlist, List *scan_clauses)
{
Index scan_relid = baserel->relid;
#ifdef DEBUG
ereport(NOTICE,
(errmsg("----> starting odbcGetForeignPlan")
));
#endif

scan_clauses = extract_actual_clauses(scan_clauses, false);

#ifdef DEBUG
ereport(NOTICE,
(errmsg("----> finishing odbcGetForeignPlan")
));
#endif

return make_foreignscan(tlist, scan_clauses, scan_relid, NIL, NIL);
}

/* routines for versions older than 9.2.0 */
#else

/*
* odbcPlanForeignScan
Expand Down Expand Up @@ -747,6 +910,7 @@ odbcPlanForeignScan(Oid foreigntableid, PlannerInfo *root, RelOptInfo *baserel)
return fdwplan;
}

#endif

/*
* odbcBeginForeignScan
Expand Down Expand Up @@ -1248,4 +1412,4 @@ odbcReScanForeignScan(ForeignScanState *node)
#ifdef DEBUG
elog(NOTICE, "odbcReScanForeignScan");
#endif
}
}