├── COPYRIGHT ├── Makefile ├── README.md ├── expected └── monetdb_fdw.out ├── monetdb_fdw--0.0.sql ├── monetdb_fdw.c ├── monetdb_fdw.control ├── sql └── monetdb_fdw.sql └── test_monetdb_fdw.sh /COPYRIGHT: -------------------------------------------------------------------------------- 1 | monetdb_fdw 2 | 3 | Copyright (c) 2013, Uptime Technologies, LLC. 4 | 5 | Permission to use, copy, modify, and distribute this software and its 6 | documentation for any purpose, without fee, and without a written 7 | agreement is hereby granted, provided that the above copyright notice 8 | and this paragraph and the following two paragraphs appear in all 9 | copies. 10 | 11 | IN NO EVENT SHALL UPTIME TECHNOLOGIES BE LIABLE TO ANY PARTY FOR 12 | DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, 13 | INCLUDING LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND 14 | ITS DOCUMENTATION, EVEN IF UPTIME TECHNOLOGIES HAS BEEN ADVISED OF THE 15 | POSSIBILITY OF SUCH DAMAGE. 16 | 17 | UPTIME TECHNOLOGIES SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, 18 | BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND 19 | FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS 20 | ON AN "AS IS" BASIS, AND UPTIME TECHNOLOGIES HAS NO OBLIGATIONS TO 21 | PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. 22 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | MODULE_big = monetdb_fdw 2 | OBJS = monetdb_fdw.o 3 | 4 | EXTENSION = monetdb_fdw 5 | DATA = monetdb_fdw--0.0.sql 6 | #DATA_built = monetdb_fdw.sql 7 | 8 | REGRESS = monetdb_fdw 9 | 10 | SHLIB_LINK = -lmapi 11 | PG_CPPFLAGS = -I/usr/include/monetdb 12 | 13 | #EXTRA_CLEAN = sql/monetdb_fdw.sql expected/monetdb_fdw.out 14 | 15 | ifdef USE_PGXS 16 | PG_CONFIG = pg_config 17 | PGXS := $(shell $(PG_CONFIG) --pgxs) 18 | include $(PGXS) 19 | else 20 | subdir = contrib/monetdb_fdw 21 | top_builddir = ../.. 22 | include $(top_builddir)/src/Makefile.global 23 | include $(top_srcdir)/contrib/contrib-global.mk 24 | endif 25 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | monetdb_fdw 2 | =========== 3 | 4 | monetdb_fdw 5 | -------------------------------------------------------------------------------- /expected/monetdb_fdw.out: -------------------------------------------------------------------------------- 1 | CREATE FUNCTION monetdb_fdw_handler() 2 | RETURNS fdw_handler 3 | AS 'monetdb_fdw' 4 | LANGUAGE C STRICT; 5 | CREATE FUNCTION monetdb_fdw_validator(text[], oid) 6 | RETURNS void 7 | AS 'monetdb_fdw' 8 | LANGUAGE C STRICT; 9 | CREATE FOREIGN DATA WRAPPER monetdb_fdw 10 | HANDLER monetdb_fdw_handler 11 | VALIDATOR monetdb_fdw_validator; 12 | CREATE SERVER monetdb_server FOREIGN DATA WRAPPER monetdb_fdw; 13 | CREATE USER MAPPING FOR current_user SERVER monetdb_server; 14 | CREATE FOREIGN TABLE nation ( 15 | "n_nationkey" INTEGER, 16 | "n_name" CHAR(25), 17 | "n_regionkey" INTEGER, 18 | "n_comment" VARCHAR(152) 19 | ) SERVER monetdb_server 20 | OPTIONS (host 'localhost', port '50000', user 'monetdb', passwd 'monetdb', dbname 'dbt3', table 'nation') 21 | ; 22 | SELECT * FROM nation ORDER BY 1; 23 | n_nationkey | n_name | n_regionkey | n_comment 24 | -------------+---------------------------+-------------+------------------------------------------------------------------------------------------------------------------ 25 | 0 | ALGERIA | 0 | final accounts wake quickly. special reques 26 | 1 | ARGENTINA | 1 | idly final instructions cajole stealthily. regular instructions wake carefully blithely express accounts. fluffi 27 | 2 | BRAZIL | 1 | always pending pinto beans sleep sil 28 | 3 | CANADA | 1 | foxes among the bold requests 29 | 4 | EGYPT | 4 | pending accounts haggle furiously. furiously bold accounts detect. platelets at the packages haggle caref 30 | 5 | ETHIOPIA | 0 | fluffily ruthless requests integrate fluffily. pending ideas wake blithely acco 31 | 6 | FRANCE | 3 | even requests detect near the pendin 32 | 7 | GERMANY | 3 | blithely ironic foxes grow. quickly pending accounts are b 33 | 8 | INDIA | 2 | ironic packages should have to are slyly around the special, ironic accounts. iron 34 | 9 | INDONESIA | 2 | unusual excuses are quickly requests. slyly ironic accounts haggle carefully above the pendin 35 | 10 | IRAN | 4 | blithely even accounts about the furiously regular foxes nag slyly final accounts. quickly final fo 36 | 11 | IRAQ | 4 | express, pending deposits boost quick 37 | 12 | JAPAN | 2 | blithely final packages cajole quickly even dependencies? blithely regular deposits haggle express, ironic re 38 | 13 | JORDAN | 4 | blithe, express deposits boost carefully busy accounts. furiously pending depos 39 | 14 | KENYA | 0 | ironic requests boost. quickly pending pinto beans cajole slyly slyly even deposits. ironic packages 40 | 15 | MOROCCO | 0 | ideas according to the fluffily final pinto beans sleep furiously 41 | 16 | MOZAMBIQUE | 0 | ironic courts wake fluffily even, bold deposi 42 | 17 | PERU | 1 | final, final accounts sleep slyly across the requests. 43 | 18 | CHINA | 2 | bold accounts are. slyly ironic escapades haggle acc 44 | 19 | ROMANIA | 3 | deposits boost against the brave id 45 | 20 | SAUDI ARABIA | 4 | fluffily final accounts wake slyly-- fi 46 | 21 | VIETNAM | 2 | doggedly ironic requests haggle furiously ironic, ironic packages. furiously final courts wake fur 47 | 22 | RUSSIA | 3 | slowly pending patterns x-ray quickly. ironic, even accounts haggle furiously. even, final deposits mold bl 48 | 23 | UNITED KINGDOM | 3 | fluffily regular pinto beans breach according to the ironic dolph 49 | 24 | UNITED STATES | 1 | blithely regular deposits serve furiously blithely regular warthogs! slyly fi 50 | (25 rows) 51 | 52 | CREATE FOREIGN TABLE nation1 ( 53 | "n_nationkey" INTEGER, 54 | "n_name" CHAR(25), 55 | "n_regionkey" INTEGER, 56 | "n_comment" VARCHAR(152) 57 | ) SERVER monetdb_server 58 | OPTIONS (host 'nosuchhost', port '50000', user 'monetdb', passwd 'monetdb', dbname 'dbt3', table 'nation') 59 | ; 60 | SELECT * FROM nation1; 61 | ERROR: monetdb_fdw: getaddrinfo failed: Name or service not known 62 | CREATE FOREIGN TABLE nation2 ( 63 | "n_nationkey" INTEGER, 64 | "n_name" CHAR(25), 65 | "n_regionkey" INTEGER, 66 | "n_comment" VARCHAR(152) 67 | ) SERVER monetdb_server 68 | OPTIONS (host 'localhost', port '50001', user 'monetdb', passwd 'monetdb', dbname 'dbt3', table 'nation') 69 | ; 70 | SELECT * FROM nation2; 71 | ERROR: monetdb_fdw: could not connect to localhost:50001: Connection refused 72 | CREATE FOREIGN TABLE nation3 ( 73 | "n_nationkey" INTEGER, 74 | "n_name" CHAR(25), 75 | "n_regionkey" INTEGER, 76 | "n_comment" VARCHAR(152) 77 | ) SERVER monetdb_server 78 | OPTIONS (host 'localhost', port '50000', user 'nosuchuser', passwd 'monetdb', dbname 'dbt3', table 'nation') 79 | ; 80 | SELECT * FROM nation3; 81 | ERROR: monetdb_fdw: InvalidCredentialsException:checkCredentials:invalid credentials for user 'nosuchuser' 82 | 83 | CREATE FOREIGN TABLE nation4 ( 84 | "n_nationkey" INTEGER, 85 | "n_name" CHAR(25), 86 | "n_regionkey" INTEGER, 87 | "n_comment" VARCHAR(152) 88 | ) SERVER monetdb_server 89 | OPTIONS (host 'localhost', port '50000', user 'monetdb', passwd 'wrongpass', dbname 'dbt3', table 'nation') 90 | ; 91 | SELECT * FROM nation4; 92 | ERROR: monetdb_fdw: InvalidCredentialsException:checkCredentials:invalid credentials for user 'monetdb' 93 | 94 | CREATE FOREIGN TABLE nation5 ( 95 | "n_nationkey" INTEGER, 96 | "n_name" CHAR(25), 97 | "n_regionkey" INTEGER, 98 | "n_comment" VARCHAR(152) 99 | ) SERVER monetdb_server 100 | OPTIONS (host 'localhost', port '50000', user 'monetdb', passwd 'monetdb', dbname 'nosuchdb', table 'nation') 101 | ; 102 | SELECT * FROM nation5; 103 | ERROR: monetdb_fdw: monetdbd: no such database 'nosuchdb', please create it first 104 | 105 | CREATE FOREIGN TABLE nation6 ( 106 | "n_nationkey" INTEGER, 107 | "n_name" CHAR(25), 108 | "n_regionkey" INTEGER, 109 | "n_comment" VARCHAR(152) 110 | ) SERVER monetdb_server 111 | OPTIONS (host 'localhost', port '50000', user 'monetdb', passwd 'monetdb', dbname 'dbt3', table 'nosuchtable') 112 | ; 113 | SELECT * FROM nation6; 114 | ERROR: monetdb_fdw: 42S02!SELECT: no such table 'nosuchtable' 115 | 116 | CONTEXT: relation nation6, line 0 117 | CREATE FOREIGN TABLE nation7 ( 118 | "n_nationkey" INTEGER, 119 | "n_name" CHAR(25), 120 | "n_regionkey" INTEGER, 121 | "n_comment" VARCHAR(152) 122 | ) SERVER monetdb_server 123 | OPTIONS (host 'localhost', port '50000', user 'monetdb', passwd 'monetdb', dbname 'dbt3', table 'select * fro nation') 124 | ; 125 | SELECT * FROM nation7; 126 | ERROR: monetdb_fdw: 42000!syntax error, unexpected SELECT in: "select * from select" 127 | 128 | CONTEXT: relation nation7, line 0 129 | CREATE FOREIGN TABLE nation8 ( 130 | "n_nationkey" INTEGER, 131 | "n_name" CHAR(25), 132 | "n_regionkey" INTEGER, 133 | "n_comment" VARCHAR(152) 134 | ) SERVER monetdb_server 135 | OPTIONS (host 'localhost', port '50000', user 'monetdb', passwd 'monetdb', dbname 'dbt3', query 'select * fro nation') 136 | ; 137 | SELECT * FROM nation8; 138 | ERROR: monetdb_fdw: 42000!syntax error, unexpected IDENT, expecting SCOLON in: "select * fro" 139 | 140 | CONTEXT: relation nation8, line 0 141 | DROP FOREIGN TABLE nation1; 142 | DROP FOREIGN TABLE nation2; 143 | DROP FOREIGN TABLE nation3; 144 | DROP FOREIGN TABLE nation4; 145 | DROP FOREIGN TABLE nation5; 146 | DROP FOREIGN TABLE nation6; 147 | DROP FOREIGN TABLE nation7; 148 | DROP FOREIGN TABLE nation8; 149 | \d 150 | List of relations 151 | Schema | Name | Type | Owner 152 | --------+--------+---------------+------- 153 | public | nation | foreign table | snaga 154 | (1 row) 155 | 156 | -------------------------------------------------------------------------------- /monetdb_fdw--0.0.sql: -------------------------------------------------------------------------------- 1 | /* contrib/monetdb_fdw/monetdb_fdw--0.0.sql */ 2 | 3 | -- complain if script is sourced in psql, rather than via CREATE EXTENSION 4 | \echo Use "CREATE EXTENSION monetdb_fdw" to load this file. \quit 5 | 6 | CREATE FUNCTION monetdb_fdw_handler() 7 | RETURNS fdw_handler 8 | AS 'MODULE_PATHNAME' 9 | LANGUAGE C STRICT; 10 | 11 | CREATE FUNCTION monetdb_fdw_validator(text[], oid) 12 | RETURNS void 13 | AS 'MODULE_PATHNAME' 14 | LANGUAGE C STRICT; 15 | 16 | CREATE FOREIGN DATA WRAPPER monetdb_fdw 17 | HANDLER monetdb_fdw_handler 18 | VALIDATOR monetdb_fdw_validator; 19 | -------------------------------------------------------------------------------- /monetdb_fdw.c: -------------------------------------------------------------------------------- 1 | /*------------------------------------------------------------------------- 2 | * 3 | * monetdb_fdw.c 4 | * a monetdb foreign-data wrapper 5 | * 6 | * Copyright (c) 2010-2013, PostgreSQL Global Development Group 7 | * Copyright (c) 2013, Satoshi Nagayasu 8 | * 9 | * IDENTIFICATION 10 | * contrib/monetdb_fdw/monetdb_fdw.c 11 | * 12 | *------------------------------------------------------------------------- 13 | */ 14 | #include "postgres.h" 15 | 16 | #include "access/reloptions.h" 17 | #include "catalog/pg_foreign_table.h" 18 | #include "commands/defrem.h" 19 | #include "commands/explain.h" 20 | #include "foreign/fdwapi.h" 21 | #include "foreign/foreign.h" 22 | #include "funcapi.h" 23 | #include "miscadmin.h" 24 | #include "optimizer/pathnode.h" 25 | #include "optimizer/planmain.h" 26 | #include "optimizer/restrictinfo.h" 27 | #include "utils/builtins.h" 28 | #include "utils/rel.h" 29 | 30 | #include 31 | #include 32 | 33 | PG_MODULE_MAGIC; 34 | 35 | //#define _DEBUG 1 36 | 37 | typedef struct MonetdbFdwPlanState 38 | { 39 | char *host; /* host */ 40 | char *port; /* port */ 41 | char *user; /* user name */ 42 | char *passwd; /* password */ 43 | char *dbname; /* database name */ 44 | char *table; /* target table name */ 45 | char *query; /* pre-defined query */ 46 | char *monetdb_opt6; /* required option 2 */ 47 | List *options; /* other options */ 48 | 49 | BlockNumber pages; /* estimate of file's physical size */ 50 | double ntuples; /* estimate of number of rows in file */ 51 | } MonetdbFdwPlanState; 52 | 53 | typedef struct MonetdbFdwExecutionState 54 | { 55 | /* 56 | * TODO: Add members here to keep status information while a table scan. 57 | * 58 | * This struct must be allocated and initialized in BeginForeignScan(), 59 | * and would be destroyed in EndForeignScan(). 60 | * 61 | * Generally, resource handler to access external resource needs to be 62 | * kept here. 63 | */ 64 | Mapi dbh; 65 | MapiHdl hdl; 66 | 67 | Relation rel; 68 | int linecount; 69 | } MonetdbFdwExecutionState; 70 | 71 | struct MonetdbFdwOption 72 | { 73 | const char *optname; 74 | Oid optcontext; /* Oid of catalog in which option may appear */ 75 | }; 76 | 77 | extern Datum monetdb_fdw_handler(PG_FUNCTION_ARGS); 78 | extern Datum monetdb_fdw_validator(PG_FUNCTION_ARGS); 79 | 80 | PG_FUNCTION_INFO_V1(monetdb_fdw_handler); 81 | PG_FUNCTION_INFO_V1(monetdb_fdw_validator); 82 | 83 | static const struct MonetdbFdwOption valid_options[] = { 84 | {"host", ForeignTableRelationId}, 85 | {"port", ForeignTableRelationId}, 86 | {"user", ForeignTableRelationId}, 87 | {"passwd", ForeignTableRelationId}, 88 | {"dbname", ForeignTableRelationId}, 89 | {"table", ForeignTableRelationId}, 90 | {"query", ForeignTableRelationId}, 91 | {"monetdb_opt6", ForeignTableRelationId}, 92 | 93 | /* Sentinel */ 94 | {NULL, InvalidOid} 95 | }; 96 | 97 | static void monetdbGetForeignRelSize(PlannerInfo *, RelOptInfo *, Oid); 98 | static void monetdbGetForeignPaths(PlannerInfo *, RelOptInfo *, Oid); 99 | static ForeignScan *monetdbGetForeignPlan(PlannerInfo *, RelOptInfo *, Oid, ForeignPath *, List *, List *); 100 | static void monetdbExplainForeignScan(ForeignScanState *, ExplainState *); 101 | static void monetdbBeginForeignScan(ForeignScanState *, int); 102 | static TupleTableSlot *monetdbIterateForeignScan(ForeignScanState *); 103 | static void monetdbEndForeignScan(ForeignScanState *); 104 | static void monetdbReScanForeignScan(ForeignScanState *); 105 | 106 | /* 107 | * Foreign-data wrapper handler function: return a struct with pointers 108 | * to my callback routines. 109 | */ 110 | Datum 111 | monetdb_fdw_handler(PG_FUNCTION_ARGS) 112 | { 113 | FdwRoutine *fdwroutine = makeNode(FdwRoutine); 114 | 115 | fdwroutine->GetForeignRelSize = monetdbGetForeignRelSize; 116 | fdwroutine->GetForeignPaths = monetdbGetForeignPaths; 117 | fdwroutine->GetForeignPlan = monetdbGetForeignPlan; 118 | fdwroutine->ExplainForeignScan = monetdbExplainForeignScan; 119 | fdwroutine->BeginForeignScan = monetdbBeginForeignScan; 120 | fdwroutine->IterateForeignScan = monetdbIterateForeignScan; 121 | fdwroutine->EndForeignScan = monetdbEndForeignScan; 122 | fdwroutine->ReScanForeignScan = monetdbReScanForeignScan; 123 | // fdwroutine->AnalyzeForeignTable = fileAnalyzeForeignTable; 124 | 125 | PG_RETURN_POINTER(fdwroutine); 126 | } 127 | 128 | static void 129 | monetdbGetOptions(Oid foreigntableid, char **host, char **port, 130 | char **user, char **passwd, 131 | char **dbname, char **tablename, char **query) 132 | { 133 | ForeignTable *table; 134 | ForeignServer *server; 135 | ForeignDataWrapper *wrapper; 136 | 137 | List *options; 138 | ListCell *cell, *prev; 139 | 140 | /* 141 | * Extract options from FDW objects. We ignore user mappings because 142 | * file_fdw doesn't have any options that can be specified there. 143 | * 144 | * (XXX Actually, given the current contents of valid_options[], there's 145 | * no point in examining anything except the foreign table's own options. 146 | * Simplify?) 147 | */ 148 | table = GetForeignTable(foreigntableid); 149 | server = GetForeignServer(table->serverid); 150 | wrapper = GetForeignDataWrapper(server->fdwid); 151 | 152 | options = NIL; 153 | options = list_concat(options, wrapper->options); 154 | options = list_concat(options, server->options); 155 | options = list_concat(options, table->options); 156 | 157 | *host = NULL; 158 | *port = NULL; 159 | *user = NULL; 160 | *passwd = NULL; 161 | *dbname = NULL; 162 | *tablename = NULL; 163 | *query = NULL; 164 | #ifdef NOT_USED 165 | monetdb_opt6 = NULL; 166 | #endif 167 | 168 | retry: 169 | prev = NULL; 170 | foreach(cell, options) 171 | { 172 | DefElem *def = (DefElem *) lfirst(cell); 173 | 174 | if (strcmp(def->defname, "host") == 0) 175 | { 176 | *host = defGetString(def); 177 | options = list_delete_cell(options, cell, prev); 178 | goto retry; 179 | } 180 | else if (strcmp(def->defname, "port") == 0) 181 | { 182 | *port = defGetString(def); 183 | options = list_delete_cell(options, cell, prev); 184 | goto retry; 185 | } 186 | else if (strcmp(def->defname, "user") == 0) 187 | { 188 | *user = defGetString(def); 189 | options = list_delete_cell(options, cell, prev); 190 | goto retry; 191 | } 192 | else if (strcmp(def->defname, "passwd") == 0) 193 | { 194 | *passwd = defGetString(def); 195 | options = list_delete_cell(options, cell, prev); 196 | goto retry; 197 | } 198 | else if (strcmp(def->defname, "dbname") == 0) 199 | { 200 | *dbname = defGetString(def); 201 | options = list_delete_cell(options, cell, prev); 202 | goto retry; 203 | } 204 | else if (strcmp(def->defname, "table") == 0) 205 | { 206 | *tablename = defGetString(def); 207 | options = list_delete_cell(options, cell, prev); 208 | goto retry; 209 | } 210 | else if (strcmp(def->defname, "query") == 0) 211 | { 212 | *query = defGetString(def); 213 | options = list_delete_cell(options, cell, prev); 214 | goto retry; 215 | } 216 | #ifdef NOT_USED 217 | else if (strcmp(def->defname, "monetdb_opt6") == 0) 218 | { 219 | monetdb_opt6 = defGetString(def); 220 | options = list_delete_cell(options, cell, prev); 221 | goto retry; 222 | } 223 | #endif 224 | else 225 | prev = cell; 226 | } 227 | 228 | /* 229 | * Check required option(s) here. 230 | */ 231 | if (host == NULL) 232 | elog(ERROR, "monetdb_fdw: host is required for monetdb_fdw foreign tables"); 233 | if (port == NULL) 234 | elog(ERROR, "monetdb_fdw: port is required for monetdb_fdw foreign tables"); 235 | if (user == NULL) 236 | elog(ERROR, "monetdb_fdw: user is required for monetdb_fdw foreign tables"); 237 | if (passwd == NULL) 238 | elog(ERROR, "monetdb_fdw: passwd is required for monetdb_fdw foreign tables"); 239 | if (dbname == NULL) 240 | elog(ERROR, "monetdb_fdw: dbname is required for monetdb_fdw foreign tables"); 241 | if (tablename == NULL && query == NULL) 242 | elog(ERROR, "monetdb_fdw: table or query is required for monetdb_fdw foreign tables"); 243 | #ifdef NOT_USED 244 | if (monetdb_opt6 == NULL) 245 | elog(ERROR, "monetdb_fdw: monetdb_opt6 is required for monetdb_fdw foreign tables"); 246 | #endif 247 | 248 | #ifdef _DEBUG 249 | elog(NOTICE, "monetdb_fdw: host=%s, port=%d, user=%s, pass=XXX, dbname=%s, table=%s, query=%s", 250 | *host, 251 | atoi(*port), 252 | *user, 253 | *dbname, 254 | *tablename, 255 | *query); 256 | #endif 257 | 258 | /* Other options */ 259 | // options = options; 260 | } 261 | 262 | static double 263 | monetdbEstimateRowsImpl(PlannerInfo *root, 264 | RelOptInfo *baserel, 265 | MonetdbFdwPlanState *fdw_private) 266 | { 267 | // estimate_size(root, baserel, fdw_private); 268 | 269 | /* 270 | * TODO: Implement this function to estimate number of rows. 271 | */ 272 | return atoi(fdw_private->port); 273 | } 274 | 275 | /* 276 | * monetdbGetForeignRelSize 277 | * 278 | * This function is intended for estimating relation size, which means 279 | * number of rows in the table. 280 | * 281 | * To estimate number of rows, this function extract fdw options at first, 282 | * and then, estimate number of rows with using it. 283 | */ 284 | static void 285 | monetdbGetForeignRelSize(PlannerInfo *root, 286 | RelOptInfo *baserel, 287 | Oid foreigntableid) 288 | { 289 | MonetdbFdwPlanState *fdw_private; 290 | 291 | /* 292 | * Fetch options. We only need filename at this point, but we might as 293 | * well get everything and not need to re-fetch it later in planning. 294 | */ 295 | fdw_private = (MonetdbFdwPlanState *) palloc(sizeof(MonetdbFdwPlanState)); 296 | 297 | monetdbGetOptions(foreigntableid, 298 | &fdw_private->host, 299 | &fdw_private->port, 300 | &fdw_private->user, 301 | &fdw_private->passwd, 302 | &fdw_private->dbname, 303 | &fdw_private->table, 304 | &fdw_private->query); 305 | 306 | baserel->fdw_private = (void *) fdw_private; 307 | 308 | /* Estimate relation size */ 309 | baserel->rows = monetdbEstimateRowsImpl(root, baserel, fdw_private); 310 | } 311 | 312 | static void 313 | monetdbGetForeignPaths(PlannerInfo *root, 314 | RelOptInfo *baserel, 315 | Oid foreigntableid) 316 | { 317 | MonetdbFdwPlanState *fdw_private = (MonetdbFdwPlanState *) baserel->fdw_private; 318 | Cost startup_cost = 0; 319 | Cost total_cost = 0; 320 | #ifdef NOT_USED 321 | List *columns; 322 | #endif 323 | List *coptions = NIL; 324 | 325 | #ifdef NOT_USED 326 | /* Decide whether to selectively perform binary conversion */ 327 | if (check_selective_binary_conversion(baserel, 328 | foreigntableid, 329 | &columns)) 330 | coptions = list_make1(makeDefElem("convert_selectively", 331 | (Node *) columns)); 332 | 333 | #endif 334 | /* Estimate costs */ 335 | // estimate_costs(root, baserel, fdw_private, 336 | // &startup_cost, &total_cost); 337 | total_cost = 0; 338 | 339 | /* 340 | * Create a ForeignPath node and add it as only possible path. We use the 341 | * fdw_private list of the path to carry the convert_selectively option; 342 | * it will be propagated into the fdw_private list of the Plan node. 343 | */ 344 | add_path(baserel, (Path *) 345 | create_foreignscan_path(root, baserel, 346 | baserel->rows, 347 | startup_cost, 348 | total_cost, 349 | NIL, /* no pathkeys */ 350 | NULL, /* no outer rel either */ 351 | coptions)); 352 | 353 | /* 354 | * If data file was sorted, and we knew it somehow, we could insert 355 | * appropriate pathkeys into the ForeignPath node to tell the planner 356 | * that. 357 | */ 358 | } 359 | 360 | static ForeignScan * 361 | monetdbGetForeignPlan(PlannerInfo *root, 362 | RelOptInfo *baserel, 363 | Oid foreigntableid, 364 | ForeignPath *best_path, 365 | List *tlist, 366 | List *scan_clauses) 367 | { 368 | Index scan_relid = baserel->relid; 369 | 370 | /* 371 | * We have no native ability to evaluate restriction clauses, so we just 372 | * put all the scan_clauses into the plan node's qual list for the 373 | * executor to check. So all we have to do here is strip RestrictInfo 374 | * nodes from the clauses and ignore pseudoconstants (which will be 375 | * handled elsewhere). 376 | */ 377 | scan_clauses = extract_actual_clauses(scan_clauses, false); 378 | 379 | /* Create the ForeignScan node */ 380 | return make_foreignscan(tlist, 381 | scan_clauses, 382 | scan_relid, 383 | NIL, /* no expressions to evaluate */ 384 | best_path->fdw_private); 385 | } 386 | 387 | static void 388 | monetdbExplainForeignScan(ForeignScanState *node, ExplainState *es) 389 | { 390 | // char *filename; 391 | // List *options; 392 | 393 | /* Fetch options --- we only need filename at this point */ 394 | // fileGetOptions(RelationGetRelid(node->ss.ss_currentRelation), 395 | // &filename, &options); 396 | 397 | ExplainPropertyText("Foreign File", "monetdb", es); 398 | 399 | /* Suppress file size if we're not showing cost details */ 400 | if (es->costs) 401 | { 402 | ExplainPropertyLong("Foreign File Size", 8192, es); 403 | } 404 | } 405 | 406 | static void 407 | monetdb_die(Mapi dbh, MapiHdl hdl) 408 | { 409 | char *err; 410 | 411 | if (mapi_result_error(hdl)) 412 | err = pstrdup(mapi_result_error(hdl)); 413 | else if (mapi_error_str(dbh)) 414 | err = pstrdup(mapi_error_str(dbh)); 415 | else 416 | err = "unknown error."; 417 | 418 | if (hdl != NULL) { 419 | mapi_explain_query(hdl, stderr); 420 | do { 421 | if (mapi_result_error(hdl) != NULL) 422 | mapi_explain_result(hdl, stderr); 423 | } while (mapi_next_result(hdl) == 1); 424 | mapi_close_handle(hdl); 425 | mapi_destroy(dbh); 426 | } else if (dbh != NULL) { 427 | mapi_explain(dbh, stderr); 428 | mapi_destroy(dbh); 429 | } 430 | 431 | elog(ERROR, "monetdb_fdw: %s", err); 432 | } 433 | 434 | static void 435 | monetdbBeginForeignScan(ForeignScanState *node, int eflags) 436 | { 437 | ForeignScan *plan = (ForeignScan *) node->ss.ps.plan; 438 | MonetdbFdwExecutionState *festate; 439 | MonetdbFdwPlanState fdw_private; 440 | 441 | char *host; 442 | char *port; 443 | char *user; 444 | char *passwd; 445 | char *dbname; 446 | char *table; 447 | char *query; 448 | 449 | monetdbGetOptions(RelationGetRelid(node->ss.ss_currentRelation), 450 | &host, &port, &user, &passwd, &dbname, &table, &query); 451 | 452 | /* 453 | * Do nothing in EXPLAIN (no ANALYZE) case. node->fdw_state stays NULL. 454 | */ 455 | if (eflags & EXEC_FLAG_EXPLAIN_ONLY) 456 | return; 457 | 458 | /* 459 | * Save state in node->fdw_state. We must save enough information to call 460 | * BeginCopyFrom() again. 461 | */ 462 | festate = (MonetdbFdwExecutionState *) palloc(sizeof(MonetdbFdwExecutionState)); 463 | 464 | festate->dbh = NULL; 465 | festate->hdl = NULL; 466 | 467 | /* 468 | * TODO: Open an external table (or resource) here. 469 | */ 470 | festate->dbh = mapi_connect(host, atoi(port), user, passwd, "sql", dbname); 471 | if (mapi_error(festate->dbh)) 472 | monetdb_die(festate->dbh, festate->hdl); 473 | 474 | festate->rel = node->ss.ss_currentRelation; 475 | festate->linecount = 0; 476 | 477 | node->fdw_state = (void *) festate; 478 | } 479 | 480 | /* 481 | * buildTupleImpl() 482 | * 483 | * TODO: Implement this function to build a single tuple. 484 | * 485 | * Read data from an external table, and pack them into 486 | * a tuple (a Datum array) with using XXXGetDatum(). 487 | * See include/postgres.h for more infomation about 488 | * XXXGetDatum() functions. 489 | * 490 | * Also FdwExecutionState needs to be updated here. 491 | * 492 | * Return true if a tuple is found (or available), 493 | * false if not available (or end of the scan). 494 | */ 495 | //static bool 496 | static HeapTuple 497 | buildTupleImpl(MonetdbFdwExecutionState *festate) 498 | { 499 | int i; 500 | char **values; 501 | HeapTuple tuple; 502 | int num_attrs = RelationGetDescr(festate->rel)->natts; 503 | 504 | values = (char **)palloc( sizeof(char *) * num_attrs ); 505 | 506 | // elog(NOTICE, "buildTupleImpl: num_attrs=%d", num_attrs); 507 | 508 | /* end of result set */ 509 | if ( !mapi_fetch_row(festate->hdl) ) 510 | return NULL; 511 | 512 | for (i=0 ; ihdl, i); 515 | 516 | #ifdef _DEBUG 517 | elog(NOTICE, "buildTupleImpl: mapi_fetch_field -> %s", values[i]); 518 | #endif 519 | } 520 | 521 | tuple = BuildTupleFromCStrings(TupleDescGetAttInMetadata(festate->rel->rd_att), values); 522 | 523 | festate->linecount++; 524 | 525 | return tuple; 526 | } 527 | 528 | static void 529 | monetdbErrorCallback(void *arg) 530 | { 531 | MonetdbFdwExecutionState *festate = (MonetdbFdwExecutionState *)arg; 532 | 533 | errcontext("relation %s, line %d", 534 | NameStr(festate->rel->rd_rel->relname), 535 | festate->linecount); 536 | } 537 | 538 | static TupleTableSlot * 539 | monetdbIterateForeignScan(ForeignScanState *node) 540 | { 541 | MonetdbFdwExecutionState *festate = (MonetdbFdwExecutionState *) node->fdw_state; 542 | TupleTableSlot *slot = node->ss.ss_ScanTupleSlot; 543 | bool found; 544 | ErrorContextCallback errcallback; 545 | 546 | /* Set up callback to identify error line number. */ 547 | errcallback.callback = monetdbErrorCallback; 548 | errcallback.arg = (void *)festate; 549 | 550 | errcallback.previous = error_context_stack; 551 | error_context_stack = &errcallback; 552 | 553 | if (!festate->hdl) 554 | { 555 | char *host; 556 | char *port; 557 | char *user; 558 | char *passwd; 559 | char *dbname; 560 | char *table; 561 | char *query; 562 | 563 | char q[1024]; 564 | 565 | monetdbGetOptions(RelationGetRelid(node->ss.ss_currentRelation), 566 | &host, &port, &user, &passwd, &dbname, &table, &query); 567 | 568 | if ( !query ) 569 | snprintf(q, sizeof(q), "SELECT * FROM %s", table); 570 | else 571 | strncpy(q, query, sizeof(q)); 572 | 573 | #ifdef _DEBUG 574 | elog(NOTICE, "monetdb_fdw: monetdbIterateForeignScan: query=%s", query); 575 | #endif 576 | 577 | if ((festate->hdl = mapi_query(festate->dbh, q)) == NULL || 578 | mapi_error(festate->dbh) != MOK) 579 | { 580 | monetdb_die(festate->dbh, festate->hdl); 581 | } 582 | 583 | #ifdef _DEBUG 584 | elog(NOTICE, "monetdb_fdw: monetdbIterateForeignScan: mapi_query done."); 585 | #endif 586 | } 587 | 588 | /* 589 | * The protocol for loading a virtual tuple into a slot is first 590 | * ExecClearTuple, then fill the values/isnull arrays, then 591 | * ExecStoreVirtualTuple. If we don't find another row in the file, we 592 | * just skip the last step, leaving the slot empty as required. 593 | * 594 | * We can pass ExprContext = NULL because we read all columns from the 595 | * file, so no need to evaluate default expressions. 596 | * 597 | * We can also pass tupleOid = NULL because we don't allow oids for 598 | * foreign tables. 599 | */ 600 | ExecClearTuple(slot); 601 | // found = NextCopyFrom(festate->cstate, NULL, 602 | // slot->tts_values, slot->tts_isnull, 603 | // NULL); 604 | 605 | { 606 | TupleDesc tupDesc = RelationGetDescr(festate->rel); 607 | int num_phys_attrs = tupDesc->natts; 608 | HeapTuple tup; 609 | 610 | MemSet(slot->tts_values, 0, num_phys_attrs * sizeof(Datum)); 611 | MemSet(slot->tts_isnull, true, num_phys_attrs * sizeof(bool)); 612 | 613 | tup = buildTupleImpl(festate); 614 | 615 | if (tup) 616 | ExecStoreTuple(tup, slot, InvalidBuffer, false); 617 | } 618 | 619 | /* Remove error callback. */ 620 | error_context_stack = errcallback.previous; 621 | 622 | return slot; 623 | } 624 | 625 | static void 626 | monetdbEndForeignScan(ForeignScanState *node) 627 | { 628 | MonetdbFdwExecutionState *festate = (MonetdbFdwExecutionState *) node->fdw_state; 629 | 630 | /* if festate is NULL, we are in EXPLAIN; nothing to do */ 631 | if (festate) 632 | { 633 | /* 634 | * TODO: Close the external table (resource), and initialize 635 | * FdwExecutionState state. 636 | */ 637 | if (festate->hdl) 638 | mapi_close_handle(festate->hdl); 639 | 640 | if (festate->dbh) 641 | mapi_destroy(festate->dbh); 642 | } 643 | } 644 | 645 | static void 646 | monetdbReScanForeignScan(ForeignScanState *node) 647 | { 648 | MonetdbFdwExecutionState *festate = (MonetdbFdwExecutionState *) node->fdw_state; 649 | 650 | /* 651 | * TODO: Close external resource here. 652 | */ 653 | 654 | /* 655 | * TODO: Re-open the external table (resource) here. 656 | */ 657 | festate->linecount = 0; 658 | } 659 | 660 | 661 | /* 662 | * Check if the option is valid. 663 | */ 664 | static bool 665 | validate_option(DefElem *def, Oid context) 666 | { 667 | const struct MonetdbFdwOption *opt; 668 | bool is_valid = false; 669 | 670 | for (opt = valid_options; opt->optname; opt++) 671 | { 672 | if (context == opt->optcontext && strcmp(opt->optname, def->defname) == 0) 673 | is_valid = true; 674 | } 675 | 676 | if (!is_valid) 677 | { 678 | StringInfoData buf; 679 | 680 | /* 681 | * Unknown option specified, complain about it. Provide a hint 682 | * with list of valid options for the object. 683 | */ 684 | initStringInfo(&buf); 685 | for (opt = valid_options; opt->optname; opt++) 686 | { 687 | if (context == opt->optcontext) 688 | appendStringInfo(&buf, "%s%s", (buf.len > 0) ? ", " : "", 689 | opt->optname); 690 | } 691 | 692 | ereport(ERROR, 693 | (errcode(ERRCODE_FDW_INVALID_OPTION_NAME), 694 | errmsg("invalid option \"%s\"", def->defname), 695 | buf.len > 0 696 | ? errhint("Valid options in this context are: %s", 697 | buf.data) 698 | : errhint("There are no valid options in this context."))); 699 | } 700 | 701 | return is_valid; 702 | } 703 | 704 | 705 | Datum 706 | monetdb_fdw_validator(PG_FUNCTION_ARGS) 707 | { 708 | List *options_list = untransformRelOptions(PG_GETARG_DATUM(0)); 709 | Oid catalog = PG_GETARG_OID(1); 710 | char *host = NULL; 711 | char *port = NULL; 712 | char *user = NULL; 713 | char *passwd = NULL; 714 | char *dbname = NULL; 715 | char *table = NULL; 716 | char *query = NULL; 717 | char *monetdb_opt6 = NULL; 718 | ListCell *cell; 719 | 720 | /* 721 | * Only superusers are allowed to set options of a file_fdw foreign table. 722 | * This is because the filename is one of those options, and we don't want 723 | * non-superusers to be able to determine which file gets read. 724 | * 725 | * Putting this sort of permissions check in a validator is a bit of a 726 | * crock, but there doesn't seem to be any other place that can enforce 727 | * the check more cleanly. 728 | * 729 | * Note that the valid_options[] array disallows setting filename at any 730 | * options level other than foreign table --- otherwise there'd still be a 731 | * security hole. 732 | */ 733 | if (catalog == ForeignTableRelationId && !superuser()) 734 | ereport(ERROR, 735 | (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), 736 | errmsg("only superuser can change options of a monetdb_fdw foreign table"))); 737 | 738 | /* 739 | * Check that only options supported by monetdb_fdw, and allowed for the 740 | * current object type, are given. 741 | */ 742 | foreach(cell, options_list) 743 | { 744 | DefElem *def = (DefElem *) lfirst(cell); 745 | 746 | /* 747 | * Check if the option is valid with looking up MonetdbFdwOption. 748 | */ 749 | validate_option(def, catalog); 750 | 751 | /* 752 | * Separate out filename and force_not_null, since ProcessCopyOptions 753 | * won't accept them. (force_not_null only comes in a boolean 754 | * per-column flavor here.) 755 | */ 756 | /* 757 | */ 758 | if (strcmp(def->defname, "host") == 0) 759 | { 760 | if (host) 761 | ereport(ERROR, 762 | (errcode(ERRCODE_SYNTAX_ERROR), 763 | errmsg("conflicting or redundant options"))); 764 | 765 | /* 766 | * Option value can be obtained by useing defGetXXX() function: 767 | * typically defGetString(), defGetNumeric(), defGetBoolean() or 768 | * defGetInt64(). 769 | * 770 | * See commands/defrem.h for more information about defGetXXX() 771 | * functions. 772 | */ 773 | host = defGetString(def); 774 | } 775 | else if (strcmp(def->defname, "port") == 0) 776 | { 777 | if (port) 778 | ereport(ERROR, 779 | (errcode(ERRCODE_SYNTAX_ERROR), 780 | errmsg("conflicting or redundant options"))); 781 | 782 | port = defGetString(def); 783 | } 784 | else if (strcmp(def->defname, "user") == 0) 785 | { 786 | if (user) 787 | ereport(ERROR, 788 | (errcode(ERRCODE_SYNTAX_ERROR), 789 | errmsg("conflicting or redundant options"))); 790 | 791 | user = defGetString(def); 792 | } 793 | else if (strcmp(def->defname, "passwd") == 0) 794 | { 795 | if (passwd) 796 | ereport(ERROR, 797 | (errcode(ERRCODE_SYNTAX_ERROR), 798 | errmsg("conflicting or redundant options"))); 799 | 800 | passwd = defGetString(def); 801 | } 802 | else if (strcmp(def->defname, "dbname") == 0) 803 | { 804 | if (dbname) 805 | ereport(ERROR, 806 | (errcode(ERRCODE_SYNTAX_ERROR), 807 | errmsg("conflicting or redundant options"))); 808 | 809 | dbname = defGetString(def); 810 | } 811 | else if (strcmp(def->defname, "table") == 0) 812 | { 813 | if (table) 814 | ereport(ERROR, 815 | (errcode(ERRCODE_SYNTAX_ERROR), 816 | errmsg("conflicting or redundant options"))); 817 | 818 | table = defGetString(def); 819 | } 820 | else if (strcmp(def->defname, "query") == 0) 821 | { 822 | if (query) 823 | ereport(ERROR, 824 | (errcode(ERRCODE_SYNTAX_ERROR), 825 | errmsg("conflicting or redundant options"))); 826 | 827 | query = defGetString(def); 828 | } 829 | #ifdef NOT_USED 830 | else if (strcmp(def->defname, "monetdb_opt6") == 0) 831 | { 832 | if (monetdb_opt6) 833 | ereport(ERROR, 834 | (errcode(ERRCODE_SYNTAX_ERROR), 835 | errmsg("conflicting or redundant options"))); 836 | 837 | monetdb_opt6 = defGetString(def); 838 | } 839 | #endif 840 | } 841 | 842 | /* 843 | * TODO: Check required option(s) here. 844 | */ 845 | if (catalog == ForeignTableRelationId) 846 | { 847 | if (host == NULL) 848 | ereport(ERROR, 849 | (errcode(ERRCODE_FDW_DYNAMIC_PARAMETER_VALUE_NEEDED), 850 | errmsg("host is required for monetdb_fdw foreign tables"))); 851 | 852 | if (port == NULL) 853 | ereport(ERROR, 854 | (errcode(ERRCODE_FDW_DYNAMIC_PARAMETER_VALUE_NEEDED), 855 | errmsg("port is required for monetdb_fdw foreign tables"))); 856 | 857 | if (user == NULL) 858 | ereport(ERROR, 859 | (errcode(ERRCODE_FDW_DYNAMIC_PARAMETER_VALUE_NEEDED), 860 | errmsg("user is required for monetdb_fdw foreign tables"))); 861 | 862 | if (passwd == NULL) 863 | ereport(ERROR, 864 | (errcode(ERRCODE_FDW_DYNAMIC_PARAMETER_VALUE_NEEDED), 865 | errmsg("passwd is required for monetdb_fdw foreign tables"))); 866 | 867 | if (dbname == NULL) 868 | ereport(ERROR, 869 | (errcode(ERRCODE_FDW_DYNAMIC_PARAMETER_VALUE_NEEDED), 870 | errmsg("dbname is required for monetdb_fdw foreign tables"))); 871 | 872 | if (table == NULL && query == NULL) 873 | ereport(ERROR, 874 | (errcode(ERRCODE_FDW_DYNAMIC_PARAMETER_VALUE_NEEDED), 875 | errmsg("table or query is required for monetdb_fdw foreign tables"))); 876 | 877 | // if (monetdb_opt6 == NULL) 878 | // ereport(ERROR, 879 | // (errcode(ERRCODE_FDW_DYNAMIC_PARAMETER_VALUE_NEEDED), 880 | // errmsg("monetdb_opt6 is required for monetdb_fdw foreign tables"))); 881 | } 882 | 883 | PG_RETURN_VOID(); 884 | } 885 | -------------------------------------------------------------------------------- /monetdb_fdw.control: -------------------------------------------------------------------------------- 1 | # monetdb_fdw extension 2 | comment = 'a monetdb foreign-data wrapper' 3 | default_version = '0.0' 4 | module_pathname = '$libdir/monetdb_fdw' 5 | relocatable = true 6 | 7 | -------------------------------------------------------------------------------- /sql/monetdb_fdw.sql: -------------------------------------------------------------------------------- 1 | CREATE FUNCTION monetdb_fdw_handler() 2 | RETURNS fdw_handler 3 | AS 'monetdb_fdw' 4 | LANGUAGE C STRICT; 5 | 6 | CREATE FUNCTION monetdb_fdw_validator(text[], oid) 7 | RETURNS void 8 | AS 'monetdb_fdw' 9 | LANGUAGE C STRICT; 10 | 11 | CREATE FOREIGN DATA WRAPPER monetdb_fdw 12 | HANDLER monetdb_fdw_handler 13 | VALIDATOR monetdb_fdw_validator; 14 | 15 | CREATE SERVER monetdb_server FOREIGN DATA WRAPPER monetdb_fdw; 16 | CREATE USER MAPPING FOR current_user SERVER monetdb_server; 17 | 18 | CREATE FOREIGN TABLE nation ( 19 | "n_nationkey" INTEGER, 20 | "n_name" CHAR(25), 21 | "n_regionkey" INTEGER, 22 | "n_comment" VARCHAR(152) 23 | ) SERVER monetdb_server 24 | OPTIONS (host 'localhost', port '50000', user 'monetdb', passwd 'monetdb', dbname 'dbt3', table 'nation') 25 | ; 26 | 27 | SELECT * FROM nation ORDER BY 1; 28 | 29 | CREATE FOREIGN TABLE nation1 ( 30 | "n_nationkey" INTEGER, 31 | "n_name" CHAR(25), 32 | "n_regionkey" INTEGER, 33 | "n_comment" VARCHAR(152) 34 | ) SERVER monetdb_server 35 | OPTIONS (host 'nosuchhost', port '50000', user 'monetdb', passwd 'monetdb', dbname 'dbt3', table 'nation') 36 | ; 37 | 38 | SELECT * FROM nation1; 39 | 40 | CREATE FOREIGN TABLE nation2 ( 41 | "n_nationkey" INTEGER, 42 | "n_name" CHAR(25), 43 | "n_regionkey" INTEGER, 44 | "n_comment" VARCHAR(152) 45 | ) SERVER monetdb_server 46 | OPTIONS (host 'localhost', port '50001', user 'monetdb', passwd 'monetdb', dbname 'dbt3', table 'nation') 47 | ; 48 | 49 | SELECT * FROM nation2; 50 | 51 | CREATE FOREIGN TABLE nation3 ( 52 | "n_nationkey" INTEGER, 53 | "n_name" CHAR(25), 54 | "n_regionkey" INTEGER, 55 | "n_comment" VARCHAR(152) 56 | ) SERVER monetdb_server 57 | OPTIONS (host 'localhost', port '50000', user 'nosuchuser', passwd 'monetdb', dbname 'dbt3', table 'nation') 58 | ; 59 | 60 | SELECT * FROM nation3; 61 | 62 | CREATE FOREIGN TABLE nation4 ( 63 | "n_nationkey" INTEGER, 64 | "n_name" CHAR(25), 65 | "n_regionkey" INTEGER, 66 | "n_comment" VARCHAR(152) 67 | ) SERVER monetdb_server 68 | OPTIONS (host 'localhost', port '50000', user 'monetdb', passwd 'wrongpass', dbname 'dbt3', table 'nation') 69 | ; 70 | 71 | SELECT * FROM nation4; 72 | 73 | CREATE FOREIGN TABLE nation5 ( 74 | "n_nationkey" INTEGER, 75 | "n_name" CHAR(25), 76 | "n_regionkey" INTEGER, 77 | "n_comment" VARCHAR(152) 78 | ) SERVER monetdb_server 79 | OPTIONS (host 'localhost', port '50000', user 'monetdb', passwd 'monetdb', dbname 'nosuchdb', table 'nation') 80 | ; 81 | 82 | SELECT * FROM nation5; 83 | 84 | CREATE FOREIGN TABLE nation6 ( 85 | "n_nationkey" INTEGER, 86 | "n_name" CHAR(25), 87 | "n_regionkey" INTEGER, 88 | "n_comment" VARCHAR(152) 89 | ) SERVER monetdb_server 90 | OPTIONS (host 'localhost', port '50000', user 'monetdb', passwd 'monetdb', dbname 'dbt3', table 'nosuchtable') 91 | ; 92 | 93 | SELECT * FROM nation6; 94 | 95 | CREATE FOREIGN TABLE nation7 ( 96 | "n_nationkey" INTEGER, 97 | "n_name" CHAR(25), 98 | "n_regionkey" INTEGER, 99 | "n_comment" VARCHAR(152) 100 | ) SERVER monetdb_server 101 | OPTIONS (host 'localhost', port '50000', user 'monetdb', passwd 'monetdb', dbname 'dbt3', table 'select * fro nation') 102 | ; 103 | 104 | SELECT * FROM nation7; 105 | 106 | CREATE FOREIGN TABLE nation8 ( 107 | "n_nationkey" INTEGER, 108 | "n_name" CHAR(25), 109 | "n_regionkey" INTEGER, 110 | "n_comment" VARCHAR(152) 111 | ) SERVER monetdb_server 112 | OPTIONS (host 'localhost', port '50000', user 'monetdb', passwd 'monetdb', dbname 'dbt3', query 'select * fro nation') 113 | ; 114 | 115 | SELECT * FROM nation8; 116 | 117 | DROP FOREIGN TABLE nation1; 118 | DROP FOREIGN TABLE nation2; 119 | DROP FOREIGN TABLE nation3; 120 | DROP FOREIGN TABLE nation4; 121 | DROP FOREIGN TABLE nation5; 122 | DROP FOREIGN TABLE nation6; 123 | DROP FOREIGN TABLE nation7; 124 | DROP FOREIGN TABLE nation8; 125 | 126 | \d 127 | -------------------------------------------------------------------------------- /test_monetdb_fdw.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | PSQL_OPTS="" 4 | 5 | if [ -z "$PGHOME" ]; then 6 | PGHOME=/tmp/pgsql 7 | fi 8 | 9 | USE_PGXS=1 10 | PATH=${PGHOME}/bin:$PATH 11 | export PGHOME USE_PGXS PATH 12 | 13 | make clean USE_PGXS=1 14 | make all USE_PGXS=1 15 | make install USE_PGXS=1 16 | 17 | DBNAME=__fdwtest__ 18 | 19 | echo dropdb ${PSQL_OPTS} ${DBNAME} 20 | dropdb ${PSQL_OPTS} ${DBNAME} 21 | createdb ${PSQL_OPTS} ${DBNAME} 22 | #psql ${PSQL_OPTS} -f monetdb_fdw.sql ${DBNAME} 23 | 24 | psql ${PSQL_OPTS} ${DBNAME} < ''1-URGENT'' and o_orderpriority <> ''2-HIGH'' then 1 else 0 end) as low_line_count 85 | from orders, lineitem 86 | where o_orderkey = l_orderkey and l_shipmode in (''TRUCK'', ''REG AIR'') 87 | and l_commitdate < l_receiptdate and l_shipdate < l_commitdate 88 | and l_receiptdate >= date ''1994-01-01'' 89 | and l_receiptdate < date ''1995-01-01'' 90 | group by l_shipmode 91 | order by l_shipmode; 92 | ' 93 | ); 94 | 95 | SELECT * FROM q12; 96 | 97 | EOF 98 | --------------------------------------------------------------------------------