Datum transform_eeg(PG_FUNCTION_ARGS) { //the size of the array we are trying to transform int arraySize; // a Datum for the constructred and deconstructed input arrays Datum *intermResult; Datum *input_data; // the final result to return and the input to the function ArrayType *result, *input; Oid element_type, return_type, input_type; //next 6 variables are inputted into get_typlenbyvalalign() and are used for construct and deconstruct array //small int in postgreSQL is int16 int16 return_typelen, input_typelen; bool return_typbyval, input_typbyval; char return_typalign, input_typalign; //the arrays we are transforming. Transforming array in[] into array out[] //fftw_complex, a data structure with real (in[i].re) and imaginary (in[i].im) floating-point components. fftw_complex *in, *out; fftw_plan my_plan; // get input row input = PG_GETARG_ARRAYTYPE_P(0); // get input array element type input_type = ARR_ELEMTYPE(input); //get needed variabels get_typlenbyvalalign(input_type, &input_typelen, &input_typbyval, &input_typalign); // deconstruct inupt array and save the array as Datum input_data deconstruct_array(input, input_type, input_typelen, input_typbyval, input_typalign, &input_data, NULL, &arraySize); // get element type of return vale (Complex[]) element_type = get_fn_expr_rettype(fcinfo->flinfo); // get element type of an element in the return value (Complex) return_type = get_element_type(element_type); //get needed variabels get_typlenbyvalalign(return_type, &return_typelen, &return_typbyval, &return_typalign); // in and out array and plan declarations in = (fftw_complex*) fftw_malloc(sizeof(fftw_complex)*arraySize); out = (fftw_complex*) fftw_malloc(sizeof(fftw_complex)*arraySize); my_plan = fftw_plan_dft_1d(arraySize, in, out, FFTW_FORWARD, FFTW_ESTIMATE); // set the in variable to the array we got as input. // the real parts are from the input_data array // the imaginary part is set to 0 for (int i = 0; i < arraySize; i++) { in[i][0] = (double) DatumGetInt16(input_data[i]); in[i][1] = 0; } // run the plan fftw_execute(my_plan); // array to store the out array (the transformed in array) intermResult = palloc(sizeof(Datum)*arraySize); // for each variable in the out array. // Create a complex variable then set its real and imaginary party from the processed array // get the datum from the pointer and save it in the result array for(int32 i = 0; i < arraySize; i++) { Complex *temp = palloc(sizeof(Complex)); temp->x = out[i][0]; temp->y = out[i][1]; intermResult[i] = PointerGetDatum(temp); } // construct a result array result = construct_array(intermResult, arraySize, return_type, return_typelen, return_typbyval, return_typalign); // free memory fftw_destroy_plan(my_plan); fftw_free(in); fftw_free(out); pfree(input_data); pfree(intermResult); // return result PG_RETURN_POINTER(result); }
Datum variant_typmod_in(PG_FUNCTION_ARGS) { ArrayType *arr = PG_GETARG_ARRAYTYPE_P(0); Datum *elem_values; int arr_nelem; char *inputCString; Datum inputDatum; Datum out; Assert(fcinfo->flinfo->fn_strict); /* Must be strict */ deconstruct_array(arr, CSTRINGOID, -2, false, 'c', /* elmlen, elmbyval, elmalign */ &elem_values, NULL, &arr_nelem); /* elements, nulls, number_of_elements */ /* TODO: Sanity check array */ /* PointerGetDatum is equivalent to TextGetDatum, which doesn't exist */ inputCString = DatumGetCString(elem_values[0]); inputDatum = PointerGetDatum( cstring_to_text( inputCString ) ); /* TODO: cache this stuff */ /* Keep cruft localized to just here */ { bool do_pop = _SPI_conn(); bool isnull; int ret; Oid type = TEXTOID; /* This should arguably be FOR KEY SHARE. See comment in variant_get_variant_name() */ char *cmd = "SELECT variant_typmod, variant_enabled FROM variant._registered WHERE lower(variant_name) = lower($1)"; /* command, nargs, Oid *argument_types, *values, *nulls, read_only, count */ if( (ret = SPI_execute_with_args( cmd, 1, &type, &inputDatum, " ", true, 0 )) != SPI_OK_SELECT ) elog( ERROR, "SPI_execute_with_args(%s) returned %s", cmd, SPI_result_code_string(ret)); Assert( SPI_tuptable ); if ( SPI_processed > 1 ) ereport(ERROR, ( errmsg( "Got %u records for variant.variant(%s)", SPI_processed, inputCString ), errhint( "This means _variant._registered is corrupted" ) ) ); if ( SPI_processed < 1 ) elog( ERROR, "variant.variant(%s) is not registered", inputCString ); /* Note 0 vs 1 based numbering */ Assert(SPI_tuptable->tupdesc->attrs[0]->atttypid == INT4OID); Assert(SPI_tuptable->tupdesc->attrs[1]->atttypid == BOOLOID); out = heap_getattr( SPI_tuptable->vals[0], 2, SPI_tuptable->tupdesc, &isnull ); if( !DatumGetBool(out) ) ereport( ERROR, ( errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg( "variant.variant(%s) is disabled", inputCString ) ) ); /* Don't need to copy the tuple because int is pass by value */ out = heap_getattr( SPI_tuptable->vals[0], 1, SPI_tuptable->tupdesc, &isnull ); if( isnull ) ereport( ERROR, ( errmsg( "Found NULL variant_typmod for variant.variant(%s)", inputCString ), errhint( "This should never happen; is _variant._registered corrupted?" ) ) ); _SPI_disc(do_pop); } PG_RETURN_INT32(out); }
/* * get_func_arg_info * * Fetch info about the argument types, names, and IN/OUT modes from the * pg_proc tuple. Return value is the total number of arguments. * Other results are palloc'd. *p_argtypes is always filled in, but * *p_argnames and *p_argmodes will be set NULL in the default cases * (no names, and all IN arguments, respectively). * * Note that this function simply fetches what is in the pg_proc tuple; * it doesn't do any interpretation of polymorphic types. */ int get_func_arg_info(HeapTuple procTup, Oid **p_argtypes, char ***p_argnames, char **p_argmodes) { Form_pg_proc procStruct = (Form_pg_proc) GETSTRUCT(procTup); Datum proallargtypes; Datum proargmodes; Datum proargnames; bool isNull; ArrayType *arr; int numargs; Datum *elems; int nelems; int i; /* First discover the total number of parameters and get their types */ proallargtypes = SysCacheGetAttr(PROCOID, procTup, Anum_pg_proc_proallargtypes, &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(proallargtypes); /* ensure not toasted */ numargs = ARR_DIMS(arr)[0]; if (ARR_NDIM(arr) != 1 || numargs < 0 || ARR_HASNULL(arr) || ARR_ELEMTYPE(arr) != OIDOID) elog(ERROR, "proallargtypes is not a 1-D Oid array"); Assert(numargs >= procStruct->pronargs); *p_argtypes = (Oid *) palloc(numargs * sizeof(Oid)); memcpy(*p_argtypes, ARR_DATA_PTR(arr), numargs * sizeof(Oid)); } else { /* If no proallargtypes, use proargtypes */ numargs = procStruct->proargtypes.dim1; Assert(numargs == procStruct->pronargs); *p_argtypes = (Oid *) palloc(numargs * sizeof(Oid)); memcpy(*p_argtypes, procStruct->proargtypes.values, numargs * sizeof(Oid)); } /* Get argument names, if available */ proargnames = SysCacheGetAttr(PROCOID, procTup, Anum_pg_proc_proargnames, &isNull); if (isNull) *p_argnames = NULL; else { deconstruct_array(DatumGetArrayTypeP(proargnames), TEXTOID, -1, false, 'i', &elems, NULL, &nelems); if (nelems != numargs) /* should not happen */ elog(ERROR, "proargnames must have the same number of elements as the function has arguments"); *p_argnames = (char **) palloc(sizeof(char *) * numargs); for (i = 0; i < numargs; i++) (*p_argnames)[i] = TextDatumGetCString(elems[i]); } /* Get argument modes, if available */ proargmodes = SysCacheGetAttr(PROCOID, procTup, Anum_pg_proc_proargmodes, &isNull); if (isNull) *p_argmodes = NULL; else { arr = DatumGetArrayTypeP(proargmodes); /* ensure not toasted */ if (ARR_NDIM(arr) != 1 || ARR_DIMS(arr)[0] != numargs || ARR_HASNULL(arr) || ARR_ELEMTYPE(arr) != CHAROID) elog(ERROR, "proargmodes is not a 1-D char array"); *p_argmodes = (char *) palloc(numargs * sizeof(char)); memcpy(*p_argmodes, ARR_DATA_PTR(arr), numargs * sizeof(char)); } return numargs; }
/* * ExecIndexEvalArrayKeys * Evaluate any array key values, and set up to iterate through arrays. * * Returns TRUE if there are array elements to consider; FALSE means there * is at least one null or empty array, so no match is possible. On TRUE * result, the scankeys are initialized with the first elements of the arrays. */ bool ExecIndexEvalArrayKeys(ExprContext *econtext, IndexArrayKeyInfo *arrayKeys, int numArrayKeys) { bool result = true; int j; MemoryContext oldContext; /* We want to keep the arrays in per-tuple memory */ oldContext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory); for (j = 0; j < numArrayKeys; j++) { ScanKey scan_key = arrayKeys[j].scan_key; ExprState *array_expr = arrayKeys[j].array_expr; Datum arraydatum; bool isNull; ArrayType *arrayval; int16 elmlen; bool elmbyval; char elmalign; int num_elems; Datum *elem_values; bool *elem_nulls; /* * Compute and deconstruct the array expression. (Notes in * ExecIndexEvalRuntimeKeys() apply here too.) */ arraydatum = ExecEvalExpr(array_expr, econtext, &isNull, NULL); if (isNull) { result = false; break; /* no point in evaluating more */ } arrayval = DatumGetArrayTypeP(arraydatum); /* We could cache this data, but not clear it's worth it */ get_typlenbyvalalign(ARR_ELEMTYPE(arrayval), &elmlen, &elmbyval, &elmalign); deconstruct_array(arrayval, ARR_ELEMTYPE(arrayval), elmlen, elmbyval, elmalign, &elem_values, &elem_nulls, &num_elems); if (num_elems <= 0) { result = false; break; /* no point in evaluating more */ } /* * Note: we expect the previous array data, if any, to be * automatically freed by resetting the per-tuple context; hence no * pfree's here. */ arrayKeys[j].elem_values = elem_values; arrayKeys[j].elem_nulls = elem_nulls; arrayKeys[j].num_elems = num_elems; scan_key->sk_argument = elem_values[0]; if (elem_nulls[0]) scan_key->sk_flags |= SK_ISNULL; else scan_key->sk_flags &= ~SK_ISNULL; arrayKeys[j].next_elem = 1; } MemoryContextSwitchTo(oldContext); return result; }
/* * get_func_result_name * * If the function has exactly one output parameter, and that parameter * is named, return the name (as a palloc'd string). Else return NULL. * * This is used to determine the default output column name for functions * returning scalar types. */ char * get_func_result_name(Oid functionId) { char *result; HeapTuple procTuple; Datum proargmodes; Datum proargnames; bool isnull; ArrayType *arr; int numargs; char *argmodes; Datum *argnames; int numoutargs; int nargnames; int i; /* First fetch the function's pg_proc row */ procTuple = SearchSysCache1(PROCOID, ObjectIdGetDatum(functionId)); if (!HeapTupleIsValid(procTuple)) elog(ERROR, "cache lookup failed for function %u", functionId); /* If there are no named OUT parameters, return NULL */ if (heap_attisnull(procTuple, Anum_pg_proc_proargmodes) || heap_attisnull(procTuple, Anum_pg_proc_proargnames)) result = NULL; else { /* Get the data out of the tuple */ proargmodes = SysCacheGetAttr(PROCOID, procTuple, Anum_pg_proc_proargmodes, &isnull); Assert(!isnull); proargnames = SysCacheGetAttr(PROCOID, procTuple, Anum_pg_proc_proargnames, &isnull); Assert(!isnull); /* * We expect the arrays to be 1-D arrays of the right types; verify * that. For the char array, 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(proargmodes); /* ensure not toasted */ numargs = ARR_DIMS(arr)[0]; if (ARR_NDIM(arr) != 1 || numargs < 0 || ARR_HASNULL(arr) || ARR_ELEMTYPE(arr) != CHAROID) elog(ERROR, "proargmodes is not a 1-D char array"); argmodes = (char *) ARR_DATA_PTR(arr); arr = DatumGetArrayTypeP(proargnames); /* ensure not toasted */ if (ARR_NDIM(arr) != 1 || ARR_DIMS(arr)[0] != numargs || ARR_HASNULL(arr) || ARR_ELEMTYPE(arr) != TEXTOID) elog(ERROR, "proargnames is not a 1-D text array"); deconstruct_array(arr, TEXTOID, -1, false, 'i', &argnames, NULL, &nargnames); Assert(nargnames == numargs); /* scan for output argument(s) */ result = NULL; numoutargs = 0; for (i = 0; i < numargs; i++) { if (argmodes[i] == PROARGMODE_IN || argmodes[i] == PROARGMODE_VARIADIC) continue; Assert(argmodes[i] == PROARGMODE_OUT || argmodes[i] == PROARGMODE_INOUT || argmodes[i] == PROARGMODE_TABLE); if (++numoutargs > 1) { /* multiple out args, so forget it */ result = NULL; break; } result = TextDatumGetCString(argnames[i]); if (result == NULL || result[0] == '\0') { /* Parameter is not named, so forget it */ result = NULL; break; } } } ReleaseSysCache(procTuple); return result; }
static uint32 gserialized_typmod_in(ArrayType *arr, int is_geography) { uint32 typmod = 0; Datum *elem_values; int n = 0; int i = 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"))); deconstruct_array(arr, CSTRINGOID, -2, false, 'c', /* hardwire cstring representation details */ &elem_values, NULL, &n); /* Set the SRID to the default value first */ if ( is_geography) TYPMOD_SET_SRID(typmod, SRID_DEFAULT); else TYPMOD_SET_SRID(typmod, SRID_UNKNOWN); for (i = 0; i < n; i++) { if ( i == 0 ) /* TYPE */ { char *s = DatumGetCString(elem_values[i]); uint8_t type = 0; int z = 0; int m = 0; if ( geometry_type_from_string(s, &type, &z, &m) == LW_FAILURE ) { ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("Invalid geometry type modifier: %s", s))); } else { TYPMOD_SET_TYPE(typmod, type); if ( z ) TYPMOD_SET_Z(typmod); if ( m ) TYPMOD_SET_M(typmod); } } if ( i == 1 ) /* SRID */ { int srid = pg_atoi(DatumGetCString(elem_values[i]), sizeof(int32), '\0'); srid = clamp_srid(srid); POSTGIS_DEBUGF(3, "srid: %d", srid); if ( srid != SRID_UNKNOWN ) { TYPMOD_SET_SRID(typmod, srid); } } } pfree(elem_values); return typmod; }
static PyObj array_item(PyObj self, Py_ssize_t item) { volatile PyObj rob = NULL; PyPgTypeInfo typinfo, atypinfo; ArrayType *at; Datum rd; bool isnull = false; int index = (int) item; PyObj elm; elm = PyPgType_GetElementType(Py_TYPE(self)); typinfo = PyPgTypeInfo(elm); atypinfo = PyPgTypeInfo(Py_TYPE(self)); at = DatumGetArrayTypeP(PyPgObject_GetDatum(self)); /* convert index */ ++index; if (ARR_NDIM(at) == 0) { PyErr_SetString(PyExc_IndexError, "empty array"); return(NULL); } /* * Note that the comparison is '>', not '>='. */ if (index > ARR_DIMS(at)[0]) { PyErr_Format(PyExc_IndexError, "index %d out of range %d", item, ARR_DIMS(at)[0]); return(NULL); } /* * Single dimenion array? Get an element. */ if (ARR_NDIM(at) == 1) { PG_TRY(); { rd = array_ref(at, 1, &index, atypinfo->typlen, typinfo->typlen, typinfo->typbyval, typinfo->typalign, &isnull); if (isnull) { rob = Py_None; Py_INCREF(rob); } else { /* * It points into the array structure, so there's no need to free. */ rob = PyPgObject_New(elm, rd); } } PG_CATCH(); { Py_XDECREF(rob); rob = NULL; PyErr_SetPgError(false); return(NULL); } PG_END_TRY(); } else { ArrayType *rat; int lower[MAXDIM] = {index,0,}; int upper[MAXDIM] = {index,0,}; /* * Multiple dimensions, so get a slice. */ PG_TRY(); { ArrayType *xat; Datum *elements; bool *nulls; int nelems; int ndims, i; int lbs[MAXDIM]; int dims[MAXDIM]; xat = array_get_slice(at, 1, upper, lower, atypinfo->typlen, typinfo->typlen, typinfo->typbyval, typinfo->typalign); /* * Eventually, this should probably be changed to change the already * allocated ArrayType at 'xat', but for now use the available * interfaces for creating the expected result. */ deconstruct_array(xat, typinfo->typoid, typinfo->typlen, typinfo->typbyval, typinfo->typalign, &elements, &nulls, &nelems ); /* * Alter dims, lbs, and ndims: we are removing the first dimension. */ ndims = ARR_NDIM(xat); for (i = 1; i < ndims; ++i) lbs[i-1] = ARR_LBOUND(xat)[i]; for (i = 1; i < ndims; ++i) dims[i-1] = ARR_DIMS(xat)[i]; --ndims; /* * Construct the expected result to a Python itemget call. */ rat = construct_md_array(elements, nulls, ndims, dims, lbs, typinfo->typoid, typinfo->typlen, typinfo->typbyval, typinfo->typalign); pfree(elements); pfree(nulls); pfree(xat); rob = PyPgObject_New(Py_TYPE(self), PointerGetDatum(rat)); pfree(rat); } PG_CATCH(); { PyErr_SetPgError(false); return(NULL); } PG_END_TRY(); } return(rob); }
static DTYPE *get_pgarray(int *num, ArrayType *input) { int ndims, *dims, *lbs; bool *nulls; Oid i_eltype; int16 i_typlen; bool i_typbyval; char i_typalign; Datum *i_data; int i, n; DTYPE *data; /* get input array element type */ i_eltype = ARR_ELEMTYPE(input); get_typlenbyvalalign(i_eltype, &i_typlen, &i_typbyval, &i_typalign); /* validate input data type */ switch(i_eltype) { case INT2OID: case INT4OID: case FLOAT4OID: case FLOAT8OID: break; default: elog(ERROR, "Invalid input data type"); break; } /* get various pieces of data from the input array */ ndims = ARR_NDIM(input); dims = ARR_DIMS(input); lbs = ARR_LBOUND(input); if (ndims != 2 || dims[0] != dims[1]) { elog(ERROR, "Error: matrix[num][num] in its definition."); } /* get src data */ deconstruct_array(input, i_eltype, i_typlen, i_typbyval, i_typalign, &i_data, &nulls, &n); DBG("get_pgarray: ndims=%d, n=%d", ndims, n); #ifdef DEBUG for (i=0; i<ndims; i++) { DBG(" dims[%d]=%d, lbs[%d]=%d", i, dims[i], i, lbs[i]); } #endif /* construct a C array */ data = (DTYPE *) palloc(n * sizeof(DTYPE)); if (!data) { elog(ERROR, "Error: Out of memory!"); } for (i=0; i<n; i++) { if (nulls[i]) { data[i] = INFINITY; } else { switch(i_eltype) { case INT2OID: data[i] = (DTYPE) DatumGetInt16(i_data[i]); break; case INT4OID: data[i] = (DTYPE) DatumGetInt32(i_data[i]); break; case FLOAT4OID: data[i] = (DTYPE) DatumGetFloat4(i_data[i]); break; case FLOAT8OID: data[i] = (DTYPE) DatumGetFloat8(i_data[i]); break; } /* we assume negative values are INFINTY */ /******************************************************** TODO: based on trying to add an endpt it is likely that this will not work and you will get and error in findEulerianPath **********************************************************/ if (data[i] < 0) data[i] = INFINITY; } DBG(" data[%d]=%.4f", i, data[i]); } pfree(nulls); pfree(i_data); *num = dims[0]; return data; }
/* * Array selectivity estimation based on most common elements statistics * * This function just deconstructs and sorts the array constant's contents, * and then passes the problem on to mcelem_array_contain_overlap_selec or * mcelem_array_contained_selec depending on the operator. */ static Selectivity mcelem_array_selec(ArrayType *array, TypeCacheEntry *typentry, Datum *mcelem, int nmcelem, float4 *numbers, int nnumbers, float4 *hist, int nhist, Oid operator, FmgrInfo *cmpfunc) { Selectivity selec; int num_elems; Datum *elem_values; bool *elem_nulls; bool null_present; int nonnull_nitems; int i; /* * Prepare constant array data for sorting. Sorting lets us find unique * elements and efficiently merge with the MCELEM array. */ deconstruct_array(array, typentry->type_id, typentry->typlen, typentry->typbyval, typentry->typalign, &elem_values, &elem_nulls, &num_elems); /* Collapse out any null elements */ nonnull_nitems = 0; null_present = false; for (i = 0; i < num_elems; i++) { if (elem_nulls[i]) null_present = true; else elem_values[nonnull_nitems++] = elem_values[i]; } /* * Query "column @> '{anything, null}'" matches nothing. For the other * two operators, presence of a null in the constant can be ignored. */ if (null_present && operator == OID_ARRAY_CONTAINS_OP) { pfree(elem_values); pfree(elem_nulls); return (Selectivity) 0.0; } /* Sort extracted elements using their default comparison function. */ qsort_arg(elem_values, nonnull_nitems, sizeof(Datum), element_compare, cmpfunc); /* Separate cases according to operator */ if (operator == OID_ARRAY_CONTAINS_OP || operator == OID_ARRAY_OVERLAP_OP) selec = mcelem_array_contain_overlap_selec(mcelem, nmcelem, numbers, nnumbers, elem_values, nonnull_nitems, operator, cmpfunc); else if (operator == OID_ARRAY_CONTAINED_OP) selec = mcelem_array_contained_selec(mcelem, nmcelem, numbers, nnumbers, elem_values, nonnull_nitems, hist, nhist, operator, cmpfunc); else { elog(ERROR, "arraycontsel called for unrecognized operator %u", operator); selec = 0.0; /* keep compiler quiet */ } pfree(elem_values); pfree(elem_nulls); return selec; }
/* * SQL function jsonb_object(text[]) * * take a one or two dimensional array of text as name value pairs * for a jsonb object. * */ Datum jsonb_object(PG_FUNCTION_ARGS) { ArrayType *in_array = PG_GETARG_ARRAYTYPE_P(0); int ndims = ARR_NDIM(in_array); Datum *in_datums; bool *in_nulls; int in_count, count, i; JsonbInState result; memset(&result, 0, sizeof(JsonbInState)); (void) pushJsonbValue(&result.parseState, WJB_BEGIN_OBJECT, NULL); switch (ndims) { case 0: goto close_object; break; 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; for (i = 0; i < count; ++i) { JsonbValue v; char *str; int len; if (in_nulls[i * 2]) ereport(ERROR, (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED), errmsg("null value not allowed for object key"))); str = TextDatumGetCString(in_datums[i * 2]); len = strlen(str); v.type = jbvString; v.val.string.len = len; v.val.string.val = str; (void) pushJsonbValue(&result.parseState, WJB_KEY, &v); if (in_nulls[i * 2 + 1]) { v.type = jbvNull; } else { str = TextDatumGetCString(in_datums[i * 2 + 1]); len = strlen(str); v.type = jbvString; v.val.string.len = len; v.val.string.val = str; } (void) pushJsonbValue(&result.parseState, WJB_VALUE, &v); } pfree(in_datums); pfree(in_nulls); close_object: result.res = pushJsonbValue(&result.parseState, WJB_END_OBJECT, NULL); PG_RETURN_POINTER(JsonbValueToJsonb(result.res)); }
/* * SQL function jsonb_object(text[], text[]) * * take separate name and value arrays of text to construct a jsonb object * pairwise. */ Datum jsonb_object_two_arg(PG_FUNCTION_ARGS) { ArrayType *key_array = PG_GETARG_ARRAYTYPE_P(0); ArrayType *val_array = PG_GETARG_ARRAYTYPE_P(1); int nkdims = ARR_NDIM(key_array); int nvdims = ARR_NDIM(val_array); Datum *key_datums, *val_datums; bool *key_nulls, *val_nulls; int key_count, val_count, i; JsonbInState result; memset(&result, 0, sizeof(JsonbInState)); (void) pushJsonbValue(&result.parseState, WJB_BEGIN_OBJECT, NULL); if (nkdims > 1 || nkdims != nvdims) ereport(ERROR, (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR), errmsg("wrong number of array subscripts"))); if (nkdims == 0) goto close_object; deconstruct_array(key_array, TEXTOID, -1, false, 'i', &key_datums, &key_nulls, &key_count); deconstruct_array(val_array, TEXTOID, -1, false, 'i', &val_datums, &val_nulls, &val_count); if (key_count != val_count) ereport(ERROR, (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR), errmsg("mismatched array dimensions"))); for (i = 0; i < key_count; ++i) { JsonbValue v; char *str; int len; if (key_nulls[i]) ereport(ERROR, (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED), errmsg("null value not allowed for object key"))); str = TextDatumGetCString(key_datums[i]); len = strlen(str); v.type = jbvString; v.val.string.len = len; v.val.string.val = str; (void) pushJsonbValue(&result.parseState, WJB_KEY, &v); if (val_nulls[i]) { v.type = jbvNull; } else { str = TextDatumGetCString(val_datums[i]); len = strlen(str); v.type = jbvString; v.val.string.len = len; v.val.string.val = str; } (void) pushJsonbValue(&result.parseState, WJB_VALUE, &v); } pfree(key_datums); pfree(key_nulls); pfree(val_datums); pfree(val_nulls); close_object: result.res = pushJsonbValue(&result.parseState, WJB_END_OBJECT, NULL); PG_RETURN_POINTER(JsonbValueToJsonb(result.res)); }
/* * Helper function for the various SQL callable logical decoding functions. */ static Datum pg_logical_slot_get_changes_guts(FunctionCallInfo fcinfo, bool confirm, bool binary) { Name name; XLogRecPtr upto_lsn; int32 upto_nchanges; ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo; MemoryContext per_query_ctx; MemoryContext oldcontext; XLogRecPtr end_of_wal; XLogRecPtr startptr; LogicalDecodingContext *ctx; ResourceOwner old_resowner = CurrentResourceOwner; ArrayType *arr; Size ndim; List *options = NIL; DecodingOutputState *p; check_permissions(); CheckLogicalDecodingRequirements(); if (PG_ARGISNULL(0)) ereport(ERROR, (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED), errmsg("slot name must not be null"))); name = PG_GETARG_NAME(0); if (PG_ARGISNULL(1)) upto_lsn = InvalidXLogRecPtr; else upto_lsn = PG_GETARG_LSN(1); if (PG_ARGISNULL(2)) upto_nchanges = InvalidXLogRecPtr; else upto_nchanges = PG_GETARG_INT32(2); if (PG_ARGISNULL(3)) ereport(ERROR, (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED), errmsg("options array must not be null"))); arr = PG_GETARG_ARRAYTYPE_P(3); /* check to see if caller supports us returning a tuplestore */ if (rsinfo == NULL || !IsA(rsinfo, ReturnSetInfo)) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("set-valued function called in context that cannot accept a set"))); if (!(rsinfo->allowedModes & SFRM_Materialize)) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("materialize mode required, but it is not allowed in this context"))); /* state to write output to */ p = palloc0(sizeof(DecodingOutputState)); p->binary_output = binary; /* Build a tuple descriptor for our result type */ if (get_call_result_type(fcinfo, NULL, &p->tupdesc) != TYPEFUNC_COMPOSITE) elog(ERROR, "return type must be a row type"); per_query_ctx = rsinfo->econtext->ecxt_per_query_memory; oldcontext = MemoryContextSwitchTo(per_query_ctx); /* Deconstruct options array */ ndim = ARR_NDIM(arr); if (ndim > 1) { ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("array must be one-dimensional"))); } else if (array_contains_nulls(arr)) { ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("array must not contain nulls"))); } else if (ndim == 1) { int nelems; Datum *datum_opts; int i; Assert(ARR_ELEMTYPE(arr) == TEXTOID); deconstruct_array(arr, TEXTOID, -1, false, 'i', &datum_opts, NULL, &nelems); if (nelems % 2 != 0) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("array must have even number of elements"))); for (i = 0; i < nelems; i += 2) { char *name = TextDatumGetCString(datum_opts[i]); char *opt = TextDatumGetCString(datum_opts[i + 1]); options = lappend(options, makeDefElem(name, (Node *) makeString(opt), -1)); } } p->tupstore = tuplestore_begin_heap(true, false, work_mem); rsinfo->returnMode = SFRM_Materialize; rsinfo->setResult = p->tupstore; rsinfo->setDesc = p->tupdesc; /* * Compute the current end-of-wal and maintain ThisTimeLineID. * RecoveryInProgress() will update ThisTimeLineID on promotion. */ if (!RecoveryInProgress()) end_of_wal = GetFlushRecPtr(); else end_of_wal = GetXLogReplayRecPtr(&ThisTimeLineID); ReplicationSlotAcquire(NameStr(*name), true); PG_TRY(); { /* restart at slot's confirmed_flush */ ctx = CreateDecodingContext(InvalidXLogRecPtr, options, false, logical_read_local_xlog_page, LogicalOutputPrepareWrite, LogicalOutputWrite, NULL); MemoryContextSwitchTo(oldcontext); /* * Check whether the output plugin writes textual output if that's * what we need. */ if (!binary && ctx->options.output_type !=OUTPUT_PLUGIN_TEXTUAL_OUTPUT) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("logical decoding output plugin \"%s\" produces binary output, but function \"%s\" expects textual data", NameStr(MyReplicationSlot->data.plugin), format_procedure(fcinfo->flinfo->fn_oid)))); ctx->output_writer_private = p; /* * Decoding of WAL must start at restart_lsn so that the entirety of * xacts that committed after the slot's confirmed_flush can be * accumulated into reorder buffers. */ startptr = MyReplicationSlot->data.restart_lsn; CurrentResourceOwner = ResourceOwnerCreate(CurrentResourceOwner, "logical decoding"); /* invalidate non-timetravel entries */ InvalidateSystemCaches(); /* Decode until we run out of records */ while ((startptr != InvalidXLogRecPtr && startptr < end_of_wal) || (ctx->reader->EndRecPtr != InvalidXLogRecPtr && ctx->reader->EndRecPtr < end_of_wal)) { XLogRecord *record; char *errm = NULL; record = XLogReadRecord(ctx->reader, startptr, &errm); if (errm) elog(ERROR, "%s", errm); /* * Now that we've set up the xlog reader state, subsequent calls * pass InvalidXLogRecPtr to say "continue from last record" */ startptr = InvalidXLogRecPtr; /* * The {begin_txn,change,commit_txn}_wrapper callbacks above will * store the description into our tuplestore. */ if (record != NULL) LogicalDecodingProcessRecord(ctx, ctx->reader); /* check limits */ if (upto_lsn != InvalidXLogRecPtr && upto_lsn <= ctx->reader->EndRecPtr) break; if (upto_nchanges != 0 && upto_nchanges <= p->returned_rows) break; CHECK_FOR_INTERRUPTS(); } tuplestore_donestoring(tupstore); CurrentResourceOwner = old_resowner; /* * Next time, start where we left off. (Hunting things, the family * business..) */ if (ctx->reader->EndRecPtr != InvalidXLogRecPtr && confirm) { LogicalConfirmReceivedLocation(ctx->reader->EndRecPtr); /* * If only the confirmed_flush_lsn has changed the slot won't get * marked as dirty by the above. Callers on the walsender * interface are expected to keep track of their own progress and * don't need it written out. But SQL-interface users cannot * specify their own start positions and it's harder for them to * keep track of their progress, so we should make more of an * effort to save it for them. * * Dirty the slot so it's written out at the next checkpoint. * We'll still lose its position on crash, as documented, but it's * better than always losing the position even on clean restart. */ ReplicationSlotMarkDirty(); } /* free context, call shutdown callback */ FreeDecodingContext(ctx); ReplicationSlotRelease(); InvalidateSystemCaches(); } PG_CATCH(); { /* clear all timetravel entries */ InvalidateSystemCaches(); PG_RE_THROW(); } PG_END_TRY(); return (Datum) 0; }
/* * extract_variadic_args * * Extract a set of argument values, types and NULL markers for a given * input function which makes use of a VARIADIC input whose argument list * depends on the caller context. When doing a VARIADIC call, the caller * has provided one argument made of an array of values, so deconstruct the * array data before using it for the next processing. If no VARIADIC call * is used, just fill in the status data based on all the arguments given * by the caller. * * This function returns the number of arguments generated, or -1 in the * case of "VARIADIC NULL". */ int extract_variadic_args(FunctionCallInfo fcinfo, int variadic_start, bool convert_unknown, Datum **args, Oid **types, bool **nulls) { bool variadic = get_fn_expr_variadic(fcinfo->flinfo); Datum *args_res; bool *nulls_res; Oid *types_res; int nargs, i; *args = NULL; *types = NULL; *nulls = NULL; if (variadic) { ArrayType *array_in; Oid element_type; bool typbyval; char typalign; int16 typlen; Assert(PG_NARGS() == variadic_start + 1); if (PG_ARGISNULL(variadic_start)) return -1; array_in = PG_GETARG_ARRAYTYPE_P(variadic_start); element_type = ARR_ELEMTYPE(array_in); get_typlenbyvalalign(element_type, &typlen, &typbyval, &typalign); deconstruct_array(array_in, element_type, typlen, typbyval, typalign, &args_res, &nulls_res, &nargs); /* All the elements of the array have the same type */ types_res = (Oid *) palloc0(nargs * sizeof(Oid)); for (i = 0; i < nargs; i++) types_res[i] = element_type; } else { nargs = PG_NARGS() - variadic_start; Assert(nargs > 0); nulls_res = (bool *) palloc0(nargs * sizeof(bool)); args_res = (Datum *) palloc0(nargs * sizeof(Datum)); types_res = (Oid *) palloc0(nargs * sizeof(Oid)); for (i = 0; i < nargs; i++) { nulls_res[i] = PG_ARGISNULL(i + variadic_start); types_res[i] = get_fn_expr_argtype(fcinfo->flinfo, i + variadic_start); /* * Turn a constant (more or less literal) value that's of unknown * type into text if required. Unknowns come in as a cstring * pointer. Note: for functions declared as taking type "any", the * parser will not do any type conversion on unknown-type literals * (that is, undecorated strings or NULLs). */ if (convert_unknown && types_res[i] == UNKNOWNOID && get_fn_expr_arg_stable(fcinfo->flinfo, i + variadic_start)) { types_res[i] = TEXTOID; if (PG_ARGISNULL(i + variadic_start)) args_res[i] = (Datum) 0; else args_res[i] = CStringGetTextDatum(PG_GETARG_POINTER(i + variadic_start)); } else { /* no conversion needed, just take the datum as given */ args_res[i] = PG_GETARG_DATUM(i + variadic_start); } if (!OidIsValid(types_res[i]) || (convert_unknown && types_res[i] == UNKNOWNOID)) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("could not determine data type for argument %d", i + 1))); } } /* Fill in results */ *args = args_res; *nulls = nulls_res; *types = types_res; return nargs; }
Datum get_power_in(PG_FUNCTION_ARGS) { //the size of the array we are trying to transform int arraySize; int power; int32 duration; float startRange; float endRange; char* freq; text *tempFreq; // a Datum for the constructred and deconstructed input arrays Datum *input_data; // the final result to return and the input to the function ArrayType *input; Oid input_type; //next 6 variables are inputted into get_typlenbyvalalign() and are used for construct and deconstruct array //small int in postgreSQL is int16 int16 input_typelen; bool input_typbyval; char input_typalign; // get input row input = PG_GETARG_ARRAYTYPE_P(0); // get duration duration = PG_GETARG_INT32(1); tempFreq = PG_GETARG_TEXT_P(2); freq = dup_pgtext(tempFreq); //get ranges if(strcmp(freq, "beta") == 0) { startRange = 13; endRange = 30; } else if(strcmp(freq, "alpha") == 0) { startRange = 7.5; endRange = 13; } else if(strcmp(freq, "theta") == 0) { startRange = 4; endRange = 7.5; } else { startRange = 1; endRange = 4; } input_type = ARR_ELEMTYPE(input); //get needed variabels get_typlenbyvalalign(input_type, &input_typelen, &input_typbyval, &input_typalign); // deconstruct inupt array and save the array as Datum input_data deconstruct_array(input, input_type, input_typelen, input_typbyval, input_typalign, &input_data, NULL, &arraySize); // set the in variable to the array we got as input. // the real parts are from the input_data array // the imaginary part is set to 0 power = 0; for (int i = 0; i < arraySize; i++) { if(startRange < i/duration && i/duration <= endRange) { Complex *temp = (Complex*) DatumGetPointer(input_data[i]); power += temp->x*temp->x + temp->y*temp->y; } } // free memory pfree(input_data); // return result PG_RETURN_INT32(power); }
/* * Compute the list of TIDs to be visited, by evaluating the expressions * for them. * * (The result is actually an array, not a list.) */ static void TidListCreate(TidScanState *tidstate) { List *evalList = tidstate->tss_tidquals; ExprContext *econtext = tidstate->ss.ps.ps_ExprContext; BlockNumber nblocks; ItemPointerData *tidList; int numAllocTids; int numTids; ListCell *l; /* * We silently discard any TIDs that are out of range at the time of scan * start. (Since we hold at least AccessShareLock on the table, it won't * be possible for someone to truncate away the blocks we intend to * visit.) */ nblocks = RelationGetNumberOfBlocks(tidstate->ss.ss_currentRelation); /* * We initialize the array with enough slots for the case that all quals * are simple OpExprs or CurrentOfExprs. If there are any * ScalarArrayOpExprs, we may have to enlarge the array. */ numAllocTids = list_length(evalList); tidList = (ItemPointerData *) palloc(numAllocTids * sizeof(ItemPointerData)); numTids = 0; tidstate->tss_isCurrentOf = false; foreach(l, evalList) { ExprState *exstate = (ExprState *) lfirst(l); Expr *expr = exstate->expr; ItemPointer itemptr; bool isNull; if (is_opclause(expr)) { FuncExprState *fexstate = (FuncExprState *) exstate; Node *arg1; Node *arg2; arg1 = get_leftop(expr); arg2 = get_rightop(expr); if (IsCTIDVar(arg1)) exstate = (ExprState *) lsecond(fexstate->args); else if (IsCTIDVar(arg2)) exstate = (ExprState *) linitial(fexstate->args); else elog(ERROR, "could not identify CTID variable"); itemptr = (ItemPointer) DatumGetPointer(ExecEvalExprSwitchContext(exstate, econtext, &isNull, NULL)); if (!isNull && ItemPointerIsValid(itemptr) && ItemPointerGetBlockNumber(itemptr) < nblocks) { if (numTids >= numAllocTids) { numAllocTids *= 2; tidList = (ItemPointerData *) repalloc(tidList, numAllocTids * sizeof(ItemPointerData)); } tidList[numTids++] = *itemptr; } } else if (expr && IsA(expr, ScalarArrayOpExpr)) { ScalarArrayOpExprState *saexstate = (ScalarArrayOpExprState *) exstate; Datum arraydatum; ArrayType *itemarray; Datum *ipdatums; bool *ipnulls; int ndatums; int i; exstate = (ExprState *) lsecond(saexstate->fxprstate.args); arraydatum = ExecEvalExprSwitchContext(exstate, econtext, &isNull, NULL); if (isNull) continue; itemarray = DatumGetArrayTypeP(arraydatum); deconstruct_array(itemarray, TIDOID, sizeof(ItemPointerData), false, 's', &ipdatums, &ipnulls, &ndatums); if (numTids + ndatums > numAllocTids) { numAllocTids = numTids + ndatums; tidList = (ItemPointerData *) repalloc(tidList, numAllocTids * sizeof(ItemPointerData)); } for (i = 0; i < ndatums; i++) { if (!ipnulls[i]) { itemptr = (ItemPointer) DatumGetPointer(ipdatums[i]); if (ItemPointerIsValid(itemptr) && ItemPointerGetBlockNumber(itemptr) < nblocks) tidList[numTids++] = *itemptr; } } pfree(ipdatums); pfree(ipnulls); } else if (expr && IsA(expr, CurrentOfExpr)) { CurrentOfExpr *cexpr = (CurrentOfExpr *) expr; ItemPointerData cursor_tid; if (execCurrentOf(cexpr, econtext, RelationGetRelid(tidstate->ss.ss_currentRelation), &cursor_tid)) { if (numTids >= numAllocTids) { numAllocTids *= 2; tidList = (ItemPointerData *) repalloc(tidList, numAllocTids * sizeof(ItemPointerData)); } tidList[numTids++] = cursor_tid; tidstate->tss_isCurrentOf = true; } } else elog(ERROR, "could not identify CTID expression"); }
Datum array_multi_index( PG_FUNCTION_ARGS ) { if( PG_ARGISNULL(0) ) { PG_RETURN_NULL(); } if( PG_ARGISNULL(1) ) { PG_RETURN_NULL(); } ArrayType* values = PG_GETARG_ARRAYTYPE_P( 0 ); ArrayType* indices = PG_GETARG_ARRAYTYPE_P( 1 ); Oid values_type = ARR_ELEMTYPE( values ); int16 values_width; bool values_passbyvalue; char values_alignmentcode; Datum* values_content; bool* values_nullflags; int values_length; get_typlenbyvalalign( values_type, &values_width, &values_passbyvalue, &values_alignmentcode ); deconstruct_array( values, values_type, values_width, values_passbyvalue, values_alignmentcode, &values_content, &values_nullflags, &values_length ); Oid indices_type = ARR_ELEMTYPE( indices ); int16 indices_width; bool indices_passbyvalue; char indices_alignmentcode; Datum* indices_content; bool* indices_nullflags; int indices_length; get_typlenbyvalalign( indices_type, &indices_width, &indices_passbyvalue, &indices_alignmentcode ); deconstruct_array( indices, indices_type, indices_width, indices_passbyvalue, indices_alignmentcode, &indices_content, &indices_nullflags, &indices_length ); Oid results_type = values_type; int16 results_width = values_width; bool results_passbyvalue = values_passbyvalue; char results_alignmentcode = values_alignmentcode; Datum* results_content = (Datum *)palloc( sizeof(Datum) * indices_length ); bool* results_nullflags = (bool *)palloc0( sizeof(bool) * indices_length ); int results_length = indices_length; int i; for( i = 0; i < indices_length; ++i ) { if( indices_nullflags[i] ) { results_content[i] = 0; results_nullflags[i] = true; } else if( indices_content[i] - 1 >= (long unsigned)values_length ) { results_content[i] = 0; results_nullflags[i] = true; } else { results_content[i] = values_content[ indices_content[i] - 1 ]; results_nullflags[i] = values_nullflags[ indices_content[i] - 1 ]; } } int dims[1]; int lbs[1]; dims[0] = results_length; lbs[0] = 1; ArrayType* results = construct_md_array( results_content, results_nullflags, 1, dims, lbs, results_type, results_width, results_passbyvalue, results_alignmentcode ); pfree( results_content ); pfree( results_nullflags ); PG_RETURN_ARRAYTYPE_P( results ); }
/* * Transform a relation options list (list of DefElem) into the text array * format that is kept in pg_class.reloptions, including only those options * that are in the passed namespace. The output values do not include the * namespace. * * This is used for three cases: CREATE TABLE/INDEX, ALTER TABLE SET, and * ALTER TABLE RESET. In the ALTER cases, oldOptions is the existing * reloptions value (possibly NULL), and we replace or remove entries * as needed. * * If ignoreOids is true, then we should ignore any occurrence of "oids" * in the list (it will be or has been handled by interpretOidsOption()). * * Note that this is not responsible for determining whether the options * are valid, but it does check that namespaces for all the options given are * listed in validnsps. The NULL namespace is always valid and need not be * explicitly listed. Passing a NULL pointer means that only the NULL * namespace is valid. * * Both oldOptions and the result are text arrays (or NULL for "default"), * but we declare them as Datums to avoid including array.h in reloptions.h. */ Datum transformRelOptions(Datum oldOptions, List *defList, char *namspace, char *validnsps[], bool ignoreOids, bool isReset) { Datum result; ArrayBuildState *astate; ListCell *cell; /* no change if empty list */ if (defList == NIL) return oldOptions; /* We build new array using accumArrayResult */ astate = NULL; /* Copy any oldOptions that aren't to be replaced */ if (PointerIsValid(DatumGetPointer(oldOptions))) { ArrayType *array = DatumGetArrayTypeP(oldOptions); Datum *oldoptions; int noldoptions; int i; deconstruct_array(array, TEXTOID, -1, false, 'i', &oldoptions, NULL, &noldoptions); for (i = 0; i < noldoptions; i++) { text *oldoption = DatumGetTextP(oldoptions[i]); char *text_str = VARDATA(oldoption); int text_len = VARSIZE(oldoption) - VARHDRSZ; /* Search for a match in defList */ foreach(cell, defList) { DefElem *def = (DefElem *) lfirst(cell); int kw_len; /* ignore if not in the same namespace */ if (namspace == NULL) { if (def->defnamespace != NULL) continue; } else if (def->defnamespace == NULL) continue; else if (pg_strcasecmp(def->defnamespace, namspace) != 0) continue; kw_len = strlen(def->defname); if (text_len > kw_len && text_str[kw_len] == '=' && pg_strncasecmp(text_str, def->defname, kw_len) == 0) break; } if (!cell) { /* No match, so keep old option */ astate = accumArrayResult(astate, oldoptions[i], false, TEXTOID, CurrentMemoryContext); } } }
/* * Helper function for pgp_armor. Converts arrays of keys and values into * plain C arrays, and checks that they don't contain invalid characters. */ static int parse_key_value_arrays(ArrayType *key_array, ArrayType *val_array, char ***p_keys, char ***p_values) { int nkdims = ARR_NDIM(key_array); int nvdims = ARR_NDIM(val_array); char **keys, **values; Datum *key_datums, *val_datums; bool *key_nulls, *val_nulls; int key_count, val_count; int i; if (nkdims > 1 || nkdims != nvdims) ereport(ERROR, (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR), errmsg("wrong number of array subscripts"))); if (nkdims == 0) return 0; deconstruct_array(key_array, TEXTOID, -1, false, 'i', &key_datums, &key_nulls, &key_count); deconstruct_array(val_array, TEXTOID, -1, false, 'i', &val_datums, &val_nulls, &val_count); if (key_count != val_count) ereport(ERROR, (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR), errmsg("mismatched array dimensions"))); keys = (char **) palloc(sizeof(char *) * key_count); values = (char **) palloc(sizeof(char *) * val_count); for (i = 0; i < key_count; i++) { char *v; /* Check that the key doesn't contain anything funny */ if (key_nulls[i]) ereport(ERROR, (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED), errmsg("null value not allowed for header key"))); v = TextDatumGetCString(key_datums[i]); if (!string_is_ascii(v)) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("header key must not contain non-ASCII characters"))); if (strstr(v, ": ")) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("header key must not contain \": \""))); if (strchr(v, '\n')) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("header key must not contain newlines"))); keys[i] = v; /* And the same for the value */ if (val_nulls[i]) ereport(ERROR, (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED), errmsg("null value not allowed for header value"))); v = TextDatumGetCString(val_datums[i]); if (!string_is_ascii(v)) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("header value must not contain non-ASCII characters"))); if (strchr(v, '\n')) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("header value must not contain newlines"))); values[i] = v; } *p_keys = keys; *p_values = values; return key_count; }
/* * array_element - get an iterator to all the elements in the array * * The short: deconstruct and build a list of element instances. */ static PyObj array_elements(PyObj self) { PyObj element_type; volatile PyObj rob = NULL; PyPgTypeInfo typinfo; element_type = PyPgType_GetElementType(Py_TYPE(self)); typinfo = PyPgTypeInfo(element_type); /* * Multiple dimensions, so get a slice. */ PG_TRY(); { Datum *elements; bool *nulls; int i, nelems; ArrayType *at; at = DatumGetArrayTypeP(PyPgObject_GetDatum(self)); deconstruct_array(at, typinfo->typoid, typinfo->typlen, typinfo->typbyval, typinfo->typalign, &elements, &nulls, &nelems ); rob = PyList_New(nelems); for (i = 0; i < nelems; ++i) { PyObj ob; if (nulls[i]) { ob = Py_None; Py_INCREF(ob); } else ob = PyPgObject_New(element_type, elements[i]); if (ob == NULL) { Py_DECREF(rob); rob = NULL; break; } PyList_SET_ITEM(rob, i, ob); } pfree(elements); pfree(nulls); } PG_CATCH(); { Py_XDECREF(rob); rob = NULL; PyErr_SetPgError(false); return(NULL); } PG_END_TRY(); return(rob); }
Datum gin_extract_hstore_query(PG_FUNCTION_ARGS) { int32 *nentries = (int32 *) PG_GETARG_POINTER(1); StrategyNumber strategy = PG_GETARG_UINT16(2); int32 *searchMode = (int32 *) PG_GETARG_POINTER(6); Datum *entries; if (strategy == HStoreContainsStrategyNumber) { /* Query is an hstore, so just apply gin_extract_hstore... */ entries = (Datum *) DatumGetPointer(DirectFunctionCall2(gin_extract_hstore, PG_GETARG_DATUM(0), PointerGetDatum(nentries))); /* ... except that "contains {}" requires a full index scan */ if (entries == NULL) *searchMode = GIN_SEARCH_MODE_ALL; } else if (strategy == HStoreExistsStrategyNumber) { text *query = PG_GETARG_TEXT_PP(0); text *item; *nentries = 1; entries = (Datum *) palloc(sizeof(Datum)); item = makeitem(VARDATA_ANY(query), VARSIZE_ANY_EXHDR(query), KEYFLAG); entries[0] = PointerGetDatum(item); } else if (strategy == HStoreExistsAnyStrategyNumber || strategy == HStoreExistsAllStrategyNumber) { ArrayType *query = PG_GETARG_ARRAYTYPE_P(0); Datum *key_datums; bool *key_nulls; int key_count; int i, j; text *item; deconstruct_array(query, TEXTOID, -1, false, 'i', &key_datums, &key_nulls, &key_count); entries = (Datum *) palloc(sizeof(Datum) * key_count); for (i = 0, j = 0; i < key_count; ++i) { /* Nulls in the array are ignored, cf hstoreArrayToPairs */ if (key_nulls[i]) continue; item = makeitem(VARDATA(key_datums[i]), VARSIZE(key_datums[i]) - VARHDRSZ, KEYFLAG); entries[j++] = PointerGetDatum(item); } *nentries = j; /* ExistsAll with no keys should match everything */ if (j == 0 && strategy == HStoreExistsAllStrategyNumber) *searchMode = GIN_SEARCH_MODE_ALL; } else { elog(ERROR, "unrecognized strategy number: %d", strategy); entries = NULL; /* keep compiler quiet */ } PG_RETURN_POINTER(entries); }
/* * Turn an array into JSON. */ static void array_to_json_internal(Datum array, StringInfo result, bool use_line_feeds) { 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, typdelim; Oid typioparam; Oid typoutputfunc; TYPCATEGORY tcategory; Oid castfunc = InvalidOid; ndim = ARR_NDIM(v); dim = ARR_DIMS(v); nitems = ArrayGetNItems(ndim, dim); if (nitems <= 0) { appendStringInfoString(result, "[]"); return; } get_type_io_data(element_type, IOFunc_output, &typlen, &typbyval, &typalign, &typdelim, &typioparam, &typoutputfunc); if (element_type > FirstNormalObjectId) { HeapTuple tuple; Form_pg_cast castForm; tuple = SearchSysCache2(CASTSOURCETARGET, ObjectIdGetDatum(element_type), ObjectIdGetDatum(JSONOID)); if (HeapTupleIsValid(tuple)) { castForm = (Form_pg_cast) GETSTRUCT(tuple); if (castForm->castmethod == COERCION_METHOD_FUNCTION) castfunc = typoutputfunc = castForm->castfunc; ReleaseSysCache(tuple); } } deconstruct_array(v, element_type, typlen, typbyval, typalign, &elements, &nulls, &nitems); if (castfunc != InvalidOid) tcategory = TYPCATEGORY_JSON_CAST; else if (element_type == RECORDOID) tcategory = TYPCATEGORY_COMPOSITE; else if (element_type == JSONOID) tcategory = TYPCATEGORY_JSON; else tcategory = TypeCategory(element_type); array_dim_to_json(result, 0, ndim, dim, elements, nulls, &count, tcategory, typoutputfunc, use_line_feeds); pfree(elements); pfree(nulls); }
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 summarize_variant( PG_FUNCTION_ARGS ) { if( PG_ARGISNULL(0) ) { ereport( ERROR, (errmsg("summarize_variant: array of values must be non-null")) ); } TupleDesc tuple_desc; if( get_call_result_type( fcinfo, NULL, &tuple_desc ) != TYPEFUNC_COMPOSITE ) { ereport( ERROR, (errmsg("summarize_variant: function returning composite type called in context that cannot accept composite type")) ); } tuple_desc = BlessTupleDesc( tuple_desc ); ArrayType* values = PG_GETARG_ARRAYTYPE_P( 0 ); Oid values_type = ARR_ELEMTYPE( values ); int16 values_width; bool values_passbyvalue; char values_alignmentcode; Datum* values_content; bool* values_nullflags; int values_length; get_typlenbyvalalign( values_type, &values_width, &values_passbyvalue, &values_alignmentcode ); deconstruct_array( values, values_type, values_width, values_passbyvalue, values_alignmentcode, &values_content, &values_nullflags, &values_length ); size_t composite_type_elements = 9; Datum* results_content = (Datum*)palloc0( sizeof(Datum) * composite_type_elements ); bool* results_nullflags = (bool*)palloc0( sizeof(bool) * composite_type_elements ); // FORMAT // [0] - call rate // [1] - subset call rate // [2] - alternate allele frequency // [3] - sample alternate allele frequency if( !PG_ARGISNULL(1) ) { ArrayType* indices = PG_GETARG_ARRAYTYPE_P(1); Oid indices_type = ARR_ELEMTYPE( indices ); int16 indices_width; bool indices_passbyvalue; char indices_alignmentcode; Datum* indices_content; bool* indices_nullflags; int indices_length; get_typlenbyvalalign( indices_type, &indices_width, &indices_passbyvalue, &indices_alignmentcode ); deconstruct_array( indices, indices_type, indices_width, indices_passbyvalue, indices_alignmentcode, &indices_content, &indices_nullflags, &indices_length ); int count = 0; int nonnull_count = 0; int alternate_count = 0; int i; for( i = 0; i < indices_length; ++i ) { if( !indices_nullflags[i] && indices_content[i] - 1 < (long unsigned)values_length ) { ++count; if( !values_nullflags[ indices_content[i] - 1 ] ) { ++nonnull_count; alternate_count += values_content[ indices_content[i] - 1 ]; } } } results_content[1] = Float4GetDatum( nonnull_count / (float4)count ); results_content[3] = Float4GetDatum( nonnull_count == 0 ? 0 : alternate_count/(2.0*nonnull_count) ); results_nullflags[3] = nonnull_count == 0; } else { results_nullflags[1] = true; results_nullflags[3] = true; } int count = values_length; unsigned int nonnull_count = 0; unsigned int alternate_count = 0; int i; for( i = 0; i < values_length; ++i ) { if( !values_nullflags[i] ) { ++nonnull_count; alternate_count += values_content[i]; } } results_content[0] = Float4GetDatum( nonnull_count / (float4)count ); results_content[2] = Float4GetDatum( nonnull_count == 0 ? 0 : alternate_count/(2.0*nonnull_count) ); results_nullflags[2] = nonnull_count == 0; HeapTuple tuple = heap_form_tuple( tuple_desc, results_content, results_nullflags ); Datum result = HeapTupleGetDatum( tuple ); PG_RETURN_DATUM( 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 gin_extract_jsonb_query(PG_FUNCTION_ARGS) { int32 *nentries = (int32 *) PG_GETARG_POINTER(1); StrategyNumber strategy = PG_GETARG_UINT16(2); int32 *searchMode = (int32 *) PG_GETARG_POINTER(6); Datum *entries; if (strategy == JsonbContainsStrategyNumber) { /* Query is a jsonb, so just apply gin_extract_jsonb... */ entries = (Datum *) DatumGetPointer(DirectFunctionCall2(gin_extract_jsonb, PG_GETARG_DATUM(0), PointerGetDatum(nentries))); /* ...although "contains {}" requires a full index scan */ if (*nentries == 0) *searchMode = GIN_SEARCH_MODE_ALL; } else if (strategy == JsonbExistsStrategyNumber) { /* Query is a text string, which we treat as a key */ text *query = PG_GETARG_TEXT_PP(0); *nentries = 1; entries = (Datum *) palloc(sizeof(Datum)); entries[0] = make_text_key(JGINFLAG_KEY, VARDATA_ANY(query), VARSIZE_ANY_EXHDR(query)); } else if (strategy == JsonbExistsAnyStrategyNumber || strategy == JsonbExistsAllStrategyNumber) { /* Query is a text array; each element is treated as a key */ ArrayType *query = PG_GETARG_ARRAYTYPE_P(0); Datum *key_datums; bool *key_nulls; int key_count; int i, j; deconstruct_array(query, TEXTOID, -1, false, 'i', &key_datums, &key_nulls, &key_count); entries = (Datum *) palloc(sizeof(Datum) * key_count); for (i = 0, j = 0; i < key_count; i++) { /* Nulls in the array are ignored */ if (key_nulls[i]) continue; entries[j++] = make_text_key(JGINFLAG_KEY, VARDATA_ANY(key_datums[i]), VARSIZE_ANY_EXHDR(key_datums[i])); } *nentries = j; /* ExistsAll with no keys should match everything */ if (j == 0 && strategy == JsonbExistsAllStrategyNumber) *searchMode = GIN_SEARCH_MODE_ALL; } else { elog(ERROR, "unrecognized strategy number: %d", strategy); entries = NULL; /* keep compiler quiet */ } PG_RETURN_POINTER(entries); }
Datum tsa_rewrite_accum(PG_FUNCTION_ARGS) { TSQuery acc; ArrayType *qa; TSQuery q; QTNode *qex = NULL, *subs = NULL, *acctree = NULL; bool isfind = false; Datum *elemsp; int nelemsp; MemoryContext aggcontext; MemoryContext oldcontext; if (!AggCheckCallContext(fcinfo, &aggcontext)) elog(ERROR, "tsa_rewrite_accum called in non-aggregate context"); if (PG_ARGISNULL(0) || PG_GETARG_POINTER(0) == NULL) { acc = (TSQuery) MemoryContextAlloc(aggcontext, HDRSIZETQ); SET_VARSIZE(acc, HDRSIZETQ); acc->size = 0; } else acc = PG_GETARG_TSQUERY(0); if (PG_ARGISNULL(1) || PG_GETARG_POINTER(1) == NULL) PG_RETURN_TSQUERY(acc); else qa = PG_GETARG_ARRAYTYPE_P_COPY(1); if (ARR_NDIM(qa) != 1) elog(ERROR, "array must be one-dimensional, not %d dimensions", ARR_NDIM(qa)); if (ArrayGetNItems(ARR_NDIM(qa), ARR_DIMS(qa)) != 3) elog(ERROR, "array must have three elements"); if (ARR_ELEMTYPE(qa) != TSQUERYOID) elog(ERROR, "array must contain tsquery elements"); deconstruct_array(qa, TSQUERYOID, -1, false, 'i', &elemsp, NULL, &nelemsp); q = DatumGetTSQuery(elemsp[0]); if (q->size == 0) { pfree(elemsp); PG_RETURN_POINTER(acc); } if (!acc->size) { if (VARSIZE(acc) > HDRSIZETQ) { 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 = DatumGetTSQuery(elemsp[1]); if (q->size == 0) { pfree(elemsp); PG_RETURN_POINTER(acc); } qex = QT2QTN(GETQUERY(q), GETOPERAND(q)); QTNTernary(qex); QTNSort(qex); q = DatumGetTSQuery(elemsp[2]); if (q->size) subs = QT2QTN(GETQUERY(q), GETOPERAND(q)); acctree = findsubquery(acctree, qex, subs, &isfind); if (isfind || !acc->size) { /* pfree( acc ); do not pfree(p), because nodeAgg.c will */ if (acctree) { QTNBinary(acctree); oldcontext = MemoryContextSwitchTo(aggcontext); acc = QTN2QT(acctree); MemoryContextSwitchTo(oldcontext); } else { acc = (TSQuery) MemoryContextAlloc(aggcontext, HDRSIZETQ); SET_VARSIZE(acc, HDRSIZETQ); acc->size = 0; } } pfree(elemsp); QTNFree(qex); QTNFree(subs); QTNFree(acctree); PG_RETURN_TSQUERY(acc); }
/* * build_function_result_tupdesc_d * * Build a RECORD function's tupledesc from the pg_proc proallargtypes, * proargmodes, and proargnames arrays. This is split out for the * convenience of ProcedureCreate, which needs to be able to compute the * tupledesc before actually creating the function. * * Returns NULL if there are not at least two OUT or INOUT arguments. */ TupleDesc build_function_result_tupdesc_d(Datum proallargtypes, Datum proargmodes, Datum proargnames) { TupleDesc desc; ArrayType *arr; int numargs; Oid *argtypes; char *argmodes; Datum *argnames = NULL; Oid *outargtypes; char **outargnames; int numoutargs; int nargnames; int i; /* Can't have output args if columns are null */ if (proallargtypes == PointerGetDatum(NULL) || proargmodes == PointerGetDatum(NULL)) return NULL; /* * 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(proallargtypes); /* ensure not toasted */ numargs = ARR_DIMS(arr)[0]; if (ARR_NDIM(arr) != 1 || numargs < 0 || ARR_HASNULL(arr) || ARR_ELEMTYPE(arr) != OIDOID) elog(ERROR, "proallargtypes is not a 1-D Oid array"); argtypes = (Oid *) ARR_DATA_PTR(arr); arr = DatumGetArrayTypeP(proargmodes); /* ensure not toasted */ if (ARR_NDIM(arr) != 1 || ARR_DIMS(arr)[0] != numargs || ARR_HASNULL(arr) || ARR_ELEMTYPE(arr) != CHAROID) elog(ERROR, "proargmodes is not a 1-D char array"); argmodes = (char *) ARR_DATA_PTR(arr); if (proargnames != PointerGetDatum(NULL)) { arr = DatumGetArrayTypeP(proargnames); /* ensure not toasted */ if (ARR_NDIM(arr) != 1 || ARR_DIMS(arr)[0] != numargs || ARR_HASNULL(arr) || ARR_ELEMTYPE(arr) != TEXTOID) elog(ERROR, "proargnames is not a 1-D text array"); deconstruct_array(arr, TEXTOID, -1, false, 'i', &argnames, NULL, &nargnames); Assert(nargnames == numargs); } /* zero elements probably shouldn't happen, but handle it gracefully */ if (numargs <= 0) return NULL; /* extract output-argument types and names */ outargtypes = (Oid *) palloc(numargs * sizeof(Oid)); outargnames = (char **) palloc(numargs * sizeof(char *)); numoutargs = 0; for (i = 0; i < numargs; i++) { char *pname; if (argmodes[i] == PROARGMODE_IN || argmodes[i] == PROARGMODE_VARIADIC) continue; Assert(argmodes[i] == PROARGMODE_OUT || argmodes[i] == PROARGMODE_INOUT || argmodes[i] == PROARGMODE_TABLE); outargtypes[numoutargs] = argtypes[i]; if (argnames) pname = TextDatumGetCString(argnames[i]); else pname = NULL; if (pname == NULL || pname[0] == '\0') { /* Parameter is not named, so gin up a column name */ pname = psprintf("column%d", numoutargs + 1); } outargnames[numoutargs] = pname; numoutargs++; } /* * If there is no output argument, or only one, the function does not * return tuples. */ if (numoutargs < 2) return NULL; desc = CreateTemplateTupleDesc(numoutargs, false); for (i = 0; i < numoutargs; i++) { TupleDescInitEntry(desc, i + 1, outargnames[i], outargtypes[i], -1, 0); } return desc; }
Datum generate_sparse_vector(PG_FUNCTION_ARGS) { SvecType *output_sfv; int16_t typlen; bool typbyval; char typalign; bool *nulls; if (PG_NARGS() != 3) elog(ERROR, "Invalid number of arguments."); ArrayType *term_index = PG_GETARG_ARRAYTYPE_P(0); ArrayType *term_count = PG_GETARG_ARRAYTYPE_P(1); int64_t dict_size = PG_GETARG_INT64(2); /* Check if arrays have null entries */ if (ARR_HASNULL(term_index) || ARR_HASNULL(term_count)) elog(ERROR, "One or both of the argument arrays has one or more null entries."); if (dict_size <= 0) elog(ERROR, "Dictionary size cannot be zero or negative."); /* Check if any of the argument arrays is empty */ if ((ARR_NDIM(term_index) == 0) || (ARR_NDIM(term_count) == 0)) elog(ERROR, "One or more argument arrays is empty."); int term_index_nelems = ARR_DIMS(term_index)[0]; int term_count_nelems = ARR_DIMS(term_count)[0]; /* If no. of elements in the arrays are not equal, throw an error */ if (term_index_nelems != term_count_nelems) elog(ERROR, "No. of elements in the argument arrays are not equal."); Datum *term_index_data; Datum *term_count_data; /* Deconstruct the arrays */ get_typlenbyvalalign(INT8OID, &typlen, &typbyval, &typalign); deconstruct_array(term_index, INT8OID, typlen, typbyval, typalign, &term_index_data, &nulls, &term_index_nelems); get_typlenbyvalalign(FLOAT8OID, &typlen, &typbyval, &typalign); deconstruct_array(term_count, FLOAT8OID, typlen, typbyval, typalign, &term_count_data, &nulls, &term_count_nelems); /* Check if term index array has indexes in proper order or not */ for(int i = 0; i < term_index_nelems; i++) { if (DatumGetInt64(term_index_data[i]) < 0 || DatumGetInt64(term_index_data[i]) >= dict_size) elog(ERROR, "Term indexes must range from 0 to total number of elements in the dictonary - 1."); } float8 *histogram = (float8 *)palloc0(sizeof(float8) * dict_size); for (int k = 0; k < dict_size; k++) { histogram[k] = 0; } for (int i = 0; i < term_index_nelems; i++) { uint64_t idx = DatumGetInt64(term_index_data[i]); histogram[idx] += DatumGetFloat8(term_count_data[i]); } output_sfv = svec_from_float8arr(histogram, dict_size); pfree(histogram); PG_RETURN_POINTER(output_sfv); }
/* * get_func_input_arg_names * * Extract the names of input arguments only, given a function's * proargnames and proargmodes entries in Datum form. * * Returns the number of input arguments, which is the length of the * palloc'd array returned to *arg_names. Entries for unnamed args * are set to NULL. You don't get anything if proargnames is NULL. */ int get_func_input_arg_names(Datum proargnames, Datum proargmodes, char ***arg_names) { ArrayType *arr; int numargs; Datum *argnames; char *argmodes; char **inargnames; int numinargs; int i; /* Do nothing if null proargnames */ if (proargnames == PointerGetDatum(NULL)) { *arg_names = NULL; return 0; } /* * We expect the arrays to be 1-D arrays of the right types; verify that. * For proargmodes, 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(proargnames); /* ensure not toasted */ if (ARR_NDIM(arr) != 1 || ARR_HASNULL(arr) || ARR_ELEMTYPE(arr) != TEXTOID) elog(ERROR, "proargnames is not a 1-D text array"); deconstruct_array(arr, TEXTOID, -1, false, 'i', &argnames, NULL, &numargs); if (proargmodes != PointerGetDatum(NULL)) { arr = DatumGetArrayTypeP(proargmodes); /* ensure not toasted */ if (ARR_NDIM(arr) != 1 || ARR_DIMS(arr)[0] != numargs || ARR_HASNULL(arr) || ARR_ELEMTYPE(arr) != CHAROID) elog(ERROR, "proargmodes is not a 1-D char array"); argmodes = (char *) ARR_DATA_PTR(arr); } else argmodes = NULL; /* zero elements probably shouldn't happen, but handle it gracefully */ if (numargs <= 0) { *arg_names = NULL; return 0; } /* extract input-argument names */ inargnames = (char **) palloc(numargs * sizeof(char *)); numinargs = 0; for (i = 0; i < numargs; i++) { if (argmodes == NULL || argmodes[i] == PROARGMODE_IN || argmodes[i] == PROARGMODE_INOUT || argmodes[i] == PROARGMODE_VARIADIC) { char *pname = TextDatumGetCString(argnames[i]); if (pname[0] != '\0') inargnames[numinargs] = pname; else inargnames[numinargs] = NULL; numinargs++; } } *arg_names = inargnames; return numinargs; }
Datum get_peak_in_range(PG_FUNCTION_ARGS) { //the size of the array we are trying to transform int arraySize; int peak; bool flag; int32 duration; float startRange; float endRange; // a Datum for the constructred and deconstructed input arrays Datum *input_data; // the final result to return and the input to the function ArrayType *input; Oid input_type; //next 6 variables are inputted into get_typlenbyvalalign() and are used for construct and deconstruct array //small int in postgreSQL is int16 int16 input_typelen; bool input_typbyval; char input_typalign; // get input row input = PG_GETARG_ARRAYTYPE_P(0); // get duration duration = PG_GETARG_INT32(1); // get ranges startRange = PG_GETARG_FLOAT8(2); endRange = PG_GETARG_FLOAT8(3); input_type = ARR_ELEMTYPE(input); //get needed variabels get_typlenbyvalalign(input_type, &input_typelen, &input_typbyval, &input_typalign); // deconstruct inupt array and save the array as Datum input_data deconstruct_array(input, input_type, input_typelen, input_typbyval, input_typalign, &input_data, NULL, &arraySize); // set the in variable to the array we got as input. // the real parts are from the input_data array // the imaginary part is set to 0 peak = 0; flag = false; for (int i = 0; i < arraySize; i++) { if(startRange < i/duration && i/duration <= endRange) { Complex *temp = (Complex*) DatumGetPointer(input_data[i]); int32 amp = temp->x*temp->x + temp->y*temp->y; if(!flag) { peak = amp; flag = true; } if(amp > peak){ peak = amp; } } } // free memory pfree(input_data); // return result PG_RETURN_INT32(peak); }