/* * Create the Datum/isnull representation of an expanded array object * if we didn't do so previously */ void deconstruct_expanded_array(ExpandedArrayHeader *eah) { if (eah->dvalues == NULL) { MemoryContext oldcxt = MemoryContextSwitchTo(eah->hdr.eoh_context); Datum *dvalues; bool *dnulls; int nelems; dnulls = NULL; deconstruct_array(eah->fvalue, eah->element_type, eah->typlen, eah->typbyval, eah->typalign, &dvalues, ARR_HASNULL(eah->fvalue) ? &dnulls : NULL, &nelems); /* * Update header only after successful completion of this step. If * deconstruct_array fails partway through, worst consequence is some * leaked memory in the object's context. If the caller fails at a * later point, that's fine, since the deconstructed representation is * valid anyhow. */ eah->dvalues = dvalues; eah->dnulls = dnulls; eah->dvalueslen = eah->nelems = nelems; MemoryContextSwitchTo(oldcxt); } }
static jvalue _doubleArray_coerceDatum(Type self, Datum arg) { jvalue result; ArrayType* v = DatumGetArrayTypeP(arg); jsize nElems = (jsize)ArrayGetNItems(ARR_NDIM(v), ARR_DIMS(v)); jdoubleArray doubleArray = JNI_newDoubleArray(nElems); #if (PGSQL_MAJOR_VER == 8 && PGSQL_MINOR_VER < 2) JNI_setDoubleArrayRegion(doubleArray, 0, nElems, (jdouble*)ARR_DATA_PTR(v)); #else if(ARR_HASNULL(v)) { jsize idx; jboolean isCopy = JNI_FALSE; bits8* nullBitMap = ARR_NULLBITMAP(v); jdouble* values = (jdouble*)ARR_DATA_PTR(v); jdouble* elems = JNI_getDoubleArrayElements(doubleArray, &isCopy); for(idx = 0; idx < nElems; ++idx) { if(arrayIsNull(nullBitMap, idx)) elems[idx] = 0; else elems[idx] = *values++; } JNI_releaseDoubleArrayElements(doubleArray, elems, JNI_COMMIT); } else JNI_setDoubleArrayRegion(doubleArray, 0, nElems, (jdouble*)ARR_DATA_PTR(v)); #endif result.l = (jobject)doubleArray; return result; }
Datum pcpoint_from_double_array(PG_FUNCTION_ARGS) { uint32 pcid = PG_GETARG_INT32(0); ArrayType *arrptr = PG_GETARG_ARRAYTYPE_P(1); int nelems; float8 *vals; PCPOINT *pt; PCSCHEMA *schema = pc_schema_from_pcid(pcid, fcinfo); SERIALIZED_POINT *serpt; if ( ! schema ) elog(ERROR, "unable to load schema for pcid = %d", pcid); if ( ARR_ELEMTYPE(arrptr) != FLOAT8OID ) elog(ERROR, "array must be of float8[]"); if ( ARR_NDIM(arrptr) != 1 ) elog(ERROR, "float8[] must have only one dimension"); if ( ARR_HASNULL(arrptr) ) elog(ERROR, "float8[] must not have null elements"); nelems = ARR_DIMS(arrptr)[0]; if ( nelems != schema->ndims || ARR_LBOUND(arrptr)[0] > 1 ) elog(ERROR, "array dimensions do not match schema dimensions of pcid = %d", pcid); vals = (float8*) ARR_DATA_PTR(arrptr); pt = pc_point_from_double_array(schema, vals, nelems); serpt = pc_point_serialize(pt); pc_point_free(pt); PG_RETURN_POINTER(serpt); }
static jvalue _booleanArray_coerceDatum(Type self, Datum arg) { jvalue result; ArrayType* v = DatumGetArrayTypeP(arg); jsize nElems = (jsize)ArrayGetNItems(ARR_NDIM(v), ARR_DIMS(v)); jbooleanArray booleanArray = JNI_newBooleanArray(nElems); if(ARR_HASNULL(v)) { jsize idx; jboolean isCopy = JNI_FALSE; bits8* nullBitMap = ARR_NULLBITMAP(v); jboolean* values = (jboolean*)ARR_DATA_PTR(v); jboolean* elems = JNI_getBooleanArrayElements(booleanArray, &isCopy); for(idx = 0; idx < nElems; ++idx) { if(arrayIsNull(nullBitMap, idx)) elems[idx] = 0; else elems[idx] = *values++; } JNI_releaseBooleanArrayElements(booleanArray, elems, JNI_COMMIT); } else JNI_setBooleanArrayRegion(booleanArray, 0, nElems, (jboolean*)ARR_DATA_PTR(v)); result.l = (jobject)booleanArray; return result; }
static bool array_iterator(ArrayType *la, PGCALL2 callback, void *param, ltree **found) { int num = ArrayGetNItems(ARR_NDIM(la), ARR_DIMS(la)); ltree *item = (ltree *) ARR_DATA_PTR(la); if (ARR_NDIM(la) > 1) ereport(ERROR, (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR), errmsg("array must be one-dimensional"))); if (ARR_HASNULL(la)) ereport(ERROR, (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED), errmsg("array must not contain nulls"))); if (found) *found = NULL; while (num > 0) { if (DatumGetBool(DirectFunctionCall2(callback, PointerGetDatum(item), PointerGetDatum(param)))) { if (found) *found = item; return true; } num--; item = NEXTVAL(item); } return false; }
Datum _lca(PG_FUNCTION_ARGS) { ArrayType *la = PG_GETARG_ARRAYTYPE_P(0); int num = ArrayGetNItems(ARR_NDIM(la), ARR_DIMS(la)); ltree *item = (ltree *) ARR_DATA_PTR(la); ltree **a, *res; if (ARR_NDIM(la) > 1) ereport(ERROR, (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR), errmsg("array must be one-dimensional"))); if (ARR_HASNULL(la)) ereport(ERROR, (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED), errmsg("array must not contain nulls"))); a = (ltree **) palloc(sizeof(ltree *) * num); while (num > 0) { num--; a[num] = item; item = NEXTVAL(item); } res = lca_inner(a, ArrayGetNItems(ARR_NDIM(la), ARR_DIMS(la))); pfree(a); PG_FREE_IF_COPY(la, 0); if (res) PG_RETURN_POINTER(res); else PG_RETURN_NULL(); }
Datum _lt_q_regex(PG_FUNCTION_ARGS) { ArrayType *_tree = PG_GETARG_ARRAYTYPE_P(0); ArrayType *_query = PG_GETARG_ARRAYTYPE_P(1); lquery *query = (lquery *) ARR_DATA_PTR(_query); bool res = false; int num = ArrayGetNItems(ARR_NDIM(_query), ARR_DIMS(_query)); if (ARR_NDIM(_query) > 1) ereport(ERROR, (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR), errmsg("array must be one-dimensional"))); if (ARR_HASNULL(_query)) ereport(ERROR, (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED), errmsg("array must not contain nulls"))); while (num > 0) { if (array_iterator(_tree, ltq_regex, (void *) query, NULL)) { res = true; break; } num--; query = (lquery *) NEXTVAL(query); } PG_FREE_IF_COPY(_tree, 0); PG_FREE_IF_COPY(_query, 1); PG_RETURN_BOOL(res); }
/* * 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, 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, no nulls */ if (ARR_NDIM(result) != 1 || ARR_HASNULL(result) || ARR_ELEMTYPE(result) != INT2OID) ereport(ERROR, (errcode(ERRCODE_INVALID_BINARY_REPRESENTATION), errmsg("invalid int2vector data"))); PG_RETURN_POINTER(result); }
/* ** Allows the construction of a cube from 2 float[]'s */ Datum cube_a_f8_f8(PG_FUNCTION_ARGS) { int i; int dim; int size; NDBOX *result; ArrayType *ur, *ll; double *dur, *dll; ur = (ArrayType *) PG_GETARG_VARLENA_P(0); ll = (ArrayType *) PG_GETARG_VARLENA_P(1); if (ARR_HASNULL(ur) || ARR_HASNULL(ll)) { ereport(ERROR, (errcode(ERRCODE_ARRAY_ELEMENT_ERROR), errmsg("Cannot work with NULL arrays"))); } dim = ARRNELEMS(ur); if (ARRNELEMS(ll) != dim) { ereport(ERROR, (errcode(ERRCODE_ARRAY_ELEMENT_ERROR), errmsg("UR and LL arrays must be of same length"))); } dur = ARRPTR(ur); dll = ARRPTR(ll); size = offsetof(NDBOX, x[0]) + sizeof(double) * 2 * dim; result = (NDBOX *) palloc(size); memset(result, 0, size); result->size = size; result->dim = dim; for (i = 0; i < dim; i++) { result->x[i] = dur[i]; result->x[i + dim] = dll[i]; } PG_RETURN_NDBOX(result); }
/** * Convert postgres Datum into a ConcreteValue object. */ AbstractValueSPtr AbstractPGValue::DatumToValue(bool inMemoryIsWritable, Oid inTypeID, Datum inDatum) const { // First check if datum is rowtype if (type_is_rowtype(inTypeID)) { HeapTupleHeader pgTuple = DatumGetHeapTupleHeader(inDatum); return AbstractValueSPtr(new PGValue<HeapTupleHeader>(pgTuple)); } else if (type_is_array(inTypeID)) { ArrayType *pgArray = DatumGetArrayTypeP(inDatum); if (ARR_NDIM(pgArray) != 1) throw std::invalid_argument("Multidimensional arrays not yet supported"); if (ARR_HASNULL(pgArray)) throw std::invalid_argument("Arrays with NULLs not yet supported"); switch (ARR_ELEMTYPE(pgArray)) { case FLOAT8OID: { MemHandleSPtr memoryHandle(new PGArrayHandle(pgArray)); if (inMemoryIsWritable) { return AbstractValueSPtr( new ConcreteValue<Array<double> >( Array<double>(memoryHandle, boost::extents[ ARR_DIMS(pgArray)[0] ]) ) ); } else { return AbstractValueSPtr( new ConcreteValue<Array_const<double> >( Array_const<double>(memoryHandle, boost::extents[ ARR_DIMS(pgArray)[0] ]) ) ); } } } } switch (inTypeID) { case BOOLOID: return AbstractValueSPtr( new ConcreteValue<bool>( DatumGetBool(inDatum) )); case INT2OID: return AbstractValueSPtr( new ConcreteValue<int16_t>( DatumGetInt16(inDatum) )); case INT4OID: return AbstractValueSPtr( new ConcreteValue<int32_t>( DatumGetInt32(inDatum) )); case INT8OID: return AbstractValueSPtr( new ConcreteValue<int64_t>( DatumGetInt64(inDatum) )); case FLOAT4OID: return AbstractValueSPtr( new ConcreteValue<float>( DatumGetFloat4(inDatum) )); case FLOAT8OID: return AbstractValueSPtr( new ConcreteValue<double>( DatumGetFloat8(inDatum) )); } return AbstractValueSPtr(); }
/* * ex_avg() - an enhanced average calculation that takes two arguments; * number of rows in this group and partial sum of the value. * Then, it eventually generate mathmatically compatible average value. */ static int64 * check_int64_array(ArrayType *transarray, int n) { if (ARR_NDIM(transarray) != 1 || ARR_DIMS(transarray)[0] != n || ARR_HASNULL(transarray) || ARR_ELEMTYPE(transarray) != INT8OID) elog(ERROR, "Two elements int8 array is expected"); return (int64 *) ARR_DATA_PTR(transarray); }
static PyObj array_has_null(PyObj self, void *closure) { PyObj rob; if (ARR_HASNULL(DatumGetArrayTypeP(PyPgObject_GetDatum(self)))) rob = Py_True; else rob = Py_False; Py_INCREF(rob); return(rob); }
Datum rank_cd(PG_FUNCTION_ARGS) { ArrayType *win; tsvector *txt = (tsvector *) PG_DETOAST_DATUM(PG_GETARG_DATUM(1)); QUERYTYPE *query = (QUERYTYPE *) PG_DETOAST_DATUM_COPY(PG_GETARG_DATUM(2)); int method = DEF_NORM_METHOD; float4 res; /* * Pre-8.2, rank_cd took just a plain int as its first argument. * It was a mistake to keep the same C function name while changing the * signature, but it's too late to fix that. Instead, do a runtime test * to make sure the expected datatype has been passed. This is needed * to prevent core dumps if tsearch2 function definitions from an old * database are loaded into an 8.2 server. */ if (get_fn_expr_argtype(fcinfo->flinfo, 0) != FLOAT4ARRAYOID) ereport(ERROR, (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), errmsg("rank_cd() now takes real[] as its first argument, not integer"))); /* now safe to dereference the first arg */ win = (ArrayType *) PG_DETOAST_DATUM(PG_GETARG_DATUM(0)); 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"))); if (PG_NARGS() == 4) method = PG_GETARG_INT32(3); res = calc_rank_cd((float4 *) ARR_DATA_PTR(win), 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); }
/* 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); }
Datum cube_subset(PG_FUNCTION_ARGS) { NDBOX *c, *result; ArrayType *idx; int size, dim, i; int *dx; c = PG_GETARG_NDBOX(0); idx = (ArrayType *) PG_GETARG_VARLENA_P(1); if (ARR_HASNULL(idx)) { ereport(ERROR, (errcode(ERRCODE_ARRAY_ELEMENT_ERROR), errmsg("Cannot work with NULL arrays"))); } dx = (int4 *) ARR_DATA_PTR(idx); dim = ARRNELEMS(idx); size = offsetof(NDBOX, x[0]) + sizeof(double) * 2 * dim; result = (NDBOX *) palloc(size); memset(result, 0, size); result->size = size; result->dim = dim; for (i = 0; i < dim; i++) { if ((dx[i] <= 0) || (dx[i] > c->dim)) { pfree(result); ereport(ERROR, (errcode(ERRCODE_ARRAY_ELEMENT_ERROR), errmsg("Index out of bounds"))); } result->x[i] = c->x[dx[i] - 1]; result->x[i + dim] = c->x[dx[i] + c->dim - 1]; } PG_FREE_IF_COPY(c,0); PG_RETURN_NDBOX(result); }
/* * 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); }
/* * int2vectorrecv - converts external binary format to int2vector */ Datum int2vectorrecv(PG_FUNCTION_ARGS) { LOCAL_FCINFO(locfcinfo, 3); StringInfo buf = (StringInfo) PG_GETARG_POINTER(0); 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->args[0].value = PointerGetDatum(buf); locfcinfo->args[0].isnull = false; locfcinfo->args[1].value = ObjectIdGetDatum(INT2OID); locfcinfo->args[1].isnull = false; locfcinfo->args[2].value = Int32GetDatum(-1); locfcinfo->args[2].isnull = 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 pc_typmod_in(PG_FUNCTION_ARGS) { uint32 typmod = 0; Datum *elem_values; int n = 0; int i = 0; ArrayType *arr = (ArrayType *) DatumGetPointer(PG_GETARG_DATUM(0)); 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"))); if (ArrayGetNItems(ARR_NDIM(arr), ARR_DIMS(arr)) > 1) ereport(ERROR, (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR), errmsg("typmod array must have one element"))); deconstruct_array(arr, CSTRINGOID, -2, false, 'c', /* hardwire cstring representation details */ &elem_values, NULL, &n); for (i = 0; i < n; i++) { if ( i == 0 ) /* PCID */ { char *s = DatumGetCString(elem_values[i]); typmod = pg_atoi(s, sizeof(int32), '\0'); } } PG_RETURN_INT32(typmod); }
/* * 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; }
/* * 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; }
static float * getWeights(ArrayType *win) { static float ws[lengthof(weights)]; int i; float4 *arrdata; if (win == 0) 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 (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"))); } return ws; }
/* ---------------------------------------------------------------- * 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); } }
/*----------------------------------------------------------------------------- * 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); }
/* * 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; } }
Datum kc_delete(PG_FUNCTION_ARGS) { char *map_name = text_to_cstring(PG_GETARG_TEXT_PP(0)); char *start_time = text_to_cstring(PG_GETARG_TEXT_PP(1)); ArrayType *rids = PG_GETARG_ARRAYTYPE_P(2); int i; Datum *rid_datums; bool *rid_nulls; int rid_count; char *next_rid; KCDB *main_db; char *vbuf; size_t vsiz; int64_t num_keys_to_run; int64_t num_keys_deleted; char **keys_to_use; Cloudflare__ZoneTimeBucket *msg_new; // Open our DB. main_db = kcdbnew(); if (!open_db (main_db, map_name, start_time)) { PG_RETURN_INT64(0); } kcdbbegintran (main_db, 0); // Now run over the array. deconstruct_array(rids, TEXTOID, -1, false, 'i', &rid_datums, &rid_nulls, &rid_count); if (ARR_HASNULL(rids)) { ereport(ERROR, (errcode(ERRCODE_ARRAY_ELEMENT_ERROR), errmsg("cannot work with arrays containing NULLs"))); } keys_to_use = (char **)palloc(KC_MAX_ENTRIES_PER_RID * sizeof(char)); num_keys_deleted = 0; char prefixes_to_use[rid_count][KC_MAX_RID]; for (i = 0; i < rid_count; i++) { next_rid = TextDatumGetCString(rid_datums[i]); snprintf(prefixes_to_use[i], KC_MAX_RID, "%s%s", next_rid, CF_LABEL_SEP); num_keys_to_run = kcdbmatchprefix (main_db, prefixes_to_use[i], keys_to_use, KC_MAX_ENTRIES_PER_RID); if (num_keys_to_run != -1) { num_keys_deleted += num_keys_to_run; int next_key; for (next_key=0; next_key < num_keys_to_run; next_key++) { vbuf = kcdbget(main_db, keys_to_use[next_key], strlen(keys_to_use[next_key]), &vsiz); if (vbuf) { msg_new = cloudflare__zone_time_bucket__unpack(NULL, vsiz, (const uint8_t *)vbuf); if (msg_new == NULL) { // Something failed ereport(ERROR, (errcode(ERRCODE_EXTERNAL_ROUTINE_EXCEPTION), errmsg("error unpacking incoming message"))); } else { if (msg_new->kv_map_file) { unlink(msg_new->kv_map_file); } kcdbremove (main_db, keys_to_use[next_key], strlen(keys_to_use[next_key])); } cloudflare__zone_time_bucket__free_unpacked(msg_new, NULL); kcfree(vbuf); kcfree(keys_to_use[next_key]); } else { ereport(NOTICE, (errcode(ERRCODE_EXTERNAL_ROUTINE_EXCEPTION), errmsg("get error on %s -- %s", keys_to_use[next_key], kcecodename(kcdbecode(main_db))))); } } } else { ereport(NOTICE, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("prefix error on %s -- %s", prefixes_to_use[i], kcecodename(kcdbecode(main_db))))); } } pfree(keys_to_use); // Done! kcdbendtran (main_db, 1); if (!kcdbclose(main_db)) { ereport(ERROR, (errcode(ERRCODE_EXTERNAL_ROUTINE_EXCEPTION), errmsg("Error Closeing db: \"%s\"", kcecodename(kcdbecode(main_db))))); } PG_RETURN_INT64(num_keys_deleted); }
Datum kc_shrink(PG_FUNCTION_ARGS) { char *map_name = text_to_cstring(PG_GETARG_TEXT_PP(0)); char *start_time = text_to_cstring(PG_GETARG_TEXT_PP(1)); // Start time + uid!!! char *new_rid = text_to_cstring(PG_GETARG_TEXT_PP(2)); ArrayType *old_rids = PG_GETARG_ARRAYTYPE_P(3); char *classification = text_to_cstring(PG_GETARG_TEXT_PP(4)); char *doctype = text_to_cstring(PG_GETARG_TEXT_PP(5)); char *pop = text_to_cstring(PG_GETARG_TEXT_PP(6)); char *psource = text_to_cstring(PG_GETARG_TEXT_PP(7)); text *tout; int i,j; Datum *rid_datums; bool *rid_nulls; int rid_count; char *next_rid; KCDB *main_db; char *vbuf; size_t vsiz; // Open our DB. main_db = kcdbnew(); if (!open_db (main_db, map_name, start_time)) { tout = cstring_to_text(new_rid); PG_RETURN_TEXT_P(tout); } kcdbbegintran (main_db, 0); // First fill in what we can from the input. Cloudflare__ZoneTimeBucket msg = CLOUDFLARE__ZONE_TIME_BUCKET__INIT; msg.map_name = (char *)palloc(MAX_KC_ROW_ENTRY * sizeof(char)); msg.doctype = (char *)palloc(MAX_KC_ROW_ENTRY * sizeof(char)); msg.classification = (char *)palloc(MAX_KC_ROW_ENTRY * sizeof(char)); msg.pop = (char *)palloc(MAX_KC_ROW_ENTRY * sizeof(char)); msg.psource = (char *)palloc(MAX_KC_ROW_ENTRY * sizeof(char)); msg.result_id = (char *)palloc(MAX_KC_ROW_ENTRY * sizeof(char)); msg.db_key = (char *)palloc(MAX_KC_ROW_ENTRY * sizeof(char)); msg.db_path = (char *)palloc(MAX_KC_ROW_ENTRY * sizeof(char)); msg.map_entry = palloc(MAX_KEYS_BEFORE_KV_MAP * sizeof(Cloudflare__ZoneTimeBucket__Counter)); msg.n_map_entry = 0; strncpy(msg.map_name, map_name, MAX_KC_ROW_ENTRY); strncpy(msg.classification, classification, MAX_KC_ROW_ENTRY); strncpy(msg.doctype, doctype, MAX_KC_ROW_ENTRY); strncpy(msg.pop, pop, MAX_KC_ROW_ENTRY); strncpy(msg.psource, psource, MAX_KC_ROW_ENTRY); strncpy(msg.result_id, new_rid, KC_MAX_RID); snprintf(msg.db_path, MAX_KC_ROW_ENTRY, "%s%s%s", map_name, "/", start_time); snprintf(msg.db_key, KC_MAX_RID, "%s%s%s%s%s%s%s%s%s%s%s", new_rid, CF_LABEL_SEP, classification, CF_LABEL_SEP, doctype, CF_LABEL_SEP, pop, CF_LABEL_SEP, psource, CF_LABEL_SEP, map_name); // Now run over the array. deconstruct_array(old_rids, TEXTOID, -1, false, 'i', &rid_datums, &rid_nulls, &rid_count); if (ARR_HASNULL(old_rids)) { ereport(ERROR, (errcode(ERRCODE_ARRAY_ELEMENT_ERROR), errmsg("cannot work with arrays containing NULLs"))); } int num_new_keys = 0; int num_entries = 0; char keys_to_use[rid_count][KC_MAX_RID]; Cloudflare__ZoneTimeBucket *msg_new[rid_count]; j=0; for (i = 0; i < rid_count; i++) { next_rid = TextDatumGetCString(rid_datums[i]); snprintf(keys_to_use[i], KC_MAX_RID, "%s%s%s%s%s%s%s%s%s%s%s", next_rid, CF_LABEL_SEP, classification, CF_LABEL_SEP, doctype, CF_LABEL_SEP, pop, CF_LABEL_SEP, psource, CF_LABEL_SEP, map_name); vbuf = kcdbget(main_db, keys_to_use[i], strlen(keys_to_use[i]), &vsiz); if (vbuf) { msg_new[j] = cloudflare__zone_time_bucket__unpack(NULL, vsiz, (const uint8_t *)vbuf); if (msg_new[j] == NULL) { // Something failed ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("error unpacking incoming message"))); } else { if (msg_new[j]->kv_map_file) { num_entries = MAX_KEYS_BEFORE_KV_MAP + 1; } else { num_entries += msg_new[j]->n_map_entry; } j++; } kcfree(vbuf); } else { #ifdef CF_DUBUG ereport(NOTICE, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("get error on %s -- %s", keys_to_use[i], kcecodename(kcdbecode(main_db))))); #endif } } // Now merge the buffers. KCDB* msg_db = NULL; if (num_entries > MAX_KEYS_BEFORE_KV_MAP) { msg_db = kcdbnew(); set_kv_path(&msg, map_name, start_time, msg_db); } for (i = 0; i < j; i++) { if (num_entries > MAX_KEYS_BEFORE_KV_MAP) { num_new_keys += merge_using_kv_map(&msg, msg_new[i], msg_db); } else { num_new_keys += merge_messages_basic(&msg, msg_new[i]); } cloudflare__zone_time_bucket__free_unpacked(msg_new[i], NULL); } if (num_entries > MAX_KEYS_BEFORE_KV_MAP) { // Close the db. kcdbendtran (msg_db, 1); kcdbclose(msg_db); } #ifdef CF_DUBUG ereport(NOTICE, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("saving: num map entries: %zu -- writting with %d keys", msg.n_map_entry, num_new_keys))); #endif // Save the updated buffer. if (num_new_keys > 0) { unsigned int len; void *buf; len = cloudflare__zone_time_bucket__get_packed_size (&msg); buf = palloc (len); cloudflare__zone_time_bucket__pack (&msg, buf); if(!kcdbset(main_db, msg.db_key, strlen(msg.db_key), buf, len)) { ereport(ERROR, (errcode(ERRCODE_EXTERNAL_ROUTINE_EXCEPTION), errmsg("set error: %s\n", kcecodename(kcdbecode(main_db))))); } pfree (buf); } // Done! kcdbendtran (main_db, 1); if (!kcdbclose(main_db)) { ereport(ERROR, (errcode(ERRCODE_EXTERNAL_ROUTINE_EXCEPTION), errmsg("Error Closeing db: \"%s\"", kcecodename(kcdbecode(main_db))))); } tout = cstring_to_text(new_rid); PG_RETURN_TEXT_P(tout); }
SvecType * svec_in_internal(char * str) { char *values; ArrayType *pgarray_vals,*pgarray_ix; double *vals, *vals_temp; StringInfo index; int64 *u_index; int32_t num_values,total_value_count; SparseData sdata; SvecType *result; bits8 *bitmap; int bitmask; int i,j; /* Read in the two arrays defining the Sparse Vector, first is the array * of run lengths (the count array), the second is an array of the * unique values (the data array). * * The input format is a pair of standard Postgres arrays separated by * a colon, like this: * {1,10,1,5,1}:{4.3,0,0.2,0,7.4} * * For now, the data array must only have float8 elements. */ if ((values=strchr(str,':')) == NULL) { ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("Invalid input string for svec"))); } else { *values = '\0'; values = values+1; } /* Get the count and data arrays */ pgarray_ix = DatumGetArrayTypeP( OidFunctionCall3(F_ARRAY_IN,CStringGetDatum(str), ObjectIdGetDatum(INT8OID),Int32GetDatum(-1))); pgarray_vals = DatumGetArrayTypeP( OidFunctionCall3(F_ARRAY_IN,CStringGetDatum(values), ObjectIdGetDatum(FLOAT8OID),Int32GetDatum(-1))); num_values = *(ARR_DIMS(pgarray_ix)); u_index = (int64 *)ARR_DATA_PTR(pgarray_ix); vals = (double *)ARR_DATA_PTR(pgarray_vals); /* The count and value arrays must be non-empty */ int size1 = ARR_NDIM(pgarray_ix); int size2 = ARR_NDIM(pgarray_vals); if (size1 == 0 || size2 == 0) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("The count and value arrays must be non-empty"))); /* The count and value arrays must have the same dimension */ if (num_values != *(ARR_DIMS(pgarray_vals))) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("Unique value count not equal to run length count %d != %d", num_values, *(ARR_DIMS(pgarray_vals))))); /* Count array cannot have NULLs */ if (ARR_HASNULL(pgarray_ix)) ereport(ERROR, (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED), errmsg("NULL value in the count array."))); /* If the data array has NULLs, then we need to create an array to * store the NULL values as NVP values defined in float_specials.h. */ if (ARR_HASNULL(pgarray_vals)) { vals_temp = vals; vals = (double *)palloc(sizeof(float8) * num_values); bitmap = ARR_NULLBITMAP(pgarray_vals); bitmask = 1; j = 0; for (i=0; i<num_values; i++) { if (bitmap && (*bitmap & bitmask) == 0) // if NULL vals[i] = NVP; else { vals[i] = vals_temp[j]; j++; } if (bitmap) { // advance bitmap pointer bitmask <<= 1; if (bitmask == 0x100) { bitmap++; bitmask = 1; } } } } /* Make an empty StringInfo because we have the data array already */ index = makeStringInfo(); total_value_count = 0; for (int i=0;i<num_values;i++) { if (u_index[i] <= 0) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("Non-positive run length in input"))); total_value_count+=u_index[i]; append_to_rle_index(index,u_index[i]); } sdata = makeInplaceSparseData((char *)vals,index->data, num_values*sizeof(double),index->len,FLOAT8OID, num_values,total_value_count); sdata->type_of_data = FLOAT8OID; result = svec_from_sparsedata(sdata,true); if (total_value_count == 1) result->dimension = -1; //Scalar if (ARR_HASNULL(pgarray_vals)) pfree(vals); pfree(str); /* str is allocated from a strdup */ pfree(pgarray_ix); pfree(pgarray_vals); return result; }
Datum hvault_table_count_step(PG_FUNCTION_ARGS) { MemoryContext aggmemctx, oldmemctx; ArrayType * ctx; ArrayType * idx_array; int i, pos_idx; int32_t * ctx_data; if (!AggCheckCallContext(fcinfo, &aggmemctx)) elog(ERROR, "hvault_table_group_step called in non-aggregate context"); ctx = PG_ARGISNULL(0) ? NULL : PG_GETARG_ARRAYTYPE_P(0); if (ctx == NULL) { int ndim; int32_t * bounds_data; int dims[MAXDIM]; int lbs[MAXDIM]; ArrayType * bounds_array; oldmemctx = MemoryContextSwitchTo(aggmemctx); if (PG_ARGISNULL(2)) elog(ERROR, "bounds array must not be null"); // if (!get_fn_expr_arg_stable(fcinfo->flinfo, 2)) // elog(ERROR, "bounds array must be const"); bounds_array = PG_GETARG_ARRAYTYPE_P(2); Assert(bounds_array != NULL); Assert(bounds_array->elemtype == INT4OID); if (bounds_array->ndim != 2 || ARR_DIMS(bounds_array)[1] != 2) elog(ERROR, "bounds array size is invalid"); if (ARR_HASNULL(bounds_array)) { int size = ARR_DIMS(bounds_array)[0] * ARR_DIMS(bounds_array)[1]; for (i = 0; i < (size + 7) / 8; i++) if (ARR_NULLBITMAP(bounds_array)[i] != 0) elog(ERROR, "bounds array must not contain NULLs"); } ndim = ARR_DIMS(bounds_array)[0]; if (ndim > MAXDIM) elog(ERROR, "too many dimensions, max supported is %d", MAXDIM); bounds_data = (int32_t *) ARR_DATA_PTR(bounds_array); for (i = 0; i < ndim; i++) { int ubs; lbs[i] = bounds_data[2*i]; ubs = bounds_data[2*i+1]; dims[i] = ubs - lbs[i]; } ctx = intArrayInit(ndim, dims, lbs); MemoryContextSwitchTo(oldmemctx); } Assert(!ARR_HASNULL(ctx)); Assert(ctx->elemtype == INT4OID); if (PG_ARGISNULL(1)) elog(ERROR, "group index array must not be null"); idx_array = PG_GETARG_ARRAYTYPE_P(1); Assert(idx_array != NULL); Assert(idx_array->elemtype == INT4OID); if (idx_array->ndim != 1) elog(ERROR, "group index array must have single dimension"); if (ARR_DIMS(idx_array)[0] != ctx->ndim) elog(ERROR, "group index array length is inconsistent"); if (ARR_HASNULL(idx_array)) { int size = ARR_DIMS(idx_array)[0]; for (i = 0; i < (size + 7) / 8; i++) /* Skip elements with nulls */ if (ARR_NULLBITMAP(idx_array)[i] != 0) { elog(WARNING, "index array contains NULL, skipping"); PG_RETURN_ARRAYTYPE_P(ctx); } } pos_idx = intArrayIdx(ctx, (int *) ARR_DATA_PTR(idx_array), true); if (pos_idx != -1) { Assert(pos_idx >= 0); if (ARR_SIZE(ctx) - ARR_DATA_OFFSET(ctx) <= pos_idx * 4) { elog(ERROR, "Array out of bounds access: %ld %d", ARR_SIZE(ctx) - ARR_DATA_OFFSET(ctx), pos_idx * 4); } ctx_data = (int32_t *) ARR_DATA_PTR(ctx); ctx_data[pos_idx]++; } PG_RETURN_ARRAYTYPE_P(ctx); }