Datum oid_text(PG_FUNCTION_ARGS) { Oid oid = PG_GETARG_OID(0); text *result; int len; char *str; str = DatumGetCString(DirectFunctionCall1(oidout, ObjectIdGetDatum(oid))); len = strlen(str) + VARHDRSZ; result = (text *) palloc(len); VARATT_SIZEP(result) = len; memcpy(VARDATA(result), str, (len - VARHDRSZ)); pfree(str); PG_RETURN_TEXT_P(result); }
static Datum uuid_generate_internal(int mode, const uuid_t *ns, const char *name) { uuid_t *uuid; char *str; uuid_rc_t rc; rc = uuid_create(&uuid); if (rc != UUID_RC_OK) pguuid_complain(rc); rc = uuid_make(uuid, mode, ns, name); if (rc != UUID_RC_OK) pguuid_complain(rc); str = uuid_to_string(uuid); rc = uuid_destroy(uuid); if (rc != UUID_RC_OK) pguuid_complain(rc); return DirectFunctionCall1(uuid_in, CStringGetDatum(str)); }
static Datum special_uuid_value(const char *name) { uuid_t *uuid; char *str; uuid_rc_t rc; rc = uuid_create(&uuid); if (rc != UUID_RC_OK) pguuid_complain(rc); rc = uuid_load(uuid, name); if (rc != UUID_RC_OK) pguuid_complain(rc); str = uuid_to_string(uuid); rc = uuid_destroy(uuid); if (rc != UUID_RC_OK) pguuid_complain(rc); return DirectFunctionCall1(uuid_in, CStringGetDatum(str)); }
/* * Convert string using encoding_name. The destination * encoding is the DB encoding. * * TEXT convert_from(BYTEA string, NAME encoding_name) */ Datum pg_convert_from(PG_FUNCTION_ARGS) { Datum string = PG_GETARG_DATUM(0); Datum src_encoding_name = PG_GETARG_DATUM(1); Datum dest_encoding_name = DirectFunctionCall1(namein, CStringGetDatum(DatabaseEncoding->name)); Datum result; result = DirectFunctionCall3(pg_convert, string, src_encoding_name, dest_encoding_name); /* * pg_convert returns a bytea, which we in turn return as text, relying on * the fact that they are both in fact varlena types, and thus * structurally identical. Although not all bytea values are valid text, * in this case it will be because we've told pg_convert to return one * that is valid as text in the current database encoding. */ PG_RETURN_DATUM(result); }
PATH * poly2path(POLYGON *poly) { int i; char *output = (char *) palloc(2 * (P_MAXDIG + 1) * poly->npts + 64); char buf[2 * (P_MAXDIG) + 20]; sprintf(output, "(1, %*d", P_MAXDIG, poly->npts); for (i = 0; i < poly->npts; i++) { snprintf(buf, sizeof(buf), ",%*g,%*g", P_MAXDIG, poly->p[i].x, P_MAXDIG, poly->p[i].y); strcat(output, buf); } snprintf(buf, sizeof(buf), "%c", RDELIM); strcat(output, buf); return DatumGetPathP(DirectFunctionCall1(path_in, CStringGetDatum(output))); }
Datum pgstrom_numeric_avg_final(PG_FUNCTION_ARGS) { numeric_agg_state *state; Datum vN; Datum result; state = PG_ARGISNULL(0) ? NULL : (numeric_agg_state *)PG_GETARG_POINTER(0); /* If there were no non-null inputs, return NULL */ if (state == NULL || state->N == 0) PG_RETURN_NULL(); /* If any NaN value is accumlated, return NaN */ if (numeric_is_nan(DatumGetNumeric(state->sumX))) PG_RETURN_NUMERIC(state->sumX); vN = DirectFunctionCall1(int8_numeric, Int64GetDatum(state->N)); result = DirectFunctionCall2(numeric_div, state->sumX, vN); PG_RETURN_NUMERIC(result); }
Datum text_macaddr(PG_FUNCTION_ARGS) { text *addr = PG_GETARG_TEXT_P(0); Datum result; char str[100]; int len; len = (VARSIZE(addr) - VARHDRSZ); if (len >= sizeof(str)) ereport(ERROR, (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION), errmsg("text too long to convert to MAC address"))); memcpy(str, VARDATA(addr), len); *(str + len) = '\0'; result = DirectFunctionCall1(macaddr_in, CStringGetDatum(str)); return (result); }
Datum pgstrom_int8_avg_accum(PG_FUNCTION_ARGS) { int32 nrows = PG_GETARG_INT32(1); Datum addNum; MemoryContext aggcxt; MemoryContext oldcxt; numeric_agg_state *state; if (!AggCheckCallContext(fcinfo, &aggcxt)) elog(ERROR, "aggregate function called in non-aggregate context"); if (PG_ARGISNULL(1)) nrows = 0; else if (nrows < 0) elog(ERROR, "Bug? negative nrows were given"); /* make a state object and update it */ oldcxt = MemoryContextSwitchTo(aggcxt); state = PG_ARGISNULL(0) ? NULL : (numeric_agg_state *)PG_GETARG_POINTER(0); if (!state) { state = palloc0(sizeof(numeric_agg_state)); state->N = 0; state->sumX = DirectFunctionCall3(numeric_in, CStringGetDatum("0"), ObjectIdGetDatum(0), Int32GetDatum(-1)); } if (nrows > 0 && !PG_ARGISNULL(2)) { state->N += nrows; addNum = DirectFunctionCall1(int8_numeric, PG_GETARG_DATUM(2)); state->sumX = DirectFunctionCall2(numeric_add, state->sumX, addNum); } MemoryContextSwitchTo(oldcxt); PG_RETURN_POINTER(state); }
Datum macaddr_text(PG_FUNCTION_ARGS) { /* Input is a macaddr, but may as well leave it in Datum form */ Datum addr = PG_GETARG_DATUM(0); text *result; char *str; int len; str = DatumGetCString(DirectFunctionCall1(macaddr_out, addr)); len = (strlen(str) + VARHDRSZ); result = palloc(len); SET_VARSIZE(result, len); memcpy(VARDATA(result), str, (len - VARHDRSZ)); pfree(str); PG_RETURN_TEXT_P(result); }
/* * float4_text - converts a float4 number to a text string */ Datum float4_text(PG_FUNCTION_ARGS) { float4 num = PG_GETARG_FLOAT4(0); text *result; int len; char *str; str = DatumGetCString(DirectFunctionCall1(float4out, Float4GetDatum(num))); len = strlen(str) + VARHDRSZ; result = (text *) palloc(len); VARATT_SIZEP(result) = len; memcpy(VARDATA(result), str, (len - VARHDRSZ)); pfree(str); PG_RETURN_TEXT_P(result); }
Datum pcpoint_get_value(PG_FUNCTION_ARGS) { SERIALIZED_POINT *serpt = PG_GETARG_SERPOINT_P(0); text *dim_name = PG_GETARG_TEXT_P(1); char *dim_str; float8 double_result; PCSCHEMA *schema = pc_schema_from_pcid(serpt->pcid, fcinfo); PCPOINT *pt = pc_point_deserialize(serpt, schema); if ( ! pt ) PG_RETURN_NULL(); dim_str = text_to_cstring(dim_name); if ( ! pc_point_get_double_by_name(pt, dim_str, &double_result) ) { pc_point_free(pt); elog(ERROR, "dimension \"%s\" does not exist in schema", dim_str); } pfree(dim_str); pc_point_free(pt); PG_RETURN_DATUM(DirectFunctionCall1(float8_numeric, Float8GetDatum(double_result))); }
Datum LWGEOM_asText(PG_FUNCTION_ARGS) { LWGEOM_UNPARSER_RESULT lwg_unparser_result; PG_LWGEOM *lwgeom; PG_LWGEOM *ogclwgeom; int len, result; char *lwgeom_result,*loc_wkt; char *semicolonLoc; POSTGIS_DEBUG(2, "LWGEOM_asText called."); lwgeom = (PG_LWGEOM *)PG_DETOAST_DATUM(PG_GETARG_DATUM(0)); /* Force to 2d */ ogclwgeom = (PG_LWGEOM *)DatumGetPointer(DirectFunctionCall1( LWGEOM_force_2d, PointerGetDatum(lwgeom))); result = serialized_lwgeom_to_ewkt(&lwg_unparser_result, SERIALIZED_FORM(ogclwgeom), PARSER_CHECK_NONE); if (result) PG_UNPARSER_ERROR(lwg_unparser_result); semicolonLoc = strchr(lwg_unparser_result.wkoutput,';'); /* loc points to start of wkt */ if (semicolonLoc == NULL) loc_wkt = lwg_unparser_result.wkoutput; else loc_wkt = semicolonLoc +1; len = strlen(loc_wkt)+VARHDRSZ; lwgeom_result = palloc(len); SET_VARSIZE(lwgeom_result, len); memcpy(VARDATA(lwgeom_result), loc_wkt, len-VARHDRSZ); pfree(lwg_unparser_result.wkoutput); PG_FREE_IF_COPY(lwgeom, 0); if ( ogclwgeom != lwgeom ) pfree(ogclwgeom); PG_RETURN_POINTER(lwgeom_result); }
Datum current_schemas(PG_FUNCTION_ARGS) { List *search_path = fetch_search_path(PG_GETARG_BOOL(0)); ListCell *l; Datum *names; int i; ArrayType *array; names = (Datum *) palloc(list_length(search_path) * sizeof(Datum)); i = 0; foreach(l, search_path) { char *nspname; nspname = get_namespace_name(lfirst_oid(l)); if (nspname) /* watch out for deleted namespace */ { names[i] = DirectFunctionCall1(namein, CStringGetDatum(nspname)); i++; } }
/* * This creates a large object, and sets its OID to the value in the * supplied string. * * If the string is empty, then a new LargeObject is created, and its oid * is placed in the resulting lo. */ Blob * lo_in(char *str) { Blob *result; Oid oid; int count; if (strlen(str) > 0) { count = sscanf(str, "%u", &oid); if (count < 1) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("error in parsing \"%s\"", str))); if (oid == InvalidOid) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("illegal oid: \"%s\"", str))); } else { /* * There is no Oid passed, so create a new one */ oid = DatumGetObjectId(DirectFunctionCall1(lo_creat, Int32GetDatum(INV_READ | INV_WRITE))); if (oid == InvalidOid) /* internal error */ elog(ERROR, "InvalidOid returned from lo_creat"); } result = (Blob *) palloc(sizeof(Blob)); *result = oid; return (result); }
Datum spherepoly_out(PG_FUNCTION_ARGS) { SPOLY *poly = PG_GETARG_SPOLY(0); int32 i; char *out = (char *) palloc(128 * poly->npts); char *tmp; strcpy(out, "{"); for (i = 0; i < poly->npts; i++) { if (i > 0) { strcat(out, ","); } tmp = DatumGetPointer(DirectFunctionCall1(spherepoint_out, PointerGetDatum(&poly->p[i]))); strcat(out, tmp); pfree(tmp); } strcat(out, "}"); PG_RETURN_CSTRING(out); }
Datum months_between(PG_FUNCTION_ARGS) { DateADT date1 = PG_GETARG_DATEADT(0); DateADT date2 = PG_GETARG_DATEADT(1); int y1, m1, d1; int y2, m2, d2; float8 result; j2date(date1 + POSTGRES_EPOCH_JDATE, &y1, &m1, &d1); j2date(date2 + POSTGRES_EPOCH_JDATE, &y2, &m2, &d2); /* Ignore day components for last days, or based on a 31-day month. */ if (d1 == days_of_month(y1, m1) && d2 == days_of_month(y2, m2)) result = (y1 - y2) * 12 + (m1 - m2); else result = (y1 - y2) * 12 + (m1 - m2) + (d1 - d2) / 31.0; PG_RETURN_NUMERIC( DirectFunctionCall1(float8_numeric, Float8GetDatumFast(result))); }
/* * GenereatePartitioningInformation returns the partitioning type and partition column * for the given parent table in the form of "PARTITION TYPE (partitioning column(s)/expression(s))". */ char * GeneratePartitioningInformation(Oid parentTableId) { char *partitionBoundCString = ""; #if (PG_VERSION_NUM >= 100000) Datum partitionBoundDatum = 0; if (!PartitionedTable(parentTableId)) { char *relationName = get_rel_name(parentTableId); ereport(ERROR, (errmsg("\"%s\" is not a parent table", relationName))); } partitionBoundDatum = DirectFunctionCall1(pg_get_partkeydef, ObjectIdGetDatum(parentTableId)); partitionBoundCString = TextDatumGetCString(partitionBoundDatum); #endif return partitionBoundCString; }
/* * Error context callback for handling errors in SQL function definitions */ static void sql_function_parse_error_callback(void *arg) { HeapTuple tuple = (HeapTuple) arg; Form_pg_proc proc = (Form_pg_proc) GETSTRUCT(tuple); bool isnull; Datum tmp; char *prosrc; /* See if it's a syntax error; if so, transpose to CREATE FUNCTION */ tmp = SysCacheGetAttr(PROCOID, tuple, Anum_pg_proc_prosrc, &isnull); if (isnull) elog(ERROR, "null prosrc"); prosrc = DatumGetCString(DirectFunctionCall1(textout, tmp)); if (!function_parse_error_transpose(prosrc)) { /* If it's not a syntax error, push info onto context stack */ errcontext("SQL function \"%s\"", NameStr(proc->proname)); } pfree(prosrc); }
static Datum uuid_generate_v35_internal(int mode, pg_uuid_t *ns, text *name) { uuid_t *ns_uuid; Datum result; uuid_rc_t rc; rc = uuid_create(&ns_uuid); if (rc != UUID_RC_OK) pguuid_complain(rc); string_to_uuid(DatumGetCString(DirectFunctionCall1(uuid_out, UUIDPGetDatum(ns))), ns_uuid); result = uuid_generate_internal(mode, ns_uuid, text_to_cstring(name)); rc = uuid_destroy(ns_uuid); if (rc != UUID_RC_OK) pguuid_complain(rc); return result; }
/* * plr_quote_literal() - quote literal strings that are to * be used in SPI_exec query strings */ SEXP plr_quote_literal(SEXP rval) { const char *value; text *value_text; text *result_text; SEXP result; /* extract the C string */ PROTECT(rval = AS_CHARACTER(rval)); value = CHAR(STRING_ELT(rval, 0)); /* convert using the pgsql quote_literal function */ value_text = PG_STR_GET_TEXT(value); result_text = DatumGetTextP(DirectFunctionCall1(quote_literal, PointerGetDatum(value_text))); /* copy result back into an R object */ PROTECT(result = NEW_CHARACTER(1)); SET_STRING_ELT(result, 0, COPY_TO_USER_STRING(PG_TEXT_GET_STR(result_text))); UNPROTECT(2); return result; }
/* * Validator for internal functions * * Check that the given internal function name (the "prosrc" value) is * a known builtin function. */ Datum fmgr_internal_validator(PG_FUNCTION_ARGS) { Oid funcoid = PG_GETARG_OID(0); HeapTuple tuple; Form_pg_proc proc; bool isnull; Datum tmp; char *prosrc; /* * We do not honor check_function_bodies since it's unlikely the function * name will be found later if it isn't there now. */ tuple = SearchSysCache(PROCOID, ObjectIdGetDatum(funcoid), 0, 0, 0); if (!HeapTupleIsValid(tuple)) elog(ERROR, "cache lookup failed for function %u", funcoid); proc = (Form_pg_proc) GETSTRUCT(tuple); tmp = SysCacheGetAttr(PROCOID, tuple, Anum_pg_proc_prosrc, &isnull); if (isnull) elog(ERROR, "null prosrc"); prosrc = DatumGetCString(DirectFunctionCall1(textout, tmp)); if (fmgr_internal_function(prosrc) == InvalidOid) ereport(ERROR, (errcode(ERRCODE_UNDEFINED_FUNCTION), errmsg("there is no built-in function named \"%s\"", prosrc))); ReleaseSysCache(tuple); PG_RETURN_VOID(); }
Datum ltree_textadd(PG_FUNCTION_ARGS) { ltree *a = PG_GETARG_LTREE(1); text *b = PG_GETARG_TEXT_PP(0); char *s; ltree *r, *tmp; s = text_to_cstring(b); tmp = (ltree *) DatumGetPointer(DirectFunctionCall1(ltree_in, PointerGetDatum(s))); pfree(s); r = ltree_concat(tmp, a); pfree(tmp); PG_FREE_IF_COPY(a, 1); PG_FREE_IF_COPY(b, 0); PG_RETURN_POINTER(r); }
/* * PLyObject_FromJsonbValue * * Transform JsonbValue to PyObject. */ static PyObject * PLyObject_FromJsonbValue(JsonbValue *jsonbValue) { switch (jsonbValue->type) { case jbvNull: Py_RETURN_NONE; case jbvBinary: return PLyObject_FromJsonbContainer(jsonbValue->val.binary.data); case jbvNumeric: { Datum num; char *str; num = NumericGetDatum(jsonbValue->val.numeric); str = DatumGetCString(DirectFunctionCall1(numeric_out, num)); return PyObject_CallFunction(decimal_constructor, "s", str); } case jbvString: return PLyString_FromJsonbValue(jsonbValue); case jbvBool: if (jsonbValue->val.boolean) Py_RETURN_TRUE; else Py_RETURN_FALSE; default: elog(ERROR, "unexpected jsonb value type: %d", jsonbValue->type); return NULL; } }
/* * regclassin - converts "classname" to class OID * * We also accept a numeric OID, for symmetry with the output routine. * * '-' signifies unknown (OID 0). In all other cases, the input must * match an existing pg_class entry. */ Datum regclassin(PG_FUNCTION_ARGS) { char *class_name_or_oid = PG_GETARG_CSTRING(0); Oid result = InvalidOid; List *names; /* '-' ? */ if (strcmp(class_name_or_oid, "-") == 0) PG_RETURN_OID(InvalidOid); /* Numeric OID? */ if (class_name_or_oid[0] >= '0' && class_name_or_oid[0] <= '9' && strspn(class_name_or_oid, "0123456789") == strlen(class_name_or_oid)) { result = DatumGetObjectId(DirectFunctionCall1(oidin, CStringGetDatum(class_name_or_oid))); PG_RETURN_OID(result); } /* Else it's a name, possibly schema-qualified */ /* * In bootstrap mode we assume the given name is not schema-qualified, and * just search pg_class for a match. This is needed for initializing * other system catalogs (pg_namespace may not exist yet, and certainly * there are no schemas other than pg_catalog). */ if (IsBootstrapProcessingMode()) { Relation hdesc; ScanKeyData skey[1]; SysScanDesc sysscan; HeapTuple tuple; ScanKeyInit(&skey[0], Anum_pg_class_relname, BTEqualStrategyNumber, F_NAMEEQ, CStringGetDatum(class_name_or_oid)); hdesc = heap_open(RelationRelationId, AccessShareLock); sysscan = systable_beginscan(hdesc, ClassNameNspIndexId, true, NULL, 1, skey); if (HeapTupleIsValid(tuple = systable_getnext(sysscan))) result = HeapTupleGetOid(tuple); else ereport(ERROR, (errcode(ERRCODE_UNDEFINED_TABLE), errmsg("relation \"%s\" does not exist", class_name_or_oid))); /* We assume there can be only one match */ systable_endscan(sysscan); heap_close(hdesc, AccessShareLock); PG_RETURN_OID(result); } /* * Normal case: parse the name into components and see if it matches any * pg_class entries in the current search path. */ names = stringToQualifiedNameList(class_name_or_oid); /* We might not even have permissions on this relation; don't lock it. */ result = RangeVarGetRelid(makeRangeVarFromNameList(names), NoLock, false); PG_RETURN_OID(result); }
/* * regtypein - converts "typename" to type OID * * We also accept a numeric OID, for symmetry with the output routine. * * '-' signifies unknown (OID 0). In all other cases, the input must * match an existing pg_type entry. * * In bootstrap mode the name must just equal some existing name in pg_type. * In normal mode the type name can be specified using the full type syntax * recognized by the parser; for example, DOUBLE PRECISION and INTEGER[] will * work and be translated to the correct type names. (We ignore any typmod * info generated by the parser, however.) */ Datum regtypein(PG_FUNCTION_ARGS) { char *typ_name_or_oid = PG_GETARG_CSTRING(0); Oid result = InvalidOid; int32 typmod; /* '-' ? */ if (strcmp(typ_name_or_oid, "-") == 0) PG_RETURN_OID(InvalidOid); /* Numeric OID? */ if (typ_name_or_oid[0] >= '0' && typ_name_or_oid[0] <= '9' && strspn(typ_name_or_oid, "0123456789") == strlen(typ_name_or_oid)) { result = DatumGetObjectId(DirectFunctionCall1(oidin, CStringGetDatum(typ_name_or_oid))); PG_RETURN_OID(result); } /* Else it's a type name, possibly schema-qualified or decorated */ /* * In bootstrap mode we assume the given name is not schema-qualified, and * just search pg_type for a match. This is needed for initializing other * system catalogs (pg_namespace may not exist yet, and certainly there * are no schemas other than pg_catalog). */ if (IsBootstrapProcessingMode()) { Relation hdesc; ScanKeyData skey[1]; SysScanDesc sysscan; HeapTuple tuple; ScanKeyInit(&skey[0], Anum_pg_type_typname, BTEqualStrategyNumber, F_NAMEEQ, CStringGetDatum(typ_name_or_oid)); hdesc = heap_open(TypeRelationId, AccessShareLock); sysscan = systable_beginscan(hdesc, TypeNameNspIndexId, true, NULL, 1, skey); if (HeapTupleIsValid(tuple = systable_getnext(sysscan))) result = HeapTupleGetOid(tuple); else ereport(ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT), errmsg("type \"%s\" does not exist", typ_name_or_oid))); /* We assume there can be only one match */ systable_endscan(sysscan); heap_close(hdesc, AccessShareLock); PG_RETURN_OID(result); } /* * Normal case: invoke the full parser to deal with special cases such as * array syntax. */ parseTypeString(typ_name_or_oid, &result, &typmod); PG_RETURN_OID(result); }
/* * regprocin - converts "proname" to proc OID * * We also accept a numeric OID, for symmetry with the output routine. * * '-' signifies unknown (OID 0). In all other cases, the input must * match an existing pg_proc entry. */ Datum regprocin(PG_FUNCTION_ARGS) { char *pro_name_or_oid = PG_GETARG_CSTRING(0); RegProcedure result = InvalidOid; List *names; FuncCandidateList clist; /* '-' ? */ if (strcmp(pro_name_or_oid, "-") == 0) PG_RETURN_OID(InvalidOid); /* Numeric OID? */ if (pro_name_or_oid[0] >= '0' && pro_name_or_oid[0] <= '9' && strspn(pro_name_or_oid, "0123456789") == strlen(pro_name_or_oid)) { result = DatumGetObjectId(DirectFunctionCall1(oidin, CStringGetDatum(pro_name_or_oid))); PG_RETURN_OID(result); } /* Else it's a name, possibly schema-qualified */ /* * In bootstrap mode we assume the given name is not schema-qualified, and * just search pg_proc for a unique match. This is needed for * initializing other system catalogs (pg_namespace may not exist yet, and * certainly there are no schemas other than pg_catalog). */ if (IsBootstrapProcessingMode()) { int matches = 0; Relation hdesc; ScanKeyData skey[1]; SysScanDesc sysscan; HeapTuple tuple; ScanKeyInit(&skey[0], Anum_pg_proc_proname, BTEqualStrategyNumber, F_NAMEEQ, CStringGetDatum(pro_name_or_oid)); hdesc = heap_open(ProcedureRelationId, AccessShareLock); sysscan = systable_beginscan(hdesc, ProcedureNameArgsNspIndexId, true, NULL, 1, skey); while (HeapTupleIsValid(tuple = systable_getnext(sysscan))) { result = (RegProcedure) HeapTupleGetOid(tuple); if (++matches > 1) break; } systable_endscan(sysscan); heap_close(hdesc, AccessShareLock); if (matches == 0) ereport(ERROR, (errcode(ERRCODE_UNDEFINED_FUNCTION), errmsg("function \"%s\" does not exist", pro_name_or_oid))); else if (matches > 1) ereport(ERROR, (errcode(ERRCODE_AMBIGUOUS_FUNCTION), errmsg("more than one function named \"%s\"", pro_name_or_oid))); PG_RETURN_OID(result); } /* * Normal case: parse the name into components and see if it matches any * pg_proc entries in the current search path. */ names = stringToQualifiedNameList(pro_name_or_oid); clist = FuncnameGetCandidates(names, -1, NIL, false, false, false); if (clist == NULL) ereport(ERROR, (errcode(ERRCODE_UNDEFINED_FUNCTION), errmsg("function \"%s\" does not exist", pro_name_or_oid))); else if (clist->next != NULL) ereport(ERROR, (errcode(ERRCODE_AMBIGUOUS_FUNCTION), errmsg("more than one function named \"%s\"", pro_name_or_oid))); result = clist->oid; PG_RETURN_OID(result); }
Datum gp_inject_fault(PG_FUNCTION_ARGS) { char *faultName = TextDatumGetCString(PG_GETARG_DATUM(0)); char *type = TextDatumGetCString(PG_GETARG_DATUM(1)); char *ddlStatement = TextDatumGetCString(PG_GETARG_DATUM(2)); char *databaseName = TextDatumGetCString(PG_GETARG_DATUM(3)); char *tableName = TextDatumGetCString(PG_GETARG_DATUM(4)); int numOccurrences = PG_GETARG_INT32(5); int sleepTimeSeconds = PG_GETARG_INT32(6); int dbid = PG_GETARG_INT32(7); StringInfo faultmsg = makeStringInfo(); /* Fast path if injecting fault in our postmaster. */ if (GpIdentity.dbid == dbid) { appendStringInfo(faultmsg, "%s\n%s\n%s\n%s\n%s\n%d\n%d\n", faultName, type, ddlStatement, databaseName, tableName, numOccurrences, sleepTimeSeconds); int offset = 0; char *response = processTransitionRequest_faultInject( faultmsg->data, &offset, faultmsg->len); if (!response) elog(ERROR, "failed to inject fault locally (dbid %d)", dbid); if (strncmp(response, "Success:", strlen("Success:")) != 0) elog(ERROR, "%s", response); elog(NOTICE, "%s", response); PG_RETURN_DATUM(true); } /* Obtain host and port of the requested dbid */ HeapTuple tuple; Relation rel = heap_open(GpSegmentConfigRelationId, AccessShareLock); ScanKeyData scankey; SysScanDesc sscan; ScanKeyInit(&scankey, Anum_gp_segment_configuration_dbid, BTEqualStrategyNumber, F_INT2EQ, Int16GetDatum((int16) dbid)); sscan = systable_beginscan(rel, GpSegmentConfigDbidIndexId, true, GetTransactionSnapshot(), 1, &scankey); tuple = systable_getnext(sscan); if (!HeapTupleIsValid(tuple)) elog(ERROR, "cannot find dbid %d", dbid); bool isnull; Datum datum = heap_getattr(tuple, Anum_gp_segment_configuration_hostname, RelationGetDescr(rel), &isnull); char *hostname; if (!isnull) hostname = DatumGetCString(DirectFunctionCall1(textout, datum)); else elog(ERROR, "hostname is null for dbid %d", dbid); int port = DatumGetInt32(heap_getattr(tuple, Anum_gp_segment_configuration_port, RelationGetDescr(rel), &isnull)); systable_endscan(sscan); heap_close(rel, NoLock); struct addrinfo *addrList = NULL; struct addrinfo hint; int ret; /* Initialize hint structure */ MemSet(&hint, 0, sizeof(hint)); hint.ai_socktype = SOCK_STREAM; hint.ai_family = AF_UNSPEC; char portStr[100]; if (snprintf(portStr, sizeof(portStr), "%d", port) >= sizeof(portStr)) elog(ERROR, "port number too long for dbid %d", dbid); /* Use pg_getaddrinfo_all() to resolve the address */ ret = pg_getaddrinfo_all(hostname, portStr, &hint, &addrList); if (ret || !addrList) { if (addrList) pg_freeaddrinfo_all(hint.ai_family, addrList); elog(ERROR, "could not translate host name \"%s\" to address: %s\n", hostname, gai_strerror(ret)); } PrimaryMirrorTransitionClientInfo client; client.receivedDataCallbackFn = transitionReceivedDataFn; client.errorLogFn = transitionErrorLogFn; client.checkForNeedToExitFn = checkForNeedToExitFn; transitionMsgErrors = makeStringInfo(); appendStringInfo(faultmsg, "%s\n%s\n%s\n%s\n%s\n%s\n%d\n%d\n", "faultInject", faultName, type, ddlStatement, databaseName, tableName, numOccurrences, sleepTimeSeconds); if (sendTransitionMessage(&client, addrList, faultmsg->data, faultmsg->len, 1 /* retries */, 60 /* timeout */) != TRANS_ERRCODE_SUCCESS) { pg_freeaddrinfo_all(hint.ai_family, addrList); ereport(ERROR, (errmsg("failed to inject %s fault in dbid %d", faultName, dbid), errdetail("%s", transitionMsgErrors->data))); } pg_freeaddrinfo_all(hint.ai_family, addrList); PG_RETURN_DATUM(BoolGetDatum(true)); }
/* * AggregateCreate */ void AggregateCreate(const char *aggName, Oid aggNamespace, List *aggtransfnName, List *aggfinalfnName, Oid aggBaseType, Oid aggTransType, const char *agginitval) { Relation aggdesc; HeapTuple tup; char nulls[Natts_pg_aggregate]; Datum values[Natts_pg_aggregate]; Form_pg_proc proc; Oid transfn; Oid finalfn = InvalidOid; /* can be omitted */ Oid rettype; Oid finaltype; Oid fnArgs[FUNC_MAX_ARGS]; int nargs_transfn; Oid procOid; TupleDesc tupDesc; int i; ObjectAddress myself, referenced; /* sanity checks (caller should have caught these) */ if (!aggName) elog(ERROR, "no aggregate name supplied"); if (!aggtransfnName) elog(ERROR, "aggregate must have a transition function"); /* * If transtype is polymorphic, basetype must be polymorphic also; * else we will have no way to deduce the actual transtype. */ if ((aggTransType == ANYARRAYOID || aggTransType == ANYELEMENTOID) && !(aggBaseType == ANYARRAYOID || aggBaseType == ANYELEMENTOID)) ereport(ERROR, (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), errmsg("cannot determine transition data type"), errdetail("An aggregate using \"anyarray\" or \"anyelement\" as " "transition type must have one of them as its base type."))); /* handle transfn */ MemSet(fnArgs, 0, FUNC_MAX_ARGS * sizeof(Oid)); fnArgs[0] = aggTransType; if (aggBaseType == ANYOID) nargs_transfn = 1; else { fnArgs[1] = aggBaseType; nargs_transfn = 2; } transfn = lookup_agg_function(aggtransfnName, nargs_transfn, fnArgs, &rettype); /* * Return type of transfn (possibly after refinement by * enforce_generic_type_consistency, if transtype isn't polymorphic) * must exactly match declared transtype. * * In the non-polymorphic-transtype case, it might be okay to allow a * rettype that's binary-coercible to transtype, but I'm not quite * convinced that it's either safe or useful. When transtype is * polymorphic we *must* demand exact equality. */ if (rettype != aggTransType) ereport(ERROR, (errcode(ERRCODE_DATATYPE_MISMATCH), errmsg("return type of transition function %s is not %s", NameListToString(aggtransfnName), format_type_be(aggTransType)))); tup = SearchSysCache(PROCOID, ObjectIdGetDatum(transfn), 0, 0, 0); if (!HeapTupleIsValid(tup)) elog(ERROR, "cache lookup failed for function %u", transfn); proc = (Form_pg_proc) GETSTRUCT(tup); /* * If the transfn is strict and the initval is NULL, make sure input * type and transtype are the same (or at least binary-compatible), so * that it's OK to use the first input value as the initial * transValue. */ if (proc->proisstrict && agginitval == NULL) { if (!IsBinaryCoercible(aggBaseType, aggTransType)) ereport(ERROR, (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), errmsg("must not omit initial value when transition function is strict and transition type is not compatible with input type"))); } ReleaseSysCache(tup); /* handle finalfn, if supplied */ if (aggfinalfnName) { MemSet(fnArgs, 0, FUNC_MAX_ARGS * sizeof(Oid)); fnArgs[0] = aggTransType; finalfn = lookup_agg_function(aggfinalfnName, 1, fnArgs, &finaltype); } else { /* * If no finalfn, aggregate result type is type of the state value */ finaltype = aggTransType; } Assert(OidIsValid(finaltype)); /* * If finaltype (i.e. aggregate return type) is polymorphic, basetype * must be polymorphic also, else parser will fail to deduce result * type. (Note: given the previous test on transtype and basetype, * this cannot happen, unless someone has snuck a finalfn definition * into the catalogs that itself violates the rule against polymorphic * result with no polymorphic input.) */ if ((finaltype == ANYARRAYOID || finaltype == ANYELEMENTOID) && !(aggBaseType == ANYARRAYOID || aggBaseType == ANYELEMENTOID)) ereport(ERROR, (errcode(ERRCODE_DATATYPE_MISMATCH), errmsg("cannot determine result data type"), errdetail("An aggregate returning \"anyarray\" or \"anyelement\" " "must have one of them as its base type."))); /* * Everything looks okay. Try to create the pg_proc entry for the * aggregate. (This could fail if there's already a conflicting * entry.) */ MemSet(fnArgs, 0, FUNC_MAX_ARGS * sizeof(Oid)); fnArgs[0] = aggBaseType; procOid = ProcedureCreate(aggName, aggNamespace, false, /* no replacement */ false, /* doesn't return a set */ finaltype, /* returnType */ INTERNALlanguageId, /* languageObjectId */ 0, "aggregate_dummy", /* placeholder proc */ "-", /* probin */ true, /* isAgg */ false, /* security invoker (currently not * definable for agg) */ false, /* isStrict (not needed for agg) */ PROVOLATILE_IMMUTABLE, /* volatility (not * needed for agg) */ 1, /* parameterCount */ fnArgs); /* parameterTypes */ /* * Okay to create the pg_aggregate entry. */ /* initialize nulls and values */ for (i = 0; i < Natts_pg_aggregate; i++) { nulls[i] = ' '; values[i] = (Datum) NULL; } values[Anum_pg_aggregate_aggfnoid - 1] = ObjectIdGetDatum(procOid); values[Anum_pg_aggregate_aggtransfn - 1] = ObjectIdGetDatum(transfn); values[Anum_pg_aggregate_aggfinalfn - 1] = ObjectIdGetDatum(finalfn); values[Anum_pg_aggregate_aggtranstype - 1] = ObjectIdGetDatum(aggTransType); if (agginitval) values[Anum_pg_aggregate_agginitval - 1] = DirectFunctionCall1(textin, CStringGetDatum(agginitval)); else nulls[Anum_pg_aggregate_agginitval - 1] = 'n'; aggdesc = heap_openr(AggregateRelationName, RowExclusiveLock); tupDesc = aggdesc->rd_att; tup = heap_formtuple(tupDesc, values, nulls); simple_heap_insert(aggdesc, tup); CatalogUpdateIndexes(aggdesc, tup); heap_close(aggdesc, RowExclusiveLock); /* * Create dependencies for the aggregate (above and beyond those * already made by ProcedureCreate). Note: we don't need an explicit * dependency on aggTransType since we depend on it indirectly through * transfn. */ myself.classId = RelOid_pg_proc; myself.objectId = procOid; myself.objectSubId = 0; /* Depends on transition function */ referenced.classId = RelOid_pg_proc; referenced.objectId = transfn; referenced.objectSubId = 0; recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); /* Depends on final function, if any */ if (OidIsValid(finalfn)) { referenced.classId = RelOid_pg_proc; referenced.objectId = finalfn; referenced.objectSubId = 0; recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); } }
static SV * plperl_trigger_build_args(FunctionCallInfo fcinfo) { TriggerData *tdata; TupleDesc tupdesc; int i; char *level; char *event; char *relid; char *when; HV *hv; hv = newHV(); tdata = (TriggerData *) fcinfo->context; tupdesc = tdata->tg_relation->rd_att; relid = DatumGetCString( DirectFunctionCall1(oidout, ObjectIdGetDatum(tdata->tg_relation->rd_id) ) ); hv_store(hv, "name", 4, newSVpv(tdata->tg_trigger->tgname, 0), 0); hv_store(hv, "relid", 5, newSVpv(relid, 0), 0); if (TRIGGER_FIRED_BY_INSERT(tdata->tg_event)) { event = "INSERT"; if (TRIGGER_FIRED_FOR_ROW(tdata->tg_event)) hv_store(hv, "new", 3, plperl_hash_from_tuple(tdata->tg_trigtuple, tupdesc), 0); } else if (TRIGGER_FIRED_BY_DELETE(tdata->tg_event)) { event = "DELETE"; if (TRIGGER_FIRED_FOR_ROW(tdata->tg_event)) hv_store(hv, "old", 3, plperl_hash_from_tuple(tdata->tg_trigtuple, tupdesc), 0); } else if (TRIGGER_FIRED_BY_UPDATE(tdata->tg_event)) { event = "UPDATE"; if (TRIGGER_FIRED_FOR_ROW(tdata->tg_event)) { hv_store(hv, "old", 3, plperl_hash_from_tuple(tdata->tg_trigtuple, tupdesc), 0); hv_store(hv, "new", 3, plperl_hash_from_tuple(tdata->tg_newtuple, tupdesc), 0); } } else event = "UNKNOWN"; hv_store(hv, "event", 5, newSVpv(event, 0), 0); hv_store(hv, "argc", 4, newSViv(tdata->tg_trigger->tgnargs), 0); if (tdata->tg_trigger->tgnargs > 0) { AV *av = newAV(); for (i = 0; i < tdata->tg_trigger->tgnargs; i++) av_push(av, newSVpv(tdata->tg_trigger->tgargs[i], 0)); hv_store(hv, "args", 4, newRV_noinc((SV *) av), 0); } hv_store(hv, "relname", 7, newSVpv(SPI_getrelname(tdata->tg_relation), 0), 0); if (TRIGGER_FIRED_BEFORE(tdata->tg_event)) when = "BEFORE"; else if (TRIGGER_FIRED_AFTER(tdata->tg_event)) when = "AFTER"; else when = "UNKNOWN"; hv_store(hv, "when", 4, newSVpv(when, 0), 0); if (TRIGGER_FIRED_FOR_ROW(tdata->tg_event)) level = "ROW"; else if (TRIGGER_FIRED_FOR_STATEMENT(tdata->tg_event)) level = "STATEMENT"; else level = "UNKNOWN"; hv_store(hv, "level", 5, newSVpv(level, 0), 0); return newRV_noinc((SV *) hv); }
static plperl_proc_desc * compile_plperl_function(Oid fn_oid, bool is_trigger) { HeapTuple procTup; Form_pg_proc procStruct; char internal_proname[64]; int proname_len; plperl_proc_desc *prodesc = NULL; int i; SV **svp; /* We'll need the pg_proc tuple in any case... */ procTup = SearchSysCache(PROCOID, ObjectIdGetDatum(fn_oid), 0, 0, 0); if (!HeapTupleIsValid(procTup)) elog(ERROR, "cache lookup failed for function %u", fn_oid); procStruct = (Form_pg_proc) GETSTRUCT(procTup); /************************************************************ * Build our internal proc name from the functions Oid ************************************************************/ if (!is_trigger) sprintf(internal_proname, "__PLPerl_proc_%u", fn_oid); else sprintf(internal_proname, "__PLPerl_proc_%u_trigger", fn_oid); proname_len = strlen(internal_proname); /************************************************************ * Lookup the internal proc name in the hashtable ************************************************************/ svp = hv_fetch(plperl_proc_hash, internal_proname, proname_len, FALSE); if (svp) { bool uptodate; prodesc = (plperl_proc_desc *) SvIV(*svp); /************************************************************ * If it's present, must check whether it's still up to date. * This is needed because CREATE OR REPLACE FUNCTION can modify the * function's pg_proc entry without changing its OID. ************************************************************/ uptodate = (prodesc->fn_xmin == HeapTupleHeaderGetXmin(procTup->t_data) && prodesc->fn_cmin == HeapTupleHeaderGetCmin(procTup->t_data)); if (!uptodate) { /* need we delete old entry? */ prodesc = NULL; } } /************************************************************ * If we haven't found it in the hashtable, we analyze * the functions arguments and returntype and store * the in-/out-functions in the prodesc block and create * a new hashtable entry for it. * * Then we load the procedure into the Perl interpreter. ************************************************************/ if (prodesc == NULL) { HeapTuple langTup; HeapTuple typeTup; Form_pg_language langStruct; Form_pg_type typeStruct; Datum prosrcdatum; bool isnull; char *proc_source; /************************************************************ * Allocate a new procedure description block ************************************************************/ prodesc = (plperl_proc_desc *) malloc(sizeof(plperl_proc_desc)); if (prodesc == NULL) ereport(ERROR, (errcode(ERRCODE_OUT_OF_MEMORY), errmsg("out of memory"))); MemSet(prodesc, 0, sizeof(plperl_proc_desc)); prodesc->proname = strdup(internal_proname); prodesc->fn_xmin = HeapTupleHeaderGetXmin(procTup->t_data); prodesc->fn_cmin = HeapTupleHeaderGetCmin(procTup->t_data); /* Remember if function is STABLE/IMMUTABLE */ prodesc->fn_readonly = (procStruct->provolatile != PROVOLATILE_VOLATILE); /************************************************************ * Lookup the pg_language tuple by Oid ************************************************************/ langTup = SearchSysCache(LANGOID, ObjectIdGetDatum(procStruct->prolang), 0, 0, 0); if (!HeapTupleIsValid(langTup)) { free(prodesc->proname); free(prodesc); elog(ERROR, "cache lookup failed for language %u", procStruct->prolang); } langStruct = (Form_pg_language) GETSTRUCT(langTup); prodesc->lanpltrusted = langStruct->lanpltrusted; ReleaseSysCache(langTup); /************************************************************ * Get the required information for input conversion of the * return value. ************************************************************/ if (!is_trigger) { typeTup = SearchSysCache(TYPEOID, ObjectIdGetDatum(procStruct->prorettype), 0, 0, 0); if (!HeapTupleIsValid(typeTup)) { free(prodesc->proname); free(prodesc); elog(ERROR, "cache lookup failed for type %u", procStruct->prorettype); } typeStruct = (Form_pg_type) GETSTRUCT(typeTup); /* Disallow pseudotype result, except VOID or RECORD */ if (typeStruct->typtype == 'p') { if (procStruct->prorettype == VOIDOID || procStruct->prorettype == RECORDOID) /* okay */ ; else if (procStruct->prorettype == TRIGGEROID) { free(prodesc->proname); free(prodesc); ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("trigger functions may only be called " "as triggers"))); } else { free(prodesc->proname); free(prodesc); ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("plperl functions cannot return type %s", format_type_be(procStruct->prorettype)))); } } prodesc->result_oid = procStruct->prorettype; prodesc->fn_retisset = procStruct->proretset; prodesc->fn_retistuple = (typeStruct->typtype == 'c' || procStruct->prorettype == RECORDOID); prodesc->fn_retisarray = (typeStruct->typlen == -1 && typeStruct->typelem); perm_fmgr_info(typeStruct->typinput, &(prodesc->result_in_func)); prodesc->result_typioparam = getTypeIOParam(typeTup); ReleaseSysCache(typeTup); } /************************************************************ * Get the required information for output conversion * of all procedure arguments ************************************************************/ if (!is_trigger) { prodesc->nargs = procStruct->pronargs; for (i = 0; i < prodesc->nargs; i++) { typeTup = SearchSysCache(TYPEOID, ObjectIdGetDatum(procStruct->proargtypes.values[i]), 0, 0, 0); if (!HeapTupleIsValid(typeTup)) { free(prodesc->proname); free(prodesc); elog(ERROR, "cache lookup failed for type %u", procStruct->proargtypes.values[i]); } typeStruct = (Form_pg_type) GETSTRUCT(typeTup); /* Disallow pseudotype argument */ if (typeStruct->typtype == 'p') { free(prodesc->proname); free(prodesc); ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("plperl functions cannot take type %s", format_type_be(procStruct->proargtypes.values[i])))); } if (typeStruct->typtype == 'c') prodesc->arg_is_rowtype[i] = true; else { prodesc->arg_is_rowtype[i] = false; perm_fmgr_info(typeStruct->typoutput, &(prodesc->arg_out_func[i])); } ReleaseSysCache(typeTup); } } /************************************************************ * create the text of the anonymous subroutine. * we do not use a named subroutine so that we can call directly * through the reference. ************************************************************/ prosrcdatum = SysCacheGetAttr(PROCOID, procTup, Anum_pg_proc_prosrc, &isnull); if (isnull) elog(ERROR, "null prosrc"); proc_source = DatumGetCString(DirectFunctionCall1(textout, prosrcdatum)); /************************************************************ * Create the procedure in the interpreter ************************************************************/ prodesc->reference = plperl_create_sub(proc_source, prodesc->lanpltrusted); pfree(proc_source); if (!prodesc->reference) /* can this happen? */ { free(prodesc->proname); free(prodesc); elog(ERROR, "could not create internal procedure \"%s\"", internal_proname); } hv_store(plperl_proc_hash, internal_proname, proname_len, newSViv((IV) prodesc), 0); } ReleaseSysCache(procTup); return prodesc; }