#include "postgres.h"
#include <sys/stat.h>
#include <unistd.h>
#include "access/htup_details.h"
#include "access/reloptions.h"
#include "access/sysattr.h"
#include "catalog/pg_foreign_table.h"
#include "commands/copy.h"
#include "commands/defrem.h"
#include "commands/explain.h"
#include "commands/vacuum.h"
#include "foreign/fdwapi.h"
#include "foreign/foreign.h"
#include "miscadmin.h"
#include "nodes/makefuncs.h"
#include "optimizer/cost.h"
#include "optimizer/pathnode.h"
#include "optimizer/planmain.h"
#include "optimizer/restrictinfo.h"
#include "optimizer/var.h"
#include "utils/memutils.h"
#include "utils/rel.h"
Go to the source code of this file.
typedef struct FileFdwExecutionState FileFdwExecutionState |
typedef struct FileFdwPlanState FileFdwPlanState |
static bool check_selective_binary_conversion | ( | RelOptInfo * | baserel, | |
Oid | foreigntableid, | |||
List ** | columns | |||
) | [static] |
Definition at line 732 of file file_fdw.c.
References AccessShareLock, tupleDesc::attrs, RelOptInfo::baserestrictinfo, bms_first_member(), RestrictInfo::clause, defGetString(), DefElem::defname, format, GetForeignTable(), heap_close, heap_open(), i, lappend(), lfirst, list_length(), makeString(), NameStr, tupleDesc::natts, ForeignTable::options, pstrdup(), pull_varattnos(), RelationGetDescr, RelOptInfo::relid, and RelOptInfo::reltargetlist.
Referenced by fileGetForeignPaths().
{ ForeignTable *table; ListCell *lc; Relation rel; TupleDesc tupleDesc; AttrNumber attnum; Bitmapset *attrs_used = NULL; bool has_wholerow = false; int numattrs; int i; *columns = NIL; /* default result */ /* * Check format of the file. If binary format, this is irrelevant. */ table = GetForeignTable(foreigntableid); foreach(lc, table->options) { DefElem *def = (DefElem *) lfirst(lc); if (strcmp(def->defname, "format") == 0) { char *format = defGetString(def); if (strcmp(format, "binary") == 0) return false; break; } } /* Collect all the attributes needed for joins or final output. */ pull_varattnos((Node *) baserel->reltargetlist, baserel->relid, &attrs_used); /* Add all the attributes used by restriction clauses. */ foreach(lc, baserel->baserestrictinfo) { RestrictInfo *rinfo = (RestrictInfo *) lfirst(lc); pull_varattnos((Node *) rinfo->clause, baserel->relid, &attrs_used); } /* Convert attribute numbers to column names. */ rel = heap_open(foreigntableid, AccessShareLock); tupleDesc = RelationGetDescr(rel); while ((attnum = bms_first_member(attrs_used)) >= 0) { /* Adjust for system attributes. */ attnum += FirstLowInvalidHeapAttributeNumber; if (attnum == 0) { has_wholerow = true; break; } /* Ignore system attributes. */ if (attnum < 0) continue; /* Get user attributes. */ if (attnum > 0) { Form_pg_attribute attr = tupleDesc->attrs[attnum - 1]; char *attname = NameStr(attr->attname); /* Skip dropped attributes (probably shouldn't see any here). */ if (attr->attisdropped) continue; *columns = lappend(*columns, makeString(pstrdup(attname))); } } /* Count non-dropped user attributes while we have the tupdesc. */ numattrs = 0; for (i = 0; i < tupleDesc->natts; i++) { Form_pg_attribute attr = tupleDesc->attrs[i]; if (attr->attisdropped) continue; numattrs++; } heap_close(rel, AccessShareLock); /* If there's a whole-row reference, fail: we need all the columns. */ if (has_wholerow) { *columns = NIL; return false; } /* If all the user attributes are needed, fail. */ if (numattrs == list_length(*columns)) { *columns = NIL; return false; } return true; }
static void estimate_costs | ( | PlannerInfo * | root, | |
RelOptInfo * | baserel, | |||
FileFdwPlanState * | fdw_private, | |||
Cost * | startup_cost, | |||
Cost * | total_cost | |||
) | [static] |
Definition at line 929 of file file_fdw.c.
References RelOptInfo::baserestrictcost, cpu_tuple_cost, FileFdwPlanState::ntuples, FileFdwPlanState::pages, QualCost::per_tuple, seq_page_cost, and QualCost::startup.
Referenced by fileGetForeignPaths().
{ BlockNumber pages = fdw_private->pages; double ntuples = fdw_private->ntuples; Cost run_cost = 0; Cost cpu_per_tuple; /* * We estimate costs almost the same way as cost_seqscan(), thus assuming * that I/O costs are equivalent to a regular table file of the same size. * However, we take per-tuple CPU costs as 10x of a seqscan, to account * for the cost of parsing records. */ run_cost += seq_page_cost * pages; *startup_cost = baserel->baserestrictcost.startup; cpu_per_tuple = cpu_tuple_cost * 10 + baserel->baserestrictcost.per_tuple; run_cost += cpu_per_tuple * ntuples; *total_cost = *startup_cost + run_cost; }
static void estimate_size | ( | PlannerInfo * | root, | |
RelOptInfo * | baserel, | |||
FileFdwPlanState * | fdw_private | |||
) | [static] |
Definition at line 849 of file file_fdw.c.
References RelOptInfo::baserestrictinfo, clamp_row_est(), clauselist_selectivity(), FileFdwPlanState::filename, JOIN_INNER, MAXALIGN, FileFdwPlanState::ntuples, NULL, RelOptInfo::pages, FileFdwPlanState::pages, RelOptInfo::rows, RelOptInfo::tuples, and RelOptInfo::width.
Referenced by fileGetForeignRelSize().
{ struct stat stat_buf; BlockNumber pages; double ntuples; double nrows; /* * Get size of the file. It might not be there at plan time, though, in * which case we have to use a default estimate. */ if (stat(fdw_private->filename, &stat_buf) < 0) stat_buf.st_size = 10 * BLCKSZ; /* * Convert size to pages for use in I/O cost estimate later. */ pages = (stat_buf.st_size + (BLCKSZ - 1)) / BLCKSZ; if (pages < 1) pages = 1; fdw_private->pages = pages; /* * Estimate the number of tuples in the file. */ if (baserel->pages > 0) { /* * We have # of pages and # of tuples from pg_class (that is, from a * previous ANALYZE), so compute a tuples-per-page estimate and scale * that by the current file size. */ double density; density = baserel->tuples / (double) baserel->pages; ntuples = clamp_row_est(density * (double) pages); } else { /* * Otherwise we have to fake it. We back into this estimate using the * planner's idea of the relation width; which is bogus if not all * columns are being read, not to mention that the text representation * of a row probably isn't the same size as its internal * representation. Possibly we could do something better, but the * real answer to anyone who complains is "ANALYZE" ... */ int tuple_width; tuple_width = MAXALIGN(baserel->width) + MAXALIGN(sizeof(HeapTupleHeaderData)); ntuples = clamp_row_est((double) stat_buf.st_size / (double) tuple_width); } fdw_private->ntuples = ntuples; /* * Now estimate the number of rows returned by the scan after applying the * baserestrictinfo quals. */ nrows = ntuples * clauselist_selectivity(root, baserel->baserestrictinfo, 0, JOIN_INNER, NULL); nrows = clamp_row_est(nrows); /* Save the output-rows estimate for the planner */ baserel->rows = nrows; }
static int file_acquire_sample_rows | ( | Relation | onerel, | |
int | elevel, | |||
HeapTuple * | rows, | |||
int | targrows, | |||
double * | totalrows, | |||
double * | totaldeadrows | |||
) | [static] |
Definition at line 967 of file file_fdw.c.
References ALLOCSET_DEFAULT_INITSIZE, ALLOCSET_DEFAULT_MAXSIZE, ALLOCSET_DEFAULT_MINSIZE, AllocSetContextCreate(), anl_get_next_S(), anl_init_selection_state(), anl_random_fract(), ErrorContextCallback::arg, Assert, BeginCopyFrom(), ErrorContextCallback::callback, CurrentMemoryContext, EndCopyFrom(), ereport, errmsg(), error_context_stack, fileGetOptions(), filename, heap_form_tuple(), heap_freetuple(), MemoryContextDelete(), MemoryContextReset(), MemoryContextSwitchTo(), tupleDesc::natts, NextCopyFrom(), NIL, NULL, palloc(), pfree(), ErrorContextCallback::previous, RelationGetDescr, RelationGetRelationName, RelationGetRelid, vacuum_delay_point(), and values.
{ int numrows = 0; double rowstoskip = -1; /* -1 means not set yet */ double rstate; TupleDesc tupDesc; Datum *values; bool *nulls; bool found; char *filename; List *options; CopyState cstate; ErrorContextCallback errcallback; MemoryContext oldcontext = CurrentMemoryContext; MemoryContext tupcontext; Assert(onerel); Assert(targrows > 0); tupDesc = RelationGetDescr(onerel); values = (Datum *) palloc(tupDesc->natts * sizeof(Datum)); nulls = (bool *) palloc(tupDesc->natts * sizeof(bool)); /* Fetch options of foreign table */ fileGetOptions(RelationGetRelid(onerel), &filename, &options); /* * Create CopyState from FDW options. */ cstate = BeginCopyFrom(onerel, filename, false, NIL, options); /* * Use per-tuple memory context to prevent leak of memory used to read * rows from the file with Copy routines. */ tupcontext = AllocSetContextCreate(CurrentMemoryContext, "file_fdw temporary context", ALLOCSET_DEFAULT_MINSIZE, ALLOCSET_DEFAULT_INITSIZE, ALLOCSET_DEFAULT_MAXSIZE); /* Prepare for sampling rows */ rstate = anl_init_selection_state(targrows); /* Set up callback to identify error line number. */ errcallback.callback = CopyFromErrorCallback; errcallback.arg = (void *) cstate; errcallback.previous = error_context_stack; error_context_stack = &errcallback; *totalrows = 0; *totaldeadrows = 0; for (;;) { /* Check for user-requested abort or sleep */ vacuum_delay_point(); /* Fetch next row */ MemoryContextReset(tupcontext); MemoryContextSwitchTo(tupcontext); found = NextCopyFrom(cstate, NULL, values, nulls, NULL); MemoryContextSwitchTo(oldcontext); if (!found) break; /* * The first targrows sample rows are simply copied into the * reservoir. Then we start replacing tuples in the sample until we * reach the end of the relation. This algorithm is from Jeff Vitter's * paper (see more info in commands/analyze.c). */ if (numrows < targrows) { rows[numrows++] = heap_form_tuple(tupDesc, values, nulls); } else { /* * t in Vitter's paper is the number of records already processed. * If we need to compute a new S value, we must use the * not-yet-incremented value of totalrows as t. */ if (rowstoskip < 0) rowstoskip = anl_get_next_S(*totalrows, targrows, &rstate); if (rowstoskip <= 0) { /* * Found a suitable tuple, so save it, replacing one old tuple * at random */ int k = (int) (targrows * anl_random_fract()); Assert(k >= 0 && k < targrows); heap_freetuple(rows[k]); rows[k] = heap_form_tuple(tupDesc, values, nulls); } rowstoskip -= 1; } *totalrows += 1; } /* Remove error callback. */ error_context_stack = errcallback.previous; /* Clean up. */ MemoryContextDelete(tupcontext); EndCopyFrom(cstate); pfree(values); pfree(nulls); /* * Emit some interesting relation info */ ereport(elevel, (errmsg("\"%s\": file contains %.0f rows; " "%d rows in sample", RelationGetRelationName(onerel), *totalrows, numrows))); return numrows; }
Datum file_fdw_handler | ( | PG_FUNCTION_ARGS | ) |
Definition at line 160 of file file_fdw.c.
References FdwRoutine::AnalyzeForeignTable, FdwRoutine::BeginForeignScan, FdwRoutine::EndForeignScan, FdwRoutine::ExplainForeignScan, FdwRoutine::GetForeignPaths, FdwRoutine::GetForeignPlan, FdwRoutine::GetForeignRelSize, FdwRoutine::IterateForeignScan, makeNode, PG_RETURN_POINTER, and FdwRoutine::ReScanForeignScan.
{ FdwRoutine *fdwroutine = makeNode(FdwRoutine); fdwroutine->GetForeignRelSize = fileGetForeignRelSize; fdwroutine->GetForeignPaths = fileGetForeignPaths; fdwroutine->GetForeignPlan = fileGetForeignPlan; fdwroutine->ExplainForeignScan = fileExplainForeignScan; fdwroutine->BeginForeignScan = fileBeginForeignScan; fdwroutine->IterateForeignScan = fileIterateForeignScan; fdwroutine->ReScanForeignScan = fileReScanForeignScan; fdwroutine->EndForeignScan = fileEndForeignScan; fdwroutine->AnalyzeForeignTable = fileAnalyzeForeignTable; PG_RETURN_POINTER(fdwroutine); }
Datum file_fdw_validator | ( | PG_FUNCTION_ARGS | ) |
Definition at line 184 of file file_fdw.c.
References appendStringInfo(), buf, StringInfoData::data, defGetBoolean(), defGetString(), DefElem::defname, ereport, errcode(), errhint(), errmsg(), ERROR, filename, ForeignTableRelationId, initStringInfo(), is_valid_option(), lappend(), StringInfoData::len, lfirst, NULL, FileFdwOption::optcontext, FileFdwOption::optname, PG_GETARG_DATUM, PG_GETARG_OID, PG_RETURN_VOID, ProcessCopyOptions(), superuser(), and untransformRelOptions().
{ List *options_list = untransformRelOptions(PG_GETARG_DATUM(0)); Oid catalog = PG_GETARG_OID(1); char *filename = NULL; DefElem *force_not_null = NULL; List *other_options = NIL; ListCell *cell; /* * Only superusers are allowed to set options of a file_fdw foreign table. * This is because the filename is one of those options, and we don't want * non-superusers to be able to determine which file gets read. * * Putting this sort of permissions check in a validator is a bit of a * crock, but there doesn't seem to be any other place that can enforce * the check more cleanly. * * Note that the valid_options[] array disallows setting filename at any * options level other than foreign table --- otherwise there'd still be a * security hole. */ if (catalog == ForeignTableRelationId && !superuser()) ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), errmsg("only superuser can change options of a file_fdw foreign table"))); /* * Check that only options supported by file_fdw, and allowed for the * current object type, are given. */ foreach(cell, options_list) { DefElem *def = (DefElem *) lfirst(cell); if (!is_valid_option(def->defname, catalog)) { const struct FileFdwOption *opt; StringInfoData buf; /* * Unknown option specified, complain about it. Provide a hint * with list of valid options for the object. */ initStringInfo(&buf); for (opt = valid_options; opt->optname; opt++) { if (catalog == opt->optcontext) appendStringInfo(&buf, "%s%s", (buf.len > 0) ? ", " : "", opt->optname); } ereport(ERROR, (errcode(ERRCODE_FDW_INVALID_OPTION_NAME), errmsg("invalid option \"%s\"", def->defname), buf.len > 0 ? errhint("Valid options in this context are: %s", buf.data) : errhint("There are no valid options in this context."))); } /* * Separate out filename and force_not_null, since ProcessCopyOptions * won't accept them. (force_not_null only comes in a boolean * per-column flavor here.) */ if (strcmp(def->defname, "filename") == 0) { if (filename) ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("conflicting or redundant options"))); filename = defGetString(def); } else if (strcmp(def->defname, "force_not_null") == 0) { if (force_not_null) ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("conflicting or redundant options"))); force_not_null = def; /* Don't care what the value is, as long as it's a legal boolean */ (void) defGetBoolean(def); } else other_options = lappend(other_options, def); } /* * Now apply the core COPY code's validation logic for more checks. */ ProcessCopyOptions(NULL, true, other_options); /* * Filename option is required for file_fdw foreign tables. */ if (catalog == ForeignTableRelationId && filename == NULL) ereport(ERROR, (errcode(ERRCODE_FDW_DYNAMIC_PARAMETER_VALUE_NEEDED), errmsg("filename is required for file_fdw foreign tables"))); PG_RETURN_VOID(); }
static bool fileAnalyzeForeignTable | ( | Relation | relation, | |
AcquireSampleRowsFunc * | func, | |||
BlockNumber * | totalpages | |||
) | [static] |
Definition at line 688 of file file_fdw.c.
References ereport, errcode_for_file_access(), errmsg(), ERROR, fileGetOptions(), filename, and RelationGetRelid.
{ char *filename; List *options; struct stat stat_buf; /* Fetch options of foreign table */ fileGetOptions(RelationGetRelid(relation), &filename, &options); /* * Get size of the file. (XXX if we fail here, would it be better to just * return false to skip analyzing the table?) */ if (stat(filename, &stat_buf) < 0) ereport(ERROR, (errcode_for_file_access(), errmsg("could not stat file \"%s\": %m", filename))); /* * Convert size to pages. Must return at least 1 so that we can tell * later on that pg_class.relpages is not default. */ *totalpages = (stat_buf.st_size + (BLCKSZ - 1)) / BLCKSZ; if (*totalpages < 1) *totalpages = 1; *func = file_acquire_sample_rows; return true; }
static void fileBeginForeignScan | ( | ForeignScanState * | node, | |
int | eflags | |||
) | [static] |
Definition at line 564 of file file_fdw.c.
References BeginCopyFrom(), EXEC_FLAG_EXPLAIN_ONLY, ForeignScan::fdw_private, ForeignScanState::fdw_state, fileGetOptions(), filename, list_concat(), NIL, palloc(), PlanState::plan, ScanState::ps, RelationGetRelid, ForeignScanState::ss, and ScanState::ss_currentRelation.
{ ForeignScan *plan = (ForeignScan *) node->ss.ps.plan; char *filename; List *options; CopyState cstate; FileFdwExecutionState *festate; /* * Do nothing in EXPLAIN (no ANALYZE) case. node->fdw_state stays NULL. */ if (eflags & EXEC_FLAG_EXPLAIN_ONLY) return; /* Fetch options of foreign table */ fileGetOptions(RelationGetRelid(node->ss.ss_currentRelation), &filename, &options); /* Add any options from the plan (currently only convert_selectively) */ options = list_concat(options, plan->fdw_private); /* * Create CopyState from FDW options. We always acquire all columns, so * as to match the expected ScanTupleSlot signature. */ cstate = BeginCopyFrom(node->ss.ss_currentRelation, filename, false, NIL, options); /* * Save state in node->fdw_state. We must save enough information to call * BeginCopyFrom() again. */ festate = (FileFdwExecutionState *) palloc(sizeof(FileFdwExecutionState)); festate->filename = filename; festate->options = options; festate->cstate = cstate; node->fdw_state = (void *) festate; }
static void fileEndForeignScan | ( | ForeignScanState * | node | ) | [static] |
Definition at line 674 of file file_fdw.c.
References FileFdwExecutionState::cstate, EndCopyFrom(), and ForeignScanState::fdw_state.
{ FileFdwExecutionState *festate = (FileFdwExecutionState *) node->fdw_state; /* if festate is NULL, we are in EXPLAIN; nothing to do */ if (festate) EndCopyFrom(festate->cstate); }
static void fileExplainForeignScan | ( | ForeignScanState * | node, | |
ExplainState * | es | |||
) | [static] |
Definition at line 537 of file file_fdw.c.
References ExplainState::costs, ExplainPropertyLong(), ExplainPropertyText(), fileGetOptions(), filename, RelationGetRelid, ForeignScanState::ss, and ScanState::ss_currentRelation.
{ char *filename; List *options; /* Fetch options --- we only need filename at this point */ fileGetOptions(RelationGetRelid(node->ss.ss_currentRelation), &filename, &options); ExplainPropertyText("Foreign File", filename, es); /* Suppress file size if we're not showing cost details */ if (es->costs) { struct stat stat_buf; if (stat(filename, &stat_buf) == 0) ExplainPropertyLong("Foreign File Size", (long) stat_buf.st_size, es); } }
static void fileGetForeignPaths | ( | PlannerInfo * | root, | |
RelOptInfo * | baserel, | |||
Oid | foreigntableid | |||
) | [static] |
Definition at line 459 of file file_fdw.c.
References add_path(), check_selective_binary_conversion(), create_foreignscan_path(), estimate_costs(), RelOptInfo::fdw_private, list_make1, makeDefElem(), NIL, NULL, and RelOptInfo::rows.
{ FileFdwPlanState *fdw_private = (FileFdwPlanState *) baserel->fdw_private; Cost startup_cost; Cost total_cost; List *columns; List *coptions = NIL; /* Decide whether to selectively perform binary conversion */ if (check_selective_binary_conversion(baserel, foreigntableid, &columns)) coptions = list_make1(makeDefElem("convert_selectively", (Node *) columns)); /* Estimate costs */ estimate_costs(root, baserel, fdw_private, &startup_cost, &total_cost); /* * Create a ForeignPath node and add it as only possible path. We use the * fdw_private list of the path to carry the convert_selectively option; * it will be propagated into the fdw_private list of the Plan node. */ add_path(baserel, (Path *) create_foreignscan_path(root, baserel, baserel->rows, startup_cost, total_cost, NIL, /* no pathkeys */ NULL, /* no outer rel either */ coptions)); /* * If data file was sorted, and we knew it somehow, we could insert * appropriate pathkeys into the ForeignPath node to tell the planner * that. */ }
static ForeignScan * fileGetForeignPlan | ( | PlannerInfo * | root, | |
RelOptInfo * | baserel, | |||
Oid | foreigntableid, | |||
ForeignPath * | best_path, | |||
List * | tlist, | |||
List * | scan_clauses | |||
) | [static] |
Definition at line 506 of file file_fdw.c.
References extract_actual_clauses(), ForeignPath::fdw_private, make_foreignscan(), NIL, and RelOptInfo::relid.
{ Index scan_relid = baserel->relid; /* * We have no native ability to evaluate restriction clauses, so we just * put all the scan_clauses into the plan node's qual list for the * executor to check. So all we have to do here is strip RestrictInfo * nodes from the clauses and ignore pseudoconstants (which will be * handled elsewhere). */ scan_clauses = extract_actual_clauses(scan_clauses, false); /* Create the ForeignScan node */ return make_foreignscan(tlist, scan_clauses, scan_relid, NIL, /* no expressions to evaluate */ best_path->fdw_private); }
static void fileGetForeignRelSize | ( | PlannerInfo * | root, | |
RelOptInfo * | baserel, | |||
Oid | foreigntableid | |||
) | [static] |
Definition at line 431 of file file_fdw.c.
References estimate_size(), RelOptInfo::fdw_private, fileGetOptions(), FileFdwPlanState::filename, FileFdwPlanState::options, and palloc().
{ FileFdwPlanState *fdw_private; /* * Fetch options. We only need filename at this point, but we might as * well get everything and not need to re-fetch it later in planning. */ fdw_private = (FileFdwPlanState *) palloc(sizeof(FileFdwPlanState)); fileGetOptions(foreigntableid, &fdw_private->filename, &fdw_private->options); baserel->fdw_private = (void *) fdw_private; /* Estimate relation size */ estimate_size(root, baserel, fdw_private); }
Definition at line 312 of file file_fdw.c.
References defGetString(), DefElem::defname, elog, ERROR, ForeignServer::fdwid, get_file_fdw_attribute_options(), GetForeignDataWrapper(), GetForeignServer(), GetForeignTable(), lfirst, list_concat(), list_delete_cell(), NULL, ForeignTable::options, ForeignServer::options, ForeignDataWrapper::options, and ForeignTable::serverid.
Referenced by file_acquire_sample_rows(), fileAnalyzeForeignTable(), fileBeginForeignScan(), fileExplainForeignScan(), and fileGetForeignRelSize().
{ ForeignTable *table; ForeignServer *server; ForeignDataWrapper *wrapper; List *options; ListCell *lc, *prev; /* * Extract options from FDW objects. We ignore user mappings because * file_fdw doesn't have any options that can be specified there. * * (XXX Actually, given the current contents of valid_options[], there's * no point in examining anything except the foreign table's own options. * Simplify?) */ table = GetForeignTable(foreigntableid); server = GetForeignServer(table->serverid); wrapper = GetForeignDataWrapper(server->fdwid); options = NIL; options = list_concat(options, wrapper->options); options = list_concat(options, server->options); options = list_concat(options, table->options); options = list_concat(options, get_file_fdw_attribute_options(foreigntableid)); /* * Separate out the filename. */ *filename = NULL; prev = NULL; foreach(lc, options) { DefElem *def = (DefElem *) lfirst(lc); if (strcmp(def->defname, "filename") == 0) { *filename = defGetString(def); options = list_delete_cell(options, lc, prev); break; } prev = lc; } /* * The validator should have checked that a filename was included in the * options, but check again, just in case. */ if (*filename == NULL) elog(ERROR, "filename is required for file_fdw foreign tables"); *other_options = options; }
static TupleTableSlot * fileIterateForeignScan | ( | ForeignScanState * | node | ) | [static] |
Definition at line 613 of file file_fdw.c.
References CopyFromErrorCallback(), FileFdwExecutionState::cstate, error_context_stack, ExecClearTuple(), ExecStoreVirtualTuple(), ForeignScanState::fdw_state, NextCopyFrom(), NULL, ErrorContextCallback::previous, ForeignScanState::ss, and ScanState::ss_ScanTupleSlot.
{ FileFdwExecutionState *festate = (FileFdwExecutionState *) node->fdw_state; TupleTableSlot *slot = node->ss.ss_ScanTupleSlot; bool found; ErrorContextCallback errcallback; /* Set up callback to identify error line number. */ errcallback.callback = CopyFromErrorCallback; errcallback.arg = (void *) festate->cstate; errcallback.previous = error_context_stack; error_context_stack = &errcallback; /* * The protocol for loading a virtual tuple into a slot is first * ExecClearTuple, then fill the values/isnull arrays, then * ExecStoreVirtualTuple. If we don't find another row in the file, we * just skip the last step, leaving the slot empty as required. * * We can pass ExprContext = NULL because we read all columns from the * file, so no need to evaluate default expressions. * * We can also pass tupleOid = NULL because we don't allow oids for * foreign tables. */ ExecClearTuple(slot); found = NextCopyFrom(festate->cstate, NULL, slot->tts_values, slot->tts_isnull, NULL); if (found) ExecStoreVirtualTuple(slot); /* Remove error callback. */ error_context_stack = errcallback.previous; return slot; }
static void fileReScanForeignScan | ( | ForeignScanState * | node | ) | [static] |
Definition at line 656 of file file_fdw.c.
References BeginCopyFrom(), FileFdwExecutionState::cstate, EndCopyFrom(), ForeignScanState::fdw_state, FileFdwExecutionState::filename, NIL, FileFdwExecutionState::options, ForeignScanState::ss, and ScanState::ss_currentRelation.
{ FileFdwExecutionState *festate = (FileFdwExecutionState *) node->fdw_state; EndCopyFrom(festate->cstate); festate->cstate = BeginCopyFrom(node->ss.ss_currentRelation, festate->filename, false, NIL, festate->options); }
Definition at line 376 of file file_fdw.c.
References AccessShareLock, tupleDesc::attrs, defGetBoolean(), DefElem::defname, GetForeignColumnOptions(), heap_close, heap_open(), lappend(), lfirst, list_make1, makeDefElem(), makeString(), NameStr, tupleDesc::natts, NIL, pstrdup(), and RelationGetDescr.
Referenced by fileGetOptions().
{ Relation rel; TupleDesc tupleDesc; AttrNumber natts; AttrNumber attnum; List *fnncolumns = NIL; rel = heap_open(relid, AccessShareLock); tupleDesc = RelationGetDescr(rel); natts = tupleDesc->natts; /* Retrieve FDW options for all user-defined attributes. */ for (attnum = 1; attnum <= natts; attnum++) { Form_pg_attribute attr = tupleDesc->attrs[attnum - 1]; List *options; ListCell *lc; /* Skip dropped attributes. */ if (attr->attisdropped) continue; options = GetForeignColumnOptions(relid, attnum); foreach(lc, options) { DefElem *def = (DefElem *) lfirst(lc); if (strcmp(def->defname, "force_not_null") == 0) { if (defGetBoolean(def)) { char *attname = pstrdup(NameStr(attr->attname)); fnncolumns = lappend(fnncolumns, makeString(attname)); } } /* maybe in future handle other options here */ } } heap_close(rel, AccessShareLock); /* Return DefElem only when some column(s) have force_not_null */ if (fnncolumns != NIL) return list_make1(makeDefElem("force_not_null", (Node *) fnncolumns)); else return NIL; }
Definition at line 293 of file file_fdw.c.
References FileFdwOption::optcontext, and FileFdwOption::optname.
Referenced by file_fdw_validator().
{ const struct FileFdwOption *opt; for (opt = valid_options; opt->optname; opt++) { if (context == opt->optcontext && strcmp(opt->optname, option) == 0) return true; } return false; }
PG_FUNCTION_INFO_V1 | ( | file_fdw_handler | ) |
PG_FUNCTION_INFO_V1 | ( | file_fdw_validator | ) |
Definition at line 38 of file file_fdw.c.
struct FileFdwOption valid_options[] [static] |
{ {"filename", ForeignTableRelationId}, {"format", ForeignTableRelationId}, {"header", ForeignTableRelationId}, {"delimiter", ForeignTableRelationId}, {"quote", ForeignTableRelationId}, {"escape", ForeignTableRelationId}, {"null", ForeignTableRelationId}, {"encoding", ForeignTableRelationId}, {"force_not_null", AttributeRelationId}, {NULL, InvalidOid} }
Definition at line 58 of file file_fdw.c.