/* * Copyright (c) 2024 Huawei Technologies Co.,Ltd. * * openGauss is licensed under Mulan PSL v2. * You can use this software according to the terms and conditions of the Mulan PSL v2. * You may obtain a copy of Mulan PSL v2 at: * * http://license.coscl.org.cn/MulanPSL2 * * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. * See the Mulan PSL v2 for more details. * -------------------------------------------------------------------------------------- * * gms_utility.cpp * gms_utility provides various utility subprograms. * * * IDENTIFICATION * contrib/gms_utility/gms_utility.cpp * * -------------------------------------------------------------------------------------- */ #include "postgres.h" #include "funcapi.h" #include "fmgr.h" #include "access/skey.h" #include "access/heapam.h" #include "catalog/indexing.h" #include "catalog/heap.h" #include "commands/sqladvisor.h" #include "commands/vacuum.h" #include "executor/spi.h" #include "lib/stringinfo.h" #include "libpq/md5.h" #include "utils/builtins.h" #include "utils/fmgroids.h" #include "utils/lsyscache.h" #include "utils/numeric.h" #include "utils/numeric_gs.h" #include "tcop/tcopprot.h" #include "parser/keywords.h" #include "gms_utility.h" PG_MODULE_MAGIC; static const char CANON_INVALID_CHARS[] = {'`', '~', '!', '@', '%', '^', '&', '*', '(', ')', '-', '=', '+', '[', ']', '{', '}', '/', '\\', '|', ';', ':', '?', '<', '>'}; static const char VALID_IDENT_CHARS[] = {'#', '$', '_'}; static const char TOKENIZE_DANGER_CHARS[] = {'`', '~', '%', '\\', ';', '?'}; static const char TOKENIZE_TRUNCATE_CHARS[] = {'!', '^', '&', '*', '(', ')', '-', '=', '+', '[', ']', '{', '}', '/', '|', ':', '<', '>'}; typedef Oid (*SearchOidByName)(Oid namespaceId, char* name, NameResolveVar* var); static List* GetRelationsInSchema(char *namespc); static void DoAnalyzeSchemaStatistic(char* schema, AnalyzeVar* var, AnalyzeMethodOpt methodOpt); static void DoDeleteSchemaStatistic(char* schema); static bool DetectKeyword(char* words); static bool CheckLegalIdenty(char* w, bool checkDigital, bool checkKeyword); static TokenizeVar* MakeTokenizeVar(); static void DestoryTokenizeVar(TokenizeVar* var); static TokenizeVar* NameParseInternal(char* name, int len); static void ResolveContextName(NameResolveContext context, Oid namespaceId, char* resolveName, NameResolveVar* var, SearchOidByName searchmtd); PG_FUNCTION_INFO_V1(gms_analyze_schema); PG_FUNCTION_INFO_V1(gms_canonicalize); PG_FUNCTION_INFO_V1(gms_compile_schema); PG_FUNCTION_INFO_V1(gms_expand_sql_text); PG_FUNCTION_INFO_V1(gms_get_cpu_time); PG_FUNCTION_INFO_V1(gms_get_endianness); PG_FUNCTION_INFO_V1(gms_get_sql_hash); PG_FUNCTION_INFO_V1(gms_name_tokenize); PG_FUNCTION_INFO_V1(gms_name_resolve); PG_FUNCTION_INFO_V1(gms_is_bit_set); PG_FUNCTION_INFO_V1(gms_old_current_schema); static List* GetIndexColName(List* colnamesList, Oid relid, int2 attnum) { if (attnum <= 0) { /* Hidden columns */ return colnamesList; } char* colname = NULL; Form_pg_attribute pgAttributeTuple = NULL; HeapTuple attrTuple; attrTuple = SearchSysCache2(ATTNUM, ObjectIdGetDatum(relid), Int16GetDatum(attnum)); if (!HeapTupleIsValid(attrTuple)) { return colnamesList; } pgAttributeTuple = (Form_pg_attribute)GETSTRUCT(attrTuple); colnamesList = lappend(colnamesList, pstrdup(pgAttributeTuple->attname.data)); ReleaseSysCache(attrTuple); return colnamesList; } static void VacuumTableAclCheck(Oid relid, char* relname) { AclResult aclresult = pg_class_aclcheck(relid, GetUserId(), ACL_VACUUM); HeapTuple tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relid)); Form_pg_class rel = (Form_pg_class)GETSTRUCT(tuple); if (aclresult != ACLCHECK_OK && !(pg_class_ownercheck(relid, GetUserId()) || (pg_database_ownercheck(u_sess->proc_cxt.MyDatabaseId, GetUserId()) && !rel->relisshared) || (isOperatoradmin(GetUserId()) && u_sess->attr.attr_security.operation_mode))) { ReleaseSysCache(tuple); aclcheck_error(aclresult, ACL_KIND_CLASS, relname); } ReleaseSysCache(tuple); } static List* GetRelationsInSchema(char *namespc) { Relation pgClassRel = NULL; ScanKeyData skey[1]; SysScanDesc sysscan; HeapTuple tuple; List* tbl_relnames = NIL; Oid nspid; nspid = get_namespace_oid(namespc, false); ScanKeyInit(&skey[0], Anum_pg_class_relnamespace, BTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(nspid)); pgClassRel = heap_open(RelationRelationId, AccessShareLock); sysscan = systable_beginscan(pgClassRel, InvalidOid, false, SnapshotNow, 1, skey); while (HeapTupleIsValid(tuple = systable_getnext(sysscan))) { Form_pg_class reltup = (Form_pg_class)GETSTRUCT(tuple); if ((reltup->relkind == RELKIND_RELATION || reltup->relkind == RELKIND_MATVIEW) && !(reltup->relpersistence == RELPERSISTENCE_TEMP || reltup->relpersistence == RELPERSISTENCE_GLOBAL_TEMP)) { VacuumTableAclCheck(HeapTupleGetOid(tuple), reltup->relname.data); tbl_relnames = lappend(tbl_relnames, pstrdup(reltup->relname.data)); } } systable_endscan(sysscan); heap_close(pgClassRel, AccessShareLock); return tbl_relnames; } static char* GetIndexNameById(Oid indexoid) { char* indexName = NULL; HeapTuple idxRelTuple = SearchSysCache1(RELOID, ObjectIdGetDatum(indexoid)); if (!HeapTupleIsValid(idxRelTuple)) { return NULL; } Form_pg_class reltup = (Form_pg_class)GETSTRUCT(idxRelTuple); indexName = pstrdup(reltup->relname.data); ReleaseSysCache(idxRelTuple); return indexName; } static List* GetAllIndexColumnNames(char* schemaname, char* relname) { List* colnamesList = NIL; Oid relid = InvalidOid; Oid namespaceId = InvalidOid; ScanKeyData skey[1]; SysScanDesc sysscan; Relation indexRel; HeapTuple idxTuple; Form_pg_index pgIndexTuple; int n; namespaceId = LookupExplicitNamespace(schemaname); relid = get_relname_relid(relname, namespaceId); ScanKeyInit(&skey[0], Anum_pg_index_indrelid, BTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(relid)); indexRel = heap_open(IndexRelationId, AccessShareLock); sysscan = systable_beginscan(indexRel, IndexIndrelidIndexId, true, SnapshotNow, 1, skey); while (HeapTupleIsValid(idxTuple = systable_getnext(sysscan))) { pgIndexTuple = (Form_pg_index)GETSTRUCT(idxTuple); if (!(IndexIsUsable(pgIndexTuple) && IndexIsValid(pgIndexTuple) && GetIndexEnableStateByTuple(idxTuple))) { systable_endscan(sysscan); heap_close(indexRel, AccessShareLock); ereport(ERROR, (errcode(ERRCODE_INVALID_STATUS), errmsg("index \"%s\" is not available", GetIndexNameById(pgIndexTuple->indexrelid)))); } for (n = 0; n < pgIndexTuple->indnatts; n++) { colnamesList = GetIndexColName(colnamesList, relid, pgIndexTuple->indkey.values[n]); } } systable_endscan(sysscan); heap_close(indexRel, AccessShareLock); return colnamesList; } static void UpdateRelTuplesAndPages(Oid relid) { Relation pgClassRel = NULL; HeapTuple tuple; HeapTuple newtuple; Form_pg_class reltuple; Datum values[Natts_pg_class] = {0}; bool nulls[Natts_pg_class] = {false}; bool replaces[Natts_pg_class] = {false}; pgClassRel = heap_open(RelationRelationId, RowExclusiveLock); tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relid)); if (!HeapTupleIsValid(tuple)) { ereport(ERROR, (errcode(ERRCODE_CACHE_LOOKUP_FAILED), errmsg("cache lookup failed for relation %u", relid))); } reltuple = (Form_pg_class)GETSTRUCT(tuple); values[Anum_pg_class_relpages - 1] = Float8GetDatum(0); replaces[Anum_pg_class_relpages - 1] = true; values[Anum_pg_class_reltuples - 1] = Float8GetDatum(0); replaces[Anum_pg_class_reltuples - 1] = true; newtuple = heap_modify_tuple(tuple, RelationGetDescr(pgClassRel), values, nulls, replaces); simple_heap_update(pgClassRel, &newtuple->t_self, newtuple); CatalogUpdateIndexes(pgClassRel, newtuple); heap_freetuple_ext(tuple); heap_freetuple_ext(newtuple); heap_close(pgClassRel, RowExclusiveLock); } static AnalyzeMethodOpt GetMethodOpFromStr(char* val) { bool canFollowSize = false; int offsetSize = 0; AnalyzeMethodOpt opt; char* str = val; if (0 == strncasecmp(str, "FORTABLE", 8)) { opt = METHOD_OPT_TABLE; offsetSize = 8; } else if (0 == strncasecmp(str, "FORALLINDEXES", 13)) { opt = METHOD_OPT_ALL_INDEX; offsetSize = 13; } else if (0 == strncasecmp(str, "FORALLCOLUMNS", 13)) { opt = METHOD_OPT_ALL_COLUMN; canFollowSize = true; offsetSize = 13; } else if (0 == strncasecmp(str, "FORALLINDEXEDCOLUMNS", 20)) { opt = METHOD_OPT_ALL_INDEX; canFollowSize = true; offsetSize = 20; } else { ereport(ERROR, (errcode(ERRCODE_UNDEFINED_KEY), errmsg("unrecognized param value"))); } str += offsetSize; if (canFollowSize) { if (*str == '\0') { return opt; } if (0 != strncasecmp(str, "SIZE", 4)) { ereport(ERROR, (errcode(ERRCODE_UNDEFINED_KEY), errmsg("unrecognized param value"))); } str += 4; if (1 != sscanf(str, "%d", &offsetSize) || offsetSize <= 0) { ereport(ERROR, (errcode(ERRCODE_UNDEFINED_KEY), errmsg("unrecognized param value"))); } } else { if (*str != '\0') { ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("invalid input value for method_opt"))); } } return opt; } static char* GetNextTokenSplitBySpace(char* val, int* travelLen) { int len = 0; char* result; char* str = val + *travelLen; while (*str != '\0' && isspace((unsigned char)*str)) { str++; (*travelLen)++; } while (*(str + len) != '\0' && !isspace((unsigned char)*(str + len))) { len++; } (*travelLen) += len; if (len == 0) return NULL; result = (char *) palloc0(len + 1); errno_t rc = memcpy_s(result, len + 1, str, len); securec_check(rc, "\0", "\0"); return result; } static AnalyzeMethodOpt HandleMethodOpt(text* methodOpt) { if (NULL == methodOpt) { return METHOD_OPT_TABLE; } char* str = TextDatumGetCString(methodOpt); char* token; int travelLen; StringInfo format; AnalyzeMethodOpt tOpt; AnalyzeMethodOpt opt = METHOD_OPT_ALL_INDEX; format = makeStringInfo(); travelLen = 0; while ((token = GetNextTokenSplitBySpace(str, &travelLen)) != NULL) { if (format->len == 0 && 0 != strcasecmp(token, "FOR")) { ereport(ERROR, (errcode(ERRCODE_UNDEFINED_KEY), errmsg("unrecognized param value: \"%s\"", str))); } if (0 == strcasecmp(token, "FOR") && format->len > 0) { tOpt = GetMethodOpFromStr(format->data); opt = (int)tOpt < (int)opt ? tOpt : opt; resetStringInfo(format); } appendStringInfo(format, "%s", token); pfree(token); token = NULL; } pfree_ext(str); tOpt = GetMethodOpFromStr(format->data); opt = (int)tOpt < (int)opt ? tOpt : opt; DestroyStringInfo(format); return opt; } static AnalyzeVar* MakeAnalyzeVar(char* method, FunctionCallInfo fcinfo, int argOffset) { Numeric estimateRows; Numeric estimatePercent; AnalyzeVar* result = (AnalyzeVar *) palloc(sizeof(AnalyzeVar)); result->isEstimate = 0 == pg_strcasecmp(method, "ESTIMATE"); if (!result->isEstimate) { return result; } result->validRows = !PG_ARGISNULL(argOffset); result->validPercent = !PG_ARGISNULL(argOffset + 1); if(result->validRows) { estimateRows = PG_GETARG_NUMERIC(argOffset); int64 rows = convert_short_numeric_to_int64_byscale(estimateRows, NUMERIC_DSCALE(estimateRows)); if (rows < 0) { pfree(result); ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("invalid value \"%ld\" for \"estimate_rows\"", rows))); } result->estimateRows = rows; } if (!result->validRows && result->validPercent) { estimatePercent = PG_GETARG_NUMERIC(argOffset + 1); int64 percent = convert_short_numeric_to_int64_byscale(estimatePercent, NUMERIC_DSCALE(estimatePercent)); if (percent < 0 || percent > 100) { pfree(result); ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("invalid value \"%ld\" for \"estimate_percent\"", percent))); } result->estimatePercent = percent; } return result; } Datum gms_analyze_schema(PG_FUNCTION_ARGS) { char* schema; char* method; AnalyzeMethodOpt analyzeMethodOpt; AnalyzeVar* var; if (PG_ARGISNULL(0) || PG_ARGISNULL(1)) { ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("Unsupported NULL input value"))); } schema = TextDatumGetCString(PG_GETARG_TEXT_P(0)); method = TextDatumGetCString(PG_GETARG_TEXT_P(1)); if (PG_ARGISNULL(4)) { analyzeMethodOpt = METHOD_OPT_TABLE; } else { analyzeMethodOpt = HandleMethodOpt(PG_GETARG_TEXT_P(4)); } if (0 == pg_strcasecmp(method, "ESTIMATE") || 0 == pg_strcasecmp(method, "COMPUTE")) { var = MakeAnalyzeVar(method, fcinfo, 2); DoAnalyzeSchemaStatistic(schema, var, analyzeMethodOpt); pfree(var); } else if (0 == pg_strcasecmp(method, "DELETE")) { if (!PG_ARGISNULL(4)) { ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("The Command did not end correctly"))); } DoDeleteSchemaStatistic(schema); } else { ereport(ERROR, (errcode(ERRCODE_UNDEFINED_KEY), errmsg("Unrecognized param value: \"%s\"", method))); } PG_RETURN_VOID(); } static void DoAnalyzeSchemaStatistic(char* schema, AnalyzeVar* var, AnalyzeMethodOpt methodOpt) { List* relnamesList; List* colnamesList; ListCell* lcRel; ListCell* lcCol; StringInfo executeSql; VacuumStmt* stmt; int saveEstimate; relnamesList = GetRelationsInSchema(schema); executeSql = makeStringInfo(); if (var->isEstimate) { saveEstimate = default_statistics_target; default_statistics_target = var->validRows ? var->estimateRows : var->validPercent ? -(var->estimatePercent) : default_statistics_target; } foreach(lcRel, relnamesList) { char* relname = (char*)lfirst(lcRel); appendStringInfo(executeSql, "ANALYZE %s.%s", quote_identifier(schema), quote_identifier(relname)); if (methodOpt == METHOD_OPT_ALL_INDEX) { colnamesList = GetAllIndexColumnNames(schema, relname); if (colnamesList != NIL && list_length(colnamesList) > 0) { appendStringInfo(executeSql, "("); int n = 0, colSize = list_length(colnamesList); foreach(lcCol, colnamesList) { char* colname = (char*)lfirst(lcCol); appendStringInfo(executeSql, "%s", quote_identifier(colname)); if (++n < colSize) { appendStringInfo(executeSql, ","); } pfree(colname); } appendStringInfo(executeSql, ")"); } list_free(colnamesList); } appendStringInfo(executeSql, ";"); List* parsetree_list = NULL; ListCell* parsetree_item = NULL; parsetree_list = raw_parser(executeSql->data, NULL); foreach (parsetree_item, parsetree_list) { Node* parsetree = (Node*)lfirst(parsetree_item); stmt = (VacuumStmt*)parsetree; } vacuum(stmt, InvalidOid, true, NULL, true); pfree_ext(relname); list_free(parsetree_list); resetStringInfo(executeSql); } default_statistics_target = saveEstimate; DestroyStringInfo(executeSql); list_free(relnamesList); } static void DoDeleteSchemaStatistic(char* schema) { List* relnamesList; ListCell* lcRel; Oid relid = InvalidOid; Oid namespaceId = InvalidOid; namespaceId = LookupExplicitNamespace(schema); relnamesList = GetRelationsInSchema(schema); foreach(lcRel, relnamesList) { char* relname = (char*)lfirst(lcRel); relid = get_relname_relid(relname, namespaceId); RemoveStatistics<'c'>(relid, 0); UpdateRelTuplesAndPages(relid); pfree_ext(relname); } list_free(relnamesList); } static bool InvalidCanonChars(char val) { for (char c : CANON_INVALID_CHARS) { if (c == val) return true; } return false; } /* * Check for keyword. return true except for unreserved ones. */ static bool DetectKeyword(char* w) { int len = strlen(w); if (len <= 0) { return true; } char* str = (char *) palloc0(len + 1); errno_t rc = memcpy_s(str, len + 1, w, len); securec_check(rc, "\0", "\0"); str = pg_strtolower(str); int kwnum = ScanKeywordLookup(str, &ScanKeywords); pfree_ext(str); return kwnum >= 0 && ScanKeywordCategories[kwnum] != UNRESERVED_KEYWORD; } static bool CheckLegalIdenty(char* w, bool checkDigital, bool checkKeyword) { if (strlen(w) >= NAMEDATALEN) { ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("identifier too long, max length is %d", NAMEDATALEN))); } /* For gms_canonicalize, legual identy must not start with digit */ if (checkDigital && isdigit(*w)) { return false; } if (checkKeyword && DetectKeyword(w)) { return false; } return true; } Datum gms_canonicalize(PG_FUNCTION_ARGS) { char* name; int32 canonLen; int len; bool dotted = false; StringInfo tmp; StringInfo result; bits8 quoteState = QUOTE_NONE; char curChar = '\0'; int traveLen = 0; text* output; if (PG_ARGISNULL(0)) { PG_RETURN_NULL(); } if (PG_ARGISNULL(1)) { ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("Input parameter \"canon_len\" is NULL"))); } name = TextDatumGetCString(PG_GETARG_TEXT_P(0)); len = VARSIZE_ANY_EXHDR(PG_GETARG_TEXT_P(0)); canonLen = PG_GETARG_INT32(1); if (canonLen < 0) { ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("Input parameter \"canon_len\" value \"%d\" is less than zero", canonLen))); } tmp = makeStringInfo(); result = makeStringInfo(); while (*name != '\0' && traveLen < len) { curChar = *name; traveLen++; name++; if (!IS_QUOTE_STARTED(quoteState) && curChar == ' ') { continue; } else if (curChar == '"') { if (BEFORE_QUOTE_STARTED(quoteState)) { if (tmp->len != 0) { ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("Invalid parameter value \"%s\" before quotation", (name - traveLen)))); } quoteState = QUOTE_STARTED; } else if (IS_QUOTE_STARTED(quoteState)) { if (tmp->len == 0) { ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("Invalid paramter value \"%s\" with zero length", (name - traveLen)))); } quoteState = QUOTE_ENDED; } else { ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("Invalid parameter value \"%s\" after quotation", (name - traveLen)))); } } else if (curChar == '.') { if (IS_QUOTE_STARTED(quoteState)) { appendStringInfoChar(tmp, curChar); } else { dotted = true; if (BEFORE_QUOTE_STARTED(quoteState) && !CheckLegalIdenty(tmp->data, true, dotted)) { ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("Invalid paramter value \"%s\" with special words", (name - traveLen)))); } if (tmp->len == 0) { ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("Invalid paramter value \"%s\" with zero length", (name - traveLen)))); } appendStringInfo(result, dotted ? "\"%s\"." : "%s.", tmp->data); resetStringInfo(tmp); quoteState = QUOTE_NONE; } } else { if (IS_QUOTE_STARTED(quoteState)) { appendStringInfoChar(tmp, curChar); } else if (IS_QUOTE_END(quoteState)) { ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("Invalid parameter value \"%s\" after quotation", (name - traveLen)))); } else { if (InvalidCanonChars(curChar)) { ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("Invalid paramter value \"%s\" with special character", (name - traveLen)))); } appendStringInfoChar(tmp, pg_toupper(curChar)); } } } if (IS_QUOTE_STARTED(quoteState)) { ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("Invalid paramter value \"%s\" with quotation not closed", (name - traveLen)))); } if (dotted && tmp->len == 0) { ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("Invalid paramter value \"%s\"", (name - traveLen)))); } if (tmp->len > 0) { if (BEFORE_QUOTE_STARTED(quoteState) && !CheckLegalIdenty(tmp->data, true, dotted)) { ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("Invalid paramter value \"%s\" with special words", (name - traveLen)))); } appendStringInfo(result, dotted ? "\"%s\"" : "%s", tmp->data); } traveLen = strlen(result->data); canonLen = canonLen < traveLen ? canonLen : traveLen; output = cstring_to_text_with_len(result->data, canonLen); DestroyStringInfo(tmp); DestroyStringInfo(result); PG_RETURN_TEXT_P(output); } static void DoCompileFuncAndProcedure(Oid namespaceId, bool compileAll) { Relation pgProcRel = NULL; ScanKeyData skey[1]; SysScanDesc sysscan; HeapTuple tuple; bool isNull = false; Oid funcOid; Oid pkgOid; char pprokind; ScanKeyInit(&skey[0], Anum_pg_proc_pronamespace, BTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(namespaceId)); pgProcRel = heap_open(ProcedureRelationId, AccessShareLock); sysscan = systable_beginscan(pgProcRel, InvalidOid, false, SnapshotNow, 1, skey); while (HeapTupleIsValid(tuple = systable_getnext(sysscan))) { funcOid = HeapTupleGetOid(tuple); if (!compileAll && GetPgObjectValid(funcOid, OBJECT_TYPE_PROC)) { continue; } pprokind = CharGetDatum(SysCacheGetAttr(PROCOID, tuple, Anum_pg_proc_prokind, &isNull)); if (!isNull && (PROC_IS_FUNC(pprokind) || PROC_IS_PRO(pprokind))) { pkgOid = SysCacheGetAttr(PROCOID, tuple, Anum_pg_proc_packageid, &isNull); /* If function or procedure is belong to a package, don't recompile here */ if (!OidIsValid(pkgOid)) { RecompileSingleFunction(funcOid, PROC_IS_PRO(pprokind)); } } } systable_endscan(sysscan); heap_close(pgProcRel, AccessShareLock); } static void DoCompilePackage(Oid namespaceId, bool compileAll) { Relation pgPkgRel = NULL; ScanKeyData skey[1]; SysScanDesc sysscan; HeapTuple tuple; bool isNull = false; Oid pkgOid; ScanKeyInit(&skey[0], Anum_gs_package_pkgnamespace, BTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(namespaceId)); pgPkgRel = heap_open(PackageRelationId, AccessShareLock); sysscan = systable_beginscan(pgPkgRel, InvalidOid, false, SnapshotNow, 1, skey); while (HeapTupleIsValid(tuple = systable_getnext(sysscan))) { SysCacheGetAttr(PACKAGEOID, tuple, Anum_gs_package_pkgbodydeclsrc, &isNull); pkgOid = HeapTupleGetOid(tuple); if (!compileAll && GetPgObjectValid(pkgOid, isNull ? OBJECT_TYPE_PKGSPEC : OBJECT_TYPE_PKGBODY)) { continue; } if (OidIsValid(pkgOid)) { RecompileSinglePackage(pkgOid, isNull); } } systable_endscan(sysscan); heap_close(pgPkgRel, AccessShareLock); } static void DoCompileView(Oid namespaceId, bool compileAll) { Relation pgClassRel = NULL; ScanKeyData skey[2]; SysScanDesc sysscan; HeapTuple tuple; List* records = NIL; Oid viewOid; ScanKeyInit(&skey[0], Anum_pg_class_relnamespace, BTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(namespaceId)); ScanKeyInit(&skey[1], Anum_pg_class_relkind, BTEqualStrategyNumber, F_CHAREQ, CharGetDatum(RELKIND_VIEW)); pgClassRel = heap_open(RelationRelationId, AccessShareLock); sysscan = systable_beginscan(pgClassRel, InvalidOid, false, SnapshotNow, 2, skey); while (HeapTupleIsValid(tuple = systable_getnext(sysscan))) { viewOid = HeapTupleGetOid(tuple); if (!ValidateDependViewDetectRecursion(viewOid, OBJECT_TYPE_VIEW, compileAll, records)) { Form_pg_class reltup = (Form_pg_class)GETSTRUCT(tuple); ereport(WARNING, (errcode(ERRCODE_UNDEFINED_OBJECT), errmsg("Compile view \"%s\" failed", reltup->relname.data))); } } list_free(records); systable_endscan(sysscan); heap_close(pgClassRel, AccessShareLock); } Datum gms_compile_schema(PG_FUNCTION_ARGS) { char* nspname; bool compileAll; Oid namespaceId; if (PG_ARGISNULL(0)) { ereport(ERROR, (errcode(ERRCODE_UNDEFINED_SCHEMA), errmsg("Input parameter \"schema\" is NULL"))); } nspname = TextDatumGetCString(PG_GETARG_TEXT_P(0)); compileAll = PG_GETARG_BOOL(1); namespaceId = get_namespace_oid(nspname, true); if (!OidIsValid(namespaceId)) { ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("schema \"%s\" does not exists", nspname))); } DoCompileFuncAndProcedure(namespaceId, compileAll); DoCompilePackage(namespaceId, compileAll); DoCompileView(namespaceId, compileAll); /* Trigger compilation is not yet supported */ PG_RETURN_VOID(); } Datum gms_expand_sql_text(PG_FUNCTION_ARGS) { text* inputSqlText; char* inputSql = NULL; text* result; List* parsetreeList = NIL; List* rewriteList = NIL; Node* parseNode; Query* query; StringInfo buf; if (PG_ARGISNULL(0)) { PG_RETURN_NULL(); } inputSqlText = PG_GETARG_TEXT_P(0); FUNC_CHECK_HUGE_POINTER(false, inputSqlText, "gms_expand_sql_text"); inputSql = TextDatumGetCString(inputSqlText); parsetreeList = pg_parse_query(inputSql); if (list_length(parsetreeList) != 1) { ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("Expand_sql_text only support one query"))); } parseNode = (Node*) linitial(parsetreeList); if (!IsA(parseNode, SelectStmt)) { ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("Unsupported query type, only support SELECT query"))); } rewriteList = pg_analyze_and_rewrite(parseNode, inputSql, NULL, 0); query = (Query *) linitial(rewriteList); buf = makeStringInfo(); #ifdef PGXC deparse_query(query, buf, NIL, false, false); #else get_query_def(query, buf, NIL, NULL, PRETTYFLAG_PAREN, WRAP_COLUMN_DEFAULT, 0); #endif result = cstring_to_text(buf->data); list_free(parsetreeList); list_free(rewriteList); DestroyStringInfo(buf); PG_RETURN_TEXT_P(result); } Datum gms_get_cpu_time(PG_FUNCTION_ARGS) { int64 result; clock_t clk = clock(); /* get_cpu_time returns the number of 100th's of a second */ result = (int64) (clk / (CLOCKS_PER_SEC / 100)); return DirectFunctionCall1(int8_numeric, Int64GetDatum(result)); } Datum gms_get_endianness(PG_FUNCTION_ARGS) { int32 result; /* 1 for big-endian or 2 for little-endian */ union { uint32_t num; uint8_t bytes[4]; } u_num; u_num.num = 0x12345678; if (u_num.bytes[0] == 0x12) { result = 1; } else if (u_num.bytes[0] == 0x78) { result = 2; } else { ereport(ERROR, (errmsg("unkonwn endianness"))); } PG_RETURN_INT32(result); } Datum gms_get_sql_hash(PG_FUNCTION_ARGS) { text* sqlText; int len; char hexsum[MD5_HASH_LEN + 1] = "\0"; StringInfo buf; int64 result; TupleDesc tupdesc; Datum values[2]; bool isnull[2]; char last4Bytes[8 + 1] = "\0"; if (PG_ARGISNULL(0)) { PG_RETURN_NULL(); } sqlText = PG_GETARG_TEXT_PP(0); FUNC_CHECK_HUGE_POINTER(false, sqlText, "gms_get_sql_hash"); len = VARSIZE_ANY_EXHDR(sqlText); buf = makeStringInfo(); tupdesc = CreateTemplateTupleDesc(2, false); TupleDescInitEntry(tupdesc, (AttrNumber)1, "hash", RAWOID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber)2, "pre10ihash", NUMERICOID, -1, 0); BlessTupleDesc(tupdesc); if (!pg_md5_hash(VARDATA_ANY(sqlText), len, hexsum)) { ereport(ERROR, (errcode(ERRCODE_OUT_OF_MEMORY), errmsg("out of memory"))); } values[0] = DirectFunctionCall1(rawin, CStringGetDatum(hexsum)); isnull[0] = false; last4Bytes[0] = hexsum[MD5_HASH_LEN - 2]; last4Bytes[1] = hexsum[MD5_HASH_LEN - 1]; last4Bytes[2] = hexsum[MD5_HASH_LEN - 4]; last4Bytes[3] = hexsum[MD5_HASH_LEN - 3]; last4Bytes[4] = hexsum[MD5_HASH_LEN - 6]; last4Bytes[5] = hexsum[MD5_HASH_LEN - 5]; last4Bytes[6] = hexsum[MD5_HASH_LEN - 8]; last4Bytes[7] = hexsum[MD5_HASH_LEN - 7]; result = strtoll(last4Bytes, NULL, 16); /* pre10ihash is not support, returns last4bytes instead */ values[1] = DirectFunctionCall1(int8_numeric, Int64GetDatum(result)); isnull[1] = false; return HeapTupleGetDatum(heap_form_tuple(tupdesc, values, isnull)); } static TokenizeVar* MakeTokenizeVar() { TokenizeVar* var = (TokenizeVar *) palloc(sizeof(TokenizeVar)); var->dblink = NULL; var->list = NIL; var->nextpos = 0; return var; } static void DestoryTokenizeVar(TokenizeVar* var) { if (var != NULL) { list_free(var->list); pfree_ext(var->dblink); pfree(var); } } static bool IsDangerousChars(char ch) { for (char c : TOKENIZE_DANGER_CHARS) { if (c == ch) return true; } return false; } static bool IsTruncateChars(char ch) { for (char c : TOKENIZE_TRUNCATE_CHARS) { if (c == ch) return true; } return false; } static TokenizeVar* NameParseInternal(char* name, int len) { List* list = NIL; int traveLen = 0; bits8 quoteState = QUOTE_NONE; char curChar = '\0'; bool startLink = false; char* token = NULL; errno_t rc; TokenizeVar* var; StringInfo tmp = makeStringInfo(); var = MakeTokenizeVar(); while (*name != '\0' && traveLen < len) { curChar = *name; traveLen++; name++; if (!IS_QUOTE_STARTED(quoteState) && curChar == ' ') { continue; } else if (curChar == '"') { if (BEFORE_QUOTE_STARTED(quoteState)) { if (tmp->len != 0) { /* If there exists valid char before double quote, end read. */ traveLen--; break; } quoteState = QUOTE_STARTED; } else if (IS_QUOTE_STARTED(quoteState)) { if (tmp->len == 0) { ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("Invalid input value \"%s\" with zero length", (name - traveLen)))); } quoteState = QUOTE_ENDED; } else { /* If there are more than one pair of double quote, end read. */ break; } } else if (curChar == '.' || curChar == '@') { if (IS_QUOTE_STARTED(quoteState) || startLink) { appendStringInfoChar(tmp, curChar); } else { if (BEFORE_QUOTE_STARTED(quoteState) && !CheckLegalIdenty(tmp->data, false, true)) { ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("Invalid input value \"%s\" with special words", (name - traveLen)))); } if (tmp->len == 0) { ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("Invalid input value \"%s\" with zero length", (name - traveLen)))); } if (list_length(list) >= 3) { ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("Invalid input value \"%s\"", (name - traveLen)))); } if (isdigit(*(tmp->data))) { /* If word start with digit, end read. */ break; } token = (char *) palloc0(tmp->len + 1); rc = strcpy_s(token, tmp->len + 1, tmp->data); securec_check(rc, "\0", "\0"); list = lappend(list, token); resetStringInfo(tmp); quoteState = QUOTE_NONE; } if (curChar == '@') { startLink = true; } } else { if (IS_QUOTE_STARTED(quoteState)) { appendStringInfoChar(tmp, curChar); } else if (IS_QUOTE_END(quoteState)) { traveLen--; break; } else { if (IsDangerousChars(curChar)) { ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("Invalid input value \"%s\" with special character", (name - traveLen)))); } if (IsTruncateChars(curChar)) { /* If meet special char, end read. */ traveLen--; break; } appendStringInfoChar(tmp, pg_toupper(curChar)); } } } if (IS_QUOTE_STARTED(quoteState)) { ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("Invalid input value \"%s\" with quotation not closed", (name - traveLen)))); } if (tmp->len == 0 && !startLink) { ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("Invalid input value \"%s\"", (name - traveLen)))); } if (BEFORE_QUOTE_STARTED(quoteState) && !CheckLegalIdenty(tmp->data, false, true)) { ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("Invalid input value \"%s\" with special words", (name - traveLen)))); } if (startLink) { if (tmp->len == 0) { ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("dblink is empty \"%s\"", (name - traveLen)))); } else if (isdigit(*(tmp->data))) { ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("Invalid dblink value \"%s\"", (name - traveLen)))); } else { var->dblink = (char *) palloc0(tmp->len + 1); errno_t rc = strcpy_s(var->dblink, tmp->len + 1, tmp->data); securec_check(rc, "\0", "\0"); } } else { if (list_length(list) >= 3) { ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("Invalid input value \"%s\"", (name - traveLen)))); } if (isdigit(*(tmp->data))) { traveLen -= (tmp->len + 2); } else { token = (char *) palloc0(tmp->len + 1); rc = strcpy_s(token, tmp->len + 1, tmp->data); securec_check(rc, "\0", "\0"); list = lappend(list, token); } } var->nextpos = traveLen; var->list = list_copy(list); DestroyStringInfo(tmp); list_free(list); return var; } Datum gms_name_tokenize(PG_FUNCTION_ARGS) { char* name = NULL; int len; ListCell* lc; TupleDesc tupdesc; Datum values[5]; bool isnull[5]; int tc = 0; TokenizeVar* var; if (PG_ARGISNULL(0)) { ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("Invalid input value"))); } name = TextDatumGetCString(PG_GETARG_TEXT_P(0)); len = VARSIZE_ANY_EXHDR(PG_GETARG_TEXT_P(0)); var = NameParseInternal(name, len); if (var->list == NIL || list_length(var->list) == 0 || list_length(var->list) > 3) { ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("Invalid Input value \"%s\"", name))); } tupdesc = CreateTemplateTupleDesc(5, false); TupleDescInitEntry(tupdesc, (AttrNumber)1, "a", TEXTOID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber)2, "b", TEXTOID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber)3, "c", TEXTOID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber)4, "dblink", TEXTOID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber)5, "nextpos", INT4OID, -1, 0); BlessTupleDesc(tupdesc); errno_t rc = memset_s(isnull, 5, 1, 5); securec_check(rc, "\0", "\0"); foreach (lc, var->list) { char* token = (char *) lfirst(lc); values[tc] = CStringGetTextDatum(token); isnull[tc++] = false; } if (var->dblink != NULL && strlen(var->dblink) > 0) { values[3] = CStringGetTextDatum(var->dblink); isnull[3] = false; } values[4] = Int32GetDatum(var->nextpos); isnull[4] = false; DestoryTokenizeVar(var); return HeapTupleGetDatum(heap_form_tuple(tupdesc, values, isnull)); } static NameResolveVar* MakeNameResolveVar(List* list, char* name) { int c = 0; ListCell* lc; errno_t rc; NameResolveVar* var = (NameResolveVar *) palloc0(sizeof(NameResolveVar)); /* identifier length can't greater than NAMEDATALEN */ var->schema = (char *) palloc0(NAMEDATALEN); var->part1 = (char *) palloc0(NAMEDATALEN); var->part2 = (char *) palloc0(NAMEDATALEN); var->part1Type = NAME_RESOLVE_TYPE_NONE; var->objectId = InvalidOid; foreach (lc, list) { char* token = (char *) lfirst(lc); token = pg_strtolower(token); if (c == 0) { rc = strcpy_s(var->schema, NAMEDATALEN, token); } else if (c == 1) { rc = strcpy_s(var->part1, NAMEDATALEN, token); } else { rc = strcpy_s(var->part2, NAMEDATALEN, token); } securec_check(rc, "\0", "\0"); c++; } var->len = list_length(list); var->synonym = false; return var; } static void DestoryNameResolveVar(NameResolveVar* var) { if (var != NULL) { pfree_ext(var->part2); pfree_ext(var->part1); pfree_ext(var->schema); pfree(var); } } static void ReportNameResolveAclErr(NameResolveVar* var) { DestoryNameResolveVar(var); ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("The Object is not exists"))); } static List* GetFillSchemaList() { List* list = NIL; Oid namespaceId = InvalidOid; char* username = GetUserNameById(GetUserId()); namespaceId = get_namespace_oid(username, true); pfree_ext(username); if (OidIsValid(namespaceId)) { list = lappend_oid(list, namespaceId); } list = lappend_oid(list, PG_PUBLIC_NAMESPACE); return list; } static Oid GetSchemaOidWithErrHandled(char* schemaName, NameResolveVar* var, HeapTuple synTuple = NULL) { Oid namespaceId = get_namespace_oid(schemaName, true); if (!OidIsValid(namespaceId)) { if (synTuple != NULL) { ReleaseSysCache(synTuple); } ReportNameResolveAclErr(var); } AclResult aclresult = pg_namespace_aclcheck(namespaceId, GetUserId(), ACL_USAGE); if (aclresult != ACLCHECK_OK) { if (synTuple != NULL) { ReleaseSysCache(synTuple); } ReportNameResolveAclErr(var); } return namespaceId; } static Oid SearchPkgOidByName(Oid namespaceId, char* pkgName, NameResolveVar* var) { Oid pkgOid = InvalidOid; AclResult aclresult; pkgOid = GetSysCacheOid2(PKGNAMENSP, CStringGetDatum(pkgName), ObjectIdGetDatum(namespaceId)); if (OidIsValid(pkgOid)) { aclresult = pg_package_aclcheck(pkgOid, GetUserId(), ACL_EXECUTE); if (aclresult != ACLCHECK_OK) { ReportNameResolveAclErr(var); } var->part1Type = NAME_RESOLVE_TYPE_PACKAGE; } return pkgOid; } static void ResolvePkgNameFillSchema(NameResolveVar* var) { List* tempActiveSearchPath = NIL; ListCell* lc; errno_t rc; char* part1 = pstrdup(var->part1); tempActiveSearchPath = GetFillSchemaList(); foreach (lc, tempActiveSearchPath) { Oid namespaceId = lfirst_oid(lc); ResolveContextName(NR_CONTEXT_PLSQL, namespaceId, var->schema, var, SearchPkgOidByName); if (OidIsValid(var->objectId)) { /* Fill schema part1 -> part2, schema -> part1, selectSchema -> schema */ rc = strcpy_s(var->part2, NAMEDATALEN, part1); securec_check(rc, "\0", "\0"); if (!var->synonym) { /* not synonym object */ rc = strcpy_s(var->part1, NAMEDATALEN, var->schema); securec_check(rc, "\0", "\0"); rc = strcpy_s(var->schema, NAMEDATALEN, get_namespace_name(namespaceId)); securec_check(rc, "\0", "\0"); } break; } } pfree_ext(part1); list_free(tempActiveSearchPath); } static Oid SearchProcOidByName(Oid namespaceId, char* procName, NameResolveVar* var) { CatCList* catlist = NULL; HeapTuple tuple; Form_pg_proc procform; Oid procOid = InvalidOid; bool isNull = true; char pprokind = '\0'; #ifndef ENABLE_MULTIPLE_NODES if (t_thrd.proc->workingVersionNum < 92470) { catlist = SearchSysCacheList1(PROCNAMEARGSNSP, CStringGetDatum(procName)); } else { catlist = SearchSysCacheList1(PROCALLARGS, CStringGetDatum(procName)); } #else catlist = SearchSysCacheList1(PROCNAMEARGSNSP, CStringGetDatum(procName)); #endif for (int i = 0; i < catlist->n_members; i++) { tuple = t_thrd.lsc_cxt.FetchTupleFromCatCList(catlist, i); procform = (Form_pg_proc)GETSTRUCT(tuple); if (procform->pronamespace != namespaceId) continue; pprokind = CharGetDatum(SysCacheGetAttr(PROCOID, tuple, Anum_pg_proc_prokind, &isNull)); if (!isNull && (PROC_IS_FUNC(pprokind) || PROC_IS_PRO(pprokind))) { procOid = HeapTupleGetOid(tuple); var->part1Type = PROC_IS_FUNC(pprokind) ? NAME_RESOLVE_TYPE_FUNCTION : PROC_IS_PRO(pprokind) ? NAME_RESOLVE_TYPE_PROCEDURE : NAME_RESOLVE_TYPE_NONE; } AclResult aclresult = pg_proc_aclcheck(procOid, GetUserId(), ACL_EXECUTE); if (aclresult != ACLCHECK_OK) { ReleaseSysCacheList(catlist); ReportNameResolveAclErr(var); } break; } ReleaseSysCacheList(catlist); return procOid; } static Oid SearchRelOidByName(Oid namespaceId, char* relName, NameResolveVar* var) { Oid relOid = InvalidOid; AclResult aclResult; relOid = get_relname_relid(relName, namespaceId); if (OidIsValid(relOid)) { aclResult = pg_class_aclcheck(relOid, GetUserId(), ACL_SELECT); if (aclResult != ACLCHECK_OK) { ReportNameResolveAclErr(var); } } return relOid; } static Oid SearchTriggerOidByName(Oid namespaceId, char* triggerName, NameResolveVar* var) { Relation tgrel; ScanKeyData keys[1]; SysScanDesc tgscan; HeapTuple tuple; bool found = false; Oid tgOid = InvalidOid; ScanKeyInit(&keys[0], Anum_pg_trigger_tgname, BTEqualStrategyNumber, F_NAMEEQ, CStringGetDatum(triggerName)); tgrel = heap_open(TriggerRelationId, AccessShareLock); tgscan = systable_beginscan(tgrel, TriggerNameIndexId, true, SnapshotNow, 1, keys); while (HeapTupleIsValid(tuple = systable_getnext(tgscan))) { if (found) { systable_endscan(tgscan); heap_close(tgrel, AccessShareLock); DestoryNameResolveVar(var); ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("duplicate trigger found"))); } tgOid = HeapTupleGetOid(tuple); found = true; } systable_endscan(tgscan); heap_close(tgrel, AccessShareLock); return tgOid; } static Oid SearchTypeOidByName(Oid namespaceId, char* typName, NameResolveVar* var) { HeapTuple tuple; Oid typId = InvalidOid; tuple = SearchSysCache2(TYPENAMENSP, CStringGetDatum(typName), ObjectIdGetDatum(namespaceId)); if (!HeapTupleIsValid(tuple)) { return InvalidOid; } typId = HeapTupleGetOid(tuple); if (OidIsValid(typId)) { AclResult aclResult = pg_type_aclcheck(typId, GetUserId(), ACL_USAGE); if (aclResult != ACLCHECK_OK) { ReleaseSysCache(tuple); ReportNameResolveAclErr(var); } } ReleaseSysCache(tuple); return typId; } static void ResolveContextName(NameResolveContext context, Oid namespaceId, char* resolveName, NameResolveVar* var, SearchOidByName searchmtd) { Oid id = InvalidOid; HeapTuple synTuple; Oid newNamespaceId = InvalidOid; id = searchmtd(namespaceId, resolveName, var); if (OidIsValid(id)) { var->objectId = id; return; } if (!(context == NR_CONTEXT_PLSQL || context == NR_CONTEXT_TABLE || context == NR_CONTEXT_SEQUENCES || context == NR_CONTEXT_TYPE)) { return; } /* Search synonym */ synTuple = SearchSysCache2(SYNONYMNAMENSP, PointerGetDatum(resolveName), ObjectIdGetDatum(namespaceId)); if (!HeapTupleIsValid(synTuple)) { return; } Form_pg_synonym synForm = (Form_pg_synonym)GETSTRUCT(synTuple); newNamespaceId = GetSchemaOidWithErrHandled(NameStr(synForm->synobjschema), var, synTuple); id = searchmtd(newNamespaceId, NameStr(synForm->synobjname), var); if (OidIsValid(id)) { /* Replace synonym to real name, and do it for schema too. */ errno_t rc = strcpy_s(var->schema, NAMEDATALEN, NameStr(synForm->synobjschema)); securec_check(rc, "\0", "\0"); rc = strcpy_s(var->part1, NAMEDATALEN, NameStr(synForm->synobjname)); securec_check(rc, "\0", "\0"); var->objectId = id; var->synonym = true; } ReleaseSysCache(synTuple); } static void ResolveObjectNameByContext(NameResolveContext context, Oid namespaceId, char* resolveName, NameResolveVar* var) { errno_t rc; switch (context) { case NR_CONTEXT_PLSQL: /* Search func、proc、package */ ResolveContextName(context, namespaceId, resolveName, var, SearchProcOidByName); break; case NR_CONTEXT_TABLE: case NR_CONTEXT_SEQUENCES: case NR_CONTEXT_INDEX: ResolveContextName(context, namespaceId, resolveName, var, SearchRelOidByName); var->part1Type = context == NR_CONTEXT_TABLE ? NAME_RESOLVE_TYPE_TABLE : context == NR_CONTEXT_INDEX ? NAME_RESOLVE_TYPE_INDEX : NAME_RESOLVE_TYPE_SEQUENCE; break; case NR_CONTEXT_TRIGGER: ResolveContextName(context, namespaceId, resolveName, var, SearchTriggerOidByName); var->part1Type = NAME_RESOLVE_TYPE_TRIGGER; break; case NR_CONTEXT_TYPE: ResolveContextName(context, namespaceId, resolveName, var, SearchTypeOidByName); var->part1Type = NAME_RESOLVE_TYPE_TYPE; break; case NR_CONTEXT_UNKNOWN: /* Only support for context value 10 */ ReportNameResolveAclErr(var); break; default: var->part1Type = NAME_RESOLVE_TYPE_NONE; break; } if (var->len == 1 && !var->synonym) { rc = strcpy_s(var->part1, NAMEDATALEN, var->schema); securec_check(rc, "\0", "\0"); rc = strcpy_s(var->schema, NAMEDATALEN, get_namespace_name(namespaceId)); securec_check(rc, "\0", "\0"); } /* top-level function | procedure, part1 is empty, part2 is real function | procedure name */ if ((var->part1Type == NAME_RESOLVE_TYPE_FUNCTION || var->part1Type == NAME_RESOLVE_TYPE_PROCEDURE) && (var->part2 == NULL || strlen(var->part2) == 0)) { rc = strcpy_s(var->part2, NAMEDATALEN, var->part1); securec_check(rc, "\0", "\0"); rc = memset_s(var->part1, NAMEDATALEN, 0, NAMEDATALEN); securec_check(rc, "\0", "\0"); } } static NameResolveContext GetNameResolveContext(Numeric num) { int32 contextNumber = DatumGetInt32(DirectFunctionCall1(numeric_int4, NumericGetDatum(num))); if (contextNumber < 0 || contextNumber > 10) { ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("context argument must be number 0 to 10"))); } NameResolveContext context = (NameResolveContext) contextNumber; if (context == NR_CONTEXT_JAVA_SOURCE || context == NR_CONTEXT_JAVA_RESOURCE || context ==NR_CONTEXT_JAVA_CLASS || context == NR_CONTEXT_JAVA_SHARED_DATA) { ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("Unsupported context type: %d", (int)context))); } return context; } static void SetResultValues(Datum* values, bool* isnull, NameResolveVar* var) { if (var->schema != NULL && strlen(var->schema) > 0) { values[0] = CStringGetTextDatum(pg_strtoupper(var->schema)); isnull[0] = false; } if (var->part1 != NULL && strlen(var->part1) > 0) { values[1] = CStringGetTextDatum(pg_strtoupper(var->part1)); isnull[1] = false; } if (var->part2 != NULL && strlen(var->part2) > 0) { values[2] = CStringGetTextDatum(pg_strtoupper(var->part2)); isnull[2] = false; } } Datum gms_name_resolve(PG_FUNCTION_ARGS) { char* name = NULL; NameResolveContext context; int len; TupleDesc tupdesc; const int outputParamLen = 6; Datum values[outputParamLen]; bool isnull[outputParamLen]; TokenizeVar* tokenizeVar; NameResolveVar* var; int listLen; Oid namespaceId = InvalidOid; if (PG_ARGISNULL(0) || PG_ARGISNULL(1)) { ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("Invalid input value"))); } name = TextDatumGetCString(PG_GETARG_TEXT_P(0)); len = VARSIZE_ANY_EXHDR(PG_GETARG_TEXT_P(0)); context = GetNameResolveContext(PG_GETARG_NUMERIC(1)); tokenizeVar = NameParseInternal(name, len); listLen = list_length(tokenizeVar->list); if (tokenizeVar->list == NIL || listLen == 0 || listLen > 3) { ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("Invalid Input value \"%s\"", name))); } tupdesc = CreateTemplateTupleDesc(outputParamLen, false); TupleDescInitEntry(tupdesc, (AttrNumber)1, "schema", TEXTOID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber)2, "part1", TEXTOID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber)3, "part2", TEXTOID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber)4, "dblink", TEXTOID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber)5, "part1_type", NUMERICOID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber)6, "object_number", NUMERICOID, -1, 0); BlessTupleDesc(tupdesc); errno_t rc = memset_s(isnull, outputParamLen, 1, outputParamLen); securec_check(rc, "\0", "\0"); var = MakeNameResolveVar(tokenizeVar->list, name); pfree_ext(name); /* Set part1_type and object_number default value */ values[4] = DirectFunctionCall1(int4_numeric, Int32GetDatum(var->part1Type)); isnull[4] = false; values[5] = DirectFunctionCall1(int4_numeric, Int32GetDatum(0)); isnull[5] = false; /* If dblink is not null, don't resolve */ if (tokenizeVar->dblink != NULL && strlen(tokenizeVar->dblink) > 0) { SetResultValues(values, isnull, var); values[3] = CStringGetTextDatum(tokenizeVar->dblink); isnull[3] = false; DestoryTokenizeVar(tokenizeVar); DestoryNameResolveVar(var); return HeapTupleGetDatum(heap_form_tuple(tupdesc, values, isnull)); } DestoryTokenizeVar(tokenizeVar); if (listLen == 3) { namespaceId = GetSchemaOidWithErrHandled(var->schema, var); ResolveContextName(context, namespaceId, var->part1, var, SearchPkgOidByName); } else if (listLen == 2) { /* If is pkg.c, fill schema and search */ if (context == NR_CONTEXT_PLSQL) { ResolvePkgNameFillSchema(var); } if (!OidIsValid(var->objectId)) { namespaceId = GetSchemaOidWithErrHandled(var->schema, var); ResolveObjectNameByContext(context, namespaceId, var->part1, var); } } else { ListCell* lc; List* tempActiveSearchPath = GetFillSchemaList(); foreach (lc, tempActiveSearchPath) { namespaceId = lfirst_oid(lc); ResolveObjectNameByContext(context, namespaceId, var->schema, var); if (OidIsValid(var->objectId)) { break; } } list_free(tempActiveSearchPath); } if (!OidIsValid(var->objectId)) { ReportNameResolveAclErr(var); } SetResultValues(values, isnull, var); values[4] = DirectFunctionCall1(int4_numeric, Int32GetDatum(var->part1Type)); values[5] = DirectFunctionCall1(int4_numeric, Int32GetDatum(var->objectId)); return HeapTupleGetDatum(heap_form_tuple(tupdesc, values, isnull)); } static int HexCharToDec(char hexChar) { char lowerHexChar = pg_tolower(hexChar); if (lowerHexChar >= '0' && lowerHexChar <= '9') { return lowerHexChar - '0'; } else if (lowerHexChar >= 'a' && lowerHexChar <= 'f') { return lowerHexChar - 'a' + 10; } else { ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("invalid hexadecimal digit: \"%d\"", hexChar))); } return -1; /* make compiler quiet */ } /* * Checks the bit setting for the given bit in the given RAW value. * - raw: input raw value; * - pos: bit in raw to check, start from 1. */ Datum gms_is_bit_set(PG_FUNCTION_ARGS) { bytea* data; char* hexStr = NULL; int len; int pos; int chVal; uint32 result; if (PG_ARGISNULL(0) || PG_ARGISNULL(1)) { ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("invalid input value"))); } data = PG_GETARG_BYTEA_P(0); len = VARSIZE_ANY_EXHDR(data); hexStr = (char *) palloc0(len * 2 + 1); hex_encode(VARDATA_ANY(data), len, hexStr); len = strlen(hexStr); pos = DirectFunctionCall1(numeric_int4, PG_GETARG_DATUM(1)); if (pos <= 0 || pos > INT32_MAX) { ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("invalid second param value range"))); } /* convert pos to cycle 0~(len * 4 - 1) */ pos = (pos - 1) % (len * 4); /* * Find binary position in chars with 'pos', * from high to low with the lowest bit being * bit number 1. * for example: hex input: 124578AF, pos: 9 * 0001 0010 0100 0101 0111 1000 1010 1111 * ^ * 9 8765 4321 * and each hex char indicate 4 bits, find offset * bit pos in single char from right, use pos % 4 * get right offset. */ chVal = HexCharToDec(hexStr[len - 1 - pos / 4]); result = (chVal >> (pos % 4)) & 0x01; pfree_ext(hexStr); return DirectFunctionCall1(int4_numeric, Int32GetDatum(result)); } Datum gms_old_current_schema(PG_FUNCTION_ARGS) { Name schemaName = DatumGetName(OidFunctionCall0(CURRENTSCHEMAFUNCOID)); return CStringGetTextDatum(schemaName->data); }