On 26.03.2018 17:19, David Steele wrote:
On 2/20/18 10:14 AM, Haozhou Wang wrote:
Thank you very much for your review!
I attached a new patch with typo fixed.
I think it's a bit premature to mark this Ready for Committer after a
review consisting of a few typos. Anthony only said that he started
looking at it so I've marked it Needs Review.
Hi.
I also have looked at this patch and found some problems.
Attached fixed 3th version of the patch:
* initialization of arg->u.scalar was moved into PLy_output_setup_func()
* added range checks for int16 and int32 types
* added subroutine PLyInt_AsLong() for correct handling OverflowError that can
be thrown from PyInt_AsLong()
* casting from Python float to PostgreSQL numeric using PyFloat_AsDouble() was
removed because it can return incorrect result for Python long and
float8_numeric() uses float8 and numeric I/O functions
* fixed whitespace
--
Nikita Glukhov
Postgres Professional: http://www.postgrespro.com
The Russian Postgres Company
diff --git a/src/pl/plpython/plpy_typeio.c b/src/pl/plpython/plpy_typeio.c
index d6a6a84..3b874e1 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,
@@ -397,16 +409,35 @@ PLy_output_setup_func(PLyObToDatum *arg, MemoryContext arg_mcxt,
{
case BOOLOID:
arg->func = PLyObject_ToBool;
- break;
+ return; /* no need to initialize arg->u.scalar */
case BYTEAOID:
arg->func = PLyObject_ToBytea;
+ return; /* no need to initialize arg->u.scalar */
+ 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;
default:
arg->func = PLyObject_ToScalar;
- getTypeInputInfo(typeOid, &typinput, &arg->u.scalar.typioparam);
- fmgr_info_cxt(typinput, &arg->u.scalar.typfunc, arg_mcxt);
break;
}
+
+ getTypeInputInfo(typeOid, &typinput, &arg->u.scalar.typioparam);
+ fmgr_info_cxt(typinput, &arg->u.scalar.typfunc, arg_mcxt);
}
}
@@ -884,6 +915,210 @@ PLyObject_ToBool(PLyObToDatum *arg, PyObject *plrv,
return BoolGetDatum(PyObject_IsTrue(plrv));
}
+/* Try to convert python Int to C long. */
+static long
+PLyInt_AsLong(PyObject *plrv, bool *err)
+{
+ long val = PyInt_AsLong(plrv);
+
+ /* If -1 is returned then OverflowError is possible. */
+ *err = val == -1 && PyErr_Occurred();
+
+ if (*err)
+ {
+ /* Catch OverflowError exception. */
+ if (!PyErr_ExceptionMatches(PyExc_OverflowError))
+ PLy_elog(ERROR, NULL);
+
+ PyErr_Clear();
+ }
+
+ return val;
+}
+
+/*
+ * Convert a Python object to a PostgreSQL int16 datum directly.
+ * If can not convert it directly, fallback to PLyObject_ToScalar
+ * 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))
+ {
+ bool err;
+ long val = PLyInt_AsLong(plrv, &err);
+
+ if (err || val < SHRT_MIN || val > SHRT_MAX)
+ ereport(ERROR,
+ (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
+ errmsg("value \"%s\" is out of range for type %s",
+ PLyObject_AsString(plrv), "smallint")));
+
+ *isnull = false;
+ return Int16GetDatum((int16) val);
+ }
+
+ return PLyObject_ToScalar(arg, plrv, isnull, inarray);
+}
+
+/*
+ * Convert a Python object to a PostgreSQL int32 datum directly.
+ * If can not convert it directly, fallback to PLyObject_ToScalar
+ * 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))
+ {
+ bool err;
+ long val = PLyInt_AsLong(plrv, &err);
+
+ if (err
+#ifdef HAVE_LONG_INT_64
+ || val < INT_MIN || val > INT_MAX
+#endif
+ )
+ ereport(ERROR,
+ (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
+ errmsg("value \"%s\" is out of range for type %s",
+ PLyObject_AsString(plrv), "integer")));
+
+ *isnull = false;
+ return Int32GetDatum((int32) val);
+ }
+
+ return PLyObject_ToScalar(arg, plrv, isnull, inarray);
+}
+
+/*
+ * Convert a Python object to a PostgreSQL int64 datum directly.
+ * If can not convert it directly, fallback to PLyObject_ToScalar
+ * 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))
+ {
+ bool err;
+ long val = PLyInt_AsLong(plrv, &err);
+
+ if (!err)
+ {
+ *isnull = false;
+ return Int64GetDatum((int64) val);
+ }
+
+ /* try to convert via I/O */
+ }
+
+ return PLyObject_ToScalar(arg, plrv, isnull, inarray);
+}
+
+/*
+ * Convert a Python object to a PostgreSQL float4 datum directly.
+ * If can not convert it directly, fallback to PLyObject_ToScalar
+ * 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));
+ }
+
+ return PLyObject_ToScalar(arg, plrv, isnull, inarray);
+}
+
+/*
+ * Convert a Python object to a PostgreSQL float8 datum directly.
+ * If can not convert it directly, fallback to PLyObject_ToScalar
+ * 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));
+ }
+
+ return PLyObject_ToScalar(arg, plrv, isnull, inarray);
+}
+
+/*
+ * Convert a Python object to a PostgreSQL numeric datum directly.
+ * If can not convert it directly, fallback to PLyObject_ToScalar
+ * 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 (PyInt_Check(plrv) || PyLong_Check(plrv))
+ {
+ bool err;
+ long val = PLyInt_AsLong(plrv, &err);
+
+ if (!err)
+ {
+ *isnull = false;
+ return DirectFunctionCall1(int8_numeric,
+ Int64GetDatum((int64) val));
+ }
+
+ /* try to convert via I/O */
+ }
+
+ 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