Index: doc/src/sgml/array.sgml =================================================================== RCS file: /opt/src/cvs/pgsql-server/doc/src/sgml/array.sgml,v retrieving revision 1.25 diff -c -r1.25 array.sgml *** doc/src/sgml/array.sgml 13 Mar 2003 01:30:26 -0000 1.25 --- doc/src/sgml/array.sgml 22 May 2003 23:42:01 -0000 *************** *** 9,15 **** PostgreSQL allows columns of a table to be ! defined as variable-length multidimensional arrays. Arrays of any built-in type or user-defined type can be created. --- 9,15 ---- PostgreSQL allows columns of a table to be ! defined as variable-length multi-dimensional arrays. Arrays of any built-in type or user-defined type can be created. *************** *** 60,73 **** ! A limitation of the present array implementation is that individual ! elements of an array cannot be SQL null values. The entire array can be set ! to null, but you can't have an array with some elements null and some ! not. Fixing this is on the to-do list. --- 60,133 ---- + + A limitation of the present array implementation is that individual + elements of an array cannot be SQL null values. The entire array can be set + to null, but you can't have an array with some elements null and some + not. + + + This can lead to surprising results. For example, the result of the + previous two inserts looks like this: + + SELECT * FROM sal_emp; + name | pay_by_quarter | schedule + -------+---------------------------+-------------------- + Bill | {10000,10000,10000,10000} | {{meeting},{""}} + Carol | {20000,25000,25000,25000} | {{talk},{meeting}} + (2 rows) + + Because the [2][2] element of + schedule is missing in each of the + INSERT statements, the [1][2] + element is discarded. + + + + + Fixing this is on the to-do list. + + + + + The ARRAY expression syntax may also be used: + + INSERT INTO sal_emp + VALUES ('Bill', + ARRAY[10000, 10000, 10000, 10000], + ARRAY[['meeting', 'lunch'], ['','']]); + + INSERT INTO sal_emp + VALUES ('Carol', + ARRAY[20000, 25000, 25000, 25000], + ARRAY[['talk', 'consult'], ['meeting', '']]); + SELECT * FROM sal_emp; + name | pay_by_quarter | schedule + -------+---------------------------+------------------------------- + Bill | {10000,10000,10000,10000} | {{meeting,lunch},{"",""}} + Carol | {20000,25000,25000,25000} | {{talk,consult},{meeting,""}} + (2 rows) + + Note that with this syntax, multi-dimesion arrays must have matching + extents for each dimension. This eliminates the missing-array-elements + problem above. For example: + + INSERT INTO sal_emp + VALUES ('Carol', + ARRAY[20000, 25000, 25000, 25000], + ARRAY[['talk', 'consult'], ['meeting']]); + ERROR: Multiple dimension arrays must have array expressions with matching dimensions + + Also notice that string literals are single quoted instead of double quoted. + + ! The examples in the rest of this section are based on the ! ARRAY expression syntax INSERTs. + *************** *** 132,142 **** with the same result. An array subscripting operation is always taken to ! represent an array slice if any of the subscripts are written in the ! form lower:upper. A lower bound of 1 is assumed for any subscript where only one value ! is specified. --- 192,221 ---- with the same result. An array subscripting operation is always taken to ! represent an array slice if any of the subscripts are written in the form lower:upper. A lower bound of 1 is assumed for any subscript where only one value ! is specified; another example follows: ! ! SELECT schedule[1:2][2] FROM sal_emp WHERE name = 'Bill'; ! schedule ! --------------------------- ! {{meeting,lunch},{"",""}} ! (1 row) ! ! ! ! ! Additionally, we can also access a single arbitrary array element of ! a one-dimensional array with the array_subscript ! function: ! ! SELECT array_subscript(pay_by_quarter, 2) FROM sal_emp WHERE name = 'Bill'; ! array_subscript ! ----------------- ! 10000 ! (1 row) ! *************** *** 147,153 **** WHERE name = 'Carol'; ! or updated at a single element: UPDATE sal_emp SET pay_by_quarter[4] = 15000 --- 226,248 ---- WHERE name = 'Carol'; ! or using the ARRAY expression syntax: ! ! ! UPDATE sal_emp SET pay_by_quarter = ARRAY[25000,25000,27000,27000] ! WHERE name = 'Carol'; ! ! ! ! ! Anywhere you can use the curly braces array syntax, ! you can also use the ARRAY expression syntax. The ! remainder of this section will illustrate only one or the other, but ! not both. ! ! ! ! An array may also be updated at a single element: UPDATE sal_emp SET pay_by_quarter[4] = 15000 *************** *** 160,165 **** --- 255,268 ---- UPDATE sal_emp SET pay_by_quarter[1:2] = '{27000,27000}' WHERE name = 'Carol'; + + A one-dimensional array may also be updated with the + array_assign function: + + + UPDATE sal_emp SET pay_by_quarter = array_assign(pay_by_quarter, 4, 15000) + WHERE name = 'Bill'; + *************** *** 169,175 **** value currently has 4 elements, it will have five elements after an update that assigns to array[5]. Currently, enlargement in this fashion is only allowed for one-dimensional arrays, not ! multidimensional arrays. --- 272,278 ---- value currently has 4 elements, it will have five elements after an update that assigns to array[5]. Currently, enlargement in this fashion is only allowed for one-dimensional arrays, not ! multi-dimensional arrays. *************** *** 179,184 **** --- 282,367 ---- + An array can also be enlarged by using the functions + array_prepend, array_append, + or array_cat. The first two only support one-dimensional + arrays, but array_cat supports multi-dimensional arrays. + Some examples: + + + SELECT array_prepend(1, ARRAY[2,3]); + array_prepend + --------------- + {1,2,3} + (1 row) + + SELECT array_append(ARRAY[1,2], 3); + array_append + -------------- + {1,2,3} + (1 row) + + SELECT array_cat(ARRAY[1,2], ARRAY[3,4]); + array_cat + --------------- + {{1,2},{3,4}} + (1 row) + + SELECT array_cat(ARRAY[[1,2],[3,4]], ARRAY[5,6]); + array_cat + --------------------- + {{1,2},{3,4},{5,6}} + (1 row) + + SELECT array_cat(ARRAY[5,6], ARRAY[[1,2],[3,4]]); + array_cat + --------------------- + {{5,6},{1,2},{3,4}} + + + array_prepend and array_append + work with a one-dimensional array and a single element to be pushed on + to the beginning or end of the array, respectively. The array is extended + in the direction of the push. Hence, by pushing onto the beginning of an + array with a one-based subscript, a zero-based subscript array is created: + + + SELECT array_dims(t.f) FROM (SELECT array_prepend(1, ARRAY[2,3]) AS f) AS t; + array_dims + ------------ + [0:2] + (1 row) + + + array_cat works with either two + n-dimension arrays, or an n-dimension + and an n+1 dimension array. In the former case, the two + n-dimension arrays become outer elements of an + n+1 dimension array. In the latter, the + n-dimension array is added as either the first or last + outer element of the n+1 dimension array. + + + + A final method of enlarging arrays is through the concatenation operator, + ||, which works exactly as array_cat + does. + + SELECT ARRAY[1,2] || ARRAY[3,4]; + ?column? + --------------- + {{1,2},{3,4}} + (1 row) + + SELECT ARRAY[5,6] || ARRAY[[1,2],[3,4]]; + ?column? + --------------------- + {{5,6},{1,2},{3,4}} + (1 row) + + + + The syntax for CREATE TABLE allows fixed-length arrays to be defined: *************** *** 194,199 **** --- 377,392 ---- + An alternative syntax for one-dimensional arrays may be used. + pay_by_quarter could have been defined as: + + pay_by_quarter integer ARRAY[4], + + This syntax may only be used with the integer + constant to denote the array size. + + + Actually, the current implementation does not enforce the declared number of dimensions either. Arrays of a particular element type are all considered to be of the same type, regardless of size or number *************** *** 292,298 **** for the array's element type. (Among the standard data types provided in the PostgreSQL distribution, type box uses a semicolon (;) but all the others ! use comma.) In a multidimensional array, each dimension (row, plane, cube, etc.) gets its own level of curly braces, and delimiters must be written between adjacent curly-braced entities of the same level. You may write whitespace before a left brace, after a right --- 485,491 ---- for the array's element type. (Among the standard data types provided in the PostgreSQL distribution, type box uses a semicolon (;) but all the others ! use comma.) In a multi-dimensional array, each dimension (row, plane, cube, etc.) gets its own level of curly braces, and delimiters must be written between adjacent curly-braced entities of the same level. You may write whitespace before a left brace, after a right *************** *** 300,305 **** --- 493,564 ---- is not ignored, however: after skipping leading whitespace, everything up to the next right brace or delimiter is taken as the item value. + + + As illustrated earlier in this chapter, arrays may also be represented + using the ARRAY expression syntax. This representation + of an array value consists of items that are interpreted according to the + I/O conversion rules for the array's element type, plus decoration that + indicates the array structure. The decoration consists of the keyword + ARRAY and square brackets ([ and + ]) around the array values, plus delimiter characters between + adjacent items. The delimiter character is always a comma (,). + When representing multi-dimensional arrays, the keyword + ARRAY is only necessary for the outer level. For example, + '{{"hello world", "happy birthday"}}' could be written as: + + SELECT ARRAY[['hello world', 'happy birthday']]; + array + ------------------------------------ + {{"hello world","happy birthday"}} + (1 row) + + or it also could be written as: + + SELECT ARRAY[ARRAY['hello world', 'happy birthday']]; + array + ------------------------------------ + {{"hello world","happy birthday"}} + (1 row) + + + + + A final method to represent an array, is through an + ARRAY sub-select expression. For example: + + SELECT ARRAY(SELECT oid FROM pg_proc WHERE proname LIKE 'bytea%'); + ?column? + ------------------------------------------------------------- + {2011,1954,1948,1952,1951,1244,1950,2005,1949,1953,2006,31} + (1 row) + + The sub-select may only return a single column. The + resulting one-dimensional array will have an element for each row in the + sub-select result, with an element type matching that of the sub-select's + target column. + + + + Arrays may be cast from one type to another in similar fashion to other + data types: + + + SELECT ARRAY[1,2,3]::oid[]; + array + --------- + {1,2,3} + (1 row) + + SELECT CAST(ARRAY[1,2,3] AS float8[]); + array + --------- + {1,2,3} + (1 row) + + + + *************** *** 316,321 **** --- 575,588 ---- Alternatively, you can use backslash-escaping to protect all data characters that would otherwise be taken as array syntax or ignorable white space. + + + + The discussion in the preceding paragraph with respect to double quoting does + not pertain to the ARRAY expression syntax. In that case, + each element is quoted exactly as any other literal value of the element type. + + The array output routine will put double quotes around element values Index: doc/src/sgml/func.sgml =================================================================== RCS file: /opt/src/cvs/pgsql-server/doc/src/sgml/func.sgml,v retrieving revision 1.154 diff -c -r1.154 func.sgml *** doc/src/sgml/func.sgml 5 May 2003 15:08:49 -0000 1.154 --- doc/src/sgml/func.sgml 22 May 2003 23:42:01 -0000 *************** *** 6962,6967 **** --- 6962,7224 ---- + + Array Functions + + + shows the operators + available for the array types. + + + + <type>array</type> Operators + + + + Operator + Description + Example + Result + + + + + = + equals + ARRAY[1.1,2.1,3.1]::int[] = ARRAY[1,2,3] + t + + + || + array-to-array concatenation + ARRAY[1,2,3] || ARRAY[4,5,6] + {{1,2,3},{4,5,6}} + + + || + array-to-array concatenation + ARRAY[1,2,3] || ARRAY[[4,5,6],[7,8,9]] + {{1,2,3},{4,5,6},{7,8,9}} + + + || + element-to-array concatenation + 3 || ARRAY[4,5,6] + {3,4,5,6} + + + || + array-to-element concatenation + ARRAY[4,5,6] || 7 + {4,5,6,7} + + + +
+ + + shows the functions + available for use with array types. See + for more discussion and examples for the use of these functions. + + + + <type>array</type> Functions + + + + Function + Return Type + Description + Example + Result + + + + + + + array_accum + (anyarray, anyelement) + + + anyarray + + append an element to the end of an array, ignoring + NULL elements, and creating an array if needed + + array_accum(null, 1) + {1} + + + + + array_append + (anyarray, anyelement) + + + anyarray + + append an element to the end of an array, returning + NULL for NULL inputs + + array_append(ARRAY[1,2], 3) + {1,2,3} + + + + + array_assign + (anyarray, integer, anyelement) + + + anyarray + + assign a value to a specific array element, returning + NULL for NULL inputs + + array_assign(ARRAY[1,2,3], 2, 99) + {1,99,3} + + + + + array_cat + (anyarray, anyarray) + + + anyarray + + concatenate two arrays, returning NULL + for NULL inputs + + array_cat(ARRAY[1,2,3], ARRAY[4,5,6]) + {{1,2,3},{4,5,6}} + + + + + array_dims + (anyarray) + + + text + + returns a text representation of array dimension lower and upper bounds, + generating an ERROR for NULL inputs + + array_dims(array[[1,2,3],[4,5,6]]) + [1:2][1:3] + + + + + array_lower + (anyarray, integer) + + + integer + + returns lower bound of the requested array dimension, returning + NULL for NULL inputs + + array_lower(array_prepend(0, ARRAY[1,2,3]), 1) + 0 + + + + + array_prepend + (anyelement, anyarray) + + + anyarray + + append an element to the beginning of an array, returning + NULL for NULL inputs + + array_prepend(1, ARRAY[2,3]) + {1,2,3} + + + + + array_subscript + (anyarray, integer) + + + anyelement + + returns requested array element, returning + NULL for NULL inputs + + array_subscript(ARRAY[1,2,3], 3) + 3 + + + + + array_to_str + (anyarray, text) + + + text + + concatenates array elements using provided delimiter, returning + NULL for NULL inputs + + array_to_str(array[1.1,2.2,3.3]::numeric(4,2)[],'~^~') + 1.10~^~2.20~^~3.30 + + + + + array_upper + (anyarray, integer) + + + integer + + returns upper bound of the requested array dimension, returning + NULL for NULL inputs + + array_upper(array_append(ARRAY[1,2,3], 4), 1) + 4 + + + + + singleton_array + (anyelement) + + + anyarray + + create an array from the provided element, returning + NULL for NULL inputs + + singleton_array(1) + {1} + + + + + str_to_array + (text, text) + + + text[] + + splits string into array elements using provided delimiter, returning + NULL for NULL inputs + + str_to_array('1.10~^~2.20~^~3.30','~^~')::float8[] + {1.1,2.2,3.3} + + + +
+
Aggregate Functions Index: src/backend/executor/nodeSubplan.c =================================================================== RCS file: /opt/src/cvs/pgsql-server/src/backend/executor/nodeSubplan.c,v retrieving revision 1.45 diff -c -r1.45 nodeSubplan.c *** src/backend/executor/nodeSubplan.c 8 Apr 2003 23:20:01 -0000 1.45 --- src/backend/executor/nodeSubplan.c 22 May 2003 23:42:01 -0000 *************** *** 28,50 **** #include "utils/datum.h" #include "utils/lsyscache.h" - - typedef struct ArrayBuildState - { - MemoryContext mcontext; /* where all the temp stuff is kept */ - Datum *dvalues; /* array of accumulated Datums */ - /* - * The allocated size of dvalues[] is always a multiple of - * ARRAY_ELEMS_CHUNKSIZE - */ - #define ARRAY_ELEMS_CHUNKSIZE 64 - int nelems; /* number of valid Datums in dvalues[] */ - Oid element_type; /* data type of the Datums */ - int16 typlen; /* needed info about datatype */ - bool typbyval; - char typalign; - } ArrayBuildState; - static Datum ExecHashSubPlan(SubPlanState *node, ExprContext *econtext, bool *isNull); --- 28,33 ---- *************** *** 54,66 **** static void buildSubPlanHash(SubPlanState *node); static bool findPartialMatch(TupleHashTable hashtable, TupleTableSlot *slot); static bool tupleAllNulls(HeapTuple tuple); - static ArrayBuildState *accumArrayResult(ArrayBuildState *astate, - Datum dvalue, bool disnull, - Oid element_type, - MemoryContext rcontext); - static Datum makeArrayResult(ArrayBuildState *astate, - MemoryContext rcontext); - /* ---------------------------------------------------------------- * ExecSubPlan --- 37,42 ---- *************** *** 1084,1185 **** prm->execPlan = node; parent->chgParam = bms_add_member(parent->chgParam, paramid); } - } - - /* - * accumArrayResult - accumulate one (more) Datum for an ARRAY_SUBLINK - * - * astate is working state (NULL on first call) - * rcontext is where to keep working state - */ - static ArrayBuildState * - accumArrayResult(ArrayBuildState *astate, - Datum dvalue, bool disnull, - Oid element_type, - MemoryContext rcontext) - { - MemoryContext arr_context, - oldcontext; - - if (astate == NULL) - { - /* First time through --- initialize */ - - /* Make a temporary context to hold all the junk */ - arr_context = AllocSetContextCreate(rcontext, - "ARRAY_SUBLINK Result", - ALLOCSET_DEFAULT_MINSIZE, - ALLOCSET_DEFAULT_INITSIZE, - ALLOCSET_DEFAULT_MAXSIZE); - oldcontext = MemoryContextSwitchTo(arr_context); - astate = (ArrayBuildState *) palloc(sizeof(ArrayBuildState)); - astate->mcontext = arr_context; - astate->dvalues = (Datum *) - palloc(ARRAY_ELEMS_CHUNKSIZE * sizeof(Datum)); - astate->nelems = 0; - astate->element_type = element_type; - get_typlenbyvalalign(element_type, - &astate->typlen, - &astate->typbyval, - &astate->typalign); - } - else - { - oldcontext = MemoryContextSwitchTo(astate->mcontext); - Assert(astate->element_type == element_type); - /* enlarge dvalues[] if needed */ - if ((astate->nelems % ARRAY_ELEMS_CHUNKSIZE) == 0) - astate->dvalues = (Datum *) - repalloc(astate->dvalues, - (astate->nelems + ARRAY_ELEMS_CHUNKSIZE) * sizeof(Datum)); - } - - if (disnull) - elog(ERROR, "NULL elements not allowed in Arrays"); - - /* Use datumCopy to ensure pass-by-ref stuff is copied into mcontext */ - astate->dvalues[astate->nelems++] = - datumCopy(dvalue, astate->typbyval, astate->typlen); - - MemoryContextSwitchTo(oldcontext); - - return astate; - } - - /* - * makeArrayResult - produce final result of ARRAY_SUBLINK - * - * astate is working state (not NULL) - * rcontext is where to construct result - */ - static Datum - makeArrayResult(ArrayBuildState *astate, - MemoryContext rcontext) - { - ArrayType *result; - int dims[1]; - int lbs[1]; - MemoryContext oldcontext; - - /* Build the final array result in rcontext */ - oldcontext = MemoryContextSwitchTo(rcontext); - - dims[0] = astate->nelems; - lbs[0] = 1; - - result = construct_md_array(astate->dvalues, - 1, - dims, - lbs, - astate->element_type, - astate->typlen, - astate->typbyval, - astate->typalign); - - MemoryContextSwitchTo(oldcontext); - - /* Clean up all the junk */ - MemoryContextDelete(astate->mcontext); - - return PointerGetDatum(result); } --- 1060,1063 ---- Index: src/backend/utils/adt/array_userfuncs.c =================================================================== RCS file: /opt/src/cvs/pgsql-server/src/backend/utils/adt/array_userfuncs.c,v retrieving revision 1.1 diff -c -r1.1 array_userfuncs.c *** src/backend/utils/adt/array_userfuncs.c 8 Apr 2003 23:20:02 -0000 1.1 --- src/backend/utils/adt/array_userfuncs.c 23 May 2003 00:05:38 -0000 *************** *** 42,48 **** else ndims = 1; ! PG_RETURN_ARRAYTYPE_P(create_singleton_array(elem_type, PG_GETARG_DATUM(0), ndims)); } --- 42,48 ---- else ndims = 1; ! PG_RETURN_ARRAYTYPE_P(create_singleton_array(fcinfo, elem_type, PG_GETARG_DATUM(0), ndims)); } *************** *** 70,75 **** --- 70,76 ---- Oid arg1_typeid = get_fn_expr_argtype(fcinfo, 1); Oid arg0_elemid; Oid arg1_elemid; + ArrayMetaState *my_extra; if (arg0_typeid == InvalidOid || arg1_typeid == InvalidOid) elog(ERROR, "array_push: cannot determine input data types"); *************** *** 113,119 **** indx = lb[0] - 1; } ! get_typlenbyvalalign(element_type, &typlen, &typbyval, &typalign); result = array_set(v, 1, &indx, newelem, -1, typlen, typbyval, typalign, &isNull); --- 114,148 ---- indx = lb[0] - 1; } ! /* ! * We arrange to look up info about element type 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 */ ! get_typlenbyvalalign(element_type, &typlen, &typbyval, &typalign); ! ! my_extra->element_type = element_type; ! my_extra->typlen = typlen; ! my_extra->typbyval = typbyval; ! my_extra->typalign = typalign; ! } ! else ! { ! typlen = my_extra->typlen; ! typbyval = my_extra->typbyval; ! typalign = my_extra->typalign; ! } result = array_set(v, 1, &indx, newelem, -1, typlen, typbyval, typalign, &isNull); *************** *** 293,299 **** if (tgt_elem_type == InvalidOid) elog(ERROR, "Target type is not an array"); ! PG_RETURN_ARRAYTYPE_P(create_singleton_array(tgt_elem_type, PG_GETARG_DATUM(1), 1)); } --- 322,328 ---- if (tgt_elem_type == InvalidOid) elog(ERROR, "Target type is not an array"); ! PG_RETURN_ARRAYTYPE_P(create_singleton_array(fcinfo, tgt_elem_type, PG_GETARG_DATUM(1), 1)); } *************** *** 329,334 **** --- 358,364 ---- int16 typlen; bool typbyval; char typalign; + ArrayMetaState *my_extra; v = PG_GETARG_ARRAYTYPE_P(0); idx_to_chg = PG_GETARG_INT32(1); *************** *** 349,355 **** if (element_type == 0) elog(ERROR, "Invalid array element type: %u", element_type); ! get_typlenbyvalalign(element_type, &typlen, &typbyval, &typalign); result = array_set(v, 1, &idx_to_chg, newelem, -1, typlen, typbyval, typalign, &isNull); --- 379,413 ---- if (element_type == 0) elog(ERROR, "Invalid array element type: %u", element_type); ! /* ! * We arrange to look up info about element type 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 */ ! get_typlenbyvalalign(element_type, &typlen, &typbyval, &typalign); ! ! my_extra->element_type = element_type; ! my_extra->typlen = typlen; ! my_extra->typbyval = typbyval; ! my_extra->typalign = typalign; ! } ! else ! { ! typlen = my_extra->typlen; ! typbyval = my_extra->typbyval; ! typalign = my_extra->typalign; ! } result = array_set(v, 1, &idx_to_chg, newelem, -1, typlen, typbyval, typalign, &isNull); *************** *** 375,380 **** --- 433,439 ---- int16 typlen; bool typbyval; char typalign; + ArrayMetaState *my_extra; v = PG_GETARG_ARRAYTYPE_P(0); idx = PG_GETARG_INT32(1); *************** *** 394,400 **** if (element_type == 0) elog(ERROR, "Invalid array element type: %u", element_type); ! get_typlenbyvalalign(element_type, &typlen, &typbyval, &typalign); result = array_ref(v, 1, &idx, -1, typlen, typbyval, typalign, &isNull); --- 453,487 ---- if (element_type == 0) elog(ERROR, "Invalid array element type: %u", element_type); ! /* ! * We arrange to look up info about element type 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 */ ! get_typlenbyvalalign(element_type, &typlen, &typbyval, &typalign); ! ! my_extra->element_type = element_type; ! my_extra->typlen = typlen; ! my_extra->typbyval = typbyval; ! my_extra->typalign = typalign; ! } ! else ! { ! typlen = my_extra->typlen; ! typbyval = my_extra->typbyval; ! typalign = my_extra->typalign; ! } result = array_ref(v, 1, &idx, -1, typlen, typbyval, typalign, &isNull); *************** *** 406,412 **** * given a null input array. */ ArrayType * ! create_singleton_array(Oid element_type, Datum element, int ndims) { Datum dvalues[1]; int16 typlen; --- 493,502 ---- * given a null input array. */ ArrayType * ! create_singleton_array(FunctionCallInfo fcinfo, ! Oid element_type, ! Datum element, ! int ndims) { Datum dvalues[1]; int16 typlen; *************** *** 415,420 **** --- 505,511 ---- int dims[MAXDIM]; int lbs[MAXDIM]; int i; + ArrayMetaState *my_extra; if (element_type == 0) elog(ERROR, "Invalid array element type: %u", element_type); *************** *** 429,435 **** lbs[i] = 1; } ! get_typlenbyvalalign(element_type, &typlen, &typbyval, &typalign); return construct_md_array(dvalues, ndims, dims, lbs, element_type, typlen, typbyval, typalign); --- 520,554 ---- lbs[i] = 1; } ! /* ! * We arrange to look up info about element type 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 */ ! get_typlenbyvalalign(element_type, &typlen, &typbyval, &typalign); ! ! my_extra->element_type = element_type; ! my_extra->typlen = typlen; ! my_extra->typbyval = typbyval; ! my_extra->typalign = typalign; ! } ! else ! { ! typlen = my_extra->typlen; ! typbyval = my_extra->typbyval; ! typalign = my_extra->typalign; ! } return construct_md_array(dvalues, ndims, dims, lbs, element_type, typlen, typbyval, typalign); Index: src/backend/utils/adt/arrayfuncs.c =================================================================== RCS file: /opt/src/cvs/pgsql-server/src/backend/utils/adt/arrayfuncs.c,v retrieving revision 1.89 diff -c -r1.89 arrayfuncs.c *** src/backend/utils/adt/arrayfuncs.c 9 May 2003 23:01:45 -0000 1.89 --- src/backend/utils/adt/arrayfuncs.c 22 May 2003 23:42:01 -0000 *************** *** 21,28 **** --- 21,30 ---- #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" *************** *** 70,85 **** #define RETURN_NULL(type) do { *isNull = true; return (type) 0; } while (0) - /* I/O function selector for system_cache_lookup */ - typedef enum IOFuncSelector - { - IOFunc_input, - IOFunc_output, - IOFunc_receive, - IOFunc_send - } IOFuncSelector; - - 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, --- 72,77 ---- *************** *** 93,102 **** static void CopyArrayEls(char *p, Datum *values, int nitems, int typlen, bool typbyval, char typalign, bool freedata); - static void system_cache_lookup(Oid element_type, IOFuncSelector which_func, - int *typlen, bool *typbyval, - char *typdelim, Oid *typelem, - Oid *proc, char *typalign); static Datum ArrayCast(char *value, bool byval, int len); static int ArrayCastAndSet(Datum src, int typlen, bool typbyval, char typalign, --- 85,90 ---- *************** *** 154,165 **** dim[MAXDIM], lBound[MAXDIM]; char typalign; ! /* Get info about element type, including its input conversion proc */ ! system_cache_lookup(element_type, IOFunc_input, ! &typlen, &typbyval, &typdelim, ! &typelem, &typinput, &typalign); ! fmgr_info(typinput, &inputproc); /* Make a modifiable copy of the input */ /* XXX why are we allocating an extra 2 bytes here? */ --- 142,190 ---- dim[MAXDIM], lBound[MAXDIM]; char typalign; + 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_metadata(element_type, IOFunc_input, ! &typlen, &typbyval, &typdelim, ! &typelem, &typinput, &typalign); ! fmgr_info(typinput, &inputproc); ! ! my_extra->element_type = element_type; ! my_extra->typlen = typlen; ! my_extra->typbyval = typbyval; ! my_extra->typdelim = typdelim; ! my_extra->typelem = typelem; ! my_extra->typiofunc = typinput; ! my_extra->typalign = typalign; ! my_extra->proc = inputproc; ! } ! else ! { ! typlen = my_extra->typlen; ! typbyval = my_extra->typbyval; ! typdelim = my_extra->typdelim; ! typelem = my_extra->typelem; ! typinput = my_extra->typiofunc; ! typalign = my_extra->typalign; ! inputproc = my_extra->proc; ! } /* Make a modifiable copy of the input */ /* XXX why are we allocating an extra 2 bytes here? */ *************** *** 636,647 **** indx[MAXDIM]; int ndim, *dim; element_type = ARR_ELEMTYPE(v); ! system_cache_lookup(element_type, IOFunc_output, ! &typlen, &typbyval, &typdelim, ! &typelem, &typoutput, &typalign); ! fmgr_info(typoutput, &outputproc); ndim = ARR_NDIM(v); dim = ARR_DIMS(v); --- 661,711 ---- indx[MAXDIM]; int ndim, *dim; + ArrayMetaState *my_extra; element_type = ARR_ELEMTYPE(v); ! ! /* ! * 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 output conversion proc */ ! get_type_metadata(element_type, IOFunc_output, ! &typlen, &typbyval, &typdelim, ! &typelem, &typoutput, &typalign); ! fmgr_info(typoutput, &outputproc); ! ! my_extra->element_type = element_type; ! my_extra->typlen = typlen; ! my_extra->typbyval = typbyval; ! my_extra->typdelim = typdelim; ! my_extra->typelem = typelem; ! my_extra->typiofunc = typoutput; ! my_extra->typalign = typalign; ! my_extra->proc = outputproc; ! } ! else ! { ! typlen = my_extra->typlen; ! typbyval = my_extra->typbyval; ! typdelim = my_extra->typdelim; ! typelem = my_extra->typelem; ! typoutput = my_extra->typiofunc; ! typalign = my_extra->typalign; ! outputproc = my_extra->proc; ! } ndim = ARR_NDIM(v); dim = ARR_DIMS(v); *************** *** 800,805 **** --- 864,870 ---- dim[MAXDIM], lBound[MAXDIM]; char typalign; + ArrayMetaState *my_extra; /* Get the array header information */ ndim = pq_getmsgint(buf, 4); *************** *** 831,844 **** PG_RETURN_ARRAYTYPE_P(retval); } ! /* Get info about element type, including its receive conversion proc */ ! system_cache_lookup(element_type, IOFunc_receive, ! &typlen, &typbyval, &typdelim, ! &typelem, &typreceive, &typalign); ! if (!OidIsValid(typreceive)) ! elog(ERROR, "No binary input function available for type %s", ! format_type_be(element_type)); ! fmgr_info(typreceive, &receiveproc); dataPtr = ReadArrayBinary(buf, nitems, &receiveproc, typelem, typlen, typbyval, typalign, --- 896,945 ---- PG_RETURN_ARRAYTYPE_P(retval); } ! /* ! * We arrange to look up info about element type, including its receive ! * 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 receive conversion proc */ ! get_type_metadata(element_type, IOFunc_receive, ! &typlen, &typbyval, &typdelim, ! &typelem, &typreceive, &typalign); ! if (!OidIsValid(typreceive)) ! elog(ERROR, "No binary input function available for type %s", ! format_type_be(element_type)); ! fmgr_info(typreceive, &receiveproc); ! ! my_extra->element_type = element_type; ! my_extra->typlen = typlen; ! my_extra->typbyval = typbyval; ! my_extra->typdelim = typdelim; ! my_extra->typelem = typelem; ! my_extra->typiofunc = typreceive; ! my_extra->typalign = typalign; ! my_extra->proc = receiveproc; ! } ! else ! { ! typlen = my_extra->typlen; ! typbyval = my_extra->typbyval; ! typdelim = my_extra->typdelim; ! typelem = my_extra->typelem; ! typreceive = my_extra->typiofunc; ! typalign = my_extra->typalign; ! receiveproc = my_extra->proc; ! } dataPtr = ReadArrayBinary(buf, nitems, &receiveproc, typelem, typlen, typbyval, typalign, *************** *** 976,990 **** int ndim, *dim; StringInfoData buf; /* Get information about the element type and the array dimensions */ element_type = ARR_ELEMTYPE(v); ! system_cache_lookup(element_type, IOFunc_send, &typlen, &typbyval, ! &typdelim, &typelem, &typsend, &typalign); ! if (!OidIsValid(typsend)) ! elog(ERROR, "No binary output function available for type %s", ! format_type_be(element_type)); ! fmgr_info(typsend, &sendproc); ndim = ARR_NDIM(v); dim = ARR_DIMS(v); --- 1077,1130 ---- int ndim, *dim; StringInfoData buf; + ArrayMetaState *my_extra; /* Get information about the element type and the array dimensions */ element_type = ARR_ELEMTYPE(v); ! ! /* ! * We arrange to look up info about element type, including its send ! * 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 send proc */ ! get_type_metadata(element_type, IOFunc_send, &typlen, &typbyval, ! &typdelim, &typelem, &typsend, &typalign); ! if (!OidIsValid(typsend)) ! elog(ERROR, "No binary output function available for type %s", ! format_type_be(element_type)); ! fmgr_info(typsend, &sendproc); ! ! my_extra->element_type = element_type; ! my_extra->typlen = typlen; ! my_extra->typbyval = typbyval; ! my_extra->typdelim = typdelim; ! my_extra->typelem = typelem; ! my_extra->typiofunc = typsend; ! my_extra->typalign = typalign; ! my_extra->proc = sendproc; ! } ! else ! { ! typlen = my_extra->typlen; ! typbyval = my_extra->typbyval; ! typdelim = my_extra->typdelim; ! typelem = my_extra->typelem; ! typsend = my_extra->typiofunc; ! typalign = my_extra->typalign; ! sendproc = my_extra->proc; ! } ndim = ARR_NDIM(v); dim = ARR_DIMS(v); *************** *** 1811,1816 **** --- 1951,1963 ---- Oid typelem; Oid proc; char *s; + typedef struct { + ArrayMetaState *inp_extra; + ArrayMetaState *ret_extra; + } am_extra; + am_extra *my_extra; + ArrayMetaState *inp_extra; + ArrayMetaState *ret_extra; /* Get input array */ if (fcinfo->nargs < 1) *************** *** 1829,1839 **** if (nitems <= 0) PG_RETURN_ARRAYTYPE_P(v); ! /* Lookup source and result types. Unneeded variables are reused. */ ! system_cache_lookup(inpType, IOFunc_input, &inp_typlen, &inp_typbyval, ! &typdelim, &typelem, &proc, &inp_typalign); ! system_cache_lookup(retType, IOFunc_input, &typlen, &typbyval, ! &typdelim, &typelem, &proc, &typalign); /* Allocate temporary array for new values */ values = (Datum *) palloc(nitems * sizeof(Datum)); --- 1976,2056 ---- if (nitems <= 0) PG_RETURN_ARRAYTYPE_P(v); ! /* ! * We arrange to look up info about input and return element types only ! * once per series of calls, assuming the element type doesn't change ! * underneath us. ! */ ! my_extra = (am_extra *) fcinfo->flinfo->fn_extra; ! if (my_extra == NULL) ! { ! fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt, ! sizeof(am_extra)); ! my_extra = (am_extra *) fcinfo->flinfo->fn_extra; ! ! my_extra->inp_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt, ! sizeof(ArrayMetaState)); ! inp_extra = my_extra->inp_extra; ! inp_extra->element_type = InvalidOid; ! ! my_extra->ret_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt, ! sizeof(ArrayMetaState)); ! ret_extra = my_extra->ret_extra; ! ret_extra->element_type = InvalidOid; ! } ! else ! { ! inp_extra = my_extra->inp_extra; ! ret_extra = my_extra->ret_extra; ! } ! ! if (inp_extra->element_type != inpType) ! { ! /* Lookup source and result types. Unneeded variables are reused. */ ! get_type_metadata(inpType, IOFunc_input, &inp_typlen, &inp_typbyval, ! &typdelim, &typelem, &proc, &inp_typalign); ! ! inp_extra->element_type = inpType; ! inp_extra->typlen = inp_typlen; ! inp_extra->typbyval = inp_typbyval; ! inp_extra->typdelim = typdelim; ! inp_extra->typelem = typelem; ! inp_extra->typiofunc = proc; ! inp_extra->typalign = inp_typalign; ! } ! else ! { ! inp_typlen = inp_extra->typlen; ! inp_typbyval = inp_extra->typbyval; ! typdelim = inp_extra->typdelim; ! typelem = inp_extra->typelem; ! proc = inp_extra->typiofunc; ! inp_typalign = inp_extra->typalign; ! } ! ! if (ret_extra->element_type != retType) ! { ! /* Lookup source and result types. Unneeded variables are reused. */ ! get_type_metadata(retType, IOFunc_input, &typlen, &typbyval, ! &typdelim, &typelem, &proc, &typalign); ! ! ret_extra->element_type = retType; ! ret_extra->typlen = typlen; ! ret_extra->typbyval = typbyval; ! ret_extra->typdelim = typdelim; ! ret_extra->typelem = typelem; ! ret_extra->typiofunc = proc; ! ret_extra->typalign = typalign; ! } ! else ! { ! typlen = ret_extra->typlen; ! typbyval = ret_extra->typbyval; ! typdelim = ret_extra->typdelim; ! typelem = ret_extra->typelem; ! proc = ret_extra->typiofunc; ! typalign = ret_extra->typalign; ! } /* Allocate temporary array for new values */ values = (Datum *) palloc(nitems * sizeof(Datum)); *************** *** 2049,2056 **** * compares two arrays for equality * result : * returns true if the arrays are equal, false otherwise. - * - * XXX bitwise equality is pretty bogus ... *----------------------------------------------------------------------------- */ Datum --- 2266,2271 ---- *************** *** 2058,2069 **** { ArrayType *array1 = PG_GETARG_ARRAYTYPE_P(0); ArrayType *array2 = PG_GETARG_ARRAYTYPE_P(1); bool result = true; ! if (ARR_SIZE(array1) != ARR_SIZE(array2)) ! result = false; ! else if (memcmp(array1, array2, ARR_SIZE(array1)) != 0) result = false; /* Avoid leaking memory when handed toasted input. */ PG_FREE_IF_COPY(array1, 0); --- 2273,2390 ---- { ArrayType *array1 = PG_GETARG_ARRAYTYPE_P(0); ArrayType *array2 = PG_GETARG_ARRAYTYPE_P(1); + char *p1 = (char *) ARR_DATA_PTR(array1); + char *p2 = (char *) ARR_DATA_PTR(array2); + int ndims1 = ARR_NDIM(array1); + int ndims2 = ARR_NDIM(array2); + int *dims1 = ARR_DIMS(array1); + int *dims2 = ARR_DIMS(array2); + int nitems1 = ArrayGetNItems(ndims1, dims1); + int nitems2 = ArrayGetNItems(ndims2, dims2); + Oid element_type = ARR_ELEMTYPE(array1); + FmgrInfo *fmgr_info = fcinfo->flinfo; bool result = true; + int typlen; + bool typbyval; + char typdelim; + Oid typelem; + char typalign; + Oid typiofunc; + int i; + ArrayMetaState *my_extra; + FunctionCallInfoData locfcinfo; ! /* fast path if the arrays do not have the same number of elements */ ! if (nitems1 != nitems2) result = false; + else + { + /* + * We arrange to look up the equality function only once per series of + * calls, assuming the element type doesn't change underneath us. + */ + my_extra = (ArrayMetaState *) fmgr_info->fn_extra; + if (my_extra == NULL) + { + fmgr_info->fn_extra = MemoryContextAlloc(fmgr_info->fn_mcxt, + sizeof(ArrayMetaState)); + my_extra = (ArrayMetaState *) fmgr_info->fn_extra; + my_extra->element_type = InvalidOid; + } + + if (my_extra->element_type != element_type) + { + Oid opfuncid = equality_oper_funcid(element_type); + + if (OidIsValid(opfuncid)) + fmgr_info_cxt(opfuncid, &my_extra->proc, fmgr_info->fn_mcxt); + else + elog(ERROR, + "array_eq: cannot find equality operator for type: %u", + element_type); + + get_type_metadata(element_type, IOFunc_output, + &typlen, &typbyval, &typdelim, + &typelem, &typiofunc, &typalign); + + my_extra->element_type = element_type; + my_extra->typlen = typlen; + my_extra->typbyval = typbyval; + my_extra->typdelim = typdelim; + my_extra->typelem = typelem; + my_extra->typiofunc = typiofunc; + my_extra->typalign = typalign; + } + else + { + typlen = my_extra->typlen; + typbyval = my_extra->typbyval; + typdelim = my_extra->typdelim; + typelem = my_extra->typelem; + typiofunc = my_extra->typiofunc; + typalign = my_extra->typalign; + } + + /* + * apply the operator to each pair of array elements. + */ + MemSet(&locfcinfo, 0, sizeof(locfcinfo)); + locfcinfo.flinfo = &my_extra->proc; + locfcinfo.nargs = 2; + + /* Loop over source data */ + for (i = 0; i < nitems1; i++) + { + Datum elt1; + Datum elt2; + bool oprresult; + + /* Get element pair */ + elt1 = fetch_att(p1, typbyval, typlen); + elt2 = fetch_att(p2, typbyval, typlen); + + p1 = att_addlength(p1, typlen, PointerGetDatum(p1)); + p1 = (char *) att_align(p1, typalign); + + p2 = att_addlength(p2, typlen, PointerGetDatum(p2)); + p2 = (char *) att_align(p2, typalign); + + /* + * Apply the operator to the element pair + */ + locfcinfo.arg[0] = elt1; + locfcinfo.arg[1] = elt2; + locfcinfo.argnull[0] = false; + locfcinfo.argnull[1] = false; + locfcinfo.isnull = false; + oprresult = DatumGetBool(FunctionCallInvoke(&locfcinfo)); + if (!oprresult) + { + result = false; + break; + } + } + } /* Avoid leaking memory when handed toasted input. */ PG_FREE_IF_COPY(array1, 0); *************** *** 2077,2125 **** /******************| Support Routines |*****************/ /***************************************************************************/ - static void - system_cache_lookup(Oid element_type, - IOFuncSelector which_func, - int *typlen, - bool *typbyval, - char *typdelim, - Oid *typelem, - Oid *proc, - char *typalign) - { - HeapTuple typeTuple; - Form_pg_type typeStruct; - - typeTuple = SearchSysCache(TYPEOID, - ObjectIdGetDatum(element_type), - 0, 0, 0); - if (!HeapTupleIsValid(typeTuple)) - elog(ERROR, "cache lookup failed for type %u", element_type); - typeStruct = (Form_pg_type) GETSTRUCT(typeTuple); - - *typlen = typeStruct->typlen; - *typbyval = typeStruct->typbyval; - *typdelim = typeStruct->typdelim; - *typelem = typeStruct->typelem; - *typalign = typeStruct->typalign; - switch (which_func) - { - case IOFunc_input: - *proc = typeStruct->typinput; - break; - case IOFunc_output: - *proc = typeStruct->typoutput; - break; - case IOFunc_receive: - *proc = typeStruct->typreceive; - break; - case IOFunc_send: - *proc = typeStruct->typsend; - break; - } - ReleaseSysCache(typeTuple); - } - /* * Fetch array element at pointer, converted correctly to a Datum */ --- 2398,2403 ---- *************** *** 2453,2456 **** --- 2731,2849 ---- locfcinfo.arg[0] = PointerGetDatum(src); return array_map(&locfcinfo, my_extra->srctype, my_extra->desttype); + } + + /* + * accumArrayResult - accumulate one (more) Datum for an ARRAY_SUBLINK + * + * astate is working state (NULL on first call) + * rcontext is where to keep working state + */ + ArrayBuildState * + accumArrayResult(ArrayBuildState *astate, + Datum dvalue, bool disnull, + Oid element_type, + MemoryContext rcontext) + { + MemoryContext arr_context, + oldcontext; + + if (astate == NULL) + { + /* First time through --- initialize */ + + /* Make a temporary context to hold all the junk */ + arr_context = AllocSetContextCreate(rcontext, + "accumArrayResult", + ALLOCSET_DEFAULT_MINSIZE, + ALLOCSET_DEFAULT_INITSIZE, + ALLOCSET_DEFAULT_MAXSIZE); + oldcontext = MemoryContextSwitchTo(arr_context); + astate = (ArrayBuildState *) palloc(sizeof(ArrayBuildState)); + astate->mcontext = arr_context; + astate->dvalues = (Datum *) + palloc(ARRAY_ELEMS_CHUNKSIZE * sizeof(Datum)); + astate->nelems = 0; + astate->element_type = element_type; + get_typlenbyvalalign(element_type, + &astate->typlen, + &astate->typbyval, + &astate->typalign); + } + else + { + oldcontext = MemoryContextSwitchTo(astate->mcontext); + Assert(astate->element_type == element_type); + /* enlarge dvalues[] if needed */ + if ((astate->nelems % ARRAY_ELEMS_CHUNKSIZE) == 0) + astate->dvalues = (Datum *) + repalloc(astate->dvalues, + (astate->nelems + ARRAY_ELEMS_CHUNKSIZE) * sizeof(Datum)); + } + + if (disnull) + elog(ERROR, "NULL elements not allowed in Arrays"); + + /* Use datumCopy to ensure pass-by-ref stuff is copied into mcontext */ + astate->dvalues[astate->nelems++] = + datumCopy(dvalue, astate->typbyval, astate->typlen); + + MemoryContextSwitchTo(oldcontext); + + return astate; + } + + /* + * makeArrayResult - produce final result of accumArrayResult + * + * astate is working state (not NULL) + * rcontext is where to construct result + */ + Datum + makeArrayResult(ArrayBuildState *astate, + MemoryContext rcontext) + { + int dims[1]; + int lbs[1]; + + dims[0] = astate->nelems; + lbs[0] = 1; + + return makeMdArrayResult(astate, 1, dims, lbs, rcontext); + } + + /* + * makeMdArrayResult - produce md final result of accumArrayResult + * + * astate is working state (not NULL) + * rcontext is where to construct result + */ + Datum + makeMdArrayResult(ArrayBuildState *astate, + int ndims, + int *dims, + int *lbs, + MemoryContext rcontext) + { + ArrayType *result; + MemoryContext oldcontext; + + /* Build the final array result in rcontext */ + oldcontext = MemoryContextSwitchTo(rcontext); + + result = construct_md_array(astate->dvalues, + ndims, + dims, + lbs, + astate->element_type, + astate->typlen, + astate->typbyval, + astate->typalign); + + MemoryContextSwitchTo(oldcontext); + + /* Clean up all the junk */ + MemoryContextDelete(astate->mcontext); + + return PointerGetDatum(result); } Index: src/backend/utils/adt/varlena.c =================================================================== RCS file: /opt/src/cvs/pgsql-server/src/backend/utils/adt/varlena.c,v retrieving revision 1.98 diff -c -r1.98 varlena.c *** src/backend/utils/adt/varlena.c 15 May 2003 15:50:19 -0000 1.98 --- src/backend/utils/adt/varlena.c 23 May 2003 00:11:27 -0000 *************** *** 19,29 **** --- 19,32 ---- #include "mb/pg_wchar.h" #include "miscadmin.h" #include "access/tuptoaster.h" + #include "catalog/pg_type.h" #include "lib/stringinfo.h" #include "libpq/crypt.h" #include "libpq/pqformat.h" + #include "utils/array.h" #include "utils/builtins.h" #include "utils/pg_locale.h" + #include "utils/lsyscache.h" typedef struct varlena unknown; *************** *** 1983,1990 **** if (fldnum == 1) /* first field - just return the input * string */ PG_RETURN_TEXT_P(inputstring); ! else ! /* otherwise return an empty string */ PG_RETURN_TEXT_P(PG_STR_GET_TEXT("")); } --- 1986,1992 ---- if (fldnum == 1) /* first field - just return the input * string */ PG_RETURN_TEXT_P(inputstring); ! else /* otherwise return an empty string */ PG_RETURN_TEXT_P(PG_STR_GET_TEXT("")); } *************** *** 2004,2011 **** if (fldnum == 1) /* first field - just return the input * string */ PG_RETURN_TEXT_P(inputstring); ! else ! /* otherwise return an empty string */ PG_RETURN_TEXT_P(PG_STR_GET_TEXT("")); } else if ((start_posn != 0) && (end_posn == 0)) --- 2006,2012 ---- if (fldnum == 1) /* first field - just return the input * string */ PG_RETURN_TEXT_P(inputstring); ! else /* otherwise return an empty string */ PG_RETURN_TEXT_P(PG_STR_GET_TEXT("")); } else if ((start_posn != 0) && (end_posn == 0)) *************** *** 2026,2031 **** --- 2027,2217 ---- result_text = text_substring(PointerGetDatum(inputstring), start_posn + fldsep_len, end_posn - start_posn - fldsep_len, false); PG_RETURN_TEXT_P(result_text); } + } + + /* + * text_to_array + * parse input string + * return text array of elements + * based on provided field separator + */ + Datum + text_to_array(PG_FUNCTION_ARGS) + { + text *inputstring = PG_GETARG_TEXT_P(0); + int inputstring_len = TEXTLEN(inputstring); + text *fldsep = PG_GETARG_TEXT_P(1); + int fldsep_len = TEXTLEN(fldsep); + int fldnum; + int start_posn = 0; + int end_posn = 0; + text *result_text = NULL; + ArrayBuildState *astate = NULL; + MemoryContext oldcontext = CurrentMemoryContext; + + /* return NULL for empty input string */ + if (inputstring_len < 1) + PG_RETURN_NULL(); + + /* empty field separator + * return one element, 1D, array using the input string */ + if (fldsep_len < 1) + PG_RETURN_ARRAYTYPE_P(create_singleton_array(fcinfo, TEXTOID, + CStringGetDatum(inputstring), 1)); + + /* start with end position holding the initial start position */ + end_posn = 0; + for (fldnum=1;;fldnum++) /* field number is 1 based */ + { + Datum dvalue; + bool disnull = false; + + start_posn = end_posn; + end_posn = text_position(PointerGetDatum(inputstring), + PointerGetDatum(fldsep), + fldnum); + + if ((start_posn == 0) && (end_posn == 0)) /* fldsep not found */ + { + if (fldnum == 1) + { + /* first element + * return one element, 1D, array using the input string */ + PG_RETURN_ARRAYTYPE_P(create_singleton_array(fcinfo, TEXTOID, + CStringGetDatum(inputstring), 1)); + } + else + { + /* otherwise create array and exit */ + PG_RETURN_ARRAYTYPE_P(makeArrayResult(astate, oldcontext)); + } + } + else if ((start_posn != 0) && (end_posn == 0)) + { + /* last field requested */ + result_text = text_substring(PointerGetDatum(inputstring), start_posn + fldsep_len, -1, true); + } + else if ((start_posn == 0) && (end_posn != 0)) + { + /* first field requested */ + result_text = LEFT(inputstring, fldsep); + } + else + { + /* prior to last field requested */ + result_text = text_substring(PointerGetDatum(inputstring), start_posn + fldsep_len, end_posn - start_posn - fldsep_len, false); + } + + /* stash away current value */ + dvalue = PointerGetDatum(result_text); + astate = accumArrayResult(astate, dvalue, + disnull, TEXTOID, oldcontext); + + } + + /* never reached -- keep compiler quiet */ + PG_RETURN_NULL(); + } + + /* + * array_to_text + * concatenate Cstring representation of input array elements + * using provided field separator + */ + Datum + array_to_text(PG_FUNCTION_ARGS) + { + ArrayType *v = PG_GETARG_ARRAYTYPE_P(0); + char *fldsep = PG_TEXTARG_GET_STR(1); + int nitems, *dims, ndims; + char *p; + Oid element_type; + int typlen; + bool typbyval; + char typdelim; + Oid typoutput, + typelem; + FmgrInfo outputproc; + char typalign; + StringInfo result_str = makeStringInfo(); + int i; + ArrayMetaState *my_extra; + + p = ARR_DATA_PTR(v); + ndims = ARR_NDIM(v); + dims = ARR_DIMS(v); + nitems = ArrayGetNItems(ndims, dims); + + /* if there are no elements, return an empty string */ + if (nitems == 0) + PG_RETURN_TEXT_P(PG_STR_GET_TEXT("")); + + element_type = ARR_ELEMTYPE(v); + + /* + * We arrange to look up info about element type, including its output + * 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 output conversion proc */ + get_type_metadata(element_type, IOFunc_output, + &typlen, &typbyval, &typdelim, + &typelem, &typoutput, &typalign); + fmgr_info(typoutput, &outputproc); + + my_extra->element_type = element_type; + my_extra->typlen = typlen; + my_extra->typbyval = typbyval; + my_extra->typdelim = typdelim; + my_extra->typelem = typelem; + my_extra->typiofunc = typoutput; + my_extra->typalign = typalign; + my_extra->proc = outputproc; + } + else + { + typlen = my_extra->typlen; + typbyval = my_extra->typbyval; + typdelim = my_extra->typdelim; + typelem = my_extra->typelem; + typoutput = my_extra->typiofunc; + typalign = my_extra->typalign; + outputproc = my_extra->proc; + } + + for (i = 0; i < nitems; i++) + { + Datum itemvalue; + char *value; + + itemvalue = fetch_att(p, typbyval, typlen); + + value = DatumGetCString(FunctionCall3(&outputproc, + itemvalue, + ObjectIdGetDatum(typelem), + Int32GetDatum(-1))); + + if (i > 0) + appendStringInfo(result_str, "%s%s", fldsep, value); + else + appendStringInfo(result_str, "%s", value); + + p = att_addlength(p, typlen, PointerGetDatum(p)); + p = (char *) att_align(p, typalign); + } + + PG_RETURN_TEXT_P(PG_STR_GET_TEXT(result_str->data)); } #define HEXBASE 16 Index: src/backend/utils/cache/lsyscache.c =================================================================== RCS file: /opt/src/cvs/pgsql-server/src/backend/utils/cache/lsyscache.c,v retrieving revision 1.94 diff -c -r1.94 lsyscache.c *** src/backend/utils/cache/lsyscache.c 13 May 2003 04:38:58 -0000 1.94 --- src/backend/utils/cache/lsyscache.c 22 May 2003 23:42:01 -0000 *************** *** 969,974 **** --- 969,1024 ---- ReleaseSysCache(tp); } + /* + * get_type_metadata + * + * A six-fer: given the type OID, return typlen, typbyval, typalign, + * typdelim, typelem, IO function Oid. The IO function + * returned is controlled by IOFuncSelector + */ + void + get_type_metadata(Oid element_type, + IOFuncSelector which_func, + int *typlen, + bool *typbyval, + char *typdelim, + Oid *typelem, + Oid *proc, + char *typalign) + { + HeapTuple typeTuple; + Form_pg_type typeStruct; + + typeTuple = SearchSysCache(TYPEOID, + ObjectIdGetDatum(element_type), + 0, 0, 0); + if (!HeapTupleIsValid(typeTuple)) + elog(ERROR, "cache lookup failed for type %u", element_type); + typeStruct = (Form_pg_type) GETSTRUCT(typeTuple); + + *typlen = typeStruct->typlen; + *typbyval = typeStruct->typbyval; + *typdelim = typeStruct->typdelim; + *typelem = typeStruct->typelem; + *typalign = typeStruct->typalign; + switch (which_func) + { + case IOFunc_input: + *proc = typeStruct->typinput; + break; + case IOFunc_output: + *proc = typeStruct->typoutput; + break; + case IOFunc_receive: + *proc = typeStruct->typreceive; + break; + case IOFunc_send: + *proc = typeStruct->typsend; + break; + } + ReleaseSysCache(typeTuple); + } + #ifdef NOT_USED char get_typalign(Oid typid) Index: src/backend/utils/fmgr/fmgr.c =================================================================== RCS file: /opt/src/cvs/pgsql-server/src/backend/utils/fmgr/fmgr.c,v retrieving revision 1.68 diff -c -r1.68 fmgr.c *** src/backend/utils/fmgr/fmgr.c 8 Apr 2003 23:20:02 -0000 1.68 --- src/backend/utils/fmgr/fmgr.c 22 May 2003 23:42:01 -0000 *************** *** 1673,1675 **** --- 1673,1701 ---- return exprType((Node *) nth(argnum, args)); } + + /* + * Get the OID of the function or operator + * + * Returns InvalidOid if information is not available + */ + Oid + get_fn_expr_functype(FunctionCallInfo fcinfo) + { + Node *expr; + + /* + * can't return anything useful if we have no FmgrInfo or if + * its fn_expr node has not been initialized + */ + if (!fcinfo || !fcinfo->flinfo || !fcinfo->flinfo->fn_expr) + return InvalidOid; + + expr = fcinfo->flinfo->fn_expr; + if (IsA(expr, FuncExpr)) + return ((FuncExpr *) expr)->funcid; + else if (IsA(expr, OpExpr)) + return ((OpExpr *) expr)->opno; + else + return InvalidOid; + } Index: src/include/fmgr.h =================================================================== RCS file: /opt/src/cvs/pgsql-server/src/include/fmgr.h,v retrieving revision 1.27 diff -c -r1.27 fmgr.h *** src/include/fmgr.h 8 Apr 2003 23:20:04 -0000 1.27 --- src/include/fmgr.h 22 May 2003 23:42:01 -0000 *************** *** 377,385 **** * Routines in fmgr.c */ extern Pg_finfo_record *fetch_finfo_record(void *filehandle, char *funcname); ! extern Oid fmgr_internal_function(const char *proname); ! extern Oid get_fn_expr_rettype(FunctionCallInfo fcinfo); ! extern Oid get_fn_expr_argtype(FunctionCallInfo fcinfo, int argnum); /* * Routines in dfmgr.c --- 377,386 ---- * Routines in fmgr.c */ extern Pg_finfo_record *fetch_finfo_record(void *filehandle, char *funcname); ! extern Oid fmgr_internal_function(const char *proname); ! extern Oid get_fn_expr_rettype(FunctionCallInfo fcinfo); ! extern Oid get_fn_expr_argtype(FunctionCallInfo fcinfo, int argnum); ! extern Oid get_fn_expr_functype(FunctionCallInfo fcinfo); /* * Routines in dfmgr.c Index: src/include/catalog/pg_proc.h =================================================================== RCS file: /opt/src/cvs/pgsql-server/src/include/catalog/pg_proc.h,v retrieving revision 1.300 diff -c -r1.300 pg_proc.h *** src/include/catalog/pg_proc.h 15 May 2003 15:50:19 -0000 1.300 --- src/include/catalog/pg_proc.h 22 May 2003 23:42:01 -0000 *************** *** 1016,1021 **** --- 1016,1025 ---- DESCR("concatenate two arrays"); DATA(insert OID = 384 ( array_coerce PGNSP PGUID 12 f f t f i 1 2277 "2277" array_type_coerce - _null_ )); DESCR("coerce array type to another array type"); + DATA(insert OID = 385 ( str_to_array PGNSP PGUID 12 f f t f i 2 1009 "25 25" text_to_array - _null_ )); + DESCR("split delimited text into text[]"); + DATA(insert OID = 386 ( array_to_str PGNSP PGUID 12 f f t f i 2 25 "2277 25" array_to_text - _null_ )); + DESCR("concatenate array elements, using delimiter, into text"); DATA(insert OID = 760 ( smgrin PGNSP PGUID 12 f f t f s 1 210 "2275" smgrin - _null_ )); DESCR("I/O"); Index: src/include/utils/array.h =================================================================== RCS file: /opt/src/cvs/pgsql-server/src/include/utils/array.h,v retrieving revision 1.38 diff -c -r1.38 array.h *** src/include/utils/array.h 8 May 2003 22:19:57 -0000 1.38 --- src/include/utils/array.h 23 May 2003 00:06:49 -0000 *************** *** 32,37 **** --- 32,68 ---- Oid elemtype; /* element type OID */ } ArrayType; + typedef struct ArrayBuildState + { + MemoryContext mcontext; /* where all the temp stuff is kept */ + Datum *dvalues; /* array of accumulated Datums */ + /* + * The allocated size of dvalues[] is always a multiple of + * ARRAY_ELEMS_CHUNKSIZE + */ + #define ARRAY_ELEMS_CHUNKSIZE 64 + int nelems; /* number of valid Datums in dvalues[] */ + Oid element_type; /* data type of the Datums */ + int16 typlen; /* needed info about datatype */ + bool typbyval; + char typalign; + } ArrayBuildState; + + /* + * structure to cache type metadata needed for array manipulation + */ + typedef struct ArrayMetaState + { + Oid element_type; + int typlen; + bool typbyval; + char typdelim; + Oid typelem; + Oid typiofunc; + char typalign; + FmgrInfo proc; + } ArrayMetaState; + /* * fmgr macros for array objects */ *************** *** 124,130 **** Oid elmtype, int elmlen, bool elmbyval, char elmalign, Datum **elemsp, int *nelemsp); ! /* * prototypes for functions defined in arrayutils.c --- 155,168 ---- Oid elmtype, int elmlen, bool elmbyval, char elmalign, Datum **elemsp, int *nelemsp); ! extern ArrayBuildState *accumArrayResult(ArrayBuildState *astate, ! Datum dvalue, bool disnull, ! Oid element_type, ! MemoryContext rcontext); ! extern Datum makeArrayResult(ArrayBuildState *astate, ! MemoryContext rcontext); ! extern Datum makeMdArrayResult(ArrayBuildState *astate, int ndims, ! int *dims, int *lbs, MemoryContext rcontext); /* * prototypes for functions defined in arrayutils.c *************** *** 146,152 **** extern Datum array_accum(PG_FUNCTION_ARGS); extern Datum array_cat(PG_FUNCTION_ARGS); ! extern ArrayType *create_singleton_array(Oid element_type, Datum element, int ndims); --- 184,191 ---- extern Datum array_accum(PG_FUNCTION_ARGS); extern Datum array_cat(PG_FUNCTION_ARGS); ! extern ArrayType *create_singleton_array(FunctionCallInfo fcinfo, ! Oid element_type, Datum element, int ndims); Index: src/include/utils/builtins.h =================================================================== RCS file: /opt/src/cvs/pgsql-server/src/include/utils/builtins.h,v retrieving revision 1.217 diff -c -r1.217 builtins.h *** src/include/utils/builtins.h 15 May 2003 15:50:20 -0000 1.217 --- src/include/utils/builtins.h 22 May 2003 23:42:01 -0000 *************** *** 539,544 **** --- 539,546 ---- List **namelist); extern Datum replace_text(PG_FUNCTION_ARGS); extern Datum split_text(PG_FUNCTION_ARGS); + extern Datum text_to_array(PG_FUNCTION_ARGS); + extern Datum array_to_text(PG_FUNCTION_ARGS); extern Datum to_hex32(PG_FUNCTION_ARGS); extern Datum to_hex64(PG_FUNCTION_ARGS); extern Datum md5_text(PG_FUNCTION_ARGS); Index: src/include/utils/lsyscache.h =================================================================== RCS file: /opt/src/cvs/pgsql-server/src/include/utils/lsyscache.h,v retrieving revision 1.69 diff -c -r1.69 lsyscache.h *** src/include/utils/lsyscache.h 9 May 2003 18:08:48 -0000 1.69 --- src/include/utils/lsyscache.h 22 May 2003 23:42:01 -0000 *************** *** 15,20 **** --- 15,29 ---- #include "access/htup.h" + /* I/O function selector for system_cache_lookup */ + typedef enum IOFuncSelector + { + IOFunc_input, + IOFunc_output, + IOFunc_receive, + IOFunc_send + } IOFuncSelector; + extern bool op_in_opclass(Oid opno, Oid opclass); extern bool op_requires_recheck(Oid opno, Oid opclass); extern char *get_attname(Oid relid, AttrNumber attnum); *************** *** 53,58 **** --- 62,75 ---- extern void get_typlenbyval(Oid typid, int16 *typlen, bool *typbyval); extern void get_typlenbyvalalign(Oid typid, int16 *typlen, bool *typbyval, char *typalign); + extern void get_type_metadata(Oid element_type, + IOFuncSelector which_func, + int *typlen, + bool *typbyval, + char *typdelim, + Oid *typelem, + Oid *proc, + char *typalign); extern char get_typstorage(Oid typid); extern int32 get_typtypmod(Oid typid); extern Node *get_typdefault(Oid typid);