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); } }
/* * Create a new PLyProcedure structure */ static PLyProcedure * PLy_procedure_create(HeapTuple procTup, Oid fn_oid, bool is_trigger) { char procName[NAMEDATALEN + 256]; Form_pg_proc procStruct; PLyProcedure *volatile proc; char *volatile procSource = NULL; Datum prosrcdatum; bool isnull; int i, rv; procStruct = (Form_pg_proc) GETSTRUCT(procTup); rv = snprintf(procName, sizeof(procName), "__plpython_procedure_%s_%u", NameStr(procStruct->proname), fn_oid); if (rv >= sizeof(procName) || rv < 0) elog(ERROR, "procedure name would overrun buffer"); proc = PLy_malloc(sizeof(PLyProcedure)); proc->proname = PLy_strdup(NameStr(procStruct->proname)); proc->pyname = PLy_strdup(procName); proc->fn_xmin = HeapTupleHeaderGetRawXmin(procTup->t_data); proc->fn_tid = procTup->t_self; /* Remember if function is STABLE/IMMUTABLE */ proc->fn_readonly = (procStruct->provolatile != PROVOLATILE_VOLATILE); PLy_typeinfo_init(&proc->result); for (i = 0; i < FUNC_MAX_ARGS; i++) PLy_typeinfo_init(&proc->args[i]); proc->nargs = 0; proc->code = proc->statics = NULL; proc->globals = NULL; proc->is_setof = procStruct->proretset; proc->setof = NULL; proc->src = NULL; proc->argnames = NULL; PG_TRY(); { /* * get information required for output conversion of the return value, * but only if this isn't a trigger. */ if (!is_trigger) { HeapTuple rvTypeTup; Form_pg_type rvTypeStruct; rvTypeTup = SearchSysCache1(TYPEOID, ObjectIdGetDatum(procStruct->prorettype)); if (!HeapTupleIsValid(rvTypeTup)) elog(ERROR, "cache lookup failed for type %u", procStruct->prorettype); rvTypeStruct = (Form_pg_type) GETSTRUCT(rvTypeTup); /* Disallow pseudotype result, except for void or record */ if (rvTypeStruct->typtype == TYPTYPE_PSEUDO) { if (procStruct->prorettype == TRIGGEROID) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("trigger functions can only be called as triggers"))); else if (procStruct->prorettype != VOIDOID && procStruct->prorettype != RECORDOID) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("PL/Python functions cannot return type %s", format_type_be(procStruct->prorettype)))); } if (rvTypeStruct->typtype == TYPTYPE_COMPOSITE || procStruct->prorettype == RECORDOID) { /* * Tuple: set up later, during first call to * PLy_function_handler */ proc->result.out.d.typoid = procStruct->prorettype; proc->result.out.d.typmod = -1; proc->result.is_rowtype = 2; } else { /* do the real work */ PLy_output_datum_func(&proc->result, rvTypeTup); } ReleaseSysCache(rvTypeTup); } /* * Now get information required for input conversion of the * procedure's arguments. Note that we ignore output arguments here. * If the function returns record, those I/O functions will be set up * when the function is first called. */ if (procStruct->pronargs) { Oid *types; char **names, *modes; int i, pos, total; /* extract argument type info from the pg_proc tuple */ total = get_func_arg_info(procTup, &types, &names, &modes); /* count number of in+inout args into proc->nargs */ if (modes == NULL) proc->nargs = total; else { /* proc->nargs was initialized to 0 above */ for (i = 0; i < total; i++) { if (modes[i] != PROARGMODE_OUT && modes[i] != PROARGMODE_TABLE) (proc->nargs)++; } } proc->argnames = (char **) PLy_malloc0(sizeof(char *) * proc->nargs); for (i = pos = 0; i < total; i++) { HeapTuple argTypeTup; Form_pg_type argTypeStruct; if (modes && (modes[i] == PROARGMODE_OUT || modes[i] == PROARGMODE_TABLE)) continue; /* skip OUT arguments */ Assert(types[i] == procStruct->proargtypes.values[pos]); argTypeTup = SearchSysCache1(TYPEOID, ObjectIdGetDatum(types[i])); if (!HeapTupleIsValid(argTypeTup)) elog(ERROR, "cache lookup failed for type %u", types[i]); argTypeStruct = (Form_pg_type) GETSTRUCT(argTypeTup); /* check argument type is OK, set up I/O function info */ switch (argTypeStruct->typtype) { case TYPTYPE_PSEUDO: /* Disallow pseudotype argument */ ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("PL/Python functions cannot accept type %s", format_type_be(types[i])))); break; case TYPTYPE_COMPOSITE: /* we'll set IO funcs at first call */ proc->args[pos].is_rowtype = 2; break; default: PLy_input_datum_func(&(proc->args[pos]), types[i], argTypeTup); break; } /* get argument name */ proc->argnames[pos] = names ? PLy_strdup(names[i]) : NULL; ReleaseSysCache(argTypeTup); pos++; } } /* * get the text of the function. */ prosrcdatum = SysCacheGetAttr(PROCOID, procTup, Anum_pg_proc_prosrc, &isnull); if (isnull) elog(ERROR, "null prosrc"); procSource = TextDatumGetCString(prosrcdatum); PLy_procedure_compile(proc, procSource); pfree(procSource); procSource = NULL; } PG_CATCH(); { PLy_procedure_delete(proc); if (procSource) pfree(procSource); PG_RE_THROW(); } PG_END_TRY(); return proc; }
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); } }
void PLy_output_tuple_funcs(PLyTypeInfo *arg, TupleDesc desc) { int i; if (arg->is_rowtype == 0) elog(ERROR, "PLyTypeInfo struct is initialized for a Datum"); arg->is_rowtype = 1; if (arg->out.r.natts != desc->natts) { if (arg->out.r.atts) PLy_free(arg->out.r.atts); arg->out.r.natts = desc->natts; arg->out.r.atts = PLy_malloc0(desc->natts * sizeof(PLyDatumToOb)); } Assert(OidIsValid(desc->tdtypeid)); /* * RECORDOID means we got called to create output functions for an * anonymous record type */ if (desc->tdtypeid != RECORDOID) { HeapTuple relTup; /* Get the pg_class tuple corresponding to the type of the output */ arg->typ_relid = typeidTypeRelid(desc->tdtypeid); relTup = SearchSysCache1(RELOID, ObjectIdGetDatum(arg->typ_relid)); if (!HeapTupleIsValid(relTup)) elog(ERROR, "cache lookup failed for relation %u", arg->typ_relid); /* Remember XMIN and TID for later validation if cache is still OK */ arg->typrel_xmin = HeapTupleHeaderGetXmin(relTup->t_data); arg->typrel_tid = relTup->t_self; ReleaseSysCache(relTup); } for (i = 0; i < desc->natts; i++) { HeapTuple typeTup; if (desc->attrs[i]->attisdropped) continue; if (arg->out.r.atts[i].typoid == desc->attrs[i]->atttypid) continue; /* already set up this entry */ typeTup = SearchSysCache1(TYPEOID, ObjectIdGetDatum(desc->attrs[i]->atttypid)); if (!HeapTupleIsValid(typeTup)) elog(ERROR, "cache lookup failed for type %u", desc->attrs[i]->atttypid); PLy_output_datum_func2(&(arg->out.r.atts[i]), typeTup); ReleaseSysCache(typeTup); } }
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); } }
void PLy_input_tuple_funcs(PLyTypeInfo *arg, TupleDesc desc) { int i; PLyExecutionContext *exec_ctx = PLy_current_execution_context(); if (arg->is_rowtype == 0) elog(ERROR, "PLyTypeInfo struct is initialized for a Datum"); arg->is_rowtype = 1; if (arg->in.r.natts != desc->natts) { if (arg->in.r.atts) PLy_free(arg->in.r.atts); arg->in.r.natts = desc->natts; arg->in.r.atts = PLy_malloc0(desc->natts * sizeof(PLyDatumToOb)); } /* Can this be an unnamed tuple? If not, then an Assert would be enough */ if (desc->tdtypmod != -1) elog(ERROR, "received unnamed record type as input"); Assert(OidIsValid(desc->tdtypeid)); /* * RECORDOID means we got called to create input functions for a tuple * fetched by plpy.execute or for an anonymous record type */ if (desc->tdtypeid != RECORDOID) { HeapTuple relTup; /* Get the pg_class tuple corresponding to the type of the input */ arg->typ_relid = typeidTypeRelid(desc->tdtypeid); relTup = SearchSysCache1(RELOID, ObjectIdGetDatum(arg->typ_relid)); if (!HeapTupleIsValid(relTup)) elog(ERROR, "cache lookup failed for relation %u", arg->typ_relid); /* Remember XMIN and TID for later validation if cache is still OK */ arg->typrel_xmin = HeapTupleHeaderGetRawXmin(relTup->t_data); arg->typrel_tid = relTup->t_self; ReleaseSysCache(relTup); } for (i = 0; i < desc->natts; i++) { HeapTuple typeTup; if (desc->attrs[i]->attisdropped) continue; if (arg->in.r.atts[i].typoid == desc->attrs[i]->atttypid) continue; /* already set up this entry */ typeTup = SearchSysCache1(TYPEOID, ObjectIdGetDatum(desc->attrs[i]->atttypid)); if (!HeapTupleIsValid(typeTup)) elog(ERROR, "cache lookup failed for type %u", desc->attrs[i]->atttypid); PLy_input_datum_func2(&(arg->in.r.atts[i]), desc->attrs[i]->atttypid, typeTup, exec_ctx->curr_proc->langid, exec_ctx->curr_proc->trftypes); ReleaseSysCache(typeTup); } }