├── doc └── sqlite_fdw.md ├── sqlite_fdw.control ├── .gitignore ├── THANKS.md ├── sql ├── sqlite_fdw.sql └── sqlite_fdw--0.0.1.sql ├── License ├── Makefile ├── README.md └── src └── sqlite_fdw.c /doc/sqlite_fdw.md: -------------------------------------------------------------------------------- 1 | ../README.md -------------------------------------------------------------------------------- /sqlite_fdw.control: -------------------------------------------------------------------------------- 1 | # sqlite FDW 2 | comment = 'SQLite Foreign Data Wrapper' 3 | default_version = '0.0.1' 4 | module_pathname = '$libdir/sqlite_fdw' 5 | relocatable = true 6 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Object files 2 | *.o 3 | 4 | # Libraries 5 | *.lib 6 | *.a 7 | 8 | # Shared objects (inc. Windows DLLs) 9 | *.dll 10 | *.so 11 | *.so.* 12 | *.dylib 13 | 14 | # Executables 15 | *.exe 16 | *.out 17 | *.app 18 | -------------------------------------------------------------------------------- /THANKS.md: -------------------------------------------------------------------------------- 1 | Thanks to Andrew Dunstan for his blackhole foreign data wrapper. The sqlite foreign data wrapper is built upon his work. 2 | 3 | Thanks to Dave Page for this mysql foreign data wrapper. It helped me a lot to code some parts of this foreign data wrapper. 4 | -------------------------------------------------------------------------------- /sql/sqlite_fdw.sql: -------------------------------------------------------------------------------- 1 | /*------------------------------------------------------------------------- 2 | * 3 | * foreign-data wrapper sqlite 4 | * 5 | * Copyright (c) 2013-2016, Guillaume Lelarge 6 | * 7 | * This software is released under the PostgreSQL Licence 8 | * 9 | * Author: Guillaume Lelarge 10 | * 11 | * IDENTIFICATION 12 | * sqlite_fdw/=sql/sqlite_fdw.sql 13 | * 14 | *------------------------------------------------------------------------- 15 | */ 16 | 17 | CREATE FUNCTION sqlite_fdw_handler() 18 | RETURNS fdw_handler 19 | AS 'MODULE_PATHNAME' 20 | LANGUAGE C STRICT; 21 | 22 | CREATE FUNCTION sqlite_fdw_validator(text[], oid) 23 | RETURNS void 24 | AS 'MODULE_PATHNAME' 25 | LANGUAGE C STRICT; 26 | 27 | CREATE FOREIGN DATA WRAPPER sqlite_fdw 28 | HANDLER sqlite_fdw_handler 29 | VALIDATOR sqlite_fdw_validator; 30 | -------------------------------------------------------------------------------- /sql/sqlite_fdw--0.0.1.sql: -------------------------------------------------------------------------------- 1 | /*------------------------------------------------------------------------- 2 | * 3 | * foreign-data wrapper sqlite 4 | * 5 | * Copyright (c) 2013-2016, Guillaume Lelarge 6 | * 7 | * This software is released under the PostgreSQL Licence 8 | * 9 | * Author: Guillaume Lelarge 10 | * 11 | * IDENTIFICATION 12 | * sqlite_fdw/=sql/sqlite_fdw.sql 13 | * 14 | *------------------------------------------------------------------------- 15 | */ 16 | 17 | CREATE FUNCTION sqlite_fdw_handler() 18 | RETURNS fdw_handler 19 | AS 'MODULE_PATHNAME' 20 | LANGUAGE C STRICT; 21 | 22 | CREATE FUNCTION sqlite_fdw_validator(text[], oid) 23 | RETURNS void 24 | AS 'MODULE_PATHNAME' 25 | LANGUAGE C STRICT; 26 | 27 | CREATE FOREIGN DATA WRAPPER sqlite_fdw 28 | HANDLER sqlite_fdw_handler 29 | VALIDATOR sqlite_fdw_validator; 30 | -------------------------------------------------------------------------------- /License: -------------------------------------------------------------------------------- 1 | This software, PostgreSQL sqlite Foreign Data Wrapper, is released under 2 | the terms of the PostgreSQL License. 3 | 4 | Copyright (c) 2013-2017, Guillaume Lelarge 5 | 6 | Permission to use, copy, modify, and distribute this software and its 7 | documentation for any purpose, without fee, and without a written agreement 8 | is hereby granted, provided that the above copyright notice and this paragraph 9 | and the following two paragraphs appear in all copies. 10 | 11 | IN NO EVENT SHALL Andrew Dunstan BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT, 12 | SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING LOST PROFITS, ARISING 13 | OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF Andrew Dunstan 14 | HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 15 | 16 | Andrew Dunstan SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT NOT 17 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 18 | PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, 19 | AND Andrew Dunstan HAS NO OBLIGATIONS TO PROVIDE MAINTENANCE, SUPPORT, 20 | UPDATES, ENHANCEMENTS, OR MODIFICATIONS. 21 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | ############################################################################## 2 | # 3 | # sqlite Foreign Data Wrapper for PostgreSQL 4 | # 5 | # Copyright (c) 2013 Andrew Dunstan 6 | # 7 | # This software is released under the PostgreSQL Licence 8 | # 9 | # Author: Andrew Dunstan 10 | # 11 | # IDENTIFICATION 12 | # sqlite_fdw/Makefile 13 | # 14 | ############################################################################## 15 | 16 | 17 | EXTENSION = sqlite_fdw 18 | EXTVERSION = $(shell grep default_version $(EXTENSION).control | sed -e "s/default_version[[:space:]]*=[[:space:]]*'\([^']*\)'/\1/") 19 | 20 | DATA = $(filter-out $(wildcard sql/*--*.sql),$(wildcard sql/*.sql)) 21 | DOCS = $(wildcard doc/*.md) 22 | USE_MODULE_DB = 1 23 | TESTS = $(wildcard test/sql/*.sql) 24 | REGRESS = $(patsubst test/sql/%.sql,%,$(TESTS)) 25 | REGRESS_OPTS = --inputdir=test --outputdir=test \ 26 | --load-language=plpgsql --load-extension=$(EXTENSION) 27 | MODULE_big = $(EXTENSION) 28 | OBJS = $(patsubst %.c,%.o,$(wildcard src/*.c)) 29 | PG_CONFIG = pg_config 30 | SHLIB_LINK := -lsqlite3 31 | 32 | all: sql/$(EXTENSION)--$(EXTVERSION).sql 33 | 34 | sql/$(EXTENSION)--$(EXTVERSION).sql: sql/$(EXTENSION).sql 35 | cp $< $@ 36 | 37 | DATA_built = sql/$(EXTENSION)--$(EXTVERSION).sql 38 | DATA = $(filter-out sql/$(EXTENSION)--$(EXTVERSION).sql, $(wildcard sql/*--*.sql)) 39 | EXTRA_CLEAN = sql/$(EXTENSION)--$(EXTVERSION).sql 40 | 41 | PGXS := $(shell $(PG_CONFIG) --pgxs) 42 | include $(PGXS) 43 | 44 | # we put all the tests in a test subdir, but pgxs expects us not to, darn it 45 | override pg_regress_clean_files = test/results/ test/regression.diffs test/regression.out tmp_check/ log/ 46 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | sqlite_fdw 2 | ========== 3 | 4 | Foreign Data Wrapper for sqlite 5 | 6 | This was my attempt to write a Foreign Data Wrapper. Unfortunately, it didn't go long. I may work on it again in the future, just to understand how to write a Foreign Data Wrapper. 7 | 8 | If you're looking for a sqlite Foreign Data Wrapper, you shouldn't use this one as it lacks important features. Instead, have a look at https://github.com/pgspider/sqlite_fdw.git. 9 | 10 | Compilation 11 | ----------- 12 | 13 | To use this FDW, you first need to compile it. You'll need pg_config and the usual build toolset. Then just launch: 14 | 15 |
16 | make
17 | make install
18 | 
19 | 20 | And you're good to go. 21 | 22 | Adding the extension 23 | -------------------- 24 | 25 | Connect to your database, and execute this query: 26 | 27 |
28 | CREATE EXTENSION sqlite_fdw;
29 | 
30 | 31 | Using it 32 | -------- 33 | 34 | You first need to add a server. It will have an option, the sqlite file path. It must be readable by the postgres process. 35 | 36 |
37 | CREATE SERVER sqlite_server
38 |   FOREIGN DATA WRAPPER sqlite_fdw
39 |   OPTIONS (database '/var/lib/pgsql/test.db');
40 | 
41 | 42 | Then you can create your foreign table. It will have one option, the table name on the sqlite database: 43 | 44 |
45 | CREATE FOREIGN TABLE local_t1(... columns ...)
46 |   SERVER sqlite_server
47 |   OPTIONS (table 'remote_table');
48 | 
49 | 50 | Since 9.5, you can also import the tables of a specific schema in your sqlite 51 | database, just like this : 52 | 53 |
54 | IMPORT FOREIGN SCHEMA public FROM SERVER sqlite_server INTO public;
55 | 
56 | 57 | Now, to get the contents of the remote table, you just need to execute a SELECT query on it: 58 | 59 |
60 | SELECT * FROM local_t1;
61 | 
62 | -------------------------------------------------------------------------------- /src/sqlite_fdw.c: -------------------------------------------------------------------------------- 1 | /*------------------------------------------------------------------------- 2 | * 3 | * sqlite Foreign Data Wrapper for PostgreSQL 4 | * 5 | * Copyright (c) 2013-2016 Guillaume Lelarge 6 | * 7 | * This software is released under the PostgreSQL Licence 8 | * 9 | * Author: Guillaume Lelarge 10 | * 11 | * IDENTIFICATION 12 | * sqlite_fdw/src/sqlite_fdw.c 13 | * 14 | *------------------------------------------------------------------------- 15 | */ 16 | 17 | #include "postgres.h" 18 | 19 | #include "access/reloptions.h" 20 | #include "foreign/fdwapi.h" 21 | #include "foreign/foreign.h" 22 | #include "optimizer/pathnode.h" 23 | #include "optimizer/planmain.h" 24 | #include "optimizer/restrictinfo.h" 25 | 26 | #include "funcapi.h" 27 | #include "catalog/pg_collation.h" 28 | #include "catalog/pg_foreign_server.h" 29 | #include "catalog/pg_foreign_table.h" 30 | #include "commands/defrem.h" 31 | #include "commands/explain.h" 32 | #include "utils/builtins.h" 33 | #include "utils/formatting.h" 34 | #include "utils/rel.h" 35 | #include "utils/lsyscache.h" 36 | 37 | #include 38 | #include 39 | 40 | PG_MODULE_MAGIC; 41 | 42 | /* 43 | * Default values 44 | */ 45 | /* ---- 46 | * This value is taken from sqlite 47 | (without stats, sqlite defaults to 1 million tuples for a table) 48 | */ 49 | #define DEFAULT_ESTIMATED_LINES 1000000 50 | 51 | /* 52 | * SQL functions 53 | */ 54 | extern Datum sqlite_fdw_handler(PG_FUNCTION_ARGS); 55 | extern Datum sqlite_fdw_validator(PG_FUNCTION_ARGS); 56 | 57 | PG_FUNCTION_INFO_V1(sqlite_fdw_handler); 58 | PG_FUNCTION_INFO_V1(sqlite_fdw_validator); 59 | 60 | 61 | /* callback functions */ 62 | #if (PG_VERSION_NUM >= 90200) 63 | static void sqliteGetForeignRelSize(PlannerInfo *root, 64 | RelOptInfo *baserel, 65 | Oid foreigntableid); 66 | 67 | static void sqliteGetForeignPaths(PlannerInfo *root, 68 | RelOptInfo *baserel, 69 | Oid foreigntableid); 70 | 71 | static ForeignScan *sqliteGetForeignPlan(PlannerInfo *root, 72 | RelOptInfo *baserel, 73 | Oid foreigntableid, 74 | ForeignPath *best_path, 75 | List *tlist, 76 | #if (PG_VERSION_NUM >= 90500) 77 | List *scan_clauses, 78 | Plan *outer_plan); 79 | #else 80 | List *scan_clauses); 81 | #endif 82 | #else 83 | static FdwPlan *sqlitePlanForeignScan(Oid foreigntableid, PlannerInfo *root, RelOptInfo *baserel); 84 | #endif 85 | 86 | static void sqliteBeginForeignScan(ForeignScanState *node, 87 | int eflags); 88 | 89 | static TupleTableSlot *sqliteIterateForeignScan(ForeignScanState *node); 90 | 91 | static void sqliteReScanForeignScan(ForeignScanState *node); 92 | 93 | static void sqliteEndForeignScan(ForeignScanState *node); 94 | 95 | #if (PG_VERSION_NUM >= 90300) 96 | static void sqliteAddForeignUpdateTargets(Query *parsetree, 97 | RangeTblEntry *target_rte, 98 | Relation target_relation); 99 | 100 | static List *sqlitePlanForeignModify(PlannerInfo *root, 101 | ModifyTable *plan, 102 | Index resultRelation, 103 | int subplan_index); 104 | 105 | static void sqliteBeginForeignModify(ModifyTableState *mtstate, 106 | ResultRelInfo *rinfo, 107 | List *fdw_private, 108 | int subplan_index, 109 | int eflags); 110 | 111 | static TupleTableSlot *sqliteExecForeignInsert(EState *estate, 112 | ResultRelInfo *rinfo, 113 | TupleTableSlot *slot, 114 | TupleTableSlot *planSlot); 115 | 116 | static TupleTableSlot *sqliteExecForeignUpdate(EState *estate, 117 | ResultRelInfo *rinfo, 118 | TupleTableSlot *slot, 119 | TupleTableSlot *planSlot); 120 | 121 | static TupleTableSlot *sqliteExecForeignDelete(EState *estate, 122 | ResultRelInfo *rinfo, 123 | TupleTableSlot *slot, 124 | TupleTableSlot *planSlot); 125 | 126 | static void sqliteEndForeignModify(EState *estate, 127 | ResultRelInfo *rinfo); 128 | #endif 129 | 130 | static void sqliteExplainForeignScan(ForeignScanState *node, 131 | struct ExplainState *es); 132 | 133 | #if (PG_VERSION_NUM >= 90300) 134 | static void sqliteExplainForeignModify(ModifyTableState *mtstate, 135 | ResultRelInfo *rinfo, 136 | List *fdw_private, 137 | int subplan_index, 138 | struct ExplainState *es); 139 | #endif 140 | 141 | #if (PG_VERSION_NUM >= 90200) 142 | static bool sqliteAnalyzeForeignTable(Relation relation, 143 | AcquireSampleRowsFunc *func, 144 | BlockNumber *totalpages); 145 | #endif 146 | 147 | #if (PG_VERSION_NUM >= 90500) 148 | static List *sqliteImportForeignSchema(ImportForeignSchemaStmt *stmt, 149 | Oid serverOid); 150 | #endif 151 | 152 | /* 153 | * Helper functions 154 | */ 155 | static void sqliteOpen(char *filename, sqlite3 **db); 156 | static void sqlitePrepare(sqlite3 *db, char *query, sqlite3_stmt **result, const char **pzTail); 157 | static bool sqliteIsValidOption(const char *option, Oid context); 158 | static void sqliteGetOptions(Oid foreigntableid, char **database, char **table); 159 | static int GetEstimatedRows(Oid foreigntableid); 160 | static bool file_exists(const char *name); 161 | #if (PG_VERSION_NUM >= 90500) 162 | /* only used for IMPORT FOREIGN SCHEMA */ 163 | static void sqliteTranslateType(StringInfo str, char *typname); 164 | #endif 165 | 166 | 167 | /* 168 | * structures used by the FDW 169 | * 170 | * These next two are not actually used by sqlite, but something like this 171 | * will be needed by anything more complicated that does actual work. 172 | * 173 | */ 174 | 175 | /* 176 | * Describes the valid options for objects that use this wrapper. 177 | */ 178 | struct SQLiteFdwOption 179 | { 180 | const char *optname; 181 | Oid optcontext; /* Oid of catalog in which option may appear */ 182 | }; 183 | 184 | /* 185 | * Describes the valid options for objects that use this wrapper. 186 | */ 187 | static struct SQLiteFdwOption valid_options[] = 188 | { 189 | 190 | /* Connection options */ 191 | { "database", ForeignServerRelationId }, 192 | 193 | /* Table options */ 194 | { "table", ForeignTableRelationId }, 195 | 196 | /* Sentinel */ 197 | { NULL, InvalidOid } 198 | }; 199 | 200 | /* 201 | * This is what will be set and stashed away in fdw_private and fetched 202 | * for subsequent routines. 203 | */ 204 | typedef struct 205 | { 206 | char *foo; 207 | int bar; 208 | } sqliteFdwPlanState; 209 | 210 | /* 211 | * FDW-specific information for ForeignScanState.fdw_state. 212 | */ 213 | 214 | typedef struct SQLiteFdwExecutionState 215 | { 216 | sqlite3 *conn; 217 | sqlite3_stmt *result; 218 | char *query; 219 | } SQLiteFdwExecutionState; 220 | 221 | 222 | Datum 223 | sqlite_fdw_handler(PG_FUNCTION_ARGS) 224 | { 225 | FdwRoutine *fdwroutine = makeNode(FdwRoutine); 226 | 227 | elog(DEBUG1,"entering function %s",__func__); 228 | 229 | /* assign the handlers for the FDW */ 230 | 231 | /* these are required */ 232 | #if (PG_VERSION_NUM >= 90200) 233 | fdwroutine->GetForeignRelSize = sqliteGetForeignRelSize; 234 | fdwroutine->GetForeignPaths = sqliteGetForeignPaths; 235 | fdwroutine->GetForeignPlan = sqliteGetForeignPlan; 236 | #else 237 | fdwroutine->PlanForeignScan = sqlitePlanForeignScan; 238 | #endif 239 | fdwroutine->BeginForeignScan = sqliteBeginForeignScan; 240 | fdwroutine->IterateForeignScan = sqliteIterateForeignScan; 241 | fdwroutine->ReScanForeignScan = sqliteReScanForeignScan; 242 | fdwroutine->EndForeignScan = sqliteEndForeignScan; 243 | 244 | /* remainder are optional - use NULL if not required */ 245 | /* support for insert / update / delete */ 246 | #if (PG_VERSION_NUM >= 90300) 247 | fdwroutine->AddForeignUpdateTargets = sqliteAddForeignUpdateTargets; 248 | fdwroutine->PlanForeignModify = sqlitePlanForeignModify; 249 | fdwroutine->BeginForeignModify = sqliteBeginForeignModify; 250 | fdwroutine->ExecForeignInsert = sqliteExecForeignInsert; 251 | fdwroutine->ExecForeignUpdate = sqliteExecForeignUpdate; 252 | fdwroutine->ExecForeignDelete = sqliteExecForeignDelete; 253 | fdwroutine->EndForeignModify = sqliteEndForeignModify; 254 | #endif 255 | 256 | /* support for EXPLAIN */ 257 | fdwroutine->ExplainForeignScan = sqliteExplainForeignScan; 258 | #if (PG_VERSION_NUM >= 90300) 259 | fdwroutine->ExplainForeignModify = sqliteExplainForeignModify; 260 | #endif 261 | 262 | #if (PG_VERSION_NUM >= 90200) 263 | /* support for ANALYSE */ 264 | fdwroutine->AnalyzeForeignTable = sqliteAnalyzeForeignTable; 265 | #endif 266 | 267 | #if (PG_VERSION_NUM >= 90500) 268 | /* support for IMPORT FOREIGN SCHEMA */ 269 | fdwroutine->ImportForeignSchema = sqliteImportForeignSchema; 270 | #endif 271 | 272 | PG_RETURN_POINTER(fdwroutine); 273 | } 274 | 275 | Datum 276 | sqlite_fdw_validator(PG_FUNCTION_ARGS) 277 | { 278 | List *options_list = untransformRelOptions(PG_GETARG_DATUM(0)); 279 | Oid catalog = PG_GETARG_OID(1); 280 | char *svr_database = NULL; 281 | char *svr_table = NULL; 282 | ListCell *cell; 283 | 284 | elog(DEBUG1,"entering function %s",__func__); 285 | 286 | /* 287 | * Check that only options supported by sqlite_fdw, 288 | * and allowed for the current object type, are given. 289 | */ 290 | foreach(cell, options_list) 291 | { 292 | DefElem *def = (DefElem *) lfirst(cell); 293 | 294 | if (!sqliteIsValidOption(def->defname, catalog)) 295 | { 296 | struct SQLiteFdwOption *opt; 297 | StringInfoData buf; 298 | 299 | /* 300 | * Unknown option specified, complain about it. Provide a hint 301 | * with list of valid options for the object. 302 | */ 303 | initStringInfo(&buf); 304 | for (opt = valid_options; opt->optname; opt++) 305 | { 306 | if (catalog == opt->optcontext) 307 | appendStringInfo(&buf, "%s%s", (buf.len > 0) ? ", " : "", 308 | opt->optname); 309 | } 310 | 311 | ereport(ERROR, 312 | (errcode(ERRCODE_FDW_INVALID_OPTION_NAME), 313 | errmsg("invalid option \"%s\"", def->defname), 314 | errhint("Valid options in this context are: %s", buf.len ? buf.data : "") 315 | )); 316 | } 317 | 318 | if (strcmp(def->defname, "database") == 0) 319 | { 320 | if (svr_database) 321 | ereport(ERROR, 322 | (errcode(ERRCODE_SYNTAX_ERROR), 323 | errmsg("redundant options: database (%s)", defGetString(def)) 324 | )); 325 | if (!file_exists(defGetString(def))) 326 | ereport(ERROR, 327 | (errcode_for_file_access(), 328 | errmsg("could not access file \"%s\"", defGetString(def)) 329 | )); 330 | 331 | svr_database = defGetString(def); 332 | } 333 | else if (strcmp(def->defname, "table") == 0) 334 | { 335 | if (svr_table) 336 | ereport(ERROR, 337 | (errcode(ERRCODE_SYNTAX_ERROR), 338 | errmsg("redundant options: table (%s)", defGetString(def)) 339 | )); 340 | 341 | svr_table = defGetString(def); 342 | } 343 | } 344 | 345 | /* Check we have the options we need to proceed */ 346 | if (catalog == ForeignServerRelationId && !svr_database) 347 | ereport(ERROR, 348 | (errcode(ERRCODE_SYNTAX_ERROR), 349 | errmsg("The database name must be specified") 350 | )); 351 | 352 | PG_RETURN_VOID(); 353 | } 354 | 355 | /* 356 | * Open the given sqlite3 file, and throw an error if the file couldn't be 357 | * opened 358 | */ 359 | static void 360 | sqliteOpen(char *filename, sqlite3 **db) 361 | { 362 | if (sqlite3_open(filename, db) != SQLITE_OK) { 363 | ereport(ERROR, 364 | (errcode(ERRCODE_FDW_OUT_OF_MEMORY), 365 | errmsg("Can't open sqlite database %s: %s", filename, sqlite3_errmsg(*db)) 366 | )); 367 | sqlite3_close(*db); 368 | } 369 | } 370 | /* 371 | * Prepare the given query. If case of error, close the db and throw an error 372 | */ 373 | static void 374 | sqlitePrepare(sqlite3 *db, char *query, sqlite3_stmt **result, 375 | const char **pzTail) 376 | { 377 | int rc; 378 | 379 | /* Execute the query */ 380 | rc = sqlite3_prepare(db, query, -1, result, pzTail); 381 | if (rc != SQLITE_OK) 382 | { 383 | ereport(ERROR, 384 | (errcode(ERRCODE_FDW_UNABLE_TO_CREATE_EXECUTION), 385 | errmsg("SQL error during prepare: %s", sqlite3_errmsg(db)) 386 | )); 387 | 388 | sqlite3_close(db); 389 | } 390 | } 391 | 392 | /* 393 | * Check if the provided option is one of the valid options. 394 | * context is the Oid of the catalog holding the object the option is for. 395 | */ 396 | static bool 397 | sqliteIsValidOption(const char *option, Oid context) 398 | { 399 | struct SQLiteFdwOption *opt; 400 | 401 | for (opt = valid_options; opt->optname; opt++) 402 | { 403 | if (context == opt->optcontext && strcmp(opt->optname, option) == 0) 404 | return true; 405 | } 406 | 407 | return false; 408 | } 409 | 410 | /* 411 | * Fetch the options for a mysql_fdw foreign table. 412 | */ 413 | static void 414 | sqliteGetOptions(Oid foreigntableid, char **database, char **table) 415 | { 416 | ForeignTable *f_table; 417 | ForeignServer *f_server; 418 | List *options; 419 | ListCell *lc; 420 | 421 | /* 422 | * Extract options from FDW objects. 423 | */ 424 | f_table = GetForeignTable(foreigntableid); 425 | f_server = GetForeignServer(f_table->serverid); 426 | 427 | options = NIL; 428 | options = list_concat(options, f_table->options); 429 | options = list_concat(options, f_server->options); 430 | 431 | /* Loop through the options */ 432 | foreach(lc, options) 433 | { 434 | DefElem *def = (DefElem *) lfirst(lc); 435 | 436 | if (strcmp(def->defname, "database") == 0) 437 | { 438 | *database = defGetString(def); 439 | } 440 | 441 | if (strcmp(def->defname, "table") == 0) 442 | { 443 | *table = defGetString(def); 444 | } 445 | } 446 | 447 | if (!*table) 448 | { 449 | *table = get_rel_name(foreigntableid); 450 | } 451 | 452 | /* Check we have the options we need to proceed */ 453 | if (!*database || !*table) 454 | ereport(ERROR, 455 | (errcode(ERRCODE_SYNTAX_ERROR), 456 | errmsg("a database and a table must be specified") 457 | )); 458 | } 459 | 460 | 461 | #if (PG_VERSION_NUM >= 90200) 462 | static void 463 | sqliteGetForeignRelSize(PlannerInfo *root, 464 | RelOptInfo *baserel, 465 | Oid foreigntableid) 466 | { 467 | /* 468 | * Obtain relation size estimates for a foreign table. This is called at 469 | * the beginning of planning for a query that scans a foreign table. root 470 | * is the planner's global information about the query; baserel is the 471 | * planner's information about this table; and foreigntableid is the 472 | * pg_class OID of the foreign table. (foreigntableid could be obtained 473 | * from the planner data structures, but it's passed explicitly to save 474 | * effort.) 475 | * 476 | * This function should update baserel->rows to be the expected number of 477 | * rows returned by the table scan, after accounting for the filtering 478 | * done by the restriction quals. The initial value of baserel->rows is 479 | * just a constant default estimate, which should be replaced if at all 480 | * possible. The function may also choose to update baserel->width if it 481 | * can compute a better estimate of the average result row width. 482 | */ 483 | sqliteFdwPlanState *fdw_private; 484 | 485 | elog(DEBUG1,"entering function %s",__func__); 486 | 487 | baserel->rows = 0; 488 | 489 | fdw_private = palloc0(sizeof(sqliteFdwPlanState)); 490 | baserel->fdw_private = (void *) fdw_private; 491 | 492 | /* initialize required state in fdw_private */ 493 | baserel->rows = GetEstimatedRows(foreigntableid); 494 | } 495 | 496 | static void 497 | sqliteGetForeignPaths(PlannerInfo *root, 498 | RelOptInfo *baserel, 499 | Oid foreigntableid) 500 | { 501 | /* 502 | * Create possible access paths for a scan on a foreign table. This is 503 | * called during query planning. The parameters are the same as for 504 | * GetForeignRelSize, which has already been called. 505 | * 506 | * This function must generate at least one access path (ForeignPath node) 507 | * for a scan on the foreign table and must call add_path to add each such 508 | * path to baserel->pathlist. It's recommended to use 509 | * create_foreignscan_path to build the ForeignPath nodes. The function 510 | * can generate multiple access paths, e.g., a path which has valid 511 | * pathkeys to represent a pre-sorted result. Each access path must 512 | * contain cost estimates, and can contain any FDW-private information 513 | * that is needed to identify the specific scan method intended. 514 | */ 515 | 516 | /* 517 | * sqliteFdwPlanState *fdw_private = baserel->fdw_private; 518 | */ 519 | 520 | Cost startup_cost, 521 | total_cost; 522 | 523 | elog(DEBUG1,"entering function %s",__func__); 524 | 525 | startup_cost = 0; 526 | total_cost = startup_cost + baserel->rows; 527 | 528 | /* Create a ForeignPath node and add it as only possible path */ 529 | add_path(baserel, (Path *) 530 | create_foreignscan_path(root, baserel, 531 | #if (PG_VERSION_NUM >= 90600) 532 | NULL, /* default pathtarget */ 533 | #endif 534 | baserel->rows, 535 | startup_cost, 536 | total_cost, 537 | NIL, /* no pathkeys */ 538 | NULL, /* no outer rel either */ 539 | #if (PG_VERSION_NUM >= 90500) 540 | NULL, /* no extra plan */ 541 | #endif 542 | NIL)); /* no fdw_private data */ 543 | } 544 | 545 | 546 | 547 | static ForeignScan * 548 | sqliteGetForeignPlan(PlannerInfo *root, 549 | RelOptInfo *baserel, 550 | Oid foreigntableid, 551 | ForeignPath *best_path, 552 | List *tlist, 553 | #if (PG_VERSION_NUM >= 90500) 554 | List *scan_clauses, 555 | Plan *outer_plan) 556 | #else 557 | List *scan_clauses) 558 | #endif 559 | { 560 | /* 561 | * Create a ForeignScan plan node from the selected foreign access path. 562 | * This is called at the end of query planning. The parameters are as for 563 | * GetForeignRelSize, plus the selected ForeignPath (previously produced 564 | * by GetForeignPaths), the target list to be emitted by the plan node, 565 | * and the restriction clauses to be enforced by the plan node. 566 | * 567 | * This function must create and return a ForeignScan plan node; it's 568 | * recommended to use make_foreignscan to build the ForeignScan node. 569 | * 570 | */ 571 | 572 | Index scan_relid = baserel->relid; 573 | 574 | /* 575 | * We have no native ability to evaluate restriction clauses, so we just 576 | * put all the scan_clauses into the plan node's qual list for the 577 | * executor to check. So all we have to do here is strip RestrictInfo 578 | * nodes from the clauses and ignore pseudoconstants (which will be 579 | * handled elsewhere). 580 | */ 581 | 582 | elog(DEBUG1,"entering function %s",__func__); 583 | 584 | scan_clauses = extract_actual_clauses(scan_clauses, false); 585 | 586 | /* Create the ForeignScan node */ 587 | return make_foreignscan(tlist, 588 | scan_clauses, 589 | scan_relid, 590 | NIL, /* no expressions to evaluate */ 591 | #if (PG_VERSION_NUM >= 90500) 592 | NIL, /* no private state */ 593 | NIL, /* no custom tlist */ 594 | NIL, /* no recheck quals */ 595 | NULL); /* no extra plan */ 596 | #else 597 | NIL); /* no private state */ 598 | #endif 599 | 600 | } 601 | #else 602 | static FdwPlan * 603 | sqlitePlanForeignScan(Oid foreigntableid, PlannerInfo *root, RelOptInfo *baserel) 604 | { 605 | FdwPlan *fdwplan; 606 | 607 | /* Construct FdwPlan with cost estimates. */ 608 | fdwplan = makeNode(FdwPlan); 609 | 610 | 611 | baserel->rows = GetEstimatedRows(foreigntableid); 612 | /* TODO: find a way to estimate the average row size */ 613 | /*baserel->width = ?; */ 614 | baserel->tuples = baserel->rows; 615 | 616 | fdwplan->startup_cost = 10; 617 | fdwplan->total_cost = baserel->rows + fdwplan->startup_cost; 618 | fdwplan->fdw_private = NIL; /* not used */ 619 | 620 | return fdwplan; 621 | } 622 | #endif 623 | 624 | 625 | static void 626 | sqliteBeginForeignScan(ForeignScanState *node, 627 | int eflags) 628 | { 629 | /* 630 | * Begin executing a foreign scan. This is called during executor startup. 631 | * It should perform any initialization needed before the scan can start, 632 | * but not start executing the actual scan (that should be done upon the 633 | * first call to IterateForeignScan). The ForeignScanState node has 634 | * already been created, but its fdw_state field is still NULL. 635 | * Information about the table to scan is accessible through the 636 | * ForeignScanState node (in particular, from the underlying ForeignScan 637 | * plan node, which contains any FDW-private information provided by 638 | * GetForeignPlan). eflags contains flag bits describing the executor's 639 | * operating mode for this plan node. 640 | * 641 | * Note that when (eflags & EXEC_FLAG_EXPLAIN_ONLY) is true, this function 642 | * should not perform any externally-visible actions; it should only do 643 | * the minimum required to make the node state valid for 644 | * ExplainForeignScan and EndForeignScan. 645 | * 646 | */ 647 | sqlite3 *db; 648 | SQLiteFdwExecutionState *festate; 649 | char *svr_database = NULL; 650 | char *svr_table = NULL; 651 | char *query; 652 | size_t len; 653 | 654 | elog(DEBUG1,"entering function %s",__func__); 655 | 656 | /* Fetch options */ 657 | sqliteGetOptions(RelationGetRelid(node->ss.ss_currentRelation), &svr_database, &svr_table); 658 | 659 | /* Connect to the server */ 660 | sqliteOpen(svr_database, &db); 661 | 662 | /* Build the query */ 663 | len = strlen(svr_table) + 15; 664 | query = (char *)palloc(len); 665 | snprintf(query, len, "SELECT * FROM %s", svr_table); 666 | 667 | /* Stash away the state info we have already */ 668 | festate = (SQLiteFdwExecutionState *) palloc(sizeof(SQLiteFdwExecutionState)); 669 | node->fdw_state = (void *) festate; 670 | festate->conn = db; 671 | festate->result = NULL; 672 | festate->query = query; 673 | } 674 | 675 | 676 | static TupleTableSlot * 677 | sqliteIterateForeignScan(ForeignScanState *node) 678 | { 679 | /* 680 | * Fetch one row from the foreign source, returning it in a tuple table 681 | * slot (the node's ScanTupleSlot should be used for this purpose). Return 682 | * NULL if no more rows are available. The tuple table slot infrastructure 683 | * allows either a physical or virtual tuple to be returned; in most cases 684 | * the latter choice is preferable from a performance standpoint. Note 685 | * that this is called in a short-lived memory context that will be reset 686 | * between invocations. Create a memory context in BeginForeignScan if you 687 | * need longer-lived storage, or use the es_query_cxt of the node's 688 | * EState. 689 | * 690 | * The rows returned must match the column signature of the foreign table 691 | * being scanned. If you choose to optimize away fetching columns that are 692 | * not needed, you should insert nulls in those column positions. 693 | * 694 | * Note that PostgreSQL's executor doesn't care whether the rows returned 695 | * violate any NOT NULL constraints that were defined on the foreign table 696 | * columns — but the planner does care, and may optimize queries 697 | * incorrectly if NULL values are present in a column declared not to 698 | * contain them. If a NULL value is encountered when the user has declared 699 | * that none should be present, it may be appropriate to raise an error 700 | * (just as you would need to do in the case of a data type mismatch). 701 | */ 702 | 703 | char **values; 704 | HeapTuple tuple; 705 | int x; 706 | const char *pzTail; 707 | 708 | SQLiteFdwExecutionState *festate = (SQLiteFdwExecutionState *) node->fdw_state; 709 | TupleTableSlot *slot = node->ss.ss_ScanTupleSlot; 710 | 711 | elog(DEBUG1,"entering function %s",__func__); 712 | 713 | /* Execute the query, if required */ 714 | if (!festate->result) 715 | { 716 | sqlitePrepare(festate->conn, festate->query, &festate->result, &pzTail); 717 | } 718 | 719 | ExecClearTuple(slot); 720 | 721 | /* get the next record, if any, and fill in the slot */ 722 | if (sqlite3_step(festate->result) == SQLITE_ROW) 723 | { 724 | /* Build the tuple */ 725 | values = (char **) palloc(sizeof(char *) * sqlite3_column_count(festate->result)); 726 | 727 | for (x = 0; x < sqlite3_column_count(festate->result); x++) 728 | { 729 | values[x] = (char *) sqlite3_column_text(festate->result, x); 730 | } 731 | 732 | tuple = BuildTupleFromCStrings(TupleDescGetAttInMetadata(node->ss.ss_currentRelation->rd_att), values); 733 | ExecStoreTuple(tuple, slot, InvalidBuffer, false); 734 | } 735 | 736 | /* then return the slot */ 737 | return slot; 738 | } 739 | 740 | 741 | static void 742 | sqliteReScanForeignScan(ForeignScanState *node) 743 | { 744 | /* 745 | * Restart the scan from the beginning. Note that any parameters the scan 746 | * depends on may have changed value, so the new scan does not necessarily 747 | * return exactly the same rows. 748 | */ 749 | 750 | elog(DEBUG1,"entering function %s",__func__); 751 | 752 | } 753 | 754 | 755 | static void 756 | sqliteEndForeignScan(ForeignScanState *node) 757 | { 758 | /* 759 | * End the scan and release resources. It is normally not important to 760 | * release palloc'd memory, but for example open files and connections to 761 | * remote servers should be cleaned up. 762 | */ 763 | 764 | SQLiteFdwExecutionState *festate = (SQLiteFdwExecutionState *) node->fdw_state; 765 | 766 | elog(DEBUG1,"entering function %s",__func__); 767 | 768 | if (festate->result) 769 | { 770 | sqlite3_finalize(festate->result); 771 | festate->result = NULL; 772 | } 773 | 774 | if (festate->conn) 775 | { 776 | sqlite3_close(festate->conn); 777 | festate->conn = NULL; 778 | } 779 | 780 | if (festate->query) 781 | { 782 | pfree(festate->query); 783 | festate->query = 0; 784 | } 785 | 786 | } 787 | 788 | 789 | #if (PG_VERSION_NUM >= 90300) 790 | static void 791 | sqliteAddForeignUpdateTargets(Query *parsetree, 792 | RangeTblEntry *target_rte, 793 | Relation target_relation) 794 | { 795 | /* 796 | * UPDATE and DELETE operations are performed against rows previously 797 | * fetched by the table-scanning functions. The FDW may need extra 798 | * information, such as a row ID or the values of primary-key columns, to 799 | * ensure that it can identify the exact row to update or delete. To 800 | * support that, this function can add extra hidden, or "junk", target 801 | * columns to the list of columns that are to be retrieved from the 802 | * foreign table during an UPDATE or DELETE. 803 | * 804 | * To do that, add TargetEntry items to parsetree->targetList, containing 805 | * expressions for the extra values to be fetched. Each such entry must be 806 | * marked resjunk = true, and must have a distinct resname that will 807 | * identify it at execution time. Avoid using names matching ctidN or 808 | * wholerowN, as the core system can generate junk columns of these names. 809 | * 810 | * This function is called in the rewriter, not the planner, so the 811 | * information available is a bit different from that available to the 812 | * planning routines. parsetree is the parse tree for the UPDATE or DELETE 813 | * command, while target_rte and target_relation describe the target 814 | * foreign table. 815 | * 816 | * If the AddForeignUpdateTargets pointer is set to NULL, no extra target 817 | * expressions are added. (This will make it impossible to implement 818 | * DELETE operations, though UPDATE may still be feasible if the FDW 819 | * relies on an unchanging primary key to identify rows.) 820 | */ 821 | 822 | elog(DEBUG1,"entering function %s",__func__); 823 | 824 | } 825 | 826 | 827 | static List * 828 | sqlitePlanForeignModify(PlannerInfo *root, 829 | ModifyTable *plan, 830 | Index resultRelation, 831 | int subplan_index) 832 | { 833 | /* 834 | * Perform any additional planning actions needed for an insert, update, 835 | * or delete on a foreign table. This function generates the FDW-private 836 | * information that will be attached to the ModifyTable plan node that 837 | * performs the update action. This private information must have the form 838 | * of a List, and will be delivered to BeginForeignModify during the 839 | * execution stage. 840 | * 841 | * root is the planner's global information about the query. plan is the 842 | * ModifyTable plan node, which is complete except for the fdwPrivLists 843 | * field. resultRelation identifies the target foreign table by its 844 | * rangetable index. subplan_index identifies which target of the 845 | * ModifyTable plan node this is, counting from zero; use this if you want 846 | * to index into plan->plans or other substructure of the plan node. 847 | * 848 | * If the PlanForeignModify pointer is set to NULL, no additional 849 | * plan-time actions are taken, and the fdw_private list delivered to 850 | * BeginForeignModify will be NIL. 851 | */ 852 | 853 | elog(DEBUG1,"entering function %s",__func__); 854 | 855 | 856 | return NULL; 857 | } 858 | 859 | 860 | static void 861 | sqliteBeginForeignModify(ModifyTableState *mtstate, 862 | ResultRelInfo *rinfo, 863 | List *fdw_private, 864 | int subplan_index, 865 | int eflags) 866 | { 867 | /* 868 | * Begin executing a foreign table modification operation. This routine is 869 | * called during executor startup. It should perform any initialization 870 | * needed prior to the actual table modifications. Subsequently, 871 | * ExecForeignInsert, ExecForeignUpdate or ExecForeignDelete will be 872 | * called for each tuple to be inserted, updated, or deleted. 873 | * 874 | * mtstate is the overall state of the ModifyTable plan node being 875 | * executed; global data about the plan and execution state is available 876 | * via this structure. rinfo is the ResultRelInfo struct describing the 877 | * target foreign table. (The ri_FdwState field of ResultRelInfo is 878 | * available for the FDW to store any private state it needs for this 879 | * operation.) fdw_private contains the private data generated by 880 | * PlanForeignModify, if any. subplan_index identifies which target of the 881 | * ModifyTable plan node this is. eflags contains flag bits describing the 882 | * executor's operating mode for this plan node. 883 | * 884 | * Note that when (eflags & EXEC_FLAG_EXPLAIN_ONLY) is true, this function 885 | * should not perform any externally-visible actions; it should only do 886 | * the minimum required to make the node state valid for 887 | * ExplainForeignModify and EndForeignModify. 888 | * 889 | * If the BeginForeignModify pointer is set to NULL, no action is taken 890 | * during executor startup. 891 | */ 892 | 893 | elog(DEBUG1,"entering function %s",__func__); 894 | 895 | } 896 | 897 | 898 | static TupleTableSlot * 899 | sqliteExecForeignInsert(EState *estate, 900 | ResultRelInfo *rinfo, 901 | TupleTableSlot *slot, 902 | TupleTableSlot *planSlot) 903 | { 904 | /* 905 | * Insert one tuple into the foreign table. estate is global execution 906 | * state for the query. rinfo is the ResultRelInfo struct describing the 907 | * target foreign table. slot contains the tuple to be inserted; it will 908 | * match the rowtype definition of the foreign table. planSlot contains 909 | * the tuple that was generated by the ModifyTable plan node's subplan; it 910 | * differs from slot in possibly containing additional "junk" columns. 911 | * (The planSlot is typically of little interest for INSERT cases, but is 912 | * provided for completeness.) 913 | * 914 | * The return value is either a slot containing the data that was actually 915 | * inserted (this might differ from the data supplied, for example as a 916 | * result of trigger actions), or NULL if no row was actually inserted 917 | * (again, typically as a result of triggers). The passed-in slot can be 918 | * re-used for this purpose. 919 | * 920 | * The data in the returned slot is used only if the INSERT query has a 921 | * RETURNING clause. Hence, the FDW could choose to optimize away 922 | * returning some or all columns depending on the contents of the 923 | * RETURNING clause. However, some slot must be returned to indicate 924 | * success, or the query's reported rowcount will be wrong. 925 | * 926 | * If the ExecForeignInsert pointer is set to NULL, attempts to insert 927 | * into the foreign table will fail with an error message. 928 | * 929 | */ 930 | 931 | elog(DEBUG1,"entering function %s",__func__); 932 | 933 | return slot; 934 | } 935 | 936 | 937 | static TupleTableSlot * 938 | sqliteExecForeignUpdate(EState *estate, 939 | ResultRelInfo *rinfo, 940 | TupleTableSlot *slot, 941 | TupleTableSlot *planSlot) 942 | { 943 | /* 944 | * Update one tuple in the foreign table. estate is global execution state 945 | * for the query. rinfo is the ResultRelInfo struct describing the target 946 | * foreign table. slot contains the new data for the tuple; it will match 947 | * the rowtype definition of the foreign table. planSlot contains the 948 | * tuple that was generated by the ModifyTable plan node's subplan; it 949 | * differs from slot in possibly containing additional "junk" columns. In 950 | * particular, any junk columns that were requested by 951 | * AddForeignUpdateTargets will be available from this slot. 952 | * 953 | * The return value is either a slot containing the row as it was actually 954 | * updated (this might differ from the data supplied, for example as a 955 | * result of trigger actions), or NULL if no row was actually updated 956 | * (again, typically as a result of triggers). The passed-in slot can be 957 | * re-used for this purpose. 958 | * 959 | * The data in the returned slot is used only if the UPDATE query has a 960 | * RETURNING clause. Hence, the FDW could choose to optimize away 961 | * returning some or all columns depending on the contents of the 962 | * RETURNING clause. However, some slot must be returned to indicate 963 | * success, or the query's reported rowcount will be wrong. 964 | * 965 | * If the ExecForeignUpdate pointer is set to NULL, attempts to update the 966 | * foreign table will fail with an error message. 967 | * 968 | */ 969 | 970 | elog(DEBUG1,"entering function %s",__func__); 971 | 972 | return slot; 973 | } 974 | 975 | 976 | static TupleTableSlot * 977 | sqliteExecForeignDelete(EState *estate, 978 | ResultRelInfo *rinfo, 979 | TupleTableSlot *slot, 980 | TupleTableSlot *planSlot) 981 | { 982 | /* 983 | * Delete one tuple from the foreign table. estate is global execution 984 | * state for the query. rinfo is the ResultRelInfo struct describing the 985 | * target foreign table. slot contains nothing useful upon call, but can 986 | * be used to hold the returned tuple. planSlot contains the tuple that 987 | * was generated by the ModifyTable plan node's subplan; in particular, it 988 | * will carry any junk columns that were requested by 989 | * AddForeignUpdateTargets. The junk column(s) must be used to identify 990 | * the tuple to be deleted. 991 | * 992 | * The return value is either a slot containing the row that was deleted, 993 | * or NULL if no row was deleted (typically as a result of triggers). The 994 | * passed-in slot can be used to hold the tuple to be returned. 995 | * 996 | * The data in the returned slot is used only if the DELETE query has a 997 | * RETURNING clause. Hence, the FDW could choose to optimize away 998 | * returning some or all columns depending on the contents of the 999 | * RETURNING clause. However, some slot must be returned to indicate 1000 | * success, or the query's reported rowcount will be wrong. 1001 | * 1002 | * If the ExecForeignDelete pointer is set to NULL, attempts to delete 1003 | * from the foreign table will fail with an error message. 1004 | */ 1005 | 1006 | elog(DEBUG1,"entering function %s",__func__); 1007 | 1008 | return slot; 1009 | } 1010 | 1011 | 1012 | static void 1013 | sqliteEndForeignModify(EState *estate, 1014 | ResultRelInfo *rinfo) 1015 | { 1016 | /* 1017 | * End the table update and release resources. It is normally not 1018 | * important to release palloc'd memory, but for example open files and 1019 | * connections to remote servers should be cleaned up. 1020 | * 1021 | * If the EndForeignModify pointer is set to NULL, no action is taken 1022 | * during executor shutdown. 1023 | */ 1024 | 1025 | elog(DEBUG1,"entering function %s",__func__); 1026 | 1027 | } 1028 | #endif 1029 | 1030 | 1031 | static void 1032 | sqliteExplainForeignScan(ForeignScanState *node, 1033 | struct ExplainState *es) 1034 | { 1035 | /* 1036 | * Print additional EXPLAIN output for a foreign table scan. This function 1037 | * can call ExplainPropertyText and related functions to add fields to the 1038 | * EXPLAIN output. The flag fields in es can be used to determine what to 1039 | * print, and the state of the ForeignScanState node can be inspected to 1040 | * provide run-time statistics in the EXPLAIN ANALYZE case. 1041 | * 1042 | * If the ExplainForeignScan pointer is set to NULL, no additional 1043 | * information is printed during EXPLAIN. 1044 | */ 1045 | sqlite3 *db; 1046 | sqlite3_stmt *result; 1047 | char *svr_database = NULL; 1048 | char *svr_table = NULL; 1049 | char *query; 1050 | size_t len; 1051 | const char *pzTail; 1052 | SQLiteFdwExecutionState *festate = (SQLiteFdwExecutionState *) node->fdw_state; 1053 | 1054 | elog(DEBUG1,"entering function %s",__func__); 1055 | 1056 | /* Show the query (only if VERBOSE) */ 1057 | if (es->verbose) 1058 | { 1059 | /* show query */ 1060 | ExplainPropertyText("sqlite query", festate->query, es); 1061 | } 1062 | 1063 | /* Fetch options */ 1064 | sqliteGetOptions(RelationGetRelid(node->ss.ss_currentRelation), &svr_database, &svr_table); 1065 | 1066 | /* Connect to the server */ 1067 | sqliteOpen(svr_database, &db); 1068 | 1069 | /* Build the query */ 1070 | len = strlen(festate->query) + 20; 1071 | query = (char *)palloc(len); 1072 | snprintf(query, len, "EXPLAIN QUERY PLAN %s", festate->query); 1073 | 1074 | /* Execute the query */ 1075 | sqlitePrepare(db, query, &result, &pzTail); 1076 | 1077 | /* get the next record, if any, and fill in the slot */ 1078 | while (sqlite3_step(result) == SQLITE_ROW) 1079 | { 1080 | /* 1081 | * I don't keep the three first columns; 1082 | it could be a good idea to add that later 1083 | */ 1084 | /* 1085 | * for (x = 0; x < sqlite3_column_count(festate->result); x++) 1086 | * { 1087 | */ 1088 | ExplainPropertyText("sqlite plan", (char*)sqlite3_column_text(result, 3), es); 1089 | /* } */ 1090 | } 1091 | 1092 | /* Free the query results */ 1093 | sqlite3_finalize(result); 1094 | 1095 | /* Close temporary connection */ 1096 | sqlite3_close(db); 1097 | 1098 | } 1099 | 1100 | 1101 | #if (PG_VERSION_NUM >= 90300) 1102 | static void 1103 | sqliteExplainForeignModify(ModifyTableState *mtstate, 1104 | ResultRelInfo *rinfo, 1105 | List *fdw_private, 1106 | int subplan_index, 1107 | struct ExplainState *es) 1108 | { 1109 | /* 1110 | * Print additional EXPLAIN output for a foreign table update. This 1111 | * function can call ExplainPropertyText and related functions to add 1112 | * fields to the EXPLAIN output. The flag fields in es can be used to 1113 | * determine what to print, and the state of the ModifyTableState node can 1114 | * be inspected to provide run-time statistics in the EXPLAIN ANALYZE 1115 | * case. The first four arguments are the same as for BeginForeignModify. 1116 | * 1117 | * If the ExplainForeignModify pointer is set to NULL, no additional 1118 | * information is printed during EXPLAIN. 1119 | */ 1120 | 1121 | elog(DEBUG1,"entering function %s",__func__); 1122 | 1123 | } 1124 | #endif 1125 | 1126 | 1127 | #if (PG_VERSION_NUM >= 90200) 1128 | static bool 1129 | sqliteAnalyzeForeignTable(Relation relation, 1130 | AcquireSampleRowsFunc *func, 1131 | BlockNumber *totalpages) 1132 | { 1133 | /* ---- 1134 | * This function is called when ANALYZE is executed on a foreign table. If 1135 | * the FDW can collect statistics for this foreign table, it should return 1136 | * true, and provide a pointer to a function that will collect sample rows 1137 | * from the table in func, plus the estimated size of the table in pages 1138 | * in totalpages. Otherwise, return false. 1139 | * 1140 | * If the FDW does not support collecting statistics for any tables, the 1141 | * AnalyzeForeignTable pointer can be set to NULL. 1142 | * 1143 | * If provided, the sample collection function must have the signature: 1144 | * 1145 | * int 1146 | * AcquireSampleRowsFunc (Relation relation, int elevel, 1147 | * HeapTuple *rows, int targrows, 1148 | * double *totalrows, 1149 | * double *totaldeadrows); 1150 | * 1151 | * A random sample of up to targrows rows should be collected from the 1152 | * table and stored into the caller-provided rows array. The actual number 1153 | * of rows collected must be returned. In addition, store estimates of the 1154 | * total numbers of live and dead rows in the table into the output 1155 | * parameters totalrows and totaldeadrows. (Set totaldeadrows to zero if 1156 | * the FDW does not have any concept of dead rows.) 1157 | * ---- 1158 | */ 1159 | 1160 | elog(DEBUG1,"entering function %s",__func__); 1161 | 1162 | return false; 1163 | } 1164 | #endif 1165 | 1166 | #if (PG_VERSION_NUM >= 90500) 1167 | static List * 1168 | sqliteImportForeignSchema(ImportForeignSchemaStmt *stmt, 1169 | Oid serverOid) 1170 | { 1171 | sqlite3 *volatile db = NULL; 1172 | sqlite3_stmt *volatile tbls = NULL; 1173 | ForeignServer *f_server; 1174 | ListCell *lc; 1175 | char *svr_database = NULL; 1176 | StringInfoData query_tbl; 1177 | const char *pzTail; 1178 | List *commands = NIL; 1179 | bool import_default = false; 1180 | bool import_not_null = true; 1181 | 1182 | elog(DEBUG1,"entering function %s",__func__); 1183 | 1184 | /* 1185 | * The only legit sqlite schema are temp and main (or name of an attached 1186 | * database, which can't happen here). Authorize only legit "main" schema, 1187 | * and "public" just in case. 1188 | */ 1189 | if (strcmp(stmt->remote_schema, "public") != 0 && 1190 | strcmp(stmt->remote_schema, "main") != 0) 1191 | { 1192 | ereport(ERROR, 1193 | (errcode(ERRCODE_FDW_SCHEMA_NOT_FOUND), 1194 | errmsg("Foreign schema \"%s\" is invalid", stmt->remote_schema) 1195 | )); 1196 | } 1197 | 1198 | /* Parse statement options */ 1199 | foreach(lc, stmt->options) 1200 | { 1201 | DefElem *def = (DefElem *) lfirst(lc); 1202 | 1203 | if (strcmp(def->defname, "import_default") == 0) 1204 | import_default = defGetBoolean(def); 1205 | else if (strcmp(def->defname, "import_not_null") == 0) 1206 | import_not_null = defGetBoolean(def); 1207 | else 1208 | ereport(ERROR, 1209 | (errcode(ERRCODE_FDW_INVALID_OPTION_NAME), 1210 | errmsg("invalid option \"%s\"", def->defname))); 1211 | } 1212 | 1213 | /* get the db filename */ 1214 | f_server = GetForeignServerByName(stmt->server_name, false); 1215 | foreach(lc, f_server->options) 1216 | { 1217 | DefElem *def = (DefElem *) lfirst(lc); 1218 | 1219 | if (strcmp(def->defname, "database") == 0) 1220 | { 1221 | svr_database = defGetString(def); 1222 | break; 1223 | } 1224 | } 1225 | 1226 | Assert(svr_database); 1227 | 1228 | /* Connect to the server */ 1229 | sqliteOpen(svr_database, (sqlite3 **) &db); 1230 | 1231 | PG_TRY(); 1232 | { 1233 | /* You want all tables, except system tables */ 1234 | initStringInfo(&query_tbl); 1235 | appendStringInfo(&query_tbl, "SELECT name FROM sqlite_master WHERE type = 'table'"); 1236 | appendStringInfo(&query_tbl, " AND name NOT LIKE 'sqlite_%%'"); 1237 | 1238 | /* Handle LIMIT TO / EXCEPT clauses in IMPORT FOREIGN SCHEMA statement */ 1239 | if (stmt->list_type == FDW_IMPORT_SCHEMA_LIMIT_TO || 1240 | stmt->list_type == FDW_IMPORT_SCHEMA_EXCEPT) 1241 | { 1242 | bool first_item = true; 1243 | 1244 | appendStringInfoString(&query_tbl, " AND name "); 1245 | if (stmt->list_type == FDW_IMPORT_SCHEMA_EXCEPT) 1246 | appendStringInfoString(&query_tbl, "NOT "); 1247 | appendStringInfoString(&query_tbl, "IN ("); 1248 | 1249 | foreach(lc, stmt->table_list) 1250 | { 1251 | RangeVar *rv = (RangeVar *) lfirst(lc); 1252 | 1253 | if (first_item) 1254 | first_item = false; 1255 | else 1256 | appendStringInfoString(&query_tbl, ", "); 1257 | 1258 | appendStringInfoString(&query_tbl, quote_literal_cstr(rv->relname)); 1259 | } 1260 | appendStringInfoChar(&query_tbl, ')'); 1261 | } 1262 | 1263 | /* Iterate to all matching tables, and get their definition */ 1264 | sqlitePrepare(db, query_tbl.data, (sqlite3_stmt **) &tbls, &pzTail); 1265 | while (sqlite3_step(tbls) == SQLITE_ROW) 1266 | { 1267 | sqlite3_stmt *cols; 1268 | char *tbl_name; 1269 | char *query_cols; 1270 | StringInfoData cft_stmt; 1271 | int i = 0; 1272 | 1273 | tbl_name = (char *) sqlite3_column_text(tbls, 0); 1274 | 1275 | /* 1276 | * user-defined list of tables has been handled in main query, don't 1277 | * try to do the job here again 1278 | */ 1279 | 1280 | /* start building the CFT stmt */ 1281 | initStringInfo(&cft_stmt); 1282 | appendStringInfo(&cft_stmt, "CREATE FOREIGN TABLE %s.%s (\n", 1283 | stmt->local_schema, quote_identifier(tbl_name)); 1284 | 1285 | query_cols = palloc0(strlen(tbl_name) + 19 + 1); 1286 | sprintf(query_cols, "PRAGMA table_info(%s)", tbl_name); 1287 | 1288 | sqlitePrepare(db, query_cols, &cols, &pzTail); 1289 | while (sqlite3_step(cols) == SQLITE_ROW) 1290 | { 1291 | char *col_name; 1292 | char *typ_name; 1293 | bool not_null; 1294 | char *default_val; 1295 | 1296 | col_name = (char *) sqlite3_column_text(cols, 1); 1297 | typ_name = (char *) sqlite3_column_text(cols, 2); 1298 | not_null = (sqlite3_column_int(cols, 3) == 1); 1299 | default_val = (char *) sqlite3_column_text(cols, 4); 1300 | 1301 | if (i != 0) 1302 | appendStringInfo(&cft_stmt, ",\n"); 1303 | 1304 | /* table name */ 1305 | appendStringInfo(&cft_stmt, "%s ", 1306 | quote_identifier(col_name)); 1307 | 1308 | /* translated datatype */ 1309 | sqliteTranslateType(&cft_stmt, typ_name); 1310 | 1311 | if (not_null && import_not_null) 1312 | appendStringInfo(&cft_stmt, " NOT NULL"); 1313 | 1314 | if (default_val && import_default) 1315 | appendStringInfo(&cft_stmt, " DEFAULT %s", default_val); 1316 | 1317 | i++; 1318 | } 1319 | appendStringInfo(&cft_stmt, "\n) SERVER %s\n" 1320 | "OPTIONS (table '%s')", 1321 | quote_identifier(stmt->server_name), 1322 | quote_identifier(tbl_name)); 1323 | 1324 | commands = lappend(commands, 1325 | pstrdup(cft_stmt.data)); 1326 | 1327 | /* free per-table allocated data */ 1328 | pfree(query_cols); 1329 | pfree(cft_stmt.data); 1330 | } 1331 | 1332 | /* Free all needed data and close connection*/ 1333 | pfree(query_tbl.data); 1334 | } 1335 | PG_CATCH(); 1336 | { 1337 | if (tbls) 1338 | sqlite3_finalize(tbls); 1339 | if (db) 1340 | sqlite3_close(db); 1341 | 1342 | PG_RE_THROW(); 1343 | } 1344 | PG_END_TRY(); 1345 | 1346 | sqlite3_finalize(tbls); 1347 | sqlite3_close(db); 1348 | 1349 | return commands; 1350 | } 1351 | #endif 1352 | 1353 | static int 1354 | GetEstimatedRows(Oid foreigntableid) 1355 | { 1356 | sqlite3 *db; 1357 | sqlite3_stmt *result; 1358 | char *svr_database = NULL; 1359 | char *svr_table = NULL; 1360 | char *query; 1361 | size_t len; 1362 | const char *pzTail; 1363 | 1364 | int rows = 0; 1365 | 1366 | /* Fetch options */ 1367 | sqliteGetOptions(foreigntableid, &svr_database, &svr_table); 1368 | 1369 | /* Connect to the server */ 1370 | sqliteOpen(svr_database, &db); 1371 | 1372 | /* Check that the sqlite_stat1 table exists */ 1373 | sqlitePrepare(db, "SELECT 1 FROM sqlite_master WHERE name='sqlite_stat1'", &result, &pzTail); 1374 | 1375 | if (sqlite3_step(result) != SQLITE_ROW) 1376 | { 1377 | ereport(WARNING, 1378 | (errcode(ERRCODE_FDW_TABLE_NOT_FOUND), 1379 | errmsg("The sqlite3 database has not been analyzed."), 1380 | errhint("Run ANALYZE on table \"%s\", database \"%s\".", svr_table, svr_database) 1381 | )); 1382 | rows = 10; 1383 | } 1384 | 1385 | /* Free the query results */ 1386 | sqlite3_finalize(result); 1387 | 1388 | if (rows == 0) 1389 | { 1390 | /* Build the query */ 1391 | len = strlen(svr_table) + 60; 1392 | query = (char *)palloc(len); 1393 | snprintf(query, len, "SELECT stat FROM sqlite_stat1 WHERE tbl='%s' AND idx IS NULL", svr_table); 1394 | elog(LOG, "query:%s", query); 1395 | 1396 | /* Execute the query */ 1397 | sqlitePrepare(db, query, &result, &pzTail); 1398 | 1399 | /* get the next record, if any, and fill in the slot */ 1400 | if (sqlite3_step(result) == SQLITE_ROW) 1401 | { 1402 | rows = sqlite3_column_int(result, 0); 1403 | } 1404 | 1405 | /* Free the query results */ 1406 | sqlite3_finalize(result); 1407 | } 1408 | else 1409 | { 1410 | /* 1411 | * The sqlite database doesn't have any statistics. 1412 | * There's not much we can do, except using a hardcoded one. 1413 | * Using a foreign table option might be a better solution. 1414 | */ 1415 | rows = DEFAULT_ESTIMATED_LINES; 1416 | } 1417 | 1418 | /* Close temporary connection */ 1419 | sqlite3_close(db); 1420 | 1421 | return rows; 1422 | } 1423 | 1424 | static bool 1425 | file_exists(const char *name) 1426 | { 1427 | struct stat st; 1428 | 1429 | AssertArg(name != NULL); 1430 | 1431 | if (stat(name, &st) == 0) 1432 | return S_ISDIR(st.st_mode) ? false : true; 1433 | else if (!(errno == ENOENT || errno == ENOTDIR || errno == EACCES)) 1434 | ereport(ERROR, 1435 | (errcode_for_file_access(), 1436 | errmsg("could not access file \"%s\": %m", name))); 1437 | 1438 | return false; 1439 | } 1440 | 1441 | #if (PG_VERSION_NUM >= 90500) 1442 | /* 1443 | * Translate sqlite type name to postgres compatible one and append it to 1444 | * given StringInfo 1445 | */ 1446 | static void 1447 | sqliteTranslateType(StringInfo str, char *typname) 1448 | { 1449 | char *type; 1450 | 1451 | /* 1452 | * get lowercase typname. We use C collation since the original type name 1453 | * should not contain exotic character. 1454 | */ 1455 | type = str_tolower(typname, strlen(typname), C_COLLATION_OID); 1456 | 1457 | /* try some easy conversion, from https://www.sqlite.org/datatype3.html */ 1458 | if (strcmp(type, "tinyint") == 0) 1459 | appendStringInfoString(str, "smallint"); 1460 | 1461 | else if (strcmp(type, "mediumint") == 0) 1462 | appendStringInfoString(str, "integer"); 1463 | 1464 | else if (strcmp(type, "unsigned big int") == 0) 1465 | appendStringInfoString(str, "bigint"); 1466 | 1467 | else if (strcmp(type, "double") == 0) 1468 | appendStringInfoString(str, "double precision"); 1469 | 1470 | else if (strcmp(type, "datetime") == 0) 1471 | appendStringInfoString(str, "timestamp"); 1472 | 1473 | else if (strcmp(type, "nvarchar text") == 0) 1474 | appendStringInfoString(str, "text"); 1475 | 1476 | else if (strcmp(type, "longvarchar") == 0) 1477 | appendStringInfoString(str, "text"); 1478 | 1479 | else if (strncmp(type, "text", 4) == 0) 1480 | appendStringInfoString(str, "text"); 1481 | 1482 | else if (strcmp(type, "blob") == 0) 1483 | appendStringInfoString(str, "bytea"); 1484 | 1485 | else if (strcmp(type, "integer") == 0) 1486 | /* Type "integer" appears dynamically sized between 1 and 8 1487 | * bytes. Need to assume worst case. */ 1488 | appendStringInfoString(str, "bigint"); 1489 | 1490 | else if (strcmp(type, "") == 0) 1491 | /* Type "integer" appears dynamically sized between 1 and 8 1492 | * bytes. Need to assume worst case. */ 1493 | appendStringInfoString(str, "text"); 1494 | 1495 | /* XXX try harder handling sqlite datatype */ 1496 | 1497 | /* if original type is compatible, return lowercase value */ 1498 | else 1499 | appendStringInfoString(str, type); 1500 | 1501 | pfree(type); 1502 | } 1503 | #endif 1504 | --------------------------------------------------------------------------------