static float * getWeights(ArrayType *win) { static float ws[lengthof(weights)]; int i; float4 *arrdata; if (win == NULL) return weights; if (ARR_NDIM(win) != 1) ereport(ERROR, (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR), errmsg("array of weight must be one-dimensional"))); if (ArrayGetNItems(ARR_NDIM(win), ARR_DIMS(win)) < lengthof(weights)) ereport(ERROR, (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR), errmsg("array of weight is too short"))); if (array_contains_nulls(win)) ereport(ERROR, (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED), errmsg("array of weight must not contain nulls"))); arrdata = (float4 *) ARR_DATA_PTR(win); for (i = 0; i < lengthof(weights); i++) { ws[i] = (arrdata[i] >= 0) ? arrdata[i] : weights[i]; if (ws[i] > 1.0) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("weight out of range"))); } return ws; }
/*------------------------------------------------------------------------ * _ReadChunkArray1El -- * returns one element of the chunked array as specified by the index "st" * the chunked file descriptor is "fp" *------------------------------------------------------------------------- */ struct varlena * _ReadChunkArray1El(int st[], int bsize, int fp, ArrayType *array, bool *isNull) { int i, j, n, temp, srcOff; int chunk_st[MAXDIM]; int *C, csize, *dim, *lb; int PCHUNK[MAXDIM], PC[MAXDIM]; CHUNK_INFO *A = (CHUNK_INFO *) ARR_DATA_PTR(array); n = ARR_NDIM(array); lb = ARR_LBOUND(array); C = A->C; dim = ARR_DIMS(array); csize = C[n-1]; PC[n-1] = 1; temp = dim[n - 1]/C[n-1]; for (i = n-2; i >= 0; i--){ PC[i] = PC[i+1] * temp; temp = dim[i] / C[i]; csize *= C[i]; } for (i = 0; i < n; st[i] -= lb[i], i++); mda_get_prod(n, C, PCHUNK); array2chunk_coord(n, C, st, chunk_st); for (i = j = 0; i < n; i++) j+= chunk_st[i]*PC[i]; srcOff = j * csize; for(i = 0; i < n; i++) srcOff += (st[i]-chunk_st[i]*C[i])*PCHUNK[i]; srcOff *= bsize; if (lo_lseek(fp, srcOff, SEEK_SET) < 0) RETURN_NULL; #ifdef LOARRAY return (struct varlena *) LOread(fp, bsize); #endif return (struct varlena *) 0; }
/* logic copied from utils/adt/float.c */ static inline float8 * check_float8_array(ArrayType *transarray, int nitems) { /* * We expect the input to be an N-element float array; verify that. We * don't need to use deconstruct_array() since the array data is just * going to look like a C array of N float8 values. */ if (ARR_NDIM(transarray) != 1 || ARR_DIMS(transarray)[0] != nitems || ARR_HASNULL(transarray) || ARR_ELEMTYPE(transarray) != FLOAT8OID) elog(ERROR, "%d-elements float8 array is expected", nitems); return (float8 *) ARR_DATA_PTR(transarray); }
Datum rank(PG_FUNCTION_ARGS) { ArrayType *win = (ArrayType *) PG_DETOAST_DATUM(PG_GETARG_DATUM(0)); tsvector *txt = (tsvector *) PG_DETOAST_DATUM(PG_GETARG_DATUM(1)); QUERYTYPE *query = (QUERYTYPE *) PG_DETOAST_DATUM(PG_GETARG_DATUM(2)); int method = DEF_NORM_METHOD; float res = 0.0; float ws[lengthof(weights)]; float4 *arrdata; int i; if (ARR_NDIM(win) != 1) ereport(ERROR, (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR), errmsg("array of weight must be one-dimensional"))); if (ARRNELEMS(win) < lengthof(weights)) ereport(ERROR, (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR), errmsg("array of weight is too short"))); if (ARR_HASNULL(win)) ereport(ERROR, (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED), errmsg("array of weight must not contain nulls"))); arrdata = (float4 *) ARR_DATA_PTR(win); for (i = 0; i < lengthof(weights); i++) { ws[i] = (arrdata[i] >= 0) ? arrdata[i] : weights[i]; if (ws[i] > 1.0) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("weight out of range"))); } if (PG_NARGS() == 4) method = PG_GETARG_INT32(3); res = calc_rank(ws, txt, query, method); PG_FREE_IF_COPY(win, 0); PG_FREE_IF_COPY(txt, 1); PG_FREE_IF_COPY(query, 2); PG_RETURN_FLOAT4(res); }
/* * Extract len and pointer to buffer from an int16[] (vector) Datum * representing a PostgreSQL INT2OID type. */ static void extract_INT2OID_array(Datum array_datum, int *lenp, int16 **vecp) { ArrayType *array_type; Assert(lenp != NULL); Assert(vecp != NULL); array_type = DatumGetArrayTypeP(array_datum); Assert(ARR_NDIM(array_type) == 1); Assert(ARR_ELEMTYPE(array_type) == INT2OID); Assert(ARR_LBOUND(array_type)[0] == 1); *lenp = ARR_DIMS(array_type)[0]; *vecp = (int16 *) ARR_DATA_PTR(array_type); return; }
/* * jsonb_set: * Replace/create value of jsonb key or jsonb element, which can be found by the specified path. * Path must be replesented as an array of key names or indexes. If indexes will be used, * the same rules implied as for jsonb_delete_idx (negative indexing and edge cases) */ Datum jsonb_set(PG_FUNCTION_ARGS) { Jsonb *in = PG_GETARG_JSONB(0); ArrayType *path = PG_GETARG_ARRAYTYPE_P(1); Jsonb *newval = PG_GETARG_JSONB(2); bool create = PG_GETARG_BOOL(3); JsonbValue *res = NULL; Datum *path_elems; bool *path_nulls; int path_len; JsonbIterator *it; JsonbParseState *st = NULL; if (ARR_NDIM(path) > 1) ereport(ERROR, (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR), errmsg("wrong number of array subscripts"))); if (JB_ROOT_IS_SCALAR(in)) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("cannot set path in scalar"))); if (JB_ROOT_COUNT(in) == 0 && !create) { PG_RETURN_JSONB(in); } deconstruct_array(path, TEXTOID, -1, false, 'i', &path_elems, &path_nulls, &path_len); if (path_len == 0) { PG_RETURN_JSONB(in); } it = JsonbIteratorInit(&in->root); res = setPath(&it, path_elems, path_nulls, path_len, &st, 0, newval, create); Assert (res != NULL); PG_RETURN_JSONB(JsonbValueToJsonb(res)); }
/* Create a new int array with room for "num" elements */ ArrayType * new_intArrayType(int num) { ArrayType *r; int nbytes = ARR_OVERHEAD_NONULLS(1) + sizeof(int) * num; r = (ArrayType *) palloc0(nbytes); SET_VARSIZE(r, nbytes); ARR_NDIM(r) = 1; r->dataoffset = 0; /* marker for no null bitmap */ ARR_ELEMTYPE(r) = INT4OID; ARR_DIMS(r)[0] = num; ARR_LBOUND(r)[0] = 1; return r; }
/* * Check that a valid state is passed to the aggregate's final function. * If we return false, the calling function should return NULL. */ static bool float8_mregr_get_state(FunctionCallInfo fcinfo, MRegrState *outState) { ArrayType *in; float8 *data; /* Input should be a single parameter, the aggregate state */ if (PG_NARGS() != 1) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("final calculation function \"%s\" called with invalid parameters", format_procedure(fcinfo->flinfo->fn_oid)))); if (PG_ARGISNULL(0)) return false; /* Validate array type */ in = PG_GETARG_ARRAYTYPE_P(0); if (ARR_ELEMTYPE(in) != FLOAT8OID || ARR_NDIM(in) != 1 || ARR_NULLBITMAP(in)) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("final calculation function \"%s\" called with invalid parameters", format_procedure(fcinfo->flinfo->fn_oid)))); /* Validate the correct size input */ if (ARR_DIMS(in)[0] < 2) return false; /* no input */ data = (float8*) ARR_DATA_PTR(in); outState->len = (int) data[0]; /* scalar: len(X[]) */ if ((uint64) ARR_DIMS(in)[0] != 4ULL + outState->len + outState->len * outState->len) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("final calculation function \"%s\" called with invalid parameters", format_procedure(fcinfo->flinfo->fn_oid)))); outState->count = data[1]; /* scalar: count(*) */ outState->sumy = data[2]; /* scalar: sum(y) */ outState->sumy2 = data[3]; /* scalar: sum(y*y) */ outState->Xty = &data[4]; /* vector: X^t * y */ outState->XtX = &data[4 + outState->len]; /* matrix: X^t * X */ return true; }
/* * int2vectorrecv - converts external binary format to int2_vector_s */ datum_t int2vectorrecv(PG_FUNC_ARGS) { struct string* buf = (struct string*) ARG_POINTER(0); struct fc_info locfcinfo; int2_vector_s *result; /* * Normally one would call array_recv() using DIRECT_FC3, but * that does not work since array_recv wants to cache some data using * fcinfo->flinfo->fn_extra. So we need to pass it our own flinfo * parameter. */ INIT_FC_INFO(locfcinfo, fcinfo->flinfo, 3, INVALID_OID, NULL, NULL); locfcinfo.arg[0] = PTR_TO_D(buf); locfcinfo.arg[1] = OID_TO_D(INT2OID); locfcinfo.arg[2] = INT32_TO_D(-1); locfcinfo.argnull[0] = false; locfcinfo.argnull[1] = false; locfcinfo.argnull[2] = false; result = (int2_vector_s *) D_TO_PTR(array_recv(&locfcinfo)); ASSERT(!locfcinfo.isnull); /* sanity checks: int2_vector_s must be 1-D, 0-based, no nulls */ if (ARR_NDIM(result) != 1 || ARR_HASNULL(result) || ARR_ELEMTYPE(result) != INT2OID || ARR_LBOUND(result)[0] != 0) { ereport(ERROR, ( errcode(E_INVALID_BINARY_REPRESENTATION), errmsg("invalid int2_vector_s data"))); } /* check length for consistency with int2vectorin() */ if (ARR_DIMS(result)[0] > FUNC_MAX_ARGS) { ereport(ERROR, ( errcode(E_INVALID_PARAMETER_VALUE), errmsg("oidvector has too many elements"))); } RET_POINTER(result); }
/* * Turn an array into JSON. */ static void array_to_jsonb_internal(Datum array, JsonbInState *result) { ArrayType *v = DatumGetArrayTypeP(array); Oid element_type = ARR_ELEMTYPE(v); int *dim; int ndim; int nitems; int count = 0; Datum *elements; bool *nulls; int16 typlen; bool typbyval; char typalign; JsonbTypeCategory tcategory; Oid outfuncoid; ndim = ARR_NDIM(v); dim = ARR_DIMS(v); nitems = ArrayGetNItems(ndim, dim); if (nitems <= 0) { result->res = pushJsonbValue(&result->parseState, WJB_BEGIN_ARRAY, NULL); result->res = pushJsonbValue(&result->parseState, WJB_END_ARRAY, NULL); return; } get_typlenbyvalalign(element_type, &typlen, &typbyval, &typalign); jsonb_categorize_type(element_type, &tcategory, &outfuncoid); deconstruct_array(v, element_type, typlen, typbyval, typalign, &elements, &nulls, &nitems); array_dim_to_jsonb(result, 0, ndim, dim, elements, nulls, &count, tcategory, outfuncoid); pfree(elements); pfree(nulls); }
/* * int2vectorrecv - converts external binary format to int2vector */ Datum int2vectorrecv(PG_FUNCTION_ARGS) { StringInfo buf = (StringInfo) PG_GETARG_POINTER(0); FunctionCallInfoData locfcinfo; int2vector *result; /* * Normally one would call array_recv() using DirectFunctionCall3, but * that does not work since array_recv wants to cache some data using * fcinfo->flinfo->fn_extra. So we need to pass it our own flinfo * parameter. */ InitFunctionCallInfoData(locfcinfo, fcinfo->flinfo, 3, InvalidOid, NULL, NULL); locfcinfo.arg[0] = PointerGetDatum(buf); locfcinfo.arg[1] = ObjectIdGetDatum(INT2OID); locfcinfo.arg[2] = Int32GetDatum(-1); locfcinfo.argnull[0] = false; locfcinfo.argnull[1] = false; locfcinfo.argnull[2] = false; result = (int2vector *) DatumGetPointer(array_recv(&locfcinfo)); Assert(!locfcinfo.isnull); /* sanity checks: int2vector must be 1-D, 0-based, no nulls */ if (ARR_NDIM(result) != 1 || ARR_HASNULL(result) || ARR_ELEMTYPE(result) != INT2OID || ARR_LBOUND(result)[0] != 0) ereport(ERROR, (errcode(ERRCODE_INVALID_BINARY_REPRESENTATION), errmsg("invalid int2vector data"))); /* check length for consistency with int2vectorin() */ if (ARR_DIMS(result)[0] > FUNC_MAX_ARGS) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("oidvector has too many elements"))); PG_RETURN_POINTER(result); }
Datum hash_array( PG_FUNCTION_ARGS) { ArrayType *state = PG_GETARG_ARRAYTYPE_P(0); int dimstate = ARR_NDIM(state); int *dimsstate = ARR_DIMS(state); int numstate = ArrayGetNItems(dimstate,dimsstate); int32 *vals_state=(int32 *)ARR_DATA_PTR(state); unsigned long hash = 65599; unsigned short c; int i = 0; for (;i<numstate;i++) { c = vals_state[i]; hash = c + (hash << 7) + (hash << 16) - hash; } PG_RETURN_INT32(hash); }
ArrayType * resize_intArrayType(ArrayType *a, int num) { int nbytes = ARR_DATA_OFFSET(a) + sizeof(int) * num; int i; if (num == ARRNELEMS(a)) return a; a = (ArrayType *) repalloc(a, nbytes); SET_VARSIZE(a, nbytes); /* usually the array should be 1-D already, but just in case ... */ for (i = 0; i < ARR_NDIM(a); i++) { ARR_DIMS(a)[i] = num; num = 1; } return a; }
Datum internal_kmeans_canopy_transition(PG_FUNCTION_ARGS) { ArrayType *canopies_arr; Datum *canopies; int num_canopies; SvecType *point; PGFunction metric_fn; float8 threshold; MemoryContext mem_context_for_function_calls; canopies_arr = PG_GETARG_ARRAYTYPE_P(verify_arg_nonnull(fcinfo, 0)); get_svec_array_elms(canopies_arr, &canopies, &num_canopies); point = PG_GETARG_SVECTYPE_P(verify_arg_nonnull(fcinfo, 1)); metric_fn = get_metric_fn(PG_GETARG_INT32(verify_arg_nonnull(fcinfo, 2))); threshold = PG_GETARG_FLOAT8(verify_arg_nonnull(fcinfo, 3)); mem_context_for_function_calls = setup_mem_context_for_functional_calls(); for (int i = 0; i < num_canopies; i++) { if (compute_metric(metric_fn, mem_context_for_function_calls, PointerGetDatum(point), canopies[i]) < threshold) PG_RETURN_ARRAYTYPE_P(canopies_arr); } MemoryContextDelete(mem_context_for_function_calls); int idx = (ARR_NDIM(canopies_arr) == 0) ? 1 : ARR_LBOUND(canopies_arr)[0] + ARR_DIMS(canopies_arr)[0]; return PointerGetDatum( array_set( canopies_arr, /* array: the initial array object (mustn't be NULL) */ 1, /* nSubscripts: number of subscripts supplied */ &idx, /* indx[]: the subscript values */ PointerGetDatum(point), /* dataValue: the datum to be inserted at the given position */ false, /* isNull: whether dataValue is NULL */ -1, /* arraytyplen: pg_type.typlen for the array type */ -1, /* elmlen: pg_type.typlen for the array's element type */ false, /* elmbyval: pg_type.typbyval for the array's element type */ 'd') /* elmalign: pg_type.typalign for the array's element type */ ); }
Datum plr_array_push(PG_FUNCTION_ARGS) { ArrayType *v; Datum newelem; int *dimv, *lb, ub; ArrayType *result; int indx; Oid element_type; int16 typlen; bool typbyval; char typalign; v = PG_GETARG_ARRAYTYPE_P(0); newelem = PG_GETARG_DATUM(1); /* Sanity check: do we have a one-dimensional array */ if (ARR_NDIM(v) != 1) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("input must be one-dimensional array"))); lb = ARR_LBOUND(v); dimv = ARR_DIMS(v); ub = dimv[0] + lb[0] - 1; indx = ub + 1; element_type = ARR_ELEMTYPE(v); /* Sanity check: do we have a non-zero element type */ if (element_type == 0) /* internal error */ elog(ERROR, "invalid array element type"); get_typlenbyvalalign(element_type, &typlen, &typbyval, &typalign); result = array_set(v, 1, &indx, newelem, FALSE, -1, typlen, typbyval, typalign); PG_RETURN_ARRAYTYPE_P(result); }
/* * Decode text[] to an array of C strings. * * We could avoid a bit of overhead here if we were willing to duplicate some * of the logic from deconstruct_array, but it doesn't seem worth the code * complexity. */ static int DecodeTextArrayToCString(Datum array, char ***cstringp) { ArrayType *arr = DatumGetArrayTypeP(array); Datum *elems; char **cstring; int i; int nelems; if (ARR_NDIM(arr) != 1 || ARR_HASNULL(arr) || ARR_ELEMTYPE(arr) != TEXTOID) elog(ERROR, "expected 1-D text array"); deconstruct_array(arr, TEXTOID, -1, false, 'i', &elems, NULL, &nelems); cstring = palloc(nelems * sizeof(char *)); for (i = 0; i < nelems; ++i) cstring[i] = TextDatumGetCString(elems[i]); pfree(elems); *cstringp = cstring; return nelems; }
/* * get_func_trftypes * * Returns a number of transformated types used by function. */ int get_func_trftypes(HeapTuple procTup, Oid **p_trftypes) { Datum protrftypes; ArrayType *arr; int nelems; bool isNull; protrftypes = SysCacheGetAttr(PROCOID, procTup, Anum_pg_proc_protrftypes, &isNull); if (!isNull) { /* * We expect the arrays to be 1-D arrays of the right types; verify * that. For the OID and char arrays, we don't need to use * deconstruct_array() since the array data is just going to look like * a C array of values. */ arr = DatumGetArrayTypeP(protrftypes); /* ensure not toasted */ nelems = ARR_DIMS(arr)[0]; if (ARR_NDIM(arr) != 1 || nelems < 0 || ARR_HASNULL(arr) || ARR_ELEMTYPE(arr) != OIDOID) elog(ERROR, "protrftypes is not a 1-D Oid array"); Assert(nelems >= ((Form_pg_proc) GETSTRUCT(procTup))->pronargs); *p_trftypes = (Oid *) palloc(nelems * sizeof(Oid)); memcpy(*p_trftypes, ARR_DATA_PTR(arr), nelems * sizeof(Oid)); return nelems; } else return 0; }
Datum ginarrayconsistent(PG_FUNCTION_ARGS) { bool *check = (bool *) PG_GETARG_POINTER(0); StrategyNumber strategy = PG_GETARG_UINT16(1); ArrayType *query = PG_GETARG_ARRAYTYPE_P(2); int res, i, nentries; /* ARRAYCHECK was already done by previous ginarrayextract call */ switch (strategy) { case GinOverlapStrategy: case GinContainedStrategy: /* at least one element in check[] is true, so result = true */ res = TRUE; break; case GinContainsStrategy: case GinEqualStrategy: nentries = ArrayGetNItems(ARR_NDIM(query), ARR_DIMS(query)); res = TRUE; for (i = 0; i < nentries; i++) if (!check[i]) { res = FALSE; break; } break; default: elog(ERROR, "ginarrayconsistent: unknown strategy number: %d", strategy); res = FALSE; } PG_RETURN_BOOL(res); }
/* * ArrayGetIntegerTypmods: verify that argument is a 1-D cstring array, * and get the contents converted to integers. Returns a palloc'd array * and places the length at *n. */ int32 * ArrayGetIntegerTypmods(ArrayType *arr, int *n) { int32 *result; Datum *elem_values; int i; if (ARR_ELEMTYPE(arr) != CSTRINGOID) ereport(ERROR, (errcode(ERRCODE_ARRAY_ELEMENT_ERROR), errmsg("typmod array must be type cstring[]"))); if (ARR_NDIM(arr) != 1) ereport(ERROR, (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR), errmsg("typmod array must be one-dimensional"))); if (ARR_HASNULL(arr)) ereport(ERROR, (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED), errmsg("typmod array must not contain nulls"))); /* hardwired knowledge about cstring's representation details here */ deconstruct_array(arr, CSTRINGOID, -2, false, 'c', &elem_values, NULL, n); result = (int32 *) palloc(*n * sizeof(int32)); for (i = 0; i < *n; i++) result[i] = pg_atoi(DatumGetCString(elem_values[i]), sizeof(int32), '\0'); pfree(elem_values); return result; }
Datum aggr_InfoGain(PG_FUNCTION_ARGS) { ArrayType *state = PG_GETARG_ARRAYTYPE_P(0); int dimstate = ARR_NDIM(state); int *dimsstate = ARR_DIMS(state); int numstate = ArrayGetNItems(dimstate,dimsstate); float8 *vals_state=(float8 *)ARR_DATA_PTR(state); float8 truevalue = PG_GETARG_FLOAT8(1); float8 trueweight = PG_GETARG_FLOAT8(2); int32 posclasses = PG_GETARG_INT32(3); int32 trueclass = PG_GETARG_INT32(5); ArrayType *pgarray; vals_state[0] += trueweight; vals_state[trueclass] += trueweight; vals_state[(int)(truevalue*(posclasses+1))] += trueweight; vals_state[(int)(truevalue*(posclasses+1) + trueclass)] += trueweight; pgarray = construct_array((Datum *)vals_state, numstate,FLOAT8OID, sizeof(float8),true,'d'); PG_RETURN_ARRAYTYPE_P(pgarray); }
/*----------------------------------------------------------------------------- * array_push : * push an element onto either end of a one-dimensional array *---------------------------------------------------------------------------- */ Datum array_push(PG_FUNCTION_ARGS) { ArrayType *v; Datum newelem; bool isNull; int *dimv, *lb; ArrayType *result; int indx; Oid element_type; int16 typlen; bool typbyval; char typalign; Oid arg0_typeid = get_fn_expr_argtype(fcinfo->flinfo, 0); Oid arg1_typeid = get_fn_expr_argtype(fcinfo->flinfo, 1); Oid arg0_elemid; Oid arg1_elemid; ArrayMetaState *my_extra; if (arg0_typeid == InvalidOid || arg1_typeid == InvalidOid) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("could not determine input data types"))); arg0_elemid = get_element_type(arg0_typeid); arg1_elemid = get_element_type(arg1_typeid); if (arg0_elemid != InvalidOid) { if (PG_ARGISNULL(0)) v = construct_empty_array(arg0_elemid); else v = PG_GETARG_ARRAYTYPE_P(0); isNull = PG_ARGISNULL(1); if (isNull) newelem = (Datum) 0; else newelem = PG_GETARG_DATUM(1); } else if (arg1_elemid != InvalidOid) { if (PG_ARGISNULL(1)) v = construct_empty_array(arg1_elemid); else v = PG_GETARG_ARRAYTYPE_P(1); isNull = PG_ARGISNULL(0); if (isNull) newelem = (Datum) 0; else newelem = PG_GETARG_DATUM(0); } else { /* Shouldn't get here given proper type checking in parser */ ereport(ERROR, (errcode(ERRCODE_DATATYPE_MISMATCH), errmsg("neither input type is an array"))); PG_RETURN_NULL(); /* keep compiler quiet */ } element_type = ARR_ELEMTYPE(v); if (ARR_NDIM(v) == 1) { lb = ARR_LBOUND(v); dimv = ARR_DIMS(v); if (arg0_elemid != InvalidOid) { /* append newelem */ int ub = dimv[0] + lb[0] - 1; indx = ub + 1; /* overflow? */ if (indx < ub) ereport(ERROR, (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), errmsg("integer out of range"))); } else { /* prepend newelem */ indx = lb[0] - 1; /* overflow? */ if (indx > lb[0]) ereport(ERROR, (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), errmsg("integer out of range"))); } } else if (ARR_NDIM(v) == 0) indx = 1; else ereport(ERROR, (errcode(ERRCODE_DATA_EXCEPTION), errmsg("argument must be empty or one-dimensional array"))); /* * 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 = ~element_type; } if (my_extra->element_type != element_type) { /* Get info about element type */ get_typlenbyvalalign(element_type, &my_extra->typlen, &my_extra->typbyval, &my_extra->typalign); my_extra->element_type = element_type; } typlen = my_extra->typlen; typbyval = my_extra->typbyval; typalign = my_extra->typalign; result = array_set(v, 1, &indx, newelem, isNull, -1, typlen, typbyval, typalign); /* * Readjust result's LB to match the input's. This does nothing in the * append case, but it's the simplest way to implement the prepend case. */ if (ARR_NDIM(v) == 1) ARR_LBOUND(result)[0] = ARR_LBOUND(v)[0]; PG_RETURN_ARRAYTYPE_P(result); }
/*----------------------------------------------------------------------------- * array_cat : * concatenate two nD arrays to form an nD array, or * push an (n-1)D array onto the end of an nD array *---------------------------------------------------------------------------- */ Datum array_cat(PG_FUNCTION_ARGS) { ArrayType *v1, *v2; ArrayType *result; int *dims, *lbs, ndims, nitems, ndatabytes, nbytes; int *dims1, *lbs1, ndims1, nitems1, ndatabytes1; int *dims2, *lbs2, ndims2, nitems2, ndatabytes2; int i; char *dat1, *dat2; bits8 *bitmap1, *bitmap2; Oid element_type; Oid element_type1; Oid element_type2; int32 dataoffset; /* Concatenating a null array is a no-op, just return the other input */ if (PG_ARGISNULL(0)) { if (PG_ARGISNULL(1)) PG_RETURN_NULL(); result = PG_GETARG_ARRAYTYPE_P(1); PG_RETURN_ARRAYTYPE_P(result); } if (PG_ARGISNULL(1)) { result = PG_GETARG_ARRAYTYPE_P(0); PG_RETURN_ARRAYTYPE_P(result); } v1 = PG_GETARG_ARRAYTYPE_P(0); v2 = PG_GETARG_ARRAYTYPE_P(1); element_type1 = ARR_ELEMTYPE(v1); element_type2 = ARR_ELEMTYPE(v2); /* Check we have matching element types */ if (element_type1 != element_type2) ereport(ERROR, (errcode(ERRCODE_DATATYPE_MISMATCH), errmsg("cannot concatenate incompatible arrays"), errdetail("Arrays with element types %s and %s are not " "compatible for concatenation.", format_type_be(element_type1), format_type_be(element_type2)))); /* OK, use it */ element_type = element_type1; /*---------- * We must have one of the following combinations of inputs: * 1) one empty array, and one non-empty array * 2) both arrays empty * 3) two arrays with ndims1 == ndims2 * 4) ndims1 == ndims2 - 1 * 5) ndims1 == ndims2 + 1 *---------- */ ndims1 = ARR_NDIM(v1); ndims2 = ARR_NDIM(v2); /* * short circuit - if one input array is empty, and the other is not, we * return the non-empty one as the result * * if both are empty, return the first one */ if (ndims1 == 0 && ndims2 > 0) PG_RETURN_ARRAYTYPE_P(v2); if (ndims2 == 0) PG_RETURN_ARRAYTYPE_P(v1); /* the rest fall under rule 3, 4, or 5 */ if (ndims1 != ndims2 && ndims1 != ndims2 - 1 && ndims1 != ndims2 + 1) ereport(ERROR, (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR), errmsg("cannot concatenate incompatible arrays"), errdetail("Arrays of %d and %d dimensions are not " "compatible for concatenation.", ndims1, ndims2))); /* get argument array details */ lbs1 = ARR_LBOUND(v1); lbs2 = ARR_LBOUND(v2); dims1 = ARR_DIMS(v1); dims2 = ARR_DIMS(v2); dat1 = ARR_DATA_PTR(v1); dat2 = ARR_DATA_PTR(v2); bitmap1 = ARR_NULLBITMAP(v1); bitmap2 = ARR_NULLBITMAP(v2); nitems1 = ArrayGetNItems(ndims1, dims1); nitems2 = ArrayGetNItems(ndims2, dims2); ndatabytes1 = ARR_SIZE(v1) - ARR_DATA_OFFSET(v1); ndatabytes2 = ARR_SIZE(v2) - ARR_DATA_OFFSET(v2); if (ndims1 == ndims2) { /* * resulting array is made up of the elements (possibly arrays * themselves) of the input argument arrays */ ndims = ndims1; dims = (int *) palloc(ndims * sizeof(int)); lbs = (int *) palloc(ndims * sizeof(int)); dims[0] = dims1[0] + dims2[0]; lbs[0] = lbs1[0]; for (i = 1; i < ndims; i++) { if (dims1[i] != dims2[i] || lbs1[i] != lbs2[i]) ereport(ERROR, (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR), errmsg("cannot concatenate incompatible arrays"), errdetail("Arrays with differing element dimensions are " "not compatible for concatenation."))); dims[i] = dims1[i]; lbs[i] = lbs1[i]; } } else if (ndims1 == ndims2 - 1) { /* * resulting array has the second argument as the outer array, with * the first argument inserted at the front of the outer dimension */ ndims = ndims2; dims = (int *) palloc(ndims * sizeof(int)); lbs = (int *) palloc(ndims * sizeof(int)); memcpy(dims, dims2, ndims * sizeof(int)); memcpy(lbs, lbs2, ndims * sizeof(int)); /* increment number of elements in outer array */ dims[0] += 1; /* make sure the added element matches our existing elements */ for (i = 0; i < ndims1; i++) { if (dims1[i] != dims[i + 1] || lbs1[i] != lbs[i + 1]) ereport(ERROR, (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR), errmsg("cannot concatenate incompatible arrays"), errdetail("Arrays with differing dimensions are not " "compatible for concatenation."))); } } else { /* * (ndims1 == ndims2 + 1) * * resulting array has the first argument as the outer array, with the * second argument appended to the end of the outer dimension */ ndims = ndims1; dims = (int *) palloc(ndims * sizeof(int)); lbs = (int *) palloc(ndims * sizeof(int)); memcpy(dims, dims1, ndims * sizeof(int)); memcpy(lbs, lbs1, ndims * sizeof(int)); /* increment number of elements in outer array */ dims[0] += 1; /* make sure the added element matches our existing elements */ for (i = 0; i < ndims2; i++) { if (dims2[i] != dims[i + 1] || lbs2[i] != lbs[i + 1]) ereport(ERROR, (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR), errmsg("cannot concatenate incompatible arrays"), errdetail("Arrays with differing dimensions are not " "compatible for concatenation."))); } } /* Do this mainly for overflow checking */ nitems = ArrayGetNItems(ndims, dims); /* build the result array */ ndatabytes = ndatabytes1 + ndatabytes2; if (ARR_HASNULL(v1) || ARR_HASNULL(v2)) { dataoffset = ARR_OVERHEAD_WITHNULLS(ndims, nitems); nbytes = ndatabytes + dataoffset; } else { dataoffset = 0; /* marker for no null bitmap */ nbytes = ndatabytes + ARR_OVERHEAD_NONULLS(ndims); } result = (ArrayType *) palloc(nbytes); SET_VARSIZE(result, nbytes); result->ndim = ndims; result->dataoffset = dataoffset; result->elemtype = element_type; memcpy(ARR_DIMS(result), dims, ndims * sizeof(int)); memcpy(ARR_LBOUND(result), lbs, ndims * sizeof(int)); /* data area is arg1 then arg2 */ memcpy(ARR_DATA_PTR(result), dat1, ndatabytes1); memcpy(ARR_DATA_PTR(result) + ndatabytes1, dat2, ndatabytes2); /* handle the null bitmap if needed */ if (ARR_HASNULL(result)) { array_bitmap_copy(ARR_NULLBITMAP(result), 0, bitmap1, 0, nitems1); array_bitmap_copy(ARR_NULLBITMAP(result), nitems1, bitmap2, 0, nitems2); } PG_RETURN_ARRAYTYPE_P(result); }
Datum hstore_from_array(PG_FUNCTION_ARGS) { ArrayType *in_array = PG_GETARG_ARRAYTYPE_P(0); int ndims = ARR_NDIM(in_array); int count; int4 buflen; HStore *out; Pairs *pairs; Datum *in_datums; bool *in_nulls; int in_count; int i; Assert(ARR_ELEMTYPE(in_array) == TEXTOID); switch (ndims) { case 0: out = hstorePairs(NULL, 0, 0); PG_RETURN_POINTER(out); case 1: if ((ARR_DIMS(in_array)[0]) % 2) ereport(ERROR, (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR), errmsg("array must have even number of elements"))); break; case 2: if ((ARR_DIMS(in_array)[1]) != 2) ereport(ERROR, (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR), errmsg("array must have two columns"))); break; default: ereport(ERROR, (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR), errmsg("wrong number of array subscripts"))); } deconstruct_array(in_array, TEXTOID, -1, false, 'i', &in_datums, &in_nulls, &in_count); count = in_count / 2; pairs = palloc(count * sizeof(Pairs)); for (i = 0; i < count; ++i) { if (in_nulls[i * 2]) ereport(ERROR, (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED), errmsg("null value not allowed for hstore key"))); if (in_nulls[i * 2 + 1]) { pairs[i].key = VARDATA_ANY(in_datums[i * 2]); pairs[i].val = NULL; pairs[i].keylen = hstoreCheckKeyLen(VARSIZE_ANY_EXHDR(in_datums[i * 2])); pairs[i].vallen = 4; pairs[i].isnull = true; pairs[i].needfree = false; } else { pairs[i].key = VARDATA_ANY(in_datums[i * 2]); pairs[i].val = VARDATA_ANY(in_datums[i * 2 + 1]); pairs[i].keylen = hstoreCheckKeyLen(VARSIZE_ANY_EXHDR(in_datums[i * 2])); pairs[i].vallen = hstoreCheckValLen(VARSIZE_ANY_EXHDR(in_datums[i * 2 + 1])); pairs[i].isnull = false; pairs[i].needfree = false; } } count = hstoreUniquePairs(pairs, count, &buflen); out = hstorePairs(pairs, count, buflen); PG_RETURN_POINTER(out); }
Datum hstore_from_arrays(PG_FUNCTION_ARGS) { int4 buflen; HStore *out; Pairs *pairs; Datum *key_datums; bool *key_nulls; int key_count; Datum *value_datums; bool *value_nulls; int value_count; ArrayType *key_array; ArrayType *value_array; int i; if (PG_ARGISNULL(0)) PG_RETURN_NULL(); key_array = PG_GETARG_ARRAYTYPE_P(0); Assert(ARR_ELEMTYPE(key_array) == TEXTOID); /* * must check >1 rather than != 1 because empty arrays have 0 dimensions, * not 1 */ if (ARR_NDIM(key_array) > 1) ereport(ERROR, (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR), errmsg("wrong number of array subscripts"))); deconstruct_array(key_array, TEXTOID, -1, false, 'i', &key_datums, &key_nulls, &key_count); /* value_array might be NULL */ if (PG_ARGISNULL(1)) { value_array = NULL; value_count = key_count; value_datums = NULL; value_nulls = NULL; } else { value_array = PG_GETARG_ARRAYTYPE_P(1); Assert(ARR_ELEMTYPE(value_array) == TEXTOID); if (ARR_NDIM(value_array) > 1) ereport(ERROR, (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR), errmsg("wrong number of array subscripts"))); if ((ARR_NDIM(key_array) > 0 || ARR_NDIM(value_array) > 0) && (ARR_NDIM(key_array) != ARR_NDIM(value_array) || ARR_DIMS(key_array)[0] != ARR_DIMS(value_array)[0] || ARR_LBOUND(key_array)[0] != ARR_LBOUND(value_array)[0])) ereport(ERROR, (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR), errmsg("arrays must have same bounds"))); deconstruct_array(value_array, TEXTOID, -1, false, 'i', &value_datums, &value_nulls, &value_count); Assert(key_count == value_count); } pairs = palloc(key_count * sizeof(Pairs)); for (i = 0; i < key_count; ++i) { if (key_nulls[i]) ereport(ERROR, (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED), errmsg("null value not allowed for hstore key"))); if (!value_nulls || value_nulls[i]) { pairs[i].key = VARDATA_ANY(key_datums[i]); pairs[i].val = NULL; pairs[i].keylen = hstoreCheckKeyLen(VARSIZE_ANY_EXHDR(key_datums[i])); pairs[i].vallen = 4; pairs[i].isnull = true; pairs[i].needfree = false; } else { pairs[i].key = VARDATA_ANY(key_datums[i]); pairs[i].val = VARDATA_ANY(value_datums[i]); pairs[i].keylen = hstoreCheckKeyLen(VARSIZE_ANY_EXHDR(key_datums[i])); pairs[i].vallen = hstoreCheckValLen(VARSIZE_ANY_EXHDR(value_datums[i])); pairs[i].isnull = false; pairs[i].needfree = false; } } key_count = hstoreUniquePairs(pairs, key_count, &buflen); out = hstorePairs(pairs, key_count, buflen); PG_RETURN_POINTER(out); }
Datum rewrite_accum(PG_FUNCTION_ARGS) { QUERYTYPE *acc = (QUERYTYPE *) PG_GETARG_POINTER(0); ArrayType *qa = (ArrayType *) DatumGetPointer(PG_DETOAST_DATUM_COPY(PG_GETARG_DATUM(1))); QUERYTYPE *q; QTNode *qex, *subs = NULL, *acctree; bool isfind = false; Datum *elemsp; int nelemsp; AggregateContext = ((AggState *) fcinfo->context)->aggcontext; if (acc == NULL || PG_ARGISNULL(0)) { acc = (QUERYTYPE *) MEMALLOC(AggMemory, sizeof(QUERYTYPE)); acc->len = HDRSIZEQT; acc->size = 0; } if (qa == NULL || PG_ARGISNULL(1)) { PG_FREE_IF_COPY(qa, 1); PG_RETURN_POINTER(acc); } if (ARR_NDIM(qa) != 1) elog(ERROR, "array must be one-dimensional, not %d dimension", ARR_NDIM(qa)); if (ArrayGetNItems(ARR_NDIM(qa), ARR_DIMS(qa)) != 3) elog(ERROR, "array should have only three elements"); if (tsqOid == InvalidOid) { SPI_connect(); get_tsq_Oid(); SPI_finish(); } if (ARR_ELEMTYPE(qa) != tsqOid) elog(ERROR, "array should contain tsquery type"); deconstruct_array(qa, tsqOid, -1, false, 'i', &elemsp, NULL, &nelemsp); q = (QUERYTYPE *) DatumGetPointer(elemsp[0]); if (q->size == 0) { pfree(elemsp); PG_RETURN_POINTER(acc); } if (!acc->size) { if (acc->len > HDRSIZEQT) { pfree(elemsp); PG_RETURN_POINTER(acc); } else acctree = QT2QTN(GETQUERY(q), GETOPERAND(q)); } else acctree = QT2QTN(GETQUERY(acc), GETOPERAND(acc)); QTNTernary(acctree); QTNSort(acctree); q = (QUERYTYPE *) DatumGetPointer(elemsp[1]); if (q->size == 0) { pfree(elemsp); PG_RETURN_POINTER(acc); } qex = QT2QTN(GETQUERY(q), GETOPERAND(q)); QTNTernary(qex); QTNSort(qex); q = (QUERYTYPE *) DatumGetPointer(elemsp[2]); if (q->size) subs = QT2QTN(GETQUERY(q), GETOPERAND(q)); acctree = findsubquery(acctree, qex, PlainMemory, subs, &isfind); if (isfind || !acc->size) { /* pfree( acc ); do not pfree(p), because nodeAgg.c will */ if (acctree) { QTNBinary(acctree); acc = QTN2QT(acctree, AggMemory); } else { acc = (QUERYTYPE *) MEMALLOC(AggMemory, HDRSIZEQT * 2); acc->len = HDRSIZEQT * 2; acc->size = 0; } } pfree(elemsp); QTNFree(qex); QTNFree(subs); QTNFree(acctree); PG_RETURN_POINTER(acc); }
/* * Determine whether a relation can be proven functionally dependent on * a set of grouping columns. If so, return TRUE and add the pg_constraint * OIDs of the constraints needed for the proof to the *constraintDeps list. * * grouping_columns is a list of grouping expressions, in which columns of * the rel of interest are Vars with the indicated varno/varlevelsup. * * Currently we only check to see if the rel has a primary key that is a * subset of the grouping_columns. We could also use plain unique constraints * if all their columns are known not null, but there's a problem: we need * to be able to represent the not-null-ness as part of the constraints added * to *constraintDeps. FIXME whenever not-null constraints get represented * in pg_constraint. */ bool check_functional_grouping(Oid relid, Index varno, Index varlevelsup, List *grouping_columns, List **constraintDeps) { bool result = false; Relation pg_constraint; HeapTuple tuple; SysScanDesc scan; ScanKeyData skey[1]; /* Scan pg_constraint for constraints of the target rel */ pg_constraint = heap_open(ConstraintRelationId, AccessShareLock); ScanKeyInit(&skey[0], Anum_pg_constraint_conrelid, BTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(relid)); scan = systable_beginscan(pg_constraint, ConstraintRelidIndexId, true, NULL, 1, skey); while (HeapTupleIsValid(tuple = systable_getnext(scan))) { Form_pg_constraint con = (Form_pg_constraint) GETSTRUCT(tuple); Datum adatum; bool isNull; ArrayType *arr; int16 *attnums; int numkeys; int i; bool found_col; /* Only PK constraints are of interest for now, see comment above */ if (con->contype != CONSTRAINT_PRIMARY) continue; /* Constraint must be non-deferrable */ if (con->condeferrable) continue; /* Extract the conkey array, ie, attnums of PK's columns */ adatum = heap_getattr(tuple, Anum_pg_constraint_conkey, RelationGetDescr(pg_constraint), &isNull); if (isNull) elog(ERROR, "null conkey for constraint %u", HeapTupleGetOid(tuple)); arr = DatumGetArrayTypeP(adatum); /* ensure not toasted */ numkeys = ARR_DIMS(arr)[0]; if (ARR_NDIM(arr) != 1 || numkeys < 0 || ARR_HASNULL(arr) || ARR_ELEMTYPE(arr) != INT2OID) elog(ERROR, "conkey is not a 1-D smallint array"); attnums = (int16 *) ARR_DATA_PTR(arr); found_col = false; for (i = 0; i < numkeys; i++) { AttrNumber attnum = attnums[i]; ListCell *gl; found_col = false; foreach(gl, grouping_columns) { Var *gvar = (Var *) lfirst(gl); if (IsA(gvar, Var) && gvar->varno == varno && gvar->varlevelsup == varlevelsup && gvar->varattno == attnum) { found_col = true; break; } } if (!found_col) break; } if (found_col) { /* The PK is a subset of grouping_columns, so we win */ *constraintDeps = lappend_oid(*constraintDeps, HeapTupleGetOid(tuple)); result = true; break; } }
/** * Returns a histogram from an array of numbers. * by Paul A. Jungwirth */ Datum array_to_hist(PG_FUNCTION_ARGS) { // Our arguments: ArrayType *vals; pgnum bucketsStart; pgnum bucketsSize; int32 bucketsCount; // The array element type: Oid valsType; // The array element type widths for our input and output arrays: int16 valsTypeWidth; int16 histTypeWidth; // The array element type "is passed by value" flags (not really used): bool valsTypeByValue; bool histTypeByValue; // The array element type alignment codes (not really used): char valsTypeAlignmentCode; char histTypeAlignmentCode; // The array contents, as PostgreSQL "Datum" objects: Datum *valsContent; Datum *histContent; // List of "is null" flags for the array contents (not used): bool *valsNullFlags; // The size of the input array: int valsLength; // The output array: ArrayType* histArray; pgnum histMax; pgnum v; int i; if (PG_ARGISNULL(0) || PG_ARGISNULL(1) || PG_ARGISNULL(2) || PG_ARGISNULL(3)) { ereport(ERROR, (errmsg("Null arguments not accepted"))); } vals = PG_GETARG_ARRAYTYPE_P(0); if (ARR_NDIM(vals) > 1) { ereport(ERROR, (errmsg("One-dimesional arrays are required"))); } if (array_contains_nulls(vals)) { ereport(ERROR, (errmsg("Array contains null elements"))); } // Determine the array element types. valsType = ARR_ELEMTYPE(vals); if (valsType != INT2OID && valsType != INT4OID && valsType != INT8OID && valsType != FLOAT4OID && valsType != FLOAT8OID) { ereport(ERROR, (errmsg("Histogram subject must be SMALLINT, INTEGER, BIGINT, REAL, or DOUBLE PRECISION values"))); } valsLength = (ARR_DIMS(vals))[0]; switch (valsType) { case INT2OID: bucketsStart.i16 = PG_GETARG_INT16(1); bucketsSize.i16 = PG_GETARG_INT16(2); break; case INT4OID: bucketsStart.i32 = PG_GETARG_INT32(1); bucketsSize.i32 = PG_GETARG_INT32(2); break; case INT8OID: bucketsStart.i64 = PG_GETARG_INT64(1); bucketsSize.i64 = PG_GETARG_INT64(2); break; case FLOAT4OID: bucketsStart.f4 = PG_GETARG_FLOAT4(1); bucketsSize.f4 = PG_GETARG_FLOAT4(2); break; case FLOAT8OID: bucketsStart.f8 = PG_GETARG_FLOAT8(1); bucketsSize.f8 = PG_GETARG_FLOAT8(2); break; default: break; } bucketsCount = PG_GETARG_INT32(3); get_typlenbyvalalign(valsType, &valsTypeWidth, &valsTypeByValue, &valsTypeAlignmentCode); // Extract the array contents (as Datum objects). deconstruct_array(vals, valsType, valsTypeWidth, valsTypeByValue, valsTypeAlignmentCode, &valsContent, &valsNullFlags, &valsLength); // Create a new array of histogram bins (as Datum objects). // Memory we palloc is freed automatically at the end of the transaction. histContent = palloc0(sizeof(Datum) * bucketsCount); // Generate the histogram switch (valsType) { case INT2OID: histMax.i16 = bucketsStart.i16 + (bucketsSize.i16 * bucketsCount); for (i = 0; i < valsLength; i++) { v.i16 = DatumGetInt16(valsContent[i]); if (v.i16 >= bucketsStart.i16 && v.i16 <= histMax.i16) { int b = (v.i16 - bucketsStart.i16) / bucketsSize.i16; if (b >= 0 && b < bucketsCount) { histContent[b] = Int32GetDatum(DatumGetInt32(histContent[b]) + 1); } } } break; case INT4OID: histMax.i32 = bucketsStart.i32 + (bucketsSize.i32 * bucketsCount); for (i = 0; i < valsLength; i++) { v.i32 = DatumGetInt32(valsContent[i]); if (v.i32 >= bucketsStart.i32 && v.i32 <= histMax.i32) { int b = (v.i32 - bucketsStart.i32) / bucketsSize.i32; if (b >= 0 && b < bucketsCount) { histContent[b] = Int32GetDatum(DatumGetInt32(histContent[b]) + 1); } } } break; case INT8OID: histMax.i64 = bucketsStart.i64 + (bucketsSize.i64 * bucketsCount); for (i = 0; i < valsLength; i++) { v.i64 = DatumGetInt64(valsContent[i]); if (v.i64 >= bucketsStart.i64 && v.i64 <= histMax.i64) { int b = (v.i64 - bucketsStart.i64) / bucketsSize.i64; if (b >= 0 && b < bucketsCount) { histContent[b] = Int64GetDatum(DatumGetInt64(histContent[b]) + 1); } } } break; case FLOAT4OID: histMax.f4 = bucketsStart.f4 + (bucketsSize.f4 * bucketsCount); for (i = 0; i < valsLength; i++) { v.f4 = DatumGetFloat4(valsContent[i]); if (v.f4 >= bucketsStart.f4 && v.f4 <= histMax.f4) { int b = (v.f4 - bucketsStart.f4) / bucketsSize.f4; if (b >= 0 && b < bucketsCount) { histContent[b] = Int32GetDatum(DatumGetInt32(histContent[b]) + 1); } } } break; case FLOAT8OID: histMax.f8 = bucketsStart.f8 + (bucketsSize.f8 * bucketsCount); for (i = 0; i < valsLength; i++) { v.f8 = DatumGetFloat8(valsContent[i]); if (v.f8 >= bucketsStart.f8 && v.f8 <= histMax.f8) { int b = (v.f8 - bucketsStart.f8) / bucketsSize.f8; if (b >= 0 && b < bucketsCount) { histContent[b] = Int32GetDatum(DatumGetInt32(histContent[b]) + 1); } } } break; default: break; } // Wrap the buckets in a new PostgreSQL array object. get_typlenbyvalalign(INT4OID, &histTypeWidth, &histTypeByValue, &histTypeAlignmentCode); histArray = construct_array(histContent, bucketsCount, INT4OID, histTypeWidth, histTypeByValue, histTypeAlignmentCode); // Return the final PostgreSQL array object. PG_RETURN_ARRAYTYPE_P(histArray); }
Datum xmlelement(PG_FUNCTION_ARGS) { Datum nameText; ArrayType *attrs = NULL; char *elName; unsigned int nameLen, resSizeMax; unsigned int childSize = 0; char *c, *result, *resData, *resCursor, *nameDst; XMLCompNodeHdr element; XMLNodeOffset *rootOffPtr; bool nameFirstChar = true; char **attrNames = NULL; char **attrValues = NULL; char *attrValFlags = NULL; XMLNodeHdr *attrNodes = NULL; XMLNodeHdr child = NULL; char **newNds = NULL; char *newNd = NULL; unsigned int attrCount = 0; unsigned int attrsSizeTotal = 0; unsigned short childCount = 0; if (PG_ARGISNULL(0)) { elog(ERROR, "invalid element name"); } nameText = PG_GETARG_DATUM(0); elName = TextDatumGetCString(nameText); nameLen = strlen(elName); if (nameLen == 0) { elog(ERROR, "invalid element name"); } if (!PG_ARGISNULL(1)) { int *dims; Oid elType, arrType; int16 arrLen, elLen; bool elByVal, elIsNull; char elAlign; unsigned int i; attrs = PG_GETARG_ARRAYTYPE_P(1); if (ARR_NDIM(attrs) != 2) { elog(ERROR, "attributes must be passed in 2 dimensional array"); } dims = ARR_DIMS(attrs); if (dims[1] != 2) { elog(ERROR, "the second dimension of attribute array must be 2"); } attrCount = dims[0]; Assert(attrCount > 0); elType = attrs->elemtype; arrType = get_array_type(elType); arrLen = get_typlen(arrType); Assert(arrType != InvalidOid); get_typlenbyvalalign(elType, &elLen, &elByVal, &elAlign); attrNames = (char **) palloc(attrCount * sizeof(char *)); attrValues = (char **) palloc(attrCount * sizeof(char *)); attrValFlags = (bool *) palloc(attrCount * sizeof(char)); for (i = 1; i <= attrCount; i++) { int subscrName[] = {i, 1}; int subscrValue[] = {i, 2}; Datum elDatum; char *nameStr, *valueStr; bool valueHasRefs = false; elDatum = array_ref(attrs, 2, subscrName, arrLen, elLen, elByVal, elAlign, &elIsNull); if (elIsNull) { elog(ERROR, "attribute name must not be null"); } nameStr = text_to_cstring(DatumGetTextP(elDatum)); if (strlen(nameStr) == 0) { elog(ERROR, "attribute name must be a string of non-zero length"); } else { /* Check validity of characters. */ char *c = nameStr; int cWidth = pg_utf_mblen((unsigned char *) c); if (!XNODE_VALID_NAME_START(c)) { elog(ERROR, "attribute name starts with invalid character"); } do { c += cWidth; cWidth = pg_utf_mblen((unsigned char *) c); } while (XNODE_VALID_NAME_CHAR(c)); if (*c != '\0') { elog(ERROR, "invalid character in attribute name"); } } /* Check uniqueness of the attribute name. */ if (i > 1) { unsigned short j; for (j = 0; j < (i - 1); j++) { if (strcmp(nameStr, attrNames[j]) == 0) { elog(ERROR, "attribute name '%s' is not unique", nameStr); } } } elDatum = array_ref(attrs, 2, subscrValue, arrLen, elLen, elByVal, elAlign, &elIsNull); if (elIsNull) { elog(ERROR, "attribute value must not be null"); } valueStr = text_to_cstring(DatumGetTextP(elDatum)); attrValFlags[i - 1] = 0; if (strlen(valueStr) > 0) { XMLNodeParserStateData state; char *valueStrOrig = valueStr; /* Parse the value and check validity. */ initXMLParserState(&state, valueStr, true); valueStr = readXMLAttValue(&state, true, &valueHasRefs); /* * If the value contains quotation mark, then apostrophe is * the delimiter. */ if (strchr(valueStr, XNODE_CHAR_QUOTMARK) != NULL) { attrValFlags[i - 1] |= XNODE_ATTR_APOSTROPHE; } finalizeXMLParserState(&state); pfree(valueStrOrig); } attrNames[i - 1] = nameStr; attrValues[i - 1] = valueStr; if (valueHasRefs) { attrValFlags[i - 1] |= XNODE_ATTR_CONTAINS_REF; } attrsSizeTotal += sizeof(XMLNodeHdrData) + strlen(nameStr) + strlen(valueStr) + 2; } } if (!PG_ARGISNULL(2)) { Datum childNodeDatum = PG_GETARG_DATUM(2); xmlnode childRaw = (xmlnode) PG_DETOAST_DATUM(childNodeDatum); child = XNODE_ROOT(childRaw); if (child->kind == XMLNODE_DOC_FRAGMENT) { childSize = getXMLNodeSize(child, true) - getXMLNodeSize(child, false); } else { childSize = getXMLNodeSize(child, true); } } /* Make sure the element name is valid. */ c = elName; while (*c != '\0') { if ((nameFirstChar && !XNODE_VALID_NAME_START(c)) || (!nameFirstChar && !XNODE_VALID_NAME_CHAR(c))) { elog(ERROR, "unrecognized character '%c' in element name", *c); } if (nameFirstChar) { nameFirstChar = false; } c += pg_utf_mblen((unsigned char *) c); }; if (child != NULL) { if (child->kind == XMLNODE_DOC_FRAGMENT) { childCount = ((XMLCompNodeHdr) child)->children; } else { childCount = 1; } } /* * It's hard to determine the byte width of references until the copying * has finished. Therefore we assume the worst case: 4 bytes per * reference. */ resSizeMax = VARHDRSZ + attrsSizeTotal + childSize + (attrCount + childCount) * 4 + sizeof(XMLCompNodeHdrData) + nameLen + 1 + sizeof(XMLNodeOffset); result = (char *) palloc(resSizeMax); resCursor = resData = VARDATA(result); if (attrCount > 0) { /* Copy attributes. */ unsigned short i; Assert(attrNames != NULL && attrValues != NULL && attrValFlags != NULL); attrNodes = (XMLNodeHdr *) palloc(attrCount * sizeof(XMLNodeHdr)); for (i = 0; i < attrCount; i++) { XMLNodeHdr attrNode = (XMLNodeHdr) resCursor; char *name = attrNames[i]; unsigned int nameLen = strlen(name); char *value = attrValues[i]; unsigned int valueLen = strlen(value); attrNodes[i] = attrNode; attrNode->kind = XMLNODE_ATTRIBUTE; attrNode->flags = attrValFlags[i]; if (xmlAttrValueIsNumber(value)) { attrNode->flags |= XNODE_ATTR_NUMBER; } resCursor = XNODE_CONTENT(attrNode); memcpy(resCursor, name, nameLen); resCursor += nameLen; *(resCursor++) = '\0'; pfree(name); memcpy(resCursor, value, valueLen); resCursor += valueLen; *(resCursor++) = '\0'; pfree(value); } pfree(attrNames); pfree(attrValues); pfree(attrValFlags); } if (child != NULL) { XMLNodeKind k = child->kind; /* * Check if the node to be inserted is of a valid kind. If the node is * document fragment, its assumed that invalid node kinds are never * added. Otherwise we'd have to check the node fragment (recursively) * not only here. */ if (k != XMLNODE_DOC_FRAGMENT) { if (k == XMLNODE_DOC || k == XMLNODE_DTD || k == XMLNODE_ATTRIBUTE) { elog(ERROR, "the nested node must not be %s", getXMLNodeKindStr(k)); } } copyXMLNodeOrDocFragment(child, childSize, &resCursor, &newNd, &newNds); } element = (XMLCompNodeHdr) resCursor; element->common.kind = XMLNODE_ELEMENT; element->common.flags = (child == NULL) ? XNODE_EMPTY : 0; element->children = attrCount + childCount; if (childCount > 0 || attrCount > 0) { XMLNodeOffset childOff, childOffMax; char bwidth; char *refPtr; /* Save relative offset(s) of the child node(s). */ if (attrCount > 0) { childOffMax = (char *) element - resData; } else if (childCount > 0) { if (child->kind == XMLNODE_DOC_FRAGMENT) { Assert(newNds != NULL); childOffMax = (char *) element - newNds[0]; } else { childOffMax = (char *) element - newNd; } } else { childOffMax = 0; } bwidth = getXMLNodeOffsetByteWidth(childOffMax); XNODE_SET_REF_BWIDTH(element, bwidth); refPtr = XNODE_FIRST_REF(element); if (attrCount > 0) { unsigned short i; /* The attribute references first... */ for (i = 0; i < attrCount; i++) { XMLNodeHdr node = attrNodes[i]; childOff = (char *) element - (char *) node; writeXMLNodeOffset(childOff, &refPtr, bwidth, true); } pfree(attrNodes); } if (childCount > 0) { /* ...followed by those of the other children. */ if (child->kind == XMLNODE_DOC_FRAGMENT) { unsigned short i; for (i = 0; i < childCount; i++) { childOff = (char *) element - newNds[i]; writeXMLNodeOffset(childOff, &refPtr, bwidth, true); } pfree(newNds); } else { childOff = (char *) element - newNd; writeXMLNodeOffset(childOff, &refPtr, bwidth, true); } } } /* And finally set the element name. */ nameDst = XNODE_ELEMENT_NAME(element); memcpy(nameDst, elName, nameLen); nameDst[nameLen] = '\0'; resCursor = nameDst + strlen(elName) + 1; SET_VARSIZE(result, (char *) resCursor - result + sizeof(XMLNodeOffset)); rootOffPtr = XNODE_ROOT_OFFSET_PTR(result); *rootOffPtr = (char *) element - resData; PG_RETURN_POINTER(result); }
/* ---------------------------------------------------------------- * ProcedureCreate * * Note: allParameterTypes, parameterModes, parameterNames, trftypes, and proconfig * are either arrays of the proper types or NULL. We declare them Datum, * not "ArrayType *", to avoid importing array.h into pg_proc.h. * ---------------------------------------------------------------- */ ObjectAddress ProcedureCreate(const char *procedureName, Oid procNamespace, bool replace, bool returnsSet, Oid returnType, Oid proowner, Oid languageObjectId, Oid languageValidator, const char *prosrc, const char *probin, char prokind, bool security_definer, bool isLeakProof, bool isStrict, char volatility, char parallel, oidvector *parameterTypes, Datum allParameterTypes, Datum parameterModes, Datum parameterNames, List *parameterDefaults, Datum trftypes, Datum proconfig, float4 procost, float4 prorows) { Oid retval; int parameterCount; int allParamCount; Oid *allParams; char *paramModes = NULL; bool genericInParam = false; bool genericOutParam = false; bool anyrangeInParam = false; bool anyrangeOutParam = false; bool internalInParam = false; bool internalOutParam = false; Oid variadicType = InvalidOid; Acl *proacl = NULL; Relation rel; HeapTuple tup; HeapTuple oldtup; bool nulls[Natts_pg_proc]; Datum values[Natts_pg_proc]; bool replaces[Natts_pg_proc]; NameData procname; TupleDesc tupDesc; bool is_update; ObjectAddress myself, referenced; int i; Oid trfid; /* * sanity checks */ Assert(PointerIsValid(prosrc)); parameterCount = parameterTypes->dim1; if (parameterCount < 0 || parameterCount > FUNC_MAX_ARGS) ereport(ERROR, (errcode(ERRCODE_TOO_MANY_ARGUMENTS), errmsg_plural("functions cannot have more than %d argument", "functions cannot have more than %d arguments", FUNC_MAX_ARGS, FUNC_MAX_ARGS))); /* note: the above is correct, we do NOT count output arguments */ /* Deconstruct array inputs */ if (allParameterTypes != PointerGetDatum(NULL)) { /* * We expect the array to be a 1-D OID array; verify that. We don't * need to use deconstruct_array() since the array data is just going * to look like a C array of OID values. */ ArrayType *allParamArray = (ArrayType *) DatumGetPointer(allParameterTypes); allParamCount = ARR_DIMS(allParamArray)[0]; if (ARR_NDIM(allParamArray) != 1 || allParamCount <= 0 || ARR_HASNULL(allParamArray) || ARR_ELEMTYPE(allParamArray) != OIDOID) elog(ERROR, "allParameterTypes is not a 1-D Oid array"); allParams = (Oid *) ARR_DATA_PTR(allParamArray); Assert(allParamCount >= parameterCount); /* we assume caller got the contents right */ } else { allParamCount = parameterCount; allParams = parameterTypes->values; } if (parameterModes != PointerGetDatum(NULL)) { /* * We expect the array to be a 1-D CHAR array; verify that. We don't * need to use deconstruct_array() since the array data is just going * to look like a C array of char values. */ ArrayType *modesArray = (ArrayType *) DatumGetPointer(parameterModes); if (ARR_NDIM(modesArray) != 1 || ARR_DIMS(modesArray)[0] != allParamCount || ARR_HASNULL(modesArray) || ARR_ELEMTYPE(modesArray) != CHAROID) elog(ERROR, "parameterModes is not a 1-D char array"); paramModes = (char *) ARR_DATA_PTR(modesArray); } /* * Detect whether we have polymorphic or INTERNAL arguments. The first * loop checks input arguments, the second output arguments. */ for (i = 0; i < parameterCount; i++) { switch (parameterTypes->values[i]) { case ANYARRAYOID: case ANYELEMENTOID: case ANYNONARRAYOID: case ANYENUMOID: genericInParam = true; break; case ANYRANGEOID: genericInParam = true; anyrangeInParam = true; break; case INTERNALOID: internalInParam = true; break; } } if (allParameterTypes != PointerGetDatum(NULL)) { for (i = 0; i < allParamCount; i++) { if (paramModes == NULL || paramModes[i] == PROARGMODE_IN || paramModes[i] == PROARGMODE_VARIADIC) continue; /* ignore input-only params */ switch (allParams[i]) { case ANYARRAYOID: case ANYELEMENTOID: case ANYNONARRAYOID: case ANYENUMOID: genericOutParam = true; break; case ANYRANGEOID: genericOutParam = true; anyrangeOutParam = true; break; case INTERNALOID: internalOutParam = true; break; } } } /* * Do not allow polymorphic return type unless at least one input argument * is polymorphic. ANYRANGE return type is even stricter: must have an * ANYRANGE input (since we can't deduce the specific range type from * ANYELEMENT). Also, do not allow return type INTERNAL unless at least * one input argument is INTERNAL. */ if ((IsPolymorphicType(returnType) || genericOutParam) && !genericInParam) ereport(ERROR, (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), errmsg("cannot determine result data type"), errdetail("A function returning a polymorphic type must have at least one polymorphic argument."))); if ((returnType == ANYRANGEOID || anyrangeOutParam) && !anyrangeInParam) ereport(ERROR, (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), errmsg("cannot determine result data type"), errdetail("A function returning \"anyrange\" must have at least one \"anyrange\" argument."))); if ((returnType == INTERNALOID || internalOutParam) && !internalInParam) ereport(ERROR, (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), errmsg("unsafe use of pseudo-type \"internal\""), errdetail("A function returning \"internal\" must have at least one \"internal\" argument."))); if (paramModes != NULL) { /* * Only the last input parameter can be variadic; if it is, save its * element type. Errors here are just elog since caller should have * checked this already. */ for (i = 0; i < allParamCount; i++) { switch (paramModes[i]) { case PROARGMODE_IN: case PROARGMODE_INOUT: if (OidIsValid(variadicType)) elog(ERROR, "variadic parameter must be last"); break; case PROARGMODE_OUT: case PROARGMODE_TABLE: /* okay */ break; case PROARGMODE_VARIADIC: if (OidIsValid(variadicType)) elog(ERROR, "variadic parameter must be last"); switch (allParams[i]) { case ANYOID: variadicType = ANYOID; break; case ANYARRAYOID: variadicType = ANYELEMENTOID; break; default: variadicType = get_element_type(allParams[i]); if (!OidIsValid(variadicType)) elog(ERROR, "variadic parameter is not an array"); break; } break; default: elog(ERROR, "invalid parameter mode '%c'", paramModes[i]); break; } } } /* * All seems OK; prepare the data to be inserted into pg_proc. */ for (i = 0; i < Natts_pg_proc; ++i) { nulls[i] = false; values[i] = (Datum) 0; replaces[i] = true; } namestrcpy(&procname, procedureName); values[Anum_pg_proc_proname - 1] = NameGetDatum(&procname); values[Anum_pg_proc_pronamespace - 1] = ObjectIdGetDatum(procNamespace); values[Anum_pg_proc_proowner - 1] = ObjectIdGetDatum(proowner); values[Anum_pg_proc_prolang - 1] = ObjectIdGetDatum(languageObjectId); values[Anum_pg_proc_procost - 1] = Float4GetDatum(procost); values[Anum_pg_proc_prorows - 1] = Float4GetDatum(prorows); values[Anum_pg_proc_provariadic - 1] = ObjectIdGetDatum(variadicType); values[Anum_pg_proc_protransform - 1] = ObjectIdGetDatum(InvalidOid); values[Anum_pg_proc_prokind - 1] = CharGetDatum(prokind); values[Anum_pg_proc_prosecdef - 1] = BoolGetDatum(security_definer); values[Anum_pg_proc_proleakproof - 1] = BoolGetDatum(isLeakProof); values[Anum_pg_proc_proisstrict - 1] = BoolGetDatum(isStrict); values[Anum_pg_proc_proretset - 1] = BoolGetDatum(returnsSet); values[Anum_pg_proc_provolatile - 1] = CharGetDatum(volatility); values[Anum_pg_proc_proparallel - 1] = CharGetDatum(parallel); values[Anum_pg_proc_pronargs - 1] = UInt16GetDatum(parameterCount); values[Anum_pg_proc_pronargdefaults - 1] = UInt16GetDatum(list_length(parameterDefaults)); values[Anum_pg_proc_prorettype - 1] = ObjectIdGetDatum(returnType); values[Anum_pg_proc_proargtypes - 1] = PointerGetDatum(parameterTypes); if (allParameterTypes != PointerGetDatum(NULL)) values[Anum_pg_proc_proallargtypes - 1] = allParameterTypes; else nulls[Anum_pg_proc_proallargtypes - 1] = true; if (parameterModes != PointerGetDatum(NULL)) values[Anum_pg_proc_proargmodes - 1] = parameterModes; else nulls[Anum_pg_proc_proargmodes - 1] = true; if (parameterNames != PointerGetDatum(NULL)) values[Anum_pg_proc_proargnames - 1] = parameterNames; else nulls[Anum_pg_proc_proargnames - 1] = true; if (parameterDefaults != NIL) values[Anum_pg_proc_proargdefaults - 1] = CStringGetTextDatum(nodeToString(parameterDefaults)); else nulls[Anum_pg_proc_proargdefaults - 1] = true; if (trftypes != PointerGetDatum(NULL)) values[Anum_pg_proc_protrftypes - 1] = trftypes; else nulls[Anum_pg_proc_protrftypes - 1] = true; values[Anum_pg_proc_prosrc - 1] = CStringGetTextDatum(prosrc); if (probin) values[Anum_pg_proc_probin - 1] = CStringGetTextDatum(probin); else nulls[Anum_pg_proc_probin - 1] = true; if (proconfig != PointerGetDatum(NULL)) values[Anum_pg_proc_proconfig - 1] = proconfig; else nulls[Anum_pg_proc_proconfig - 1] = true; /* proacl will be determined later */ rel = table_open(ProcedureRelationId, RowExclusiveLock); tupDesc = RelationGetDescr(rel); /* Check for pre-existing definition */ oldtup = SearchSysCache3(PROCNAMEARGSNSP, PointerGetDatum(procedureName), PointerGetDatum(parameterTypes), ObjectIdGetDatum(procNamespace)); if (HeapTupleIsValid(oldtup)) { /* There is one; okay to replace it? */ Form_pg_proc oldproc = (Form_pg_proc) GETSTRUCT(oldtup); Datum proargnames; bool isnull; const char *dropcmd; if (!replace) ereport(ERROR, (errcode(ERRCODE_DUPLICATE_FUNCTION), errmsg("function \"%s\" already exists with same argument types", procedureName))); if (!pg_proc_ownercheck(oldproc->oid, proowner)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_FUNCTION, procedureName); /* Not okay to change routine kind */ if (oldproc->prokind != prokind) ereport(ERROR, (errcode(ERRCODE_WRONG_OBJECT_TYPE), errmsg("cannot change routine kind"), (oldproc->prokind == PROKIND_AGGREGATE ? errdetail("\"%s\" is an aggregate function.", procedureName) : oldproc->prokind == PROKIND_FUNCTION ? errdetail("\"%s\" is a function.", procedureName) : oldproc->prokind == PROKIND_PROCEDURE ? errdetail("\"%s\" is a procedure.", procedureName) : oldproc->prokind == PROKIND_WINDOW ? errdetail("\"%s\" is a window function.", procedureName) : 0))); dropcmd = (prokind == PROKIND_PROCEDURE ? "DROP PROCEDURE" : "DROP FUNCTION"); /* * Not okay to change the return type of the existing proc, since * existing rules, views, etc may depend on the return type. * * In case of a procedure, a changing return type means that whether * the procedure has output parameters was changed. Since there is no * user visible return type, we produce a more specific error message. */ if (returnType != oldproc->prorettype || returnsSet != oldproc->proretset) ereport(ERROR, (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), prokind == PROKIND_PROCEDURE ? errmsg("cannot change whether a procedure has output parameters") : errmsg("cannot change return type of existing function"), /* translator: first %s is DROP FUNCTION or DROP PROCEDURE */ errhint("Use %s %s first.", dropcmd, format_procedure(oldproc->oid)))); /* * If it returns RECORD, check for possible change of record type * implied by OUT parameters */ if (returnType == RECORDOID) { TupleDesc olddesc; TupleDesc newdesc; olddesc = build_function_result_tupdesc_t(oldtup); newdesc = build_function_result_tupdesc_d(prokind, allParameterTypes, parameterModes, parameterNames); if (olddesc == NULL && newdesc == NULL) /* ok, both are runtime-defined RECORDs */ ; else if (olddesc == NULL || newdesc == NULL || !equalTupleDescs(olddesc, newdesc)) ereport(ERROR, (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), errmsg("cannot change return type of existing function"), errdetail("Row type defined by OUT parameters is different."), /* translator: first %s is DROP FUNCTION or DROP PROCEDURE */ errhint("Use %s %s first.", dropcmd, format_procedure(oldproc->oid)))); } /* * If there were any named input parameters, check to make sure the * names have not been changed, as this could break existing calls. We * allow adding names to formerly unnamed parameters, though. */ proargnames = SysCacheGetAttr(PROCNAMEARGSNSP, oldtup, Anum_pg_proc_proargnames, &isnull); if (!isnull) { Datum proargmodes; char **old_arg_names; char **new_arg_names; int n_old_arg_names; int n_new_arg_names; int j; proargmodes = SysCacheGetAttr(PROCNAMEARGSNSP, oldtup, Anum_pg_proc_proargmodes, &isnull); if (isnull) proargmodes = PointerGetDatum(NULL); /* just to be sure */ n_old_arg_names = get_func_input_arg_names(proargnames, proargmodes, &old_arg_names); n_new_arg_names = get_func_input_arg_names(parameterNames, parameterModes, &new_arg_names); for (j = 0; j < n_old_arg_names; j++) { if (old_arg_names[j] == NULL) continue; if (j >= n_new_arg_names || new_arg_names[j] == NULL || strcmp(old_arg_names[j], new_arg_names[j]) != 0) ereport(ERROR, (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), errmsg("cannot change name of input parameter \"%s\"", old_arg_names[j]), /* translator: first %s is DROP FUNCTION or DROP PROCEDURE */ errhint("Use %s %s first.", dropcmd, format_procedure(oldproc->oid)))); } } /* * If there are existing defaults, check compatibility: redefinition * must not remove any defaults nor change their types. (Removing a * default might cause a function to fail to satisfy an existing call. * Changing type would only be possible if the associated parameter is * polymorphic, and in such cases a change of default type might alter * the resolved output type of existing calls.) */ if (oldproc->pronargdefaults != 0) { Datum proargdefaults; List *oldDefaults; ListCell *oldlc; ListCell *newlc; if (list_length(parameterDefaults) < oldproc->pronargdefaults) ereport(ERROR, (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), errmsg("cannot remove parameter defaults from existing function"), /* translator: first %s is DROP FUNCTION or DROP PROCEDURE */ errhint("Use %s %s first.", dropcmd, format_procedure(oldproc->oid)))); proargdefaults = SysCacheGetAttr(PROCNAMEARGSNSP, oldtup, Anum_pg_proc_proargdefaults, &isnull); Assert(!isnull); oldDefaults = castNode(List, stringToNode(TextDatumGetCString(proargdefaults))); Assert(list_length(oldDefaults) == oldproc->pronargdefaults); /* new list can have more defaults than old, advance over 'em */ newlc = list_head(parameterDefaults); for (i = list_length(parameterDefaults) - oldproc->pronargdefaults; i > 0; i--) newlc = lnext(newlc); foreach(oldlc, oldDefaults) { Node *oldDef = (Node *) lfirst(oldlc); Node *newDef = (Node *) lfirst(newlc); if (exprType(oldDef) != exprType(newDef)) ereport(ERROR, (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), errmsg("cannot change data type of existing parameter default value"), /* translator: first %s is DROP FUNCTION or DROP PROCEDURE */ errhint("Use %s %s first.", dropcmd, format_procedure(oldproc->oid)))); newlc = lnext(newlc); } }
/** * Returns a count from an array of numbers. * by Paul A. Jungwirth */ Datum array_to_count(PG_FUNCTION_ARGS) { // Our arguments: ArrayType *vals; // The array element type: Oid valsType; // The array element type widths for our input array: int16 valsTypeWidth; // The array element type "is passed by value" flags (not really used): bool valsTypeByValue; // The array element type alignment codes (not really used): char valsTypeAlignmentCode; // The array contents, as PostgreSQL "Datum" objects: Datum *valsContent; // List of "is null" flags for the array contents: bool *valsNullFlags; // I'd prefer int64 here but deconstruct_array wants a plain int. int valsLength; int valsCount = 0; int i; if (PG_ARGISNULL(0)) { ereport(ERROR, (errmsg("Null arrays not accepted"))); } vals = PG_GETARG_ARRAYTYPE_P(0); if (ARR_NDIM(vals) == 0) { PG_RETURN_NULL(); } if (ARR_NDIM(vals) > 1) { ereport(ERROR, (errmsg("One-dimesional arrays are required"))); } valsType = ARR_ELEMTYPE(vals); valsLength = (ARR_DIMS(vals))[0]; get_typlenbyvalalign(valsType, &valsTypeWidth, &valsTypeByValue, &valsTypeAlignmentCode); // Extract the array contents (as Datum objects). // It's kind of a shame we even need to fill valsContent, // but if we pass a NULL it crashes: deconstruct_array(vals, valsType, valsTypeWidth, valsTypeByValue, valsTypeAlignmentCode, &valsContent, &valsNullFlags, &valsLength); // Compute the count. for (i = 0; i < valsLength; i++) { if (!valsNullFlags[i]) valsCount++; } PG_RETURN_INT64(valsCount); }