1/*-------------------------------------------------------------------------
4 * foreign-data wrapper for server-side flat files (or programs).
6 * Copyright (c) 2010-2025, PostgreSQL Global Development Group
9 * contrib/file_fdw/file_fdw.c
11 *-------------------------------------------------------------------------
51 * Describes the valid options for objects that use this wrapper.
60 * Valid options for file_fdw.
61 * These options are based on the options for the COPY FROM command.
62 * But note that force_not_null and force_null are handled as boolean options
63 * attached to a column, not as table options.
65 * Note: If you are adding new option for user mapping, you need to modify
66 * fileGetOptions(), which currently doesn't bother to look at user mappings.
69 /* Data source options */
70 {
"filename", ForeignTableRelationId},
71 {
"program", ForeignTableRelationId},
74 /* oids option is not supported */
75 {
"format", ForeignTableRelationId},
76 {
"header", ForeignTableRelationId},
77 {
"delimiter", ForeignTableRelationId},
78 {
"quote", ForeignTableRelationId},
79 {
"escape", ForeignTableRelationId},
80 {
"null", ForeignTableRelationId},
81 {
"default", ForeignTableRelationId},
82 {
"encoding", ForeignTableRelationId},
83 {
"on_error", ForeignTableRelationId},
84 {
"log_verbosity", ForeignTableRelationId},
85 {
"reject_limit", ForeignTableRelationId},
86 {
"force_not_null", AttributeRelationId},
87 {
"force_null", AttributeRelationId},
90 * force_quote is not supported by file_fdw because it's for COPY TO.
98 * FDW-specific information for RelOptInfo.fdw_private.
103 bool is_program;
/* true if filename represents an OS command */
107 double ntuples;
/* estimate of number of data rows */
111 * FDW-specific information for ForeignScanState.fdw_state.
116 bool is_program;
/* true if filename represents an OS command */
129 * FDW callback routines
162 List **other_options);
171 Cost *startup_cost,
Cost *total_cost);
174 double *totalrows,
double *totaldeadrows);
178 * Foreign-data wrapper handler function: return a struct with pointers
179 * to my callback routines.
201 * Validate the generic options given to a FOREIGN DATA WRAPPER, SERVER,
202 * USER MAPPING or FOREIGN TABLE that uses file_fdw.
204 * Raise an ERROR if the option or its value is considered invalid.
212 DefElem *force_not_null = NULL;
218 * Check that only options supported by file_fdw, and allowed for the
219 * current object type, are given.
221 foreach(cell, options_list)
228 const char *closest_match;
230 bool has_valid_options =
false;
233 * Unknown option specified, complain about it. Provide a hint
234 * with a valid option that looks similar, if there is one.
241 has_valid_options =
true;
248 (
errcode(ERRCODE_FDW_INVALID_OPTION_NAME),
250 has_valid_options ? closest_match ?
251 errhint(
"Perhaps you meant the option \"%s\".",
253 errhint(
"There are no valid options in this context.")));
257 * Separate out filename, program, and column-specific options, since
258 * ProcessCopyOptions won't accept them.
260 if (strcmp(def->
defname,
"filename") == 0 ||
261 strcmp(def->
defname,
"program") == 0)
265 (
errcode(ERRCODE_SYNTAX_ERROR),
266 errmsg(
"conflicting or redundant options")));
269 * Check permissions for changing which file or program is used by
272 * Only members of the role 'pg_read_server_files' are allowed to
273 * set the 'filename' option of a file_fdw foreign table, while
274 * only members of the role 'pg_execute_server_program' are
275 * allowed to set the 'program' option. This is because we don't
276 * want regular users to be able to control which file gets read
277 * or which program gets executed.
279 * Putting this sort of permissions check in a validator is a bit
280 * of a crock, but there doesn't seem to be any other place that
281 * can enforce the check more cleanly.
283 * Note that the valid_options[] array disallows setting filename
284 * and program at any options level other than foreign table ---
285 * otherwise there'd still be a security hole.
287 if (strcmp(def->
defname,
"filename") == 0 &&
290 (
errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
291 errmsg(
"permission denied to set the \"%s\" option of a file_fdw foreign table",
293 errdetail(
"Only roles with privileges of the \"%s\" role may set this option.",
294 "pg_read_server_files")));
296 if (strcmp(def->
defname,
"program") == 0 &&
299 (
errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
300 errmsg(
"permission denied to set the \"%s\" option of a file_fdw foreign table",
302 errdetail(
"Only roles with privileges of the \"%s\" role may set this option.",
303 "pg_execute_server_program")));
309 * force_not_null is a boolean option; after validation we can discard
310 * it - it will be retrieved later in get_file_fdw_attribute_options()
312 else if (strcmp(def->
defname,
"force_not_null") == 0)
316 (
errcode(ERRCODE_SYNTAX_ERROR),
317 errmsg(
"conflicting or redundant options"),
318 errhint(
"Option \"force_not_null\" supplied more than once for a column.")));
319 force_not_null = def;
320 /* Don't care what the value is, as long as it's a legal boolean */
323 /* See comments for force_not_null above */
324 else if (strcmp(def->
defname,
"force_null") == 0)
328 (
errcode(ERRCODE_SYNTAX_ERROR),
329 errmsg(
"conflicting or redundant options"),
330 errhint(
"Option \"force_null\" supplied more than once for a column.")));
335 other_options =
lappend(other_options, def);
339 * Now apply the core COPY code's validation logic for more checks.
344 * Either filename or program option is required for file_fdw foreign
347 if (catalog == ForeignTableRelationId &&
filename == NULL)
349 (
errcode(ERRCODE_FDW_DYNAMIC_PARAMETER_VALUE_NEEDED),
350 errmsg(
"either filename or program is required for file_fdw foreign tables")));
356 * Check if the provided option is one of the valid options.
357 * context is the Oid of the catalog holding the object the option is for.
373 * Fetch the options for a file_fdw foreign table.
375 * We have to separate out filename/program from the other options because
376 * those must not appear in the options list passed to the core COPY code.
380 char **
filename,
bool *is_program,
List **other_options)
389 * Extract options from FDW objects. We ignore user mappings because
390 * file_fdw doesn't have any options that can be specified there.
392 * (XXX Actually, given the current contents of valid_options[], there's
393 * no point in examining anything except the foreign table's own options.
407 * Separate out the filename or program option (we assume there is only
416 if (strcmp(def->
defname,
"filename") == 0)
422 else if (strcmp(def->
defname,
"program") == 0)
432 * The validator should have checked that filename or program was included
433 * in the options, but check again, just in case.
436 elog(
ERROR,
"either filename or program is required for file_fdw foreign tables");
442 * Retrieve per-column generic options from pg_attribute and construct a list
443 * of DefElems representing them.
445 * At the moment we only have "force_not_null", and "force_null",
446 * which should each be combined into a single DefElem listing all such
447 * columns, since that's what COPY expects.
463 natts = tupleDesc->
natts;
465 /* Retrieve FDW options for all user-defined attributes. */
469 List *column_options;
472 /* Skip dropped attributes. */
473 if (attr->attisdropped)
477 foreach(lc, column_options)
481 if (strcmp(def->
defname,
"force_not_null") == 0)
490 else if (strcmp(def->
defname,
"force_null") == 0)
499 /* maybe in future handle other column options here */
506 * Return DefElem only when some column(s) have force_not_null /
507 * force_null options set
509 if (fnncolumns !=
NIL)
512 if (fncolumns !=
NIL)
519 * fileGetForeignRelSize
520 * Obtain relation size estimates for a foreign table
530 * Fetch options. We only need filename (or program) at this point, but
531 * we might as well get everything and not need to re-fetch it later in
539 baserel->fdw_private = fdw_private;
541 /* Estimate relation size */
546 * fileGetForeignPaths
547 * Create possible access paths for a scan on the foreign table
549 * Currently we don't support any push-down feature, so there is only one
550 * possible access path, which simply returns all records in the order in
564 /* Decide whether to selectively perform binary conversion */
569 (
Node *) columns, -1));
573 &startup_cost, &total_cost);
576 * Create a ForeignPath node and add it as only possible path. We use the
577 * fdw_private list of the path to carry the convert_selectively option;
578 * it will be propagated into the fdw_private list of the Plan node.
580 * We don't support pushing join clauses into the quals of this path, but
581 * it could still have required parameterization due to LATERAL refs in
586 NULL,
/* default pathtarget */
591 NIL,
/* no pathkeys */
593 NULL,
/* no extra plan */
594 NIL,
/* no fdw_restrictinfo list */
598 * If data file was sorted, and we knew it somehow, we could insert
599 * appropriate pathkeys into the ForeignPath node to tell the planner
606 * Create a ForeignScan plan node for scanning the foreign table
620 * We have no native ability to evaluate restriction clauses, so we just
621 * put all the scan_clauses into the plan node's qual list for the
622 * executor to check. So all we have to do here is strip RestrictInfo
623 * nodes from the clauses and ignore pseudoconstants (which will be
624 * handled elsewhere).
628 /* Create the ForeignScan node */
632 NIL,
/* no expressions to evaluate */
634 NIL,
/* no custom tlist */
635 NIL,
/* no remote quals */
640 * fileExplainForeignScan
641 * Produce extra output for EXPLAIN
650 /* Fetch options --- we only need filename and is_program at this point */
659 /* Suppress file size if we're not showing cost details */
662 struct stat stat_buf;
672 * fileBeginForeignScan
673 * Initiate access to the file by creating CopyState
686 * Do nothing in EXPLAIN (no ANALYZE) case. node->fdw_state stays NULL.
691 /* Fetch options of foreign table */
695 /* Add any options from the plan (currently only convert_selectively) */
699 * Create CopyState from FDW options. We always acquire all columns, so
700 * as to match the expected ScanTupleSlot signature.
712 * Save state in node->fdw_state. We must save enough information to call
713 * BeginCopyFrom() again.
717 festate->is_program = is_program;
719 festate->cstate = cstate;
725 * fileIterateForeignScan
726 * Read next record from the data file and store it into the
727 * ScanTupleSlot as a virtual tuple
740 /* Set up callback to identify error line number. */
742 errcallback.
arg = cstate;
747 * We pass ExprContext because there might be a use of the DEFAULT option
748 * in COPY FROM, so we may need to evaluate default expressions.
755 * DEFAULT expressions need to be evaluated in a per-tuple context, so
756 * switch in case we are doing that.
761 * The protocol for loading a virtual tuple into a slot is first
762 * ExecClearTuple, then fill the values/isnull arrays, then
763 * ExecStoreVirtualTuple. If we don't find another row in the file, we
764 * just skip the last step, leaving the slot empty as required.
775 * Soft error occurred, skip this tuple and just make
776 * ErrorSaveContext ready for the next NextCopyFrom. Since we
777 * don't set details_wanted and error_data is not to be filled,
778 * just resetting error_occurred is enough.
782 /* Switch back to original memory context */
786 * Make sure we are interruptible while repeatedly calling
787 * NextCopyFrom() until no soft error occurs.
792 * Reset the per-tuple exprcontext, to clean-up after expression
800 (
errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
801 errmsg(
"skipped more than REJECT_LIMIT (%" PRId64
") rows due to data type incompatibility",
804 /* Repeat NextCopyFrom() until no soft error occurs */
811 /* Switch back to original memory context */
814 /* Remove error callback. */
821 * fileReScanForeignScan
822 * Rescan table, possibly with new parameters
843 * Finish scanning foreign table and dispose objects used for this scan
850 /* if festate is NULL, we are in EXPLAIN; nothing to do */
858 errmsg_plural(
"%" PRIu64
" row was skipped due to data type incompatibility",
859 "%" PRIu64
" rows were skipped due to data type incompatibility",
867 * fileAnalyzeForeignTable
868 * Test whether analyzing this foreign table is supported
878 struct stat stat_buf;
880 /* Fetch options of foreign table */
884 * If this is a program instead of a file, just return false to skip
885 * analyzing the table. We could run the program and collect stats on
886 * whatever it currently returns, but it seems likely that in such cases
887 * the output would be too volatile for the stats to be useful. Maybe
888 * there should be an option to enable doing this?
894 * Get size of the file. (XXX if we fail here, would it be better to just
895 * return false to skip analyzing the table?)
900 errmsg(
"could not stat file \"%s\": %m",
904 * Convert size to pages. Must return at least 1 so that we can tell
905 * later on that pg_class.relpages is not default.
907 *totalpages = (stat_buf.
st_size + (BLCKSZ - 1)) / BLCKSZ;
917 * fileIsForeignScanParallelSafe
918 * Reading a file, or external program, in a parallel worker should work
919 * just the same as reading it in the leader, so mark scans safe.
929 * check_selective_binary_conversion
931 * Check to see if it's useful to convert only a subset of the file's columns
932 * to binary. If so, construct a list of the column names to be converted,
933 * return that at *columns, and return true. (Note that it's possible to
934 * determine that no columns need be converted, for instance with a COUNT(*)
935 * query. So we can't use returning a NIL list to indicate failure.)
948 bool has_wholerow =
false;
952 *columns =
NIL;
/* default result */
955 * Check format of the file. If binary format, this is irrelevant.
958 foreach(lc,
table->options)
962 if (strcmp(def->
defname,
"format") == 0)
966 if (strcmp(
format,
"binary") == 0)
972 /* Collect all the attributes needed for joins or final output. */
976 /* Add all the attributes used by restriction clauses. */
985 /* Convert attribute numbers to column names. */
992 /* attidx is zero-based, attnum is the normal attribute number */
1001 /* Ignore system attributes. */
1005 /* Get user attributes. */
1011 /* Skip dropped attributes (probably shouldn't see any here). */
1012 if (attr->attisdropped)
1016 * Skip generated columns (COPY won't accept them in the column
1019 if (attr->attgenerated)
1025 /* Count non-dropped user attributes while we have the tupdesc. */
1027 for (
i = 0;
i < tupleDesc->
natts;
i++)
1031 if (attr->attisdropped)
1038 /* If there's a whole-row reference, fail: we need all the columns. */
1045 /* If all the user attributes are needed, fail. */
1056 * Estimate size of a foreign table.
1058 * The main result is returned in baserel->rows. We also set
1059 * fdw_private->pages and fdw_private->ntuples for later use in the cost
1066 struct stat stat_buf;
1072 * Get size of the file. It might not be there at plan time, though, in
1073 * which case we have to use a default estimate. We also have to fall
1074 * back to the default if using a program as the input.
1077 stat_buf.
st_size = 10 * BLCKSZ;
1080 * Convert size to pages for use in I/O cost estimate later.
1082 pages = (stat_buf.
st_size + (BLCKSZ - 1)) / BLCKSZ;
1085 fdw_private->
pages = pages;
1088 * Estimate the number of tuples in the file.
1093 * We have # of pages and # of tuples from pg_class (that is, from a
1094 * previous ANALYZE), so compute a tuples-per-page estimate and scale
1095 * that by the current file size.
1099 density = baserel->
tuples / (double) baserel->
pages;
1105 * Otherwise we have to fake it. We back into this estimate using the
1106 * planner's idea of the relation width; which is bogus if not all
1107 * columns are being read, not to mention that the text representation
1108 * of a row probably isn't the same size as its internal
1109 * representation. Possibly we could do something better, but the
1110 * real answer to anyone who complains is "ANALYZE" ...
1117 (
double) tuple_width);
1119 fdw_private->
ntuples = ntuples;
1122 * Now estimate the number of rows returned by the scan after applying the
1123 * baserestrictinfo quals.
1134 /* Save the output-rows estimate for the planner */
1135 baserel->
rows = nrows;
1139 * Estimate costs of scanning a foreign table.
1141 * Results are returned in *startup_cost and *total_cost.
1146 Cost *startup_cost,
Cost *total_cost)
1149 double ntuples = fdw_private->
ntuples;
1154 * We estimate costs almost the same way as cost_seqscan(), thus assuming
1155 * that I/O costs are equivalent to a regular table file of the same size.
1156 * However, we take per-tuple CPU costs as 10x of a seqscan, to account
1157 * for the cost of parsing records.
1159 * In the case of a program source, this calculation is even more divorced
1160 * from reality, but we have no good alternative; and it's not clear that
1161 * the numbers we produce here matter much anyway, since there's only one
1162 * access path for the rel.
1168 run_cost += cpu_per_tuple * ntuples;
1169 *total_cost = *startup_cost + run_cost;
1173 * file_acquire_sample_rows -- acquire a random sample of rows from the table
1175 * Selected rows are returned in the caller-allocated array rows[],
1176 * which must have at least targrows entries.
1177 * The actual number of rows selected is returned as the function result.
1178 * We also count the total number of rows in the file and return it into
1179 * *totalrows. Rows skipped due to on_error = 'ignore' are not included
1180 * in this count. Note that *totaldeadrows is always set to 0.
1182 * Note that the returned list of rows is not always in order by physical
1183 * position in the file. Therefore, correlation estimates derived later
1184 * may be meaningless, but it's OK because we don't use the estimates
1185 * currently (the planner only pays attention to correlation for indexscans).
1190 double *totalrows,
double *totaldeadrows)
1193 double rowstoskip = -1;
/* -1 means not set yet */
1212 nulls = (
bool *)
palloc(tupDesc->
natts *
sizeof(
bool));
1214 /* Fetch options of foreign table */
1218 * Create CopyState from FDW options.
1224 * Use per-tuple memory context to prevent leak of memory used to read
1225 * rows from the file with Copy routines.
1228 "file_fdw temporary context",
1231 /* Prepare for sampling rows */
1234 /* Set up callback to identify error line number. */
1236 errcallback.
arg = cstate;
1244 /* Check for user-requested abort or sleep */
1247 /* Fetch next row */
1262 * Soft error occurred, skip this tuple and just make
1263 * ErrorSaveContext ready for the next NextCopyFrom. Since we
1264 * don't set details_wanted and error_data is not to be filled,
1265 * just resetting error_occurred is enough.
1269 /* Repeat NextCopyFrom() until no soft error occurs */
1274 * The first targrows sample rows are simply copied into the
1275 * reservoir. Then we start replacing tuples in the sample until we
1276 * reach the end of the relation. This algorithm is from Jeff Vitter's
1277 * paper (see more info in commands/analyze.c).
1279 if (numrows < targrows)
1286 * t in Vitter's paper is the number of records already processed.
1287 * If we need to compute a new S value, we must use the
1288 * not-yet-incremented value of totalrows as t.
1293 if (rowstoskip <= 0)
1296 * Found a suitable tuple, so save it, replacing one old tuple
1301 Assert(k >= 0 && k < targrows);
1312 /* Remove error callback. */
1322 errmsg_plural(
"%" PRIu64
" row was skipped due to data type incompatibility",
1323 "%" PRIu64
" rows were skipped due to data type incompatibility",
1333 * Emit some interesting relation info
1336 (
errmsg(
"\"%s\": file contains %.0f rows; "
1337 "%d rows in sample",
1339 *totalrows, numrows)));
bool has_privs_of_role(Oid member, Oid role)
void ProcessCopyOptions(ParseState *pstate, CopyFormatOptions *opts_out, bool is_from, List *options)
int bms_next_member(const Bitmapset *a, int prevbit)
static Datum values[MAXATTR]
Selectivity clauselist_selectivity(PlannerInfo *root, List *clauses, int varRelid, JoinType jointype, SpecialJoinInfo *sjinfo)
CopyFromState BeginCopyFrom(ParseState *pstate, Relation rel, Node *whereClause, const char *filename, bool is_program, copy_data_source_cb data_source_cb, List *attnamelist, List *options)
void EndCopyFrom(CopyFromState cstate)
void CopyFromErrorCallback(void *arg)
bool NextCopyFrom(CopyFromState cstate, ExprContext *econtext, Datum *values, bool *nulls)
double clamp_row_est(double nrows)
ForeignScan * make_foreignscan(List *qptlist, List *qpqual, Index scanrelid, List *fdw_exprs, List *fdw_private, List *fdw_scan_tlist, List *fdw_recheck_quals, Plan *outer_plan)
char * defGetString(DefElem *def)
bool defGetBoolean(DefElem *def)
int errmsg_plural(const char *fmt_singular, const char *fmt_plural, unsigned long n,...)
int errcode_for_file_access(void)
int errdetail(const char *fmt,...)
ErrorContextCallback * error_context_stack
int errhint(const char *fmt,...)
int errcode(int sqlerrcode)
int errmsg(const char *fmt,...)
#define ereport(elevel,...)
TupleTableSlot * ExecStoreVirtualTuple(TupleTableSlot *slot)
EState * CreateExecutorState(void)
#define ResetPerTupleExprContext(estate)
#define GetPerTupleExprContext(estate)
#define GetPerTupleMemoryContext(estate)
#define EXEC_FLAG_EXPLAIN_ONLY
int(* AcquireSampleRowsFunc)(Relation relation, int elevel, HeapTuple *rows, int targrows, double *totalrows, double *totaldeadrows)
static ForeignScan * fileGetForeignPlan(PlannerInfo *root, RelOptInfo *baserel, Oid foreigntableid, ForeignPath *best_path, List *tlist, List *scan_clauses, Plan *outer_plan)
static void fileEndForeignScan(ForeignScanState *node)
Datum file_fdw_handler(PG_FUNCTION_ARGS)
static bool check_selective_binary_conversion(RelOptInfo *baserel, Oid foreigntableid, List **columns)
static void fileGetForeignRelSize(PlannerInfo *root, RelOptInfo *baserel, Oid foreigntableid)
static void fileExplainForeignScan(ForeignScanState *node, ExplainState *es)
static List * get_file_fdw_attribute_options(Oid relid)
Datum file_fdw_validator(PG_FUNCTION_ARGS)
static void estimate_costs(PlannerInfo *root, RelOptInfo *baserel, FileFdwPlanState *fdw_private, Cost *startup_cost, Cost *total_cost)
static int file_acquire_sample_rows(Relation onerel, int elevel, HeapTuple *rows, int targrows, double *totalrows, double *totaldeadrows)
PG_FUNCTION_INFO_V1(file_fdw_handler)
static const struct FileFdwOption valid_options[]
PG_MODULE_MAGIC_EXT(.name="file_fdw",.version=PG_VERSION)
static bool fileAnalyzeForeignTable(Relation relation, AcquireSampleRowsFunc *func, BlockNumber *totalpages)
static void fileReScanForeignScan(ForeignScanState *node)
struct FileFdwExecutionState FileFdwExecutionState
static bool is_valid_option(const char *option, Oid context)
static bool fileIsForeignScanParallelSafe(PlannerInfo *root, RelOptInfo *rel, RangeTblEntry *rte)
static void fileGetForeignPaths(PlannerInfo *root, RelOptInfo *baserel, Oid foreigntableid)
static void fileGetOptions(Oid foreigntableid, char **filename, bool *is_program, List **other_options)
static void estimate_size(PlannerInfo *root, RelOptInfo *baserel, FileFdwPlanState *fdw_private)
static void fileBeginForeignScan(ForeignScanState *node, int eflags)
static TupleTableSlot * fileIterateForeignScan(ForeignScanState *node)
struct FileFdwPlanState FileFdwPlanState
#define PG_GETARG_DATUM(n)
#define PG_RETURN_POINTER(x)
ForeignDataWrapper * GetForeignDataWrapper(Oid fdwid)
ForeignTable * GetForeignTable(Oid relid)
ForeignServer * GetForeignServer(Oid serverid)
List * GetForeignColumnOptions(Oid relid, AttrNumber attnum)
Assert(PointerIsAligned(start, uint64))
HeapTuple heap_form_tuple(TupleDesc tupleDescriptor, const Datum *values, const bool *isnull)
void heap_freetuple(HeapTuple htup)
#define SizeofHeapTupleHeader
@ COPY_LOG_VERBOSITY_DEFAULT
struct parser_state match_state[5]
if(TABLE==NULL||TABLE_index==NULL)
List * lappend(List *list, void *datum)
List * list_concat(List *list1, const List *list2)
DefElem * makeDefElem(char *name, Node *arg, int location)
void MemoryContextReset(MemoryContext context)
char * pstrdup(const char *in)
void pfree(void *pointer)
MemoryContext CurrentMemoryContext
void MemoryContextDelete(MemoryContext context)
#define AllocSetContextCreate
#define ALLOCSET_DEFAULT_SIZES
#define CHECK_FOR_INTERRUPTS()
static MemoryContext MemoryContextSwitchTo(MemoryContext context)
ForeignPath * create_foreignscan_path(PlannerInfo *root, RelOptInfo *rel, PathTarget *target, double rows, int disabled_nodes, Cost startup_cost, Cost total_cost, List *pathkeys, Relids required_outer, Path *fdw_outerpath, List *fdw_restrictinfo, List *fdw_private)
void add_path(RelOptInfo *parent_rel, Path *new_path)
FormData_pg_attribute * Form_pg_attribute
static int list_length(const List *l)
#define foreach_delete_current(lst, var_or_cell)
static const struct lconv_member_info table[]
#define RelationGetRelid(relation)
#define RelationGetDescr(relation)
#define RelationGetRelationName(relation)
List * untransformRelOptions(Datum options)
List * extract_actual_clauses(List *restrictinfo_list, bool pseudoconstant)
void reservoir_init_selection_state(ReservoirState rs, int n)
double sampler_random_fract(pg_prng_state *randstate)
double reservoir_get_next_S(ReservoirState rs, double t, int n)
ErrorSaveContext * escontext
struct ErrorContextCallback * previous
void(* callback)(void *arg)
ReScanForeignScan_function ReScanForeignScan
BeginForeignScan_function BeginForeignScan
IsForeignScanParallelSafe_function IsForeignScanParallelSafe
GetForeignPaths_function GetForeignPaths
GetForeignRelSize_function GetForeignRelSize
ExplainForeignScan_function ExplainForeignScan
EndForeignScan_function EndForeignScan
AnalyzeForeignTable_function AnalyzeForeignTable
IterateForeignScan_function IterateForeignScan
GetForeignPlan_function GetForeignPlan
struct PathTarget * reltarget
QualCost baserestrictcost
Relation ss_currentRelation
TupleTableSlot * ss_ScanTupleSlot
#define FirstLowInvalidHeapAttributeNumber
void table_close(Relation relation, LOCKMODE lockmode)
Relation table_open(Oid relationId, LOCKMODE lockmode)
static FormData_pg_attribute * TupleDescAttr(TupleDesc tupdesc, int i)
static TupleTableSlot * ExecClearTuple(TupleTableSlot *slot)
void vacuum_delay_point(bool is_analyze)
String * makeString(char *str)
void pull_varattnos(Node *node, Index varno, Bitmapset **varattnos)
const char * getClosestMatch(ClosestMatchState *state)
void initClosestMatch(ClosestMatchState *state, const char *source, int max_d)
void updateClosestMatch(ClosestMatchState *state, const char *candidate)