/* * int2in - converts "num" to short */ Datum int2in(PG_FUNCTION_ARGS) { char *num = PG_GETARG_CSTRING(0); PG_RETURN_INT16(pg_atoi(num, sizeof(int16), '\0')); }
/* * int2vectorin - converts "num num ..." to internal form */ Datum int2vectorin(PG_FUNCTION_ARGS) { char *intString = PG_GETARG_CSTRING(0); int2vector *result; int n; result = (int2vector *) palloc0(Int2VectorSize(FUNC_MAX_ARGS)); for (n = 0; *intString && n < FUNC_MAX_ARGS; n++) { while (*intString && isspace((unsigned char) *intString)) intString++; if (*intString == '\0') break; result->values[n] = pg_atoi(intString, sizeof(int16), ' '); while (*intString && !isspace((unsigned char) *intString)) intString++; } while (*intString && isspace((unsigned char) *intString)) intString++; if (*intString) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("int2vector has too many elements"))); SET_VARSIZE(result, Int2VectorSize(n)); result->ndim = 1; result->dataoffset = 0; /* never any nulls */ result->elemtype = INT2OID; result->dim1 = n; result->lbound1 = 0; PG_RETURN_POINTER(result); }
/* * strToRelfilenode() - convert a string into a relfilenode and segment number. * * Returns false if the string doesn't match "^\d+(\.\d+)?$" */ static bool strToRelfilenode(char *str, Oid *relfilenode, int32 *segmentnum) { char *s; /* String must contain characters */ if (strlen(str) == 0) return false; /* If it isn't numbers and dots then its not a relfilenode. */ if (strlen(str) != strspn(str, "0123456789.")) return false; /* first character can't be a dot */ if (str[0] == '.') return false; /* Convert the string to a number for the relfilenode */ *relfilenode = pg_atoi(str, 4, '.'); /* Find the dot, if any, and repeat for the segmentnum */ s = strchr(str, '.'); if (s == NULL) { *segmentnum = 0; return true; } /* Dot exists, next should be another number */ s++; if (s[0] == '\0' || s[0] == '.') return false; *segmentnum = pg_atoi(s, 4, '.'); /* We should be at the end of the string, no more dots */ s = strchr(s, '.'); if (s != NULL) return false; /* All done, return success */ return true; }
/* * PurgeConnection removes the given connection from the connection hash and * closes it using PQfinish. If our hash does not contain the given connection, * this method simply prints a warning and exits. */ void PurgeConnection(PGconn *connection) { NodeConnectionKey nodeConnectionKey; NodeConnectionEntry *nodeConnectionEntry = NULL; bool entryFound = false; char *nodeNameString = ConnectionGetOptionValue(connection, "host"); char *nodePortString = ConnectionGetOptionValue(connection, "port"); if (nodeNameString != NULL && nodePortString != NULL) { int32 nodePort = pg_atoi(nodePortString, sizeof(int32), 0); memset(&nodeConnectionKey, 0, sizeof(nodeConnectionKey)); strncpy(nodeConnectionKey.nodeName, nodeNameString, MAX_NODE_LENGTH); nodeConnectionKey.nodePort = nodePort; pfree(nodeNameString); pfree(nodePortString); } else { ereport(ERROR, (errmsg("connections must have host and port options set"))); } nodeConnectionEntry = hash_search(NodeConnectionHash, &nodeConnectionKey, HASH_REMOVE, &entryFound); if (entryFound) { /* * It's possible the provided connection matches the host and port for * an entry in the hash without being precisely the same connection. In * that case, we will want to close the hash's connection (because the * entry has already been removed) in addition to the provided one. */ if (nodeConnectionEntry->connection != connection) { ereport(WARNING, (errmsg("hash entry for %s:%d contained different " "connection than that provided by caller", nodeConnectionKey.nodeName, nodeConnectionKey.nodePort))); PQfinish(nodeConnectionEntry->connection); } } else { ereport(WARNING, (errmsg("could not find hash entry for connection to %s:%d", nodeConnectionKey.nodeName, nodeConnectionKey.nodePort))); } PQfinish(connection); }
/** * @brief Parse int32 expression */ int ParseInt32(char *value, int minValue) { int32 i; i = pg_atoi(value, sizeof(int32), 0); if (i < minValue) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("value \"%s\" is out of range", value))); return i; }
/* * mongo_fdw_validator validates options given to one of the following commands: * foreign data wrapper, server, user mapping, or foreign table. This function * errors out if the given option name or its value is considered invalid. */ Datum mongo_fdw_validator(PG_FUNCTION_ARGS) { Datum optionArray = PG_GETARG_DATUM(0); Oid optionContextId = PG_GETARG_OID(1); List *optionList = untransformRelOptions(optionArray); ListCell *optionCell = NULL; foreach(optionCell, optionList) { DefElem *optionDef = (DefElem *) lfirst(optionCell); char *optionName = optionDef->defname; bool optionValid = false; int32 optionIndex = 0; for (optionIndex = 0; optionIndex < ValidOptionCount; optionIndex++) { const MongoValidOption *validOption = &(ValidOptionArray[optionIndex]); if ((optionContextId == validOption->optionContextId) && (strncmp(optionName, validOption->optionName, NAMEDATALEN) == 0)) { optionValid = true; break; } } /* if invalid option, display an informative error message */ if (!optionValid) { StringInfo optionNamesString = OptionNamesString(optionContextId); ereport(ERROR, (errcode(ERRCODE_FDW_INVALID_OPTION_NAME), errmsg("invalid option \"%s\"", optionName), errhint("Valid options in this context are: %s", optionNamesString->data))); } /* if port option is given, error out if its value isn't an integer */ if (strncmp(optionName, OPTION_NAME_PORT, NAMEDATALEN) == 0) { char *optionValue = defGetString(optionDef); int32 portNumber = pg_atoi(optionValue, sizeof(int32), 0); (void) portNumber; } }
Datum pc_typmod_in(PG_FUNCTION_ARGS) { uint32 typmod = 0; Datum *elem_values; int n = 0; int i = 0; ArrayType *arr = (ArrayType *) DatumGetPointer(PG_GETARG_DATUM(0)); if (ARR_ELEMTYPE(arr) != CSTRINGOID) ereport(ERROR, (errcode(ERRCODE_ARRAY_ELEMENT_ERROR), errmsg("typmod array must be type cstring[]"))); if (ARR_NDIM(arr) != 1) ereport(ERROR, (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR), errmsg("typmod array must be one-dimensional"))); if (ARR_HASNULL(arr)) ereport(ERROR, (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED), errmsg("typmod array must not contain nulls"))); if (ArrayGetNItems(ARR_NDIM(arr), ARR_DIMS(arr)) > 1) ereport(ERROR, (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR), errmsg("typmod array must have one element"))); deconstruct_array(arr, CSTRINGOID, -2, false, 'c', /* hardwire cstring representation details */ &elem_values, NULL, &n); for (i = 0; i < n; i++) { if ( i == 0 ) /* PCID */ { char *s = DatumGetCString(elem_values[i]); typmod = pg_atoi(s, sizeof(int32), '\0'); } } PG_RETURN_INT32(typmod); }
/* * ArrayGetIntegerTypmods: verify that argument is a 1-D cstring array, * and get the contents converted to integers. Returns a palloc'd array * and places the length at *n. */ int32 * ArrayGetIntegerTypmods(ArrayType *arr, int *n) { int32 *result; Datum *elem_values; int i; if (ARR_ELEMTYPE(arr) != CSTRINGOID) ereport(ERROR, (errcode(ERRCODE_ARRAY_ELEMENT_ERROR), errmsg("typmod array must be type cstring[]"))); if (ARR_NDIM(arr) != 1) ereport(ERROR, (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR), errmsg("typmod array must be one-dimensional"))); if (ARR_HASNULL(arr)) ereport(ERROR, (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED), errmsg("typmod array must not contain nulls"))); /* hardwired knowledge about cstring's representation details here */ deconstruct_array(arr, CSTRINGOID, -2, false, 'c', &elem_values, NULL, n); result = (int32 *) palloc(*n * sizeof(int32)); for (i = 0; i < *n; i++) result[i] = pg_atoi(DatumGetCString(elem_values[i]), sizeof(int32), '\0'); pfree(elem_values); return result; }
/* * Establish the connection to the primary server for XLOG streaming */ static bool libpqrcv_connect(char *conninfo, XLogRecPtr startpoint) { char conninfo_repl[MAXCONNINFO + 37]; char *primary_sysid; char standby_sysid[32]; TimeLineID primary_tli; TimeLineID standby_tli; PGresult *res; char cmd[64]; /* * Connect using deliberately undocumented parameter: replication. The * database name is ignored by the server in replication mode, but specify * "replication" for .pgpass lookup. */ snprintf(conninfo_repl, sizeof(conninfo_repl), "%s dbname=replication replication=true", conninfo); streamConn = PQconnectdb(conninfo_repl); if (PQstatus(streamConn) != CONNECTION_OK) ereport(ERROR, (errmsg("could not connect to the primary server: %s", PQerrorMessage(streamConn)))); /* * Get the system identifier and timeline ID as a DataRow message from the * primary server. */ res = libpqrcv_PQexec("IDENTIFY_SYSTEM"); if (PQresultStatus(res) != PGRES_TUPLES_OK) { PQclear(res); ereport(ERROR, (errmsg("could not receive database system identifier and timeline ID from " "the primary server: %s", PQerrorMessage(streamConn)))); } if (PQnfields(res) != 2 || PQntuples(res) != 1) { int ntuples = PQntuples(res); int nfields = PQnfields(res); PQclear(res); ereport(ERROR, (errmsg("invalid response from primary server"), errdetail("Expected 1 tuple with 2 fields, got %d tuples with %d fields.", ntuples, nfields))); } primary_sysid = PQgetvalue(res, 0, 0); primary_tli = pg_atoi(PQgetvalue(res, 0, 1), 4, 0); /* * Confirm that the system identifier of the primary is the same as ours. */ snprintf(standby_sysid, sizeof(standby_sysid), UINT64_FORMAT, GetSystemIdentifier()); if (strcmp(primary_sysid, standby_sysid) != 0) { PQclear(res); ereport(ERROR, (errmsg("database system identifier differs between the primary and standby"), errdetail("The primary's identifier is %s, the standby's identifier is %s.", primary_sysid, standby_sysid))); } /* * Confirm that the current timeline of the primary is the same as the * recovery target timeline. */ standby_tli = GetRecoveryTargetTLI(); PQclear(res); if (primary_tli != standby_tli) ereport(ERROR, (errmsg("timeline %u of the primary does not match recovery target timeline %u", primary_tli, standby_tli))); ThisTimeLineID = primary_tli; /* Start streaming from the point requested by startup process */ snprintf(cmd, sizeof(cmd), "START_REPLICATION %X/%X", startpoint.xlogid, startpoint.xrecoff); res = libpqrcv_PQexec(cmd); if (PQresultStatus(res) != PGRES_COPY_BOTH) { PQclear(res); ereport(ERROR, (errmsg("could not start WAL streaming: %s", PQerrorMessage(streamConn)))); } PQclear(res); justconnected = true; ereport(LOG, (errmsg("streaming replication successfully connected to primary"))); return true; }
/* * Parse an ErrorResponse or NoticeResponse payload and populate an ErrorData * structure with the results. */ void pq_parse_errornotice(StringInfo msg, ErrorData *edata) { /* Initialize edata with reasonable defaults. */ MemSet(edata, 0, sizeof(ErrorData)); edata->elevel = ERROR; edata->assoc_context = CurrentMemoryContext; /* Loop over fields and extract each one. */ for (;;) { char code = pq_getmsgbyte(msg); const char *value; if (code == '\0') { pq_getmsgend(msg); break; } value = pq_getmsgstring(msg); switch (code) { case PG_DIAG_SEVERITY: if (strcmp(value, "DEBUG") == 0) edata->elevel = DEBUG1; /* or some other DEBUG level */ else if (strcmp(value, "LOG") == 0) edata->elevel = LOG; /* can't be COMMERROR */ else if (strcmp(value, "INFO") == 0) edata->elevel = INFO; else if (strcmp(value, "NOTICE") == 0) edata->elevel = NOTICE; else if (strcmp(value, "WARNING") == 0) edata->elevel = WARNING; else if (strcmp(value, "ERROR") == 0) edata->elevel = ERROR; else if (strcmp(value, "FATAL") == 0) edata->elevel = FATAL; else if (strcmp(value, "PANIC") == 0) edata->elevel = PANIC; else elog(ERROR, "unknown error severity"); break; case PG_DIAG_SQLSTATE: if (strlen(value) != 5) elog(ERROR, "malformed sql state"); edata->sqlerrcode = MAKE_SQLSTATE(value[0], value[1], value[2], value[3], value[4]); break; case PG_DIAG_MESSAGE_PRIMARY: edata->message = pstrdup(value); break; case PG_DIAG_MESSAGE_DETAIL: edata->detail = pstrdup(value); break; case PG_DIAG_MESSAGE_HINT: edata->hint = pstrdup(value); break; case PG_DIAG_STATEMENT_POSITION: edata->cursorpos = pg_atoi(value, sizeof(int), '\0'); break; case PG_DIAG_INTERNAL_POSITION: edata->internalpos = pg_atoi(value, sizeof(int), '\0'); break; case PG_DIAG_INTERNAL_QUERY: edata->internalquery = pstrdup(value); break; case PG_DIAG_CONTEXT: edata->context = pstrdup(value); break; case PG_DIAG_SCHEMA_NAME: edata->schema_name = pstrdup(value); break; case PG_DIAG_TABLE_NAME: edata->table_name = pstrdup(value); break; case PG_DIAG_COLUMN_NAME: edata->column_name = pstrdup(value); break; case PG_DIAG_DATATYPE_NAME: edata->datatype_name = pstrdup(value); break; case PG_DIAG_CONSTRAINT_NAME: edata->constraint_name = pstrdup(value); break; case PG_DIAG_SOURCE_FILE: edata->filename = pstrdup(value); break; case PG_DIAG_SOURCE_LINE: edata->lineno = pg_atoi(value, sizeof(int), '\0'); break; case PG_DIAG_SOURCE_FUNCTION: edata->funcname = pstrdup(value); break; default: elog(ERROR, "unknown error field: %d", (int) code); break; } } }
Datum check_foreign_key(PG_FUNCTION_ARGS) { TriggerData *trigdata = (TriggerData *) fcinfo->context; Trigger *trigger; /* to get trigger name */ int nargs; /* # of args specified in CREATE TRIGGER */ char **args; /* arguments: as described above */ char **args_temp; int nrefs; /* number of references (== # of plans) */ char action; /* 'R'estrict | 'S'etnull | 'C'ascade */ int nkeys; /* # of key columns */ Datum *kvals; /* key values */ char *relname; /* referencing relation name */ Relation rel; /* triggered relation */ HeapTuple trigtuple = NULL; /* tuple to being changed */ HeapTuple newtuple = NULL; /* tuple to return */ TupleDesc tupdesc; /* tuple description */ EPlan *plan; /* prepared plan(s) */ Oid *argtypes = NULL; /* key types to prepare execution plan */ bool isnull; /* to know is some column NULL or not */ bool isequal = true; /* are keys in both tuples equal (in UPDATE) */ char ident[2 * NAMEDATALEN]; /* to identify myself */ int is_update = 0; int ret; int i, r; #ifdef DEBUG_QUERY elog(DEBUG4, "check_foreign_key: Enter Function"); #endif /* * Some checks first... */ /* Called by trigger manager ? */ if (!CALLED_AS_TRIGGER(fcinfo)) /* internal error */ elog(ERROR, "check_foreign_key: not fired by trigger manager"); /* Should be called for ROW trigger */ if (!TRIGGER_FIRED_FOR_ROW(trigdata->tg_event)) /* internal error */ elog(ERROR, "check_foreign_key: must be fired for row"); /* Not should be called for INSERT */ if (TRIGGER_FIRED_BY_INSERT(trigdata->tg_event)) /* internal error */ elog(ERROR, "check_foreign_key: cannot process INSERT events"); /* Have to check tg_trigtuple - tuple being deleted */ trigtuple = trigdata->tg_trigtuple; /* * But if this is UPDATE then we have to return tg_newtuple. Also, if key * in tg_newtuple is the same as in tg_trigtuple then nothing to do. */ is_update = 0; if (TRIGGER_FIRED_BY_UPDATE(trigdata->tg_event)) { newtuple = trigdata->tg_newtuple; is_update = 1; } trigger = trigdata->tg_trigger; nargs = trigger->tgnargs; args = trigger->tgargs; if (nargs < 5) /* nrefs, action, key, Relation, key - at * least */ /* internal error */ elog(ERROR, "check_foreign_key: too short %d (< 5) list of arguments", nargs); nrefs = pg_atoi(args[0], sizeof(int), 0); if (nrefs < 1) /* internal error */ elog(ERROR, "check_foreign_key: %d (< 1) number of references specified", nrefs); action = tolower((unsigned char) *(args[1])); if (action != 'r' && action != 'c' && action != 's') /* internal error */ elog(ERROR, "check_foreign_key: invalid action %s", args[1]); nargs -= 2; args += 2; nkeys = (nargs - nrefs) / (nrefs + 1); if (nkeys <= 0 || nargs != (nrefs + nkeys * (nrefs + 1))) /* internal error */ elog(ERROR, "check_foreign_key: invalid number of arguments %d for %d references", nargs + 2, nrefs); rel = trigdata->tg_relation; tupdesc = rel->rd_att; /* Connect to SPI manager */ if ((ret = SPI_connect()) < 0) /* internal error */ elog(ERROR, "check_foreign_key: SPI_connect returned %d", ret); /* * We use SPI plan preparation feature, so allocate space to place key * values. */ kvals = (Datum *) palloc(nkeys * sizeof(Datum)); /* * Construct ident string as TriggerName $ TriggeredRelationId and try to * find prepared execution plan(s). */ snprintf(ident, sizeof(ident), "%s$%u", trigger->tgname, rel->rd_id); plan = find_plan(ident, &FPlans, &nFPlans); /* if there is no plan(s) then allocate argtypes for preparation */ if (plan->nplans <= 0) argtypes = (Oid *) palloc(nkeys * sizeof(Oid)); /* * else - check that we have exactly nrefs plan(s) ready */ else if (plan->nplans != nrefs) /* internal error */ elog(ERROR, "%s: check_foreign_key: # of plans changed in meantime", trigger->tgname); /* For each column in key ... */ for (i = 0; i < nkeys; i++) { /* get index of column in tuple */ int fnumber = SPI_fnumber(tupdesc, args[i]); /* Bad guys may give us un-existing column in CREATE TRIGGER */ if (fnumber < 0) ereport(ERROR, (errcode(ERRCODE_UNDEFINED_COLUMN), errmsg("there is no attribute \"%s\" in relation \"%s\"", args[i], SPI_getrelname(rel)))); /* Well, get binary (in internal format) value of column */ kvals[i] = SPI_getbinval(trigtuple, tupdesc, fnumber, &isnull); /* * If it's NULL then nothing to do! DON'T FORGET call SPI_finish ()! * DON'T FORGET return tuple! Executor inserts tuple you're returning! * If you return NULL then nothing will be inserted! */ if (isnull) { SPI_finish(); return PointerGetDatum((newtuple == NULL) ? trigtuple : newtuple); } /* * If UPDATE then get column value from new tuple being inserted and * compare is this the same as old one. For the moment we use string * presentation of values... */ if (newtuple != NULL) { char *oldval = SPI_getvalue(trigtuple, tupdesc, fnumber); char *newval; /* this shouldn't happen! SPI_ERROR_NOOUTFUNC ? */ if (oldval == NULL) /* internal error */ elog(ERROR, "check_foreign_key: SPI_getvalue returned %d", SPI_result); newval = SPI_getvalue(newtuple, tupdesc, fnumber); if (newval == NULL || strcmp(oldval, newval) != 0) isequal = false; } if (plan->nplans <= 0) /* Get typeId of column */ argtypes[i] = SPI_gettypeid(tupdesc, fnumber); } args_temp = args; nargs -= nkeys; args += nkeys; /* * If we have to prepare plans ... */ if (plan->nplans <= 0) { SPIPlanPtr pplan; char sql[8192]; char **args2 = args; plan->splan = (SPIPlanPtr *) malloc(nrefs * sizeof(SPIPlanPtr)); for (r = 0; r < nrefs; r++) { relname = args2[0]; /*--------- * For 'R'estrict action we construct SELECT query: * * SELECT 1 * FROM _referencing_relation_ * WHERE Fkey1 = $1 [AND Fkey2 = $2 [...]] * * to check is tuple referenced or not. *--------- */ if (action == 'r') snprintf(sql, sizeof(sql), "select 1 from %s where ", relname); /*--------- * For 'C'ascade action we construct DELETE query * * DELETE * FROM _referencing_relation_ * WHERE Fkey1 = $1 [AND Fkey2 = $2 [...]] * * to delete all referencing tuples. *--------- */ /* * Max : Cascade with UPDATE query i create update query that * updates new key values in referenced tables */ else if (action == 'c') { if (is_update == 1) { int fn; char *nv; int k; snprintf(sql, sizeof(sql), "update %s set ", relname); for (k = 1; k <= nkeys; k++) { int is_char_type = 0; char *type; fn = SPI_fnumber(tupdesc, args_temp[k - 1]); nv = SPI_getvalue(newtuple, tupdesc, fn); type = SPI_gettype(tupdesc, fn); if ((strcmp(type, "text") && strcmp(type, "varchar") && strcmp(type, "char") && strcmp(type, "bpchar") && strcmp(type, "date") && strcmp(type, "timestamp")) == 0) is_char_type = 1; #ifdef DEBUG_QUERY elog(DEBUG4, "check_foreign_key Debug value %s type %s %d", nv, type, is_char_type); #endif /* * is_char_type =1 i set ' ' for define a new value */ snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql), " %s = %s%s%s %s ", args2[k], (is_char_type > 0) ? "'" : "", nv, (is_char_type > 0) ? "'" : "", (k < nkeys) ? ", " : ""); is_char_type = 0; } strcat(sql, " where "); } else /* DELETE */ snprintf(sql, sizeof(sql), "delete from %s where ", relname); } /* * For 'S'etnull action we construct UPDATE query - UPDATE * _referencing_relation_ SET Fkey1 null [, Fkey2 null [...]] * WHERE Fkey1 = $1 [AND Fkey2 = $2 [...]] - to set key columns in * all referencing tuples to NULL. */ else if (action == 's') { snprintf(sql, sizeof(sql), "update %s set ", relname); for (i = 1; i <= nkeys; i++) { snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql), "%s = null%s", args2[i], (i < nkeys) ? ", " : ""); } strcat(sql, " where "); } /* Construct WHERE qual */ for (i = 1; i <= nkeys; i++) { snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql), "%s = $%d %s", args2[i], i, (i < nkeys) ? "and " : ""); } /* Prepare plan for query */ pplan = SPI_prepare(sql, nkeys, argtypes); if (pplan == NULL) /* internal error */ elog(ERROR, "check_foreign_key: SPI_prepare returned %d", SPI_result); /* * Remember that SPI_prepare places plan in current memory context * - so, we have to save plan in Top memory context for later use. */ if (SPI_keepplan(pplan)) /* internal error */ elog(ERROR, "check_foreign_key: SPI_keepplan failed"); plan->splan[r] = pplan; args2 += nkeys + 1; /* to the next relation */ } plan->nplans = nrefs; #ifdef DEBUG_QUERY elog(DEBUG4, "check_foreign_key Debug Query is : %s ", sql); #endif } /* * If UPDATE and key is not changed ... */ if (newtuple != NULL && isequal) { SPI_finish(); return PointerGetDatum(newtuple); } /* * Ok, execute prepared plan(s). */ for (r = 0; r < nrefs; r++) { /* * For 'R'estrict we may to execute plan for one tuple only, for other * actions - for all tuples. */ int tcount = (action == 'r') ? 1 : 0; relname = args[0]; snprintf(ident, sizeof(ident), "%s$%u", trigger->tgname, rel->rd_id); plan = find_plan(ident, &FPlans, &nFPlans); ret = SPI_execp(plan->splan[r], kvals, NULL, tcount); /* we have no NULLs - so we pass ^^^^ here */ if (ret < 0) ereport(ERROR, (errcode(ERRCODE_TRIGGERED_ACTION_EXCEPTION), errmsg("SPI_execp returned %d", ret))); /* If action is 'R'estrict ... */ if (action == 'r') { /* If there is tuple returned by SELECT then ... */ if (SPI_processed > 0) ereport(ERROR, (errcode(ERRCODE_TRIGGERED_ACTION_EXCEPTION), errmsg("\"%s\": tuple is referenced in \"%s\"", trigger->tgname, relname))); } else { #ifdef REFINT_VERBOSE elog(NOTICE, "%s: " UINT64_FORMAT " tuple(s) of %s are %s", trigger->tgname, SPI_processed, relname, (action == 'c') ? "deleted" : "set to null"); #endif } args += nkeys + 1; /* to the next relation */ } SPI_finish(); return PointerGetDatum((newtuple == NULL) ? trigtuple : newtuple); }
static uint32 gserialized_typmod_in(ArrayType *arr, int is_geography) { uint32 typmod = 0; Datum *elem_values; int n = 0; int i = 0; if (ARR_ELEMTYPE(arr) != CSTRINGOID) ereport(ERROR, (errcode(ERRCODE_ARRAY_ELEMENT_ERROR), errmsg("typmod array must be type cstring[]"))); if (ARR_NDIM(arr) != 1) ereport(ERROR, (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR), errmsg("typmod array must be one-dimensional"))); if (ARR_HASNULL(arr)) ereport(ERROR, (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED), errmsg("typmod array must not contain nulls"))); deconstruct_array(arr, CSTRINGOID, -2, false, 'c', /* hardwire cstring representation details */ &elem_values, NULL, &n); /* Set the SRID to the default value first */ if ( is_geography) TYPMOD_SET_SRID(typmod, SRID_DEFAULT); else TYPMOD_SET_SRID(typmod, SRID_UNKNOWN); for (i = 0; i < n; i++) { if ( i == 0 ) /* TYPE */ { char *s = DatumGetCString(elem_values[i]); uint8_t type = 0; int z = 0; int m = 0; if ( geometry_type_from_string(s, &type, &z, &m) == LW_FAILURE ) { ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("Invalid geometry type modifier: %s", s))); } else { TYPMOD_SET_TYPE(typmod, type); if ( z ) TYPMOD_SET_Z(typmod); if ( m ) TYPMOD_SET_M(typmod); } } if ( i == 1 ) /* SRID */ { int srid = pg_atoi(DatumGetCString(elem_values[i]), sizeof(int32), '\0'); srid = clamp_srid(srid); POSTGIS_DEBUGF(3, "srid: %d", srid); if ( srid != SRID_UNKNOWN ) { TYPMOD_SET_SRID(typmod, srid); } } } pfree(elem_values); return typmod; }
/* * Get resource usage. * * On QD this function dispatch the request to all QEs, collecting both * QEs' and QD's resource usage. * * On QE this function only collect the resource usage on itself. * * Memory & cpu usage are returned in JSON format. */ static void getResUsage(ResGroupStatCtx *ctx, Oid inGroupId) { int64 *usages; TimestampTz *timestamps; int i, j; usages = palloc(sizeof(*usages) * ctx->nGroups); timestamps = palloc(sizeof(*timestamps) * ctx->nGroups); for (j = 0; j < ctx->nGroups; j++) { ResGroupStat *row = &ctx->groups[j]; Oid groupId = DatumGetObjectId(row->groupId); usages[j] = ResGroupOps_GetCpuUsage(groupId); timestamps[j] = GetCurrentTimestamp(); } if (Gp_role == GP_ROLE_DISPATCH) { CdbPgResults cdb_pgresults = {NULL, 0}; StringInfoData buffer; initStringInfo(&buffer); appendStringInfo(&buffer, "SELECT groupid, cpu_usage, memory_usage " "FROM pg_resgroup_get_status(%d)", inGroupId); CdbDispatchCommand(buffer.data, DF_WITH_SNAPSHOT, &cdb_pgresults); if (cdb_pgresults.numResults == 0) elog(ERROR, "pg_resgroup_get_status() didn't get back any resource statistic from the segDBs"); for (i = 0; i < cdb_pgresults.numResults; i++) { struct pg_result *pg_result = cdb_pgresults.pg_results[i]; /* * Any error here should have propagated into errbuf, so we shouldn't * ever see anything other that tuples_ok here. But, check to be * sure. */ if (PQresultStatus(pg_result) != PGRES_TUPLES_OK) { cdbdisp_clearCdbPgResults(&cdb_pgresults); elog(ERROR, "pg_resgroup_get_status(): resultStatus not tuples_Ok"); } Assert(PQntuples(pg_result) == ctx->nGroups); for (j = 0; j < ctx->nGroups; j++) { const char *result; ResGroupStat *row = &ctx->groups[j]; Oid groupId = pg_atoi(PQgetvalue(pg_result, j, 0), sizeof(Oid), 0); Assert(groupId == row->groupId); if (row->memUsage->len == 0) { Datum d = ResGroupGetStat(groupId, RES_GROUP_STAT_MEM_USAGE); row->groupId = groupId; appendStringInfo(row->memUsage, "{\"%d\":%s", GpIdentity.segindex, DatumGetCString(d)); appendStringInfo(row->cpuUsage, "{"); calcCpuUsage(row->cpuUsage, usages[j], timestamps[j], ResGroupOps_GetCpuUsage(groupId), GetCurrentTimestamp()); } result = PQgetvalue(pg_result, j, 1); appendStringInfo(row->cpuUsage, ", %s", result); result = PQgetvalue(pg_result, j, 2); appendStringInfo(row->memUsage, ", %s", result); if (i == cdb_pgresults.numResults - 1) { appendStringInfoChar(row->cpuUsage, '}'); appendStringInfoChar(row->memUsage, '}'); } } } cdbdisp_clearCdbPgResults(&cdb_pgresults); } else { pg_usleep(300000); for (j = 0; j < ctx->nGroups; j++) { ResGroupStat *row = &ctx->groups[j]; Oid groupId = DatumGetObjectId(row->groupId); Datum d = ResGroupGetStat(groupId, RES_GROUP_STAT_MEM_USAGE); appendStringInfo(row->memUsage, "\"%d\":%s", GpIdentity.segindex, DatumGetCString(d)); calcCpuUsage(row->cpuUsage, usages[j], timestamps[j], ResGroupOps_GetCpuUsage(groupId), GetCurrentTimestamp()); } } }
/* * gp_persistent_relation_node_check() * * Reads the physical filesystem for every defined filespace and returns the * list of relfilenodes that actually exist. This list should match the set of * relfilenodes tracked in gp_persistent_relation_node. */ Datum gp_persistent_relation_node_check(PG_FUNCTION_ARGS) { FuncCallContext *fcontext; node_check_data *fdata; ReturnSetInfo *rsinfo; MemoryContext oldcontext; Oid relfilenode = InvalidOid; int32 segnum = 0; HeapTuple tuple; char *primaryPath = NULL; char *mirrorPath = NULL; if (SRF_IS_FIRSTCALL()) { Relation rel; TupleDesc tupdesc; fcontext = SRF_FIRSTCALL_INIT(); rsinfo = (ReturnSetInfo *) fcinfo->resultinfo; /* * The fdata cannot be allocated in the multi_call_ctx because the * multi_call_context gets cleaned up by the MultiFuncCall callback * function which gets called before the callback this function * registers to cleanup the fdata structure. So instead we allocate * in the parent context fn_mcxt. */ oldcontext = MemoryContextSwitchTo(fcinfo->flinfo->fn_mcxt); fdata = (node_check_data*) palloc0(sizeof(node_check_data)); fcontext->user_fctx = fdata; /* * Register a call to cleanup when the function ends. */ RegisterExprContextCallback(rsinfo->econtext, nodeCheckCleanup, PointerGetDatum(fdata)); /* * Setup the main loop over the list of tablespaces */ fdata->tablespaceRelation = heap_open(TableSpaceRelationId, AccessShareLock); fdata->scandesc = heap_beginscan(fdata->tablespaceRelation, SnapshotNow, 0, NULL); /* * Bless a tuple descriptor for the return type */ MemoryContextSwitchTo(fcontext->multi_call_memory_ctx); rel = RelationIdGetRelation(GpPersistentRelationNodeRelationId); tupdesc = RelationGetDescr(rel); fcontext->tuple_desc = BlessTupleDesc(tupdesc); relation_close(rel, NoLock); MemoryContextSwitchTo(oldcontext); } fcontext = SRF_PERCALL_SETUP(); fdata = fcontext->user_fctx; /* * The code here is basically a nested loop that has been unwound so that * it can be wrapped up into a set-returning function. * * Basic structure is: * - Loop over tablespace relation * - Loop over database directories in the tablespace * - Loop over relfilenodes in the directory * - Return each tablespace, database, relfilenode, segment_number. * * The complicating factor is that we return from this function and * reenter the loop at the innermost level, so the entire loop is turned * inside-out. */ while (true) { /* Innermost loop */ if (fdata->databaseDir) { struct dirent *dent; Datum values[Natts_gp_persistent_relation_node]; bool nulls[Natts_gp_persistent_relation_node]; dent = ReadDir(fdata->databaseDir, fdata->databaseDirName); if (!dent) { /* step out of innermost loop */ FreeDir(fdata->databaseDir); fdata->databaseDir = NULL; continue; } /* skip the boring stuff */ if (strcmp(dent->d_name, ".") == 0 || strcmp(dent->d_name, "..") == 0) continue; /* Skip things that don't look like relfilenodes */ if (!strToRelfilenode(dent->d_name, &relfilenode, &segnum)) continue; /* Return relfilenodes as we find them */ MemSet(nulls, true, sizeof(nulls)); nulls[Anum_gp_persistent_relation_node_tablespace_oid-1] = false; nulls[Anum_gp_persistent_relation_node_database_oid-1] = false; nulls[Anum_gp_persistent_relation_node_relfilenode_oid-1] = false; nulls[Anum_gp_persistent_relation_node_segment_file_num-1] = false; values[Anum_gp_persistent_relation_node_tablespace_oid-1] = ObjectIdGetDatum(fdata->tablespaceOid); values[Anum_gp_persistent_relation_node_database_oid-1] = ObjectIdGetDatum(fdata->databaseOid); values[Anum_gp_persistent_relation_node_relfilenode_oid-1] = ObjectIdGetDatum(relfilenode); values[Anum_gp_persistent_relation_node_segment_file_num-1] = Int32GetDatum(segnum); tuple = heap_form_tuple(fcontext->tuple_desc, values, nulls); SRF_RETURN_NEXT(fcontext, HeapTupleGetDatum(tuple)); } /* Loop over database directories in the tablespace */ if (fdata->tablespaceDir) { struct dirent *dent; dent = ReadDir(fdata->tablespaceDir, fdata->tablespaceDirName); if (!dent) { /* step out of database loop */ FreeDir(fdata->tablespaceDir); fdata->tablespaceDir = NULL; continue; } /* skip the borring stuff */ if (strcmp(dent->d_name, ".") == 0 || strcmp(dent->d_name, "..") == 0) continue; /* Skip things that don't look like database oids */ if (strlen(dent->d_name) != strspn(dent->d_name, "0123456789")) continue; /* convert the string to an oid */ fdata->databaseOid = pg_atoi(dent->d_name, 4, 0); /* form a database path using this oid */ snprintf(fdata->databaseDirName, MAXPGPATH, "%s/%s", fdata->tablespaceDirName, dent->d_name); oldcontext = MemoryContextSwitchTo(fcontext->multi_call_memory_ctx); fdata->databaseDir = AllocateDir(fdata->databaseDirName); MemoryContextSwitchTo(oldcontext); if (fdata->databaseDir == NULL) ereport(ERROR, (errcode_for_file_access(), errmsg("could not open directory \"%s\": %m", fdata->databaseDirName))); continue; } /* Outermost loop over tablespaces */ tuple = heap_getnext(fdata->scandesc, ForwardScanDirection); if (!HeapTupleIsValid(tuple)) SRF_RETURN_DONE(fcontext); /* FINAL return */ fdata->tablespaceOid = HeapTupleGetOid(tuple); PersistentTablespace_GetPrimaryAndMirrorFilespaces( fdata->tablespaceOid, &primaryPath, &mirrorPath); /* Find the location of this tablespace on disk */ FormTablespacePath(fdata->tablespaceDirName, primaryPath, fdata->tablespaceOid); /* * Primary path is null for the pg_system filespace, additionally * Mirror path is null if there are no mirrors */ if (primaryPath) { pfree(primaryPath); primaryPath = NULL; } if (mirrorPath) { pfree(mirrorPath); mirrorPath = NULL; } oldcontext = MemoryContextSwitchTo(fcontext->multi_call_memory_ctx); fdata->tablespaceDir = AllocateDir(fdata->tablespaceDirName); MemoryContextSwitchTo(oldcontext); if (fdata->tablespaceDir == NULL) ereport(ERROR, (errcode_for_file_access(), errmsg("could not open directory \"%s\": %m", fdata->tablespaceDirName))); /* The global tablespace doesn't have database directories */ if (fdata->tablespaceOid == GLOBALTABLESPACE_OID) { fdata->databaseOid = 0; /* Skip to the innermost loop */ fdata->databaseDir = fdata->tablespaceDir; fdata->tablespaceDir = NULL; strncpy(fdata->databaseDirName, fdata->tablespaceDirName, MAXPGPATH); } } /* Unreachable */ SRF_RETURN_DONE(fcontext); }
/* * int2in - converts "num" to short */ datum_t int2in(PG_FUNC_ARGS) { char *num = ARG_CSTRING(0); RET_INT16(pg_atoi(num, sizeof(int16), '\0')); }
/* * int4in - converts "num" to int4 */ datum_t int4in(PG_FUNC_ARGS) { char *num = ARG_CSTRING(0); RET_INT32(pg_atoi(num, sizeof(int32), '\0')); }