From 56f86cd1d4513c2b2e40e19479d9cf2f64e857d4 Mon Sep 17 00:00:00 2001
From: Haozhou Wang <hawang@pivotal.io>
Date: Tue, 30 Jan 2018 17:59:23 +0800
Subject: [PATCH] Add missing type conversion functions for PL/Python

PL/Python already has different type conversion functions to
convert PostgreSQL datum to Python object. However, the conversion
functions from Python object to PostgreSQL datum only has Boolean,
Bytea and String functions. This commit add rest of Integer
and Float related datatype conversion functions, and can
increase the performance of data conversion greatly especially
when returning a large array.

Author: Haozhou Wang <hawang@pivotal.io>
Author: Paul Guo <paulguo@gmail.com>
Author: Hubert Zhang <zhanghuan929@gmail.com>
---
 src/pl/plpython/plpy_typeio.c | 201 ++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 201 insertions(+)

diff --git a/src/pl/plpython/plpy_typeio.c b/src/pl/plpython/plpy_typeio.c
index 6c6b16f..6f25103 100644
--- a/src/pl/plpython/plpy_typeio.c
+++ b/src/pl/plpython/plpy_typeio.c
@@ -46,6 +46,18 @@ static PyObject *PLyDict_FromTuple(PLyDatumToOb *arg, HeapTuple tuple, TupleDesc
 /* conversion from Python objects to Datums */
 static Datum PLyObject_ToBool(PLyObToDatum *arg, PyObject *plrv,
 				 bool *isnull, bool inarray);
+static Datum PLyObject_ToInt16(PLyObToDatum *arg, PyObject *plrv,
+				 bool *isnull, bool inarray);
+static Datum PLyObject_ToInt32(PLyObToDatum *arg, PyObject *plrv,
+				 bool *isnull, bool inarray);
+static Datum PLyObject_ToInt64(PLyObToDatum *arg, PyObject *plrv,
+				 bool *isnull, bool inarray);
+static Datum PLyObject_ToFloat4(PLyObToDatum *arg, PyObject *plrv,
+				 bool *isnull, bool inarray);
+static Datum PLyObject_ToFloat8(PLyObToDatum *arg, PyObject *plrv,
+				 bool *isnull, bool inarray);
+static Datum PLyObject_ToNumeric(PLyObToDatum *arg, PyObject *plrv,
+				 bool *isnull, bool inarray);
 static Datum PLyObject_ToBytea(PLyObToDatum *arg, PyObject *plrv,
 				  bool *isnull, bool inarray);
 static Datum PLyObject_ToComposite(PLyObToDatum *arg, PyObject *plrv,
@@ -398,6 +410,24 @@ PLy_output_setup_func(PLyObToDatum *arg, MemoryContext arg_mcxt,
 			case BOOLOID:
 				arg->func = PLyObject_ToBool;
 				break;
+			case INT2OID:
+				arg->func = PLyObject_ToInt16;
+				break;
+			case INT4OID:
+				arg->func = PLyObject_ToInt32;
+				break;
+			case INT8OID:
+				arg->func = PLyObject_ToInt64;
+				break;
+			case FLOAT4OID:
+				arg->func = PLyObject_ToFloat4;
+				break;
+			case FLOAT8OID:
+				arg->func = PLyObject_ToFloat8;
+				break;
+			case NUMERICOID:
+				arg->func = PLyObject_ToNumeric;
+				break;
 			case BYTEAOID:
 				arg->func = PLyObject_ToBytea;
 				break;
@@ -885,6 +915,177 @@ PLyObject_ToBool(PLyObToDatum *arg, PyObject *plrv,
 }
 
 /*
+ * Convert a Python object to a PostgreSQL int16 datum directly.
+ * If can not convert if directly, fallback to PLyObject_ToDatum
+ * to convert it.
+ */
+static Datum
+PLyObject_ToInt16(PLyObToDatum *arg, PyObject *plrv,
+				 bool *isnull, bool inarray)
+{
+	if (plrv == Py_None)
+	{
+		*isnull = true;
+		return (Datum) 0;
+	}
+	if (PyInt_Check(plrv) || PyLong_Check(plrv))
+	{
+		*isnull = false;
+		return Int16GetDatum((int16)PyInt_AsLong(plrv));
+	}
+	else
+	{
+		Oid typinput;
+		getTypeInputInfo(arg->typoid, &typinput, &arg->u.scalar.typioparam);
+		fmgr_info_cxt(typinput, &arg->u.scalar.typfunc, arg->mcxt);
+		return PLyObject_ToScalar(arg, plrv, isnull, inarray);
+	}
+}
+
+/*
+ * Convert a Python object to a PostgreSQL int32 datum directly.
+ * If can not convert if directly, fallback to PLyObject_ToDatum
+ * to convert it.
+ */
+static Datum
+PLyObject_ToInt32(PLyObToDatum *arg, PyObject *plrv,
+				 bool *isnull, bool inarray)
+{
+	if (plrv == Py_None)
+	{
+		*isnull = true;
+		return (Datum) 0;
+	}
+	if (PyInt_Check(plrv) || PyLong_Check(plrv))
+	{
+		*isnull = false;
+		return Int32GetDatum((int32)PyInt_AsLong(plrv));
+	}
+	else
+	{
+		Oid typinput;
+		getTypeInputInfo(arg->typoid, &typinput, &arg->u.scalar.typioparam);
+		fmgr_info_cxt(typinput, &arg->u.scalar.typfunc, arg->mcxt);
+		return PLyObject_ToScalar(arg, plrv, isnull, inarray);
+	}
+}
+
+/*
+ * Convert a Python object to a PostgreSQL int64 datum directly.
+ * If can not convert if directly, fallback to PLyObject_ToDatum
+ * to convert it.
+ */
+static Datum
+PLyObject_ToInt64(PLyObToDatum *arg, PyObject *plrv,
+				 bool *isnull, bool inarray)
+{
+	if (plrv == Py_None)
+	{
+		*isnull = true;
+		return (Datum) 0;
+	}
+	if (PyInt_Check(plrv) || PyLong_Check(plrv))
+	{
+		*isnull = false;
+		return Int64GetDatum((int64)PyLong_AsLong(plrv));
+	}
+	else
+	{
+		Oid typinput;
+		getTypeInputInfo(arg->typoid, &typinput, &arg->u.scalar.typioparam);
+		fmgr_info_cxt(typinput, &arg->u.scalar.typfunc, arg->mcxt);
+		return PLyObject_ToScalar(arg, plrv, isnull, inarray);
+	}
+}
+
+/*
+ * Convert a Python object to a PostgreSQL float4 datum directly.
+ * If can not convert if directly, fallback to PLyObject_ToDatum
+ * to convert it.
+ */
+static Datum
+PLyObject_ToFloat4(PLyObToDatum *arg, PyObject *plrv,
+				 bool *isnull, bool inarray)
+{
+	if (plrv == Py_None)
+	{
+		*isnull = true;
+		return (Datum) 0;
+	}
+	if (PyFloat_Check(plrv) || PyInt_Check(plrv) || PyLong_Check(plrv))
+	{
+		*isnull = false;
+		return Float4GetDatum((float)PyFloat_AsDouble(plrv));
+	}
+	else
+	{
+		Oid typinput;
+		getTypeInputInfo(arg->typoid, &typinput, &arg->u.scalar.typioparam);
+		fmgr_info_cxt(typinput, &arg->u.scalar.typfunc, arg->mcxt);
+		return PLyObject_ToScalar(arg, plrv, isnull, inarray);
+	}
+}
+
+/*
+ * Convert a Python object to a PostgreSQL float8 datum directly.
+ * If can not convert if directly, fallback to PLyObject_ToDatum
+ * to convert it.
+ */
+static Datum
+PLyObject_ToFloat8(PLyObToDatum *arg, PyObject *plrv,
+				 bool *isnull, bool inarray)
+{
+	if (plrv == Py_None)
+	{
+		*isnull = true;
+		return (Datum) 0;
+	}
+	if (PyFloat_Check(plrv) || PyInt_Check(plrv) || PyLong_Check(plrv))
+	{
+		*isnull = false;
+		return Float8GetDatum((double)PyFloat_AsDouble(plrv));
+	}
+	else
+	{
+		Oid typinput;
+		getTypeInputInfo(arg->typoid, &typinput, &arg->u.scalar.typioparam);
+		fmgr_info_cxt(typinput, &arg->u.scalar.typfunc, arg->mcxt);
+		return PLyObject_ToScalar(arg, plrv, isnull, inarray);
+	}
+}
+
+/*
+ * Convert a Python object to a PostgreSQL numeric datum directly.
+ * If can not convert if directly, fallback to PLyObject_ToDatum
+ * to convert it.
+ */
+static Datum
+PLyObject_ToNumeric(PLyObToDatum *arg, PyObject *plrv,
+				 bool *isnull, bool inarray)
+{
+	if (plrv == Py_None)
+	{
+		*isnull = true;
+		return (Datum) 0;
+	}
+	if (PyFloat_Check(plrv) || PyInt_Check(plrv) || PyLong_Check(plrv))
+	{
+		Datum fdatum;
+
+		*isnull = false;
+		fdatum = Float8GetDatum((double)PyFloat_AsDouble(plrv));
+		return DirectFunctionCall1(float8_numeric, fdatum);
+	}
+	else
+	{
+		Oid typinput;
+		getTypeInputInfo(arg->typoid, &typinput, &arg->u.scalar.typioparam);
+		fmgr_info_cxt(typinput, &arg->u.scalar.typfunc, arg->mcxt);
+		return PLyObject_ToScalar(arg, plrv, isnull, inarray);
+	}
+}
+
+/*
  * Convert a Python object to a PostgreSQL bytea datum.  This doesn't
  * go through the generic conversion function to circumvent problems
  * with embedded nulls.  And it's faster this way.
-- 
2.7.4

