tighten-multidim-array-shape-checks.patch
text/x-diff
Filename: tighten-multidim-array-shape-checks.patch
Type: text/x-diff
Part: 0
Patch
Format: unified
| File | + | − |
|---|---|---|
| src/pl/plpython/expected/plpython_types.out | 18 | 2 |
| src/pl/plpython/plpy_typeio.c | 52 | 25 |
| src/pl/plpython/sql/plpython_types.sql | 12 | 0 |
diff --git a/src/pl/plpython/expected/plpython_types.out b/src/pl/plpython/expected/plpython_types.out
index c4e749a5a8..f477e239bc 100644
--- a/src/pl/plpython/expected/plpython_types.out
+++ b/src/pl/plpython/expected/plpython_types.out
@@ -700,10 +700,26 @@ CREATE FUNCTION test_type_conversion_mdarray_malformed() RETURNS int[] AS $$
return [[1,2,3],[4,5]]
$$ LANGUAGE plpython3u;
SELECT * FROM test_type_conversion_mdarray_malformed();
-ERROR: wrong length of inner sequence: has length 2, but 3 was expected
-DETAIL: To construct a multidimensional array, the inner sequences must all have the same length.
+ERROR: wrong length of inner list: has length 2, but 3 was expected
+DETAIL: To construct a multidimensional array, the inner lists must all have the same length.
CONTEXT: while creating return value
PL/Python function "test_type_conversion_mdarray_malformed"
+CREATE FUNCTION test_type_conversion_mdarray_malformed2() RETURNS text[] AS $$
+return [[1,2,3], "abc"]
+$$ LANGUAGE plpython3u;
+SELECT * FROM test_type_conversion_mdarray_malformed2();
+ERROR: array element is a scalar, but should be a list
+DETAIL: To construct a multidimensional array, the inner lists must be nested to a uniform depth.
+CONTEXT: while creating return value
+PL/Python function "test_type_conversion_mdarray_malformed2"
+CREATE FUNCTION test_type_conversion_mdarray_malformed3() RETURNS text[] AS $$
+return ["abc", [1,2,3]]
+$$ LANGUAGE plpython3u;
+SELECT * FROM test_type_conversion_mdarray_malformed3();
+ERROR: array element is a list, but should be a scalar
+DETAIL: To construct a multidimensional array, the inner lists must be nested to a uniform depth.
+CONTEXT: while creating return value
+PL/Python function "test_type_conversion_mdarray_malformed3"
CREATE FUNCTION test_type_conversion_mdarray_toodeep() RETURNS int[] AS $$
return [[[[[[[1]]]]]]]
$$ LANGUAGE plpython3u;
diff --git a/src/pl/plpython/plpy_typeio.c b/src/pl/plpython/plpy_typeio.c
index 864b5f1765..eddedec3d4 100644
--- a/src/pl/plpython/plpy_typeio.c
+++ b/src/pl/plpython/plpy_typeio.c
@@ -1126,7 +1126,7 @@ PLyObject_ToTransform(PLyObToDatum *arg, PyObject *plrv,
/*
- * Convert Python sequence to SQL array.
+ * Convert Python sequence (or list of lists) to SQL array.
*/
static Datum
PLySequence_ToArray(PLyObToDatum *arg, PyObject *plrv,
@@ -1205,8 +1205,15 @@ PLySequence_ToArray(PLyObToDatum *arg, PyObject *plrv,
dims[0] = PySequence_Length(plrv);
}
- /* Allocate space for work arrays, after detecting array size overflow */
len = ArrayGetNItems(ndim, dims);
+ if (len == 0)
+ {
+ /* If no elements, ensure we return a zero-D array */
+ array = construct_empty_array(arg->u.array.elmbasetype);
+ return PointerGetDatum(array);
+ }
+
+ /* Allocate space for work arrays, after detecting array size overflow */
elems = palloc(sizeof(Datum) * len);
nulls = palloc(sizeof(bool) * len);
@@ -1246,35 +1253,55 @@ PLySequence_ToArray_recurse(PLyObToDatum *elm, PyObject *list,
{
int i;
- if (PySequence_Length(list) != dims[dim])
- ereport(ERROR,
- (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
- errmsg("wrong length of inner sequence: has length %d, but %d was expected",
- (int) PySequence_Length(list), dims[dim]),
- (errdetail("To construct a multidimensional array, the inner sequences must all have the same length."))));
-
- if (dim < ndim - 1)
+ /*
+ * The list nesting depth in all array items must match what
+ * PLySequence_ToArray saw in the first item. At the first dimension, we
+ * might have a non-list sequence, but recurse anyway.
+ */
+ if (dim == 0 || PyList_Check(list))
{
- for (i = 0; i < dims[dim]; i++)
- {
- PyObject *sublist = PySequence_GetItem(list, i);
+ int thisdim = PySequence_Length(list);
- PLySequence_ToArray_recurse(elm, sublist, dims, ndim, dim + 1,
- elems, nulls, currelem);
- Py_XDECREF(sublist);
- }
- }
- else
- {
- for (i = 0; i < dims[dim]; i++)
+ if (thisdim < 0)
+ PLy_elog(ERROR, "could not determine sequence length for function return value");
+
+ /* like PLySequence_ToArray, we treat 0-length list as a scalar */
+ if (thisdim > 0)
{
- PyObject *obj = PySequence_GetItem(list, i);
+ /* Check for uniform array structure */
+ if (dim >= ndim)
+ ereport(ERROR,
+ (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
+ errmsg("array element is a list, but should be a scalar"),
+ (errdetail("To construct a multidimensional array, the inner lists must be nested to a uniform depth."))));
+ if (thisdim != dims[dim])
+ ereport(ERROR,
+ (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
+ errmsg("wrong length of inner list: has length %d, but %d was expected",
+ thisdim, dims[dim]),
+ (errdetail("To construct a multidimensional array, the inner lists must all have the same length."))));
+ /* OK, so recurse */
+ for (i = 0; i < thisdim; i++)
+ {
+ PyObject *sublist = PySequence_GetItem(list, i);
- elems[*currelem] = elm->func(elm, obj, &nulls[*currelem], true);
- Py_XDECREF(obj);
- (*currelem)++;
+ PLySequence_ToArray_recurse(elm, sublist, dims, ndim, dim + 1,
+ elems, nulls, currelem);
+ Py_XDECREF(sublist);
+ }
+ return;
}
}
+
+ /* The "list" is, or should be treated as, a scalar */
+ if (dim != ndim)
+ ereport(ERROR,
+ (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
+ errmsg("array element is a scalar, but should be a list"),
+ (errdetail("To construct a multidimensional array, the inner lists must be nested to a uniform depth."))));
+
+ elems[*currelem] = elm->func(elm, list, &nulls[*currelem], true);
+ (*currelem)++;
}
diff --git a/src/pl/plpython/sql/plpython_types.sql b/src/pl/plpython/sql/plpython_types.sql
index 9702a10a72..b57b9a6463 100644
--- a/src/pl/plpython/sql/plpython_types.sql
+++ b/src/pl/plpython/sql/plpython_types.sql
@@ -341,6 +341,18 @@ $$ LANGUAGE plpython3u;
SELECT * FROM test_type_conversion_mdarray_malformed();
+CREATE FUNCTION test_type_conversion_mdarray_malformed2() RETURNS text[] AS $$
+return [[1,2,3], "abc"]
+$$ LANGUAGE plpython3u;
+
+SELECT * FROM test_type_conversion_mdarray_malformed2();
+
+CREATE FUNCTION test_type_conversion_mdarray_malformed3() RETURNS text[] AS $$
+return ["abc", [1,2,3]]
+$$ LANGUAGE plpython3u;
+
+SELECT * FROM test_type_conversion_mdarray_malformed3();
+
CREATE FUNCTION test_type_conversion_mdarray_toodeep() RETURNS int[] AS $$
return [[[[[[[1]]]]]]]
$$ LANGUAGE plpython3u;