Beispiel #1
0
static PyObj
func_new_from_oid(PyTypeObject *subtype, Oid fn_oid, PyObj fn_oid_int, PyObj fn_oid_str)
{
	volatile HeapTuple ht = NULL;
	volatile PyObj rob = NULL;

	Assert(OidIsValid(fn_oid));
	Assert(fn_oid_int != NULL);
	Assert(fn_oid_str != NULL);

	rob = subtype->tp_alloc(subtype, 0);
	if (rob == NULL)
		return(NULL);

	PyPgFunction_SetOid(rob, fn_oid);
	PyPgFunction_SetStateful(rob, false);

	Py_INCREF(fn_oid_int);
	Py_INCREF(fn_oid_str);

	PyPgFunction_SetPyLongOid(rob, fn_oid_int);
	PyPgFunction_SetPyUnicodeOid(rob, fn_oid_str);

	/*
	 * Collect the Function information from the system cache
	 */
	PG_TRY();
	{
		Form_pg_proc ps;
		Form_pg_namespace ns;
		FmgrInfo flinfo;
		text *prosrc;
		Datum prosrc_datum;
		bool isnull = true;
		const char *filename = NULL, *nspname, *q_nspname;
		TupleDesc argdesc = NULL, result_desc = NULL;
		Oid prorettype = InvalidOid;
		PyObj id_str_ob = NULL, nspname_str_ob = NULL;
		PyObj filename_str_ob = NULL, q_nspname_str_ob = NULL;
		PyObj output = NULL, src = NULL;
		PyObj input;

		ht = SearchSysCache(PROCOID, fn_oid, 0, 0, 0);
		if (!HeapTupleIsValid(ht))
		{
			ereport(ERROR,(
				errcode(ERRCODE_UNDEFINED_FUNCTION),
				errmsg("failed to find function at oid %d", fn_oid)
			));
		}

		PyPgFunction_SetXMin(rob, HeapTupleHeaderGetXmin(ht->t_data));
		PyPgFunction_SetItemPointer(rob, &(ht->t_self));

		ps = (Form_pg_proc) GETSTRUCT(ht);

		PyPgFunction_SetNamespace(rob, ps->pronamespace);
		PyPgFunction_SetLanguage(rob, ps->prolang);
		PyPgFunction_SetReturnsSet(rob, ps->proretset);
		PyPgFunction_SetVolatile(rob, ps->provolatile);

		prorettype = ps->prorettype;
		prosrc_datum = SysCacheGetAttr(
			PROCOID, ht, Anum_pg_proc_prosrc, &isnull);
		if (!isnull)
		{
			prosrc = DatumGetTextPCopy(prosrc_datum);
			src = PyUnicode_FromTEXT(prosrc);
			PyPgFunction_SetSource(rob, src);
			pfree(prosrc);
			prosrc = NULL;
		}
		else
		{
			src = Py_None;
			Py_INCREF(src);
			PyPgFunction_SetSource(rob, src);
		}
		if (src == NULL)
			PyErr_RelayException();

		/*
		 * Get the function's address.
		 */
		fmgr_info(fn_oid, &flinfo);
		PyPgFunction_SetPGFunction(rob, flinfo.fn_addr);

		/*
		 * Build function parameters TupleDesc
		 */
		if (ps->pronargs > 0)
		{
			argdesc = TupleDesc_From_pg_proc_arginfo(ht);
			input = PyPgTupleDesc_FromCopy(argdesc);
			if (input == NULL)
				PyErr_RelayException();
			PyPgFunction_SetInput(rob, input);
			FreeTupleDesc(argdesc);
		}
		else
		{
			Py_INCREF(EmptyPyPgTupleDesc);
			PyPgFunction_SetInput(rob, EmptyPyPgTupleDesc);
		}

		/*
		 * If it's a registered composite,
		 * PyPgType_FromOid will resolve that below.
		 */
		if (prorettype == RECORDOID)
		{
			/*
			 * Otherwise, build out a function result tupdesc.
			 */
			result_desc = build_function_result_tupdesc_t(ht);
			if (result_desc != NULL)
			{
				/*
				 * Anonymous composite returned by function.
				 */
				output = PyPgType_FromTupleDesc(result_desc);
				PyPgFunction_SetOutput(rob, output);
				FreeTupleDesc(result_desc);
				/*
				 * We will certainly be using it, so bless it right now iff
				 * it's *not* polymorphic.
				 */
				if (output && !PyPgType_IsPolymorphic(output))
					BlessTupleDesc(PyPgType_GetTupleDesc(output));
			}
			else
			{
				/*
				 * ew..
				 */
				goto lookup_output_type;
			}
		}
		else
		{
lookup_output_type:
			output = PyPgType_FromOid(prorettype);
			if (output == NULL)
				PyErr_RelayException();
			PyPgFunction_SetOutput(rob, output);
		}

		RELEASESYSCACHE(&ht);

		/*
		 * Don't worry *too* much about leaking memory.
		 */
		filename = format_procedure(fn_oid);
		Assert(filename != NULL);

		ht = SearchSysCache(NAMESPACEOID,
							PyPgFunction_GetNamespace(rob), 0, 0, 0);
		if (!HeapTupleIsValid(ht))
		{
			pfree((char *) filename);
			elog(ERROR, "function %u namespace %u does not exist",
						fn_oid, PyPgFunction_GetNamespace(rob));
		}

		ns = (Form_pg_namespace) GETSTRUCT(ht);
		nspname = pstrdup(NameStr(ns->nspname));
		RELEASESYSCACHE(&ht);

		/*
		 * Build the filename string.
		 */
		q_nspname = quote_identifier(nspname);

		nspname_str_ob = PyUnicode_FromCString(nspname);
		PyPgFunction_SetNamespaceName(rob, nspname_str_ob);
		if (nspname_str_ob == NULL)
		{
			/*
			 * Invalid encoded string?
			 */
			if (nspname != q_nspname)
				pfree((char *) q_nspname);
			pfree((char *) nspname);
			PyErr_RelayException();
		}

		q_nspname_str_ob = PyUnicode_FromCString(q_nspname);
		if (nspname != q_nspname)
			pfree((char *) q_nspname);
		pfree((char *) nspname);
		/*
		 * Ignore the potential exception for a moment.
		 */
		id_str_ob = PyUnicode_FromCString(filename);

		/*
		 * Skip the filename_str_ob if either of the above failed.
		 */
		if (id_str_ob != NULL && q_nspname_str_ob != NULL)
		{
			if (FunctionIsVisible(fn_oid))
				filename_str_ob = PyUnicode_FromFormat("%U.%U", q_nspname_str_ob, id_str_ob);
			else
			{
				filename_str_ob = id_str_ob;
				Py_INCREF(id_str_ob);
			}
		}
		PyPgFunction_SetFilename(rob, filename_str_ob);
		Py_XDECREF(q_nspname_str_ob);
		Py_XDECREF(id_str_ob);

		pfree((char *) filename);

		if (filename_str_ob == NULL)
			PyErr_RelayException();
	}
	PG_CATCH();
	{
		Py_XDECREF(rob);
		rob = NULL;

		PyErr_SetPgError(false);

		if (ht != NULL)
			ReleaseSysCache(ht);
	}
	PG_END_TRY();

	return(rob);
}
Beispiel #2
0
/*
 * Polymorph the polymorphic types in the TupleDesc to the appropriate type
 * related to the 'target' base type.
 *
 * Polymorphic types are pseudo types and thus Postgres won't store them,
 * so 'self' will never be a relation type.
 */
PyObj
PyPgTupleDesc_Polymorph(PyObj self, PyObj target)
{
	int i;
	TupleDesc td;
	PyObj td_types, rob;
	MemoryContext former;

	Assert(PyPgTupleDesc_CheckExact(self));
	Assert(PyPgTupleDesc_GetPolymorphic(self) != -1);
	Assert(PyPgType_Check(target));
	Assert(!PyPgType_IsPolymorphic(target));

	td_types = PyPgTupleDesc_GetTypesTuple(self);
	td = PyPgTupleDesc_GetTupleDesc(self);

	/*
	 * We need to update any polymorphic attributes, so
	 * grab a copy.
	 */
	former = MemoryContextSwitchTo(PythonMemoryContext);
	if (td->constr != NULL)
		td = Py_CreateTupleDescCopyConstr(td);
	else
		td = Py_CreateTupleDescCopy(td);
	MemoryContextSwitchTo(former);
	if (td == NULL)
		return(NULL);

	for (i = 0; i < td->natts; ++i)
	{
		PyObj polymorphic_type, polymorphed_type;
		PyPgTypeInfo typinfo;

		if (!IsPolymorphicType(td->attrs[i]->atttypid))
			continue;

		polymorphic_type = PyTuple_GET_ITEM(td_types, i);
		polymorphed_type = PyPgType_Polymorph(polymorphic_type, target);
		if (polymorphed_type == NULL)
		{
			Py_FreeTupleDesc(td);
			return(NULL);
		}

		typinfo = PyPgTypeInfo(polymorphed_type);
		/*
		 * It's not a pseudo type anymore; change the oid.
		 */
		td->attrs[i]->atttypid = typinfo->typoid;
		td->attrs[i]->attalign = typinfo->typalign;
		td->attrs[i]->attbyval = typinfo->typbyval;
		td->attrs[i]->attlen = typinfo->typlen;

		/*
		 * Done with that type for now.
		 * PyPgTupleDesc_New() will grab a new reference when it builds the types
		 * tuple.
		 */
		Py_DECREF(polymorphed_type);
	}

	/*
	 * Make and return the PyPgTupleDesc object..
	 */
	rob = PyPgTupleDesc_New(td);
	Assert(PyPgTupleDesc_GetPolymorphic(rob) == -1);

	return(rob);
}
Beispiel #3
0
static PyObj
func_call(PyObj self, PyObj args, PyObj kw)
{
	MemoryContext former = CurrentMemoryContext;
	PyObj fn_input, fn_output, input, rob = NULL;
	TupleDesc td;
	FmgrInfo flinfo;
	FunctionCallInfoData fcinfo;
	volatile Datum datum = 0;

	/*
	 * Disallow execution of "anonymous" functions.
	 */
	flinfo.fn_addr = PyPgFunction_GetPGFunction(self);
	flinfo.fn_oid = PyPgFunction_GetOid(self);
	flinfo.fn_retset = PyPgFunction_GetReturnsSet(self);
	if (flinfo.fn_addr == NULL || flinfo.fn_oid == InvalidOid)
	{
		PyErr_SetString(PyExc_TypeError, "internal functions are not directly callable");
		return(NULL);
	}
	if (flinfo.fn_retset)
	{
		PyErr_SetString(PyExc_NotImplementedError,
			"cannot directly execute set returning functions");
		return(NULL);
	}

	fn_input = PyPgFunction_GetInput(self);
	fn_output = PyPgFunction_GetOutput(self);
	if (PyPgTupleDesc_IsPolymorphic(fn_input) ||
		PyPgType_IsPolymorphic(fn_output))
	{
		PyErr_SetString(PyExc_NotImplementedError,
			"cannot directly execute polymorphic functions");
		return(NULL);
	}

	if (PyPgType_GetOid(fn_output) == TRIGGEROID)
	{
		PyErr_SetString(PyExc_NotImplementedError,
			"cannot directly execute TRIGGER returning functions");
		return(NULL);
	}

	/* No access if failed transaction */
	if (DB_IS_NOT_READY())
		return(NULL);

	td = PyPgTupleDesc_GetTupleDesc(fn_input);

	/*
	 * Normalize the parameters.
	 */
	input = PyTuple_FromTupleDescAndParameters(td, args, kw);
	if (input == NULL)
		return(NULL);

	flinfo.fn_nargs = td->natts;
	flinfo.fn_extra = NULL;
	flinfo.fn_mcxt = CurrentMemoryContext;
	flinfo.fn_expr = NULL;
	fcinfo.flinfo = &flinfo;
	fcinfo.context = NULL;
	fcinfo.resultinfo = NULL;
	fcinfo.isnull = false;
	/*
	 * Custom built descriptor; no dropped attributes.
	 */
	fcinfo.nargs = td->natts;

	SPI_push();
	PG_TRY();
	{
		Py_BuildDatumsAndNulls(td,
			PyPgTupleDesc_GetTypesTuple(fn_input),
			input, fcinfo.arg, fcinfo.argnull);

		datum = FunctionCallInvoke(&fcinfo);

		/*
		 * Special casing void to avoid the singleton.
		 */
		if (fcinfo.isnull ||
			PyPgType_GetOid(fn_output) == VOIDOID)
		{
			rob = Py_None;
			Py_INCREF(rob);
		}
		else
		{
			/*
			 * Some functions will return a parameter that its given.
			 * This is problematic if we are going to free the output
			 * after re-allocating as a Postgres.Object.
			 */
			if (PyPgType_ShouldFree(fn_output))
			{
				int i;

				/*
				 * Scan for !typbyval parameters.
				 * When one is found, compare the datum to the result datum.
				 */
				for (i = 0; i < PyTuple_GET_SIZE(input); ++i)
				{
					PyObj param = PyTuple_GET_ITEM(input, i);
					/*
					 * It's tempting to check the types first, but in situations
					 * of functions doing binary compatible coercion, it would be a
					 * mistake.
					 */
					if (PyPgType_ShouldFree(Py_TYPE(param)))
					{
						if (PyPgObject_GetDatum(param) == datum)
						{
							/*
							 * It's the same Datum of an argument,
							 * inc the ref and return the param.
							 */
							if (fn_output == (PyObj) Py_TYPE(param))
							{
								rob = param;
								Py_INCREF(rob);
							}
							else
							{
								/*
								 * It's the same Datum, but a different type.
								 * Make a Copy.
								 */
								rob = PyPgObject_New(fn_output, datum);
							}

							break;
						}
					}
				}

				/*
				 * It's a newly allocated result? (not an argument)
				 */
				if (rob == NULL)
				{
					/*
					 * New result, Datum is copied into the PythonMemoryContext
					 */
					rob = PyPgObject_New(fn_output, datum);
					/*
					 * Cleanup.
					 */
					pfree(DatumGetPointer(datum));
				}
			}
			else
			{
				/* Not pfree'ing typbyval, so no need to check parameters. */
				rob = PyPgObject_New(fn_output, datum);
			}
		}
	}
	PG_CATCH();
	{
		Py_XDECREF(rob);
		rob = NULL;
		PyErr_SetPgError(false);
	}
	PG_END_TRY();
	SPI_pop();

	Py_DECREF(input);

	MemoryContextSwitchTo(former);
	return(rob);
}