/* * Return permissive policies to be added */ List * test_rls_hooks_permissive(CmdType cmdtype, Relation relation) { List *policies = NIL; RowSecurityPolicy *policy = palloc0(sizeof(RowSecurityPolicy)); Datum role; FuncCall *n; Node *e; ColumnRef *c; ParseState *qual_pstate; RangeTblEntry *rte; if (strcmp(RelationGetRelationName(relation), "rls_test_permissive") && strcmp(RelationGetRelationName(relation), "rls_test_both")) return NIL; qual_pstate = make_parsestate(NULL); rte = addRangeTableEntryForRelation(qual_pstate, relation, NULL, false, false); addRTEtoQuery(qual_pstate, rte, false, true, true); role = ObjectIdGetDatum(ACL_ID_PUBLIC); policy->policy_name = pstrdup("extension policy"); policy->policy_id = InvalidOid; policy->polcmd = '*'; policy->roles = construct_array(&role, 1, OIDOID, sizeof(Oid), true, 'i'); /* * policy->qual = (Expr *) makeConst(BOOLOID, -1, InvalidOid, * sizeof(bool), BoolGetDatum(true), false, true); */ n = makeFuncCall(list_make2(makeString("pg_catalog"), makeString("current_user")), NIL, 0); c = makeNode(ColumnRef); c->fields = list_make1(makeString("username")); c->location = 0; e = (Node *) makeSimpleA_Expr(AEXPR_OP, "=", (Node *) n, (Node *) c, 0); policy->qual = (Expr *) transformWhereClause(qual_pstate, copyObject(e), EXPR_KIND_POLICY, "POLICY"); policy->with_check_qual = copyObject(policy->qual); policy->hassublinks = false; policies = list_make1(policy); return policies; }
static void createJoinCondition (Query *query, SublinkInfo *info, bool isTargetRewrite) { JoinExpr *join; Node *condition; Node *Csub; Node *CsubPlus; if (info->sublink->subLinkType == ANY_SUBLINK || info->sublink->subLinkType == ALL_SUBLINK) { /* generate Csub and CsubPlus from sublink condition */ if (info->targetVar) { Csub = copyObject(info->targetVar); } else { Csub = generateCsub (info); } CsubPlus = generateCsubPlus (info, list_length(query->rtable) - 1); /* create condition */ if (info->sublink->subLinkType == ANY_SUBLINK) { /* C_sub' OR NOT C_sub */ condition = (Node *) makeBoolExpr(NOT_EXPR, list_make1(Csub)); condition = (Node *) makeBoolExpr(OR_EXPR, list_make2(CsubPlus, condition)); } if (info->sublink->subLinkType == ALL_SUBLINK) { /* C_sub OR NOT C_sub' */ condition = (Node *) makeBoolExpr(NOT_EXPR, list_make1(CsubPlus)); condition = (Node *) makeBoolExpr(OR_EXPR, list_make2(Csub, condition)); } } else { condition = makeBoolConst(true, false); } if (list_length(query->rtable) > 1) { join = (JoinExpr *) linitial(query->jointree->fromlist); join->quals = condition; } else { query->jointree->quals = condition; } }
/* Similar mechanism as in parse_oper.ci, in particular * in the static function oper_select_candidate */ Oid find_equality_operator(Oid ltypeId, Oid rtypeId) { List * const equals=list_make1(makeString("=")); FuncCandidateList clist; Oid inputOids[2] = {ltypeId,rtypeId}; int ncandidates; Oid result = binary_oper_exact(equals, ltypeId, rtypeId); if(result!=InvalidOid) return result; clist = OpernameGetCandidates(equals, 'b', false); ncandidates = func_match_argtypes(2, inputOids, clist, &clist); if (ncandidates == 0) return InvalidOid; else if (ncandidates == 1) return clist->oid; clist = func_select_candidate(2, inputOids, clist); if(clist) return clist->oid; else return InvalidOid; }
/* * Extract a possibly-qualified name (as a List of Strings) from a DefElem. */ List * defGetQualifiedName(DefElem *def) { if (def->arg == NULL) ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("%s requires a parameter", def->defname))); switch (nodeTag(def->arg)) { case T_TypeName: return ((TypeName *) def->arg)->names; case T_List: return (List *) def->arg; case T_String: /* Allow quoted name for backwards compatibility */ return list_make1(def->arg); default: ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("argument of %s must be a name", def->defname))); } return NIL; /* keep compiler quiet */ }
/* * Extract a TypeName from a DefElem. * * Note: we do not accept a List arg here, because the parser will only * return a bare List when the name looks like an operator name. */ TypeName * defGetTypeName(DefElem *def) { if (def->arg == NULL) ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("%s requires a parameter", def->defname))); switch (nodeTag(def->arg)) { case T_TypeName: return (TypeName *) def->arg; case T_String: { /* Allow quoted typename for backwards compatibility */ TypeName *n = makeNode(TypeName); n->names = list_make1(def->arg); n->typmod = -1; return n; } default: ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("argument of %s must be a type name", def->defname))); } return NULL; /* keep compiler quiet */ }
/* * Executor state preparation for evaluation of constraint expressions, * indexes and triggers. * * This is based on similar code in copy.c */ static EState * create_estate_for_relation(LogicalRepRelMapEntry *rel) { EState *estate; ResultRelInfo *resultRelInfo; RangeTblEntry *rte; estate = CreateExecutorState(); rte = makeNode(RangeTblEntry); rte->rtekind = RTE_RELATION; rte->relid = RelationGetRelid(rel->localrel); rte->relkind = rel->localrel->rd_rel->relkind; estate->es_range_table = list_make1(rte); resultRelInfo = makeNode(ResultRelInfo); InitResultRelInfo(resultRelInfo, rel->localrel, 1, NULL, 0); estate->es_result_relations = resultRelInfo; estate->es_num_result_relations = 1; estate->es_result_relation_info = resultRelInfo; estate->es_output_cid = GetCurrentCommandId(true); /* Triggers might need a slot */ if (resultRelInfo->ri_TrigDesc) estate->es_trig_tuple_slot = ExecInitExtraTupleSlot(estate, NULL); /* Prepare to catch AFTER triggers. */ AfterTriggerBeginQuery(); return estate; }
/* * regprocout - converts proc OID to "pro_name" */ Datum regprocout(PG_FUNCTION_ARGS) { RegProcedure proid = PG_GETARG_OID(0); char *result; HeapTuple proctup; if (proid == InvalidOid) { result = pstrdup("-"); PG_RETURN_CSTRING(result); } proctup = SearchSysCache(PROCOID, ObjectIdGetDatum(proid), 0, 0, 0); if (HeapTupleIsValid(proctup)) { Form_pg_proc procform = (Form_pg_proc) GETSTRUCT(proctup); char *proname = NameStr(procform->proname); /* * In bootstrap mode, skip the fancy namespace stuff and just return * the proc name. (This path is only needed for debugging output * anyway.) */ if (IsBootstrapProcessingMode()) result = pstrdup(proname); else { char *nspname; FuncCandidateList clist; /* * Would this proc be found (uniquely!) by regprocin? If not, * qualify it. */ clist = FuncnameGetCandidates(list_make1(makeString(proname)), -1); if (clist != NULL && clist->next == NULL && clist->oid == proid) nspname = NULL; else nspname = get_namespace_name(procform->pronamespace); result = quote_qualified_identifier(nspname, proname); } ReleaseSysCache(proctup); } else { /* If OID doesn't match any pg_proc entry, return it numerically */ result = (char *) palloc(NAMEDATALEN); snprintf(result, NAMEDATALEN, "%u", proid); } PG_RETURN_CSTRING(result); }
/* * makeTypeName - * build a TypeName node for an unqualified name. */ TypeName * makeTypeName(char *typnam) { TypeName *n = makeNode(TypeName); n->names = list_make1(makeString(typnam)); n->typmod = -1; return n; }
/* * PruneShardList prunes shards from given list based on the selection criteria, * and returns remaining shards in another list. */ List * PruneShardList(Oid relationId, List *whereClauseList, List *shardIntervalList) { List *remainingShardList = NIL; ListCell *shardIntervalCell = NULL; List *restrictInfoList = NIL; Node *baseConstraint = NULL; Var *partitionColumn = PartitionColumn(relationId); char partitionMethod = PartitionType(relationId); /* build the filter clause list for the partition method */ if (partitionMethod == DISTRIBUTE_BY_HASH) { Node *hashedNode = HashableClauseMutator((Node *) whereClauseList, partitionColumn); List *hashedClauseList = (List *) hashedNode; restrictInfoList = BuildRestrictInfoList(hashedClauseList); } else { restrictInfoList = BuildRestrictInfoList(whereClauseList); } /* override the partition column for hash partitioning */ if (partitionMethod == DISTRIBUTE_BY_HASH) { partitionColumn = MakeInt4Column(); } /* build the base expression for constraint */ baseConstraint = BuildBaseConstraint(partitionColumn); /* walk over shard list and check if shards can be pruned */ foreach(shardIntervalCell, shardIntervalList) { ShardInterval *shardInterval = lfirst(shardIntervalCell); List *constraintList = NIL; bool shardPruned = false; /* set the min/max values in the base constraint */ UpdateConstraint(baseConstraint, shardInterval); constraintList = list_make1(baseConstraint); shardPruned = predicate_refuted_by(constraintList, restrictInfoList); if (shardPruned) { ereport(DEBUG2, (errmsg("predicate pruning for shardId " UINT64_FORMAT, shardInterval->id))); } else { remainingShardList = lappend(remainingShardList, &(shardInterval->id)); } }
/* * CopyTaskFilesFromDirectory finds all files in the given directory, except for * those having an attempt suffix. The function then copies these files into the * database table identified by the given schema and table name. */ static void CopyTaskFilesFromDirectory(StringInfo schemaName, StringInfo relationName, StringInfo sourceDirectoryName) { const char *directoryName = sourceDirectoryName->data; struct dirent *directoryEntry = NULL; uint64 copiedRowTotal = 0; DIR *directory = AllocateDir(directoryName); if (directory == NULL) { ereport(ERROR, (errcode_for_file_access(), errmsg("could not open directory \"%s\": %m", directoryName))); } directoryEntry = ReadDir(directory, directoryName); for (; directoryEntry != NULL; directoryEntry = ReadDir(directory, directoryName)) { const char *baseFilename = directoryEntry->d_name; const char *queryString = NULL; StringInfo fullFilename = NULL; RangeVar *relation = NULL; CopyStmt *copyStatement = NULL; uint64 copiedRowCount = 0; /* if system file or lingering task file, skip it */ if (strncmp(baseFilename, ".", MAXPGPATH) == 0 || strncmp(baseFilename, "..", MAXPGPATH) == 0 || strstr(baseFilename, ATTEMPT_FILE_SUFFIX) != NULL) { continue; } fullFilename = makeStringInfo(); appendStringInfo(fullFilename, "%s/%s", directoryName, baseFilename); /* build relation object and copy statement */ relation = makeRangeVar(schemaName->data, relationName->data, -1); copyStatement = CopyStatement(relation, fullFilename->data); if (BinaryWorkerCopyFormat) { DefElem *copyOption = makeDefElem("format", (Node *) makeString("binary")); copyStatement->options = list_make1(copyOption); } DoCopy(copyStatement, queryString, &copiedRowCount); copiedRowTotal += copiedRowCount; CommandCounterIncrement(); } ereport(DEBUG2, (errmsg("copied " UINT64_FORMAT " rows into table: \"%s.%s\"", copiedRowTotal, schemaName->data, relationName->data))); FreeDir(directory); }
/* * prune_using_single_value returns the shards for the specified distributed * table after pruning using a single value provided by the caller. */ Datum prune_using_single_value(PG_FUNCTION_ARGS) { Oid distributedTableId = PG_GETARG_OID(0); text *value = (PG_ARGISNULL(1)) ? NULL : PG_GETARG_TEXT_P(1); Expr *equalityExpr = MakeTextPartitionExpression(distributedTableId, value); List *whereClauseList = list_make1(equalityExpr); ArrayType *shardIdArrayType = PrunedShardIdsForTable(distributedTableId, whereClauseList); PG_RETURN_ARRAYTYPE_P(shardIdArrayType); }
Datum add_pg_enum_label(PG_FUNCTION_ARGS) { Oid enumoid = PG_GETARG_OID(0); Oid typoid = PG_GETARG_OID(1); Name label = PG_GETARG_NAME(2); EnumValuesCreate(typoid, list_make1(makeString(NameStr(*label))), enumoid); PG_RETURN_VOID(); }
/* * makeSimpleA_Expr - * As above, given a simple (unqualified) operator name */ A_Expr * makeSimpleA_Expr(A_Expr_Kind kind, const char *name, Node *lexpr, Node *rexpr) { A_Expr *a = makeNode(A_Expr); a->kind = kind; a->name = list_make1(makeString((char *) name)); a->lexpr = lexpr; a->rexpr = rexpr; return a; }
static Index addLeftJoinWithRewrittenSublink (Query *query, SublinkInfo *info) { JoinExpr *joinExpr; RangeTblRef *rtRef; Index sublinkIndex; /* add the rewritten sublink query to the queries range table */ addSubqueryToRT(query, info->rewrittenSublinkQuery, appendIdToString("rewrittenSublink", &curUniqueRelNum)); correctRTEAlias((RangeTblEntry *) lfirst(query->rtable->tail)); sublinkIndex = list_length(query->rtable); rtRef = makeNode(RangeTblRef); rtRef->rtindex = sublinkIndex; /* if original query has a range table entry, join it with the rewriten sublink */ if (sublinkIndex > 1) { /* create JoinExpr for left join */ joinExpr = createJoinExpr(query, JOIN_LEFT); joinExpr->larg = (Node *) linitial(query->jointree->fromlist); joinExpr->rarg = (Node *) rtRef; query->jointree->fromlist = list_make1(joinExpr); /* adapt join RTE for left join */ adaptRTEsForJoins(list_make1(joinExpr), query, "query_leftjoin_sublink"); } /* original query does not have any range table entry, set rtRef for rewritten sublink as fromlist */ else { query->jointree->fromlist = list_make1(rtRef); } return sublinkIndex; }
/* * prune_using_either_value returns the shards for the specified distributed * table after pruning using either of two values provided by the caller (OR). */ Datum prune_using_either_value(PG_FUNCTION_ARGS) { Oid distributedTableId = PG_GETARG_OID(0); text *firstValue = PG_GETARG_TEXT_P(1); text *secondValue = PG_GETARG_TEXT_P(2); Expr *firstQual = MakeTextPartitionExpression(distributedTableId, firstValue); Expr *secondQual = MakeTextPartitionExpression(distributedTableId, secondValue); Expr *orClause = make_orclause(list_make2(firstQual, secondQual)); List *whereClauseList = list_make1(orClause); ArrayType *shardIdArrayType = PrunedShardIdsForTable(distributedTableId, whereClauseList); PG_RETURN_ARRAYTYPE_P(shardIdArrayType); }
/* * worker_copy_shard_placement implements a internal UDF to copy a table's data from * a healthy placement into a receiving table on an unhealthy placement. This * function returns a boolean reflecting success or failure. */ Datum worker_copy_shard_placement(PG_FUNCTION_ARGS) { text *shardRelationNameText = PG_GETARG_TEXT_P(0); text *nodeNameText = PG_GETARG_TEXT_P(1); int32 nodePort = PG_GETARG_INT32(2); char *shardRelationName = text_to_cstring(shardRelationNameText); char *nodeName = text_to_cstring(nodeNameText); bool fetchSuccessful = false; Oid shardRelationId = ResolveRelationId(shardRelationNameText); Relation shardTable = heap_open(shardRelationId, RowExclusiveLock); TupleDesc tupleDescriptor = RelationGetDescr(shardTable); Tuplestorestate *tupleStore = tuplestore_begin_heap(false, false, work_mem); StringInfo selectAllQuery = NULL; ShardPlacement *placement = NULL; Task *task = NULL; selectAllQuery = makeStringInfo(); appendStringInfo(selectAllQuery, SELECT_ALL_QUERY, quote_identifier(shardRelationName)); placement = (ShardPlacement *) palloc0(sizeof(ShardPlacement)); placement->nodeName = nodeName; placement->nodePort = nodePort; task = (Task *) palloc0(sizeof(Task)); task->queryString = selectAllQuery; task->taskPlacementList = list_make1(placement); fetchSuccessful = ExecuteTaskAndStoreResults(task, tupleDescriptor, tupleStore); if (!fetchSuccessful) { ereport(ERROR, (errmsg("could not store shard rows from healthy placement"), errhint("Consult recent messages in the server logs for " "details."))); } CopyDataFromTupleStoreToRelation(tupleStore, shardTable); tuplestore_end(tupleStore); heap_close(shardTable, RowExclusiveLock); PG_RETURN_VOID(); }
void joinQueryRTEs(Query *query) { ListCell *lc; JoinExpr *newJoin; List *fromItems; List *subJoins; /* if query has only one or no range table entry return */ if (list_length(query->jointree->fromlist) <= 1) return; subJoins = NIL; fromItems = query->jointree->fromlist; query->jointree->fromlist = NIL; /* create a join tree that joins all from items */ lc = fromItems->head; newJoin = createJoinExpr (query, JOIN_INNER); newJoin->quals = (Node *) makeBoolConst(true, false); newJoin->larg = (Node *) lfirst(lc); lc = lc ->next; newJoin->rarg = (Node *) lfirst(lc); subJoins = lappend(subJoins, newJoin); for(lc = lc->next; lc != NULL; lc = lc->next) { newJoin = createJoinExpr (query, JOIN_INNER); /* set join condition to true */ newJoin->quals = (Node *) makeBoolConst(true, false); /* set children */ newJoin->rarg = (Node *) llast(subJoins); newJoin->larg = (Node *) lfirst(lc); /* append to join list */ subJoins = lappend(subJoins, newJoin); } /* create from list as new top join */ query->jointree->fromlist = list_make1(newJoin); adaptRTEsForJoins(subJoins, query, "query_rte_joined"); }
/* * pglogBeginForeignScan * Initiate access to the log by creating CopyState */ static void pglogBeginForeignScan(ForeignScanState *node, int eflags) { ForeignScan *plan = (ForeignScan *) node->ss.ps.plan; PgLogExecutionState *festate; elog(DEBUG1,"Entering function %s",__func__); /* * Do nothing in EXPLAIN (no ANALYZE) case. node->fdw_state stays NULL. */ if (eflags & EXEC_FLAG_EXPLAIN_ONLY) return; /* Initialise the execution state */ festate = (PgLogExecutionState *) palloc(sizeof(PgLogExecutionState)); festate->filenames = initLogFileNames(Pglog_directory); festate->i = 0; /* Forces CSV format */ festate->options = list_make1(makeDefElem("format", (Node *) makeString("csv"))); /* Add any options from the plan (currently only convert_selectively) */ festate->options = list_concat(festate->options, plan->fdw_private); /* Remember scan memory context */ festate->scan_cxt = CurrentMemoryContext; /* * Create CopyState from FDW options. We always acquire all columns, so * as to match the expected ScanTupleSlot signature. * Starts from the first file in the list. */ festate->cstate = NULL; BeginNextCopy(node->ss.ss_currentRelation, festate); elog(DEBUG1,"Copy state %p",festate->cstate); /* * Save state in node->fdw_state. We must save enough information to call * BeginCopyFrom() again. */ node->fdw_state = (void *) festate; }
/* * truncate relation */ void TruncateTable(Oid relid) { TruncateStmt stmt; RangeVar *heap; Assert(OidIsValid(relid)); heap = makeRangeVar(get_namespace_name(get_rel_namespace(relid)), get_rel_name(relid), -1); memset(&stmt, 0, sizeof(stmt)); stmt.type = T_TruncateStmt; stmt.relations = list_make1(heap); stmt.behavior = DROP_RESTRICT; ExecuteTruncate(&stmt); CommandCounterIncrement(); }
/* * Pull up children of a UNION node that are identically-propertied UNIONs. * * NOTE: we can also pull a UNION ALL up into a UNION, since the distinct * output rows will be lost anyway. */ static List * recurse_union_children(Node *setOp, PlannerInfo *root, double tuple_fraction, SetOperationStmt *top_union, List *refnames_tlist) { List *child_sortclauses; if (IsA(setOp, SetOperationStmt)) { SetOperationStmt *op = (SetOperationStmt *) setOp; if (op->op == top_union->op && (op->all == top_union->all || op->all) && equal(op->colTypes, top_union->colTypes)) { /* Same UNION, so fold children into parent's subplan list */ return list_concat(recurse_union_children(op->larg, root, tuple_fraction, top_union, refnames_tlist), recurse_union_children(op->rarg, root, tuple_fraction, top_union, refnames_tlist)); } } /* * Not same, so plan this child separately. * * Note we disallow any resjunk columns in child results. This is * necessary since the Append node that implements the union won't do any * projection, and upper levels will get confused if some of our output * tuples have junk and some don't. This case only arises when we have an * EXCEPT or INTERSECT as child, else there won't be resjunk anyway. */ return list_make1(recurse_set_operations(setOp, root, tuple_fraction, top_union->colTypes, false, -1, refnames_tlist, &child_sortclauses)); }
/* * pglogGetForeignPaths * Create possible access paths for a scan on the foreign table * * Currently we don't support any push-down feature, so there is only one * possible access path, which simply returns all records in the order in * the data file. */ static void pglogGetForeignPaths(PlannerInfo *root, RelOptInfo *baserel, Oid foreigntableid) { PgLogPlanState *fdw_private = (PgLogPlanState *) baserel->fdw_private; Cost startup_cost; Cost total_cost; List *columns; List *coptions = NIL; elog(DEBUG1,"Entering function %s",__func__); /* 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. */ }
/* * CopyIntoStream * * COPY events to a stream from an input source */ void CopyIntoStream(Relation rel, TupleDesc desc, HeapTuple *tuples, int ntuples) { bool snap = ActiveSnapshotSet(); ResultRelInfo rinfo; StreamInsertState *sis; MemSet(&rinfo, 0, sizeof(ResultRelInfo)); rinfo.ri_RangeTableIndex = 1; /* dummy */ rinfo.ri_TrigDesc = NULL; rinfo.ri_RelationDesc = rel; if (snap) PopActiveSnapshot(); BeginStreamModify(NULL, &rinfo, list_make1(desc), 0, 0); sis = (StreamInsertState *) rinfo.ri_FdwState; Assert(sis); if (sis->queries) { TupleTableSlot *slot = MakeSingleTupleTableSlot(RelationGetDescr(rel)); int i; for (i = 0; i < ntuples; i++) { ExecStoreTuple(tuples[i], slot, InvalidBuffer, false); ExecStreamInsert(NULL, &rinfo, slot, NULL); ExecClearTuple(slot); } ExecDropSingleTupleTableSlot(slot); Assert(sis->ntups == ntuples); pgstat_increment_cq_write(ntuples, sis->nbytes); } EndStreamModify(NULL, &rinfo); if (snap) PushActiveSnapshot(GetTransactionSnapshot()); }
/* * StartPlacementConnection initiates a connection to a remote node, * associated with the placement and transaction. * * The connection is established for the current database. If userName is NULL * the current user is used, otherwise the provided one. * * See StartNodeUserDatabaseConnection for details. * * Flags have the corresponding meaning from StartNodeUserDatabaseConnection, * except that two additional flags have an effect: * - FOR_DML - signal that connection is going to be used for DML (modifications) * - FOR_DDL - signal that connection is going to be used for DDL * * Only one connection associated with the placement may have FOR_DML or * FOR_DDL set. For hash-partitioned tables only one connection for a set of * colocated placements may have FOR_DML/DDL set. This restriction prevents * deadlocks and wrong results due to in-progress transactions. */ MultiConnection * StartPlacementConnection(uint32 flags, ShardPlacement *placement, const char *userName) { ShardPlacementAccess *placementAccess = (ShardPlacementAccess *) palloc0(sizeof(ShardPlacementAccess)); placementAccess->placement = placement; if (flags & FOR_DDL) { placementAccess->accessType = PLACEMENT_ACCESS_DDL; } else if (flags & FOR_DML) { placementAccess->accessType = PLACEMENT_ACCESS_DML; } else { placementAccess->accessType = PLACEMENT_ACCESS_SELECT; } return StartPlacementListConnection(flags, list_make1(placementAccess), userName); }
/* * A shorthand for prepare_plan_for_sharing() followed by * 'numpartners' calls to share_prepared_plan(). */ List * share_plan(PlannerInfo *root, Plan *common, int numpartners) { List *shared_nodes = NIL; Plan *shared; int i; Assert(numpartners > 0); if (numpartners == 1) return list_make1(common); shared = prepare_plan_for_sharing(root, common); for (i = 0; i < numpartners; i++) { Plan *p; p = (Plan *) make_shareinputscan(root, shared); shared_nodes = lappend(shared_nodes, p); } return shared_nodes; }
/* * get_proposed_default_constraint * * This function returns the negation of new_part_constraints, which * would be an integral part of the default partition constraints after * addition of the partition to which the new_part_constraints belongs. */ List * get_proposed_default_constraint(List *new_part_constraints) { Expr *defPartConstraint; defPartConstraint = make_ands_explicit(new_part_constraints); /* * Derive the partition constraints of default partition by negating the * given partition constraints. The partition constraint never evaluates * to NULL, so negating it like this is safe. */ defPartConstraint = makeBoolExpr(NOT_EXPR, list_make1(defPartConstraint), -1); /* Simplify, to put the negated expression into canonical form */ defPartConstraint = (Expr *) eval_const_expressions(NULL, (Node *) defPartConstraint); defPartConstraint = canonicalize_qual(defPartConstraint, true); return make_ands_implicit(defPartConstraint); }
/* * regoperout - converts operator OID to "opr_name" */ Datum regoperout(PG_FUNCTION_ARGS) { Oid oprid = PG_GETARG_OID(0); char *result; HeapTuple opertup; if (oprid == InvalidOid) { result = pstrdup("0"); PG_RETURN_CSTRING(result); } opertup = SearchSysCache1(OPEROID, ObjectIdGetDatum(oprid)); if (HeapTupleIsValid(opertup)) { Form_pg_operator operform = (Form_pg_operator) GETSTRUCT(opertup); char *oprname = NameStr(operform->oprname); /* * In bootstrap mode, skip the fancy namespace stuff and just return * the oper name. (This path is only needed for debugging output * anyway.) */ if (IsBootstrapProcessingMode()) result = pstrdup(oprname); else { FuncCandidateList clist; /* * Would this oper be found (uniquely!) by regoperin? If not, * qualify it. */ clist = OpernameGetCandidates(list_make1(makeString(oprname)), '\0'); if (clist != NULL && clist->next == NULL && clist->oid == oprid) result = pstrdup(oprname); else { const char *nspname; nspname = get_namespace_name(operform->oprnamespace); nspname = quote_identifier(nspname); result = (char *) palloc(strlen(nspname) + strlen(oprname) + 2); sprintf(result, "%s.%s", nspname, oprname); } } ReleaseSysCache(opertup); } else { /* * If OID doesn't match any pg_operator entry, return it numerically */ result = (char *) palloc(NAMEDATALEN); snprintf(result, NAMEDATALEN, "%u", oprid); } PG_RETURN_CSTRING(result); }
/* * make_op() * Operator expression construction. * * Transform operator expression ensuring type compatibility. * This is where some type conversion happens. * * As with coerce_type, pstate may be NULL if no special unknown-Param * processing is wanted. */ Expr * make_op(ParseState *pstate, List *opname, Node *ltree, Node *rtree, int location) { Oid ltypeId, rtypeId; Operator tup; Form_pg_operator opform; Oid actual_arg_types[2]; Oid declared_arg_types[2]; int nargs; List *args; Oid rettype; OpExpr *result; /* Select the operator */ if (rtree == NULL) { /* right operator */ ltypeId = exprType(ltree); rtypeId = InvalidOid; tup = right_oper(pstate, opname, ltypeId, false, location); } else if (ltree == NULL) { /* left operator */ rtypeId = exprType(rtree); ltypeId = InvalidOid; tup = left_oper(pstate, opname, rtypeId, false, location); } else { /* otherwise, binary operator */ ltypeId = exprType(ltree); rtypeId = exprType(rtree); tup = oper(pstate, opname, ltypeId, rtypeId, false, location); } opform = (Form_pg_operator) GETSTRUCT(tup); /* Check it's not a shell */ if (!RegProcedureIsValid(opform->oprcode)) ereport(ERROR, (errcode(ERRCODE_UNDEFINED_FUNCTION), errmsg("operator is only a shell: %s", op_signature_string(opname, opform->oprkind, opform->oprleft, opform->oprright)), parser_errposition(pstate, location))); /* Do typecasting and build the expression tree */ if (rtree == NULL) { /* right operator */ args = list_make1(ltree); actual_arg_types[0] = ltypeId; declared_arg_types[0] = opform->oprleft; nargs = 1; } else if (ltree == NULL) { /* left operator */ args = list_make1(rtree); actual_arg_types[0] = rtypeId; declared_arg_types[0] = opform->oprright; nargs = 1; } else { /* otherwise, binary operator */ args = list_make2(ltree, rtree); actual_arg_types[0] = ltypeId; actual_arg_types[1] = rtypeId; declared_arg_types[0] = opform->oprleft; declared_arg_types[1] = opform->oprright; nargs = 2; } /* * enforce consistency with polymorphic argument and return types, * possibly adjusting return type or declared_arg_types (which will be * used as the cast destination by make_fn_arguments) */ rettype = enforce_generic_type_consistency(actual_arg_types, declared_arg_types, nargs, opform->oprresult, false); /* perform the necessary typecasting of arguments */ make_fn_arguments(pstate, args, actual_arg_types, declared_arg_types); /* and build the expression node */ result = makeNode(OpExpr); result->opno = oprid(tup); result->opfuncid = opform->oprcode; result->opresulttype = rettype; result->opretset = get_func_retset(opform->oprcode); /* opcollid and inputcollid will be set by parse_collate.c */ result->args = args; result->location = location; ReleaseSysCache(tup); return (Expr *) result; }
/* * PerformCursorOpen * Execute SQL DECLARE CURSOR command. * * The query has already been through parse analysis, rewriting, and planning. * When it gets here, it looks like a SELECT PlannedStmt, except that the * utilityStmt field is set. */ void PerformCursorOpen(PlannedStmt *stmt, ParamListInfo params, const char *queryString, bool isTopLevel) { DeclareCursorStmt *cstmt = (DeclareCursorStmt *) stmt->utilityStmt; Portal portal; MemoryContext oldContext; if (cstmt == NULL || !IsA(cstmt, DeclareCursorStmt)) elog(ERROR, "PerformCursorOpen called for non-cursor query"); /* * Disallow empty-string cursor name (conflicts with protocol-level * unnamed portal). */ if (!cstmt->portalname || cstmt->portalname[0] == '\0') ereport(ERROR, (errcode(ERRCODE_INVALID_CURSOR_NAME), errmsg("invalid cursor name: must not be empty"))); /* * If this is a non-holdable cursor, we require that this statement has * been executed inside a transaction block (or else, it would have no * user-visible effect). */ if (!(cstmt->options & CURSOR_OPT_HOLD)) RequireTransactionChain(isTopLevel, "DECLARE CURSOR"); /* * Create a portal and copy the plan and queryString into its memory. */ portal = CreatePortal(cstmt->portalname, false, false); oldContext = MemoryContextSwitchTo(PortalGetHeapMemory(portal)); stmt = copyObject(stmt); stmt->utilityStmt = NULL; /* make it look like plain SELECT */ queryString = pstrdup(queryString); PortalDefineQuery(portal, NULL, queryString, "SELECT", /* cursor's query is always a SELECT */ list_make1(stmt), NULL); /*---------- * Also copy the outer portal's parameter list into the inner portal's * memory context. We want to pass down the parameter values in case we * had a command like * DECLARE c CURSOR FOR SELECT ... WHERE foo = $1 * This will have been parsed using the outer parameter set and the * parameter value needs to be preserved for use when the cursor is * executed. *---------- */ params = copyParamList(params); MemoryContextSwitchTo(oldContext); /* * Set up options for portal. * * If the user didn't specify a SCROLL type, allow or disallow scrolling * based on whether it would require any additional runtime overhead to do * so. Also, we disallow scrolling for FOR UPDATE cursors. */ portal->cursorOptions = cstmt->options; if (!(portal->cursorOptions & (CURSOR_OPT_SCROLL | CURSOR_OPT_NO_SCROLL))) { if (stmt->rowMarks == NIL && ExecSupportsBackwardScan(stmt->planTree)) portal->cursorOptions |= CURSOR_OPT_SCROLL; else portal->cursorOptions |= CURSOR_OPT_NO_SCROLL; } /* * Start execution, inserting parameters if any. */ PortalStart(portal, params, GetActiveSnapshot()); Assert(portal->strategy == PORTAL_ONE_SELECT); /* * We're done; the query won't actually be run until PerformPortalFetch is * called. */ }
/* * make_opr() * Operator expression construction. * * Transform operator expression ensuring type compatibility. * This is where some type conversion happens. * * As with coerce_type, pstate may be NULL if no special unknown-Param * processing is wanted. */ expr_n* make_opr(parse_state_s *pstate, struct list *opname, node_n *ltree, node_n *rtree, int location) { oid_t ltypeId; oid_t rtypeId; Operator tup; Form_pg_operator opform; oid_t actual_arg_types[2]; oid_t declared_arg_types[2]; int nargs; struct list* args; oid_t rettype; opr_xp* result; /* Select the operator */ if (rtree == NULL) { /* right operator */ ltypeId = expr_type(ltree); rtypeId = INVALID_OID; tup = right_opr(pstate, opname, ltypeId, false, location); } else if (ltree == NULL) { /* left operator */ rtypeId = expr_type(rtree); ltypeId = INVALID_OID; tup = left_opr(pstate, opname, rtypeId, false, location); } else { /* otherwise, binary operator */ ltypeId = expr_type(ltree); rtypeId = expr_type(rtree); tup = oper(pstate, opname, ltypeId, rtypeId, false, location); } opform = (Form_pg_operator) GET_STRUCT(tup); /* Check it's not a shell */ if (!REGPROC_VALID(opform->oprcode)) ereport(ERROR, ( errcode(E_UNDEFINED_FUNCTION), errmsg("operator is only a shell: %s", opr_signature_string(opname, opform->oprkind, opform->oprleft, opform->oprright)), parser_errpos(pstate, location))); /* Do typecasting and build the expression tree */ if (rtree == NULL) { /* right operator */ args = list_make1(ltree); actual_arg_types[0] = ltypeId; declared_arg_types[0] = opform->oprleft; nargs = 1; } else if (ltree == NULL) { /* left operator */ args = list_make1(rtree); actual_arg_types[0] = rtypeId; declared_arg_types[0] = opform->oprright; nargs = 1; } else { /* otherwise, binary operator */ args = list_make2(ltree, rtree); actual_arg_types[0] = ltypeId; actual_arg_types[1] = rtypeId; declared_arg_types[0] = opform->oprleft; declared_arg_types[1] = opform->oprright; nargs = 2; } /* * enforce consistency with polymorphic argument and return types, * possibly adjusting return type or declared_arg_types (which will be * used as the cast destination by make_fn_arguments) */ rettype = enforce_generic_type_consistency(actual_arg_types, declared_arg_types, nargs, opform->oprresult, false); /* perform the necessary typecasting of arguments */ make_fn_arguments(pstate, args, actual_arg_types, declared_arg_types); /* and build the expression node */ result = MK_N(OpExpr,opr_xp); result->opno = opr_id(tup); result->opfuncid = opform->oprcode; result->opresulttype = rettype; result->opretset = get_func_retset(opform->oprcode); /* opcollid and inputcollid will be set by parse_collate.c */ result->args = args; result->location = location; release_syscache(tup); return (expr_n *) result; }
/* * Traverse the tree to find path from root page to specified "child" block. * * returns a new insertion stack, starting from the parent of "child", up * to the root. *downlinkoffnum is set to the offset of the downlink in the * direct parent of child. * * To prevent deadlocks, this should lock only one page at a time. */ static GISTInsertStack * gistFindPath(Relation r, BlockNumber child, OffsetNumber *downlinkoffnum) { Page page; Buffer buffer; OffsetNumber i, maxoff; ItemId iid; IndexTuple idxtuple; List *fifo; GISTInsertStack *top, *ptr; BlockNumber blkno; top = (GISTInsertStack *) palloc0(sizeof(GISTInsertStack)); top->blkno = GIST_ROOT_BLKNO; top->downlinkoffnum = InvalidOffsetNumber; fifo = list_make1(top); while (fifo != NIL) { /* Get next page to visit */ top = linitial(fifo); fifo = list_delete_first(fifo); buffer = ReadBuffer(r, top->blkno); LockBuffer(buffer, GIST_SHARE); gistcheckpage(r, buffer); page = (Page) BufferGetPage(buffer); if (GistPageIsLeaf(page)) { /* * Because we scan the index top-down, all the rest of the pages * in the queue must be leaf pages as well. */ UnlockReleaseBuffer(buffer); break; } top->lsn = PageGetLSN(page); /* * If F_FOLLOW_RIGHT is set, the page to the right doesn't have a * downlink. This should not normally happen.. */ if (GistFollowRight(page)) elog(ERROR, "concurrent GiST page split was incomplete"); if (top->parent && top->parent->lsn < GistPageGetNSN(page) && GistPageGetOpaque(page)->rightlink != InvalidBlockNumber /* sanity check */ ) { /* * Page was split while we looked elsewhere. We didn't see the * downlink to the right page when we scanned the parent, so add * it to the queue now. * * Put the right page ahead of the queue, so that we visit it * next. That's important, because if this is the lowest internal * level, just above leaves, we might already have queued up some * leaf pages, and we assume that there can't be any non-leaf * pages behind leaf pages. */ ptr = (GISTInsertStack *) palloc0(sizeof(GISTInsertStack)); ptr->blkno = GistPageGetOpaque(page)->rightlink; ptr->downlinkoffnum = InvalidOffsetNumber; ptr->parent = top->parent; fifo = lcons(ptr, fifo); } maxoff = PageGetMaxOffsetNumber(page); for (i = FirstOffsetNumber; i <= maxoff; i = OffsetNumberNext(i)) { iid = PageGetItemId(page, i); idxtuple = (IndexTuple) PageGetItem(page, iid); blkno = ItemPointerGetBlockNumber(&(idxtuple->t_tid)); if (blkno == child) { /* Found it! */ UnlockReleaseBuffer(buffer); *downlinkoffnum = i; return top; } else { /* Append this child to the list of pages to visit later */ ptr = (GISTInsertStack *) palloc0(sizeof(GISTInsertStack)); ptr->blkno = blkno; ptr->downlinkoffnum = i; ptr->parent = top; fifo = lappend(fifo, ptr); } } UnlockReleaseBuffer(buffer); } elog(ERROR, "failed to re-find parent of a page in index \"%s\", block %u", RelationGetRelationName(r), child); return NULL; /* keep compiler quiet */ }