arrayfuncs.c

来自「postgresql8.3.4源码,开源数据库」· C语言 代码 · 共 2,484 行 · 第 1/5 页

C
2,484
字号
/*------------------------------------------------------------------------- * * arrayfuncs.c *	  Support functions for arrays. * * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION *	  $PostgreSQL: pgsql/src/backend/utils/adt/arrayfuncs.c,v 1.140.2.1 2008/04/11 22:52:17 tgl Exp $ * *------------------------------------------------------------------------- */#include "postgres.h"#include <ctype.h>#include "access/tupmacs.h"#include "libpq/pqformat.h"#include "parser/parse_coerce.h"#include "utils/array.h"#include "utils/builtins.h"#include "utils/datum.h"#include "utils/lsyscache.h"#include "utils/memutils.h"#include "utils/typcache.h"/* * GUC parameter */bool		Array_nulls = true;/* * Local definitions */#define ASSGN	 "="typedef enum{	ARRAY_NO_LEVEL,	ARRAY_LEVEL_STARTED,	ARRAY_ELEM_STARTED,	ARRAY_ELEM_COMPLETED,	ARRAY_QUOTED_ELEM_STARTED,	ARRAY_QUOTED_ELEM_COMPLETED,	ARRAY_ELEM_DELIMITED,	ARRAY_LEVEL_COMPLETED,	ARRAY_LEVEL_DELIMITED} ArrayParseState;static int	ArrayCount(const char *str, int *dim, char typdelim);static void ReadArrayStr(char *arrayStr, const char *origStr,			 int nitems, int ndim, int *dim,			 FmgrInfo *inputproc, Oid typioparam, int32 typmod,			 char typdelim,			 int typlen, bool typbyval, char typalign,			 Datum *values, bool *nulls,			 bool *hasnulls, int32 *nbytes);static void ReadArrayBinary(StringInfo buf, int nitems,				FmgrInfo *receiveproc, Oid typioparam, int32 typmod,				int typlen, bool typbyval, char typalign,				Datum *values, bool *nulls,				bool *hasnulls, int32 *nbytes);static void CopyArrayEls(ArrayType *array,			 Datum *values, bool *nulls, int nitems,			 int typlen, bool typbyval, char typalign,			 bool freedata);static bool array_get_isnull(const bits8 *nullbitmap, int offset);static void array_set_isnull(bits8 *nullbitmap, int offset, bool isNull);static Datum ArrayCast(char *value, bool byval, int len);static int ArrayCastAndSet(Datum src,				int typlen, bool typbyval, char typalign,				char *dest);static char *array_seek(char *ptr, int offset, bits8 *nullbitmap, int nitems,		   int typlen, bool typbyval, char typalign);static int array_nelems_size(char *ptr, int offset, bits8 *nullbitmap,				  int nitems, int typlen, bool typbyval, char typalign);static int array_copy(char *destptr, int nitems,		   char *srcptr, int offset, bits8 *nullbitmap,		   int typlen, bool typbyval, char typalign);static int array_slice_size(char *arraydataptr, bits8 *arraynullsptr,				 int ndim, int *dim, int *lb,				 int *st, int *endp,				 int typlen, bool typbyval, char typalign);static void array_extract_slice(ArrayType *newarray,					int ndim, int *dim, int *lb,					char *arraydataptr, bits8 *arraynullsptr,					int *st, int *endp,					int typlen, bool typbyval, char typalign);static void array_insert_slice(ArrayType *destArray, ArrayType *origArray,				   ArrayType *srcArray,				   int ndim, int *dim, int *lb,				   int *st, int *endp,				   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			typioparam;	char	   *string_save,			   *p;	int			i,				nitems;	Datum	   *dataPtr;	bool	   *nullsPtr;	bool		hasnulls;	int32		nbytes;	int32		dataoffset;	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 = ~element_type;	}	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->typioparam, &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;	typioparam = my_extra->typioparam;	/* Make a modifiable copy of the input */	string_save = pstrdup(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 == '-') || (*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 == '-') || (*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	{		int			ndim_braces,					dim_braces[MAXDIM];		/* 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++;		/*		 * 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	printf("array_in- ndim %d (", ndim);	for (i = 0; i < ndim; i++)	{		printf(" %d", dim[i]);	};	printf(") for %s\n", string);#endif	/* This checks for overflow of the array dimensions */	nitems = ArrayGetNItems(ndim, dim);	/* Empty array? */	if (nitems == 0)		PG_RETURN_ARRAYTYPE_P(construct_empty_array(element_type));	dataPtr = (Datum *) palloc(nitems * sizeof(Datum));	nullsPtr = (bool *) palloc(nitems * sizeof(bool));	ReadArrayStr(p, string,				 nitems, ndim, dim,				 &my_extra->proc, typioparam, typmod,				 typdelim,				 typlen, typbyval, typalign,				 dataPtr, nullsPtr,				 &hasnulls, &nbytes);	if (hasnulls)	{		dataoffset = ARR_OVERHEAD_WITHNULLS(ndim, nitems);		nbytes += dataoffset;	}	else	{		dataoffset = 0;			/* marker for no null bitmap */		nbytes += ARR_OVERHEAD_NONULLS(ndim);	}	retval = (ArrayType *) palloc0(nbytes);	SET_VARSIZE(retval, nbytes);	retval->ndim = ndim;	retval->dataoffset = dataoffset;	retval->elemtype = element_type;	memcpy(ARR_DIMS(retval), dim, ndim * sizeof(int));	memcpy(ARR_LBOUND(retval), lBound, ndim * sizeof(int));	CopyArrayEls(retval,				 dataPtr, nullsPtr, nitems,				 typlen, typbyval, typalign,				 true);	pfree(dataPtr);	pfree(nullsPtr);	pfree(string_save);	PG_RETURN_ARRAYTYPE_P(retval);}/* * ArrayCount *	 Determines the dimensions for an array string. * * Returns number of dimensions as function result.  The axis lengths are * returned in dim[], which must be of size MAXDIM. */static intArrayCount(const char *str, int *dim, char typdelim){	int			nest_level = 0,				i;	int			ndim = 1,				temp[MAXDIM],				nelems[MAXDIM],				nelems_last[MAXDIM];	bool		in_quotes = false;	bool		eoArray = false;	bool		empty_array = true;	const char *ptr;	ArrayParseState parse_state = ARRAY_NO_LEVEL;	for (i = 0; i < MAXDIM; ++i)	{		temp[i] = dim[i] = 0;		nelems_last[i] = nelems[i] = 1;	}	ptr = str;	while (!eoArray)	{		bool		itemdone = false;		while (!itemdone)		{			if (parse_state == ARRAY_ELEM_STARTED ||				parse_state == ARRAY_QUOTED_ELEM_STARTED)				empty_array = false;			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 '\\':					/*					 * An escape must be after a level start, after an element					 * start, or after an element delimiter. In any case we					 * now must be past an element start.					 */					if (parse_state != ARRAY_LEVEL_STARTED &&						parse_state != ARRAY_ELEM_STARTED &&						parse_state != ARRAY_QUOTED_ELEM_STARTED &&						parse_state != ARRAY_ELEM_DELIMITED)						ereport(ERROR,								(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),							errmsg("malformed array literal: \"%s\"", str)));					if (parse_state != ARRAY_QUOTED_ELEM_STARTED)						parse_state = ARRAY_ELEM_STARTED;					/* skip the escaped character */					if (*(ptr + 1))						ptr++;					else						ereport(ERROR,								(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),							errmsg("malformed array literal: \"%s\"", str)));					break;				case '\"':					/*					 * A quote must be after a level start, after a quoted					 * element start, or after an element delimiter. In any					 * case we now must be past an element start.					 */					if (parse_state != ARRAY_LEVEL_STARTED &&						parse_state != ARRAY_QUOTED_ELEM_STARTED &&						parse_state != ARRAY_ELEM_DELIMITED)						ereport(ERROR,								(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),							errmsg("malformed array literal: \"%s\"", str)));					in_quotes = !in_quotes;					if (in_quotes)						parse_state = ARRAY_QUOTED_ELEM_STARTED;					else						parse_state = ARRAY_QUOTED_ELEM_COMPLETED;					break;				case '{':					if (!in_quotes)					{						/*						 * A left brace can occur if no nesting has occurred						 * yet, after a level start, or after a level						 * delimiter.						 */						if (parse_state != ARRAY_NO_LEVEL &&							parse_state != ARRAY_LEVEL_STARTED &&							parse_state != ARRAY_LEVEL_DELIMITED)							ereport(ERROR,							   (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),							errmsg("malformed array literal: \"%s\"", str)));						parse_state = ARRAY_LEVEL_STARTED;						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 (!in_quotes)					{						/*						 * A right brace can occur after an element start, an						 * element completion, a quoted element completion, or						 * a level completion.						 */						if (parse_state != ARRAY_ELEM_STARTED &&							parse_state != ARRAY_ELEM_COMPLETED &&							parse_state != ARRAY_QUOTED_ELEM_COMPLETED &&							parse_state != ARRAY_LEVEL_COMPLETED &&							!(nest_level == 1 && parse_state == ARRAY_LEVEL_STARTED))							ereport(ERROR,							   (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),							errmsg("malformed array literal: \"%s\"", str)));						parse_state = ARRAY_LEVEL_COMPLETED;						if (nest_level == 0)							ereport(ERROR,							   (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						{							/*							 * We don't set itemdone here; see comments in							 * ReadArrayStr							 */

⌨️ 快捷键说明

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