arrayfuncs.c

来自「PostgreSQL7.4.6 for Linux」· C语言 代码 · 共 2,367 行 · 第 1/5 页

C
2,367
字号
/*------------------------------------------------------------------------- * * arrayfuncs.c *	  Support functions for arrays. * * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION *	  $Header: /cvsroot/pgsql/src/backend/utils/adt/arrayfuncs.c,v 1.100.2.1 2004/06/08 20:28:29 tgl Exp $ * *------------------------------------------------------------------------- */#include "postgres.h"#include <ctype.h>#include "access/tupmacs.h"#include "catalog/catalog.h"#include "catalog/pg_type.h"#include "libpq/pqformat.h"#include "parser/parse_coerce.h"#include "parser/parse_oper.h"#include "utils/array.h"#include "utils/builtins.h"#include "utils/datum.h"#include "utils/memutils.h"#include "utils/lsyscache.h"#include "utils/syscache.h"#include "utils/typcache.h"/*---------- * A standard varlena array has the following internal structure: *	  <size>		- total number of bytes (also, TOAST info flags) *	  <ndim>		- number of dimensions of the array *	  <flags>		- bit mask of flags *	  <elemtype>	- element type OID *	  <dim>			- size of each array axis (C array of int) *	  <dim_lower>	- lower boundary of each dimension (C array of int) *	  <actual data> - whatever is the stored data * The actual data starts on a MAXALIGN boundary.  Individual items in the * array are aligned as specified by the array element type. * * 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.) * * There is currently no support for NULL elements in arrays, either. * A reasonable (and backwards-compatible) way to add support would be to * add a nulls bitmap following the <dim_lower> array, which would be present * if needed; and its presence would be signaled by a bit in the flags word. * * * There are also some "fixed-length array" datatypes, such as NAME and * OIDVECTOR.  These are simply a sequence of a fixed number of items each * of a fixed-length datatype, with no overhead; the item size must be * a multiple of its alignment requirement, because we do no padding. * We support subscripting on these types, but array_in() and array_out() * only work with varlena arrays. *---------- *//* ---------- * Local definitions * ---------- */#define ASSGN	 "="#define RETURN_NULL(type)  do { *isNull = true; return (type) 0; } while (0)static int	ArrayCount(char *str, int *dim, char typdelim);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);static Datum *ReadArrayBinary(StringInfo buf, int nitems,				FmgrInfo *receiveproc, Oid typelem,				int typlen, bool typbyval, char typalign,				int *nbytes);static void CopyArrayEls(char *p, Datum *values, int nitems,			 int typlen, bool typbyval, char typalign,			 bool freedata);static Datum ArrayCast(char *value, bool byval, int len);static int ArrayCastAndSet(Datum src,				int typlen, bool typbyval, char typalign,				char *dest);static int array_nelems_size(char *ptr, int nitems,				  int typlen, bool typbyval, char typalign);static char *array_seek(char *ptr, int nitems,		   int typlen, bool typbyval, char typalign);static int array_copy(char *destptr, int nitems, char *srcptr,		   int typlen, bool typbyval, char typalign);static int array_slice_size(int ndim, int *dim, int *lb, char *arraydataptr,				 int *st, int *endp,				 int typlen, bool typbyval, char typalign);static void array_extract_slice(int ndim, int *dim, int *lb,					char *arraydataptr,					int *st, int *endp, char *destPtr,					int typlen, bool typbyval, char typalign);static void array_insert_slice(int ndim, int *dim, int *lb,				   char *origPtr, int origdatasize,				   char *destPtr,				   int *st, int *endp, char *srcPtr,				   int typlen, bool typbyval, char typalign);static int	array_cmp(FunctionCallInfo fcinfo);/*--------------------------------------------------------------------- * array_in : *		  converts an array from the external format in "string" to *		  its internal format. * return value : *		  the internal representation of the input array *-------------------------------------------------------------------- */Datumarray_in(PG_FUNCTION_ARGS){	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 */	int			typlen;	bool		typbyval;	char		typalign;	char		typdelim;	Oid			typelem;	char	   *string_save,			   *p;	int			i,				nitems;	int32		nbytes;	Datum	   *dataPtr;	ArrayType  *retval;	int			ndim,				dim[MAXDIM],				lBound[MAXDIM];	ArrayMetaState *my_extra;	/*	 * We arrange to look up info about element type, including its input	 * conversion proc, only once per series of calls, assuming the	 * element type doesn't change underneath us.	 */	my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;	if (my_extra == NULL)	{		fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,												 sizeof(ArrayMetaState));		my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;		my_extra->element_type = InvalidOid;	}	if (my_extra->element_type != element_type)	{		/*		 * Get info about element type, including its input conversion		 * proc		 */		get_type_io_data(element_type, IOFunc_input,						 &my_extra->typlen, &my_extra->typbyval,						 &my_extra->typalign, &my_extra->typdelim,						 &my_extra->typelem, &my_extra->typiofunc);		fmgr_info_cxt(my_extra->typiofunc, &my_extra->proc,					  fcinfo->flinfo->fn_mcxt);		my_extra->element_type = element_type;	}	typlen = my_extra->typlen;	typbyval = my_extra->typbyval;	typalign = my_extra->typalign;	typdelim = my_extra->typdelim;	typelem = my_extra->typelem;	/* Make a modifiable copy of the input */	/* XXX why are we allocating an extra 2 bytes here? */	string_save = (char *) palloc(strlen(string) + 3);	strcpy(string_save, string);	/*	 * 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.	 *	 * Dimension info takes the form of one or more [n] or [m:n] items. The	 * outer loop iterates once per dimension item.	 */	p = string_save;	ndim = 0;	for (;;)	{		char	   *q;		int			ub;		/*		 * Note: we currently allow whitespace between, but not within,		 * dimension items.		 */		while (isspace((unsigned char) *p))			p++;		if (*p != '[')			break;				/* no more dimension items */		p++;		if (ndim >= MAXDIM)			ereport(ERROR,					(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),					 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),					 errmsg("missing dimension value")));		if (*q == ':')		{			/* [m:n] format */			*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),						 errmsg("missing dimension value")));		}		else		{			/* [n] format */			lBound[ndim] = 1;		}		if (*q != ']')			ereport(ERROR,					(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),					 errmsg("missing \"]\" in array dimensions")));		*q = '\0';		ub = atoi(p);		p = q + 1;		if (ub < lBound[ndim])			ereport(ERROR,					(errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),				 errmsg("upper bound cannot be less than lower bound")));		dim[ndim] = ub - lBound[ndim] + 1;		ndim++;	}	if (ndim == 0)	{		/* No array dimensions, so intuit dimensions from brace structure */		if (*p != '{')			ereport(ERROR,					(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),					 errmsg("array value must start with \"{\" or dimension information")));		ndim = ArrayCount(p, dim, typdelim);		for (i = 0; i < ndim; i++)			lBound[i] = 1;	}	else	{		/* If array dimensions are given, expect '=' operator */		if (strncmp(p, ASSGN, strlen(ASSGN)) != 0)			ereport(ERROR,					(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),					 errmsg("missing assignment operator")));		p += strlen(ASSGN);		while (isspace((unsigned char) *p))			p++;	}#ifdef ARRAYDEBUG	printf("array_in- ndim %d (", ndim);	for (i = 0; i < ndim; i++)	{		printf(" %d", dim[i]);	};	printf(") for %s\n", string);#endif	nitems = ArrayGetNItems(ndim, dim);	if (nitems == 0)	{		/* Return empty array */		retval = (ArrayType *) palloc0(sizeof(ArrayType));		retval->size = sizeof(ArrayType);		retval->elemtype = element_type;		PG_RETURN_ARRAYTYPE_P(retval);	}	if (*p != '{')		ereport(ERROR,				(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),				 errmsg("missing left brace")));	dataPtr = ReadArrayStr(p, nitems, ndim, dim, &my_extra->proc, typelem,						   typmod, typdelim, typlen, typbyval, typalign,						   &nbytes);	nbytes += ARR_OVERHEAD(ndim);	retval = (ArrayType *) palloc0(nbytes);	retval->size = nbytes;	retval->ndim = ndim;	retval->elemtype = element_type;	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,				 typlen, typbyval, typalign, true);	pfree(dataPtr);	pfree(string_save);	PG_RETURN_ARRAYTYPE_P(retval);}/*----------------------------------------------------------------------------- * ArrayCount *	 Counts the number of dimensions and the *dim array for an array string. *		 The syntax for array input is C-like nested curly braces *----------------------------------------------------------------------------- */static intArrayCount(char *str, int *dim, char typdelim){	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;	ptr = str;	while (!eoArray)	{		bool		itemdone = false;		while (!itemdone)		{			switch (*ptr)			{				case '\0':					/* Signal a premature end of the string */					ereport(ERROR,							(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),						errmsg("malformed array literal: \"%s\"", str)));					break;				case '\\':					/* skip the escaped character */					if (*(ptr + 1))						ptr++;					else						ereport(ERROR,						   (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),						errmsg("malformed array literal: \"%s\"", str)));					break;				case '\"':					scanning_string = !scanning_string;					break;				case '{':					if (!scanning_string)					{						if (nest_level >= MAXDIM)							ereport(ERROR,								(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),								 errmsg("number of array dimensions (%d) exceeds the maximum allowed (%d)",										nest_level, MAXDIM)));						temp[nest_level] = 0;						nest_level++;						if (ndim < nest_level)							ndim = nest_level;					}					break;				case '}':					if (!scanning_string)					{						if (nest_level == 0)							ereport(ERROR,							(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),							 errmsg("malformed array literal: \"%s\"", str)));						nest_level--;						if (nest_level == 0)							eoArray = itemdone = true;						else						{							/*							 * We don't set itemdone here; see comments in							 * ReadArrayStr							 */							temp[nest_level - 1]++;						}					}					break;				default:					if (*ptr == typdelim && !scanning_string)						itemdone = true;					break;			}			if (!itemdone)				ptr++;		}		temp[ndim - 1]++;		ptr++;	}	for (i = 0; i < ndim; ++i)		dim[i] = temp[i];	return ndim;}/*--------------------------------------------------------------------------- * ReadArrayStr : *	 parses the array string pointed by "arrayStr" and converts it to *	 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.  (This is pretty bogus; NULL would be much safer.) * result : *	 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. *	 CAUTION: the contents of "arrayStr" may be modified! *--------------------------------------------------------------------------- */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){	int			i,				nest_level = 0;	Datum	   *values;	char	   *ptr;	bool		scanning_string = false;	bool		eoArray = false;	int			indx[MAXDIM],				prod[MAXDIM];	mda_get_prod(ndim, dim, prod);	values = (Datum *) palloc0(nitems * sizeof(Datum));	MemSet(indx, 0, sizeof(indx));	/* read array enclosed within {} */	ptr = arrayStr;	while (!eoArray)	{		bool		itemdone = false;		int			i = -1;		char	   *itemstart;		/* skip leading whitespace */		while (isspace((unsigned char) *ptr))

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?