/** * A modified version of PostgreSQL's DirectFunctionCall1 which allows NULL results; this * is required for aggregates that return NULL. */ Datum PGISDirectFunctionCall1(PGFunction func, Datum arg1) { FunctionCallInfoData fcinfo; Datum result; #if POSTGIS_PGSQL_VERSION > 90 InitFunctionCallInfoData(fcinfo, NULL, 1, InvalidOid, NULL, NULL); #else InitFunctionCallInfoData(fcinfo, NULL, 1, NULL, NULL); #endif fcinfo.arg[0] = arg1; fcinfo.argnull[0] = false; result = (*func) (&fcinfo); /* Check for null result, returning a "NULL" Datum if indicated */ if (fcinfo.isnull) return (Datum) 0; return result; }
/* * int2vectorrecv - converts external binary format to int2vector */ Datum int2vectorrecv(PG_FUNCTION_ARGS) { StringInfo buf = (StringInfo) PG_GETARG_POINTER(0); FunctionCallInfoData locfcinfo; int2vector *result; /* * Normally one would call array_recv() using DirectFunctionCall3, but * that does not work since array_recv wants to cache some data using * fcinfo->flinfo->fn_extra. So we need to pass it our own flinfo * parameter. */ InitFunctionCallInfoData(locfcinfo, fcinfo->flinfo, 3, NULL, NULL); locfcinfo.arg[0] = PointerGetDatum(buf); locfcinfo.arg[1] = ObjectIdGetDatum(INT2OID); locfcinfo.arg[2] = Int32GetDatum(-1); locfcinfo.argnull[0] = false; locfcinfo.argnull[1] = false; locfcinfo.argnull[2] = false; result = (int2vector *) DatumGetPointer(array_recv(&locfcinfo)); Assert(!locfcinfo.isnull); /* sanity checks: int2vector must be 1-D, no nulls */ if (ARR_NDIM(result) != 1 || ARR_HASNULL(result) || ARR_ELEMTYPE(result) != INT2OID) ereport(ERROR, (errcode(ERRCODE_INVALID_BINARY_REPRESENTATION), errmsg("invalid int2vector data"))); PG_RETURN_POINTER(result); }
Datum plvsubst_string_string(PG_FUNCTION_ARGS) { Datum r; ArrayType *array; FunctionCallInfoData locfcinfo; init_c_subst(); if (PG_ARGISNULL(0) || PG_ARGISNULL(1)) PG_RETURN_NULL(); /* * I can't use DirectFunctionCall2 */ InitFunctionCallInfoData(locfcinfo, fcinfo->flinfo, 2, NULL, NULL); locfcinfo.arg[0] = PG_GETARG_DATUM(1); locfcinfo.arg[1] = PG_GETARG_IF_EXISTS(2, DATUM, CStringGetTextDatum(",")); locfcinfo.argnull[0] = false; locfcinfo.argnull[1] = false; r = text_to_array(&locfcinfo); if (locfcinfo.isnull || r == (Datum) 0) array = NULL; else array = DatumGetArrayTypeP(r); PG_RETURN_TEXT_P(plvsubst_string(PG_GETARG_TEXT_P(0), array, PG_GETARG_IF_EXISTS(3, TEXT_P, c_subst), fcinfo)); }
hdfsFS HdfsConnect(FsysName protocol, char * host, uint16_t port, char *ccname, void *token) { FunctionCallInfoData fcinfo; FileSystemUdfData fsysUdf; FmgrInfo *fsysFunc = FsysInterfaceGetFunc(protocol, FSYS_FUNC_CONNECT); #ifdef USE_ASSERT_CHECKING if (testmode_fault(gp_fsys_fault_inject_percent)) return NULL; #endif fsysUdf.type = T_FileSystemFunctionData; fsysUdf.fsys_host = host; fsysUdf.fsys_port = port; fsysUdf.fsys_hdfs = NULL; fsysUdf.fsys_ccname = ccname; fsysUdf.fsys_token = token; InitFunctionCallInfoData(/* FunctionCallInfoData */ fcinfo, /* FmgrInfo */ fsysFunc, /* nArgs */ 0, /* Call Context */ (Node *) (&fsysUdf), /* ResultSetInfo */ NULL); FunctionCallInvoke(&fcinfo); return FSYS_UDF_GET_HDFS(&fcinfo); }
hdfsFile HdfsOpenFile(FsysName protocol, hdfsFS fileSystem, char * path, int flags, int bufferSize, short replication, int64_t blocksize) { FunctionCallInfoData fcinfo; FileSystemUdfData fsysUdf; FmgrInfo *fsysFunc = FsysInterfaceGetFunc(protocol, FSYS_FUNC_OPEN); #ifdef USE_ASSERT_CHECKING if (testmode_fault(gp_fsys_fault_inject_percent)) return NULL; #endif fsysUdf.type = T_FileSystemFunctionData; fsysUdf.fsys_hdfs = fileSystem; fsysUdf.fsys_filepath = path; fsysUdf.fsys_fileflags = flags; fsysUdf.fsys_filebufsize = bufferSize; fsysUdf.fsys_replication = replication; fsysUdf.fsys_fileblksize = blocksize; fsysUdf.fsys_hfile = NULL; InitFunctionCallInfoData(/* FunctionCallInfoData */ fcinfo, /* FmgrInfo */ fsysFunc, /* nArgs */ 0, /* Call Context */ (Node *) (&fsysUdf), /* ResultSetInfo */ NULL); FunctionCallInvoke(&fcinfo); return FSYS_UDF_GET_HFILE(&fcinfo); }
int HdfsTruncate(FsysName protocol, hdfsFS fileSystem, char * path, int64_t size) { FunctionCallInfoData fcinfo; FileSystemUdfData fsysUdf; FmgrInfo *fsysFunc = FsysInterfaceGetFunc(protocol, FSYS_FUNC_TRUNCATE); #ifdef USE_ASSERT_CHECKING if (testmode_fault(gp_fsys_fault_inject_percent)) return -1; #endif fsysUdf.type = T_FileSystemFunctionData; fsysUdf.fsys_hdfs = fileSystem; fsysUdf.fsys_filepath = path; fsysUdf.fsys_pos = size; InitFunctionCallInfoData(/* FunctionCallInfoData */ fcinfo, /* FmgrInfo */ fsysFunc, /* nArgs */ 0, /* Call Context */ (Node *) (&fsysUdf), /* ResultSetInfo */ NULL); Datum d = FunctionCallInvoke(&fcinfo); return DatumGetInt32(d); }
int HdfsWrite(FsysName protocol, hdfsFS fileSystem, hdfsFile file, const void * buffer, int length) { FunctionCallInfoData fcinfo; FileSystemUdfData fsysUdf; FmgrInfo *fsysFunc = FsysInterfaceGetFunc(protocol, FSYS_FUNC_WRITE); #ifdef USE_ASSERT_CHECKING if (testmode_fault(gp_fsys_fault_inject_percent)) return -1; #endif fsysUdf.type = T_FileSystemFunctionData; fsysUdf.fsys_hdfs = fileSystem; fsysUdf.fsys_hfile = file; fsysUdf.fsys_databuf = (char *) buffer; fsysUdf.fsys_maxbytes = length; InitFunctionCallInfoData(/* FunctionCallInfoData */ fcinfo, /* FmgrInfo */ fsysFunc, /* nArgs */ 0, /* Call Context */ (Node *) (&fsysUdf), /* ResultSetInfo */ NULL); Datum d = FunctionCallInvoke(&fcinfo); return DatumGetInt32(d); }
int HdfsSeek(FsysName protocol, hdfsFS fileSystem, hdfsFile file, int64_t desiredPos) { FunctionCallInfoData fcinfo; FileSystemUdfData fsysUdf; FmgrInfo *fsysFunc = FsysInterfaceGetFunc(protocol, FSYS_FUNC_SEEK); #ifdef USE_ASSERT_CHECKING if (testmode_fault(gp_fsys_fault_inject_percent)) return -1; #endif fsysUdf.type = T_FileSystemFunctionData; fsysUdf.fsys_hdfs = fileSystem; fsysUdf.fsys_hfile = file; fsysUdf.fsys_pos = desiredPos; InitFunctionCallInfoData(/* FunctionCallInfoData */ fcinfo, /* FmgrInfo */ fsysFunc, /* nArgs */ 0, /* Call Context */ (Node *) (&fsysUdf), /* ResultSetInfo */ NULL); Datum d = FunctionCallInvoke(&fcinfo); return DatumGetInt32(d); }
static int pg_callable_func(lua_State *L) { MemoryContext m; int i; FunctionCallInfoData fcinfo; Lua_pgfunc *fi; fi = (Lua_pgfunc *) lua_touserdata(L, lua_upvalueindex(1)); InitFunctionCallInfoData(fcinfo, &fi->fi, fi->numargs, InvalidOid, NULL, NULL); if(tmpcontext_usage> RESET_CONTEXT_AFTER ){ MemoryContextReset(tmpcontext); tmpcontext_usage = 0; } ++tmpcontext_usage; m = MemoryContextSwitchTo(tmpcontext); for (i=0; i<fi->numargs; ++i){ fcinfo.arg[i] = luaP_todatum(L, fi->argtypes[i], 0, &fcinfo.argnull[i], i+1); } if(!fi->options.only_internal && fi->options.throwable){ SPI_push(); PG_TRY(); { Datum d = FunctionCallInvoke(&fcinfo); MemoryContextSwitchTo(m); if (fcinfo.isnull) { lua_pushnil(L); } else { luaP_pushdatum(L, d, fi->prorettype); } SPI_pop(); } PG_CATCH(); { lua_pop(L, lua_gettop(L)); push_spi_error(L, m); /*context switch to m inside push_spi_error*/ SPI_pop(); return lua_error(L); }PG_END_TRY(); }else{ Datum d = FunctionCallInvoke(&fcinfo); MemoryContextSwitchTo(m); if (fcinfo.isnull) { lua_pushnil(L); } else { luaP_pushdatum(L, d, fi->prorettype); } } return 1; }
static int pgfunc_rows (lua_State *L) { int i; Lua_pgfunc *fi; ReturnSetInfo *rsinfo; ExprContext *econtext; FunctionCallInfoData *fcinfo; Lua_pgfunc_srf *srfi; int argc; BEGINLUA; argc = lua_gettop(L); fi = (Lua_pgfunc *) lua_touserdata(L, lua_upvalueindex(1)); srfi = (Lua_pgfunc_srf *)lua_newuserdata(L, sizeof(Lua_pgfunc_srf)); econtext = &srfi->econtext; rsinfo = &srfi->rsinfo; fcinfo = &srfi->fcinfo; srfi->prorettype = fi->prorettype; fmgr_info(fi->funcid, &srfi->fi); memset(econtext, 0, sizeof(ExprContext)); econtext->ecxt_per_query_memory = CurrentMemoryContext; rsinfo->type = T_ReturnSetInfo; rsinfo->econtext = econtext; rsinfo->allowedModes = (int)(SFRM_ValuePerCall /*| SFRM_Materialize*/); rsinfo->returnMode = SFRM_ValuePerCall;//SFRM_Materialize; rsinfo->setResult = NULL; rsinfo->setDesc = NULL; InitFunctionCallInfoData((*fcinfo), &srfi->fi, fi->numargs, InvalidOid, NULL, (fmNodePtr)rsinfo); for (i=0; i<fi->numargs; ++i){ if(i>=argc){ for (i = argc; i<fi->numargs; ++i){ fcinfo->arg[i] = 0 ;//Datum; fcinfo->argnull[i] = true; } break; } fcinfo->arg[i] = luaP_todatum(L, fi->argtypes[i], 0, &fcinfo->argnull[i], i+1); } lua_pushcclosure(L, pgfunc_rowsaux, 1); ENDLUAV(1); return 1; }
static Jsonb * jnumber_op(PGFunction f, Jsonb *l, Jsonb *r) { FunctionCallInfoData fcinfo; JsonbValue *jv; Datum n; AssertArg(r != NULL); if (!((l == NULL || JB_ROOT_IS_SCALAR(l)) && JB_ROOT_IS_SCALAR(r))) ereport_op(f, l, r); InitFunctionCallInfoData(fcinfo, NULL, 0, InvalidOid, NULL, NULL); if (l != NULL) { jv = getIthJsonbValueFromContainer(&l->root, 0); if (jv->type != jbvNumeric) ereport_op(f, l, r); fcinfo.arg[fcinfo.nargs] = NumericGetDatum(jv->val.numeric); fcinfo.argnull[fcinfo.nargs] = false; fcinfo.nargs++; } jv = getIthJsonbValueFromContainer(&r->root, 0); if (jv->type != jbvNumeric) ereport_op(f, l, r); fcinfo.arg[fcinfo.nargs] = NumericGetDatum(jv->val.numeric); fcinfo.argnull[fcinfo.nargs] = false; fcinfo.nargs++; n = (*f) (&fcinfo); if (fcinfo.isnull) elog(ERROR, "function %p returned NULL", (void *) f); if (f == numeric_power || f == numeric_div) { int s; s = DatumGetInt32(DirectFunctionCall1(numeric_scale, fcinfo.arg[0])) + DatumGetInt32(DirectFunctionCall1(numeric_scale, fcinfo.arg[1])); if (s == 0) n = DirectFunctionCall2(numeric_trunc, n, 0); } return numeric_to_jnumber(DatumGetNumeric(n)); }
/* * int2vectorrecv - converts external binary format to int2vector */ Datum int2vectorrecv(PG_FUNCTION_ARGS) { LOCAL_FCINFO(locfcinfo, 3); StringInfo buf = (StringInfo) PG_GETARG_POINTER(0); int2vector *result; /* * Normally one would call array_recv() using DirectFunctionCall3, but * that does not work since array_recv wants to cache some data using * fcinfo->flinfo->fn_extra. So we need to pass it our own flinfo * parameter. */ InitFunctionCallInfoData(*locfcinfo, fcinfo->flinfo, 3, InvalidOid, NULL, NULL); locfcinfo->args[0].value = PointerGetDatum(buf); locfcinfo->args[0].isnull = false; locfcinfo->args[1].value = ObjectIdGetDatum(INT2OID); locfcinfo->args[1].isnull = false; locfcinfo->args[2].value = Int32GetDatum(-1); locfcinfo->args[2].isnull = false; result = (int2vector *) DatumGetPointer(array_recv(locfcinfo)); Assert(!locfcinfo->isnull); /* sanity checks: int2vector must be 1-D, 0-based, no nulls */ if (ARR_NDIM(result) != 1 || ARR_HASNULL(result) || ARR_ELEMTYPE(result) != INT2OID || ARR_LBOUND(result)[0] != 0) ereport(ERROR, (errcode(ERRCODE_INVALID_BINARY_REPRESENTATION), errmsg("invalid int2vector data"))); /* check length for consistency with int2vectorin() */ if (ARR_DIMS(result)[0] > FUNC_MAX_ARGS) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("oidvector has too many elements"))); PG_RETURN_POINTER(result); }
int HdfsFreeFileInfo(FsysName protocol, hdfsFileInfo * info, int numEntries) { FunctionCallInfoData fcinfo; FileSystemUdfData fsysUdf; FmgrInfo *fsysFunc = FsysInterfaceGetFunc(protocol, FSYS_FUNC_FREEFILEINFO); fsysUdf.type = T_FileSystemFunctionData; fsysUdf.fsys_fileinfo = info; fsysUdf.fsys_fileinfonum = numEntries; InitFunctionCallInfoData(/* FunctionCallInfoData */ fcinfo, /* FmgrInfo */ fsysFunc, /* nArgs */ 0, /* Call Context */ (Node *) (&fsysUdf), /* ResultSetInfo */ NULL); Datum d = FunctionCallInvoke(&fcinfo); return DatumGetInt32(d); }
/* * Set up a shim function to allow use of an old-style btree comparison * function as if it were a sort support comparator. */ void PrepareSortSupportComparisonShim(Oid cmpFunc, SortSupport ssup) { SortShimExtra *extra; extra = (SortShimExtra *) MemoryContextAlloc(ssup->ssup_cxt, sizeof(SortShimExtra)); /* Lookup the comparison function */ fmgr_info_cxt(cmpFunc, &extra->flinfo, ssup->ssup_cxt); /* We can initialize the callinfo just once and re-use it */ InitFunctionCallInfoData(extra->fcinfo, &extra->flinfo, 2, ssup->ssup_collation, NULL, NULL); extra->fcinfo.argnull[0] = false; extra->fcinfo.argnull[1] = false; ssup->ssup_extra = extra; ssup->comparator = comparison_shim; }
hdfsFileInfo * HdfsGetPathInfo(FsysName protocol, hdfsFS fileSystem, char * path) { FunctionCallInfoData fcinfo; FileSystemUdfData fsysUdf; FmgrInfo *fsysFunc = FsysInterfaceGetFunc(protocol, FSYS_FUNC_GETPATHINFO); fsysUdf.type = T_FileSystemFunctionData; fsysUdf.fsys_hdfs = fileSystem; fsysUdf.fsys_filepath = path; fsysUdf.fsys_fileinfo = NULL; InitFunctionCallInfoData(/* FunctionCallInfoData */ fcinfo, /* FmgrInfo */ fsysFunc, /* nArgs */ 0, /* Call Context */ (Node *) (&fsysUdf), /* ResultSetInfo */ NULL); (void) FunctionCallInvoke(&fcinfo); return FSYS_UDF_GET_FILEINFO(&fcinfo); }
Datum dbms_pipe_pack_message_record(PG_FUNCTION_ARGS) { HeapTupleHeader rec = PG_GETARG_HEAPTUPLEHEADER(0); Oid tupType; bytea *data; #if PG_VERSION_NUM >= 120000 LOCAL_FCINFO(info, 3); #else FunctionCallInfoData info_data; FunctionCallInfo info = &info_data; #endif tupType = HeapTupleHeaderGetTypeId(rec); /* * Normally one would call record_send() using DirectFunctionCall3, * but that does not work since record_send wants to cache some data * using fcinfo->flinfo->fn_extra. So we need to pass it our own * flinfo parameter. */ InitFunctionCallInfoData(*info, fcinfo->flinfo, 3, InvalidOid, NULL, NULL); init_args_3(info, PointerGetDatum(rec), ObjectIdGetDatum(tupType), Int32GetDatum(-1)); data = (bytea*) DatumGetPointer(record_send(info)); output_buffer = check_buffer(output_buffer, LOCALMSGSZ); pack_field(output_buffer, IT_RECORD, VARSIZE(data), VARDATA(data), tupType); PG_RETURN_VOID(); }
/* * compare_keys */ static int compare_keys(KeyedAggState *state, KeyValue *kv, Datum incoming, bool incoming_null, Oid collation, bool *result_null) { TypeCacheEntry *type = state->key_type; FunctionCallInfoData cmp_fcinfo; int result; if (incoming_null || KV_KEY_IS_NULL(kv)) { *result_null = true; return 0; } InitFunctionCallInfoData(cmp_fcinfo, &type->cmp_proc_finfo, 2, collation, NULL, NULL); cmp_fcinfo.arg[0] = kv->key; cmp_fcinfo.argnull[0] = KV_KEY_IS_NULL(kv); cmp_fcinfo.arg[1] = incoming; cmp_fcinfo.argnull[1] = incoming_null; result = DatumGetInt32(FunctionCallInvoke(&cmp_fcinfo)); *result_null = cmp_fcinfo.isnull; return result; }
/* * record_cmp() * Internal comparison function for records. * * Returns -1, 0 or 1 * * Do not assume that the two inputs are exactly the same record type; * for instance we might be comparing an anonymous ROW() construct against a * named composite type. We will compare as long as they have the same number * of non-dropped columns of the same types. */ static int record_cmp(FunctionCallInfo fcinfo) { HeapTupleHeader record1 = PG_GETARG_HEAPTUPLEHEADER(0); HeapTupleHeader record2 = PG_GETARG_HEAPTUPLEHEADER(1); int result = 0; Oid tupType1; Oid tupType2; int32 tupTypmod1; int32 tupTypmod2; TupleDesc tupdesc1; TupleDesc tupdesc2; HeapTupleData tuple1; HeapTupleData tuple2; int ncolumns1; int ncolumns2; RecordCompareData *my_extra; int ncols; Datum *values1; Datum *values2; bool *nulls1; bool *nulls2; int i1; int i2; int j; /* Extract type info from the tuples */ tupType1 = HeapTupleHeaderGetTypeId(record1); tupTypmod1 = HeapTupleHeaderGetTypMod(record1); tupdesc1 = lookup_rowtype_tupdesc(tupType1, tupTypmod1); ncolumns1 = tupdesc1->natts; tupType2 = HeapTupleHeaderGetTypeId(record2); tupTypmod2 = HeapTupleHeaderGetTypMod(record2); tupdesc2 = lookup_rowtype_tupdesc(tupType2, tupTypmod2); ncolumns2 = tupdesc2->natts; /* Build temporary HeapTuple control structures */ tuple1.t_len = HeapTupleHeaderGetDatumLength(record1); ItemPointerSetInvalid(&(tuple1.t_self)); tuple1.t_tableOid = InvalidOid; tuple1.t_data = record1; tuple2.t_len = HeapTupleHeaderGetDatumLength(record2); ItemPointerSetInvalid(&(tuple2.t_self)); tuple2.t_tableOid = InvalidOid; tuple2.t_data = record2; /* * We arrange to look up the needed comparison info just once per series * of calls, assuming the record types don't change underneath us. */ ncols = Max(ncolumns1, ncolumns2); my_extra = (RecordCompareData *) fcinfo->flinfo->fn_extra; if (my_extra == NULL || my_extra->ncolumns < ncols) { fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt, sizeof(RecordCompareData) - sizeof(ColumnCompareData) + ncols * sizeof(ColumnCompareData)); my_extra = (RecordCompareData *) fcinfo->flinfo->fn_extra; my_extra->ncolumns = ncols; my_extra->record1_type = InvalidOid; my_extra->record1_typmod = 0; my_extra->record2_type = InvalidOid; my_extra->record2_typmod = 0; } if (my_extra->record1_type != tupType1 || my_extra->record1_typmod != tupTypmod1 || my_extra->record2_type != tupType2 || my_extra->record2_typmod != tupTypmod2) { MemSet(my_extra->columns, 0, ncols * sizeof(ColumnCompareData)); my_extra->record1_type = tupType1; my_extra->record1_typmod = tupTypmod1; my_extra->record2_type = tupType2; my_extra->record2_typmod = tupTypmod2; } /* Break down the tuples into fields */ values1 = (Datum *) palloc(ncolumns1 * sizeof(Datum)); nulls1 = (bool *) palloc(ncolumns1 * sizeof(bool)); heap_deform_tuple(&tuple1, tupdesc1, values1, nulls1); values2 = (Datum *) palloc(ncolumns2 * sizeof(Datum)); nulls2 = (bool *) palloc(ncolumns2 * sizeof(bool)); heap_deform_tuple(&tuple2, tupdesc2, values2, nulls2); /* * Scan corresponding columns, allowing for dropped columns in different * places in the two rows. i1 and i2 are physical column indexes, j is * the logical column index. */ i1 = i2 = j = 0; while (i1 < ncolumns1 || i2 < ncolumns2) { TypeCacheEntry *typentry; Oid collation; FunctionCallInfoData locfcinfo; int32 cmpresult; /* * Skip dropped columns */ if (i1 < ncolumns1 && tupdesc1->attrs[i1]->attisdropped) { i1++; continue; } if (i2 < ncolumns2 && tupdesc2->attrs[i2]->attisdropped) { i2++; continue; } if (i1 >= ncolumns1 || i2 >= ncolumns2) break; /* we'll deal with mismatch below loop */ /* * Have two matching columns, they must be same type */ if (tupdesc1->attrs[i1]->atttypid != tupdesc2->attrs[i2]->atttypid) ereport(ERROR, (errcode(ERRCODE_DATATYPE_MISMATCH), errmsg("cannot compare dissimilar column types %s and %s at record column %d", format_type_be(tupdesc1->attrs[i1]->atttypid), format_type_be(tupdesc2->attrs[i2]->atttypid), j + 1))); /* * If they're not same collation, we don't complain here, but the * comparison function might. */ collation = tupdesc1->attrs[i1]->attcollation; if (collation != tupdesc2->attrs[i2]->attcollation) collation = InvalidOid; /* * Lookup the comparison function if not done already */ typentry = my_extra->columns[j].typentry; if (typentry == NULL || typentry->type_id != tupdesc1->attrs[i1]->atttypid) { typentry = lookup_type_cache(tupdesc1->attrs[i1]->atttypid, TYPECACHE_CMP_PROC_FINFO); if (!OidIsValid(typentry->cmp_proc_finfo.fn_oid)) ereport(ERROR, (errcode(ERRCODE_UNDEFINED_FUNCTION), errmsg("could not identify a comparison function for type %s", format_type_be(typentry->type_id)))); my_extra->columns[j].typentry = typentry; } /* * We consider two NULLs equal; NULL > not-NULL. */ if (!nulls1[i1] || !nulls2[i2]) { if (nulls1[i1]) { /* arg1 is greater than arg2 */ result = 1; break; } if (nulls2[i2]) { /* arg1 is less than arg2 */ result = -1; break; } /* Compare the pair of elements */ InitFunctionCallInfoData(locfcinfo, &typentry->cmp_proc_finfo, 2, collation, NULL, NULL); locfcinfo.arg[0] = values1[i1]; locfcinfo.arg[1] = values2[i2]; locfcinfo.argnull[0] = false; locfcinfo.argnull[1] = false; locfcinfo.isnull = false; cmpresult = DatumGetInt32(FunctionCallInvoke(&locfcinfo)); if (cmpresult < 0) { /* arg1 is less than arg2 */ result = -1; break; } else if (cmpresult > 0) { /* arg1 is greater than arg2 */ result = 1; break; } } /* equal, so continue to next column */ i1++, i2++, j++; } /* * If we didn't break out of the loop early, check for column count * mismatch. (We do not report such mismatch if we found unequal column * values; is that a feature or a bug?) */ if (result == 0) { if (i1 != ncolumns1 || i2 != ncolumns2) ereport(ERROR, (errcode(ERRCODE_DATATYPE_MISMATCH), errmsg("cannot compare record types with different numbers of columns"))); } pfree(values1); pfree(nulls1); pfree(values2); pfree(nulls2); ReleaseTupleDesc(tupdesc1); ReleaseTupleDesc(tupdesc2); /* Avoid leaking memory when handed toasted input. */ PG_FREE_IF_COPY(record1, 0); PG_FREE_IF_COPY(record2, 1); return result; }
/* * ExecMakeTableFunctionResult * * Evaluate a table function, producing a materialized result in a Tuplestore * object. * * This is used by nodeFunctionscan.c. */ Tuplestorestate * ExecMakeTableFunctionResult(SetExprState *setexpr, ExprContext *econtext, MemoryContext argContext, TupleDesc expectedDesc, bool randomAccess) { Tuplestorestate *tupstore = NULL; TupleDesc tupdesc = NULL; Oid funcrettype; bool returnsTuple; bool returnsSet = false; FunctionCallInfoData fcinfo; PgStat_FunctionCallUsage fcusage; ReturnSetInfo rsinfo; HeapTupleData tmptup; MemoryContext callerContext; MemoryContext oldcontext; bool first_time = true; callerContext = CurrentMemoryContext; funcrettype = exprType((Node *) setexpr->expr); returnsTuple = type_is_rowtype(funcrettype); /* * Prepare a resultinfo node for communication. We always do this even if * not expecting a set result, so that we can pass expectedDesc. In the * generic-expression case, the expression doesn't actually get to see the * resultinfo, but set it up anyway because we use some of the fields as * our own state variables. */ rsinfo.type = T_ReturnSetInfo; rsinfo.econtext = econtext; rsinfo.expectedDesc = expectedDesc; rsinfo.allowedModes = (int) (SFRM_ValuePerCall | SFRM_Materialize | SFRM_Materialize_Preferred); if (randomAccess) rsinfo.allowedModes |= (int) SFRM_Materialize_Random; rsinfo.returnMode = SFRM_ValuePerCall; /* isDone is filled below */ rsinfo.setResult = NULL; rsinfo.setDesc = NULL; /* * Normally the passed expression tree will be a SetExprState, since the * grammar only allows a function call at the top level of a table * function reference. However, if the function doesn't return set then * the planner might have replaced the function call via constant-folding * or inlining. So if we see any other kind of expression node, execute * it via the general ExecEvalExpr() code; the only difference is that we * don't get a chance to pass a special ReturnSetInfo to any functions * buried in the expression. */ if (!setexpr->elidedFuncState) { /* * This path is similar to ExecMakeFunctionResultSet. */ returnsSet = setexpr->funcReturnsSet; InitFunctionCallInfoData(fcinfo, &(setexpr->func), list_length(setexpr->args), setexpr->fcinfo_data.fncollation, NULL, (Node *) &rsinfo); /* * Evaluate the function's argument list. * * We can't do this in the per-tuple context: the argument values * would disappear when we reset that context in the inner loop. And * the caller's CurrentMemoryContext is typically a query-lifespan * context, so we don't want to leak memory there. We require the * caller to pass a separate memory context that can be used for this, * and can be reset each time through to avoid bloat. */ MemoryContextReset(argContext); oldcontext = MemoryContextSwitchTo(argContext); ExecEvalFuncArgs(&fcinfo, setexpr->args, econtext); MemoryContextSwitchTo(oldcontext); /* * If function is strict, and there are any NULL arguments, skip * calling the function and act like it returned NULL (or an empty * set, in the returns-set case). */ if (setexpr->func.fn_strict) { int i; for (i = 0; i < fcinfo.nargs; i++) { if (fcinfo.argnull[i]) goto no_function_result; } } } else { /* Treat setexpr as a generic expression */ InitFunctionCallInfoData(fcinfo, NULL, 0, InvalidOid, NULL, NULL); } /* * Switch to short-lived context for calling the function or expression. */ MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory); /* * Loop to handle the ValuePerCall protocol (which is also the same * behavior needed in the generic ExecEvalExpr path). */ for (;;) { Datum result; CHECK_FOR_INTERRUPTS(); /* * reset per-tuple memory context before each call of the function or * expression. This cleans up any local memory the function may leak * when called. */ ResetExprContext(econtext); /* Call the function or expression one time */ if (!setexpr->elidedFuncState) { pgstat_init_function_usage(&fcinfo, &fcusage); fcinfo.isnull = false; rsinfo.isDone = ExprSingleResult; result = FunctionCallInvoke(&fcinfo); pgstat_end_function_usage(&fcusage, rsinfo.isDone != ExprMultipleResult); } else { result = ExecEvalExpr(setexpr->elidedFuncState, econtext, &fcinfo.isnull); rsinfo.isDone = ExprSingleResult; } /* Which protocol does function want to use? */ if (rsinfo.returnMode == SFRM_ValuePerCall) { /* * Check for end of result set. */ if (rsinfo.isDone == ExprEndResult) break; /* * If first time through, build tuplestore for result. For a * scalar function result type, also make a suitable tupdesc. */ if (first_time) { oldcontext = MemoryContextSwitchTo(econtext->ecxt_per_query_memory); tupstore = tuplestore_begin_heap(randomAccess, false, work_mem); rsinfo.setResult = tupstore; if (!returnsTuple) { tupdesc = CreateTemplateTupleDesc(1, false); TupleDescInitEntry(tupdesc, (AttrNumber) 1, "column", funcrettype, -1, 0); rsinfo.setDesc = tupdesc; } MemoryContextSwitchTo(oldcontext); } /* * Store current resultset item. */ if (returnsTuple) { if (!fcinfo.isnull) { HeapTupleHeader td = DatumGetHeapTupleHeader(result); if (tupdesc == NULL) { /* * This is the first non-NULL result from the * function. Use the type info embedded in the * rowtype Datum to look up the needed tupdesc. Make * a copy for the query. */ oldcontext = MemoryContextSwitchTo(econtext->ecxt_per_query_memory); tupdesc = lookup_rowtype_tupdesc_copy(HeapTupleHeaderGetTypeId(td), HeapTupleHeaderGetTypMod(td)); rsinfo.setDesc = tupdesc; MemoryContextSwitchTo(oldcontext); } else { /* * Verify all later returned rows have same subtype; * necessary in case the type is RECORD. */ if (HeapTupleHeaderGetTypeId(td) != tupdesc->tdtypeid || HeapTupleHeaderGetTypMod(td) != tupdesc->tdtypmod) ereport(ERROR, (errcode(ERRCODE_DATATYPE_MISMATCH), errmsg("rows returned by function are not all of the same row type"))); } /* * tuplestore_puttuple needs a HeapTuple not a bare * HeapTupleHeader, but it doesn't need all the fields. */ tmptup.t_len = HeapTupleHeaderGetDatumLength(td); tmptup.t_data = td; tuplestore_puttuple(tupstore, &tmptup); } else { /* * NULL result from a tuple-returning function; expand it * to a row of all nulls. We rely on the expectedDesc to * form such rows. (Note: this would be problematic if * tuplestore_putvalues saved the tdtypeid/tdtypmod from * the provided descriptor, since that might not match * what we get from the function itself. But it doesn't.) */ int natts = expectedDesc->natts; bool *nullflags; nullflags = (bool *) palloc(natts * sizeof(bool)); memset(nullflags, true, natts * sizeof(bool)); tuplestore_putvalues(tupstore, expectedDesc, NULL, nullflags); } } else { /* Scalar-type case: just store the function result */ tuplestore_putvalues(tupstore, tupdesc, &result, &fcinfo.isnull); } /* * Are we done? */ if (rsinfo.isDone != ExprMultipleResult) break; } else if (rsinfo.returnMode == SFRM_Materialize) { /* check we're on the same page as the function author */ if (!first_time || rsinfo.isDone != ExprSingleResult) ereport(ERROR, (errcode(ERRCODE_E_R_I_E_SRF_PROTOCOL_VIOLATED), errmsg("table-function protocol for materialize mode was not followed"))); /* Done evaluating the set result */ break; } else ereport(ERROR, (errcode(ERRCODE_E_R_I_E_SRF_PROTOCOL_VIOLATED), errmsg("unrecognized table-function returnMode: %d", (int) rsinfo.returnMode))); first_time = false; } no_function_result: /* * If we got nothing from the function (ie, an empty-set or NULL result), * we have to create the tuplestore to return, and if it's a * non-set-returning function then insert a single all-nulls row. As * above, we depend on the expectedDesc to manufacture the dummy row. */ if (rsinfo.setResult == NULL) { MemoryContextSwitchTo(econtext->ecxt_per_query_memory); tupstore = tuplestore_begin_heap(randomAccess, false, work_mem); rsinfo.setResult = tupstore; if (!returnsSet) { int natts = expectedDesc->natts; bool *nullflags; MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory); nullflags = (bool *) palloc(natts * sizeof(bool)); memset(nullflags, true, natts * sizeof(bool)); tuplestore_putvalues(tupstore, expectedDesc, NULL, nullflags); } } /* * If function provided a tupdesc, cross-check it. We only really need to * do this for functions returning RECORD, but might as well do it always. */ if (rsinfo.setDesc) { tupledesc_match(expectedDesc, rsinfo.setDesc); /* * If it is a dynamically-allocated TupleDesc, free it: it is * typically allocated in a per-query context, so we must avoid * leaking it across multiple usages. */ if (rsinfo.setDesc->tdrefcount == -1) FreeTupleDesc(rsinfo.setDesc); } MemoryContextSwitchTo(callerContext); /* All done, pass back the tuplestore */ return rsinfo.setResult; }
/* * init_sexpr - initialize a SetExprState node during first use */ static void init_sexpr(Oid foid, Oid input_collation, Expr *node, SetExprState *sexpr, PlanState *parent, MemoryContext sexprCxt, bool allowSRF, bool needDescForSRF) { AclResult aclresult; /* Check permission to call function */ aclresult = pg_proc_aclcheck(foid, GetUserId(), ACL_EXECUTE); if (aclresult != ACLCHECK_OK) aclcheck_error(aclresult, OBJECT_FUNCTION, get_func_name(foid)); InvokeFunctionExecuteHook(foid); /* * Safety check on nargs. Under normal circumstances this should never * fail, as parser should check sooner. But possibly it might fail if * server has been compiled with FUNC_MAX_ARGS smaller than some functions * declared in pg_proc? */ if (list_length(sexpr->args) > FUNC_MAX_ARGS) ereport(ERROR, (errcode(ERRCODE_TOO_MANY_ARGUMENTS), errmsg_plural("cannot pass more than %d argument to a function", "cannot pass more than %d arguments to a function", FUNC_MAX_ARGS, FUNC_MAX_ARGS))); /* Set up the primary fmgr lookup information */ fmgr_info_cxt(foid, &(sexpr->func), sexprCxt); fmgr_info_set_expr((Node *) sexpr->expr, &(sexpr->func)); /* Initialize the function call parameter struct as well */ InitFunctionCallInfoData(sexpr->fcinfo_data, &(sexpr->func), list_length(sexpr->args), input_collation, NULL, NULL); /* If function returns set, check if that's allowed by caller */ if (sexpr->func.fn_retset && !allowSRF) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("set-valued function called in context that cannot accept a set"), parent ? executor_errposition(parent->state, exprLocation((Node *) node)) : 0)); /* Otherwise, caller should have marked the sexpr correctly */ Assert(sexpr->func.fn_retset == sexpr->funcReturnsSet); /* If function returns set, prepare expected tuple descriptor */ if (sexpr->func.fn_retset && needDescForSRF) { TypeFuncClass functypclass; Oid funcrettype; TupleDesc tupdesc; MemoryContext oldcontext; functypclass = get_expr_result_type(sexpr->func.fn_expr, &funcrettype, &tupdesc); /* Must save tupdesc in sexpr's context */ oldcontext = MemoryContextSwitchTo(sexprCxt); if (functypclass == TYPEFUNC_COMPOSITE || functypclass == TYPEFUNC_COMPOSITE_DOMAIN) { /* Composite data type, e.g. a table's row type */ Assert(tupdesc); /* Must copy it out of typcache for safety */ sexpr->funcResultDesc = CreateTupleDescCopy(tupdesc); sexpr->funcReturnsTuple = true; } else if (functypclass == TYPEFUNC_SCALAR) { /* Base data type, i.e. scalar */ tupdesc = CreateTemplateTupleDesc(1, false); TupleDescInitEntry(tupdesc, (AttrNumber) 1, NULL, funcrettype, -1, 0); sexpr->funcResultDesc = tupdesc; sexpr->funcReturnsTuple = false; } else if (functypclass == TYPEFUNC_RECORD) { /* This will work if function doesn't need an expectedDesc */ sexpr->funcResultDesc = NULL; sexpr->funcReturnsTuple = true; } else { /* Else, we will fail if function needs an expectedDesc */ sexpr->funcResultDesc = NULL; } MemoryContextSwitchTo(oldcontext); } else sexpr->funcResultDesc = NULL; /* Initialize additional state */ sexpr->funcResultStore = NULL; sexpr->funcResultSlot = NULL; sexpr->shutdown_reg = false; }
static Datum dbms_pipe_unpack_message(PG_FUNCTION_ARGS, message_data_type dtype) { Oid tupType; void *ptr; message_data_type type; int32 size; Datum result; message_data_type next_type; if (input_buffer == NULL || input_buffer->items_count <= 0 || input_buffer->next == NULL || input_buffer->next->type == IT_NO_MORE_ITEMS) PG_RETURN_NULL(); next_type = input_buffer->next->type; if (next_type != dtype) ereport(ERROR, (errcode(ERRCODE_DATATYPE_MISMATCH), errmsg("datatype mismatch"), errdetail("unpack unexpected type: %d", next_type))); ptr = unpack_field(input_buffer, &type, &size, &tupType); Assert(ptr != NULL); switch (type) { case IT_TIMESTAMPTZ: result = TimestampTzGetDatum(*(TimestampTz*)ptr); break; case IT_DATE: result = DateADTGetDatum(*(DateADT*)ptr); break; case IT_VARCHAR: case IT_NUMBER: case IT_BYTEA: result = PointerGetDatum(cstring_to_text_with_len(ptr, size)); break; case IT_RECORD: { #if PG_VERSION_NUM >= 120000 LOCAL_FCINFO(info, 3); #else FunctionCallInfoData info_data; FunctionCallInfo info = &info_data; #endif StringInfoData buf; text *data = cstring_to_text_with_len(ptr, size); buf.data = VARDATA(data); buf.len = VARSIZE(data) - VARHDRSZ; buf.maxlen = buf.len; buf.cursor = 0; /* * Normally one would call record_recv() using DirectFunctionCall3, * but that does not work since record_recv wants to cache some data * using fcinfo->flinfo->fn_extra. So we need to pass it our own * flinfo parameter. */ InitFunctionCallInfoData(*info, fcinfo->flinfo, 3, InvalidOid, NULL, NULL); init_args_3(info, PointerGetDatum(&buf), ObjectIdGetDatum(tupType), Int32GetDatum(-1)); result = record_recv(info); break; } default: elog(ERROR, "unexpected type: %d", type); result = (Datum) 0; /* keep compiler quiet */ } if (input_buffer->items_count == 0) { pfree(input_buffer); input_buffer = NULL; } PG_RETURN_DATUM(result); }
/* * decode(lhs, [rhs, ret], ..., [default]) */ Datum ora_decode(PG_FUNCTION_ARGS) { int nargs; int i; int retarg; /* default value is last arg or NULL. */ nargs = PG_NARGS(); if (nargs % 2 == 0) { retarg = nargs - 1; nargs -= 1; /* ignore the last argument */ } else retarg = -1; /* NULL */ if (PG_ARGISNULL(0)) { for (i = 1; i < nargs; i += 2) { if (PG_ARGISNULL(i)) { retarg = i + 1; break; } } } else { FmgrInfo *eq; #if PG_VERSION_NUM >= 90100 Oid collation = PG_GET_COLLATION(); #endif /* * On first call, get the input type's operator '=' and save at * fn_extra. */ if (fcinfo->flinfo->fn_extra == NULL) { MemoryContext oldctx; Oid typid = get_fn_expr_argtype(fcinfo->flinfo, 0); Oid eqoid = equality_oper_funcid(typid); oldctx = MemoryContextSwitchTo(fcinfo->flinfo->fn_mcxt); eq = palloc(sizeof(FmgrInfo)); fmgr_info(eqoid, eq); MemoryContextSwitchTo(oldctx); fcinfo->flinfo->fn_extra = eq; } else eq = fcinfo->flinfo->fn_extra; for (i = 1; i < nargs; i += 2) { FunctionCallInfoData func; Datum result; if (PG_ARGISNULL(i)) continue; #if PG_VERSION_NUM >= 90100 InitFunctionCallInfoData(func, eq, 2, collation, NULL, NULL); #else InitFunctionCallInfoData(func, eq, 2, NULL, NULL); #endif func.arg[0] = PG_GETARG_DATUM(0); func.arg[1] = PG_GETARG_DATUM(i); func.argnull[0] = false; func.argnull[1] = false; result = FunctionCallInvoke(&func); if (!func.isnull && DatumGetBool(result)) { retarg = i + 1; break; } } } if (retarg < 0 || PG_ARGISNULL(retarg)) PG_RETURN_NULL(); else PG_RETURN_DATUM(PG_GETARG_DATUM(retarg)); }
/* * Initialize the TABLESAMPLE Descriptor and the TABLESAMPLE Method. */ TableSampleDesc * tablesample_init(SampleScanState *scanstate, TableSampleClause *tablesample) { FunctionCallInfoData fcinfo; int i; List *args = tablesample->args; ListCell *arg; ExprContext *econtext = scanstate->ss.ps.ps_ExprContext; TableSampleDesc *tsdesc = (TableSampleDesc *) palloc0(sizeof(TableSampleDesc)); /* Load functions */ fmgr_info(tablesample->tsminit, &(tsdesc->tsminit)); fmgr_info(tablesample->tsmnextblock, &(tsdesc->tsmnextblock)); fmgr_info(tablesample->tsmnexttuple, &(tsdesc->tsmnexttuple)); if (OidIsValid(tablesample->tsmexaminetuple)) fmgr_info(tablesample->tsmexaminetuple, &(tsdesc->tsmexaminetuple)); else tsdesc->tsmexaminetuple.fn_oid = InvalidOid; fmgr_info(tablesample->tsmreset, &(tsdesc->tsmreset)); fmgr_info(tablesample->tsmend, &(tsdesc->tsmend)); InitFunctionCallInfoData(fcinfo, &tsdesc->tsminit, list_length(args) + 2, InvalidOid, NULL, NULL); tsdesc->tupDesc = scanstate->ss.ss_ScanTupleSlot->tts_tupleDescriptor; tsdesc->heapScan = scanstate->ss.ss_currentScanDesc; /* First argument for init function is always TableSampleDesc */ fcinfo.arg[0] = PointerGetDatum(tsdesc); fcinfo.argnull[0] = false; /* * Second arg for init function is always REPEATABLE * When tablesample->repeatable is NULL then REPEATABLE clause was not * specified. * When specified, the expression cannot evaluate to NULL. */ if (tablesample->repeatable) { ExprState *argstate = ExecInitExpr((Expr *) tablesample->repeatable, (PlanState *) scanstate); fcinfo.arg[1] = ExecEvalExpr(argstate, econtext, &fcinfo.argnull[1], NULL); if (fcinfo.argnull[1]) ereport(ERROR, (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED), errmsg("REPEATABLE clause must be NOT NULL numeric value"))); } else { fcinfo.arg[1] = UInt32GetDatum(random()); fcinfo.argnull[1] = false; } /* Rest of the arguments come from user. */ i = 2; foreach(arg, args) { Expr *argexpr = (Expr *) lfirst(arg); ExprState *argstate = ExecInitExpr(argexpr, (PlanState *) scanstate); if (argstate == NULL) { fcinfo.argnull[i] = true; fcinfo.arg[i] = (Datum) 0;; } fcinfo.arg[i] = ExecEvalExpr(argstate, econtext, &fcinfo.argnull[i], NULL); i++; }