static void FunctionParserInit(FunctionParser *self, Checker *checker, const char *infile, TupleDesc desc, bool multi_process, Oid collation) { int i; ParsedFunction function; int nargs; Oid funcid; HeapTuple ftup; Form_pg_proc pp; bool tupledesc_matched = false; if (pg_strcasecmp(infile, "stdin") == 0) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("cannot load from STDIN in the case of \"TYPE = FUNCTION\""))); if (checker->encoding != -1) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("does not support parameter \"ENCODING\" in \"TYPE = FUNCTION\""))); function = ParseFunction(infile, false); funcid = function.oid; fmgr_info(funcid, &self->flinfo); if (!self->flinfo.fn_retset) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("function must return set"))); ftup = SearchSysCache(PROCOID, ObjectIdGetDatum(funcid), 0, 0, 0); pp = (Form_pg_proc) GETSTRUCT(ftup); /* Check data type of the function result value */ if (pp->prorettype == desc->tdtypeid && desc->tdtypeid != RECORDOID) tupledesc_matched = true; else if (pp->prorettype == RECORDOID) { TupleDesc resultDesc = NULL; /* Check for OUT parameters defining a RECORD result */ resultDesc = build_function_result_tupdesc_t(ftup); if (resultDesc) { tupledesc_match(desc, resultDesc); tupledesc_matched = true; FreeTupleDesc(resultDesc); } } else if (get_typtype(pp->prorettype) != TYPTYPE_COMPOSITE) ereport(ERROR, (errcode(ERRCODE_DATATYPE_MISMATCH), errmsg("function return data type and target table data type do not match"))); if (tupledesc_matched && checker->tchecker) checker->tchecker->status = NO_COERCION; /* * assign arguments */ nargs = function.nargs; for (i = 0; #if PG_VERSION_NUM >= 80400 i < nargs - function.nvargs; #else i < nargs; #endif ++i) { if (function.args[i] == NULL) { if (self->flinfo.fn_strict) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("function is strict, but argument %d is NULL", i))); self->fcinfo.argnull[i] = true; } else { Oid typinput; Oid typioparam; getTypeInputInfo(pp->proargtypes.values[i], &typinput, &typioparam); self->fcinfo.arg[i] = OidInputFunctionCall(typinput, (char *) function.args[i], typioparam, -1); self->fcinfo.argnull[i] = false; pfree(function.args[i]); } } /* * assign variadic arguments */ #if PG_VERSION_NUM >= 80400 if (function.nvargs > 0) { int nfixedarg; Oid func; Oid element_type; int16 elmlen; bool elmbyval; char elmalign; char elmdelim; Oid elmioparam; Datum *elems; bool *nulls; int dims[1]; int lbs[1]; ArrayType *arry; nfixedarg = i; element_type = pp->provariadic; /* * Get info about element type, including its input conversion proc */ get_type_io_data(element_type, IOFunc_input, &elmlen, &elmbyval, &elmalign, &elmdelim, &elmioparam, &func); elems = (Datum *) palloc(function.nvargs * sizeof(Datum)); nulls = (bool *) palloc0(function.nvargs * sizeof(bool)); for (i = 0; i < function.nvargs; i++) { if (function.args[nfixedarg + i] == NULL) nulls[i] = true; else { elems[i] = OidInputFunctionCall(func, (char *) function.args[nfixedarg + i], elmioparam, -1); pfree(function.args[nfixedarg + i]); } } dims[0] = function.nvargs; lbs[0] = 1; arry = construct_md_array(elems, nulls, 1, dims, lbs, element_type, elmlen, elmbyval, elmalign); self->fcinfo.arg[nfixedarg] = PointerGetDatum(arry); } /* * assign default arguments */ if (function.ndargs > 0) { Datum proargdefaults; bool isnull; char *str; List *defaults; int ndelete; ListCell *l; /* shouldn't happen, FuncnameGetCandidates messed up */ if (function.ndargs > pp->pronargdefaults) elog(ERROR, "not enough default arguments"); proargdefaults = SysCacheGetAttr(PROCOID, ftup, Anum_pg_proc_proargdefaults, &isnull); Assert(!isnull); str = TextDatumGetCString(proargdefaults); defaults = (List *) stringToNode(str); Assert(IsA(defaults, List)); pfree(str); /* Delete any unused defaults from the returned list */ ndelete = list_length(defaults) - function.ndargs; while (ndelete-- > 0) defaults = list_delete_first(defaults); self->arg_econtext = CreateStandaloneExprContext(); foreach(l, defaults) { Expr *expr = (Expr *) lfirst(l); ExprState *argstate; ExprDoneCond thisArgIsDone; /* probably shouldn't happen ... */ if (nargs >= FUNC_MAX_ARGS) ereport(ERROR, (errcode(ERRCODE_TOO_MANY_ARGUMENTS), errmsg("cannot pass more than %d arguments to a function", FUNC_MAX_ARGS))); argstate = ExecInitExpr(expr, NULL); self->fcinfo.arg[nargs] = ExecEvalExpr(argstate, self->arg_econtext, &self->fcinfo.argnull[nargs], &thisArgIsDone); if (thisArgIsDone != ExprSingleResult) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("functions and operators can take at most one set argument"))); nargs++; }
/* * plr_SPI_prepare - The builtin SPI_prepare command for the R interpreter */ SEXP plr_SPI_prepare(SEXP rsql, SEXP rargtypes) { const char *sql; int nargs; int i; Oid *typeids = NULL; Oid *typelems = NULL; FmgrInfo *typinfuncs = NULL; void *pplan = NULL; void *saved_plan; saved_plan_desc *plan_desc; SEXP result; MemoryContext oldcontext; PREPARE_PG_TRY; /* set up error context */ PUSH_PLERRCONTEXT(rsupport_error_callback, "pg.spi.prepare"); /* switch to long lived context to create plan description */ oldcontext = MemoryContextSwitchTo(TopMemoryContext); plan_desc = (saved_plan_desc *) palloc(sizeof(saved_plan_desc)); MemoryContextSwitchTo(oldcontext); PROTECT(rsql = AS_CHARACTER(rsql)); sql = CHAR(STRING_ELT(rsql, 0)); UNPROTECT(1); if (sql == NULL) error("%s", "cannot prepare empty query"); PROTECT(rargtypes = AS_INTEGER(rargtypes)); if (!isVector(rargtypes) || !isInteger(rargtypes)) error("%s", "second parameter must be a vector of PostgreSQL datatypes"); /* deal with case of no parameters for the prepared query */ if (rargtypes == R_MissingArg || INTEGER(rargtypes)[0] == NA_INTEGER) nargs = 0; else nargs = length(rargtypes); if (nargs < 0) /* can this even happen?? */ error("%s", "second parameter must be a vector of PostgreSQL datatypes"); if (nargs > 0) { /* switch to long lived context to create plan description elements */ oldcontext = MemoryContextSwitchTo(TopMemoryContext); typeids = (Oid *) palloc(nargs * sizeof(Oid)); typelems = (Oid *) palloc(nargs * sizeof(Oid)); typinfuncs = (FmgrInfo *) palloc(nargs * sizeof(FmgrInfo)); MemoryContextSwitchTo(oldcontext); for (i = 0; i < nargs; i++) { int16 typlen; bool typbyval; char typdelim; Oid typinput, typelem; char typalign; FmgrInfo typinfunc; typeids[i] = INTEGER(rargtypes)[i]; /* switch to long lived context to create plan description elements */ oldcontext = MemoryContextSwitchTo(TopMemoryContext); get_type_io_data(typeids[i], IOFunc_input, &typlen, &typbyval, &typalign, &typdelim, &typelem, &typinput); typelems[i] = typelem; MemoryContextSwitchTo(oldcontext); /* perm_fmgr_info already uses TopMemoryContext */ perm_fmgr_info(typinput, &typinfunc); typinfuncs[i] = typinfunc; } } else typeids = NULL; UNPROTECT(1); /* switch to SPI memory context */ oldcontext = MemoryContextSwitchTo(plr_SPI_context); /* * trap elog/ereport so we can let R finish up gracefully * and generate the error once we exit the interpreter */ PG_TRY(); { /* Prepare plan for query */ pplan = SPI_prepare(sql, nargs, typeids); } PLR_PG_CATCH(); PLR_PG_END_TRY(); if (pplan == NULL) { char buf[128]; char *reason; switch (SPI_result) { case SPI_ERROR_ARGUMENT: reason = "SPI_ERROR_ARGUMENT"; break; case SPI_ERROR_UNCONNECTED: reason = "SPI_ERROR_UNCONNECTED"; break; case SPI_ERROR_COPY: reason = "SPI_ERROR_COPY"; break; case SPI_ERROR_CURSOR: reason = "SPI_ERROR_CURSOR"; break; case SPI_ERROR_TRANSACTION: reason = "SPI_ERROR_TRANSACTION"; break; case SPI_ERROR_OPUNKNOWN: reason = "SPI_ERROR_OPUNKNOWN"; break; default: snprintf(buf, sizeof(buf), "unknown RC %d", SPI_result); reason = buf; break; } /* internal error */ error("SPI_prepare() failed: %s", reason); } /* SPI_saveplan already uses TopMemoryContext */ saved_plan = SPI_saveplan(pplan); if (saved_plan == NULL) { char buf[128]; char *reason; switch (SPI_result) { case SPI_ERROR_ARGUMENT: reason = "SPI_ERROR_ARGUMENT"; break; case SPI_ERROR_UNCONNECTED: reason = "SPI_ERROR_UNCONNECTED"; break; default: snprintf(buf, sizeof(buf), "unknown RC %d", SPI_result); reason = buf; break; } /* internal error */ error("SPI_saveplan() failed: %s", reason); } /* back to caller's memory context */ MemoryContextSwitchTo(oldcontext); /* no longer need this */ SPI_freeplan(pplan); plan_desc->saved_plan = saved_plan; plan_desc->nargs = nargs; plan_desc->typeids = typeids; plan_desc->typelems = typelems; plan_desc->typinfuncs = typinfuncs; result = R_MakeExternalPtr(plan_desc, R_NilValue, R_NilValue); POP_PLERRCONTEXT; return result; }
/*! * scalar function taking an mfv sketch, returning a histogram of * its most frequent values */ Datum __mfvsketch_final(PG_FUNCTION_ARGS) { bytea * transblob = PG_GETARG_BYTEA_P(0); mfvtransval *transval = NULL; ArrayType * retval; uint32 i; int dims[2], lbs[2]; /* Oid typInput, typIOParam; */ Oid outFuncOid; bool typIsVarlena; int16 typlen; bool typbyval; char typalign; char typdelim; Oid typioparam; Oid typiofunc; if (PG_ARGISNULL(0)) PG_RETURN_NULL(); if (VARSIZE(transblob) < MFV_TRANSVAL_SZ(0)) PG_RETURN_NULL(); check_mfvtransval(transblob); transval = (mfvtransval *)VARDATA(transblob); /* * We only declare the variable-length array histo here after some sanity * checking. We risk a stack overflow otherwise. In particular, we need to * make sure that transval->max_mfvs is initialized. It might not be if the * (strict) transition function is never called. (MADLIB-254) */ Datum histo[transval->max_mfvs][2]; qsort(transval->mfvs, transval->next_mfv, sizeof(offsetcnt), cnt_cmp_desc); getTypeOutputInfo(INT8OID, &outFuncOid, &typIsVarlena); for (i = 0; i < transval->next_mfv; i++) { void *tmpp = mfv_transval_getval(transblob,i); Datum curval = PointerExtractDatum(tmpp, transval->typByVal); char *countbuf = OidOutputFunctionCall(outFuncOid, Int64GetDatum(transval->mfvs[i].cnt)); char *valbuf = OidOutputFunctionCall(transval->outFuncOid, curval); histo[i][0] = PointerGetDatum(cstring_to_text(valbuf)); histo[i][1] = PointerGetDatum(cstring_to_text(countbuf)); pfree(countbuf); pfree(valbuf); } /* * Get info about element type */ get_type_io_data(TEXTOID, IOFunc_output, &typlen, &typbyval, &typalign, &typdelim, &typioparam, &typiofunc); dims[0] = i; dims[1] = 2; lbs[0] = lbs[1] = 0; retval = construct_md_array((Datum *)histo, NULL, 2, dims, lbs, TEXTOID, -1, 0, 'i'); PG_RETURN_ARRAYTYPE_P(retval); }
/* * Reading quantiles from an input array, based mostly on * array_to_text_internal (it's a modified copy). This expects * to receive a single-dimensional float8 array as input, fails * otherwise. */ static double * array_to_double(FunctionCallInfo fcinfo, ArrayType *v, int * len) { double *result; int nitems, *dims, ndims; Oid element_type; int typlen; bool typbyval; char typalign; char *p; int i, idx = 0; ArrayMetaState *my_extra; ndims = ARR_NDIM(v); dims = ARR_DIMS(v); nitems = ArrayGetNItems(ndims, dims); /* this is a special-purpose function for single-dimensional arrays */ if (ndims != 1) { elog(ERROR, "error, array_to_double expects a single-dimensional array" "(dims = %d)", ndims); } /* if there are no elements, set the length to 0 and return NULL */ if (nitems == 0) { (*len) = 0; return NULL; } element_type = ARR_ELEMTYPE(v); result = (double*)palloc(nitems * sizeof(double)); /* * We arrange to look up info about element type, including its output * conversion proc, only once per series of calls, assuming the element * type doesn't change underneath us. */ my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra; if (my_extra == NULL) { fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt, sizeof(ArrayMetaState)); my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra; my_extra->element_type = ~element_type; } if (my_extra->element_type != element_type) { /* * Get info about element type, including its output conversion proc */ get_type_io_data(element_type, IOFunc_output, &my_extra->typlen, &my_extra->typbyval, &my_extra->typalign, &my_extra->typdelim, &my_extra->typioparam, &my_extra->typiofunc); fmgr_info_cxt(my_extra->typiofunc, &my_extra->proc, fcinfo->flinfo->fn_mcxt); my_extra->element_type = element_type; } typlen = my_extra->typlen; typbyval = my_extra->typbyval; typalign = my_extra->typalign; p = ARR_DATA_PTR(v); for (i = 0; i < nitems; i++) { Datum itemvalue = fetch_att(p, typbyval, typlen); double val = DatumGetFloat8(itemvalue); result[idx++] = val; p = att_addlength_pointer(p, typlen, p); p = (char *) att_align_nominal(p, typalign); } (*len) = idx; return result; }
/* * Turn an array into JSON. */ static void array_to_json_internal(Datum array, StringInfo result, bool use_line_feeds) { ArrayType *v = DatumGetArrayTypeP(array); Oid element_type = ARR_ELEMTYPE(v); int *dim; int ndim; int nitems; int count = 0; Datum *elements; bool *nulls; int16 typlen; bool typbyval; char typalign, typdelim; Oid typioparam; Oid typoutputfunc; TYPCATEGORY tcategory; Oid castfunc = InvalidOid; ndim = ARR_NDIM(v); dim = ARR_DIMS(v); nitems = ArrayGetNItems(ndim, dim); if (nitems <= 0) { appendStringInfoString(result, "[]"); return; } get_type_io_data(element_type, IOFunc_output, &typlen, &typbyval, &typalign, &typdelim, &typioparam, &typoutputfunc); if (element_type > FirstNormalObjectId) { HeapTuple tuple; Form_pg_cast castForm; tuple = SearchSysCache2(CASTSOURCETARGET, ObjectIdGetDatum(element_type), ObjectIdGetDatum(JSONOID)); if (HeapTupleIsValid(tuple)) { castForm = (Form_pg_cast) GETSTRUCT(tuple); if (castForm->castmethod == COERCION_METHOD_FUNCTION) castfunc = typoutputfunc = castForm->castfunc; ReleaseSysCache(tuple); } } deconstruct_array(v, element_type, typlen, typbyval, typalign, &elements, &nulls, &nitems); if (castfunc != InvalidOid) tcategory = TYPCATEGORY_JSON_CAST; else if (element_type == RECORDOID) tcategory = TYPCATEGORY_COMPOSITE; else if (element_type == JSONOID) tcategory = TYPCATEGORY_JSON; else tcategory = TypeCategory(element_type); array_dim_to_json(result, 0, ndim, dim, elements, nulls, &count, tcategory, typoutputfunc, use_line_feeds); pfree(elements); pfree(nulls); }
static void PLy_input_datum_func2(PLyDatumToOb *arg, Oid typeOid, HeapTuple typeTup) { Form_pg_type typeStruct = (Form_pg_type) GETSTRUCT(typeTup); Oid element_type = get_element_type(typeOid); /* Get the type's conversion information */ perm_fmgr_info(typeStruct->typoutput, &arg->typfunc); arg->typoid = HeapTupleGetOid(typeTup); arg->typmod = -1; arg->typioparam = getTypeIOParam(typeTup); arg->typbyval = typeStruct->typbyval; arg->typlen = typeStruct->typlen; arg->typalign = typeStruct->typalign; /* Determine which kind of Python object we will convert to */ switch (getBaseType(element_type ? element_type : typeOid)) { case BOOLOID: arg->func = PLyBool_FromBool; break; case FLOAT4OID: arg->func = PLyFloat_FromFloat4; break; case FLOAT8OID: arg->func = PLyFloat_FromFloat8; break; case NUMERICOID: arg->func = PLyFloat_FromNumeric; break; case INT2OID: arg->func = PLyInt_FromInt16; break; case INT4OID: arg->func = PLyInt_FromInt32; break; case INT8OID: arg->func = PLyLong_FromInt64; break; case BYTEAOID: arg->func = PLyBytes_FromBytea; break; default: arg->func = PLyString_FromDatum; break; } if (element_type) { char dummy_delim; Oid funcid; arg->elm = PLy_malloc0(sizeof(*arg->elm)); arg->elm->func = arg->func; arg->func = PLyList_FromArray; arg->elm->typoid = element_type; arg->elm->typmod = -1; get_type_io_data(element_type, IOFunc_output, &arg->elm->typlen, &arg->elm->typbyval, &arg->elm->typalign, &dummy_delim, &arg->elm->typioparam, &funcid); perm_fmgr_info(funcid, &arg->elm->typfunc); } }
static void PLy_output_datum_func2(PLyObToDatum *arg, HeapTuple typeTup) { Form_pg_type typeStruct = (Form_pg_type) GETSTRUCT(typeTup); Oid element_type; perm_fmgr_info(typeStruct->typinput, &arg->typfunc); arg->typoid = HeapTupleGetOid(typeTup); arg->typmod = -1; arg->typioparam = getTypeIOParam(typeTup); arg->typbyval = typeStruct->typbyval; element_type = get_element_type(arg->typoid); /* * Select a conversion function to convert Python objects to PostgreSQL * datums. Most data types can go through the generic function. */ switch (getBaseType(element_type ? element_type : arg->typoid)) { case BOOLOID: arg->func = PLyObject_ToBool; break; case BYTEAOID: arg->func = PLyObject_ToBytea; break; default: arg->func = PLyObject_ToDatum; break; } /* Composite types need their own input routine, though */ if (typeStruct->typtype == TYPTYPE_COMPOSITE) { arg->func = PLyObject_ToComposite; } if (element_type) { char dummy_delim; Oid funcid; if (type_is_rowtype(element_type)) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("PL/Python functions cannot return type %s", format_type_be(arg->typoid)), errdetail("PL/Python does not support conversion to arrays of row types."))); arg->elm = PLy_malloc0(sizeof(*arg->elm)); arg->elm->func = arg->func; arg->func = PLySequence_ToArray; arg->elm->typoid = element_type; arg->elm->typmod = -1; get_type_io_data(element_type, IOFunc_input, &arg->elm->typlen, &arg->elm->typbyval, &arg->elm->typalign, &dummy_delim, &arg->elm->typioparam, &funcid); perm_fmgr_info(funcid, &arg->elm->typfunc); } }
Datum serialize_array(PG_FUNCTION_ARGS) { ArrayType *v = PG_GETARG_ARRAYTYPE_P(0); Oid element_type = ARR_ELEMTYPE(v); int16 typlen; bool typbyval; char typalign; char typdelim; char *p, *value; char type_category; bool needComma = false; bits8 *bitmap; int bitmask; int nitems, i; int ndim, *dims; Oid typioparam, typiofunc; HeapTuple type_tuple; FmgrInfo proc, flinfo; StringInfoData buf; // FILE* log; // log = fopen("/var/lib/postgresql/serializer.log", "a"); // fprintf(log, "Doing serialize_array\n"); // fflush(log); /* * Get info about element type, including its output conversion proc */ get_type_io_data(element_type, IOFunc_output, &typlen, &typbyval, &typalign, &typdelim, &typioparam, &typiofunc); fmgr_info_cxt( typiofunc, &proc, fcinfo->flinfo->fn_mcxt ); ndim = ARR_NDIM(v); dims = ARR_DIMS(v); nitems = ArrayGetNItems(ndim, dims); if( ndim > 1 ) elog( ERROR, "multidimensional arrays doesn't supported" ); p = ARR_DATA_PTR(v); bitmap = ARR_NULLBITMAP(v); bitmask = 1; /* obtain type information from pg_catalog */ type_tuple = SearchSysCache1( TYPEOID, ObjectIdGetDatum(element_type) ); if (!HeapTupleIsValid( type_tuple )) elog(ERROR, "cache lookup failed for relation %u", element_type); type_category = ((Form_pg_type) GETSTRUCT( type_tuple ))->typcategory; ReleaseSysCache( type_tuple ); /* Build the result string */ initStringInfo(&buf); appendStringInfoChar(&buf, '['); for (i = 0; i < nitems; i++) { if (needComma) appendStringInfoChar(&buf, ','); needComma = true; /* Get source element, checking for NULL */ if (bitmap && (*bitmap & bitmask) == 0) { // append null appendStringInfoString(&buf, "null"); } else { /* get item value and advance array data pointer */ Datum itemvalue = fetch_att(p, typbyval, typlen); p = att_addlength_pointer(p, typlen, p); p = (char *) att_align_nominal(p, typalign); //------------------------ switch( type_category ) { // http://www.postgresql.org/docs/current/static/catalog-pg-type.html#CATALOG-TYPCATEGORY-TABLE case 'A': //array - impossible case break; case 'C': //composite //call to serialize_record( ... ) MemSet( &flinfo, 0, sizeof( flinfo ) ); flinfo.fn_addr = serialize_record; flinfo.fn_nargs = 1; flinfo.fn_mcxt = fcinfo->flinfo->fn_mcxt; value = PG_TEXT_DATUM_GET_CSTR( FunctionCall1( &flinfo, itemvalue ) ); appendStringInfoString(&buf, value); break; case 'N': //numeric // get column text value value = OutputFunctionCall( &proc, itemvalue ); appendStringInfoString(&buf, value); break; default: //another // get column text value value = OutputFunctionCall( &proc, itemvalue ); appendStringInfoQuotedString(&buf, value); } } /* advance bitmap pointer if any */ if (bitmap) { bitmask <<= 1; if (bitmask == 0x100) { bitmap++; bitmask = 1; } } } appendStringInfoChar(&buf, ']'); // fclose(log); //PG_RETURN_CSTRING(retval); PG_RETURN_TEXT_P( PG_CSTR_GET_TEXT( buf.data ) ); }
static void PLy_output_datum_func2(PLyObToDatum *arg, HeapTuple typeTup, Oid langid, List *trftypes) { Form_pg_type typeStruct = (Form_pg_type) GETSTRUCT(typeTup); Oid element_type; Oid base_type; Oid funcid; perm_fmgr_info(typeStruct->typinput, &arg->typfunc); arg->typoid = HeapTupleGetOid(typeTup); arg->typmod = -1; arg->typioparam = getTypeIOParam(typeTup); arg->typbyval = typeStruct->typbyval; element_type = get_base_element_type(arg->typoid); base_type = getBaseType(element_type ? element_type : arg->typoid); /* * Select a conversion function to convert Python objects to PostgreSQL * datums. */ if ((funcid = get_transform_tosql(base_type, langid, trftypes))) { arg->func = PLyObject_ToTransform; perm_fmgr_info(funcid, &arg->typtransform); } else if (typeStruct->typtype == TYPTYPE_COMPOSITE) { arg->func = PLyObject_ToComposite; } else switch (base_type) { case BOOLOID: arg->func = PLyObject_ToBool; break; case BYTEAOID: arg->func = PLyObject_ToBytea; break; default: arg->func = PLyObject_ToDatum; break; } if (element_type) { char dummy_delim; Oid funcid; if (type_is_rowtype(element_type)) arg->func = PLyObject_ToComposite; arg->elm = PLy_malloc0(sizeof(*arg->elm)); arg->elm->func = arg->func; arg->elm->typtransform = arg->typtransform; arg->func = PLySequence_ToArray; arg->elm->typoid = element_type; arg->elm->typmod = -1; get_type_io_data(element_type, IOFunc_input, &arg->elm->typlen, &arg->elm->typbyval, &arg->elm->typalign, &dummy_delim, &arg->elm->typioparam, &funcid); perm_fmgr_info(funcid, &arg->elm->typfunc); } }
static text* plvsubst_string(text *template_in, ArrayType *vals_in, text *c_subst, FunctionCallInfo fcinfo) { ArrayType *v = vals_in; int nitems, *dims, ndims; char *p; int16 typlen; bool typbyval; char typalign; char typdelim; Oid typelem; Oid typiofunc; FmgrInfo proc; int i = 0, items = 0; StringInfo sinfo; const char *template_str; int template_len; char *sizes; int *positions; int subst_mb_len; int subst_len; const bits8 *bitmap; int bitmask; if (v != NULL && (ndims = ARR_NDIM(v)) > 0) { if (ndims != 1) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("invalid parameter"), errdetail("Array of arguments has wrong dimension: %d", ndims))); p = ARR_DATA_PTR(v); dims = ARR_DIMS(v); nitems = ArrayGetNItems(ndims, dims); bitmap = ARR_NULLBITMAP(v); get_type_io_data(ARR_ELEMTYPE(v), IOFunc_output, &typlen, &typbyval, &typalign, &typdelim, &typelem, &typiofunc); fmgr_info_cxt(typiofunc, &proc, fcinfo->flinfo->fn_mcxt); } else { nitems = 0; p = NULL; bitmap = NULL; } template_str = VARDATA(template_in); template_len = ora_mb_strlen(template_in, &sizes, &positions); subst_mb_len = ora_mb_strlen1(c_subst); subst_len = VARSIZE_ANY_EXHDR(c_subst); sinfo = makeStringInfo(); bitmask = 1; for (i = 0; i < template_len; i++) { if (strncmp(&template_str[positions[i]], VARDATA(c_subst), subst_len) == 0) { Datum itemvalue; char *value; if (items++ < nitems) { if (bitmap && (*bitmap & bitmask) == 0) value = pstrdup("NULL"); else { itemvalue = fetch_att(p, typbyval, typlen); value = DatumGetCString(FunctionCall3(&proc, itemvalue, ObjectIdGetDatum(typelem), Int32GetDatum(-1))); p = att_addlength_pointer(p, typlen, p); p = (char *) att_align_nominal(p, typalign); } appendStringInfoString(sinfo, value); pfree(value); if (bitmap) { bitmask <<= 1; if (bitmask == 0x100) { bitmap++; bitmask = 1; } } } else ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("too few parameters specified for template string"))); i += subst_mb_len - 1; } else appendBinaryStringInfo(sinfo, &template_str[positions[i]], sizes[i]); } return cstring_to_text(sinfo->data); }