/* * The PostgreSQL function & trigger manager calls this function * for execution of PL/Proxy procedures. * * Main entry point for rest of the code. */ Datum plproxy_call_handler(PG_FUNCTION_ARGS) { ProxyFunction *func; Datum ret; if (CALLED_AS_TRIGGER(fcinfo)) elog(ERROR, "PL/Proxy procedures can't be used as triggers"); /* clean old results */ if (!fcinfo->flinfo->fn_retset || SRF_IS_FIRSTCALL()) run_maint(); if (fcinfo->flinfo->fn_retset) { ret = handle_ret_set(fcinfo); } else { func = compile_and_execute(fcinfo); if (func->cur_cluster->ret_total != 1) plproxy_error(func, "Non-SETOF function requires 1 row from remote query, got %d", func->cur_cluster->ret_total); ret = plproxy_result(func, fcinfo); plproxy_clean_results(func->cur_cluster); } return ret; }
/* * Execute ProxyQuery locally. * * Result will be in SPI_tuptable. */ void plproxy_query_exec(ProxyFunction *func, FunctionCallInfo fcinfo, ProxyQuery *q, DatumArray **array_params, int array_row) { int i, idx, err; char arg_nulls[FUNC_MAX_ARGS]; Datum arg_values[FUNC_MAX_ARGS]; /* fill args */ for (i = 0; i < q->arg_count; i++) { idx = q->arg_lookup[i]; if (PG_ARGISNULL(idx)) { arg_nulls[i] = 'n'; arg_values[i] = (Datum) NULL; } else if (array_params && IS_SPLIT_ARG(func, idx)) { DatumArray *ats = array_params[idx]; arg_nulls[i] = ats->nulls[array_row] ? 'n' : ' '; arg_values[i] = ats->nulls[array_row] ? (Datum) NULL : ats->values[array_row]; } else { arg_nulls[i] = ' '; arg_values[i] = PG_GETARG_DATUM(idx); } } /* run query */ err = SPI_execute_plan(q->plan, arg_values, arg_nulls, true, 0); if (err != SPI_OK_SELECT) plproxy_error(func, "query '%s' failed: %s", q->sql, SPI_result_code_string(err)); }
/* * Do compilation and execution under SPI. * * Result conversion will be done without SPI. */ static ProxyFunction * compile_and_execute(FunctionCallInfo fcinfo) { int err; ProxyFunction *func; ProxyCluster *cluster; /* prepare SPI */ err = SPI_connect(); if (err != SPI_OK_CONNECT) elog(ERROR, "SPI_connect: %s", SPI_result_code_string(err)); /* do the initialization also under SPI */ plproxy_startup_init(); /* compile code */ func = plproxy_compile_and_cache(fcinfo); /* get actual cluster to run on */ cluster = plproxy_find_cluster(func, fcinfo); /* Don't allow nested calls on the same cluster */ if (cluster->busy) plproxy_error(func, "Nested PL/Proxy calls to the same cluster are not supported."); /* fetch PGresults */ func->cur_cluster = cluster; plproxy_exec(func, fcinfo); /* done with SPI */ err = SPI_finish(); if (err != SPI_OK_FINISH) elog(ERROR, "SPI_finish: %s", SPI_result_code_string(err)); return func; }
/* some error happened */ static void conn_error(ProxyFunction *func, ProxyConnection *conn, const char *desc) { plproxy_error(func, "%s: %s", desc, PQerrorMessage(conn->db)); }
/* 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; }