Datum xmlnode_children(PG_FUNCTION_ARGS) { xmlnode nodeRaw = (xmlnode) PG_GETARG_VARLENA_P(0); char *data = (char *) VARDATA(nodeRaw); XMLNodeOffset rootNdOff = XNODE_ROOT_OFFSET(nodeRaw); XMLNodeHdr node = (XMLNodeHdr) (data + rootNdOff); TypeInfo nodeType; ArrayType *result; /* * Unfortunately the type info has to be retrieved every time again. If * the backend used global variable to remember the values, all backends * would have to invalidate the values whenever the extension gets dropped * / created. */ Assert(fcinfo->flinfo != NULL); initXNodeTypeInfo(fcinfo->flinfo->fn_oid, 0, &nodeType); if (node->kind == XMLNODE_DOC || node->kind == XMLNODE_ELEMENT || node->kind == XMLNODE_DOC_FRAGMENT) { XMLCompNodeHdr root = (XMLCompNodeHdr) node; unsigned short children = root->children; Datum *elems; char *childOffPtr; unsigned short i; if (children == 0) { result = construct_empty_array(nodeType.oid); PG_RETURN_POINTER(result); } elems = (Datum *) palloc(children * sizeof(Datum)); childOffPtr = XNODE_FIRST_REF(root); for (i = 0; i < children; i++) { XMLNodeOffset childOff = readXMLNodeOffset(&childOffPtr, XNODE_GET_REF_BWIDTH(root), true); XMLNodeHdr childNode = (XMLNodeHdr) (data + rootNdOff - childOff); char *childNodeCopy = copyXMLNode(childNode, NULL, true, NULL); elems[i] = PointerGetDatum(childNodeCopy); } result = construct_array(elems, children, nodeType.oid, nodeType.elmlen, nodeType.elmbyval, nodeType.elmalign); PG_RETURN_POINTER(result); } else { result = construct_empty_array(nodeType.oid); PG_RETURN_POINTER(result); } }
Datum hstore_akeys(PG_FUNCTION_ARGS) { HStore *hs = PG_GETARG_HS(0); Datum *d; ArrayType *a; HEntry *entries = ARRPTR(hs); char *base = STRPTR(hs); int count = HS_COUNT(hs); int i; if (count == 0) { a = construct_empty_array(TEXTOID); PG_RETURN_POINTER(a); } d = (Datum *) palloc(sizeof(Datum) * count); for (i = 0; i < count; ++i) { text *item = cstring_to_text_with_len(HS_KEY(entries, base, i), HS_KEYLEN(entries, i)); d[i] = PointerGetDatum(item); } a = construct_array(d, count, TEXTOID, -1, false, 'i'); PG_RETURN_POINTER(a); }
static ArrayType * intArrayInit(int ndims, int const * dims, int const * lbs) { ArrayType * res; int size; int nbytes; if (ndims < 0) /* we do allow zero-dimension arrays */ ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("invalid number of dimensions: %d", ndims))); if (ndims > MAXDIM) ereport(ERROR, (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), errmsg("number of array dimensions (%d) exceeds the maximum allowed (%d)", ndims, MAXDIM))); /* fast track for empty array */ if (ndims == 0) return construct_empty_array(INT4OID); size = ArrayGetNItems(ndims, dims); nbytes = ARR_OVERHEAD_NONULLS(ndims); nbytes += size * 4; res = palloc0(nbytes); SET_VARSIZE(res, nbytes); res->ndim = ndims; res->elemtype = INT4OID; res->dataoffset = 0; memcpy(ARR_DIMS(res), dims, ndims * sizeof(int)); memcpy(ARR_LBOUND(res), lbs, ndims * sizeof(int)); return res; }
/* Create a new int array with room for "num" elements */ ArrayType * new_intArrayType(int num) { ArrayType *r; int nbytes; /* if no elements, return a zero-dimensional array */ if (num <= 0) { r = construct_empty_array(INT4OID); return r; } 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; }
Datum hstore_slice_to_array(PG_FUNCTION_ARGS) { HStore *hs = PG_GETARG_HS(0); HEntry *entries = ARRPTR(hs); char *ptr = STRPTR(hs); ArrayType *key_array = PG_GETARG_ARRAYTYPE_P(1); ArrayType *aout; Datum *key_datums; bool *key_nulls; Datum *out_datums; bool *out_nulls; int key_count; int i; deconstruct_array(key_array, TEXTOID, -1, false, 'i', &key_datums, &key_nulls, &key_count); if (key_count == 0) { aout = construct_empty_array(TEXTOID); PG_RETURN_POINTER(aout); } out_datums = palloc(sizeof(Datum) * key_count); out_nulls = palloc(sizeof(bool) * key_count); for (i = 0; i < key_count; ++i) { text *key = (text *) DatumGetPointer(key_datums[i]); int idx; if (key_nulls[i]) idx = -1; else idx = hstoreFindKey(hs, NULL, VARDATA(key), VARSIZE(key) - VARHDRSZ); if (idx < 0 || HS_VALISNULL(entries, idx)) { out_nulls[i] = true; out_datums[i] = (Datum) 0; } else { out_datums[i] = PointerGetDatum( cstring_to_text_with_len(HS_VAL(entries, ptr, idx), HS_VALLEN(entries, idx))); out_nulls[i] = false; } } aout = construct_md_array(out_datums, out_nulls, ARR_NDIM(key_array), ARR_DIMS(key_array), ARR_LBOUND(key_array), TEXTOID, -1, false, 'i'); PG_RETURN_POINTER(aout); }
/* * fetch_array_arg_replace_nulls * * Fetch an array-valued argument; if it's null, construct an empty array * value of the proper data type. Also cache basic element type information * in fn_extra. */ static ArrayType * fetch_array_arg_replace_nulls(FunctionCallInfo fcinfo, int argno) { ArrayType *v; Oid element_type; ArrayMetaState *my_extra; /* First collect the array value */ if (!PG_ARGISNULL(argno)) { v = PG_GETARG_ARRAYTYPE_P(argno); element_type = ARR_ELEMTYPE(v); } else { /* We have to look up the array type and element type */ Oid arr_typeid = get_fn_expr_argtype(fcinfo->flinfo, argno); if (!OidIsValid(arr_typeid)) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("could not determine input data type"))); element_type = get_element_type(arr_typeid); if (!OidIsValid(element_type)) ereport(ERROR, (errcode(ERRCODE_DATATYPE_MISMATCH), errmsg("input data type is not an array"))); v = construct_empty_array(element_type); } /* Now cache required info, which might change from call to call */ my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra; if (my_extra == NULL) { my_extra = (ArrayMetaState *) MemoryContextAlloc(fcinfo->flinfo->fn_mcxt, sizeof(ArrayMetaState)); my_extra->element_type = InvalidOid; fcinfo->flinfo->fn_extra = my_extra; } if (my_extra->element_type != element_type) { get_typlenbyvalalign(element_type, &my_extra->typlen, &my_extra->typbyval, &my_extra->typalign); my_extra->element_type = element_type; } return v; }
/** * @fn Datum get_table_and_inheritors(PG_FUNCTION_ARGS) * @brief Return array containing Oids of parent table and its children. * Note that this function does not release relation locks. * * get_table_and_inheritors(table) * * @param table parent table. * @retval regclass[] */ Datum repack_get_table_and_inheritors(PG_FUNCTION_ARGS) { Oid parent = PG_GETARG_OID(0); List *relations; Datum *relations_array; int relations_array_size; ArrayType *result; ListCell *lc; int i; LockRelationOid(parent, AccessShareLock); /* Check that parent table exists */ if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(parent))) PG_RETURN_ARRAYTYPE_P(construct_empty_array(OIDOID)); /* Also check that children exist */ relations = find_all_inheritors(parent, AccessShareLock, NULL); relations_array_size = list_length(relations); if (relations_array_size == 0) PG_RETURN_ARRAYTYPE_P(construct_empty_array(OIDOID)); relations_array = palloc(relations_array_size * sizeof(Datum)); i = 0; foreach (lc, relations) relations_array[i++] = ObjectIdGetDatum(lfirst_oid(lc)); result = construct_array(relations_array, relations_array_size, OIDOID, sizeof(Oid), true, 'i'); pfree(relations_array); PG_RETURN_ARRAYTYPE_P(result); }
Datum array_agg_distinct_type_by_element(PG_FUNCTION_ARGS) { /* get element type for the dummy second parameter (anynonarray item) */ Oid element_type = get_fn_expr_argtype(fcinfo->flinfo, 1); CHECK_AGG_CONTEXT("count_distinct", fcinfo); /* return empty array if the state was not initialized */ if (PG_ARGISNULL(0)) PG_RETURN_DATUM(PointerGetDatum(construct_empty_array(element_type))); return build_array((element_set_t *)PG_GETARG_POINTER(0), element_type); }
/* * Test GUCArrayReset * * superuser: return NULL */ void test__GUCArrayReset__superuser(void **state) { ArrayType *in; ArrayType *out; will_return(superuser, true); in = construct_empty_array(TEXTOID); assert_not_null(in); out = GUCArrayReset(in); assert_null(out); pfree(in); }
static ArrayType * hstore_to_array_internal(HStore *hs, int ndims) { HEntry *entries = ARRPTR(hs); char *base = STRPTR(hs); int count = HS_COUNT(hs); int out_size[2] = {0, 2}; int lb[2] = {1, 1}; Datum *out_datums; bool *out_nulls; int i; Assert(ndims < 3); if (count == 0 || ndims == 0) return construct_empty_array(TEXTOID); out_size[0] = count * 2 / ndims; out_datums = palloc(sizeof(Datum) * count * 2); out_nulls = palloc(sizeof(bool) * count * 2); for (i = 0; i < count; ++i) { text *key = cstring_to_text_with_len(HS_KEY(entries, base, i), HS_KEYLEN(entries, i)); out_datums[i * 2] = PointerGetDatum(key); out_nulls[i * 2] = false; if (HS_VALISNULL(entries, i)) { out_datums[i * 2 + 1] = (Datum) 0; out_nulls[i * 2 + 1] = true; } else { text *item = cstring_to_text_with_len(HS_VAL(entries, base, i), HS_VALLEN(entries, i)); out_datums[i * 2 + 1] = PointerGetDatum(item); out_nulls[i * 2 + 1] = false; } } return construct_md_array(out_datums, out_nulls, ndims, out_size, lb, TEXTOID, -1, false, 'i'); }
Datum hstore_avals(PG_FUNCTION_ARGS) { HStore *hs = PG_GETARG_HS(0); Datum *d; bool *nulls; ArrayType *a; HEntry *entries = ARRPTR(hs); char *base = STRPTR(hs); int count = HS_COUNT(hs); int lb = 1; int i; if (count == 0) { a = construct_empty_array(TEXTOID); PG_RETURN_POINTER(a); } d = (Datum *) palloc(sizeof(Datum) * count); nulls = (bool *) palloc(sizeof(bool) * count); for (i = 0; i < count; ++i) { if (HS_VALISNULL(entries, i)) { d[i] = (Datum) 0; nulls[i] = true; } else { text *item = cstring_to_text_with_len(HS_VAL(entries, base, i), HS_VALLEN(entries, i)); d[i] = PointerGetDatum(item); nulls[i] = false; } } a = construct_md_array(d, nulls, 1, &count, &lb, TEXTOID, -1, false, 'i'); PG_RETURN_POINTER(a); }
/*----------------------------------------------------------------------------- * 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); }
/* * UpdateTopnArray is a helper function for the unions and inserts. It takes * given item and its frequency. If the item is not in the top-n array, it tries * to insert new item. If there is place in the top-n array, it insert directly. * Otherwise, it compares its frequency with the minimum of current items in the * array and updates top-n array if new frequency is bigger. */ static ArrayType * UpdateTopnArray(CmsTopn *cmsTopn, Datum candidateItem, TypeCacheEntry *itemTypeCacheEntry, Frequency itemFrequency, bool *topnArrayUpdated) { ArrayType *currentTopnArray = TopnArray(cmsTopn); ArrayType *updatedTopnArray = NULL; int16 itemTypeLength = itemTypeCacheEntry->typlen; bool itemTypeByValue = itemTypeCacheEntry->typbyval; char itemTypeAlignment = itemTypeCacheEntry->typalign; Frequency minOfNewTopnItems = MAX_FREQUENCY; bool candidateAlreadyInArray = false; int candidateIndex = -1; int currentArrayLength = ARR_DIMS(currentTopnArray)[0]; if (currentArrayLength == 0) { Oid itemType = itemTypeCacheEntry->type_id; if (itemTypeCacheEntry->typtype == TYPTYPE_COMPOSITE) { ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("composite types are not supported"))); } currentTopnArray = construct_empty_array(itemType); } /* * If item frequency is smaller than min frequency of old top-n items, * it cannot be in the top-n items and we insert it only if there is place. * Otherwise, we need to check new minimum and whether it is in the top-n. */ if (itemFrequency <= cmsTopn->minFrequencyOfTopnItems) { if (currentArrayLength < cmsTopn->topnItemCount) { candidateIndex = currentArrayLength + 1; minOfNewTopnItems = itemFrequency; } } else { ArrayIterator iterator = array_create_iterator(currentTopnArray, 0); Datum topnItem = 0; int topnItemIndex = 1; int minItemIndex = 1; bool hasMoreItem = false; bool isNull = false; /* * Find the top-n item with minimum frequency to replace it with candidate * item. */ hasMoreItem = array_iterate(iterator, &topnItem, &isNull); while (hasMoreItem) { Frequency topnItemFrequency = 0; /* check if we already have this item in top-n array */ candidateAlreadyInArray = datumIsEqual(topnItem, candidateItem, itemTypeByValue, itemTypeLength); if (candidateAlreadyInArray) { minItemIndex = -1; break; } /* keep track of minumum frequency item in the top-n array */ topnItemFrequency = CmsTopnEstimateItemFrequency(cmsTopn, topnItem, itemTypeCacheEntry); if (topnItemFrequency < minOfNewTopnItems) { minOfNewTopnItems = topnItemFrequency; minItemIndex = topnItemIndex; } hasMoreItem = array_iterate(iterator, &topnItem, &isNull); topnItemIndex++; } /* if new item is not in the top-n and there is place, insert the item */ if (!candidateAlreadyInArray && currentArrayLength < cmsTopn->topnItemCount) { minItemIndex = currentArrayLength + 1; minOfNewTopnItems = Min(minOfNewTopnItems, itemFrequency); } candidateIndex = minItemIndex; } /* * If it is not in the top-n structure and its frequency bigger than minimum * put into top-n instead of the item which has minimum frequency. If it is * in top-n or not frequent items, do not change anything. */ if (!candidateAlreadyInArray && minOfNewTopnItems <= itemFrequency) { updatedTopnArray = array_set(currentTopnArray, 1, &candidateIndex, candidateItem, false, -1, itemTypeLength, itemTypeByValue, itemTypeAlignment); cmsTopn->minFrequencyOfTopnItems = minOfNewTopnItems; *topnArrayUpdated = true; } else { updatedTopnArray = currentTopnArray; *topnArrayUpdated = false; } return updatedTopnArray; }