On 25.11.2010 09:34, Shigeru HANADA wrote:
Attached is a patch that adds core functionality of SQL/MED. This
patch provides:
* new option HANDLER for FOREIGN DATA WRAPPER
* CREATE/ALTER DDLs are supported
* psql \dew command shows handler option too
* pg_dump can dump HANDLER option
* new object type FOREIGN TABLE
* CREATE/ALTER/DROP DDLs are supported
* system columns except TABLEOID are not supported
* inheriting normal table is supported
* psql \d shows detail of foreign tables
* psql \det lists foreign tables
* psql \dE lists foreign tables in \d format
* pg_dump can dump the definition
* information_schema views added
* foreign table is read-only, so INSERT/UPDATE/DELETE are denied
* ANALYZE and VACUUM skips foreign tables
* new executor node ForeignScan
* it's a counterpart of SeqScan
* this node scans one foreign table at a time
* FDW HANDLER is necessary to execute SELECT statement
Patches for FDWs which can be used to execute SELECT statement will be
posted in their own thread soon.
"SQL/MED - file_fdw" : FDW for external PostgreSQL
"SEL/MED - postgresql_fdw" : FDW for server-side file (CSV, TEXT)
I would reuse existing CommitFest item "SQL/MED" for this patch, and
add new item for each FDW patch.
Looking at the API again, there's a few things I don't like about it:
* It's tied to the ForeignScanState, so all the executor state
structures are exposed to the FDW implementation. It feels like a
modularity violation that the FDW Iterate function returns the tuple by
storing it directly in scanstate->ss.ss_ScanTupleSlot for example. And
it's not going to work for remote scans that don't go through the
executor, for example if you wanted to rewrite contrib/dblink to use
foreign data wrappers. Or the SQL/MED passthrough mode.
* There's no clear Plan stage in the API. Except for EstimateCosts,
which just fills in the estimated costs in RelOptInfo, so it needs to
understand quite a lot of the planner data structures to come up with a
reasonable estimate. But if it e.g wants to apply a qual remotely, like
the PostgreSQL FDW does, it has to check for such quals at execution
time. And as I complained before, you don't get any meaningful EXPLAIN
output.
I propose the attached API instead. This has a clear separation between
plan and execution. I'm sure we'll have to extend the API in the future
FDWs want tighter integration, but I think this is a good start. It
makes it quite straightforward to write simple FDW like the file FDW,
without having to know anything about the executor or planner internals,
but provides enough flexibility to cover the functionality in your
PostgreSQL FDW.
--
Heikki Linnakangas
EnterpriseDB http://www.enterprisedb.com
/*-------------------------------------------------------------------------
*
* fdwapi.h
* API for foreign-data wrappers
*
* Portions Copyright (c) 1996-2010, PostgreSQL Global Development Group
*
* src/include/foreign/fdwapi.h
*
*-------------------------------------------------------------------------
*/
#ifndef FDWAPI_H
#define FDWAPI_H
#include "nodes/pg_list.h"
#include "nodes/relation.h"
typedef struct
{
TupleDesc *tupdesc;
/*
* Free-form text shown in EXPLAIN. The SQL to be sent to the remote
* server is typically shown here.
*/
char *explainInfo;
/*
* Cost estimation info. The startup_cost should include the cost of
* connecting to the remote host and sending over the query, as well as
* the cost of starting up the query so that it returns the first result
* row.
*/
double startup_cost;
double total_cost;
double rows;
double width;
/* FDW-private data */
void *private;
} FdwPlan;
typedef struct
{
/* FDW-private data */
void *private;
} FdwExecutionState;
/*
* Common interface routines of FDW, inspired by the FDW API in the SQL/MED
* standard, but adapted to the PostgreSQL world.
*
* A foreign-data wrapper implements these routines. At a minimum, it must
* implement BeginScan, Iterate and EndScan, and either PlanNative or
* PlanRelScan.
*
* The Plan* functions return an FdwPlan struct that can later be executed
* with BeginScan. The implementation should fill in the cost estimates in
* FdwPlan, as well as a tuple descriptor that describes the result set.
*/
struct FdwRoutine
{
#ifdef IN_THE_FUTURE
/*
* Plan a query of arbitrary native SQL (or other query language supported
* by the foreign server). This is used for SQL/MED passthrough mode, or
* e.g contrib/dblink.
*/
FdwPlan (*PlanNative)(Oid serverid, char *query);
/*
* Plan a whole subquery. This is used for example to execute an aggregate
* query remotely without pulling all the rows to the local server.
*
* The implementation can return NULL if it cannot satisfy the whole
* subquery, in which case the planner will break down the query into
* smaller parts and call PlanRelScan for the foreign tables involved.
*
* The implementation must be careful to only accept queries it fully
* understands! For example, if it ignores windowClauses, and returns
* a non-NULL results for a query that contains one, the windowClause
* would be lost and the query would return incorrect results.
*/
FdwPlan (*PlanQuery)(PlannerInfo *root, Query query);
#endif
/*
* Plan a scan on a foreign table. 'foreigntableid' identifies the foreign
* table, and 'attnos' is an integer list of attribute numbers for the
* columns to be returned. Note that 'attnos' can also be an empty list,
* typically for "SELECT COUNT(*) FROM foreigntable" style queries where
* we just need to know how many rows there are. The number and type of
* attributes in the tuple descriptor in the returned FdwPlan must match
* the attributes specified in attnos, or an error will be thrown.
*
* 'root' and 'baserel' contain context information that the
* implementation can use to restrict the rows that are fetched.
* baserel->baserestrictinfo is particularly interseting, as it contains
* quals (WHERE clauses) that can be used to filter the rows in the remote
* server. 'root' and 'baserel' can be safely ignored, the planner will
* re-check the quals on every fetched row anyway.
*/
FdwPlan (*PlanRelScan)(Oid serverid,
Oid foreigntableid, List *attnos,
PlannerInfo *root, RelOptInfo *baserel)
/*
* Begin execution of a foreign scan.
*/
FdwExecutionState (*BeginScan)(FdwPlan *plan);
/*
* Fetch the next record and fill *values and *isnulls with it.
*/
void (*Iterate)(FdwExecutionState *state, Datum *values, bool *isnulls);
/*
* End the foreign scan and close cursor.
*/
void (*EndScan)(FdwExecutionState *state);
};
#endif /* FDWAPI_H */
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers