/* * refresh_matview_datafill */ static void refresh_matview_datafill(DestReceiver *dest, Query *query, const char *queryString) { List *rewritten; PlannedStmt *plan; QueryDesc *queryDesc; Query *copied_query; /* Lock and rewrite, using a copy to preserve the original query. */ copied_query = copyObject(query); AcquireRewriteLocks(copied_query, true, false); rewritten = QueryRewrite(copied_query); /* SELECT should never rewrite to more or less than one SELECT query */ if (list_length(rewritten) != 1) elog(ERROR, "unexpected rewrite result for REFRESH MATERIALIZED VIEW"); query = (Query *) linitial(rewritten); /* Check for user-requested abort. */ CHECK_FOR_INTERRUPTS(); /* Plan the query which will generate data for the refresh. */ plan = pg_plan_query(query, 0, NULL); /* * Use a snapshot with an updated command ID to ensure this query sees * results of any previously executed queries. (This could only matter if * the planner executed an allegedly-stable function that changed the * database contents, but let's do it anyway to be safe.) */ PushCopiedSnapshot(GetActiveSnapshot()); UpdateActiveSnapshotCommandId(); /* Create a QueryDesc, redirecting output to our tuple receiver */ queryDesc = CreateQueryDesc(plan, queryString, GetActiveSnapshot(), InvalidSnapshot, dest, NULL, 0); /* call ExecutorStart to prepare the plan for execution */ ExecutorStart(queryDesc, EXEC_FLAG_WITHOUT_OIDS); /* run the plan */ ExecutorRun(queryDesc, ForwardScanDirection, 0L); /* and clean up */ ExecutorFinish(queryDesc); ExecutorEnd(queryDesc); FreeQueryDesc(queryDesc); PopActiveSnapshot(); }
/* * Walker function to find non immutable function call. */ static bool non_immutable_function_call_walker(Node *node, void *context) { SelectContext *ctx = (SelectContext *) context; if (node == NULL) return false; if (IsA(node, FuncCall)) { FuncCall *fcall = (FuncCall *)node; char *fname; int length = list_length(fcall->funcname); if (length > 0) { if (length == 1) /* no schema qualification? */ { fname = strVal(linitial(fcall->funcname)); } else { fname = strVal(lsecond(fcall->funcname)); /* with schema qualification */ } pool_debug("non_immutable_function_call_walker: function name: %s", fname); /* Check system catalog if the function is immutable */ if (is_immutable_function(fname) == false) { /* Non immutable function call found */ ctx->has_non_immutable_function_call = true; return false; } } } else if (IsA(node, TypeCast)) { /* CURRENT_DATE, CURRENT_TIME, LOCALTIMESTAMP, LOCALTIME etc.*/ TypeCast *tc = (TypeCast *) node; if ((isSystemType((Node *) tc->typeName, "date") || isSystemType((Node *) tc->typeName, "timestamp") || isSystemType((Node *) tc->typeName, "timestamptz") || isSystemType((Node *) tc->typeName, "time") || isSystemType((Node *) tc->typeName, "timetz"))) { ctx->has_non_immutable_function_call = true; return false; } } return raw_expression_tree_walker(node, non_immutable_function_call_walker, context); }
/* * Returns true if the plan contains exactly one command * and that command originates from normal SELECT (i.e. * *not* a SELECT ... INTO). In essence, the result indicates * if the command can be used with SPI_cursor_open * * Parameters * plan A plan previously prepared using SPI_prepare */ bool SPI_is_cursor_plan(void *plan) { _SPI_plan *spiplan = (_SPI_plan *) plan; List *qtlist; if (spiplan == NULL) { SPI_result = SPI_ERROR_ARGUMENT; return false; } qtlist = spiplan->qtlist; if (list_length(spiplan->ptlist) == 1 && list_length(qtlist) == 1) { Query *queryTree = (Query *) linitial((List *) linitial(qtlist)); if (queryTree->commandType == CMD_SELECT && queryTree->into == NULL) return true; } return false; }
/* * ChoosePortalStrategy * Select portal execution strategy given the intended query list. * * See the comments in portal.h. */ PortalStrategy ChoosePortalStrategy(List *parseTrees) { int nSetTag; ListCell *lc; /* * PORTAL_ONE_SELECT and PORTAL_UTIL_SELECT need only consider the * single-Query-struct case, since there are no rewrite rules that can add * auxiliary queries to a SELECT or a utility command. */ if (list_length(parseTrees) == 1) { Query *query = (Query *) linitial(parseTrees); Assert(IsA(query, Query)); if (query->canSetTag) { if (query->commandType == CMD_SELECT && query->into == NULL) return PORTAL_ONE_SELECT; if (query->commandType == CMD_UTILITY && query->utilityStmt != NULL) { if (UtilityReturnsTuples(query->utilityStmt)) return PORTAL_UTIL_SELECT; /* it can't be ONE_RETURNING, so give up */ return PORTAL_MULTI_QUERY; } } } /* * PORTAL_ONE_RETURNING has to allow auxiliary queries added by rewrite. * Choose PORTAL_ONE_RETURNING if there is exactly one canSetTag query and * it has a RETURNING list. */ nSetTag = 0; foreach(lc, parseTrees) { Query *query = (Query *) lfirst(lc); Assert(IsA(query, Query)); if (query->canSetTag) { if (++nSetTag > 1) return PORTAL_MULTI_QUERY; /* no need to look further */ if (query->returningList == NIL) return PORTAL_MULTI_QUERY; /* no need to look further */ } }
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; } }
static PlannedStmt* get_worker_plan(ContinuousView *view) { List *parsetree_list; SelectStmt *selectstmt; parsetree_list = pg_parse_query(view->query); Assert(list_length(parsetree_list) == 1); selectstmt = (SelectStmt *) linitial(parsetree_list); selectstmt = TransformSelectStmtForContProcess(view->matrel, selectstmt, NULL, Worker); return get_plan_from_stmt(view->id, (Node *) selectstmt, view->query, selectstmt->forCombiner); }
/* * RebuildQueryStrings deparses the job query for each task to * include execution-time changes such as function evaluation. */ void RebuildQueryStrings(Query *originalQuery, List *taskList) { ListCell *taskCell = NULL; Oid relationId = ((RangeTblEntry *) linitial(originalQuery->rtable))->relid; foreach(taskCell, taskList) { Task *task = (Task *) lfirst(taskCell); StringInfo newQueryString = makeStringInfo(); Query *query = originalQuery; if (task->insertSelectQuery) { /* for INSERT..SELECT, adjust shard names in SELECT part */ RangeTblEntry *copiedInsertRte = NULL; RangeTblEntry *copiedSubqueryRte = NULL; Query *copiedSubquery = NULL; List *relationShardList = task->relationShardList; ShardInterval *shardInterval = LoadShardInterval(task->anchorShardId); query = copyObject(originalQuery); copiedInsertRte = ExtractInsertRangeTableEntry(query); copiedSubqueryRte = ExtractSelectRangeTableEntry(query); copiedSubquery = copiedSubqueryRte->subquery; AddShardIntervalRestrictionToSelect(copiedSubquery, shardInterval); ReorderInsertSelectTargetLists(query, copiedInsertRte, copiedSubqueryRte); /* setting an alias simplifies deparsing of RETURNING */ if (copiedInsertRte->alias == NULL) { Alias *alias = makeAlias(CITUS_TABLE_ALIAS, NIL); copiedInsertRte->alias = alias; } UpdateRelationToShardNames((Node *) copiedSubquery, relationShardList); } deparse_shard_query(query, relationId, task->anchorShardId, newQueryString); ereport(DEBUG4, (errmsg("query before rebuilding: %s", task->queryString))); ereport(DEBUG4, (errmsg("query after rebuilding: %s", newQueryString->data))); task->queryString = newQueryString->data; }
/* * As for pljavaCheckExtension, livecheck == null when called from _PG_init * (when the real questions are whether PL/Java itself is being loaded, from * what path, and whether or not as an extension). When livecheck is not null, * PL/Java is already alive and the caller wants to know if an extension is * being created for some other reason. That wouldn't even involve this * function, except for the need to work around creating_extension visibility * on Windows. So if livecheck isn't null, this function only needs to proceed * as far as the CREATING_EXTENSION_HACK and then return. */ static void checkLoadPath( bool *livecheck) { List *l; Node *ut; LoadStmt *ls; #ifndef CREATING_EXTENSION_HACK if ( NULL != livecheck ) return; #endif if ( NULL == ActivePortal ) return; l = ActivePortal->stmts; if ( NULL == l ) return; if ( 1 < list_length( l) ) elog(DEBUG2, "ActivePortal lists %d statements", list_length( l)); ut = (Node *)linitial(l); if ( NULL == ut ) { elog(DEBUG2, "got null for first statement from ActivePortal"); return; } if ( T_LoadStmt != nodeTag(ut) ) #ifdef CREATING_EXTENSION_HACK if ( T_CreateExtensionStmt == nodeTag(ut) ) { if ( NULL != livecheck ) { *livecheck = true; return; } getExtensionLoadPath(); if ( NULL != pljavaLoadPath ) pljavaLoadingAsExtension = true; } #endif return; if ( NULL != livecheck ) return; ls = (LoadStmt *)ut; if ( NULL == ls->filename ) { elog(DEBUG2, "got null for a LOAD statement's filename"); return; } pljavaLoadPath = (char const *)MemoryContextStrdup(TopMemoryContext, ls->filename); }
/* * Set environment variables for libpq access */ void set_libpq_envvars(void) { setenv("PGAPPNAME", "plsh", 1); unsetenv("PGCLIENTENCODING"); setenv("PGDATABASE", get_database_name(MyDatabaseId), 1); #if PG_VERSION_NUM >= 90300 if (Unix_socket_directories) { char *rawstring; List *elemlist; rawstring = pstrdup(Unix_socket_directories); if (!SplitDirectoriesString(rawstring, ',', &elemlist)) ereport(WARNING, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("invalid list syntax for \"unix_socket_directories\""))); if (list_length(elemlist)) setenv("PGHOST", linitial(elemlist), 1); else setenv("PGHOST", "localhost", 0); } #else if (UnixSocketDir && *UnixSocketDir) setenv("PGHOST", UnixSocketDir, 1); #endif else setenv("PGHOST", "localhost", 0); { char buf[16]; sprintf(buf, "%u", PostPortNumber); setenv("PGPORT", buf, 1); } if (getenv("PATH")) { char buf[MAXPGPATH]; char *p; strlcpy(buf, my_exec_path, sizeof(buf)); p = strrchr(buf, '/'); snprintf(p, sizeof(buf) - (p - buf), ":%s", getenv("PATH")); setenv("PATH", buf, 1); } }
/* * AtSubCommit_Notify() --- Take care of subtransaction commit. * * Reassign all items in the pending notifies list to the parent transaction. */ void AtSubCommit_Notify(void) { List *parentPendingNotifies; parentPendingNotifies = (List *) linitial(upperPendingNotifies); upperPendingNotifies = list_delete_first(upperPendingNotifies); Assert(list_length(upperPendingNotifies) == GetCurrentTransactionNestLevel() - 2); /* * We could try to eliminate duplicates here, but it seems not worthwhile. */ pendingNotifies = list_concat(parentPendingNotifies, pendingNotifies); }
static SelectStmt * get_worker_select_stmt(ContinuousView* view, SelectStmt** viewptr) { List *parsetree_list; SelectStmt *selectstmt; parsetree_list = pg_parse_query(view->query); Assert(list_length(parsetree_list) == 1); selectstmt = (SelectStmt *) linitial(parsetree_list); selectstmt->swStepFactor = view->sw_step_factor; selectstmt = TransformSelectStmtForContProcess(view->matrel, selectstmt, viewptr, Worker); return selectstmt; }
/* * Extract an expression node from one of following jsonpath path expressions: * EXISTS(jsp) (when 'scalar' is NULL) * jsp == scalar (when 'scalar' is not NULL). * * The current path (@) is passed in 'path'. */ static JsonPathGinNode * extract_jsp_path_expr(JsonPathGinContext *cxt, JsonPathGinPath path, JsonPathItem *jsp, JsonbValue *scalar) { /* extract a list of nodes to be AND-ed */ List *nodes = extract_jsp_path_expr_nodes(cxt, path, jsp, scalar); if (list_length(nodes) <= 0) /* no nodes were extracted => full scan is needed for this path */ return NULL; if (list_length(nodes) == 1) return linitial(nodes); /* avoid extra AND-node */ /* construct AND-node for path with filters */ return make_jsp_expr_node_args(JSP_GIN_AND, nodes); }
/* * ShardLength finds shard placements for the given shardId, extracts the length * of a finalized shard, and returns the shard's length. This function errors * out if we cannot find any finalized shard placements for the given shardId. */ uint64 ShardLength(uint64 shardId) { uint64 shardLength = 0; List *shardPlacementList = FinalizedShardPlacementList(shardId); if (shardPlacementList == NIL) { ereport(ERROR, (errmsg("could not find length of shard " UINT64_FORMAT, shardId), errdetail("Could not find any shard placements for the shard."))); } else { ShardPlacement *shardPlacement = (ShardPlacement *) linitial(shardPlacementList); shardLength = shardPlacement->shardLength; } return shardLength; }
/* * exec_seclabel_stmt -- * * Apply a security label to a database object. */ void exec_seclabel_stmt(SecLabelStmt * stmt) { LabelProvider *provider = NULL; struct objaddr address; struct relation* relation; struct list_cell* lc; /* * Find the named label provider, or if none specified, check whether * there's exactly one, and if so use it. */ if (stmt->provider == NULL) { if (label_provider_list == NIL) { ereport(ERROR, ( errcode(E_INVALID_PARAMETER_VALUE), errmsg("no security label providers have been loaded"))); } if (lnext(list_head(label_provider_list)) != NULL) { ereport(ERROR, ( errcode(E_INVALID_PARAMETER_VALUE), errmsg("must specify provider when multiple security" " label providers have been loaded"))); } provider = (LabelProvider *) linitial(label_provider_list); } else { foreach(lc, label_provider_list) { LabelProvider *lp; lp = lfirst(lc); if (strcmp(stmt->provider, lp->provider_name) == 0) { provider = lp; break; } } if (provider == NULL) ereport(ERROR, ( errcode(E_INVALID_PARAMETER_VALUE), errmsg("security label provider \"%s\" is not loaded", stmt->provider))); }
List * GetVirtualSegmentList(void) { #if 0 List *real_segments = GetSegmentList(); int real_segment_num = list_length(real_segments); Segment *dest; Segment *src; src = linitial(real_segments); dest = CopySegment(src); dest->id = real_segment_num; dest->dbid = real_segment_num + 1; return lappend(real_segments, dest); #else return GetSegmentList(); #endif }
static PlannedStmt * get_plan_from_stmt(Oid id, Node *node, const char *sql, bool is_combine) { Query *query; PlannedStmt *plan; query = linitial(pg_analyze_and_rewrite(node, sql, NULL, 0)); query->isContinuous = true; query->isCombine = is_combine; query->cq_id = id; plan = pg_plan_query(query, 0, NULL); plan->is_continuous = true; plan->is_combine = is_combine; plan->cq_id = id; /* * Unique plans get transformed into ContinuousUnique plans for * continuous query processes. */ if (IsA(plan->planTree, Unique)) { ContinuousUnique *cunique = makeNode(ContinuousUnique); Unique *unique = (Unique *) plan->planTree; memcpy((char *) &cunique->unique, (char *) unique, sizeof(Unique)); cunique->cq_id = id; cunique->unique.plan.type = T_ContinuousUnique; plan->planTree = (Plan *) cunique; Assert(IsA(plan->planTree->lefttree, Sort)); /* Strip out the sort since its not needed */ plan->planTree->lefttree = plan->planTree->lefttree->lefttree; } return plan; }
/* * keyed_trans_startup * * Get type information for the key and value based on argument types */ static KeyValue * keyed_trans_startup(FunctionCallInfo fcinfo) { List *args = NIL; KeyedAggState *state; Node *node; Oid type; MemoryContext old; KeyValue *result; if (AggGetAggref(fcinfo)) args = AggGetAggref(fcinfo)->args; else if (AggGetWindowFunc(fcinfo)) args = AggGetWindowFunc(fcinfo)->args; else elog(ERROR, "fcinfo must be an aggregate function call"); node = linitial(args); type = IsA(node, TargetEntry) ? exprType((Node *) ((TargetEntry *) node)->expr) : exprType(node); old = MemoryContextSwitchTo(fcinfo->flinfo->fn_mcxt); state = palloc0(sizeof(KeyedAggState)); state->key_type = lookup_type_cache(type, TYPECACHE_CMP_PROC_FINFO); if (!OidIsValid(state->key_type->cmp_proc)) elog(ERROR, "could not determine key type"); node = lsecond(args); type = IsA(node, TargetEntry) ? exprType((Node *) ((TargetEntry *) node)->expr) : exprType(node); state->value_type = lookup_type_cache(type, 0); fcinfo->flinfo->fn_extra = state; MemoryContextSwitchTo(old); result = set_kv(state, NULL, PG_GETARG_DATUM(1), PG_ARGISNULL(1), PG_GETARG_DATUM(2), PG_ARGISNULL(2)); result->key_type = state->key_type->type_id; result->value_type = state->value_type->type_id; result->key_collation = PG_GET_COLLATION(); return result; }
/* * AtSubAbort_Notify() --- Take care of subtransaction abort. */ void AtSubAbort_Notify(void) { int my_level = GetCurrentTransactionNestLevel(); /* * All we have to do is pop the stack --- the notifies made in this * subxact are no longer interesting, and the space will be freed when * CurTransactionContext is recycled. * * This routine could be called more than once at a given nesting level if * there is trouble during subxact abort. Avoid dumping core by using * GetCurrentTransactionNestLevel as the indicator of how far we need to * prune the list. */ while (list_length(upperPendingNotifies) > my_level - 2) { pendingNotifies = (List *) linitial(upperPendingNotifies); upperPendingNotifies = list_delete_first(upperPendingNotifies); } }
/* * infer_tupledesc * * Given a stream, infer a TupleDesc based on the supertype of all * the casted types for each of the stream's columns */ static void infer_tupledesc(StreamTargetsEntry *stream) { HASH_SEQ_STATUS status; StreamColumnsEntry *entry; List *names = NIL; List *types = NIL; List *mods = NIL; List *collations = NIL; Const *preferred = makeConst(NUMERIC_OID, -1, 0, -1, 0, false, false); hash_seq_init(&status, stream->colstotypes); while ((entry = (StreamColumnsEntry *) hash_seq_search(&status)) != NULL) { char err[128]; Oid supertype; char category; bool typispreferred; Oid t = exprType(linitial(entry->types)); /* * If there are any numeric types in our target types, we prepend a float8 * to the list of types to select from, as that is our preferred type when * there is any ambiguity about how to interpret numeric types. */ get_type_category_preferred(t, &category, &typispreferred); if (category == TYPCATEGORY_NUMERIC) entry->types = lcons(preferred, entry->types); sprintf(err, "type conflict with stream \"%s\":", get_rel_name(stream->relid)); supertype = select_common_type(NULL, entry->types, err, NULL); names = lappend(names, makeString(entry->name)); types = lappend_int(types, supertype); mods = lappend_int(mods, -1); collations = lappend_int(collations, 0); } stream->desc = BuildDescFromLists(names, types, mods, collations); }
/** * Get weight associated with queue. See queue.c. * * Attention is paid in order to avoid catalog lookups when not allowed. The * superuser() function performs catalog lookups in certain cases. Also the * GetResqueueCapabilityEntry will always do a catalog lookup. In such cases * use the default weight. */ static int ResourceQueueGetPriorityWeight(Oid queueId) { List *capabilitiesList = NULL; List *entry = NULL; ListCell *le = NULL; int weight = BackoffDefaultWeight(); if (!IsTransactionState()) return weight; if (superuser()) return BackoffSuperuserStatementWeight(); if (queueId == InvalidOid) return weight; capabilitiesList = GetResqueueCapabilityEntry(queueId); /* This is a list of * lists */ if (!capabilitiesList) return weight; foreach(le, capabilitiesList) { Value *key = NULL; entry = (List *) lfirst(le); Assert(entry); key = (Value *) linitial(entry); Assert(key->type == T_Integer); /* This is resource type id */ if (intVal(key) == PG_RESRCTYPE_PRIORITY) { Value *val = lsecond(entry); Assert(val->type == T_String); weight = BackoffPriorityValueToInt(strVal(val)); } }
/* * setInverseRecordForList * Set the record value array for the inverse function on a list partition, based * on the given partition rule. * * This function only supports single-column partition key in the partition level. */ static void setInverseRecordForList(PartitionRule *rule, ListCell *listValueCell, Datum *values, bool *nulls, int numAttrs) { Assert(numAttrs == PARTITION_INVERSE_RECORD_NUM_ATTRS); Assert(rule != NULL && rule->parlistvalues != NULL && listValueCell != NULL); /* * Note that in partition rule, list values are stored in a list of lists to support * multi-column partitions. */ List *listValue = (List *)lfirst(listValueCell); /* This function only supports single-column partition key. */ Assert(list_length(listValue) == 1); Const *listValueConst = (Const *)linitial(listValue); Assert(IsA(listValueConst, Const)); values[PARTITION_INVERSE_RECORD_PARCHILDRELID_ATTNO - 1] = ObjectIdGetDatum(rule->parchildrelid); nulls[PARTITION_INVERSE_RECORD_PARCHILDRELID_ATTNO - 1] = false; values[PARTITION_INVERSE_RECORD_MINKEY_ATTNO - 1] = listValueConst->constvalue; nulls[PARTITION_INVERSE_RECORD_MINKEY_ATTNO - 1] = listValueConst->constisnull; values[PARTITION_INVERSE_RECORD_MININCLUDED_ATTNO - 1] = BoolGetDatum(true); nulls[PARTITION_INVERSE_RECORD_MININCLUDED_ATTNO - 1] = false; values[PARTITION_INVERSE_RECORD_MAXKEY_ATTNO - 1] = listValueConst->constvalue; nulls[PARTITION_INVERSE_RECORD_MAXKEY_ATTNO - 1] = false; values[PARTITION_INVERSE_RECORD_MAXINCLUDED_ATTNO - 1] = BoolGetDatum(true); nulls[PARTITION_INVERSE_RECORD_MAXINCLUDED_ATTNO - 1] = false; }
/* * Sample size estimation. */ static void system_samplescangetsamplesize(PlannerInfo *root, RelOptInfo *baserel, List *paramexprs, BlockNumber *pages, double *tuples) { Node *pctnode; float4 samplefract; /* Try to extract an estimate for the sample percentage */ pctnode = (Node *) linitial(paramexprs); pctnode = estimate_expression_value(root, pctnode); if (IsA(pctnode, Const) && !((Const *) pctnode)->constisnull) { samplefract = DatumGetFloat4(((Const *) pctnode)->constvalue); if (samplefract >= 0 && samplefract <= 100 && !isnan(samplefract)) samplefract /= 100.0f; else { /* Default samplefract if the value is bogus */ samplefract = 0.1f; } } else { /* Default samplefract if we didn't obtain a non-null Const */ samplefract = 0.1f; } /* We'll visit a sample of the pages ... */ *pages = clamp_row_est(baserel->pages * samplefract); /* ... and hopefully get a representative number of tuples from them */ *tuples = clamp_row_est(baserel->tuples * samplefract); }
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; }
Datum dbms_assert_schema_name(PG_FUNCTION_ARGS) { Oid namespaceId; AclResult aclresult; text *sname; char *nspname; List *names; if (PG_ARGISNULL(0)) INVALID_SCHEMA_NAME_EXCEPTION(); sname = PG_GETARG_TEXT_P(0); if (EMPTY_STR(sname)) INVALID_SCHEMA_NAME_EXCEPTION(); nspname = text_to_cstring(sname); #ifdef GP_VERSION_NUM names = stringToQualifiedNameList(nspname, "dbms"); #else names = stringToQualifiedNameList(nspname); #endif if (list_length(names) != 1) INVALID_SCHEMA_NAME_EXCEPTION(); namespaceId = GetSysCacheOid(NAMESPACENAME, CStringGetDatum(strVal(linitial(names))), 0, 0, 0); if (!OidIsValid(namespaceId)) INVALID_SCHEMA_NAME_EXCEPTION(); aclresult = pg_namespace_aclcheck(namespaceId, GetUserId(), ACL_USAGE); if (aclresult != ACLCHECK_OK) INVALID_SCHEMA_NAME_EXCEPTION(); PG_RETURN_TEXT_P(sname); }
/* ---------------- * free_exec_state * * Release an exec_state_n along with all remaining working storage. * * Note: this is not responsible for releasing non-memory resources, * such as open relations or buffer pins. But it will shut down any * still-active ExprContexts within the EState. That is sufficient * cleanup for situations where the exec_state_n has only been used for expression * evaluation, and not to run a complete Plan. * * This can be called in any memory context ... so long as it's not one * of the ones to be freed. * ---------------- */ void free_exec_state(exec_state_n *estate) { /* * Shut down and free any remaining ExprContexts. We do this explicitly * to ensure that any remaining shutdown callbacks get called (since they * might need to release resources that aren't simply memory within the * per-query memory context). */ while (estate->es_exprcontexts) { /* * XXX: seems there ought to be a faster way to implement this than * repeated list_delete(), no? */ free_expr_ctx((expr_ctx_n *) linitial(estate->es_exprcontexts), true); /* free_expr_ctx removed the list link for us */ } /* * Free the per-query memory context, thereby releasing all working * memory, including the exec_state_n node itself. */ mctx_delete(estate->es_query_cxt); }
/* * Costing function. */ Datum tsm_bernoulli_cost(PG_FUNCTION_ARGS) { PlannerInfo *root = (PlannerInfo *) PG_GETARG_POINTER(0); Path *path = (Path *) PG_GETARG_POINTER(1); RelOptInfo *baserel = (RelOptInfo *) PG_GETARG_POINTER(2); List *args = (List *) PG_GETARG_POINTER(3); BlockNumber *pages = (BlockNumber *) PG_GETARG_POINTER(4); double *tuples = (double *) PG_GETARG_POINTER(5); Node *pctnode; float4 samplesize; *pages = baserel->pages; pctnode = linitial(args); pctnode = estimate_expression_value(root, pctnode); if (IsA(pctnode, RelabelType)) pctnode = (Node *) ((RelabelType *) pctnode)->arg; if (IsA(pctnode, Const)) { samplesize = DatumGetFloat4(((Const *) pctnode)->constvalue); samplesize /= 100.0; } else { /* Default samplesize if the estimation didn't return Const. */ samplesize = 0.1f; } *tuples = path->rows * samplesize; path->rows = *tuples; PG_RETURN_VOID(); }
/* * ExecIndexBuildScanKeys * Build the index scan keys from the index qualification expressions * * The index quals are passed to the index AM in the form of a ScanKey array. * This routine sets up the ScanKeys, fills in all constant fields of the * ScanKeys, and prepares information about the keys that have non-constant * comparison values. We divide index qual expressions into five types: * * 1. Simple operator with constant comparison value ("indexkey op constant"). * For these, we just fill in a ScanKey containing the constant value. * * 2. Simple operator with non-constant value ("indexkey op expression"). * For these, we create a ScanKey with everything filled in except the * expression value, and set up an IndexRuntimeKeyInfo struct to drive * evaluation of the expression at the right times. * * 3. RowCompareExpr ("(indexkey, indexkey, ...) op (expr, expr, ...)"). * For these, we create a header ScanKey plus a subsidiary ScanKey array, * as specified in access/skey.h. The elements of the row comparison * can have either constant or non-constant comparison values. * * 4. ScalarArrayOpExpr ("indexkey op ANY (array-expression)"). For these, * we create a ScanKey with everything filled in except the comparison value, * and set up an IndexArrayKeyInfo struct to drive processing of the qual. * (Note that we treat all array-expressions as requiring runtime evaluation, * even if they happen to be constants.) * * 5. NullTest ("indexkey IS NULL/IS NOT NULL"). We just fill in the * ScanKey properly. * * This code is also used to prepare ORDER BY expressions for amcanorderbyop * indexes. The behavior is exactly the same, except that we have to look up * the operator differently. Note that only cases 1 and 2 are currently * possible for ORDER BY. * * Input params are: * * planstate: executor state node we are working for * index: the index we are building scan keys for * scanrelid: varno of the index's relation within current query * quals: indexquals (or indexorderbys) expressions * isorderby: true if processing ORDER BY exprs, false if processing quals * *runtimeKeys: ptr to pre-existing IndexRuntimeKeyInfos, or NULL if none * *numRuntimeKeys: number of pre-existing runtime keys * * Output params are: * * *scanKeys: receives ptr to array of ScanKeys * *numScanKeys: receives number of scankeys * *runtimeKeys: receives ptr to array of IndexRuntimeKeyInfos, or NULL if none * *numRuntimeKeys: receives number of runtime keys * *arrayKeys: receives ptr to array of IndexArrayKeyInfos, or NULL if none * *numArrayKeys: receives number of array keys * * Caller may pass NULL for arrayKeys and numArrayKeys to indicate that * ScalarArrayOpExpr quals are not supported. */ void ExecIndexBuildScanKeys(PlanState *planstate, Relation index, Index scanrelid, List *quals, bool isorderby, ScanKey *scanKeys, int *numScanKeys, IndexRuntimeKeyInfo **runtimeKeys, int *numRuntimeKeys, IndexArrayKeyInfo **arrayKeys, int *numArrayKeys) { ListCell *qual_cell; ScanKey scan_keys; IndexRuntimeKeyInfo *runtime_keys; IndexArrayKeyInfo *array_keys; int n_scan_keys; int n_runtime_keys; int max_runtime_keys; int n_array_keys; int j; /* Allocate array for ScanKey structs: one per qual */ n_scan_keys = list_length(quals); scan_keys = (ScanKey) palloc(n_scan_keys * sizeof(ScanKeyData)); /* * runtime_keys array is dynamically resized as needed. We handle it this * way so that the same runtime keys array can be shared between * indexquals and indexorderbys, which will be processed in separate calls * of this function. Caller must be sure to pass in NULL/0 for first * call. */ runtime_keys = *runtimeKeys; n_runtime_keys = max_runtime_keys = *numRuntimeKeys; /* Allocate array_keys as large as it could possibly need to be */ array_keys = (IndexArrayKeyInfo *) palloc0(n_scan_keys * sizeof(IndexArrayKeyInfo)); n_array_keys = 0; /* * for each opclause in the given qual, convert the opclause into a single * scan key */ j = 0; foreach(qual_cell, quals) { Expr *clause = (Expr *) lfirst(qual_cell); ScanKey this_scan_key = &scan_keys[j++]; Oid opno; /* operator's OID */ RegProcedure opfuncid; /* operator proc id used in scan */ Oid opfamily; /* opfamily of index column */ int op_strategy; /* operator's strategy number */ Oid op_lefttype; /* operator's declared input types */ Oid op_righttype; Expr *leftop; /* expr on lhs of operator */ Expr *rightop; /* expr on rhs ... */ AttrNumber varattno; /* att number used in scan */ if (IsA(clause, OpExpr)) { /* indexkey op const or indexkey op expression */ int flags = 0; Datum scanvalue; opno = ((OpExpr *) clause)->opno; opfuncid = ((OpExpr *) clause)->opfuncid; /* * leftop should be the index key Var, possibly relabeled */ leftop = (Expr *) get_leftop(clause); if (leftop && IsA(leftop, RelabelType)) leftop = ((RelabelType *) leftop)->arg; Assert(leftop != NULL); if (!(IsA(leftop, Var) && ((Var *) leftop)->varno == scanrelid)) elog(ERROR, "indexqual doesn't have key on left side"); varattno = ((Var *) leftop)->varattno; if (varattno < 1 || varattno > index->rd_index->indnatts) elog(ERROR, "bogus index qualification"); /* * We have to look up the operator's strategy number. This * provides a cross-check that the operator does match the index. */ opfamily = index->rd_opfamily[varattno - 1]; get_op_opfamily_properties(opno, opfamily, isorderby, &op_strategy, &op_lefttype, &op_righttype); if (isorderby) flags |= SK_ORDER_BY; /* * rightop is the constant or variable comparison value */ rightop = (Expr *) get_rightop(clause); if (rightop && IsA(rightop, RelabelType)) rightop = ((RelabelType *) rightop)->arg; Assert(rightop != NULL); if (IsA(rightop, Const)) { /* OK, simple constant comparison value */ scanvalue = ((Const *) rightop)->constvalue; if (((Const *) rightop)->constisnull) flags |= SK_ISNULL; } else { /* Need to treat this one as a runtime key */ if (n_runtime_keys >= max_runtime_keys) { if (max_runtime_keys == 0) { max_runtime_keys = 8; runtime_keys = (IndexRuntimeKeyInfo *) palloc(max_runtime_keys * sizeof(IndexRuntimeKeyInfo)); } else { max_runtime_keys *= 2; runtime_keys = (IndexRuntimeKeyInfo *) repalloc(runtime_keys, max_runtime_keys * sizeof(IndexRuntimeKeyInfo)); } } runtime_keys[n_runtime_keys].scan_key = this_scan_key; runtime_keys[n_runtime_keys].key_expr = ExecInitExpr(rightop, planstate); runtime_keys[n_runtime_keys].key_toastable = TypeIsToastable(op_righttype); n_runtime_keys++; scanvalue = (Datum) 0; } /* * initialize the scan key's fields appropriately */ ScanKeyEntryInitialize(this_scan_key, flags, varattno, /* attribute number to scan */ op_strategy, /* op's strategy */ op_righttype, /* strategy subtype */ ((OpExpr *) clause)->inputcollid, /* collation */ opfuncid, /* reg proc to use */ scanvalue); /* constant */ } else if (IsA(clause, RowCompareExpr)) { /* (indexkey, indexkey, ...) op (expression, expression, ...) */ RowCompareExpr *rc = (RowCompareExpr *) clause; ListCell *largs_cell = list_head(rc->largs); ListCell *rargs_cell = list_head(rc->rargs); ListCell *opnos_cell = list_head(rc->opnos); ListCell *collids_cell = list_head(rc->inputcollids); ScanKey first_sub_key; int n_sub_key; Assert(!isorderby); first_sub_key = (ScanKey) palloc(list_length(rc->opnos) * sizeof(ScanKeyData)); n_sub_key = 0; /* Scan RowCompare columns and generate subsidiary ScanKey items */ while (opnos_cell != NULL) { ScanKey this_sub_key = &first_sub_key[n_sub_key]; int flags = SK_ROW_MEMBER; Datum scanvalue; Oid inputcollation; /* * leftop should be the index key Var, possibly relabeled */ leftop = (Expr *) lfirst(largs_cell); largs_cell = lnext(largs_cell); if (leftop && IsA(leftop, RelabelType)) leftop = ((RelabelType *) leftop)->arg; Assert(leftop != NULL); if (!(IsA(leftop, Var) && ((Var *) leftop)->varno == scanrelid)) elog(ERROR, "indexqual doesn't have key on left side"); varattno = ((Var *) leftop)->varattno; /* * We have to look up the operator's associated btree support * function */ opno = lfirst_oid(opnos_cell); opnos_cell = lnext(opnos_cell); if (index->rd_rel->relam != BTREE_AM_OID || varattno < 1 || varattno > index->rd_index->indnatts) elog(ERROR, "bogus RowCompare index qualification"); opfamily = index->rd_opfamily[varattno - 1]; get_op_opfamily_properties(opno, opfamily, isorderby, &op_strategy, &op_lefttype, &op_righttype); if (op_strategy != rc->rctype) elog(ERROR, "RowCompare index qualification contains wrong operator"); opfuncid = get_opfamily_proc(opfamily, op_lefttype, op_righttype, BTORDER_PROC); inputcollation = lfirst_oid(collids_cell); collids_cell = lnext(collids_cell); /* * rightop is the constant or variable comparison value */ rightop = (Expr *) lfirst(rargs_cell); rargs_cell = lnext(rargs_cell); if (rightop && IsA(rightop, RelabelType)) rightop = ((RelabelType *) rightop)->arg; Assert(rightop != NULL); if (IsA(rightop, Const)) { /* OK, simple constant comparison value */ scanvalue = ((Const *) rightop)->constvalue; if (((Const *) rightop)->constisnull) flags |= SK_ISNULL; } else { /* Need to treat this one as a runtime key */ if (n_runtime_keys >= max_runtime_keys) { if (max_runtime_keys == 0) { max_runtime_keys = 8; runtime_keys = (IndexRuntimeKeyInfo *) palloc(max_runtime_keys * sizeof(IndexRuntimeKeyInfo)); } else { max_runtime_keys *= 2; runtime_keys = (IndexRuntimeKeyInfo *) repalloc(runtime_keys, max_runtime_keys * sizeof(IndexRuntimeKeyInfo)); } } runtime_keys[n_runtime_keys].scan_key = this_sub_key; runtime_keys[n_runtime_keys].key_expr = ExecInitExpr(rightop, planstate); runtime_keys[n_runtime_keys].key_toastable = TypeIsToastable(op_righttype); n_runtime_keys++; scanvalue = (Datum) 0; } /* * initialize the subsidiary scan key's fields appropriately */ ScanKeyEntryInitialize(this_sub_key, flags, varattno, /* attribute number */ op_strategy, /* op's strategy */ op_righttype, /* strategy subtype */ inputcollation, /* collation */ opfuncid, /* reg proc to use */ scanvalue); /* constant */ n_sub_key++; } /* Mark the last subsidiary scankey correctly */ first_sub_key[n_sub_key - 1].sk_flags |= SK_ROW_END; /* * We don't use ScanKeyEntryInitialize for the header because it * isn't going to contain a valid sk_func pointer. */ MemSet(this_scan_key, 0, sizeof(ScanKeyData)); this_scan_key->sk_flags = SK_ROW_HEADER; this_scan_key->sk_attno = first_sub_key->sk_attno; this_scan_key->sk_strategy = rc->rctype; /* sk_subtype, sk_collation, sk_func not used in a header */ this_scan_key->sk_argument = PointerGetDatum(first_sub_key); } else if (IsA(clause, ScalarArrayOpExpr)) { /* indexkey op ANY (array-expression) */ ScalarArrayOpExpr *saop = (ScalarArrayOpExpr *) clause; Assert(!isorderby); Assert(saop->useOr); opno = saop->opno; opfuncid = saop->opfuncid; /* * leftop should be the index key Var, possibly relabeled */ leftop = (Expr *) linitial(saop->args); if (leftop && IsA(leftop, RelabelType)) leftop = ((RelabelType *) leftop)->arg; Assert(leftop != NULL); if (!(IsA(leftop, Var) && ((Var *) leftop)->varno == scanrelid)) elog(ERROR, "indexqual doesn't have key on left side"); varattno = ((Var *) leftop)->varattno; if (varattno < 1 || varattno > index->rd_index->indnatts) elog(ERROR, "bogus index qualification"); /* * We have to look up the operator's strategy number. This * provides a cross-check that the operator does match the index. */ opfamily = index->rd_opfamily[varattno - 1]; get_op_opfamily_properties(opno, opfamily, isorderby, &op_strategy, &op_lefttype, &op_righttype); /* * rightop is the constant or variable array value */ rightop = (Expr *) lsecond(saop->args); if (rightop && IsA(rightop, RelabelType)) rightop = ((RelabelType *) rightop)->arg; Assert(rightop != NULL); array_keys[n_array_keys].scan_key = this_scan_key; array_keys[n_array_keys].array_expr = ExecInitExpr(rightop, planstate); /* the remaining fields were zeroed by palloc0 */ n_array_keys++; /* * initialize the scan key's fields appropriately */ ScanKeyEntryInitialize(this_scan_key, 0, /* flags */ varattno, /* attribute number to scan */ op_strategy, /* op's strategy */ op_righttype, /* strategy subtype */ saop->inputcollid, /* collation */ opfuncid, /* reg proc to use */ (Datum) 0); /* constant */ } else if (IsA(clause, NullTest)) { /* indexkey IS NULL or indexkey IS NOT NULL */ NullTest *ntest = (NullTest *) clause; int flags; Assert(!isorderby); /* * argument should be the index key Var, possibly relabeled */ leftop = ntest->arg; if (leftop && IsA(leftop, RelabelType)) leftop = ((RelabelType *) leftop)->arg; Assert(leftop != NULL); if (!(IsA(leftop, Var) && ((Var *) leftop)->varno == scanrelid)) elog(ERROR, "NullTest indexqual has wrong key"); varattno = ((Var *) leftop)->varattno; /* * initialize the scan key's fields appropriately */ switch (ntest->nulltesttype) { case IS_NULL: flags = SK_ISNULL | SK_SEARCHNULL; break; case IS_NOT_NULL: flags = SK_ISNULL | SK_SEARCHNOTNULL; break; default: elog(ERROR, "unrecognized nulltesttype: %d", (int) ntest->nulltesttype); flags = 0; /* keep compiler quiet */ break; } ScanKeyEntryInitialize(this_scan_key, flags, varattno, /* attribute number to scan */ InvalidStrategy, /* no strategy */ InvalidOid, /* no strategy subtype */ InvalidOid, /* no collation */ InvalidOid, /* no reg proc for this */ (Datum) 0); /* constant */ } else elog(ERROR, "unsupported indexqual type: %d", (int) nodeTag(clause)); }
/* * preprocess_minmax_aggregates - preprocess MIN/MAX aggregates * * Check to see whether the query contains MIN/MAX aggregate functions that * might be optimizable via indexscans. If it does, and all the aggregates * are potentially optimizable, then set up root->minmax_aggs with a list of * these aggregates. * * Note: we are passed the preprocessed targetlist separately, because it's * not necessarily equal to root->parse->targetList. */ void preprocess_minmax_aggregates(PlannerInfo *root, List *tlist) { Query *parse = root->parse; FromExpr *jtnode; RangeTblRef *rtr; RangeTblEntry *rte; List *aggs_list; ListCell *lc; /* minmax_aggs list should be empty at this point */ Assert(root->minmax_aggs == NIL); /* Nothing to do if query has no aggregates */ if (!parse->hasAggs) return; Assert(!parse->setOperations); /* shouldn't get here if a setop */ Assert(parse->rowMarks == NIL); /* nor if FOR UPDATE */ /* * Reject unoptimizable cases. * * We don't handle GROUP BY or windowing, because our current * implementations of grouping require looking at all the rows anyway, and * so there's not much point in optimizing MIN/MAX. */ if (parse->groupClause || parse->hasWindowFuncs) return; /* * We also restrict the query to reference exactly one table, since join * conditions can't be handled reasonably. (We could perhaps handle a * query containing cartesian-product joins, but it hardly seems worth the * trouble.) However, the single real table could be buried in several * levels of FromExpr due to subqueries. Note the single table could be * an inheritance parent, too. */ jtnode = parse->jointree; while (IsA(jtnode, FromExpr)) { if (list_length(jtnode->fromlist) != 1) return; jtnode = linitial(jtnode->fromlist); } if (!IsA(jtnode, RangeTblRef)) return; rtr = (RangeTblRef *) jtnode; rte = planner_rt_fetch(rtr->rtindex, root); if (rte->rtekind != RTE_RELATION) return; /* * Scan the tlist and HAVING qual to find all the aggregates and verify * all are MIN/MAX aggregates. Stop as soon as we find one that isn't. */ aggs_list = NIL; if (find_minmax_aggs_walker((Node *) tlist, &aggs_list)) return; if (find_minmax_aggs_walker(parse->havingQual, &aggs_list)) return; /* * OK, there is at least the possibility of performing the optimization. * Build pathkeys (and thereby EquivalenceClasses) for each aggregate. * The existence of the EquivalenceClasses will prompt the path generation * logic to try to build paths matching the desired sort ordering(s). * * Note: the pathkeys are non-canonical at this point. They'll be fixed * later by canonicalize_all_pathkeys(). */ foreach(lc, aggs_list) { MinMaxAggInfo *mminfo = (MinMaxAggInfo *) lfirst(lc); mminfo->pathkeys = make_pathkeys_for_aggregate(root, mminfo->target, mminfo->aggsortop); }
/* * optimize_minmax_aggregates - check for optimizing MIN/MAX via indexes * * This checks to see if we can replace MIN/MAX aggregate functions by * subqueries of the form * (SELECT col FROM tab WHERE ... ORDER BY col ASC/DESC LIMIT 1) * Given a suitable index on tab.col, this can be much faster than the * generic scan-all-the-rows plan. * * We are passed the preprocessed tlist, and the best path * devised for computing the input of a standard Agg node. If we are able * to optimize all the aggregates, and the result is estimated to be cheaper * than the generic aggregate method, then generate and return a Plan that * does it that way. Otherwise, return NULL. */ Plan * optimize_minmax_aggregates(PlannerInfo *root, List *tlist, Path *best_path) { Query *parse = root->parse; FromExpr *jtnode; RangeTblRef *rtr; RangeTblEntry *rte; RelOptInfo *rel; List *aggs_list; ListCell *l; Cost total_cost; Path agg_p; Plan *plan; Node *hqual; QualCost tlist_cost; /* Nothing to do if query has no aggregates */ if (!parse->hasAggs) return NULL; Assert(!parse->setOperations); /* shouldn't get here if a setop */ Assert(parse->rowMarks == NIL); /* nor if FOR UPDATE */ /* * Reject unoptimizable cases. * * We don't handle GROUP BY, because our current implementations of * grouping require looking at all the rows anyway, and so there's not * much point in optimizing MIN/MAX. */ if (parse->groupClause) return NULL; /* * We also restrict the query to reference exactly one table, since join * conditions can't be handled reasonably. (We could perhaps handle a * query containing cartesian-product joins, but it hardly seems worth the * trouble.) However, the single real table could be buried in several * levels of FromExpr. */ jtnode = parse->jointree; while (IsA(jtnode, FromExpr)) { if (list_length(jtnode->fromlist) != 1) return NULL; jtnode = linitial(jtnode->fromlist); } if (!IsA(jtnode, RangeTblRef)) return NULL; rtr = (RangeTblRef *) jtnode; rte = rt_fetch(rtr->rtindex, parse->rtable); if (rte->rtekind != RTE_RELATION || rte->inh) return NULL; rel = find_base_rel(root, rtr->rtindex); /* * Since this optimization is not applicable all that often, we want to * fall out before doing very much work if possible. Therefore we do the * work in several passes. The first pass scans the tlist and HAVING qual * to find all the aggregates and verify that each of them is a MIN/MAX * aggregate. If that succeeds, the second pass looks at each aggregate * to see if it is optimizable; if so we make an IndexPath describing how * we would scan it. (We do not try to optimize if only some aggs are * optimizable, since that means we'll have to scan all the rows anyway.) * If that succeeds, we have enough info to compare costs against the * generic implementation. Only if that test passes do we build a Plan. */ /* Pass 1: find all the aggregates */ aggs_list = NIL; if (find_minmax_aggs_walker((Node *) tlist, &aggs_list)) return NULL; if (find_minmax_aggs_walker(parse->havingQual, &aggs_list)) return NULL; /* Pass 2: see if each one is optimizable */ total_cost = 0; foreach(l, aggs_list) { MinMaxAggInfo *info = (MinMaxAggInfo *) lfirst(l); if (!build_minmax_path(root, rel, info)) return NULL; total_cost += info->pathcost; }
/* * Preprocess the query string and build up hash_cookie, which will be * passed to caql_switch later. */ struct caql_hash_cookie * cq_lookup(const char *str, unsigned int len, cq_list *pcql) { Node *query; struct caql_hash_cookie *hash_cookie; hash_cookie = caql_get_parser_cache(str, len); if (hash_cookie != NULL) return hash_cookie; query = caql_raw_parser(str, (const char *) pcql->filename, pcql->lineno); if (query == NULL) return NULL; hash_cookie = palloc0(sizeof(struct caql_hash_cookie)); switch(nodeTag(query)) { case T_CaQLSelect: { CaQLSelect *node = (CaQLSelect *) query; char *attname; if (node->forupdate) hash_cookie->bUpdate = true; if (node->count) hash_cookie->bCount = true; hash_cookie->relation = catcore_lookup_rel(node->from); if (hash_cookie->relation == NULL) elog(ERROR, "could not find relation \"%s\" in %s at %s:%d", node->from, str, pcql->filename, pcql->lineno); attname = strVal(linitial(node->targetlist)); /* * Look up attribute number if target list has a column. * '*' includes count('*'). The first character test is * not wrong due to the syntax limitation, and this is quick. */ if (attname[0] != '*') { hash_cookie->attnum = catcore_lookup_attnum(hash_cookie->relation, attname, &hash_cookie->atttype); if (hash_cookie->attnum == InvalidAttrNumber) elog(ERROR, "could not find attribute \"%s\" in %s at %s:%d", attname, str, pcql->filename, pcql->lineno); } hash_cookie->bAllEqual = caql_process_predicates(hash_cookie, node->where); } break; case T_CaQLInsert: { CaQLInsert *node = (CaQLInsert *) query; hash_cookie->bInsert = true; hash_cookie->relation = catcore_lookup_rel(node->into); if (hash_cookie->relation == NULL) elog(ERROR, "could not find relation \"%s\" in %s at %s:%d", node->into, str, pcql->filename, pcql->lineno); } break; case T_CaQLDelete: { CaQLDelete *node = (CaQLDelete *) query; hash_cookie->bDelete = true; hash_cookie->relation = catcore_lookup_rel(node->from); if (hash_cookie->relation == NULL) elog(ERROR, "could not find relation \"%s\" in %s at %s:%d", node->from, str, pcql->filename, pcql->lineno); hash_cookie->bAllEqual = caql_process_predicates(hash_cookie, node->where); } break; default: return NULL; } hash_cookie->name = str; hash_cookie->query = query; hash_cookie->file = (char *) pcql->filename; hash_cookie->lineno = pcql->lineno; /* Find an available index based on predicates or ORDER BY */ hash_cookie->index = caql_find_index(hash_cookie, query); if (hash_cookie->index != NULL) hash_cookie->syscacheid = GetSysCacheId(hash_cookie->index->indexoid); else hash_cookie->syscacheid = -1; caql_put_parser_cache(str, len, hash_cookie); return hash_cookie; }