8
8
*
9
9
*
10
10
* IDENTIFICATION
11
- * $PostgreSQL: pgsql/src/backend/utils/adt/arrayfuncs.c,v 1.106 2004/08/05 03:29:37 joe Exp $
11
+ * $PostgreSQL: pgsql/src/backend/utils/adt/arrayfuncs.c,v 1.107 2004/08/08 05:01:55 joe Exp $
12
12
*
13
13
*-------------------------------------------------------------------------
14
14
*/
@@ -351,25 +351,40 @@ array_in(PG_FUNCTION_ARGS)
351
351
* The syntax for array input is C-like nested curly braces
352
352
*-----------------------------------------------------------------------------
353
353
*/
354
+ typedef enum
355
+ {
356
+ ARRAY_NO_LEVEL ,
357
+ ARRAY_LEVEL_STARTED ,
358
+ ARRAY_ELEM_STARTED ,
359
+ ARRAY_ELEM_COMPLETED ,
360
+ ARRAY_QUOTED_ELEM_STARTED ,
361
+ ARRAY_QUOTED_ELEM_COMPLETED ,
362
+ ARRAY_ELEM_DELIMITED ,
363
+ ARRAY_LEVEL_COMPLETED ,
364
+ ARRAY_LEVEL_DELIMITED
365
+ } ArrayParseState ;
366
+
354
367
static int
355
368
ArrayCount (char * str , int * dim , char typdelim )
356
369
{
357
- int nest_level = 0 ,
358
- i ;
359
- int ndim = 1 ,
360
- temp [MAXDIM ],
361
- nelems [MAXDIM ],
362
- nelems_last [MAXDIM ];
363
- bool scanning_string = false;
364
- bool eoArray = false;
365
- char * ptr ;
370
+ int nest_level = 0 ,
371
+ i ;
372
+ int ndim = 1 ,
373
+ temp [MAXDIM ],
374
+ nelems [MAXDIM ],
375
+ nelems_last [MAXDIM ];
376
+ bool scanning_string = false;
377
+ bool eoArray = false;
378
+ char * ptr ;
379
+ ArrayParseState parse_state = ARRAY_NO_LEVEL ;
366
380
367
381
for (i = 0 ; i < MAXDIM ; ++ i )
368
382
{
369
383
temp [i ] = dim [i ] = 0 ;
370
384
nelems_last [i ] = nelems [i ] = 1 ;
371
385
}
372
386
387
+ /* special case for an empty array */
373
388
if (strncmp (str , "{}" , 2 ) == 0 )
374
389
return 0 ;
375
390
@@ -389,6 +404,20 @@ ArrayCount(char *str, int *dim, char typdelim)
389
404
errmsg ("malformed array literal: \"%s\"" , str )));
390
405
break ;
391
406
case '\\' :
407
+ /*
408
+ * An escape must be after a level start, after an
409
+ * element start, or after an element delimiter. In any
410
+ * case we now must be past an element start.
411
+ */
412
+ if (parse_state != ARRAY_LEVEL_STARTED &&
413
+ parse_state != ARRAY_ELEM_STARTED &&
414
+ parse_state != ARRAY_QUOTED_ELEM_STARTED &&
415
+ parse_state != ARRAY_ELEM_DELIMITED )
416
+ ereport (ERROR ,
417
+ (errcode (ERRCODE_INVALID_TEXT_REPRESENTATION ),
418
+ errmsg ("malformed array literal: \"%s\"" , str )));
419
+ if (parse_state != ARRAY_QUOTED_ELEM_STARTED )
420
+ parse_state = ARRAY_ELEM_STARTED ;
392
421
/* skip the escaped character */
393
422
if (* (ptr + 1 ))
394
423
ptr ++ ;
@@ -398,11 +427,38 @@ ArrayCount(char *str, int *dim, char typdelim)
398
427
errmsg ("malformed array literal: \"%s\"" , str )));
399
428
break ;
400
429
case '\"' :
430
+ /*
431
+ * A quote must be after a level start, after a quoted
432
+ * element start, or after an element delimiter. In any
433
+ * case we now must be past an element start.
434
+ */
435
+ if (parse_state != ARRAY_LEVEL_STARTED &&
436
+ parse_state != ARRAY_QUOTED_ELEM_STARTED &&
437
+ parse_state != ARRAY_ELEM_DELIMITED )
438
+ ereport (ERROR ,
439
+ (errcode (ERRCODE_INVALID_TEXT_REPRESENTATION ),
440
+ errmsg ("malformed array literal: \"%s\"" , str )));
401
441
scanning_string = !scanning_string ;
442
+ if (scanning_string )
443
+ parse_state = ARRAY_QUOTED_ELEM_STARTED ;
444
+ else
445
+ parse_state = ARRAY_QUOTED_ELEM_COMPLETED ;
402
446
break ;
403
447
case '{' :
404
448
if (!scanning_string )
405
449
{
450
+ /*
451
+ * A left brace can occur if no nesting has
452
+ * occurred yet, after a level start, or
453
+ * after a level delimiter.
454
+ */
455
+ if (parse_state != ARRAY_NO_LEVEL &&
456
+ parse_state != ARRAY_LEVEL_STARTED &&
457
+ parse_state != ARRAY_LEVEL_DELIMITED )
458
+ ereport (ERROR ,
459
+ (errcode (ERRCODE_INVALID_TEXT_REPRESENTATION ),
460
+ errmsg ("malformed array literal: \"%s\"" , str )));
461
+ parse_state = ARRAY_LEVEL_STARTED ;
406
462
if (nest_level >= MAXDIM )
407
463
ereport (ERROR ,
408
464
(errcode (ERRCODE_PROGRAM_LIMIT_EXCEEDED ),
@@ -417,6 +473,19 @@ ArrayCount(char *str, int *dim, char typdelim)
417
473
case '}' :
418
474
if (!scanning_string )
419
475
{
476
+ /*
477
+ * A right brace can occur after an element start,
478
+ * an element completion, a quoted element completion,
479
+ * or a level completion.
480
+ */
481
+ if (parse_state != ARRAY_ELEM_STARTED &&
482
+ parse_state != ARRAY_ELEM_COMPLETED &&
483
+ parse_state != ARRAY_QUOTED_ELEM_COMPLETED &&
484
+ parse_state != ARRAY_LEVEL_COMPLETED )
485
+ ereport (ERROR ,
486
+ (errcode (ERRCODE_INVALID_TEXT_REPRESENTATION ),
487
+ errmsg ("malformed array literal: \"%s\"" , str )));
488
+ parse_state = ARRAY_LEVEL_COMPLETED ;
420
489
if (nest_level == 0 )
421
490
ereport (ERROR ,
422
491
(errcode (ERRCODE_INVALID_TEXT_REPRESENTATION ),
@@ -445,10 +514,45 @@ ArrayCount(char *str, int *dim, char typdelim)
445
514
}
446
515
break ;
447
516
default :
448
- if (* ptr == typdelim && !scanning_string )
517
+ if (!scanning_string )
449
518
{
450
- itemdone = true;
451
- nelems [nest_level - 1 ]++ ;
519
+ if (* ptr == typdelim )
520
+ {
521
+ /*
522
+ * Delimiters can occur after an element start,
523
+ * an element completion, a quoted element
524
+ * completion, or a level completion.
525
+ */
526
+ if (parse_state != ARRAY_ELEM_STARTED &&
527
+ parse_state != ARRAY_ELEM_COMPLETED &&
528
+ parse_state != ARRAY_QUOTED_ELEM_COMPLETED &&
529
+ parse_state != ARRAY_LEVEL_COMPLETED )
530
+ ereport (ERROR ,
531
+ (errcode (ERRCODE_INVALID_TEXT_REPRESENTATION ),
532
+ errmsg ("malformed array literal: \"%s\"" , str )));
533
+ if (parse_state == ARRAY_LEVEL_COMPLETED )
534
+ parse_state = ARRAY_LEVEL_DELIMITED ;
535
+ else
536
+ parse_state = ARRAY_ELEM_DELIMITED ;
537
+ itemdone = true;
538
+ nelems [nest_level - 1 ]++ ;
539
+ }
540
+ else if (!isspace (* ptr ))
541
+ {
542
+ /*
543
+ * Other non-space characters must be after a level
544
+ * start, after an element start, or after an element
545
+ * delimiter. In any case we now must be past an
546
+ * element start.
547
+ */
548
+ if (parse_state != ARRAY_LEVEL_STARTED &&
549
+ parse_state != ARRAY_ELEM_STARTED &&
550
+ parse_state != ARRAY_ELEM_DELIMITED )
551
+ ereport (ERROR ,
552
+ (errcode (ERRCODE_INVALID_TEXT_REPRESENTATION ),
553
+ errmsg ("malformed array literal: \"%s\"" , str )));
554
+ parse_state = ARRAY_ELEM_STARTED ;
555
+ }
452
556
}
453
557
break ;
454
558
}
@@ -511,12 +615,15 @@ ReadArrayStr(char *arrayStr,
511
615
while (!eoArray )
512
616
{
513
617
bool itemdone = false;
618
+ bool itemquoted = false;
514
619
int i = -1 ;
515
620
char * itemstart ;
621
+ char * eptr ;
516
622
517
623
/* skip leading whitespace */
518
624
while (isspace ((unsigned char ) * ptr ))
519
625
ptr ++ ;
626
+
520
627
itemstart = ptr ;
521
628
522
629
while (!itemdone )
@@ -547,11 +654,15 @@ ReadArrayStr(char *arrayStr,
547
654
char * cptr ;
548
655
549
656
scanning_string = !scanning_string ;
550
- /* Crunch the string on top of the quote. */
551
- for (cptr = ptr ; * cptr != '\0' ; cptr ++ )
552
- * cptr = * (cptr + 1 );
553
- /* Back up to not miss following character. */
554
- ptr -- ;
657
+ if (scanning_string )
658
+ {
659
+ itemquoted = true;
660
+ /* Crunch the string on top of the first quote. */
661
+ for (cptr = ptr ; * cptr != '\0' ; cptr ++ )
662
+ * cptr = * (cptr + 1 );
663
+ /* Back up to not miss following character. */
664
+ ptr -- ;
665
+ }
555
666
break ;
556
667
}
557
668
case '{' :
@@ -615,6 +726,25 @@ ReadArrayStr(char *arrayStr,
615
726
(errcode (ERRCODE_INVALID_TEXT_REPRESENTATION ),
616
727
errmsg ("malformed array literal: \"%s\"" , arrayStr )));
617
728
729
+ /*
730
+ * skip trailing whitespace
731
+ */
732
+ eptr = ptr - 1 ;
733
+ if (!itemquoted )
734
+ {
735
+ /* skip to last non-NULL, non-space, character */
736
+ while ((* eptr == '\0' ) || (isspace ((unsigned char ) * eptr )))
737
+ eptr -- ;
738
+ * (++ eptr ) = '\0' ;
739
+ }
740
+ else
741
+ {
742
+ /* skip to last quote character */
743
+ while (* eptr != '"' )
744
+ eptr -- ;
745
+ * eptr = '\0' ;
746
+ }
747
+
618
748
values [i ] = FunctionCall3 (inputproc ,
619
749
CStringGetDatum (itemstart ),
620
750
ObjectIdGetDatum (typioparam ),
0 commit comments