arrayfuncs.c 44.6 KB
Newer Older
1 2
/*-------------------------------------------------------------------------
 *
3
 * arrayfuncs.c
4
 *	  Support functions for arrays.
5
 *
6
 * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
Bruce Momjian's avatar
Add:  
Bruce Momjian committed
7
 * Portions Copyright (c) 1994, Regents of the University of California
8 9 10
 *
 *
 * IDENTIFICATION
11
 *	  $Header: /cvsroot/pgsql/src/backend/utils/adt/arrayfuncs.c,v 1.74 2002/03/01 22:17:10 petere Exp $
12 13 14
 *
 *-------------------------------------------------------------------------
 */
15
#include "postgres.h"
16

17 18
#include <ctype.h>

19
#include "access/tupmacs.h"
20 21
#include "catalog/catalog.h"
#include "catalog/pg_type.h"
Bruce Momjian's avatar
Bruce Momjian committed
22 23 24
#include "utils/array.h"
#include "utils/memutils.h"
#include "utils/syscache.h"
25

26

27 28
/*
 * An array has the following internal structure:
29 30 31 32 33 34
 *	  <nbytes>		- total number of bytes
 *	  <ndim>		- number of dimensions of the array
 *	  <flags>		- bit mask of flags
 *	  <dim>			- size of each array axis
 *	  <dim_lower>	- lower boundary of each dimension
 *	  <actual data> - whatever is the stored data
35
 * The actual data starts on a MAXALIGN boundary.
36 37 38 39 40 41
 *
 * NOTE: it is important that array elements of toastable datatypes NOT be
 * toasted, since the tupletoaster won't know they are there.  (We could
 * support compressed toasted items; only out-of-line items are dangerous.
 * However, it seems preferable to store such items uncompressed and allow
 * the toaster to compress the whole array as one input.)
42
 */
43

44 45 46 47 48 49 50 51 52 53

/* ----------
 * Local definitions
 * ----------
 */
#define ASSGN	 "="

#define RETURN_NULL(type)  do { *isNull = true; return (type) 0; } while (0)


54
static int	ArrayCount(char *str, int *dim, int typdelim);
55
static Datum *ReadArrayStr(char *arrayStr, int nitems, int ndim, int *dim,
56 57 58
			 FmgrInfo *inputproc, Oid typelem, int32 typmod,
			 char typdelim, int typlen, bool typbyval,
			 char typalign, int *nbytes);
59
static void CopyArrayEls(char *p, Datum *values, int nitems,
60 61
			 bool typbyval, int typlen, char typalign,
			 bool freedata);
62
static void system_cache_lookup(Oid element_type, bool input, int *typlen,
63 64
					bool *typbyval, char *typdelim, Oid *typelem,
					Oid *proc, char *typalign);
65
static Datum ArrayCast(char *value, bool byval, int len);
66
static int	ArrayCastAndSet(Datum src, bool typbyval, int typlen, char *dest);
67
static int	array_nelems_size(char *ptr, int eltsize, int nitems);
68
static char *array_seek(char *ptr, int eltsize, int nitems);
69
static int	array_copy(char *destptr, int eltsize, int nitems, char *srcptr);
70 71
static int array_slice_size(int ndim, int *dim, int *lb, char *arraydataptr,
				 int eltsize, int *st, int *endp);
72
static void array_extract_slice(int ndim, int *dim, int *lb,
73 74
					char *arraydataptr, int eltsize,
					int *st, int *endp, char *destPtr);
75
static void array_insert_slice(int ndim, int *dim, int *lb,
76 77 78
				   char *origPtr, int origdatasize,
				   char *destPtr, int eltsize,
				   int *st, int *endp, char *srcPtr);
79

80

81
/*---------------------------------------------------------------------
82 83
 * array_in :
 *		  converts an array from the external format in "string" to
84
 *		  its internal format.
85
 * return value :
86
 *		  the internal representation of the input array
87 88
 *--------------------------------------------------------------------
 */
89 90
Datum
array_in(PG_FUNCTION_ARGS)
91
{
92 93 94 95 96
	char	   *string = PG_GETARG_CSTRING(0);	/* external form */
	Oid			element_type = PG_GETARG_OID(1);		/* type of an array
														 * element */
	int32		typmod = PG_GETARG_INT32(2);	/* typmod for array
												 * elements */
97
	int			typlen;
98
	bool		typbyval;
99 100 101 102
	char		typdelim;
	Oid			typinput;
	Oid			typelem;
	char	   *string_save,
103
			   *p;
104
	FmgrInfo	inputproc;
105
	int			i,
106
				nitems;
107
	int32		nbytes;
108
	Datum	   *dataPtr;
109
	ArrayType  *retval;
110 111 112 113
	int			ndim,
				dim[MAXDIM],
				lBound[MAXDIM];
	char		typalign;
114

115
	/* Get info about element type, including its input conversion proc */
116 117
	system_cache_lookup(element_type, true, &typlen, &typbyval, &typdelim,
						&typelem, &typinput, &typalign);
118
	fmgr_info(typinput, &inputproc);
119

120 121
	/* Make a modifiable copy of the input */
	/* XXX why are we allocating an extra 2 bytes here? */
122 123 124
	string_save = (char *) palloc(strlen(string) + 3);
	strcpy(string_save, string);

125 126 127 128 129
	/*
	 * If the input string starts with dimension info, read and use that.
	 * Otherwise, we require the input to be in curly-brace style, and we
	 * prescan the input to determine dimensions.
	 *
130 131
	 * Dimension info takes the form of one or more [n] or [m:n] items. The
	 * outer loop iterates once per dimension item.
132 133 134 135
	 */
	p = string_save;
	ndim = 0;
	for (;;)
136
	{
137 138 139 140 141 142 143
		char	   *q;
		int			ub;

		/*
		 * Note: we currently allow whitespace between, but not within,
		 * dimension items.
		 */
144
		while (isspace((unsigned char) *p))
145
			p++;
146 147 148 149 150
		if (*p != '[')
			break;				/* no more dimension items */
		p++;
		if (ndim >= MAXDIM)
			elog(ERROR, "array_in: more than %d dimensions", MAXDIM);
151
		for (q = p; isdigit((unsigned char) *q); q++);
152 153 154
		if (q == p)				/* no digits? */
			elog(ERROR, "array_in: missing dimension value");
		if (*q == ':')
155
		{
156
			/* [m:n] format */
157
			*q = '\0';
158
			lBound[ndim] = atoi(p);
159
			p = q + 1;
160
			for (q = p; isdigit((unsigned char) *q); q++);
161 162
			if (q == p)			/* no digits? */
				elog(ERROR, "array_in: missing dimension value");
163 164
		}
		else
165 166 167 168 169 170 171 172 173 174 175 176 177
		{
			/* [n] format */
			lBound[ndim] = 1;
		}
		if (*q != ']')
			elog(ERROR, "array_in: missing ']' in array declaration");
		*q = '\0';
		ub = atoi(p);
		p = q + 1;
		if (ub < lBound[ndim])
			elog(ERROR, "array_in: upper_bound cannot be < lower_bound");
		dim[ndim] = ub - lBound[ndim] + 1;
		ndim++;
178 179 180 181
	}

	if (ndim == 0)
	{
182 183
		/* No array dimensions, so intuit dimensions from brace structure */
		if (*p != '{')
184
			elog(ERROR, "array_in: Need to specify dimension");
185 186 187
		ndim = ArrayCount(p, dim, typdelim);
		for (i = 0; i < ndim; i++)
			lBound[i] = 1;
188 189 190
	}
	else
	{
191
		/* If array dimensions are given, expect '=' operator */
192
		if (strncmp(p, ASSGN, strlen(ASSGN)) != 0)
193
			elog(ERROR, "array_in: missing assignment operator");
194
		p += strlen(ASSGN);
195
		while (isspace((unsigned char) *p))
196 197 198
			p++;
	}

199
#ifdef ARRAYDEBUG
200 201 202 203 204 205
	printf("array_in- ndim %d (", ndim);
	for (i = 0; i < ndim; i++)
	{
		printf(" %d", dim[i]);
	};
	printf(") for %s\n", string);
206 207
#endif

208
	nitems = ArrayGetNItems(ndim, dim);
209 210
	if (nitems == 0)
	{
211
		/* Return empty array */
212 213
		retval = (ArrayType *) palloc(sizeof(ArrayType));
		MemSet(retval, 0, sizeof(ArrayType));
214
		retval->size = sizeof(ArrayType);
215
		PG_RETURN_ARRAYTYPE_P(retval);
216 217
	}

218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236
	if (*p != '{')
		elog(ERROR, "array_in: missing left brace");

	dataPtr = ReadArrayStr(p, nitems, ndim, dim, &inputproc, typelem,
						   typmod, typdelim, typlen, typbyval, typalign,
						   &nbytes);
	nbytes += ARR_OVERHEAD(ndim);
	retval = (ArrayType *) palloc(nbytes);
	MemSet(retval, 0, nbytes);
	retval->size = nbytes;
	retval->ndim = ndim;
	memcpy((char *) ARR_DIMS(retval), (char *) dim,
		   ndim * sizeof(int));
	memcpy((char *) ARR_LBOUND(retval), (char *) lBound,
		   ndim * sizeof(int));

	CopyArrayEls(ARR_DATA_PTR(retval), dataPtr, nitems,
				 typbyval, typlen, typalign, true);
	pfree(dataPtr);
237
	pfree(string_save);
238
	PG_RETURN_ARRAYTYPE_P(retval);
239 240 241
}

/*-----------------------------------------------------------------------------
242
 * ArrayCount
243
 *	 Counts the number of dimensions and the *dim array for an array string.
244
 *		 The syntax for array input is C-like nested curly braces
245 246 247
 *-----------------------------------------------------------------------------
 */
static int
248
ArrayCount(char *str, int *dim, int typdelim)
249
{
250 251 252 253 254 255 256
	int			nest_level = 0,
				i;
	int			ndim = 0,
				temp[MAXDIM];
	bool		scanning_string = false;
	bool		eoArray = false;
	char	   *q;
257 258 259 260 261

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

	if (strncmp(str, "{}", 2) == 0)
262
		return 0;
263 264 265 266

	q = str;
	while (eoArray != true)
	{
267
		bool		done = false;
268 269 270 271 272

		while (!done)
		{
			switch (*q)
			{
273 274 275 276 277 278 279 280 281 282 283
				case '\\':
					/* skip escaped characters (\ and ") inside strings */
					if (scanning_string && *(q + 1))
						q++;
					break;
				case '\0':

					/*
					 * Signal a premature end of the string.  DZ -
					 * 2-9-1996
					 */
284
					elog(ERROR, "malformed array constant: %s", str);
285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308
					break;
				case '\"':
					scanning_string = !scanning_string;
					break;
				case '{':
					if (!scanning_string)
					{
						temp[nest_level] = 0;
						nest_level++;
					}
					break;
				case '}':
					if (!scanning_string)
					{
						if (!ndim)
							ndim = nest_level;
						nest_level--;
						if (nest_level)
							temp[nest_level - 1]++;
						if (nest_level == 0)
							eoArray = done = true;
					}
					break;
				default:
309 310
					if (!ndim)
						ndim = nest_level;
311 312 313
					if (*q == typdelim && !scanning_string)
						done = true;
					break;
314 315 316
			}
			if (!done)
				q++;
317
		}
318 319 320
		temp[ndim - 1]++;
		q++;
		if (!eoArray)
321
			while (isspace((unsigned char) *q))
322 323 324 325 326
				q++;
	}
	for (i = 0; i < ndim; ++i)
		dim[i] = temp[i];

327
	return ndim;
328 329 330
}

/*---------------------------------------------------------------------------
331 332
 * ReadArrayStr :
 *	 parses the array string pointed by "arrayStr" and converts it to
333 334 335 336
 *	 internal format. The external format expected is like C array
 *	 declaration. Unspecified elements are initialized to zero for fixed length
 *	 base types and to empty varlena structures for variable length base
 *	 types.
337
 * result :
338 339 340 341
 *	 returns a palloc'd array of Datum representations of the array elements.
 *	 If element type is pass-by-ref, the Datums point to palloc'd values.
 *	 *nbytes is set to the amount of data space needed for the array,
 *	 including alignment padding but not including array header overhead.
342
 *	 CAUTION: the contents of "arrayStr" may be modified!
343 344
 *---------------------------------------------------------------------------
 */
345 346 347 348 349 350 351 352 353 354 355 356 357
static Datum *
ReadArrayStr(char *arrayStr,
			 int nitems,
			 int ndim,
			 int *dim,
			 FmgrInfo *inputproc,
			 Oid typelem,
			 int32 typmod,
			 char typdelim,
			 int typlen,
			 bool typbyval,
			 char typalign,
			 int *nbytes)
358
{
359 360
	int			i,
				nest_level = 0;
361
	Datum	   *values;
362 363
	char	   *p,
			   *q,
364
			   *r;
365 366 367 368
	bool		scanning_string = false;
	int			indx[MAXDIM],
				prod[MAXDIM];
	bool		eoArray = false;
369 370

	mda_get_prod(ndim, dim, prod);
371 372 373
	values = (Datum *) palloc(nitems * sizeof(Datum));
	MemSet(values, 0, nitems * sizeof(Datum));
	MemSet(indx, 0, sizeof(indx));
374 375
	q = p = arrayStr;

376
	/* read array enclosed within {} */
377 378
	while (!eoArray)
	{
379 380
		bool		done = false;
		int			i = -1;
381 382 383 384 385

		while (!done)
		{
			switch (*q)
			{
386 387 388 389 390 391 392
				case '\\':
					/* Crunch the string on top of the backslash. */
					for (r = q; *r != '\0'; r++)
						*r = *(r + 1);
					break;
				case '\"':
					if (!scanning_string)
393
					{
394 395 396 397 398
						while (p != q)
							p++;
						p++;	/* get p past first doublequote */
					}
					else
399
						*q = '\0';
400 401 402 403 404 405 406 407
					scanning_string = !scanning_string;
					break;
				case '{':
					if (!scanning_string)
					{
						p++;
						nest_level++;
						if (nest_level > ndim)
408
							elog(ERROR, "array_in: illformed array constant");
409 410
						indx[nest_level - 1] = 0;
						indx[ndim - 1] = 0;
411
					}
412 413 414 415 416
					break;
				case '}':
					if (!scanning_string)
					{
						if (i == -1)
417
							i = ArrayGetOffset0(ndim, indx, prod);
418 419 420 421 422 423 424 425 426 427 428 429 430 431
						nest_level--;
						if (nest_level == 0)
							eoArray = done = true;
						else
						{
							*q = '\0';
							indx[nest_level - 1]++;
						}
					}
					break;
				default:
					if (*q == typdelim && !scanning_string)
					{
						if (i == -1)
432
							i = ArrayGetOffset0(ndim, indx, prod);
433 434 435 436
						done = true;
						indx[ndim - 1]++;
					}
					break;
437 438 439
			}
			if (!done)
				q++;
440
		}
441 442
		*q = '\0';
		if (i >= nitems)
443
			elog(ERROR, "array_in: illformed array constant");
444 445 446 447
		values[i] = FunctionCall3(inputproc,
								  CStringGetDatum(p),
								  ObjectIdGetDatum(typelem),
								  Int32GetDatum(typmod));
448
		p = ++q;
449

450 451 452
		/*
		 * if not at the end of the array skip white space
		 */
453
		if (!eoArray)
454
			while (isspace((unsigned char) *q))
455 456 457 458 459
			{
				p++;
				q++;
			}
	}
460

461 462 463
	/*
	 * Initialize any unset items and compute total data space needed
	 */
464 465 466 467 468
	if (typlen > 0)
	{
		*nbytes = nitems * typlen;
		if (!typbyval)
			for (i = 0; i < nitems; i++)
469
				if (values[i] == (Datum) 0)
470
				{
471 472
					values[i] = PointerGetDatum(palloc(typlen));
					MemSet(DatumGetPointer(values[i]), 0, typlen);
473 474 475 476
				}
	}
	else
	{
477 478
		*nbytes = 0;
		for (i = 0; i < nitems; i++)
479
		{
480
			if (values[i] != (Datum) 0)
481
			{
482 483
				/* let's just make sure data is not toasted */
				values[i] = PointerGetDatum(PG_DETOAST_DATUM(values[i]));
484
				if (typalign == 'd')
485
					*nbytes += MAXALIGN(VARSIZE(DatumGetPointer(values[i])));
486
				else
487
					*nbytes += INTALIGN(VARSIZE(DatumGetPointer(values[i])));
488 489 490 491
			}
			else
			{
				*nbytes += sizeof(int32);
492 493
				values[i] = PointerGetDatum(palloc(sizeof(int32)));
				VARATT_SIZEP(DatumGetPointer(values[i])) = sizeof(int32);
494
			}
495
		}
496
	}
497
	return values;
498 499 500
}


501 502 503 504 505 506 507 508 509
/*----------
 * Copy data into an array object from a temporary array of Datums.
 *
 * p: pointer to start of array data area
 * values: array of Datums to be copied
 * nitems: number of Datums to be copied
 * typbyval, typlen, typalign: info about element datatype
 * freedata: if TRUE and element type is pass-by-ref, pfree data values
 * referenced by Datums after copying them.
510 511
 *
 * If the input data is of varlena type, the caller must have ensured that
512
 * the values are not toasted.	(Doing it here doesn't work since the
513
 * caller has already allocated space for the array...)
514 515
 *----------
 */
516
static void
517 518 519 520 521 522 523
CopyArrayEls(char *p,
			 Datum *values,
			 int nitems,
			 bool typbyval,
			 int typlen,
			 char typalign,
			 bool freedata)
524
{
525
	int			i;
526 527 528 529
	int			inc;

	if (typbyval)
		freedata = false;
530 531 532

	for (i = 0; i < nitems; i++)
	{
533
		inc = ArrayCastAndSet(values[i], typbyval, typlen, p);
534
		p += inc;
535 536
		if (freedata)
			pfree(DatumGetPointer(values[i]));
537
	}
538 539 540
}

/*-------------------------------------------------------------------------
541 542 543
 * array_out :
 *		   takes the internal representation of an array and returns a string
 *		  containing the array in its external format.
544 545
 *-------------------------------------------------------------------------
 */
546 547
Datum
array_out(PG_FUNCTION_ARGS)
548
{
549
	ArrayType  *v = PG_GETARG_ARRAYTYPE_P(0);
550
	Oid			element_type = PG_GETARG_OID(1);
551 552 553 554 555
	int			typlen;
	bool		typbyval;
	char		typdelim;
	Oid			typoutput,
				typelem;
556
	FmgrInfo	outputproc;
557
	char		typalign;
558 559
	char	   *p,
			   *tmp,
560
			   *retval,
561 562
			  **values;
	bool	   *needquotes;
563 564 565 566 567 568 569 570
	int			nitems,
				overall_length,
				i,
				j,
				k,
				indx[MAXDIM];
	int			ndim,
			   *dim;
571 572 573

	system_cache_lookup(element_type, false, &typlen, &typbyval,
						&typdelim, &typelem, &typoutput, &typalign);
574
	fmgr_info(typoutput, &outputproc);
575 576
	ndim = ARR_NDIM(v);
	dim = ARR_DIMS(v);
577
	nitems = ArrayGetNItems(ndim, dim);
578 579 580

	if (nitems == 0)
	{
581
		retval = pstrdup("{}");
582
		PG_RETURN_CSTRING(retval);
583 584
	}

585 586 587 588 589 590 591
	/*
	 * Convert all values to string form, count total space needed
	 * (including any overhead such as escaping backslashes),
	 * and detect whether each item needs double quotes.
	 */
	values = (char **) palloc(nitems * sizeof(char *));
	needquotes = (bool *) palloc(nitems * sizeof(bool));
592 593 594 595
	p = ARR_DATA_PTR(v);
	overall_length = 1;			/* [TRH] don't forget to count \0 at end. */
	for (i = 0; i < nitems; i++)
	{
596
		Datum		itemvalue;
597
		bool		nq;
598 599 600 601

		itemvalue = fetch_att(p, typbyval, typlen);
		values[i] = DatumGetCString(FunctionCall3(&outputproc,
												  itemvalue,
602
											   ObjectIdGetDatum(typelem),
603 604
												  Int32GetDatum(-1)));
		if (typlen > 0)
605 606
			p += typlen;
		else
607
			p += INTALIGN(*(int32 *) p);
608

609 610
		/* count data plus backslashes; detect chars needing quotes */
		nq = (values[i][0] == '\0');	/* force quotes for empty string */
611 612
		for (tmp = values[i]; *tmp; tmp++)
		{
613 614
			char	ch = *tmp;

615
			overall_length += 1;
616 617 618
			if (ch == '"' || ch == '\\')
			{
				nq = true;
619
#ifndef TCL_ARRAYS
620
				overall_length += 1;
621
#endif
622 623 624 625
			}
			else if (ch == '{' || ch == '}' || ch == typdelim ||
					 isspace((unsigned char) ch))
				nq = true;
626
		}
627 628 629 630 631 632 633 634

		needquotes[i] = nq;

		/* Count the pair of double quotes, if needed */
		if (nq)
			overall_length += 2;

		/* and the comma */
635
		overall_length += 1;
636 637 638 639
	}

	/*
	 * count total number of curly braces in output string
640
	 */
641 642
	for (i = j = 0, k = 1; i < ndim; k *= dim[i++], j += k);

643 644 645 646 647
	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')
648

649
	APPENDCHAR('{');
650 651 652 653 654 655
	for (i = 0; i < ndim; indx[i++] = 0);
	j = 0;
	k = 0;
	do
	{
		for (i = j; i < ndim - 1; i++)
656
			APPENDCHAR('{');
657

658
		if (needquotes[k])
659
		{
660
			APPENDCHAR('"');
661
#ifndef TCL_ARRAYS
662 663
			for (tmp = values[k]; *tmp; tmp++)
			{
664 665 666 667 668
				char	ch = *tmp;

				if (ch == '"' || ch == '\\')
					*p++ = '\\';
				*p++ = ch;
669
			}
670
			*p = '\0';
671
#else
672
			APPENDSTR(values[k]);
673
#endif
674
			APPENDCHAR('"');
675 676
		}
		else
677
			APPENDSTR(values[k]);
678 679 680 681 682 683 684
		pfree(values[k++]);

		for (i = ndim - 1; i >= 0; i--)
		{
			indx[i] = (indx[i] + 1) % dim[i];
			if (indx[i])
			{
685
				APPENDCHAR(typdelim);
686 687 688
				break;
			}
			else
689
				APPENDCHAR('}');
690 691 692 693
		}
		j = i;
	} while (j != -1);

694 695 696
#undef APPENDSTR
#undef APPENDCHAR

697
	pfree(values);
698 699
	pfree(needquotes);

700
	PG_RETURN_CSTRING(retval);
701 702 703 704
}

/*-----------------------------------------------------------------------------
 * array_dims :
705
 *		  returns the dimensions of the array pointed to by "v", as a "text"
706
 *----------------------------------------------------------------------------
707
 */
708 709
Datum
array_dims(PG_FUNCTION_ARGS)
710
{
711
	ArrayType  *v = PG_GETARG_ARRAYTYPE_P(0);
712 713
	text	   *result;
	char	   *p;
714 715 716 717
	int			nbytes,
				i;
	int		   *dimv,
			   *lb;
718

719 720 721 722
	/* Sanity check: does it look like an array at all? */
	if (ARR_NDIM(v) <= 0 || ARR_NDIM(v) > MAXDIM)
		PG_RETURN_NULL();

723
	nbytes = ARR_NDIM(v) * 33 + 1;
724

725 726
	/*
	 * 33 since we assume 15 digits per number + ':' +'[]'
727 728
	 *
	 * +1 allows for temp trailing null
729
	 */
730 731 732 733

	result = (text *) palloc(nbytes + VARHDRSZ);
	p = VARDATA(result);

734 735
	dimv = ARR_DIMS(v);
	lb = ARR_LBOUND(v);
736

737 738 739 740 741
	for (i = 0; i < ARR_NDIM(v); i++)
	{
		sprintf(p, "[%d:%d]", lb[i], dimv[i] + lb[i] - 1);
		p += strlen(p);
	}
Jan Wieck's avatar
TOAST  
Jan Wieck committed
742
	VARATT_SIZEP(result) = strlen(VARDATA(result)) + VARHDRSZ;
743 744

	PG_RETURN_TEXT_P(result);
745
}
746 747 748

/*---------------------------------------------------------------------------
 * array_ref :
749
 *	  This routine takes an array pointer and an index array and returns
750 751
 *	  the referenced item as a Datum.  Note that for a pass-by-reference
 *	  datatype, the returned Datum is a pointer into the array object.
752 753 754
 *---------------------------------------------------------------------------
 */
Datum
Bruce Momjian's avatar
Bruce Momjian committed
755
array_ref(ArrayType *array,
756
		  int nSubscripts,
757
		  int *indx,
758
		  bool elmbyval,
759 760
		  int elmlen,
		  int arraylen,
761
		  bool *isNull)
762
{
763 764
	int			i,
				ndim,
765 766
			   *dim,
			   *lb,
767 768 769 770 771
				offset,
				fixedDim[1],
				fixedLb[1];
	char	   *arraydataptr,
			   *retptr;
772 773

	if (array == (ArrayType *) NULL)
774
		RETURN_NULL(Datum);
775

776 777 778
	if (arraylen > 0)
	{
		/*
779
		 * fixed-length arrays -- these are assumed to be 1-d, 0-based
780
		 */
781 782 783 784 785 786
		ndim = 1;
		fixedDim[0] = arraylen / elmlen;
		fixedLb[0] = 0;
		dim = fixedDim;
		lb = fixedLb;
		arraydataptr = (char *) array;
787
	}
788 789 790 791
	else
	{
		/* detoast input if necessary */
		array = DatumGetArrayTypeP(PointerGetDatum(array));
792

793 794 795 796 797
		ndim = ARR_NDIM(array);
		dim = ARR_DIMS(array);
		lb = ARR_LBOUND(array);
		arraydataptr = ARR_DATA_PTR(array);
	}
798

799 800 801 802
	/*
	 * Return NULL for invalid subscript
	 */
	if (ndim != nSubscripts || ndim <= 0 || ndim > MAXDIM)
803
		RETURN_NULL(Datum);
804 805 806
	for (i = 0; i < ndim; i++)
		if (indx[i] < lb[i] || indx[i] >= (dim[i] + lb[i]))
			RETURN_NULL(Datum);
807

808 809 810
	/*
	 * OK, get the element
	 */
811
	offset = ArrayGetOffset(nSubscripts, dim, lb, indx);
812

813
	retptr = array_seek(arraydataptr, elmlen, offset);
814

815
	*isNull = false;
816
	return ArrayCast(retptr, elmbyval, elmlen);
817 818 819
}

/*-----------------------------------------------------------------------------
820 821
 * array_get_slice :
 *		   This routine takes an array and a range of indices (upperIndex and
822 823
 *		   lowerIndx), creates a new array structure for the referred elements
 *		   and returns a pointer to it.
824 825 826
 *
 * NOTE: we assume it is OK to scribble on the provided index arrays
 * lowerIndx[] and upperIndx[].  These are generally just temporaries.
827 828
 *-----------------------------------------------------------------------------
 */
829
ArrayType *
830 831 832 833 834 835 836 837
array_get_slice(ArrayType *array,
				int nSubscripts,
				int *upperIndx,
				int *lowerIndx,
				bool elmbyval,
				int elmlen,
				int arraylen,
				bool *isNull)
838
{
839 840 841
	int			i,
				ndim,
			   *dim,
842
			   *lb;
843 844 845 846
	int			fixedDim[1],
				fixedLb[1];
	char	   *arraydataptr;
	ArrayType  *newarray;
847 848
	int			bytes,
				span[MAXDIM];
849 850

	if (array == (ArrayType *) NULL)
851
		RETURN_NULL(ArrayType *);
852 853 854 855

	if (arraylen > 0)
	{
		/*
856 857 858 859
		 * fixed-length arrays -- currently, cannot slice these because
		 * parser labels output as being of the fixed-length array type!
		 * Code below shows how we could support it if the parser were
		 * changed to label output as a suitable varlena array type.
860 861 862
		 */
		elog(ERROR, "Slices of fixed-length arrays not implemented");

863 864 865 866 867 868 869 870 871 872 873 874 875 876
		/*
		 * fixed-length arrays -- these are assumed to be 1-d, 0-based
		 */
		ndim = 1;
		fixedDim[0] = arraylen / elmlen;
		fixedLb[0] = 0;
		dim = fixedDim;
		lb = fixedLb;
		arraydataptr = (char *) array;
	}
	else
	{
		/* detoast input if necessary */
		array = DatumGetArrayTypeP(PointerGetDatum(array));
877

878 879 880 881 882
		ndim = ARR_NDIM(array);
		dim = ARR_DIMS(array);
		lb = ARR_LBOUND(array);
		arraydataptr = ARR_DATA_PTR(array);
	}
883

884 885
	/*
	 * Check provided subscripts.  A slice exceeding the current array
886 887 888
	 * limits is silently truncated to the array limits.  If we end up
	 * with an empty slice, return NULL (should it be an empty array
	 * instead?)
889 890
	 */
	if (ndim != nSubscripts || ndim <= 0 || ndim > MAXDIM)
891
		RETURN_NULL(ArrayType *);
892

893 894 895 896 897 898
	for (i = 0; i < ndim; i++)
	{
		if (lowerIndx[i] < lb[i])
			lowerIndx[i] = lb[i];
		if (upperIndx[i] >= (dim[i] + lb[i]))
			upperIndx[i] = dim[i] + lb[i] - 1;
899
		if (lowerIndx[i] > upperIndx[i])
900
			RETURN_NULL(ArrayType *);
901
	}
902

903
	mda_get_range(nSubscripts, span, lowerIndx, upperIndx);
904

905 906 907 908 909 910 911 912 913 914 915 916 917 918
	bytes = array_slice_size(ndim, dim, lb, arraydataptr,
							 elmlen, lowerIndx, upperIndx);
	bytes += ARR_OVERHEAD(ndim);

	newarray = (ArrayType *) palloc(bytes);
	newarray->size = bytes;
	newarray->ndim = ndim;
	newarray->flags = 0;
	memcpy(ARR_DIMS(newarray), span, ndim * sizeof(int));
	memcpy(ARR_LBOUND(newarray), lowerIndx, ndim * sizeof(int));
	array_extract_slice(ndim, dim, lb, arraydataptr, elmlen,
						lowerIndx, upperIndx, ARR_DATA_PTR(newarray));

	return newarray;
919 920 921
}

/*-----------------------------------------------------------------------------
922
 * array_set :
923 924
 *		  This routine sets the value of an array location (specified by
 *		  an index array) to a new value specified by "dataValue".
925
 * result :
926 927 928
 *		  A new array is returned, just like the old except for the one
 *		  modified entry.
 *
929 930 931 932 933
 * For one-dimensional arrays only, we allow the array to be extended
 * by assigning to the position one above or one below the existing range.
 * (We could be more flexible if we had a way to represent NULL elements.)
 *
 * NOTE: For assignments, we throw an error for invalid subscripts etc,
934 935 936
 * rather than returning a NULL as the fetch operations do.  The reasoning
 * is that returning a NULL would cause the user's whole array to be replaced
 * with NULL, which will probably not make him happy.
937 938
 *-----------------------------------------------------------------------------
 */
939
ArrayType *
Bruce Momjian's avatar
Bruce Momjian committed
940
array_set(ArrayType *array,
941
		  int nSubscripts,
942
		  int *indx,
943 944
		  Datum dataValue,
		  bool elmbyval,
945 946
		  int elmlen,
		  int arraylen,
947
		  bool *isNull)
948
{
949 950 951 952
	int			i,
				ndim,
				dim[MAXDIM],
				lb[MAXDIM],
953 954 955
				offset;
	ArrayType  *newarray;
	char	   *elt_ptr;
956 957 958
	bool		extendbefore = false;
	bool		extendafter = false;
	int			olddatasize,
959
				newsize,
960 961 962 963 964
				olditemlen,
				newitemlen,
				overheadlen,
				lenbefore,
				lenafter;
965 966

	if (array == (ArrayType *) NULL)
967
		RETURN_NULL(ArrayType *);
968

969 970 971
	if (arraylen > 0)
	{
		/*
972 973
		 * fixed-length arrays -- these are assumed to be 1-d, 0-based. We
		 * cannot extend them, either.
974
		 */
975 976 977 978 979 980 981 982 983
		if (nSubscripts != 1)
			elog(ERROR, "Invalid array subscripts");
		if (indx[0] < 0 || indx[0] * elmlen >= arraylen)
			elog(ERROR, "Invalid array subscripts");
		newarray = (ArrayType *) palloc(arraylen);
		memcpy(newarray, array, arraylen);
		elt_ptr = (char *) newarray + indx[0] * elmlen;
		ArrayCastAndSet(dataValue, elmbyval, elmlen, elt_ptr);
		return newarray;
984
	}
985

986 987 988 989
	/* make sure item to be inserted is not toasted */
	if (elmlen < 0)
		dataValue = PointerGetDatum(PG_DETOAST_DATUM(dataValue));

990 991 992 993
	/* detoast input if necessary */
	array = DatumGetArrayTypeP(PointerGetDatum(array));

	ndim = ARR_NDIM(array);
994
	if (ndim != nSubscripts || ndim <= 0 || ndim > MAXDIM)
995
		elog(ERROR, "Invalid array subscripts");
996

997 998 999
	/* copy dim/lb since we may modify them */
	memcpy(dim, ARR_DIMS(array), ndim * sizeof(int));
	memcpy(lb, ARR_LBOUND(array), ndim * sizeof(int));
1000

1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027
	/*
	 * Check subscripts
	 */
	for (i = 0; i < ndim; i++)
	{
		if (indx[i] < lb[i])
		{
			if (ndim == 1 && indx[i] == lb[i] - 1)
			{
				dim[i]++;
				lb[i]--;
				extendbefore = true;
			}
			else
				elog(ERROR, "Invalid array subscripts");
		}
		if (indx[i] >= (dim[i] + lb[i]))
		{
			if (ndim == 1 && indx[i] == (dim[i] + lb[i]))
			{
				dim[i]++;
				extendafter = true;
			}
			else
				elog(ERROR, "Invalid array subscripts");
		}
	}
1028

1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040
	/*
	 * Compute sizes of items and areas to copy
	 */
	overheadlen = ARR_OVERHEAD(ndim);
	olddatasize = ARR_SIZE(array) - overheadlen;
	if (extendbefore)
	{
		lenbefore = 0;
		olditemlen = 0;
		lenafter = olddatasize;
	}
	else if (extendafter)
1041
	{
1042 1043 1044
		lenbefore = olddatasize;
		olditemlen = 0;
		lenafter = 0;
1045 1046 1047
	}
	else
	{
1048 1049 1050 1051 1052 1053 1054 1055
		offset = ArrayGetOffset(nSubscripts, dim, lb, indx);
		elt_ptr = array_seek(ARR_DATA_PTR(array), elmlen, offset);
		lenbefore = (int) (elt_ptr - ARR_DATA_PTR(array));
		if (elmlen > 0)
			olditemlen = elmlen;
		else
			olditemlen = INTALIGN(*(int32 *) elt_ptr);
		lenafter = (int) (olddatasize - lenbefore - olditemlen);
1056
	}
1057

1058 1059 1060 1061 1062 1063
	if (elmlen > 0)
		newitemlen = elmlen;
	else
		newitemlen = INTALIGN(*(int32 *) DatumGetPointer(dataValue));

	newsize = overheadlen + lenbefore + newitemlen + lenafter;
1064

1065 1066 1067
	/*
	 * OK, do the assignment
	 */
1068 1069
	newarray = (ArrayType *) palloc(newsize);
	newarray->size = newsize;
1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082
	newarray->ndim = ndim;
	newarray->flags = 0;
	memcpy(ARR_DIMS(newarray), dim, ndim * sizeof(int));
	memcpy(ARR_LBOUND(newarray), lb, ndim * sizeof(int));
	memcpy((char *) newarray + overheadlen,
		   (char *) array + overheadlen,
		   lenbefore);
	memcpy((char *) newarray + overheadlen + lenbefore + newitemlen,
		   (char *) array + overheadlen + lenbefore + olditemlen,
		   lenafter);

	ArrayCastAndSet(dataValue, elmbyval, elmlen,
					(char *) newarray + overheadlen + lenbefore);
1083 1084

	return newarray;
1085 1086 1087
}

/*----------------------------------------------------------------------------
1088
 * array_set_slice :
1089 1090 1091
 *		  This routine sets the value of a range of array locations (specified
 *		  by upper and lower index values ) to new values passed as
 *		  another array
1092
 * result :
1093 1094 1095 1096 1097 1098 1099
 *		  A new array is returned, just like the old except for the
 *		  modified range.
 *
 * NOTE: For assignments, we throw an error for silly subscripts etc,
 * rather than returning a NULL as the fetch operations do.  The reasoning
 * is that returning a NULL would cause the user's whole array to be replaced
 * with NULL, which will probably not make him happy.
1100 1101
 *----------------------------------------------------------------------------
 */
1102
ArrayType *
1103 1104 1105 1106 1107 1108 1109 1110 1111
array_set_slice(ArrayType *array,
				int nSubscripts,
				int *upperIndx,
				int *lowerIndx,
				ArrayType *srcArray,
				bool elmbyval,
				int elmlen,
				int arraylen,
				bool *isNull)
1112
{
1113 1114
	int			i,
				ndim,
1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126
				dim[MAXDIM],
				lb[MAXDIM],
				span[MAXDIM];
	ArrayType  *newarray;
	int			nsrcitems,
				olddatasize,
				newsize,
				olditemsize,
				newitemsize,
				overheadlen,
				lenbefore,
				lenafter;
1127 1128

	if (array == (ArrayType *) NULL)
1129
		RETURN_NULL(ArrayType *);
1130
	if (srcArray == (ArrayType *) NULL)
1131
		return array;
1132 1133 1134 1135

	if (arraylen > 0)
	{
		/*
1136
		 * fixed-length arrays -- not got round to doing this...
1137 1138 1139 1140
		 */
		elog(ERROR, "Updates on slices of fixed-length arrays not implemented");
	}

1141 1142
	/* detoast arrays if necessary */
	array = DatumGetArrayTypeP(PointerGetDatum(array));
1143 1144
	srcArray = DatumGetArrayTypeP(PointerGetDatum(srcArray));

1145 1146
	/* note: we assume srcArray contains no toasted elements */

1147
	ndim = ARR_NDIM(array);
1148
	if (ndim != nSubscripts || ndim <= 0 || ndim > MAXDIM)
1149
		elog(ERROR, "Invalid array subscripts");
1150

1151 1152 1153 1154 1155 1156 1157
	/* copy dim/lb since we may modify them */
	memcpy(dim, ARR_DIMS(array), ndim * sizeof(int));
	memcpy(lb, ARR_LBOUND(array), ndim * sizeof(int));

	/*
	 * Check provided subscripts.  A slice exceeding the current array
	 * limits throws an error, *except* in the 1-D case where we will
1158 1159
	 * extend the array as long as no hole is created. An empty slice is
	 * an error, too.
1160 1161 1162
	 */
	for (i = 0; i < ndim; i++)
	{
1163
		if (lowerIndx[i] > upperIndx[i])
1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182
			elog(ERROR, "Invalid array subscripts");
		if (lowerIndx[i] < lb[i])
		{
			if (ndim == 1 && upperIndx[i] >= lb[i] - 1)
			{
				dim[i] += lb[i] - lowerIndx[i];
				lb[i] = lowerIndx[i];
			}
			else
				elog(ERROR, "Invalid array subscripts");
		}
		if (upperIndx[i] >= (dim[i] + lb[i]))
		{
			if (ndim == 1 && lowerIndx[i] <= (dim[i] + lb[i]))
				dim[i] = upperIndx[i] - lb[i] + 1;
			else
				elog(ERROR, "Invalid array subscripts");
		}
	}
1183

1184
	/*
1185 1186
	 * Make sure source array has enough entries.  Note we ignore the
	 * shape of the source array and just read entries serially.
1187
	 */
1188
	mda_get_range(ndim, span, lowerIndx, upperIndx);
1189 1190
	nsrcitems = ArrayGetNItems(ndim, span);
	if (nsrcitems > ArrayGetNItems(ARR_NDIM(srcArray), ARR_DIMS(srcArray)))
1191 1192
		elog(ERROR, "Source array too small");

1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203
	/*
	 * Compute space occupied by new entries, space occupied by replaced
	 * entries, and required space for new array.
	 */
	newitemsize = array_nelems_size(ARR_DATA_PTR(srcArray), elmlen,
									nsrcitems);
	overheadlen = ARR_OVERHEAD(ndim);
	olddatasize = ARR_SIZE(array) - overheadlen;
	if (ndim > 1)
	{
		/*
1204 1205
		 * here we do not need to cope with extension of the array; it
		 * would be a lot more complicated if we had to do so...
1206 1207 1208
		 */
		olditemsize = array_slice_size(ndim, dim, lb, ARR_DATA_PTR(array),
									   elmlen, lowerIndx, upperIndx);
1209
		lenbefore = lenafter = 0;		/* keep compiler quiet */
1210 1211 1212 1213
	}
	else
	{
		/*
1214 1215
		 * here we must allow for possibility of slice larger than orig
		 * array
1216
		 */
1217 1218
		int			oldlb = ARR_LBOUND(array)[0];
		int			oldub = oldlb + ARR_DIMS(array)[0] - 1;
1219 1220
		int			slicelb = Max(oldlb, lowerIndx[0]);
		int			sliceub = Min(oldub, upperIndx[0]);
1221
		char	   *oldarraydata = ARR_DATA_PTR(array);
1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246

		lenbefore = array_nelems_size(oldarraydata,
									  elmlen,
									  slicelb - oldlb);
		if (slicelb > sliceub)
			olditemsize = 0;
		else
			olditemsize = array_nelems_size(oldarraydata + lenbefore,
											elmlen,
											sliceub - slicelb + 1);
		lenafter = olddatasize - lenbefore - olditemsize;
	}

	newsize = overheadlen + olddatasize - olditemsize + newitemsize;

	newarray = (ArrayType *) palloc(newsize);
	newarray->size = newsize;
	newarray->ndim = ndim;
	newarray->flags = 0;
	memcpy(ARR_DIMS(newarray), dim, ndim * sizeof(int));
	memcpy(ARR_LBOUND(newarray), lb, ndim * sizeof(int));

	if (ndim > 1)
	{
		/*
1247 1248
		 * here we do not need to cope with extension of the array; it
		 * would be a lot more complicated if we had to do so...
1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265
		 */
		array_insert_slice(ndim, dim, lb, ARR_DATA_PTR(array), olddatasize,
						   ARR_DATA_PTR(newarray), elmlen,
						   lowerIndx, upperIndx, ARR_DATA_PTR(srcArray));
	}
	else
	{
		memcpy((char *) newarray + overheadlen,
			   (char *) array + overheadlen,
			   lenbefore);
		memcpy((char *) newarray + overheadlen + lenbefore,
			   ARR_DATA_PTR(srcArray),
			   newitemsize);
		memcpy((char *) newarray + overheadlen + lenbefore + newitemsize,
			   (char *) array + overheadlen + lenbefore + olditemsize,
			   lenafter);
	}
1266

1267
	return newarray;
1268 1269
}

1270 1271 1272
/*
 * array_map()
 *
1273
 * Map an array through an arbitrary function.	Return a new array with
1274 1275 1276 1277 1278 1279 1280
 * same dimensions and each source element transformed by fn().  Each
 * source element is passed as the first argument to fn(); additional
 * arguments to be passed to fn() can be specified by the caller.
 * The output array can have a different element type than the input.
 *
 * Parameters are:
 * * fcinfo: a function-call data structure pre-constructed by the caller
1281 1282 1283 1284 1285
 *	 to be ready to call the desired function, with everything except the
 *	 first argument position filled in.  In particular, flinfo identifies
 *	 the function fn(), and if nargs > 1 then argument positions after the
 *	 first must be preset to the additional values to be passed.  The
 *	 first argument position initially holds the input array value.
1286
 * * inpType: OID of element type of input array.  This must be the same as,
1287 1288 1289
 *	 or binary-compatible with, the first argument type of fn().
 * * retType: OID of element type of output array.	This must be the same as,
 *	 or binary-compatible with, the result type of fn().
1290 1291 1292 1293
 *
 * NB: caller must assure that input array is not NULL.  Currently,
 * any additional parameters passed to fn() may not be specified as NULL
 * either.
1294
 */
1295 1296
Datum
array_map(FunctionCallInfo fcinfo, Oid inpType, Oid retType)
1297
{
1298
	ArrayType  *v;
Bruce Momjian's avatar
Bruce Momjian committed
1299
	ArrayType  *result;
1300
	Datum	   *values;
1301
	Datum		elt;
Bruce Momjian's avatar
Bruce Momjian committed
1302
	int		   *dim;
1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313
	int			ndim;
	int			nitems;
	int			i;
	int			nbytes = 0;
	int			inp_typlen;
	bool		inp_typbyval;
	int			typlen;
	bool		typbyval;
	char		typdelim;
	Oid			typelem;
	Oid			proc;
Bruce Momjian's avatar
Bruce Momjian committed
1314 1315
	char		typalign;
	char	   *s;
1316 1317 1318 1319 1320 1321

	/* Get input array */
	if (fcinfo->nargs < 1)
		elog(ERROR, "array_map: invalid nargs: %d", fcinfo->nargs);
	if (PG_ARGISNULL(0))
		elog(ERROR, "array_map: null input array");
1322
	v = PG_GETARG_ARRAYTYPE_P(0);
1323

Bruce Momjian's avatar
Bruce Momjian committed
1324 1325
	ndim = ARR_NDIM(v);
	dim = ARR_DIMS(v);
1326
	nitems = ArrayGetNItems(ndim, dim);
1327 1328

	/* Check for empty array */
Bruce Momjian's avatar
Bruce Momjian committed
1329
	if (nitems <= 0)
1330
		PG_RETURN_ARRAYTYPE_P(v);
1331 1332 1333 1334 1335 1336

	/* Lookup source and result types. Unneeded variables are reused. */
	system_cache_lookup(inpType, false, &inp_typlen, &inp_typbyval,
						&typdelim, &typelem, &proc, &typalign);
	system_cache_lookup(retType, false, &typlen, &typbyval,
						&typdelim, &typelem, &proc, &typalign);
1337 1338

	/* Allocate temporary array for new values */
1339
	values = (Datum *) palloc(nitems * sizeof(Datum));
1340 1341 1342

	/* Loop over source data */
	s = (char *) ARR_DATA_PTR(v);
Bruce Momjian's avatar
Bruce Momjian committed
1343 1344
	for (i = 0; i < nitems; i++)
	{
1345
		/* Get source element */
1346 1347 1348
		elt = fetch_att(s, inp_typbyval, inp_typlen);

		if (inp_typlen > 0)
1349
			s += inp_typlen;
Bruce Momjian's avatar
Bruce Momjian committed
1350
		else
1351
			s += INTALIGN(*(int32 *) s);
1352 1353

		/*
1354 1355
		 * Apply the given function to source elt and extra args.
		 *
1356 1357 1358
		 * We assume the extra args are non-NULL, so need not check whether
		 * fn() is strict.	Would need to do more work here to support
		 * arrays containing nulls, too.
1359
		 */
1360
		fcinfo->arg[0] = elt;
1361 1362
		fcinfo->argnull[0] = false;
		fcinfo->isnull = false;
1363
		values[i] = FunctionCallInvoke(fcinfo);
1364 1365
		if (fcinfo->isnull)
			elog(ERROR, "array_map: cannot handle NULL in array");
1366

1367 1368
		/* Ensure data is not toasted, and update total result size */
		if (typbyval || typlen > 0)
1369
			nbytes += typlen;
Bruce Momjian's avatar
Bruce Momjian committed
1370
		else
1371 1372 1373 1374
		{
			values[i] = PointerGetDatum(PG_DETOAST_DATUM(values[i]));
			nbytes += INTALIGN(VARSIZE(DatumGetPointer(values[i])));
		}
1375 1376 1377 1378 1379 1380 1381
	}

	/* Allocate and initialize the result array */
	nbytes += ARR_OVERHEAD(ndim);
	result = (ArrayType *) palloc(nbytes);
	MemSet(result, 0, nbytes);

1382 1383 1384
	result->size = nbytes;
	result->ndim = ndim;
	memcpy(ARR_DIMS(result), ARR_DIMS(v), 2 * ndim * sizeof(int));
1385

1386 1387 1388 1389
	/*
	 * Note: do not risk trying to pfree the results of the called
	 * function
	 */
1390 1391 1392
	CopyArrayEls(ARR_DATA_PTR(result), values, nitems,
				 typbyval, typlen, typalign, false);
	pfree(values);
1393

1394
	PG_RETURN_ARRAYTYPE_P(result);
1395 1396
}

1397
/*----------
1398
 * construct_array	--- simple method for constructing an array object
1399 1400 1401 1402 1403 1404 1405 1406 1407 1408
 *
 * elems: array of Datum items to become the array contents
 * nelems: number of items
 * elmbyval, elmlen, elmalign: info for the datatype of the items
 *
 * A palloc'd 1-D array object is constructed and returned.  Note that
 * elem values will be copied into the object even if pass-by-ref type.
 * NULL element values are not supported.
 *----------
 */
1409
ArrayType *
1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422 1423
construct_array(Datum *elems, int nelems,
				bool elmbyval, int elmlen, char elmalign)
{
	ArrayType  *result;
	int			nbytes;
	int			i;

	if (elmlen > 0)
	{
		/* XXX what about alignment? */
		nbytes = elmlen * nelems;
	}
	else
	{
1424
		/* varlena type ... make sure it is untoasted */
1425 1426
		nbytes = 0;
		for (i = 0; i < nelems; i++)
1427 1428
		{
			elems[i] = PointerGetDatum(PG_DETOAST_DATUM(elems[i]));
1429
			nbytes += INTALIGN(VARSIZE(DatumGetPointer(elems[i])));
1430
		}
1431 1432 1433 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 1451 1452 1453 1454 1455 1456 1457 1458 1459 1460 1461 1462 1463 1464 1465 1466 1467 1468 1469 1470
	}

	/* Allocate and initialize 1-D result array */
	nbytes += ARR_OVERHEAD(1);
	result = (ArrayType *) palloc(nbytes);

	result->size = nbytes;
	result->ndim = 1;
	result->flags = 0;
	ARR_DIMS(result)[0] = nelems;
	ARR_LBOUND(result)[0] = 1;

	CopyArrayEls(ARR_DATA_PTR(result), elems, nelems,
				 elmbyval, elmlen, elmalign, false);

	return result;
}

/*----------
 * deconstruct_array  --- simple method for extracting data from an array
 *
 * array: array object to examine (must not be NULL)
 * elmbyval, elmlen, elmalign: info for the datatype of the items
 * elemsp: return value, set to point to palloc'd array of Datum values
 * nelemsp: return value, set to number of extracted values
 *
 * If array elements are pass-by-ref data type, the returned Datums will
 * be pointers into the array object.
 *----------
 */
void
deconstruct_array(ArrayType *array,
				  bool elmbyval, int elmlen, char elmalign,
				  Datum **elemsp, int *nelemsp)
{
	Datum	   *elems;
	int			nelems;
	char	   *p;
	int			i;

1471
	nelems = ArrayGetNItems(ARR_NDIM(array), ARR_DIMS(array));
1472 1473 1474 1475 1476 1477 1478 1479 1480 1481 1482 1483
	if (nelems <= 0)
	{
		*elemsp = NULL;
		*nelemsp = 0;
		return;
	}
	*elemsp = elems = (Datum *) palloc(nelems * sizeof(Datum));
	*nelemsp = nelems;

	p = ARR_DATA_PTR(array);
	for (i = 0; i < nelems; i++)
	{
1484 1485
		elems[i] = fetch_att(p, elmbyval, elmlen);
		if (elmlen > 0)
1486 1487
			p += elmlen;
		else
1488
			p += INTALIGN(VARSIZE(p));
1489 1490 1491 1492
	}
}


1493 1494
/*-----------------------------------------------------------------------------
 * array_eq :
1495
 *		  compares two arrays for equality
1496
 * result :
1497
 *		  returns true if the arrays are equal, false otherwise.
1498 1499
 *
 * XXX bitwise equality is pretty bogus ...
1500 1501
 *-----------------------------------------------------------------------------
 */
1502 1503
Datum
array_eq(PG_FUNCTION_ARGS)
1504
{
1505 1506
	ArrayType  *array1 = PG_GETARG_ARRAYTYPE_P(0);
	ArrayType  *array2 = PG_GETARG_ARRAYTYPE_P(1);
1507 1508 1509 1510 1511 1512
	bool		result = true;

	if (ARR_SIZE(array1) != ARR_SIZE(array2))
		result = false;
	else if (memcmp(array1, array2, ARR_SIZE(array1)) != 0)
		result = false;
1513

1514 1515 1516 1517 1518
	/* Avoid leaking memory when handed toasted input. */
	PG_FREE_IF_COPY(array1, 0);
	PG_FREE_IF_COPY(array2, 1);

	PG_RETURN_BOOL(result);
1519 1520
}

1521

1522
/***************************************************************************/
1523
/******************|		  Support  Routines			  |*****************/
1524
/***************************************************************************/
1525

1526 1527
static void
system_cache_lookup(Oid element_type,
1528 1529
					bool input,
					int *typlen,
1530
					bool *typbyval,
1531
					char *typdelim,
1532 1533
					Oid *typelem,
					Oid *proc,
1534
					char *typalign)
1535
{
1536
	HeapTuple	typeTuple;
1537
	Form_pg_type typeStruct;
1538

1539 1540 1541
	typeTuple = SearchSysCache(TYPEOID,
							   ObjectIdGetDatum(element_type),
							   0, 0, 0);
1542
	if (!HeapTupleIsValid(typeTuple))
1543
		elog(ERROR, "array_out: Cache lookup failed for type %u",
1544
			 element_type);
1545
	typeStruct = (Form_pg_type) GETSTRUCT(typeTuple);
1546

1547 1548 1549 1550 1551 1552 1553 1554 1555
	*typlen = typeStruct->typlen;
	*typbyval = typeStruct->typbyval;
	*typdelim = typeStruct->typdelim;
	*typelem = typeStruct->typelem;
	*typalign = typeStruct->typalign;
	if (input)
		*proc = typeStruct->typinput;
	else
		*proc = typeStruct->typoutput;
1556
	ReleaseSysCache(typeTuple);
1557 1558
}

1559 1560 1561
/*
 * Fetch array element at pointer, converted correctly to a Datum
 */
1562
static Datum
1563
ArrayCast(char *value, bool byval, int len)
1564
{
1565
	return fetch_att(value, byval, len);
1566 1567
}

1568 1569 1570 1571 1572
/*
 * Copy datum to *dest and return total space used (including align padding)
 *
 * XXX this routine needs to be told typalign too!
 */
1573
static int
1574
ArrayCastAndSet(Datum src,
1575 1576 1577
				bool typbyval,
				int typlen,
				char *dest)
1578
{
1579
	int			inc;
1580 1581 1582 1583 1584

	if (typlen > 0)
	{
		if (typbyval)
		{
1585
			store_att_byval(dest, src, typlen);
1586 1587
			/* For by-val types, assume no alignment padding is needed */
			inc = typlen;
1588 1589
		}
		else
1590
		{
1591
			memmove(dest, DatumGetPointer(src), typlen);
1592 1593 1594
			/* XXX WRONG: need to consider type's alignment requirement */
			inc = typlen;
		}
1595 1596 1597
	}
	else
	{
1598 1599 1600 1601
		/* varlena type */
		memmove(dest, DatumGetPointer(src), VARSIZE(DatumGetPointer(src)));
		/* XXX WRONG: should use MAXALIGN or type's alignment requirement */
		inc = INTALIGN(VARSIZE(DatumGetPointer(src)));
1602
	}
1603

1604
	return inc;
1605
}
1606

1607 1608 1609 1610 1611 1612 1613
/*
 * Compute total size of the nitems array elements starting at *ptr
 *
 * XXX should consider alignment spec for fixed-length types
 */
static int
array_nelems_size(char *ptr, int eltsize, int nitems)
1614
{
1615
	char	   *origptr;
1616
	int			i;
1617

1618 1619 1620 1621 1622 1623 1624 1625
	/* fixed-size elements? */
	if (eltsize > 0)
		return eltsize * nitems;
	/* else assume they are varlena items */
	origptr = ptr;
	for (i = 0; i < nitems; i++)
		ptr += INTALIGN(*(int32 *) ptr);
	return ptr - origptr;
1626 1627
}

1628 1629 1630 1631 1632
/*
 * Advance ptr over nitems array elements
 */
static char *
array_seek(char *ptr, int eltsize, int nitems)
1633
{
1634 1635
	return ptr + array_nelems_size(ptr, eltsize, nitems);
}
1636

1637 1638 1639 1640 1641 1642 1643 1644 1645 1646 1647 1648
/*
 * Copy nitems array elements from srcptr to destptr
 *
 * Returns number of bytes copied
 */
static int
array_copy(char *destptr, int eltsize, int nitems, char *srcptr)
{
	int			numbytes = array_nelems_size(srcptr, eltsize, nitems);

	memmove(destptr, srcptr, numbytes);
	return numbytes;
1649 1650
}

1651 1652 1653 1654 1655
/*
 * Compute space needed for a slice of an array
 *
 * We assume the caller has verified that the slice coordinates are valid.
 */
1656
static int
1657 1658
array_slice_size(int ndim, int *dim, int *lb, char *arraydataptr,
				 int eltsize, int *st, int *endp)
1659
{
1660 1661 1662
	int			st_pos,
				span[MAXDIM],
				prod[MAXDIM],
1663 1664
				dist[MAXDIM],
				indx[MAXDIM];
1665
	char	   *ptr;
1666 1667
	int			i,
				j,
1668
				inc;
1669
	int			count = 0;
1670

1671 1672 1673 1674 1675 1676 1677 1678 1679 1680 1681 1682
	mda_get_range(ndim, span, st, endp);

	/* Pretty easy for fixed element length ... */
	if (eltsize > 0)
		return ArrayGetNItems(ndim, span) * eltsize;

	/* Else gotta do it the hard way */
	st_pos = ArrayGetOffset(ndim, dim, lb, st);
	ptr = array_seek(arraydataptr, eltsize, st_pos);
	mda_get_prod(ndim, dim, prod);
	mda_get_offset_values(ndim, dist, prod, span);
	for (i = 0; i < ndim; i++)
1683
		indx[i] = 0;
1684
	j = ndim - 1;
1685 1686
	do
	{
1687
		ptr = array_seek(ptr, eltsize, dist[j]);
1688 1689 1690
		inc = INTALIGN(*(int32 *) ptr);
		ptr += inc;
		count += inc;
1691
	} while ((j = mda_next_tuple(ndim, indx, span)) != -1);
1692
	return count;
1693 1694
}

1695 1696 1697 1698 1699 1700 1701 1702 1703 1704 1705 1706 1707 1708 1709
/*
 * Extract a slice of an array into consecutive elements at *destPtr.
 *
 * We assume the caller has verified that the slice coordinates are valid
 * and allocated enough storage at *destPtr.
 */
static void
array_extract_slice(int ndim,
					int *dim,
					int *lb,
					char *arraydataptr,
					int eltsize,
					int *st,
					int *endp,
					char *destPtr)
1710
{
1711 1712 1713 1714 1715 1716 1717 1718 1719
	int			st_pos,
				prod[MAXDIM],
				span[MAXDIM],
				dist[MAXDIM],
				indx[MAXDIM];
	char	   *srcPtr;
	int			i,
				j,
				inc;
1720

1721 1722 1723 1724 1725 1726 1727 1728 1729 1730 1731 1732 1733 1734 1735
	st_pos = ArrayGetOffset(ndim, dim, lb, st);
	srcPtr = array_seek(arraydataptr, eltsize, st_pos);
	mda_get_prod(ndim, dim, prod);
	mda_get_range(ndim, span, st, endp);
	mda_get_offset_values(ndim, dist, prod, span);
	for (i = 0; i < ndim; i++)
		indx[i] = 0;
	j = ndim - 1;
	do
	{
		srcPtr = array_seek(srcPtr, eltsize, dist[j]);
		inc = array_copy(destPtr, eltsize, 1, srcPtr);
		destPtr += inc;
		srcPtr += inc;
	} while ((j = mda_next_tuple(ndim, indx, span)) != -1);
1736 1737
}

1738 1739 1740 1741 1742 1743 1744 1745 1746 1747 1748 1749 1750 1751 1752 1753 1754 1755 1756 1757 1758 1759 1760 1761
/*
 * Insert a slice into an array.
 *
 * ndim/dim/lb are dimensions of the dest array, which has data area
 * starting at origPtr.  A new array with those same dimensions is to
 * be constructed; its data area starts at destPtr.
 *
 * Elements within the slice volume are taken from consecutive locations
 * at srcPtr; elements outside it are copied from origPtr.
 *
 * We assume the caller has verified that the slice coordinates are valid
 * and allocated enough storage at *destPtr.
 */
static void
array_insert_slice(int ndim,
				   int *dim,
				   int *lb,
				   char *origPtr,
				   int origdatasize,
				   char *destPtr,
				   int eltsize,
				   int *st,
				   int *endp,
				   char *srcPtr)
1762
{
1763 1764 1765 1766 1767 1768
	int			st_pos,
				prod[MAXDIM],
				span[MAXDIM],
				dist[MAXDIM],
				indx[MAXDIM];
	char	   *origEndpoint = origPtr + origdatasize;
1769
	int			i,
1770 1771
				j,
				inc;
1772

1773 1774 1775 1776 1777 1778 1779 1780 1781 1782 1783
	st_pos = ArrayGetOffset(ndim, dim, lb, st);
	inc = array_copy(destPtr, eltsize, st_pos, origPtr);
	destPtr += inc;
	origPtr += inc;
	mda_get_prod(ndim, dim, prod);
	mda_get_range(ndim, span, st, endp);
	mda_get_offset_values(ndim, dist, prod, span);
	for (i = 0; i < ndim; i++)
		indx[i] = 0;
	j = ndim - 1;
	do
1784
	{
1785 1786 1787 1788 1789 1790 1791 1792 1793 1794 1795 1796 1797 1798
		/* Copy/advance over elements between here and next part of slice */
		inc = array_copy(destPtr, eltsize, dist[j], origPtr);
		destPtr += inc;
		origPtr += inc;
		/* Copy new element at this slice position */
		inc = array_copy(destPtr, eltsize, 1, srcPtr);
		destPtr += inc;
		srcPtr += inc;
		/* Advance over old element at this slice position */
		origPtr = array_seek(origPtr, eltsize, 1);
	} while ((j = mda_next_tuple(ndim, indx, span)) != -1);

	/* don't miss any data at the end */
	memcpy(destPtr, origPtr, origEndpoint - origPtr);
1799
}