📄 arrayfuncs.c
字号:
/*------------------------------------------------------------------------- * * arrayfuncs.c * Support functions for arrays. * * Portions Copyright (c) 1996-2005, 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.123.2.1 2005/11/22 18:23:20 momjian 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 * POINT. 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, const char *origStr, int nitems, int ndim, int *dim, FmgrInfo *inputproc, Oid typioparam, int32 typmod, char typdelim, int typlen, bool typbyval, char typalign, int *nbytes);static Datum *ReadArrayBinary(StringInfo buf, int nitems, FmgrInfo *receiveproc, Oid typioparam, int32 typmod, 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);static Datum array_type_length_coerce_internal(ArrayType *src, int32 desttypmod, bool isExplicit, FmgrInfo *fmgr_info);/*--------------------------------------------------------------------- * 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; 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 = ~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 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, string, nitems, ndim, dim, &my_extra->proc, typioparam, 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(ARR_DIMS(retval), dim, ndim * sizeof(int)); memcpy(ARR_LBOUND(retval), 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 *----------------------------------------------------------------------------- */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 intArrayCount(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; 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; } /* special case for an empty array */ if (strcmp(str, "{}") == 0) return 0; 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++;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -