Re: [BUGS] casting strings to multidimensional arrays yields strange - Mailing list pgsql-patches

From Joe Conway
Subject Re: [BUGS] casting strings to multidimensional arrays yields strange
Date
Msg-id 41111DE4.4000901@joeconway.com
Whole thread Raw
Responses Re: [BUGS] casting strings to multidimensional arrays yields strange results
Re: [BUGS] casting strings to multidimensional arrays yields strange results
List pgsql-patches
Tom Lane wrote:
> [ cc'ing pghackers in case anyone wants to object ]
> Joe Conway <mail@joeconway.com> writes:
>>I think that even once we support NULL array elements, they should be
>>explicitly requested -- i.e. throwing an error on non-rectangular input
>>is still the right thing to do. I haven't suggested that in the past
>>because of the backward-compatibility issue, but maybe now is the time
>>to bite the bullet.
>
> Okay with me.  Anyone on pghackers not happy?
>
>>If you think this qualifies as a bug fix for 7.5, I can take a look at
>>it next week.
>
> Yeah, we can call it a bug fix.

The attached addresses the above issue as well as the ones mentioned in
my RFC post from yesterday found here:
http://archives.postgresql.org/pgsql-hackers/2004-08/msg00126.php

So whereas you used to get this:
test=# select '{{{1,2},{4,5,6}},{7,8}}'::int[];
  int4
------
  {}
(1 row)

you now get this:
test=# select '{{{1,2},{4,5,6}},{7,8}}'::int[];
ERROR:  multidimensional arrays must have array expressions with
matching dimensions

Negative lower bound indicies now work also, and array_out will always
output explicit dimensions for an array with any dimension having a
lower bound of other than one. This allows the dimensions to be
preserved across pg_dump/reload cycles:

CREATE TABLE foo (
     f1 integer[]
);
COPY foo (f1) FROM stdin;
[1:1][3:4][-2:0]={{{1,2,3},{4,5,6}}}
[-42:-41][12:14]={{1,2,3},{4,5,6}}
\.

test=# select f1, array_lower(f1, 1) as lb1, array_lower(f1, 2) as lb2,
array_lower(f1, 3) as lb3 from foo;
                   f1                  | lb1 | lb2 | lb3
--------------------------------------+-----+-----+-----
  [1:1][3:4][-2:0]={{{1,2,3},{4,5,6}}} |   1 |   3 |  -2
  [-42:-41][12:14]={{1,2,3},{4,5,6}}   | -42 |  12 |
(2 rows)

If there are no objections, I'll adjust the appropriate regression
script/expected files and commit tonight. And of course I'll follow up
with documentation updates too.

Any thoughts on whether or not to apply this to 7.4?

Thanks,

Joe
Index: src/backend/utils/adt/arrayfuncs.c
===================================================================
RCS file: /cvsroot/pgsql-server/src/backend/utils/adt/arrayfuncs.c,v
retrieving revision 1.105
diff -c -r1.105 arrayfuncs.c
*** src/backend/utils/adt/arrayfuncs.c    16 Jun 2004 01:26:47 -0000    1.105
--- src/backend/utils/adt/arrayfuncs.c    4 Aug 2004 15:37:34 -0000
***************
*** 217,223 ****
                       errmsg("number of array dimensions (%d) exceeds the maximum allowed (%d)",
                              ndim, MAXDIM)));

!         for (q = p; isdigit((unsigned char) *q); q++);
          if (q == p)                /* no digits? */
              ereport(ERROR,
                      (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
--- 217,223 ----
                       errmsg("number of array dimensions (%d) exceeds the maximum allowed (%d)",
                              ndim, MAXDIM)));

!         for (q = p; isdigit((unsigned char) *q) || (*q == '-') || (*q == '+'); q++);
          if (q == p)                /* no digits? */
              ereport(ERROR,
                      (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
***************
*** 229,235 ****
              *q = '\0';
              lBound[ndim] = atoi(p);
              p = q + 1;
!             for (q = p; isdigit((unsigned char) *q); q++);
              if (q == p)            /* no digits? */
                  ereport(ERROR,
                          (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
--- 229,235 ----
              *q = '\0';
              lBound[ndim] = atoi(p);
              p = q + 1;
!             for (q = p; isdigit((unsigned char) *q) || (*q == '-') || (*q == '+'); q++);
              if (q == p)            /* no digits? */
                  ereport(ERROR,
                          (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
***************
*** 270,275 ****
--- 270,278 ----
      }
      else
      {
+         int            ndim_braces,
+                     dim_braces[MAXDIM];
+
          /* If array dimensions are given, expect '=' operator */
          if (strncmp(p, ASSGN, strlen(ASSGN)) != 0)
              ereport(ERROR,
***************
*** 278,283 ****
--- 281,307 ----
          p += strlen(ASSGN);
          while (isspace((unsigned char) *p))
              p++;
+
+         /*
+          * intuit dimensions from brace structure -- it better match what
+          * we were given
+          */
+         if (*p != '{')
+             ereport(ERROR,
+                     (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+                      errmsg("array value must start with \"{\" or dimension information")));
+         ndim_braces = ArrayCount(p, dim_braces, typdelim);
+         if (ndim_braces != ndim)
+             ereport(ERROR,
+                     (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+                      errmsg("array dimensions incompatible with array literal")));
+         for (i = 0; i < ndim; ++i)
+         {
+             if (dim[i] != dim_braces[i])
+                 ereport(ERROR,
+                         (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+                         errmsg("array dimensions incompatible with array literal")));
+         }
      }

  #ifdef ARRAYDEBUG
***************
*** 303,309 ****
          ereport(ERROR,
                  (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
                   errmsg("missing left brace")));
-
      dataPtr = ReadArrayStr(p, nitems, ndim, dim, &my_extra->proc, typioparam,
                             typmod, typdelim, typlen, typbyval, typalign,
                             &nbytes);
--- 327,332 ----
***************
*** 334,346 ****
      int            nest_level = 0,
                  i;
      int            ndim = 1,
!                 temp[MAXDIM];
      bool        scanning_string = false;
      bool        eoArray = false;
      char       *ptr;

      for (i = 0; i < MAXDIM; ++i)
          temp[i] = dim[i] = 0;

      if (strncmp(str, "{}", 2) == 0)
          return 0;
--- 357,374 ----
      int            nest_level = 0,
                  i;
      int            ndim = 1,
!                 temp[MAXDIM],
!                 nelems[MAXDIM],
!                 nelems_last[MAXDIM];
      bool        scanning_string = false;
      bool        eoArray = false;
      char       *ptr;

      for (i = 0; i < MAXDIM; ++i)
+     {
          temp[i] = dim[i] = 0;
+         nelems_last[i] = nelems[i] = 1;
+     }

      if (strncmp(str, "{}", 2) == 0)
          return 0;
***************
*** 394,399 ****
--- 422,437 ----
                              (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
                               errmsg("malformed array literal: \"%s\"", str)));
                          nest_level--;
+
+                         if ((nelems_last[nest_level] != 1) &&
+                             (nelems[nest_level] != nelems_last[nest_level]))
+                             ereport(ERROR,
+                                 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+                                  errmsg("multidimensional arrays must have "
+                                         "array expressions with matching "
+                                         "dimensions")));
+                         nelems_last[nest_level] = nelems[nest_level];
+                         nelems[nest_level] = 1;
                          if (nest_level == 0)
                              eoArray = itemdone = true;
                          else
***************
*** 408,414 ****
--- 446,455 ----
                      break;
                  default:
                      if (*ptr == typdelim && !scanning_string)
+                     {
                          itemdone = true;
+                         nelems[nest_level - 1]++;
+                     }
                      break;
              }
              if (!itemdone)
***************
*** 684,699 ****
      char       *p,
                 *tmp,
                 *retval,
!               **values;
!     bool       *needquotes;
      int            nitems,
                  overall_length,
                  i,
                  j,
                  k,
                  indx[MAXDIM];
      int            ndim,
!                *dim;
      ArrayMetaState *my_extra;

      element_type = ARR_ELEMTYPE(v);
--- 725,748 ----
      char       *p,
                 *tmp,
                 *retval,
!               **values,
!                 /*
!                  * each dim: (sign + 10 digits) * 2, semicolon, 2 brackets
!                  * assignment operator + terminator
!                  */
!                 dims_str[(MAXDIM * 25) + 2];
!     bool       *needquotes,
!                 needdims = false;
      int            nitems,
                  overall_length,
+                 dims_str_length = 0,
                  i,
                  j,
                  k,
                  indx[MAXDIM];
      int            ndim,
!                *dim,
!                *lb;
      ArrayMetaState *my_extra;

      element_type = ARR_ELEMTYPE(v);
***************
*** 734,739 ****
--- 783,789 ----

      ndim = ARR_NDIM(v);
      dim = ARR_DIMS(v);
+     lb = ARR_LBOUND(v);
      nitems = ArrayGetNItems(ndim, dim);

      if (nitems == 0)
***************
*** 742,747 ****
--- 792,806 ----
          PG_RETURN_CSTRING(retval);
      }

+     for (i = 0; i < ndim; i++)
+     {
+         if (lb[i] != 1)
+         {
+             needdims = true;
+             break;
+         }
+     }
+
      /*
       * Convert all values to string form, count total space needed
       * (including any overhead such as escaping backslashes), and detect
***************
*** 798,809 ****
       */
      for (i = j = 0, k = 1; i < ndim; k *= dim[i++], j += k);

!     retval = (char *) palloc(overall_length + 2 * j);
      p = retval;

  #define APPENDSTR(str)    (strcpy(p, (str)), p += strlen(p))
  #define APPENDCHAR(ch)    (*p++ = (ch), *p = '\0')

      APPENDCHAR('{');
      for (i = 0; i < ndim; indx[i++] = 0);
      j = 0;
--- 857,896 ----
       */
      for (i = j = 0, k = 1; i < ndim; k *= dim[i++], j += k);

!     if (needdims)
!     {
!         char   *ptr = dims_str;
!
!         for (i = 0; i < ndim; i++)
!         {
!             char    buf_lb[12];    /* sign, 10 digits, '\0' */
!             char    buf_ub[12];    /* sign, 10 digits, '\0' */
!
!             *ptr++ = '[';
!
!             sprintf(buf_lb, "%d", lb[i]);
!             strcpy(ptr, buf_lb), ptr += strlen(buf_lb);
!
!             *ptr++ = ':';
!
!             sprintf(buf_ub, "%d", lb[i] + dim[i] - 1);
!             strcpy(ptr, buf_ub), ptr += strlen(buf_ub);
!
!             *ptr++ = ']';
!         }
!         *ptr++ = *ASSGN;
!         *ptr = '\0';
!         dims_str_length = strlen(dims_str);
!     }
!
!     retval = (char *) palloc(dims_str_length + overall_length + 2 * j);
      p = retval;

  #define APPENDSTR(str)    (strcpy(p, (str)), p += strlen(p))
  #define APPENDCHAR(ch)    (*p++ = (ch), *p = '\0')

+     if (needdims)
+         APPENDSTR(dims_str);
      APPENDCHAR('{');
      for (i = 0; i < ndim; indx[i++] = 0);
      j = 0;

pgsql-patches by date:

Previous
From: Tom Lane
Date:
Subject: Re: More fixes for pg_dump
Next
From: Tom Lane
Date:
Subject: Re: [BUGS] casting strings to multidimensional arrays yields strange results