+
+/*
+ * Find out whether a specific function argument is constant for the
+ * duration of a query
+ *
+ * Returns false if information is not available
+ */
+bool
+get_fn_expr_arg_stable(FmgrInfo *flinfo, int argnum)
+{
+ /*
+ * can't return anything useful if we have no FmgrInfo or if its fn_expr
+ * node has not been initialized
+ */
+ if (!flinfo || !flinfo->fn_expr)
+ return false;
+
+ return get_call_expr_arg_stable(flinfo->fn_expr, argnum);
+}
+
+/*
+ * Find out whether a specific function argument is constant for the
+ * duration of a query, but working from the calling expression tree
+ *
+ * Returns false if information is not available
+ */
+bool
+get_call_expr_arg_stable(Node *expr, int argnum)
+{
+ List *args;
+ Node *arg;
+
+ if (expr == NULL)
+ return false;
+
+ if (IsA(expr, FuncExpr))
+ args = ((FuncExpr *) expr)->args;
+ else if (IsA(expr, OpExpr))
+ args = ((OpExpr *) expr)->args;
+ else if (IsA(expr, DistinctExpr))
+ args = ((DistinctExpr *) expr)->args;
+ else if (IsA(expr, ScalarArrayOpExpr))
+ args = ((ScalarArrayOpExpr *) expr)->args;
+ else if (IsA(expr, NullIfExpr))
+ args = ((NullIfExpr *) expr)->args;
+ else if (IsA(expr, WindowFunc))
+ args = ((WindowFunc *) expr)->args;
+ else
+ return false;
+
+ if (argnum < 0 || argnum >= list_length(args))
+ return false;
+
+ arg = (Node *) list_nth(args, argnum);
+
+ /*
+ * Either a true Const or an external Param will have a value that doesn't
+ * change during the execution of the query. In future we might want to
+ * consider other cases too, e.g. now().
+ */
+ if (IsA(arg, Const))
+ return true;
+ if (IsA(arg, Param) &&
+ ((Param *) arg)->paramkind == PARAM_EXTERN)
+ return true;
+
+ return false;
+}
+
+/*
+ * Get the VARIADIC flag from the function invocation
+ *
+ * Returns false (the default assumption) if information is not available
+ *
+ * Note this is generally only of interest to VARIADIC ANY functions
+ */
+bool
+get_fn_expr_variadic(FmgrInfo *flinfo)
+{
+ Node *expr;
+
+ /*
+ * can't return anything useful if we have no FmgrInfo or if its fn_expr
+ * node has not been initialized
+ */
+ if (!flinfo || !flinfo->fn_expr)
+ return false;
+
+ expr = flinfo->fn_expr;
+
+ if (IsA(expr, FuncExpr))
+ return ((FuncExpr *) expr)->funcvariadic;
+ else
+ return false;
+}
+
+/*-------------------------------------------------------------------------
+ * Support routines for procedural language implementations
+ *-------------------------------------------------------------------------
+ */
+
+/*
+ * Verify that a validator is actually associated with the language of a
+ * particular function and that the user has access to both the language and
+ * the function. All validators should call this before doing anything
+ * substantial. Doing so ensures a user cannot achieve anything with explicit
+ * calls to validators that he could not achieve with CREATE FUNCTION or by
+ * simply calling an existing function.
+ *
+ * When this function returns false, callers should skip all validation work
+ * and call PG_RETURN_VOID(). This never happens at present; it is reserved
+ * for future expansion.
+ *
+ * In particular, checking that the validator corresponds to the function's
+ * language allows untrusted language validators to assume they process only
+ * superuser-chosen source code. (Untrusted language call handlers, by
+ * definition, do assume that.) A user lacking the USAGE language privilege
+ * would be unable to reach the validator through CREATE FUNCTION, so we check
+ * that to block explicit calls as well. Checking the EXECUTE privilege on
+ * the function is often superfluous, because most users can clone the
+ * function to get an executable copy. It is meaningful against users with no
+ * database TEMP right and no permanent schema CREATE right, thereby unable to
+ * create any function. Also, if the function tracks persistent state by
+ * function OID or name, validating the original function might permit more
+ * mischief than creating and validating a clone thereof.
+ */
+bool
+CheckFunctionValidatorAccess(Oid validatorOid, Oid functionOid)
+{
+ HeapTuple procTup;
+ HeapTuple langTup;
+ Form_pg_proc procStruct;
+ Form_pg_language langStruct;
+ AclResult aclresult;
+
+ /*
+ * Get the function's pg_proc entry. Throw a user-facing error for bad
+ * OID, because validators can be called with user-specified OIDs.
+ */
+ procTup = SearchSysCache1(PROCOID, ObjectIdGetDatum(functionOid));
+ if (!HeapTupleIsValid(procTup))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_FUNCTION),
+ errmsg("function with OID %u does not exist", functionOid)));
+ procStruct = (Form_pg_proc) GETSTRUCT(procTup);
+
+ /*
+ * Fetch pg_language entry to know if this is the correct validation
+ * function for that pg_proc entry.
+ */
+ langTup = SearchSysCache1(LANGOID, ObjectIdGetDatum(procStruct->prolang));
+ if (!HeapTupleIsValid(langTup))
+ elog(ERROR, "cache lookup failed for language %u", procStruct->prolang);
+ langStruct = (Form_pg_language) GETSTRUCT(langTup);
+
+ if (langStruct->lanvalidator != validatorOid)
+ ereport(ERROR,
+ (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+ errmsg("language validation function %u called for language %u instead of %u",
+ validatorOid, procStruct->prolang,
+ langStruct->lanvalidator)));
+
+ /* first validate that we have permissions to use the language */
+ aclresult = pg_language_aclcheck(procStruct->prolang, GetUserId(),
+ ACL_USAGE);
+ if (aclresult != ACLCHECK_OK)
+ aclcheck_error(aclresult, OBJECT_LANGUAGE,
+ NameStr(langStruct->lanname));
+
+ /*
+ * Check whether we are allowed to execute the function itself. If we can
+ * execute it, there should be no possible side-effect of
+ * compiling/validation that execution can't have.
+ */
+ aclresult = pg_proc_aclcheck(functionOid, GetUserId(), ACL_EXECUTE);
+ if (aclresult != ACLCHECK_OK)
+ aclcheck_error(aclresult, OBJECT_FUNCTION, NameStr(procStruct->proname));
+
+ ReleaseSysCache(procTup);
+ ReleaseSysCache(langTup);
+
+ return true;
+}