00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013 #include "postgres.h"
00014
00015 #include "utils/array.h"
00016 #include "utils/builtins.h"
00017 #include "utils/lsyscache.h"
00018
00019
00020
00021
00022
00023
00024
00025 Datum
00026 array_push(PG_FUNCTION_ARGS)
00027 {
00028 ArrayType *v;
00029 Datum newelem;
00030 bool isNull;
00031 int *dimv,
00032 *lb;
00033 ArrayType *result;
00034 int indx;
00035 Oid element_type;
00036 int16 typlen;
00037 bool typbyval;
00038 char typalign;
00039 Oid arg0_typeid = get_fn_expr_argtype(fcinfo->flinfo, 0);
00040 Oid arg1_typeid = get_fn_expr_argtype(fcinfo->flinfo, 1);
00041 Oid arg0_elemid;
00042 Oid arg1_elemid;
00043 ArrayMetaState *my_extra;
00044
00045 if (arg0_typeid == InvalidOid || arg1_typeid == InvalidOid)
00046 ereport(ERROR,
00047 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
00048 errmsg("could not determine input data types")));
00049
00050 arg0_elemid = get_element_type(arg0_typeid);
00051 arg1_elemid = get_element_type(arg1_typeid);
00052
00053 if (arg0_elemid != InvalidOid)
00054 {
00055 if (PG_ARGISNULL(0))
00056 v = construct_empty_array(arg0_elemid);
00057 else
00058 v = PG_GETARG_ARRAYTYPE_P(0);
00059 isNull = PG_ARGISNULL(1);
00060 if (isNull)
00061 newelem = (Datum) 0;
00062 else
00063 newelem = PG_GETARG_DATUM(1);
00064 }
00065 else if (arg1_elemid != InvalidOid)
00066 {
00067 if (PG_ARGISNULL(1))
00068 v = construct_empty_array(arg1_elemid);
00069 else
00070 v = PG_GETARG_ARRAYTYPE_P(1);
00071 isNull = PG_ARGISNULL(0);
00072 if (isNull)
00073 newelem = (Datum) 0;
00074 else
00075 newelem = PG_GETARG_DATUM(0);
00076 }
00077 else
00078 {
00079
00080 ereport(ERROR,
00081 (errcode(ERRCODE_DATATYPE_MISMATCH),
00082 errmsg("neither input type is an array")));
00083 PG_RETURN_NULL();
00084 }
00085
00086 element_type = ARR_ELEMTYPE(v);
00087
00088 if (ARR_NDIM(v) == 1)
00089 {
00090 lb = ARR_LBOUND(v);
00091 dimv = ARR_DIMS(v);
00092
00093 if (arg0_elemid != InvalidOid)
00094 {
00095
00096 int ub = dimv[0] + lb[0] - 1;
00097
00098 indx = ub + 1;
00099
00100 if (indx < ub)
00101 ereport(ERROR,
00102 (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
00103 errmsg("integer out of range")));
00104 }
00105 else
00106 {
00107
00108 indx = lb[0] - 1;
00109
00110 if (indx > lb[0])
00111 ereport(ERROR,
00112 (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
00113 errmsg("integer out of range")));
00114 }
00115 }
00116 else if (ARR_NDIM(v) == 0)
00117 indx = 1;
00118 else
00119 ereport(ERROR,
00120 (errcode(ERRCODE_DATA_EXCEPTION),
00121 errmsg("argument must be empty or one-dimensional array")));
00122
00123
00124
00125
00126
00127 my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
00128 if (my_extra == NULL)
00129 {
00130 fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
00131 sizeof(ArrayMetaState));
00132 my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
00133 my_extra->element_type = ~element_type;
00134 }
00135
00136 if (my_extra->element_type != element_type)
00137 {
00138
00139 get_typlenbyvalalign(element_type,
00140 &my_extra->typlen,
00141 &my_extra->typbyval,
00142 &my_extra->typalign);
00143 my_extra->element_type = element_type;
00144 }
00145 typlen = my_extra->typlen;
00146 typbyval = my_extra->typbyval;
00147 typalign = my_extra->typalign;
00148
00149 result = array_set(v, 1, &indx, newelem, isNull,
00150 -1, typlen, typbyval, typalign);
00151
00152
00153
00154
00155
00156 if (ARR_NDIM(v) == 1)
00157 ARR_LBOUND(result)[0] = ARR_LBOUND(v)[0];
00158
00159 PG_RETURN_ARRAYTYPE_P(result);
00160 }
00161
00162
00163
00164
00165
00166
00167
00168 Datum
00169 array_cat(PG_FUNCTION_ARGS)
00170 {
00171 ArrayType *v1,
00172 *v2;
00173 ArrayType *result;
00174 int *dims,
00175 *lbs,
00176 ndims,
00177 nitems,
00178 ndatabytes,
00179 nbytes;
00180 int *dims1,
00181 *lbs1,
00182 ndims1,
00183 nitems1,
00184 ndatabytes1;
00185 int *dims2,
00186 *lbs2,
00187 ndims2,
00188 nitems2,
00189 ndatabytes2;
00190 int i;
00191 char *dat1,
00192 *dat2;
00193 bits8 *bitmap1,
00194 *bitmap2;
00195 Oid element_type;
00196 Oid element_type1;
00197 Oid element_type2;
00198 int32 dataoffset;
00199
00200
00201 if (PG_ARGISNULL(0))
00202 {
00203 if (PG_ARGISNULL(1))
00204 PG_RETURN_NULL();
00205 result = PG_GETARG_ARRAYTYPE_P(1);
00206 PG_RETURN_ARRAYTYPE_P(result);
00207 }
00208 if (PG_ARGISNULL(1))
00209 {
00210 result = PG_GETARG_ARRAYTYPE_P(0);
00211 PG_RETURN_ARRAYTYPE_P(result);
00212 }
00213
00214 v1 = PG_GETARG_ARRAYTYPE_P(0);
00215 v2 = PG_GETARG_ARRAYTYPE_P(1);
00216
00217 element_type1 = ARR_ELEMTYPE(v1);
00218 element_type2 = ARR_ELEMTYPE(v2);
00219
00220
00221 if (element_type1 != element_type2)
00222 ereport(ERROR,
00223 (errcode(ERRCODE_DATATYPE_MISMATCH),
00224 errmsg("cannot concatenate incompatible arrays"),
00225 errdetail("Arrays with element types %s and %s are not "
00226 "compatible for concatenation.",
00227 format_type_be(element_type1),
00228 format_type_be(element_type2))));
00229
00230
00231 element_type = element_type1;
00232
00233
00234
00235
00236
00237
00238
00239
00240
00241
00242 ndims1 = ARR_NDIM(v1);
00243 ndims2 = ARR_NDIM(v2);
00244
00245
00246
00247
00248
00249
00250
00251 if (ndims1 == 0 && ndims2 > 0)
00252 PG_RETURN_ARRAYTYPE_P(v2);
00253
00254 if (ndims2 == 0)
00255 PG_RETURN_ARRAYTYPE_P(v1);
00256
00257
00258 if (ndims1 != ndims2 &&
00259 ndims1 != ndims2 - 1 &&
00260 ndims1 != ndims2 + 1)
00261 ereport(ERROR,
00262 (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
00263 errmsg("cannot concatenate incompatible arrays"),
00264 errdetail("Arrays of %d and %d dimensions are not "
00265 "compatible for concatenation.",
00266 ndims1, ndims2)));
00267
00268
00269 lbs1 = ARR_LBOUND(v1);
00270 lbs2 = ARR_LBOUND(v2);
00271 dims1 = ARR_DIMS(v1);
00272 dims2 = ARR_DIMS(v2);
00273 dat1 = ARR_DATA_PTR(v1);
00274 dat2 = ARR_DATA_PTR(v2);
00275 bitmap1 = ARR_NULLBITMAP(v1);
00276 bitmap2 = ARR_NULLBITMAP(v2);
00277 nitems1 = ArrayGetNItems(ndims1, dims1);
00278 nitems2 = ArrayGetNItems(ndims2, dims2);
00279 ndatabytes1 = ARR_SIZE(v1) - ARR_DATA_OFFSET(v1);
00280 ndatabytes2 = ARR_SIZE(v2) - ARR_DATA_OFFSET(v2);
00281
00282 if (ndims1 == ndims2)
00283 {
00284
00285
00286
00287
00288 ndims = ndims1;
00289 dims = (int *) palloc(ndims * sizeof(int));
00290 lbs = (int *) palloc(ndims * sizeof(int));
00291
00292 dims[0] = dims1[0] + dims2[0];
00293 lbs[0] = lbs1[0];
00294
00295 for (i = 1; i < ndims; i++)
00296 {
00297 if (dims1[i] != dims2[i] || lbs1[i] != lbs2[i])
00298 ereport(ERROR,
00299 (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
00300 errmsg("cannot concatenate incompatible arrays"),
00301 errdetail("Arrays with differing element dimensions are "
00302 "not compatible for concatenation.")));
00303
00304 dims[i] = dims1[i];
00305 lbs[i] = lbs1[i];
00306 }
00307 }
00308 else if (ndims1 == ndims2 - 1)
00309 {
00310
00311
00312
00313
00314 ndims = ndims2;
00315 dims = (int *) palloc(ndims * sizeof(int));
00316 lbs = (int *) palloc(ndims * sizeof(int));
00317 memcpy(dims, dims2, ndims * sizeof(int));
00318 memcpy(lbs, lbs2, ndims * sizeof(int));
00319
00320
00321 dims[0] += 1;
00322
00323
00324 for (i = 0; i < ndims1; i++)
00325 {
00326 if (dims1[i] != dims[i + 1] || lbs1[i] != lbs[i + 1])
00327 ereport(ERROR,
00328 (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
00329 errmsg("cannot concatenate incompatible arrays"),
00330 errdetail("Arrays with differing dimensions are not "
00331 "compatible for concatenation.")));
00332 }
00333 }
00334 else
00335 {
00336
00337
00338
00339
00340
00341
00342 ndims = ndims1;
00343 dims = (int *) palloc(ndims * sizeof(int));
00344 lbs = (int *) palloc(ndims * sizeof(int));
00345 memcpy(dims, dims1, ndims * sizeof(int));
00346 memcpy(lbs, lbs1, ndims * sizeof(int));
00347
00348
00349 dims[0] += 1;
00350
00351
00352 for (i = 0; i < ndims2; i++)
00353 {
00354 if (dims2[i] != dims[i + 1] || lbs2[i] != lbs[i + 1])
00355 ereport(ERROR,
00356 (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
00357 errmsg("cannot concatenate incompatible arrays"),
00358 errdetail("Arrays with differing dimensions are not "
00359 "compatible for concatenation.")));
00360 }
00361 }
00362
00363
00364 nitems = ArrayGetNItems(ndims, dims);
00365
00366
00367 ndatabytes = ndatabytes1 + ndatabytes2;
00368 if (ARR_HASNULL(v1) || ARR_HASNULL(v2))
00369 {
00370 dataoffset = ARR_OVERHEAD_WITHNULLS(ndims, nitems);
00371 nbytes = ndatabytes + dataoffset;
00372 }
00373 else
00374 {
00375 dataoffset = 0;
00376 nbytes = ndatabytes + ARR_OVERHEAD_NONULLS(ndims);
00377 }
00378 result = (ArrayType *) palloc0(nbytes);
00379 SET_VARSIZE(result, nbytes);
00380 result->ndim = ndims;
00381 result->dataoffset = dataoffset;
00382 result->elemtype = element_type;
00383 memcpy(ARR_DIMS(result), dims, ndims * sizeof(int));
00384 memcpy(ARR_LBOUND(result), lbs, ndims * sizeof(int));
00385
00386 memcpy(ARR_DATA_PTR(result), dat1, ndatabytes1);
00387 memcpy(ARR_DATA_PTR(result) + ndatabytes1, dat2, ndatabytes2);
00388
00389 if (ARR_HASNULL(result))
00390 {
00391 array_bitmap_copy(ARR_NULLBITMAP(result), 0,
00392 bitmap1, 0,
00393 nitems1);
00394 array_bitmap_copy(ARR_NULLBITMAP(result), nitems1,
00395 bitmap2, 0,
00396 nitems2);
00397 }
00398
00399 PG_RETURN_ARRAYTYPE_P(result);
00400 }
00401
00402
00403
00404
00405
00406 ArrayType *
00407 create_singleton_array(FunctionCallInfo fcinfo,
00408 Oid element_type,
00409 Datum element,
00410 bool isNull,
00411 int ndims)
00412 {
00413 Datum dvalues[1];
00414 bool nulls[1];
00415 int16 typlen;
00416 bool typbyval;
00417 char typalign;
00418 int dims[MAXDIM];
00419 int lbs[MAXDIM];
00420 int i;
00421 ArrayMetaState *my_extra;
00422
00423 if (ndims < 1)
00424 ereport(ERROR,
00425 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
00426 errmsg("invalid number of dimensions: %d", ndims)));
00427 if (ndims > MAXDIM)
00428 ereport(ERROR,
00429 (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
00430 errmsg("number of array dimensions (%d) exceeds the maximum allowed (%d)",
00431 ndims, MAXDIM)));
00432
00433 dvalues[0] = element;
00434 nulls[0] = isNull;
00435
00436 for (i = 0; i < ndims; i++)
00437 {
00438 dims[i] = 1;
00439 lbs[i] = 1;
00440 }
00441
00442
00443
00444
00445
00446 my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
00447 if (my_extra == NULL)
00448 {
00449 fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
00450 sizeof(ArrayMetaState));
00451 my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
00452 my_extra->element_type = ~element_type;
00453 }
00454
00455 if (my_extra->element_type != element_type)
00456 {
00457
00458 get_typlenbyvalalign(element_type,
00459 &my_extra->typlen,
00460 &my_extra->typbyval,
00461 &my_extra->typalign);
00462 my_extra->element_type = element_type;
00463 }
00464 typlen = my_extra->typlen;
00465 typbyval = my_extra->typbyval;
00466 typalign = my_extra->typalign;
00467
00468 return construct_md_array(dvalues, nulls, ndims, dims, lbs, element_type,
00469 typlen, typbyval, typalign);
00470 }
00471
00472
00473
00474
00475
00476 Datum
00477 array_agg_transfn(PG_FUNCTION_ARGS)
00478 {
00479 Oid arg1_typeid = get_fn_expr_argtype(fcinfo->flinfo, 1);
00480 MemoryContext aggcontext;
00481 ArrayBuildState *state;
00482 Datum elem;
00483
00484 if (arg1_typeid == InvalidOid)
00485 ereport(ERROR,
00486 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
00487 errmsg("could not determine input data type")));
00488
00489 if (!AggCheckCallContext(fcinfo, &aggcontext))
00490 {
00491
00492 elog(ERROR, "array_agg_transfn called in non-aggregate context");
00493 }
00494
00495 state = PG_ARGISNULL(0) ? NULL : (ArrayBuildState *) PG_GETARG_POINTER(0);
00496 elem = PG_ARGISNULL(1) ? (Datum) 0 : PG_GETARG_DATUM(1);
00497 state = accumArrayResult(state,
00498 elem,
00499 PG_ARGISNULL(1),
00500 arg1_typeid,
00501 aggcontext);
00502
00503
00504
00505
00506
00507
00508 PG_RETURN_POINTER(state);
00509 }
00510
00511 Datum
00512 array_agg_finalfn(PG_FUNCTION_ARGS)
00513 {
00514 Datum result;
00515 ArrayBuildState *state;
00516 int dims[1];
00517 int lbs[1];
00518
00519
00520
00521
00522
00523
00524 if (PG_ARGISNULL(0))
00525 PG_RETURN_NULL();
00526
00527
00528 Assert(AggCheckCallContext(fcinfo, NULL));
00529
00530 state = (ArrayBuildState *) PG_GETARG_POINTER(0);
00531
00532 dims[0] = state->nelems;
00533 lbs[0] = 1;
00534
00535
00536
00537
00538
00539
00540
00541 result = makeMdArrayResult(state, 1, dims, lbs,
00542 CurrentMemoryContext,
00543 false);
00544
00545 PG_RETURN_DATUM(result);
00546 }