/* 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; }
/* * Generate a function call based on own signature. */ ProxyQuery * plproxy_standard_query(ProxyFunction *func, bool add_types) { StringInfoData sql; ProxyQuery *pq; const char *target; int i, len; pq = plproxy_func_alloc(func, sizeof(*pq)); pq->sql = NULL; pq->plan = NULL; pq->arg_count = func->arg_count; len = pq->arg_count * sizeof(int); pq->arg_lookup = plproxy_func_alloc(func, len); initStringInfo(&sql); appendStringInfo(&sql, "select "); /* try to fill in all result column names */ if (func->ret_composite) { ProxyComposite *t = func->ret_composite; for (i = 0; i < t->tupdesc->natts; i++) { appendStringInfo(&sql, "%s%s::%s", ((i > 0) ? ", " : ""), t->name_list[i], t->type_list[i]->name); } } else /* names not available, do a simple query */ appendStringInfo(&sql, "r::%s", func->ret_scalar->name); /* function call */ target = func->target_name ? func->target_name : func->name; appendStringInfo(&sql, " from %s(", target); /* fill in function arguments */ for (i = 0; i < func->arg_count; i++) { if (i > 0) appendStringInfoChar(&sql, ','); add_ref(&sql, i, func, i, add_types); pq->arg_lookup[i] = i; } appendStringInfoChar(&sql, ')'); /* * Untyped RECORD needs types specified in AS (..) clause. */ if (func->dynamic_record) { ProxyComposite *t = func->ret_composite; appendStringInfo(&sql, " as ("); for (i = 0; i < t->tupdesc->natts; i++) { appendStringInfo(&sql, "%s%s %s", ((i > 0) ? ", " : ""), t->name_list[i], t->type_list[i]->name); } appendStringInfoChar(&sql, ')'); } if (func->ret_scalar) appendStringInfo(&sql, " r"); pq->sql = plproxy_func_strdup(func, sql.data); pfree(sql.data); return pq; }
/* * Collects info about fields of a composite type. * * Based on TupleDescGetAttInMetadata. */ ProxyComposite * plproxy_composite_info(ProxyFunction *func, TupleDesc tupdesc) { int i, natts = tupdesc->natts; ProxyComposite *ret; MemoryContext old_ctx; Form_pg_attribute a; ProxyType *type; const char *name; Oid oid = tupdesc->tdtypeid; old_ctx = MemoryContextSwitchTo(func->ctx); ret = palloc(sizeof(*ret)); ret->type_list = palloc(sizeof(ProxyType *) * natts); ret->name_list = palloc0(sizeof(char *) * natts); ret->tupdesc = BlessTupleDesc(tupdesc); ret->use_binary = 1; ret->alterable = 0; if (oid != RECORDOID) { HeapTuple type_tuple; HeapTuple rel_tuple; Form_pg_type pg_type; type_tuple = SearchSysCache(TYPEOID, ObjectIdGetDatum(oid), 0, 0, 0); if (!HeapTupleIsValid(type_tuple)) elog(ERROR, "cache lookup failed for type %u", oid); pg_type = (Form_pg_type) GETSTRUCT(type_tuple); rel_tuple = SearchSysCache(RELOID, ObjectIdGetDatum(pg_type->typrelid), 0, 0, 0); if (!HeapTupleIsValid(rel_tuple)) elog(ERROR, "cache lookup failed for type relation %u", pg_type->typrelid); plproxy_set_stamp(&ret->stamp, rel_tuple); ReleaseSysCache(rel_tuple); ReleaseSysCache(type_tuple); ret->alterable = 1; if (ret->tupdesc->tdtypeid != oid) elog(ERROR, "lost oid"); } MemoryContextSwitchTo(old_ctx); ret->nfields = 0; for (i = 0; i < natts; i++) { a = TupleDescAttr(tupdesc, i); if (a->attisdropped) { ret->name_list[i] = NULL; ret->type_list[i] = NULL; continue; } ret->nfields++; name = quote_identifier(NameStr(a->attname)); ret->name_list[i] = plproxy_func_strdup(func, name); type = plproxy_find_type_info(func, a->atttypid, 0); ret->type_list[i] = type; if (!type->has_recv) ret->use_binary = 0; } return ret; }