#include "nodes/relation.h"
#include "optimizer/clauses.h"
#include "optimizer/cost.h"
+#include "optimizer/plancat.h"
#include "parser/parsetree.h"
#include "parser/scansup.h"
#include "utils/builtins.h"
static void pgEndForeignScan(ForeignScanState *node);
/* helper for deparsing a request into SQL statement */
+static void estimate_costs(PlannerInfo *root, RelOptInfo *baserel, Cost *startup_cost, Cost *total_cost);
static bool is_foreign_qual(PlannerInfo *root, RelOptInfo *baserel, Expr *expr);
static bool foreign_qual_walker(Node *node, void *context);
-static void deparseSelectClause(StringInfo sql, ForeignTable *table, TupleDesc tupdesc, const char *aliasname, bool prefix);
static char *deparseSql(Oid foreigntableid, PlannerInfo *root, RelOptInfo *baserel);
static void storeResult(TupleTableSlot *slot, PGresult *res, int rowno);
* Connection management
*/
static PGconn *GetConnection(ForeignServer *server, UserMapping *user);
+#ifdef NOT_USED
static void ReleaseConnection(PGconn *conn);
+#endif
static void check_conn_params(const char **keywords, const char **values);
static PGconn *connect_pg_server(ForeignServer *server, UserMapping *user);
static void cleanup_connection(ResourceReleasePhase phase,
sql = deparseSql(foreigntableid, root, baserel);
+ estimate_costs(root, baserel, &fdwplan->startup_cost,
+ &fdwplan->total_cost);
+
fdwplan->fdw_private = lappend(NIL, makeString(sql));
return fdwplan;
return expression_tree_walker(node, foreign_qual_walker, context);
}
-/*
- * Deparse the passed TupleDesc into SELECT clauses and append to the buffer
- * 'sql'.
- */
-static void
-deparseSelectClause(StringInfo sql, ForeignTable *table, TupleDesc tupdesc,
- const char *aliasname, bool prefix)
-{
- bool first;
- int i;
- const char *aliasname_q;
-
- /* The alias of relation is used in both SELECT clause and FROM clause. */
- aliasname_q = quote_identifier(aliasname);
-
- /* deparse SELECT clause */
- appendStringInfoString(sql, "SELECT ");
-
- /*
- * TODO: omit (deparse to "NULL") columns which are not used in the
- * original SQL.
- *
- * We must parse nodes parents of this ForeignScan node to determine unused
- * columns because some columns may be used only in parent Sort/Agg/Limit
- * nodes.
- */
- first = true;
- for (i = 0; i < tupdesc->natts; i++)
- {
-#ifdef NOT_USED
- List *options;
- ListCell *lc;
-#endif
- char *colname = NULL;
-
- /* skip dropped attributes */
- if (tupdesc->attrs[i]->attisdropped)
- continue;
-
- /* Determine column name to be used */
-#ifdef NOT_USED /* XXX: What was this all about? */
- options = GetGenericOptionsPerColumn(table->relid, i + 1);
- foreach (lc, options)
- {
- DefElem *def = (DefElem *) lfirst(lc);
- if (strcmp(def->defname, "colname") == 0)
- {
- colname = strVal(def->arg);
- break;
- }
- }
-#endif
- if (!colname)
- colname = tupdesc->attrs[i]->attname.data;
-
- if (!first)
- appendStringInfoString(sql, ", ");
-
- if (prefix)
- appendStringInfo(sql, "%s.%s", aliasname_q, colname);
- else
- appendStringInfo(sql, "%s", colname);
-
- first = false;
- }
-
- /* if target list is composed only of system attributes, add dummy column */
- if (first)
- appendStringInfoString(sql, "NULL");
-
- if (aliasname_q != aliasname)
- pfree((char *) aliasname_q);
-}
static void
deparse_var(PlannerInfo *root, StringInfo buf, Var *var)
first = false;
deparse_var(root, &sql, var);
}
+ if (strcmp(sql.data, "SELECT ") == 0)
+ appendStringInfo(&sql, "*");
/*
* Deparse FROM
relid = RelationGetRelid(node->ss.ss_currentRelation);
table = GetForeignTable(relid);
server = GetForeignServer(table->serverid);
- user = GetUserMapping(table->serverid, GetOuterUserId());
+ user = GetUserMapping(GetOuterUserId(), table->serverid);
conn = GetConnection(server, user);
festate->conn = conn;
pfree(values);
}
-#ifdef NOT_USED
-/*
- * Retrieve cost-factors of the foreign server from catalog.
- */
-static void
-get_server_costs(Oid relid, double *connection_cost, double *transfer_cost)
-{
- ForeignTable *table;
- ForeignServer *server;
- int n;
- const char **keywords;
- const char **values;
- int i;
-
- /*
- * Retrieve generic options from the target table and its server to correct
- * costs.
- */
- table = GetForeignTable(relid);
- server = GetForeignServer(table->serverid);
- n = list_length(table->options) + list_length(server->options) + 1;
- keywords = (const char **) palloc(sizeof(char *) * n);
- values = (const char **) palloc(sizeof(char *) * n);
- n = 0;
- n += flatten_generic_options(server->options, keywords + n, values + n);
- n += flatten_generic_options(table->options, keywords + n, values + n);
- keywords[n] = values[n] = NULL;
-
- for (i = 0; keywords[i]; i++)
- {
- if (pg_strcasecmp(keywords[i], "connection_cost") == 0)
- *connection_cost = strtod(values[i], NULL);
- else if (pg_strcasecmp(keywords[i], "transfer_cost") == 0)
- *transfer_cost = strtod(values[i], NULL);
- }
-
- pfree(keywords);
- pfree(values);
-}
-#endif
/*
* Estimate costs of scanning on a foreign table.
* baserel->baserestrictinfo can be used to examine quals on the relation.
*/
static void
-pgEstimateCosts(ForeignPath *path, PlannerInfo *root, RelOptInfo *baserel)
+estimate_costs(PlannerInfo *root, RelOptInfo *baserel, Cost *startup_cost, Cost *total_cost)
{
RangeTblEntry *rte;
+ int width;
double connection_cost = 0.0;
double transfer_cost = 0.0;
/*
* Use cost_seqscan() to get approximate value.
*/
- cost_seqscan(&path->path, root, baserel);
-
- /* Get cost factor from catalog to correct costs. */
rte = planner_rt_fetch(baserel->relid, root);
-#ifdef NOT_USED
- get_server_costs(rte->relid, &connection_cost, &transfer_cost);
-#endif
+// cost_seqscan(&path->path, root, baserel);
+
+ width = get_relation_data_width(rte->relid, NULL);
+
/* XXX arbitrary guesses */
connection_cost = 10.0;
transfer_cost = 1.0;
- path->path.startup_cost += connection_cost;
- path->path.total_cost += connection_cost;
- path->path.total_cost += transfer_cost *
- path->path.parent->width * path->path.parent->rows;
+ *startup_cost += connection_cost;
+ *total_cost += connection_cost;
+ *total_cost += transfer_cost * width * baserel->tuples;
}
/* ============================================================================
for (i = 0, j = 0; all_keywords[i]; i++)
{
- /* Use only libpq connection options. */
- if (!is_libpq_connection_option(all_keywords[i]))
+ StringInfoData conninfo;
+ PQconninfoOption *opt;
+
+ initStringInfo(&conninfo);
+ appendStringInfo(&conninfo, "%s=%s", all_keywords[i], all_values[i]);
+ opt = PQconninfoParse(conninfo.data, NULL);
+
+ /* Use only valid libpq connection options. */
+ if (opt == NULL)
+ {
+ PQconninfoFree(opt);
continue;
+ }
keywords[j] = all_keywords[i];
values[j] = all_values[i];
j++;
+
+ PQconninfoFree(opt);
+ pfree(conninfo.data);
}
keywords[j] = values[j] = NULL;
pfree(all_keywords);
return conn;
}
+#ifdef NOT_USED
/*
* Mark the connection as "unused", and close it if the caller was the last
* user of the connection.
entry->conn = NULL;
}
}
+#endif
/*
* Clean the connection up via ResourceOwner when pgClose couldn't close the