/* * getTypeBinaryInputInfo * * Get info needed for binary input of values of a type */ void getTypeBinaryInputInfo(Oid type, Oid *typReceive, Oid *typIOParam) { HeapTuple typeTuple; Form_pg_type pt; typeTuple = SearchSysCache(TYPEOID, ObjectIdGetDatum(type), 0, 0, 0); if (!HeapTupleIsValid(typeTuple)) elog(ERROR, "cache lookup failed for type %u", type); pt = (Form_pg_type) GETSTRUCT(typeTuple); if (!pt->typisdefined) ereport(ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT), errmsg("type %s is only a shell", format_type_be(type)))); if (!OidIsValid(pt->typreceive)) ereport(ERROR, (errcode(ERRCODE_UNDEFINED_FUNCTION), errmsg("no binary input function available for type %s", format_type_be(type)))); *typReceive = pt->typreceive; *typIOParam = getTypeIOParam(typeTuple); ReleaseSysCache(typeTuple); }
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_base_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)) arg->func = PLyObject_ToComposite; 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); } }
static plperl_proc_desc * compile_plperl_function(Oid fn_oid, bool is_trigger) { HeapTuple procTup; Form_pg_proc procStruct; char internal_proname[64]; int proname_len; plperl_proc_desc *prodesc = NULL; int i; SV **svp; /* We'll need the pg_proc tuple in any case... */ procTup = SearchSysCache(PROCOID, ObjectIdGetDatum(fn_oid), 0, 0, 0); if (!HeapTupleIsValid(procTup)) elog(ERROR, "cache lookup failed for function %u", fn_oid); procStruct = (Form_pg_proc) GETSTRUCT(procTup); /************************************************************ * Build our internal proc name from the functions Oid ************************************************************/ if (!is_trigger) sprintf(internal_proname, "__PLPerl_proc_%u", fn_oid); else sprintf(internal_proname, "__PLPerl_proc_%u_trigger", fn_oid); proname_len = strlen(internal_proname); /************************************************************ * Lookup the internal proc name in the hashtable ************************************************************/ svp = hv_fetch(plperl_proc_hash, internal_proname, proname_len, FALSE); if (svp) { bool uptodate; prodesc = (plperl_proc_desc *) SvIV(*svp); /************************************************************ * If it's present, must check whether it's still up to date. * This is needed because CREATE OR REPLACE FUNCTION can modify the * function's pg_proc entry without changing its OID. ************************************************************/ uptodate = (prodesc->fn_xmin == HeapTupleHeaderGetXmin(procTup->t_data) && prodesc->fn_cmin == HeapTupleHeaderGetCmin(procTup->t_data)); if (!uptodate) { /* need we delete old entry? */ prodesc = NULL; } } /************************************************************ * If we haven't found it in the hashtable, we analyze * the functions arguments and returntype and store * the in-/out-functions in the prodesc block and create * a new hashtable entry for it. * * Then we load the procedure into the Perl interpreter. ************************************************************/ if (prodesc == NULL) { HeapTuple langTup; HeapTuple typeTup; Form_pg_language langStruct; Form_pg_type typeStruct; Datum prosrcdatum; bool isnull; char *proc_source; /************************************************************ * Allocate a new procedure description block ************************************************************/ prodesc = (plperl_proc_desc *) malloc(sizeof(plperl_proc_desc)); if (prodesc == NULL) ereport(ERROR, (errcode(ERRCODE_OUT_OF_MEMORY), errmsg("out of memory"))); MemSet(prodesc, 0, sizeof(plperl_proc_desc)); prodesc->proname = strdup(internal_proname); prodesc->fn_xmin = HeapTupleHeaderGetXmin(procTup->t_data); prodesc->fn_cmin = HeapTupleHeaderGetCmin(procTup->t_data); /* Remember if function is STABLE/IMMUTABLE */ prodesc->fn_readonly = (procStruct->provolatile != PROVOLATILE_VOLATILE); /************************************************************ * Lookup the pg_language tuple by Oid ************************************************************/ langTup = SearchSysCache(LANGOID, ObjectIdGetDatum(procStruct->prolang), 0, 0, 0); if (!HeapTupleIsValid(langTup)) { free(prodesc->proname); free(prodesc); elog(ERROR, "cache lookup failed for language %u", procStruct->prolang); } langStruct = (Form_pg_language) GETSTRUCT(langTup); prodesc->lanpltrusted = langStruct->lanpltrusted; ReleaseSysCache(langTup); /************************************************************ * Get the required information for input conversion of the * return value. ************************************************************/ if (!is_trigger) { typeTup = SearchSysCache(TYPEOID, ObjectIdGetDatum(procStruct->prorettype), 0, 0, 0); if (!HeapTupleIsValid(typeTup)) { free(prodesc->proname); free(prodesc); elog(ERROR, "cache lookup failed for type %u", procStruct->prorettype); } typeStruct = (Form_pg_type) GETSTRUCT(typeTup); /* Disallow pseudotype result, except VOID or RECORD */ if (typeStruct->typtype == 'p') { if (procStruct->prorettype == VOIDOID || procStruct->prorettype == RECORDOID) /* okay */ ; else if (procStruct->prorettype == TRIGGEROID) { free(prodesc->proname); free(prodesc); ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("trigger functions may only be called " "as triggers"))); } else { free(prodesc->proname); free(prodesc); ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("plperl functions cannot return type %s", format_type_be(procStruct->prorettype)))); } } prodesc->result_oid = procStruct->prorettype; prodesc->fn_retisset = procStruct->proretset; prodesc->fn_retistuple = (typeStruct->typtype == 'c' || procStruct->prorettype == RECORDOID); prodesc->fn_retisarray = (typeStruct->typlen == -1 && typeStruct->typelem); perm_fmgr_info(typeStruct->typinput, &(prodesc->result_in_func)); prodesc->result_typioparam = getTypeIOParam(typeTup); ReleaseSysCache(typeTup); } /************************************************************ * Get the required information for output conversion * of all procedure arguments ************************************************************/ if (!is_trigger) { prodesc->nargs = procStruct->pronargs; for (i = 0; i < prodesc->nargs; i++) { typeTup = SearchSysCache(TYPEOID, ObjectIdGetDatum(procStruct->proargtypes.values[i]), 0, 0, 0); if (!HeapTupleIsValid(typeTup)) { free(prodesc->proname); free(prodesc); elog(ERROR, "cache lookup failed for type %u", procStruct->proargtypes.values[i]); } typeStruct = (Form_pg_type) GETSTRUCT(typeTup); /* Disallow pseudotype argument */ if (typeStruct->typtype == 'p') { free(prodesc->proname); free(prodesc); ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("plperl functions cannot take type %s", format_type_be(procStruct->proargtypes.values[i])))); } if (typeStruct->typtype == 'c') prodesc->arg_is_rowtype[i] = true; else { prodesc->arg_is_rowtype[i] = false; perm_fmgr_info(typeStruct->typoutput, &(prodesc->arg_out_func[i])); } ReleaseSysCache(typeTup); } } /************************************************************ * create the text of the anonymous subroutine. * we do not use a named subroutine so that we can call directly * through the reference. ************************************************************/ prosrcdatum = SysCacheGetAttr(PROCOID, procTup, Anum_pg_proc_prosrc, &isnull); if (isnull) elog(ERROR, "null prosrc"); proc_source = DatumGetCString(DirectFunctionCall1(textout, prosrcdatum)); /************************************************************ * Create the procedure in the interpreter ************************************************************/ prodesc->reference = plperl_create_sub(proc_source, prodesc->lanpltrusted); pfree(proc_source); if (!prodesc->reference) /* can this happen? */ { free(prodesc->proname); free(prodesc); elog(ERROR, "could not create internal procedure \"%s\"", internal_proname); } hv_store(plperl_proc_hash, internal_proname, proname_len, newSViv((IV) prodesc), 0); } ReleaseSysCache(procTup); return prodesc; }
/* Look up all of the information that SerializeTuple() and DeserializeTuple() * need to perform their jobs quickly. Also, scratchpad space is allocated * for serialization and desrialization of datum values, and for formation/ * deformation of tuples themselves. * * NOTE: This function allocates various data-structures, but it assumes that * the current memory-context is acceptable. So the caller should set * the desired memory-context before calling this function. */ void InitSerTupInfo(TupleDesc tupdesc, SerTupInfo * pSerInfo) { int i, numAttrs; AssertArg(tupdesc != NULL); AssertArg(pSerInfo != NULL); if (s_tupSerMemCtxt == NULL) { /* Create tuple-serialization memory context. */ s_tupSerMemCtxt = AllocSetContextCreate(TopMemoryContext, "TupSerMemCtxt", ALLOCSET_DEFAULT_INITSIZE, /* always have some memory */ ALLOCSET_DEFAULT_INITSIZE, ALLOCSET_DEFAULT_MAXSIZE); } /* Set contents to all 0, just to make things clean and easy. */ memset(pSerInfo, 0, sizeof(SerTupInfo)); /* Store the tuple-descriptor so we can use it later. */ pSerInfo->tupdesc = tupdesc; pSerInfo->chunkCache.len = 0; pSerInfo->chunkCache.items = NULL; /* * If we have some attributes, go ahead and prepare the information for * each attribute in the descriptor. Otherwise, we can return right away. */ numAttrs = tupdesc->natts; if (numAttrs <= 0) return; pSerInfo->myinfo = (SerAttrInfo *) palloc0(numAttrs * sizeof(SerAttrInfo)); pSerInfo->values = (Datum *) palloc(numAttrs * sizeof(Datum)); pSerInfo->nulls = (bool *) palloc(numAttrs * sizeof(bool)); for (i = 0; i < numAttrs; i++) { SerAttrInfo *attrInfo = pSerInfo->myinfo + i; /* * Get attribute's data-type Oid. This lets us shortcut the comm * operations for some attribute-types. */ attrInfo->atttypid = tupdesc->attrs[i]->atttypid; /* * Ok, we want the Binary input/output routines for the type if they exist, * else we want the normal text input/output routines. * * User defined types might or might not have binary routines. * * getTypeBinaryOutputInfo throws an error if we try to call it to get * the binary output routine and one doesn't exist, so let's not call that. */ { HeapTuple typeTuple; Form_pg_type pt; typeTuple = SearchSysCache1(TYPEOID, ObjectIdGetDatum(attrInfo->atttypid)); if (!HeapTupleIsValid(typeTuple)) elog(ERROR, "cache lookup failed for type %u", attrInfo->atttypid); pt = (Form_pg_type) GETSTRUCT(typeTuple); if (!pt->typisdefined) ereport(ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT), errmsg("type %s is only a shell", format_type_be(attrInfo->atttypid)))); /* If we don't have both binary routines */ if (!OidIsValid(pt->typsend) || !OidIsValid(pt->typreceive)) { /* Use the normal text routines (slower) */ if (!OidIsValid(pt->typoutput)) ereport(ERROR, (errcode(ERRCODE_UNDEFINED_FUNCTION), errmsg("no output function available for type %s", format_type_be(attrInfo->atttypid)))); if (!OidIsValid(pt->typinput)) ereport(ERROR, (errcode(ERRCODE_UNDEFINED_FUNCTION), errmsg("no input function available for type %s", format_type_be(attrInfo->atttypid)))); attrInfo->typsend = pt->typoutput; attrInfo->send_typio_param = getTypeIOParam(typeTuple); attrInfo->typisvarlena = (!pt->typbyval) && (pt->typlen == -1); attrInfo->typrecv = pt->typinput; attrInfo->recv_typio_param = getTypeIOParam(typeTuple); } else { /* Use binary routines */ attrInfo->typsend = pt->typsend; attrInfo->send_typio_param = getTypeIOParam(typeTuple); attrInfo->typisvarlena = (!pt->typbyval) && (pt->typlen == -1); attrInfo->typrecv = pt->typreceive; attrInfo->recv_typio_param = getTypeIOParam(typeTuple); } ReleaseSysCache(typeTuple); } fmgr_info(attrInfo->typsend, &attrInfo->send_finfo); fmgr_info(attrInfo->typrecv, &attrInfo->recv_finfo); #ifdef TUPSER_SCRATCH_SPACE /* * If the field is a varlena, allocate some space to use for * deserializing it. If most of the values are smaller than this * scratch-space then we save time on allocation and freeing. */ attrInfo->pv_varlen_scratch = palloc(VARLEN_SCRATCH_SIZE); attrInfo->varlen_scratch_size = VARLEN_SCRATCH_SIZE; #endif } }
Datum pl_bf_call_handler(PG_FUNCTION_ARGS) { FmgrInfo flinfo; Datum retval, proc_source_datum; Form_pg_proc procStruct; Form_pg_type typeStruct; HeapTuple procTup; HeapTuple typeTup; bool isnull; Oid resultTypeIOParam, returnTypeOID; char *proc_source; char *a, *p; int i; // Get the function tuple procTup = SearchSysCache(PROCOID, ObjectIdGetDatum(fcinfo->flinfo->fn_oid), 0, 0, 0); if (!HeapTupleIsValid(procTup)) { elog(ERROR, "Cache lookup failed for procedure %u", fcinfo->flinfo->fn_oid); } procStruct = (Form_pg_proc)GETSTRUCT(procTup); // Get the function source proc_source_datum = SysCacheGetAttr(PROCOID, procTup, Anum_pg_proc_prosrc, &isnull); if (isnull) { elog(ERROR, "Function source is null"); } proc_source = DatumGetCString(DirectFunctionCall1(textout, proc_source_datum)); // Get the return type tuple typeTup = SearchSysCache(TYPEOID, ObjectIdGetDatum(procStruct->prorettype), 0, 0, 0); if (!HeapTupleIsValid(typeTup)) { elog(ERROR, "Cache lookup failed for type %u", procStruct->prorettype); } typeStruct = (Form_pg_type)GETSTRUCT(typeTup); resultTypeIOParam = getTypeIOParam(typeTup); returnTypeOID = procStruct -> prorettype; a = calloc(5000, 1); if (!a) { elog(ERROR, "BAD A!"); } p = a; for (i = 0; i < procStruct->pronargs; i++) { p += append_datum(p, fcinfo->arg[i], fcinfo->argnull[i], procStruct->proargtypes.values[i]); } interpret(a, 0, proc_source); fmgr_info_cxt(typeStruct->typinput, &flinfo, TopMemoryContext); if (returnTypeOID != VOIDOID) { if (returnTypeOID == INT4OID) { retval = Int32GetDatum(*((int*)a)); } else { SPI_push(); if (returnTypeOID == BOOLOID) retval = InputFunctionCall(&flinfo, a[0] ? "TRUE" : "FALSE", resultTypeIOParam, -1); else { retval = InputFunctionCall(&flinfo, pstrdup(a), resultTypeIOParam, -1); } SPI_pop(); } } ReleaseSysCache(procTup); ReleaseSysCache(typeTup); free(a); SPI_finish(); return retval; }
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, 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); } }
/* * get_typdefault * Given a type OID, return the type's default value, if any. * * The result is a palloc'd expression node tree, or NULL if there * is no defined default for the datatype. * * NB: caller should be prepared to coerce result to correct datatype; * the returned expression tree might produce something of the wrong type. */ Node * get_typdefault(Oid typid) { HeapTuple typeTuple; Form_pg_type type; Datum datum; bool isNull; Node *expr; typeTuple = SearchSysCache(TYPEOID, ObjectIdGetDatum(typid), 0, 0, 0); if (!HeapTupleIsValid(typeTuple)) elog(ERROR, "cache lookup failed for type %u", typid); type = (Form_pg_type) GETSTRUCT(typeTuple); /* * typdefault and typdefaultbin are potentially null, so don't try to * access 'em as struct fields. Must do it the hard way with * SysCacheGetAttr. */ datum = SysCacheGetAttr(TYPEOID, typeTuple, Anum_pg_type_typdefaultbin, &isNull); if (!isNull) { /* We have an expression default */ expr = stringToNode(DatumGetCString(DirectFunctionCall1(textout, datum))); } else { /* Perhaps we have a plain literal default */ datum = SysCacheGetAttr(TYPEOID, typeTuple, Anum_pg_type_typdefault, &isNull); if (!isNull) { char *strDefaultVal; /* Convert text datum to C string */ strDefaultVal = DatumGetCString(DirectFunctionCall1(textout, datum)); /* Convert C string to a value of the given type */ datum = OidInputFunctionCall(type->typinput, strDefaultVal, getTypeIOParam(typeTuple), -1); /* Build a Const node containing the value */ expr = (Node *) makeConst(typid, type->typlen, datum, false, type->typbyval); pfree(strDefaultVal); } else { /* No default */ expr = NULL; } } ReleaseSysCache(typeTuple); return expr; }
/* * get_type_io_data * * A six-fer: given the type OID, return typlen, typbyval, typalign, * typdelim, typioparam, and IO function OID. The IO function * returned is controlled by IOFuncSelector */ void get_type_io_data(Oid typid, IOFuncSelector which_func, int16 *typlen, bool *typbyval, char *typalign, char *typdelim, Oid *typioparam, Oid *func) { HeapTuple typeTuple; Form_pg_type typeStruct; /* * In bootstrap mode, pass it off to bootstrap.c. This hack allows us to * use array_in and array_out during bootstrap. */ if (IsBootstrapProcessingMode()) { Oid typinput; Oid typoutput; boot_get_type_io_data(typid, typlen, typbyval, typalign, typdelim, typioparam, &typinput, &typoutput); switch (which_func) { case IOFunc_input: *func = typinput; break; case IOFunc_output: *func = typoutput; break; default: elog(ERROR, "binary I/O not supported during bootstrap"); break; } return; } typeTuple = SearchSysCache(TYPEOID, ObjectIdGetDatum(typid), 0, 0, 0); if (!HeapTupleIsValid(typeTuple)) elog(ERROR, "cache lookup failed for type %u", typid); typeStruct = (Form_pg_type) GETSTRUCT(typeTuple); *typlen = typeStruct->typlen; *typbyval = typeStruct->typbyval; *typalign = typeStruct->typalign; *typdelim = typeStruct->typdelim; *typioparam = getTypeIOParam(typeTuple); switch (which_func) { case IOFunc_input: *func = typeStruct->typinput; break; case IOFunc_output: *func = typeStruct->typoutput; break; case IOFunc_receive: *func = typeStruct->typreceive; break; case IOFunc_send: *func = typeStruct->typsend; break; } ReleaseSysCache(typeTuple); }
/* Find info about scalar type */ ProxyType * plproxy_find_type_info(ProxyFunction *func, Oid oid, bool for_send) { ProxyType *type; HeapTuple t_type, t_nsp; Form_pg_type s_type; Form_pg_namespace s_nsp; char namebuf[NAMEDATALEN * 4 + 2 + 1 + 2 + 1]; Oid nsoid; /* fetch pg_type row */ t_type = SearchSysCache(TYPEOID, ObjectIdGetDatum(oid), 0, 0, 0); if (!HeapTupleIsValid(t_type)) plproxy_error(func, "cache lookup failed for type %u", oid); /* typname, typnamespace, PG_CATALOG_NAMESPACE, PG_PUBLIC_NAMESPACE */ s_type = (Form_pg_type) GETSTRUCT(t_type); nsoid = s_type->typnamespace; if (nsoid != PG_CATALOG_NAMESPACE) { t_nsp = SearchSysCache(NAMESPACEOID, ObjectIdGetDatum(nsoid), 0, 0, 0); if (!HeapTupleIsValid(t_nsp)) plproxy_error(func, "cache lookup failed for namespace %u", nsoid); s_nsp = (Form_pg_namespace) GETSTRUCT(t_nsp); snprintf(namebuf, sizeof(namebuf), "%s.%s", quote_identifier(NameStr(s_nsp->nspname)), quote_identifier(NameStr(s_type->typname))); ReleaseSysCache(t_nsp); } else { snprintf(namebuf, sizeof(namebuf), "%s", quote_identifier(NameStr(s_type->typname))); } /* sanity check */ switch (s_type->typtype) { default: plproxy_error(func, "unsupported type code: %s (%u)", namebuf, oid); break; case TYPTYPE_PSEUDO: if (oid != VOIDOID) plproxy_error(func, "unsupported pseudo type: %s (%u)", namebuf, oid); break; case TYPTYPE_BASE: case TYPTYPE_COMPOSITE: case TYPTYPE_DOMAIN: case TYPTYPE_ENUM: case TYPTYPE_RANGE: break; } /* allocate & fill structure */ type = plproxy_func_alloc(func, sizeof(*type)); memset(type, 0, sizeof(*type)); type->type_oid = oid; type->io_param = getTypeIOParam(t_type); type->for_send = for_send; type->by_value = s_type->typbyval; type->name = plproxy_func_strdup(func, namebuf); type->is_array = (s_type->typelem != 0 && s_type->typlen == -1); type->elem_type_oid = s_type->typelem; type->elem_type_t = NULL; type->alignment = s_type->typalign; type->length = s_type->typlen; /* decide what function is needed */ if (for_send) { fmgr_info_cxt(s_type->typoutput, &type->io.out.output_func, func->ctx); if (OidIsValid(s_type->typsend) && usable_binary(oid)) { fmgr_info_cxt(s_type->typsend, &type->io.out.send_func, func->ctx); type->has_send = 1; } } else { fmgr_info_cxt(s_type->typinput, &type->io.in.input_func, func->ctx); if (OidIsValid(s_type->typreceive) && usable_binary(oid)) { fmgr_info_cxt(s_type->typreceive, &type->io.in.recv_func, func->ctx); type->has_recv = 1; } } ReleaseSysCache(t_type); return type; }