/* * Find 'findelem' within array 'v'. Returns -1 if not found in an array * without any NULL elements, NULL if not found but one or more NULLs appeared * in the array. * * Declared STRICT, must not be called with NULL input(s). */ Datum array_idx(PG_FUNCTION_ARGS) { Datum findelem = PG_GETARG_DATUM(0); ArrayType *v = PG_GETARG_ARRAYTYPE_P(1); Oid arg0_typeid = get_fn_expr_argtype(fcinfo->flinfo, 0); Oid arg1_typeid = get_fn_expr_argtype(fcinfo->flinfo, 1); Oid arg0_elemid; Oid arg1_elemid; Oid element_type; int16 typlen; bool typbyval; char typalign; Datum *elems; bool *nulls; int nelems; Datum cur_elem; bool has_nulls; int i; if (ARR_NDIM(v) == 0) PG_RETURN_NULL(); else if (ARR_NDIM(v) != 1) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("idx(...) only makes sense for one-dimensional arrays, not %i", ARR_NDIM(v)))); element_type = ARR_ELEMTYPE(v); /* For optimal performance we'd get this from a syscache or cache it in the function * call context; see array_push in src/backend/utils/adt/array_userfuncs.c */ get_typlenbyvalalign(element_type, &typlen, &typbyval, &typalign); deconstruct_array(v, element_type, typlen, typbyval, typalign, &elems, &nulls, &nelems); has_nulls = false; for (i = 0; i < nelems; i++) { has_nulls |= nulls[i]; cur_elem = elems[i]; /* Compare find_elem for equality against cur_elem */ PG_RETURN_INT(i+1); } if (has_nulls) PG_RETURN_NULL(); else PG_RETURN_INT(-1); }
/** * @brief Internal function for determining the type of a function argument * * @param inID Number of function argument * @param[out] outTypeID PostgreSQL OID of the function argument's type * @param[out] outIsMutable True if the data structure of this function argument * can be safely modified. For objects passed by reference (like arrays) * this is only true when passed as the first argument of a transition * function. * * @internal * Having this as separate function isolates the PG_TRY block. Otherwise, * the compiler might warn that the longjmp could clobber local variables. */ inline void AbstractionLayer::AnyType::backendGetTypeIDForFunctionArg(uint16_t inID, Oid &outTypeID, bool &outIsMutable) const { madlib_assert(mContent == FunctionComposite, std::logic_error( "Inconsistency detected while converting from PostgreSQL to C++ types.")); bool exceptionOccurred = false; PG_TRY(); { outTypeID = get_fn_expr_argtype(fcinfo->flinfo, inID); // If we are called as an aggregate function, the first argument is the // transition state. In that case, we are free to modify the data. // In fact, for performance reasons, we *should* even do all modifications // in-place. In all other cases, directly modifying memory is dangerous. // See warning at: // http://www.postgresql.org/docs/current/static/xfunc-c.html#XFUNC-C-BASETYPE outIsMutable = (inID == 0 && AggCheckCallContext(fcinfo, NULL)); } PG_CATCH(); { exceptionOccurred = true; } PG_END_TRY(); if (exceptionOccurred) throw PGException(); }
/* * ARRAY_AGG aggregate function */ Datum array_agg_transfn(PG_FUNCTION_ARGS) { Oid arg1_typeid = get_fn_expr_argtype(fcinfo->flinfo, 1); MemoryContext aggcontext; ArrayBuildState *state; Datum elem; if (arg1_typeid == InvalidOid) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("could not determine input data type"))); if (!AggCheckCallContext(fcinfo, &aggcontext)) { /* cannot be called directly because of internal-type argument */ elog(ERROR, "array_agg_transfn called in non-aggregate context"); } state = PG_ARGISNULL(0) ? NULL : (ArrayBuildState *) PG_GETARG_POINTER(0); elem = PG_ARGISNULL(1) ? (Datum) 0 : PG_GETARG_DATUM(1); state = accumArrayResult(state, elem, PG_ARGISNULL(1), arg1_typeid, aggcontext); /* * The transition type for array_agg() is declared to be "internal", which * is a pass-by-value type the same size as a pointer. So we can safely * pass the ArrayBuildState pointer through nodeAgg.c's machinations. */ PG_RETURN_POINTER(state); }
/* * ARRAY_AGG aggregate function */ datum_t array_agg_transfn(PG_FUNC_ARGS) { oid_t arg1_typeid = get_fn_expr_argtype(fcinfo->flinfo, 1); struct mctx * aggcontext; array_build_s *state; datum_t elem; if (arg1_typeid == INVALID_OID) ereport(ERROR, ( errcode(E_INVALID_PARAMETER_VALUE), errmsg("could not determine input data type"))); if (!AggCheckCallContext(fcinfo, &aggcontext)) { /* cannot be called directly because of internal-type argument */ elog(ERROR, "array_agg_transfn called in non-aggregate context"); } state = PG_ARG_ISNULL(0) ? NULL : (array_build_s *) ARG_POINTER(0); elem = PG_ARG_ISNULL(1) ? (datum_t) 0 : ARG_DATUM(1); state = accum_array_result(state, elem, PG_ARG_ISNULL(1), arg1_typeid, aggcontext); /* * The transition type for array_agg() is declared to be "internal", which * is a pass-by-value type the same size as a pointer. So we can safely * pass the array_build_s pointer through nodeAgg.c's machinations. */ RET_POINTER(state); }
static bool assert_equals_base(FunctionCallInfo fcinfo) { Datum value1 = PG_GETARG_DATUM(0); Datum value2 = PG_GETARG_DATUM(1); Oid *ptr; ptr = (Oid *) fcinfo->flinfo->fn_extra; if (ptr == NULL) { Oid valtype = get_fn_expr_argtype(fcinfo->flinfo, 0); Oid eqopfcid; if (!OidIsValid(valtype)) elog(ERROR, "could not determine data type of input"); eqopfcid = equality_oper_funcid(valtype); if (!OidIsValid(eqopfcid)) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("unknown equal operand for datatype"))); /* First time calling for current query: allocate storage */ fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt, sizeof(Oid)); ptr = (Oid *) fcinfo->flinfo->fn_extra; *ptr = eqopfcid; } return DatumGetBool(OidFunctionCall2(*ptr, value1, value2)); }
/* * Returns whether the Bloom filter contains the item or not */ Datum bloom_contains(PG_FUNCTION_ARGS) { BloomFilter *bloom; Datum elem = PG_GETARG_DATUM(1); bool contains = false; Oid val_type = get_fn_expr_argtype(fcinfo->flinfo, 1); TypeCacheEntry *typ; StringInfo buf; if (val_type == InvalidOid) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("could not determine input data type"))); if (PG_ARGISNULL(0)) PG_RETURN_BOOL(contains); bloom = (BloomFilter *) PG_GETARG_VARLENA_P(0); typ = lookup_type_cache(val_type, 0); buf = makeStringInfo(); DatumToBytes(elem, typ, buf); contains = BloomFilterContains(bloom, buf->data, buf->len); pfree(buf->data); pfree(buf); PG_RETURN_BOOL(contains); }
Datum variant_cast_in(PG_FUNCTION_ARGS) { VariantInt vi = palloc0(sizeof(*vi)); vi->isnull = PG_ARGISNULL(0); vi->typid = get_fn_expr_argtype(fcinfo->flinfo, 0); vi->typmod = get_fn_expr_argtypmod(fcinfo->flinfo, 0); Assert(!fcinfo->flinfo->fn_strict); /* Must be callable on NULL input */ if (!OidIsValid(vi->typid)) elog(ERROR, "could not determine data type of input"); /* Validate that we're casting to a registered variant */ if( PG_ARGISNULL(1) ) elog( ERROR, "Target typemod must not be NULL" ); variant_get_variant_name(PG_GETARG_INT32(1), vi->typid, false); if( !vi->isnull ) vi->data = PG_GETARG_DATUM(0); /* Since we're casting in, we'll call for INFunc_input, even though we don't need it */ PG_RETURN_VARIANT( make_variant(vi, fcinfo, IOFunc_input) ); }
static BloomFilter * bloom_operation(FunctionCallInfo fcinfo, bool intersection) { Oid bf_type = LookupTypeNameOid(NULL, SystemTypeName("bloom"), false); BloomFilter *result = NULL; int i; for (i = 0; i < PG_NARGS(); i++) { BloomFilter *bf; if (PG_ARGISNULL(i)) continue; if (get_fn_expr_argtype(fcinfo->flinfo, i) != bf_type) elog(ERROR, "argument %d is not of type \"bloom\"", i + 1); bf = (BloomFilter *) PG_GETARG_VARLENA_P(i); if (result) { if (bf->m != result->m) elog(ERROR, "bloom filters must have the same p"); else if (bf->k != result->k) elog(ERROR, "bloom filters must have the same n"); } if (result == NULL) result = bf; else if (intersection) result = BloomFilterIntersection(result, bf); else result = BloomFilterUnion(result, bf); } return result; }
/* * worker_hash returns the hashed value of the given value. */ Datum worker_hash(PG_FUNCTION_ARGS) { Datum valueDatum = PG_GETARG_DATUM(0); Datum hashedValueDatum = 0; TypeCacheEntry *typeEntry = NULL; FmgrInfo *hashFunction = NULL; Oid valueDataType = InvalidOid; /* figure out hash function from the data type */ valueDataType = get_fn_expr_argtype(fcinfo->flinfo, 0); typeEntry = lookup_type_cache(valueDataType, TYPECACHE_HASH_PROC_FINFO); if (typeEntry->hash_proc_finfo.fn_oid == InvalidOid) { ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("cannot find a hash function for the input type"), errhint("Cast input to a data type with a hash function."))); } hashFunction = palloc0(sizeof(FmgrInfo)); fmgr_info_copy(hashFunction, &(typeEntry->hash_proc_finfo), CurrentMemoryContext); /* calculate hash value */ hashedValueDatum = FunctionCall1(hashFunction, valueDatum); PG_RETURN_INT32(hashedValueDatum); }
Datum enum_last(PG_FUNCTION_ARGS) { Oid enumtypoid; Oid max; /* * We rely on being able to get the specific enum type from the calling * expression tree. Notice that the actual value of the argument isn't * examined at all; in particular it might be NULL. */ enumtypoid = get_fn_expr_argtype(fcinfo->flinfo, 0); if (enumtypoid == InvalidOid) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("could not determine actual enum type"))); /* Get the OID using the index */ max = enum_endpoint(enumtypoid, BackwardScanDirection); if (!OidIsValid(max)) ereport(ERROR, (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), errmsg("enum %s contains no values", format_type_be(enumtypoid)))); PG_RETURN_OID(max); }
/* * Returns the estimate normalized frequency of the item */ Datum cmsketch_norm_frequency(PG_FUNCTION_ARGS) { CountMinSketch *cms; Datum elem = PG_GETARG_DATUM(1); float8 freq = 0; Oid val_type = get_fn_expr_argtype(fcinfo->flinfo, 1); TypeCacheEntry *typ; StringInfo buf; if (val_type == InvalidOid) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("could not determine input data type"))); if (PG_ARGISNULL(0)) PG_RETURN_FLOAT8(freq); cms = (CountMinSketch *) PG_GETARG_VARLENA_P(0); typ = lookup_type_cache(val_type, 0); buf = makeStringInfo(); DatumToBytes(elem, typ, buf); freq = CountMinSketchEstimateNormFrequency(cms, buf->data, buf->len); pfree(buf->data); pfree(buf); PG_RETURN_FLOAT8(freq); }
/* 2-argument variant of enum_range */ Datum enum_range_bounds(PG_FUNCTION_ARGS) { Oid lower; Oid upper; Oid enumtypoid; if (PG_ARGISNULL(0)) lower = InvalidOid; else lower = PG_GETARG_OID(0); if (PG_ARGISNULL(1)) upper = InvalidOid; else upper = PG_GETARG_OID(1); /* * We rely on being able to get the specific enum type from the calling * expression tree. The generic type mechanism should have ensured that * both are of the same type. */ enumtypoid = get_fn_expr_argtype(fcinfo->flinfo, 0); if (enumtypoid == InvalidOid) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("could not determine actual enum type"))); PG_RETURN_ARRAYTYPE_P(enum_range_internal(enumtypoid, lower, upper)); }
/* * cms_topn_frequency is a user-facing UDF which returns the estimated frequency * of an item. The first parameter is for CmsTopn and second is for the item to * return the frequency. */ Datum cms_topn_frequency(PG_FUNCTION_ARGS) { CmsTopn *cmsTopn = (CmsTopn *) PG_GETARG_VARLENA_P(0); ArrayType *topnArray = TopnArray(cmsTopn); Datum item = PG_GETARG_DATUM(1); Oid itemType = get_fn_expr_argtype(fcinfo->flinfo, 1); TypeCacheEntry *itemTypeCacheEntry = NULL; Frequency frequency = 0; if (itemType == InvalidOid) { ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("could not determine input data types"))); } if (topnArray != NULL && itemType != ARR_ELEMTYPE(topnArray)) { ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("Not proper type for this cms_topn"))); } itemTypeCacheEntry = lookup_type_cache(itemType, 0); frequency = CmsTopnEstimateItemFrequency(cmsTopn, item, itemTypeCacheEntry); PG_RETURN_INT32(frequency); }
/* * Returns the estimate count of the item */ Datum cmsketch_count(PG_FUNCTION_ARGS) { CountMinSketch *cms; Datum elem = PG_GETARG_DATUM(1); uint32_t count = false; Oid val_type = get_fn_expr_argtype(fcinfo->flinfo, 1); TypeCacheEntry *typ; Size size; if (val_type == InvalidOid) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("could not determine input data type"))); if (PG_ARGISNULL(0)) PG_RETURN_INT32(count); cms = (CountMinSketch *) PG_GETARG_VARLENA_P(0); typ = lookup_type_cache(val_type, 0); size = datumGetSize(elem, typ->typbyval, typ->typlen); if (typ->typbyval) count = CountMinSketchEstimateCount(cms, (char *) &elem, size); else count = CountMinSketchEstimateCount(cms, DatumGetPointer(elem), size); PG_RETURN_INT32(count); }
Datum Function_invoke(Function self, PG_FUNCTION_ARGS) { Datum retVal; int32 top; jvalue* args; Type invokerType; fcinfo->isnull = false; currentInvocation->function = self; if(self->isUDT) return self->func.udt.udtFunction(self->func.udt.udt, fcinfo); if(self->func.nonudt.isMultiCall && SRF_IS_FIRSTCALL()) Invocation_assertDisconnect(); top = self->func.nonudt.numParams; /* Leave room for one extra parameter. Functions that returns unmapped * composite types must have a single row ResultSet as an OUT parameter. */ args = (jvalue*)palloc((top + 1) * sizeof(jvalue)); invokerType = self->func.nonudt.returnType; if(top > 0) { int32 idx; Type* types = self->func.nonudt.paramTypes; /* a class loader or other mechanism might have connected already. This * connection must be dropped since its parent context is wrong. */ if(Type_isDynamic(invokerType)) invokerType = Type_getRealType(invokerType, get_fn_expr_rettype(fcinfo->flinfo), self->func.nonudt.typeMap); for(idx = 0; idx < top; ++idx) { if(PG_ARGISNULL(idx)) /* * Set this argument to zero (or null in case of object) */ args[idx].j = 0L; else { Type paramType = types[idx]; if(Type_isDynamic(paramType)) paramType = Type_getRealType(paramType, get_fn_expr_argtype(fcinfo->flinfo, idx), self->func.nonudt.typeMap); args[idx] = Type_coerceDatum(paramType, PG_GETARG_DATUM(idx)); } } } retVal = self->func.nonudt.isMultiCall ? Type_invokeSRF(invokerType, self->clazz, self->func.nonudt.method, args, fcinfo) : Type_invoke(invokerType, self->clazz, self->func.nonudt.method, args, fcinfo); pfree(args); return retVal; }
Datum cmsketch_add(PG_FUNCTION_ARGS) { CountMinSketch *cms = (CountMinSketch *) PG_GETARG_VARLENA_P(0); fcinfo->flinfo->fn_extra = lookup_type_cache(get_fn_expr_argtype(fcinfo->flinfo, 1), 0); cms = cmsketch_add_datum(fcinfo, cms, PG_GETARG_DATUM(1)); PG_RETURN_POINTER(cms); }
Datum orafce_dump(PG_FUNCTION_ARGS) { Oid valtype = get_fn_expr_argtype(fcinfo->flinfo, 0); List *args; int16 typlen; bool typbyval; Size length; Datum value; int format; StringInfoData str; if (!fcinfo->flinfo || !fcinfo->flinfo->fn_expr) elog(ERROR, "function is called from invalid context"); if (PG_ARGISNULL(0)) elog(ERROR, "argument is NULL"); value = PG_GETARG_DATUM(0); format = PG_GETARG_IF_EXISTS(1, INT32, 10); args = ((FuncExpr *) fcinfo->flinfo->fn_expr)->args; valtype = exprType((Node *) list_nth(args, 0)); get_typlenbyval(valtype, &typlen, &typbyval); length = datumGetSize(value, typbyval, typlen); initStringInfo(&str); appendStringInfo(&str, "Typ=%d Len=%d: ", valtype, (int) length); if (!typbyval) { appendDatum(&str, DatumGetPointer(value), length, format); } else if (length <= 1) { char v = DatumGetChar(value); appendDatum(&str, &v, sizeof(char), format); } else if (length <= 2) { int16 v = DatumGetInt16(value); appendDatum(&str, &v, sizeof(int16), format); } else if (length <= 4) { int32 v = DatumGetInt32(value); appendDatum(&str, &v, sizeof(int32), format); } else { int64 v = DatumGetInt64(value); appendDatum(&str, &v, sizeof(int64), format); } PG_RETURN_TEXT_P(cstring_to_text(str.data)); }
/* * cms_topn_add_agg_with_parameters is a aggregate function to add items. It * allows to specify parameters of created CmsTopn structure. In addition to * cms_topn_add_agg function, it takes error bound and confidence interval * parameters as the forth and fifth parameters. */ Datum cms_topn_add_agg_with_parameters(PG_FUNCTION_ARGS) { CmsTopn *currentCmsTopn = NULL; CmsTopn *updatedCmsTopn = NULL; uint32 topnItemCount = PG_GETARG_UINT32(2); float8 errorBound = PG_GETARG_FLOAT8(3); float8 confidenceInterval = PG_GETARG_FLOAT8(4); Datum newItem = 0; TypeCacheEntry *newItemTypeCacheEntry = NULL; Oid newItemType = InvalidOid; if (!AggCheckCallContext(fcinfo, NULL)) { ereport(ERROR, (errcode(ERRCODE_DATA_EXCEPTION), errmsg("cms_topn_add_agg_with_parameters called in " "non-aggregate context"))); } /* check whether cms_topn is null and create if it is */ if (PG_ARGISNULL(0)) { currentCmsTopn = CreateCmsTopn(topnItemCount, errorBound, confidenceInterval); } else { currentCmsTopn = (CmsTopn *) PG_GETARG_VARLENA_P(0); } /* if new item is null, return current CmsTopn */ if (PG_ARGISNULL(1)) { PG_RETURN_POINTER(currentCmsTopn); } /* * Keep type cache entry between subsequent calls in order to get rid of * cache lookup overhead. */ newItem = PG_GETARG_DATUM(1); if (fcinfo->flinfo->fn_extra == NULL) { newItemType = get_fn_expr_argtype(fcinfo->flinfo, 1); newItemTypeCacheEntry = lookup_type_cache(newItemType, 0); fcinfo->flinfo->fn_extra = newItemTypeCacheEntry; } else { newItemTypeCacheEntry = fcinfo->flinfo->fn_extra; } updatedCmsTopn = UpdateCmsTopn(currentCmsTopn, newItem, newItemTypeCacheEntry); PG_RETURN_POINTER(updatedCmsTopn); }
Datum count_distinct_append(PG_FUNCTION_ARGS) { element_set_t *eset; /* info for anyelement */ Oid element_type = get_fn_expr_argtype(fcinfo->flinfo, 1); Datum element = PG_GETARG_DATUM(1); /* memory contexts */ MemoryContext oldcontext; MemoryContext aggcontext; /* * If the new value is NULL, we simply return the current aggregate state * (it might be NULL, so check it). */ if (PG_ARGISNULL(1) && PG_ARGISNULL(0)) PG_RETURN_NULL(); else if (PG_ARGISNULL(1)) PG_RETURN_DATUM(PG_GETARG_DATUM(0)); /* from now on we know the new value is not NULL */ /* switch to the per-group hash-table memory context */ GET_AGG_CONTEXT("count_distinct_append", fcinfo, aggcontext); oldcontext = MemoryContextSwitchTo(aggcontext); /* init the hash table, if needed */ if (PG_ARGISNULL(0)) { int16 typlen; bool typbyval; char typalign; /* get type information for the second parameter (anyelement item) */ get_typlenbyvalalign(element_type, &typlen, &typbyval, &typalign); /* we can't handle varlena types yet or values passed by reference */ if ((typlen < 0) || (! typbyval)) elog(ERROR, "count_distinct handles only fixed-length types passed by value"); eset = init_set(typlen, typalign, aggcontext); } else eset = (element_set_t *)PG_GETARG_POINTER(0); /* add the value into the set */ add_element(eset, (char*)&element); MemoryContextSwitchTo(oldcontext); PG_RETURN_POINTER(eset); }
Datum pgis_geometry_accum_transfn(PG_FUNCTION_ARGS) { Oid arg1_typeid = get_fn_expr_argtype(fcinfo->flinfo, 1); MemoryContext aggcontext; ArrayBuildState *state; pgis_abs *p; Datum elem; if (arg1_typeid == InvalidOid) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("could not determine input data type"))); if (fcinfo->context && IsA(fcinfo->context, AggState)) aggcontext = ((AggState *) fcinfo->context)->aggcontext; #if POSTGIS_PGSQL_VERSION == 84 else if (fcinfo->context && IsA(fcinfo->context, WindowAggState)) aggcontext = ((WindowAggState *) fcinfo->context)->wincontext; #endif #if POSTGIS_PGSQL_VERSION > 84 else if (fcinfo->context && IsA(fcinfo->context, WindowAggState)) aggcontext = ((WindowAggState *) fcinfo->context)->aggcontext; #endif else { /* cannot be called directly because of dummy-type argument */ elog(ERROR, "array_agg_transfn called in non-aggregate context"); aggcontext = NULL; /* keep compiler quiet */ } if ( PG_ARGISNULL(0) ) { p = (pgis_abs*) palloc(sizeof(pgis_abs)); p->a = NULL; } else { p = (pgis_abs*) PG_GETARG_POINTER(0); } state = p->a; elem = PG_ARGISNULL(1) ? (Datum) 0 : PG_GETARG_DATUM(1); state = accumArrayResult(state, elem, PG_ARGISNULL(1), arg1_typeid, aggcontext); p->a = state; PG_RETURN_POINTER(p); }
/* * SQL function to_json(anyvalue) */ Datum to_json(PG_FUNCTION_ARGS) { Datum val = PG_GETARG_DATUM(0); Oid val_type = get_fn_expr_argtype(fcinfo->flinfo, 0); StringInfo result; TYPCATEGORY tcategory; Oid typoutput; bool typisvarlena; Oid castfunc = InvalidOid; if (val_type == InvalidOid) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("could not determine input data type"))); result = makeStringInfo(); getTypeOutputInfo(val_type, &typoutput, &typisvarlena); if (val_type > FirstNormalObjectId) { HeapTuple tuple; Form_pg_cast castForm; tuple = SearchSysCache2(CASTSOURCETARGET, ObjectIdGetDatum(val_type), ObjectIdGetDatum(JSONOID)); if (HeapTupleIsValid(tuple)) { castForm = (Form_pg_cast) GETSTRUCT(tuple); if (castForm->castmethod == COERCION_METHOD_FUNCTION) castfunc = typoutput = castForm->castfunc; ReleaseSysCache(tuple); } } if (castfunc != InvalidOid) tcategory = TYPCATEGORY_JSON_CAST; else if (val_type == RECORDARRAYOID) tcategory = TYPCATEGORY_ARRAY; else if (val_type == RECORDOID) tcategory = TYPCATEGORY_COMPOSITE; else if (val_type == JSONOID) tcategory = TYPCATEGORY_JSON; else tcategory = TypeCategory(val_type); datum_to_json(val, false, result, tcategory, typoutput); PG_RETURN_TEXT_P(cstring_to_text(result->data)); }
Datum int8_add(PG_FUNCTION_ARGS) { int64 state1; int64 state2; /* * GUARD against an incorrectly defined SQL function by verifying * that the parameters are the types we are expecting: * int8_add(int64, int64) => int64 */ if (PG_NARGS() != 2) { elog(ERROR, "%s defined with %d arguments, expected 2", add_str, PG_NARGS() ); } if (get_fn_expr_argtype(fcinfo->flinfo, 0) != INT8OID || get_fn_expr_argtype(fcinfo->flinfo, 1) != INT8OID) { elog(ERROR, "%s defined with invalid types, expected (int8, int8)", add_str ); } if (get_fn_expr_rettype(fcinfo->flinfo) != INT8OID) { elog(ERROR, "%s defined with invalid return type, expected int8", add_str ); } /* * GUARD against NULL input: * - IF both are null return NULL * - otherwise treat NULL as a zero value */ if (PG_ARGISNULL(0) && PG_ARGISNULL(1)) PG_RETURN_NULL(); state1 = PG_ARGISNULL(0) ? 0 : PG_GETARG_INT64(0); state2 = PG_ARGISNULL(1) ? 0 : PG_GETARG_INT64(1); /* Do the math and return the result */ PG_RETURN_INT64(state1 + state2); }
/* * Checks if range overlaps with existing partitions. * Returns TRUE if overlaps and FALSE otherwise. */ Datum check_overlap(PG_FUNCTION_ARGS) { int parent_oid = DatumGetInt32(PG_GETARG_DATUM(0)); Datum p1 = PG_GETARG_DATUM(1); Oid p1_type = get_fn_expr_argtype(fcinfo->flinfo, 1); Datum p2 = PG_GETARG_DATUM(2); Oid p2_type = get_fn_expr_argtype(fcinfo->flinfo, 2); PartRelationInfo *prel; RangeRelation *rangerel; RangeEntry *ranges; FmgrInfo cmp_func_1; FmgrInfo cmp_func_2; int i; bool byVal; prel = get_pathman_relation_info(parent_oid, NULL); rangerel = get_pathman_range_relation(parent_oid, NULL); if (!prel || !rangerel || prel->parttype != PT_RANGE) PG_RETURN_NULL(); /* comparison functions */ cmp_func_1 = *get_cmp_func(p1_type, prel->atttype); cmp_func_2 = *get_cmp_func(p2_type, prel->atttype); byVal = rangerel->by_val; ranges = (RangeEntry *) dsm_array_get_pointer(&rangerel->ranges); for (i=0; i<rangerel->ranges.length; i++) { int c1 = FunctionCall2(&cmp_func_1, p1, PATHMAN_GET_DATUM(ranges[i].max, byVal)); int c2 = FunctionCall2(&cmp_func_2, p2, PATHMAN_GET_DATUM(ranges[i].min, byVal)); if (c1 < 0 && c2 > 0) PG_RETURN_BOOL(true); } PG_RETURN_BOOL(false); }
/* * fetch_array_arg_replace_nulls * * Fetch an array-valued argument in expanded form; if it's null, construct an * empty array value of the proper data type. Also cache basic element type * information in fn_extra. * * Caution: if the input is a read/write pointer, this returns the input * argument; so callers must be sure that their changes are "safe", that is * they cannot leave the array in a corrupt state. * * If we're being called as an aggregate function, make sure any newly-made * expanded array is allocated in the aggregate state context, so as to save * copying operations. */ static ExpandedArrayHeader * fetch_array_arg_replace_nulls(FunctionCallInfo fcinfo, int argno) { ExpandedArrayHeader *eah; Oid element_type; ArrayMetaState *my_extra; MemoryContext resultcxt; /* If first time through, create datatype cache struct */ my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra; if (my_extra == NULL) { my_extra = (ArrayMetaState *) MemoryContextAlloc(fcinfo->flinfo->fn_mcxt, sizeof(ArrayMetaState)); my_extra->element_type = InvalidOid; fcinfo->flinfo->fn_extra = my_extra; } /* Figure out which context we want the result in */ if (!AggCheckCallContext(fcinfo, &resultcxt)) resultcxt = CurrentMemoryContext; /* Now collect the array value */ if (!PG_ARGISNULL(argno)) { MemoryContext oldcxt = MemoryContextSwitchTo(resultcxt); eah = PG_GETARG_EXPANDED_ARRAYX(argno, my_extra); MemoryContextSwitchTo(oldcxt); } else { /* We have to look up the array type and element type */ Oid arr_typeid = get_fn_expr_argtype(fcinfo->flinfo, argno); if (!OidIsValid(arr_typeid)) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("could not determine input data type"))); element_type = get_element_type(arr_typeid); if (!OidIsValid(element_type)) ereport(ERROR, (errcode(ERRCODE_DATATYPE_MISMATCH), errmsg("input data type is not an array"))); eah = construct_empty_expanded_array(element_type, resultcxt, my_extra); } return eah; }
/* * fetch_array_arg_replace_nulls * * Fetch an array-valued argument; if it's null, construct an empty array * value of the proper data type. Also cache basic element type information * in fn_extra. */ static ArrayType * fetch_array_arg_replace_nulls(FunctionCallInfo fcinfo, int argno) { ArrayType *v; Oid element_type; ArrayMetaState *my_extra; /* First collect the array value */ if (!PG_ARGISNULL(argno)) { v = PG_GETARG_ARRAYTYPE_P(argno); element_type = ARR_ELEMTYPE(v); } else { /* We have to look up the array type and element type */ Oid arr_typeid = get_fn_expr_argtype(fcinfo->flinfo, argno); if (!OidIsValid(arr_typeid)) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("could not determine input data type"))); element_type = get_element_type(arr_typeid); if (!OidIsValid(element_type)) ereport(ERROR, (errcode(ERRCODE_DATATYPE_MISMATCH), errmsg("input data type is not an array"))); v = construct_empty_array(element_type); } /* Now cache required info, which might change from call to call */ my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra; if (my_extra == NULL) { my_extra = (ArrayMetaState *) MemoryContextAlloc(fcinfo->flinfo->fn_mcxt, sizeof(ArrayMetaState)); my_extra->element_type = InvalidOid; fcinfo->flinfo->fn_extra = my_extra; } if (my_extra->element_type != element_type) { get_typlenbyvalalign(element_type, &my_extra->typlen, &my_extra->typbyval, &my_extra->typalign); my_extra->element_type = element_type; } return v; }
Datum rank_cd(PG_FUNCTION_ARGS) { ArrayType *win; tsvector *txt = (tsvector *) PG_DETOAST_DATUM(PG_GETARG_DATUM(1)); QUERYTYPE *query = (QUERYTYPE *) PG_DETOAST_DATUM_COPY(PG_GETARG_DATUM(2)); int method = DEF_NORM_METHOD; float4 res; /* * Pre-8.2, rank_cd took just a plain int as its first argument. * It was a mistake to keep the same C function name while changing the * signature, but it's too late to fix that. Instead, do a runtime test * to make sure the expected datatype has been passed. This is needed * to prevent core dumps if tsearch2 function definitions from an old * database are loaded into an 8.2 server. */ if (get_fn_expr_argtype(fcinfo->flinfo, 0) != FLOAT4ARRAYOID) ereport(ERROR, (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), errmsg("rank_cd() now takes real[] as its first argument, not integer"))); /* now safe to dereference the first arg */ win = (ArrayType *) PG_DETOAST_DATUM(PG_GETARG_DATUM(0)); if (ARR_NDIM(win) != 1) ereport(ERROR, (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR), errmsg("array of weight must be one-dimensional"))); if (ARRNELEMS(win) < lengthof(weights)) ereport(ERROR, (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR), errmsg("array of weight is too short"))); if (ARR_HASNULL(win)) ereport(ERROR, (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED), errmsg("array of weight must not contain nulls"))); if (PG_NARGS() == 4) method = PG_GETARG_INT32(3); res = calc_rank_cd((float4 *) ARR_DATA_PTR(win), txt, query, method); PG_FREE_IF_COPY(win, 0); PG_FREE_IF_COPY(txt, 1); PG_FREE_IF_COPY(query, 2); PG_RETURN_FLOAT4(res); }
Datum array_agg_distinct_type_by_element(PG_FUNCTION_ARGS) { /* get element type for the dummy second parameter (anynonarray item) */ Oid element_type = get_fn_expr_argtype(fcinfo->flinfo, 1); CHECK_AGG_CONTEXT("count_distinct", fcinfo); /* return empty array if the state was not initialized */ if (PG_ARGISNULL(0)) PG_RETURN_DATUM(PointerGetDatum(construct_empty_array(element_type))); return build_array((element_set_t *)PG_GETARG_POINTER(0), element_type); }
Datum bloom_add(PG_FUNCTION_ARGS) { BloomFilter *bloom; if (PG_ARGISNULL(0)) bloom = BloomFilterCreate(); else bloom = (BloomFilter *) PG_GETARG_VARLENA_P(0); fcinfo->flinfo->fn_extra = lookup_type_cache(get_fn_expr_argtype(fcinfo->flinfo, 1), 0); bloom = bloom_add_datum(fcinfo, bloom, PG_GETARG_DATUM(1)); PG_RETURN_POINTER(bloom); }
Datum anyold_transfn(PG_FUNCTION_ARGS) { Oid type; Datum state; MemoryContext aggcontext, oldcontext; int16 typlen; bool typbyval; char typalign; if (!AggCheckCallContext(fcinfo, &aggcontext)) { /* cannot be called directly because of internal-type argument */ elog(ERROR, "anyold_transfn called in non-aggregate context"); } if (PG_ARGISNULL(0)) { if (PG_ARGISNULL(1)) PG_RETURN_NULL(); /* First non-null value --- initialize */ oldcontext = MemoryContextSwitchTo(aggcontext); type = get_fn_expr_argtype(fcinfo->flinfo, 1); if (type == InvalidOid) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("could not determine input data type"))); get_typlenbyvalalign(type, &typlen, &typbyval, &typalign); /* Copy initial value */ if (typlen == -1) state = PointerGetDatum(PG_DETOAST_DATUM_COPY(PG_GETARG_DATUM(1))); else state = datumCopy(PG_GETARG_DATUM(1), typbyval, typlen); MemoryContextSwitchTo(oldcontext); } else { state = PG_GETARG_DATUM(0); } PG_RETURN_DATUM(state); }
Datum adaptive_add_item_agg2(PG_FUNCTION_ARGS) { AdaptiveCounter acounter; /* info for anyelement */ Oid element_type = get_fn_expr_argtype(fcinfo->flinfo, 1); Datum element = PG_GETARG_DATUM(1); int16 typlen; bool typbyval; char typalign; /* is the counter created (if not, create it with default parameters) */ if (PG_ARGISNULL(0)) { acounter = ac_init(DEFAULT_ERROR, DEFAULT_NDISTINCT); } else { acounter = (AdaptiveCounter)PG_GETARG_BYTEA_P(0); } /* add the item to the estimator */ if (! PG_ARGISNULL(1)) { /* TODO The requests for type info shouldn't be a problem (thanks to lsyscache), * but if it turns out to have a noticeable impact it's possible to cache that * between the calls (in the estimator). */ /* get type information for the second parameter (anyelement item) */ get_typlenbyvalalign(element_type, &typlen, &typbyval, &typalign); /* it this a varlena type, passed by reference or by value ? */ if (typlen == -1) { /* varlena */ ac_add_item(acounter, VARDATA(element), VARSIZE(element) - VARHDRSZ); } else if (typbyval) { /* fixed-length, passed by value */ ac_add_item(acounter, (char*)&element, typlen); } else { /* fixed-length, passed by reference */ ac_add_item(acounter, (char*)element, typlen); } } /* return the updated bytea */ PG_RETURN_BYTEA_P(acounter); }