Datum turn_restrict_shortest_path_edge(PG_FUNCTION_ARGS) { FuncCallContext *funcctx; uint32_t call_cntr; uint32_t max_calls; TupleDesc tuple_desc; path_element_t *path; char * sql; // stuff done only on the first call of the function if (SRF_IS_FIRSTCALL()) { MemoryContext oldcontext; size_t path_count = 0; #ifdef DEBUG int ret = -1; #endif int i; double s_pos; double e_pos; // create a function context for cross-call persistence funcctx = SRF_FIRSTCALL_INIT(); // switch to memory context appropriate for multiple function calls oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx); // verify that the first 5 args are not NULL for (i=0; i<7; i++) { if(i==2 || i==4) continue; if(PG_ARGISNULL(i)) { elog(ERROR, "turn_restrict_shortest_path(): Argument %i may not be NULL", i+1); } } if (PG_ARGISNULL(2)) s_pos = 0.5; else { s_pos = PG_GETARG_FLOAT8(2); if (s_pos < 0.0) s_pos = 0.5; if (s_pos > 1.0) s_pos = 0.5; } if (PG_ARGISNULL(4)) e_pos = 0.5; else { e_pos = PG_GETARG_FLOAT8(4); if (e_pos < 0.0) e_pos = 0.5; if (e_pos > 1.0) e_pos = 0.5; } if (PG_ARGISNULL(7)) sql = NULL; else { sql = pgr_text2char(PG_GETARG_TEXT_P(7)); if (strlen(sql) == 0) sql = NULL; } PGR_DBG("Calling compute_trsp"); #ifdef DEBUG ret = #endif compute_trsp(pgr_text2char(PG_GETARG_TEXT_P(0)), 0, //sdo edge PG_GETARG_INT32(1), s_pos, PG_GETARG_INT32(3), e_pos, PG_GETARG_BOOL(5), PG_GETARG_BOOL(6), sql, &path, &path_count); #ifdef DEBUG double total_cost = 0; PGR_DBG("Ret is %i", ret); if (ret >= 0) { int i; for (i = 0; i < path_count; i++) { // PGR_DBG("Step %i vertex_id %i ", i, path[i].vertex_id); // PGR_DBG(" edge_id %i ", path[i].edge_id); // PGR_DBG(" cost %f ", path[i].cost); total_cost+=path[i].cost; } } PGR_DBG("Total cost is: %f",total_cost); #endif // total number of tuples to be returned funcctx->max_calls = (uint32_t)path_count; funcctx->user_fctx = path; funcctx->tuple_desc = BlessTupleDesc(RelationNameGetTupleDesc("pgr_costResult")); MemoryContextSwitchTo(oldcontext); } // stuff done on every call of the function funcctx = SRF_PERCALL_SETUP(); call_cntr = funcctx->call_cntr; max_calls = funcctx->max_calls; tuple_desc = funcctx->tuple_desc; path = (path_element_t*) funcctx->user_fctx; if (call_cntr < max_calls) // do when there is more left to send { HeapTuple tuple; Datum result; Datum *values; bool* nulls; values = palloc(4 * sizeof(Datum)); nulls = palloc(4 * sizeof(bool)); values[0] = Int32GetDatum(call_cntr); nulls[0] = false; values[1] = Int32GetDatum(path[call_cntr].vertex_id); nulls[1] = false; values[2] = Int32GetDatum(path[call_cntr].edge_id); nulls[2] = false; values[3] = Float8GetDatum(path[call_cntr].cost); nulls[3] = false; tuple = heap_form_tuple(tuple_desc, values, nulls); // make the tuple into a datum result = HeapTupleGetDatum(tuple); // clean up (this is not really necessary) pfree(values); pfree(nulls); SRF_RETURN_NEXT(funcctx, result); } else // do when there is no more left { PGR_DBG("Going to free path"); if (path) free(path); SRF_RETURN_DONE(funcctx); } }
Datum fft_agg_finalfn(PG_FUNCTION_ARGS) { ArrayType *input, *result = NULL; Oid eltype; int16 typlen; bool typbyval; char typalign; Datum *data; int i, n; int ndims, *dims; kiss_fft_cfg cfg; kiss_fft_cpx *cx_in = NULL, *cx_out = NULL; if (!AggCheckCallContext(fcinfo, NULL)) elog(ERROR, "fft_agg_finalfn() Not aggregate context"); if (PG_ARGISNULL(0)) elog(ERROR, "fft_agg_finalfn() args cannot be null"); /* elog(INFO, "fft_agg_finalfn() nargs: %d", PG_NARGS()); */ /* elog(INFO, "p1 %p", PG_GETARG_POINTER(0)); */ /* state array */ input = PG_GETARG_ARRAYTYPE_P(0); /* elog(INFO, "arg 0 array type: 0x%p", input); */ /* get various pieces of data from the input array */ ndims = ARR_NDIM(input); dims = ARR_DIMS(input); eltype = ARR_ELEMTYPE(input); /* elog(INFO, "input ndims: %d dims: %d elemtype: %d", ndims, *dims, eltype); */ Assert(ndims == 1); Assert(eltype == FLOAT4OID); /* get input array element type */ get_typlenbyvalalign(eltype, &typlen, &typbyval, &typalign); /* elog(INFO, "olen: %d obyval: %d align: %d", typlen, typbyval, typalign); */ /* get src data */ deconstruct_array(input, eltype, typlen, typbyval, typalign, &data, NULL, &n); /* elog(INFO, "idata: %p n: %d", data, n); */ Assert(*dims == n); cx_in = palloc0(*dims * sizeof(kiss_fft_cpx)); cx_out = palloc0(*dims * sizeof(kiss_fft_cpx)); /* apply scale */ for (i=0; i<*dims; i++) { cx_in[i].r = DatumGetFloat4(data[i]); /* elog(INFO, "%d %f %f", i, DatumGetFloat4(data[i]), cx_in[i].i); */ } if ((cfg = kiss_fft_alloc(*dims, 0, 0, 0)) == NULL) elog(ERROR, "kiss_fft_alloc() failed"); kiss_fft(cfg, cx_in, cx_out); for (i=0; i<*dims; i++) { ((float *)data)[i] = (cx_out[i].r * cx_out[i].r + cx_out[i].i * cx_out[i].i) / ((double)*dims); /* printf("%23.15e %23.15e\n", freq / (double)n * (double)i, (cx_out[i].r * cx_out[i].r + cx_out[i].i * cx_out[i].i) / (double)n); */ /* elog(INFO, "data %d %f", i, (double)(data[i])); */ } /* elog(INFO, "odata: %p dims: %d otype: %d olen: %d obyval: %d align: %d", data, *dims, eltype, typlen, typbyval, typalign); */ result = construct_array((void *)data, *dims, eltype, typlen, typbyval, typalign); free(cfg); pfree(data); pfree(cx_in); pfree(cx_out); PG_RETURN_ARRAYTYPE_P(result); }
Datum pg_file_rename(PG_FUNCTION_ARGS) { char *fn1, *fn2, *fn3; int rc; requireSuperuser(); if (PG_ARGISNULL(0) || PG_ARGISNULL(1)) PG_RETURN_NULL(); fn1 = convert_and_check_filename(PG_GETARG_TEXT_P(0), false); fn2 = convert_and_check_filename(PG_GETARG_TEXT_P(1), false); if (PG_ARGISNULL(2)) fn3 = 0; else fn3 = convert_and_check_filename(PG_GETARG_TEXT_P(2), false); if (access(fn1, W_OK) < 0) { ereport(WARNING, (errcode_for_file_access(), errmsg("file \"%s\" is not accessible: %m", fn1))); PG_RETURN_BOOL(false); } if (fn3 && access(fn2, W_OK) < 0) { ereport(WARNING, (errcode_for_file_access(), errmsg("file \"%s\" is not accessible: %m", fn2))); PG_RETURN_BOOL(false); } rc = access(fn3 ? fn3 : fn2, 2); if (rc >= 0 || errno != ENOENT) { ereport(ERROR, (ERRCODE_DUPLICATE_FILE, errmsg("cannot rename to target file \"%s\"", fn3 ? fn3 : fn2))); } if (fn3) { if (rename(fn2, fn3) != 0) { ereport(ERROR, (errcode_for_file_access(), errmsg("could not rename \"%s\" to \"%s\": %m", fn2, fn3))); } if (rename(fn1, fn2) != 0) { ereport(WARNING, (errcode_for_file_access(), errmsg("could not rename \"%s\" to \"%s\": %m", fn1, fn2))); if (rename(fn3, fn2) != 0) { ereport(ERROR, (errcode_for_file_access(), errmsg("could not rename \"%s\" back to \"%s\": %m", fn3, fn2))); } else { ereport(ERROR, (ERRCODE_UNDEFINED_FILE, errmsg("renaming \"%s\" to \"%s\" was reverted", fn2, fn3))); } } } else if (rename(fn1, fn2) != 0) { ereport(ERROR, (errcode_for_file_access(), errmsg("could not rename \"%s\" to \"%s\": %m", fn1, fn2))); } PG_RETURN_BOOL(true); }
/*! UDA transition function for the fmsketch aggregate. */ Datum __fmsketch_trans(PG_FUNCTION_ARGS) { bytea * transblob = (bytea *)PG_GETARG_BYTEA_P(0); fmtransval *transval; Oid element_type = get_fn_expr_argtype(fcinfo->flinfo, 1); Oid funcOid; bool typIsVarlena; Datum retval; Datum inval; if (!OidIsValid(element_type)) elog(ERROR, "could not determine data type of input"); /* * This is Postgres boilerplate for UDFs that modify the data in their own context. * Such UDFs can only be correctly called in an agg context since regular scalar * UDFs are essentially stateless across invocations. */ if (!(fcinfo->context && (IsA(fcinfo->context, AggState) #ifdef NOTGP || IsA(fcinfo->context, WindowAggState) #endif ))) elog( ERROR, "UDF call to a function that only works for aggs (destructive pass by reference)"); /* get the provided element, being careful in case it's NULL */ if (!PG_ARGISNULL(1)) { inval = PG_GETARG_DATUM(1); /* * if this is the first call, initialize transval to hold a sortasort * on the first call, we should have the empty string (if the agg was declared properly!) */ if (VARSIZE(transblob) <= VARHDRSZ) { size_t blobsz = VARHDRSZ + sizeof(fmtransval) + SORTASORT_INITIAL_STORAGE; transblob = (bytea *)palloc0(blobsz); SET_VARSIZE(transblob, blobsz); transval = (fmtransval *)VARDATA(transblob); transval->typOid = element_type; /* figure out the outfunc for this type */ getTypeOutputInfo(element_type, &funcOid, &typIsVarlena); get_typlenbyval(element_type, &(transval->typLen), &(transval->typByVal)); transval->status = SMALL; sortasort_init((sortasort *)transval->storage, MINVALS, SORTASORT_INITIAL_STORAGE, transval->typLen, transval->typByVal); } else { /* extract the existing transval from the transblob */ transval = (fmtransval *)VARDATA(transblob); } /* * if we've seen < MINVALS distinct values, place datum into the sortasort * XXXX Would be cleaner to try the sortasort insert and if it fails, then continue. */ if (transval->status == SMALL && ((sortasort *)(transval->storage))->num_vals < MINVALS) { int len = ExtractDatumLen(inval, transval->typLen, transval->typByVal); retval = PointerGetDatum(fmsketch_sortasort_insert( transblob, inval, len)); PG_RETURN_DATUM(retval); } /* * if we've seen exactly MINVALS distinct values, create FM bitmaps * and load the contents of the sortasort into the FM sketch */ else if (transval->status == SMALL && ((sortasort *)(transval->storage))->num_vals == MINVALS) { int i; sortasort *s = (sortasort *)(transval->storage); bytea *newblob = fm_new(transval); transval = (fmtransval *)VARDATA(newblob); /* * "catch up" on the past as if we were doing FM from the beginning: * apply the FM sketching algorithm to each value previously stored in the sortasort */ for (i = 0; i < MINVALS; i++) __fmsketch_trans_c(newblob, PointerExtractDatum(SORTASORT_GETVAL(s,i), s->typByVal)); /* * XXXX would like to pfree the old transblob, but the memory allocator doesn't like it * XXXX Meanwhile we know that this memory "leak" is of fixed size and will get * XXXX deallocated "soon" when the memory context is destroyed. */ /* drop through to insert the current datum in "BIG" mode */ transblob = newblob; } /* * if we're here we've seen >=MINVALS distinct values and are in BIG mode. * Just for sanity, let's check. */ if (transval->status != BIG) elog( ERROR, "FM sketch failed internal sanity check"); /* Apply FM algorithm to this datum */ retval = __fmsketch_trans_c(transblob, inval); PG_RETURN_DATUM(retval); } else PG_RETURN_NULL(); }
Datum RASTER_dwithin(PG_FUNCTION_ARGS) { const int set_count = 2; rt_pgraster *pgrast[2]; int pgrastpos[2] = {-1, -1}; rt_raster rast[2] = {NULL}; uint32_t bandindex[2] = {0}; uint32_t hasbandindex[2] = {0}; double distance = 0; uint32_t i; uint32_t j; uint32_t k; uint32_t numBands; int rtn; int result; for (i = 0, j = 0; i < set_count; i++) { /* pgrast is null, return null */ if (PG_ARGISNULL(j)) { for (k = 0; k < i; k++) { rt_raster_destroy(rast[k]); PG_FREE_IF_COPY(pgrast[k], pgrastpos[k]); } PG_RETURN_NULL(); } pgrast[i] = (rt_pgraster *) PG_DETOAST_DATUM(PG_GETARG_DATUM(j)); pgrastpos[i] = j; j++; /* raster */ rast[i] = rt_raster_deserialize(pgrast[i], FALSE); if (!rast[i]) { for (k = 0; k <= i; k++) { if (k < i) rt_raster_destroy(rast[k]); PG_FREE_IF_COPY(pgrast[k], pgrastpos[k]); } elog(ERROR, "RASTER_dwithin: Could not deserialize the %s raster", i < 1 ? "first" : "second"); PG_RETURN_NULL(); } /* numbands */ numBands = rt_raster_get_num_bands(rast[i]); if (numBands < 1) { elog(NOTICE, "The %s raster provided has no bands", i < 1 ? "first" : "second"); if (i > 0) i++; for (k = 0; k < i; k++) { rt_raster_destroy(rast[k]); PG_FREE_IF_COPY(pgrast[k], pgrastpos[k]); } PG_RETURN_NULL(); } /* band index */ if (!PG_ARGISNULL(j)) { bandindex[i] = PG_GETARG_INT32(j); if (bandindex[i] < 1 || bandindex[i] > numBands) { elog(NOTICE, "Invalid band index (must use 1-based) for the %s raster. Returning NULL", i < 1 ? "first" : "second"); if (i > 0) i++; for (k = 0; k < i; k++) { rt_raster_destroy(rast[k]); PG_FREE_IF_COPY(pgrast[k], pgrastpos[k]); } PG_RETURN_NULL(); } hasbandindex[i] = 1; } else hasbandindex[i] = 0; POSTGIS_RT_DEBUGF(4, "hasbandindex[%d] = %d", i, hasbandindex[i]); POSTGIS_RT_DEBUGF(4, "bandindex[%d] = %d", i, bandindex[i]); j++; } /* distance */ if (PG_ARGISNULL(4)) { elog(NOTICE, "Distance cannot be NULL. Returning NULL"); for (k = 0; k < set_count; k++) { rt_raster_destroy(rast[k]); PG_FREE_IF_COPY(pgrast[k], pgrastpos[k]); } PG_RETURN_NULL(); } distance = PG_GETARG_FLOAT8(4); if (distance < 0) { elog(NOTICE, "Distance cannot be less than zero. Returning NULL"); for (k = 0; k < set_count; k++) { rt_raster_destroy(rast[k]); PG_FREE_IF_COPY(pgrast[k], pgrastpos[k]); } PG_RETURN_NULL(); } /* hasbandindex must be balanced */ if ( (hasbandindex[0] && !hasbandindex[1]) || (!hasbandindex[0] && hasbandindex[1]) ) { elog(NOTICE, "Missing band index. Band indices must be provided for both rasters if any one is provided"); for (k = 0; k < set_count; k++) { rt_raster_destroy(rast[k]); PG_FREE_IF_COPY(pgrast[k], pgrastpos[k]); } PG_RETURN_NULL(); } /* SRID must match */ if (rt_raster_get_srid(rast[0]) != rt_raster_get_srid(rast[1])) { for (k = 0; k < set_count; k++) { rt_raster_destroy(rast[k]); PG_FREE_IF_COPY(pgrast[k], pgrastpos[k]); } elog(ERROR, "The two rasters provided have different SRIDs"); PG_RETURN_NULL(); } rtn = rt_raster_within_distance( rast[0], (hasbandindex[0] ? bandindex[0] - 1 : -1), rast[1], (hasbandindex[1] ? bandindex[1] - 1 : -1), distance, &result ); for (k = 0; k < set_count; k++) { rt_raster_destroy(rast[k]); PG_FREE_IF_COPY(pgrast[k], pgrastpos[k]); } if (rtn != ES_NONE) { elog(ERROR, "RASTER_dwithin: Could not test that the two rasters are within the specified distance of each other"); PG_RETURN_NULL(); } PG_RETURN_BOOL(result); }
/* * similar_escape() * Convert a SQL:2008 regexp pattern to POSIX style, so it can be used by * our regexp engine. */ Datum similar_escape(PG_FUNCTION_ARGS) { text *pat_text; text *esc_text; text *result; char *p, *e, *r; int plen, elen; bool afterescape = false; bool incharclass = false; int nquotes = 0; /* This function is not strict, so must test explicitly */ if (PG_ARGISNULL(0)) PG_RETURN_NULL(); pat_text = PG_GETARG_TEXT_PP(0); p = VARDATA_ANY(pat_text); plen = VARSIZE_ANY_EXHDR(pat_text); if (PG_ARGISNULL(1)) { /* No ESCAPE clause provided; default to backslash as escape */ e = "\\"; elen = 1; } else { esc_text = PG_GETARG_TEXT_PP(1); e = VARDATA_ANY(esc_text); elen = VARSIZE_ANY_EXHDR(esc_text); if (elen == 0) e = NULL; /* no escape character */ else { int escape_mblen = pg_mbstrlen_with_len(e, elen); if (escape_mblen > 1) ereport(ERROR, (errcode(ERRCODE_INVALID_ESCAPE_SEQUENCE), errmsg("invalid escape string"), errhint("Escape string must be empty or one character."))); } } /*---------- * We surround the transformed input string with * ^(?: ... )$ * which requires some explanation. We need "^" and "$" to force * the pattern to match the entire input string as per SQL99 spec. * The "(?:" and ")" are a non-capturing set of parens; we have to have * parens in case the string contains "|", else the "^" and "$" will * be bound into the first and last alternatives which is not what we * want, and the parens must be non capturing because we don't want them * to count when selecting output for SUBSTRING. *---------- */ /* * We need room for the prefix/postfix plus as many as 3 output bytes per * input byte; since the input is at most 1GB this can't overflow */ result = (text *) palloc(VARHDRSZ + 6 + 3 * plen); r = VARDATA(result); *r++ = '^'; *r++ = '('; *r++ = '?'; *r++ = ':'; while (plen > 0) { char pchar = *p; /* * If both the escape character and the current character from the * pattern are multi-byte, we need to take the slow path. * * But if one of them is single-byte, we can process the pattern one * byte at a time, ignoring multi-byte characters. (This works * because all server-encodings have the property that a valid * multi-byte character representation cannot contain the * representation of a valid single-byte character.) */ if (elen > 1) { int mblen = pg_mblen(p); if (mblen > 1) { /* slow, multi-byte path */ if (afterescape) { *r++ = '\\'; memcpy(r, p, mblen); r += mblen; afterescape = false; } else if (e && elen == mblen && memcmp(e, p, mblen) == 0) { /* SQL99 escape character; do not send to output */ afterescape = true; } else { /* * We know it's a multi-byte character, so we don't need * to do all the comparisons to single-byte characters * that we do below. */ memcpy(r, p, mblen); r += mblen; } p += mblen; plen -= mblen; continue; } } /* fast path */ if (afterescape) { if (pchar == '"' && !incharclass) /* for SUBSTRING patterns */ *r++ = ((nquotes++ % 2) == 0) ? '(' : ')'; else { *r++ = '\\'; *r++ = pchar; } afterescape = false; } else if (e && pchar == *e) { /* SQL99 escape character; do not send to output */ afterescape = true; } else if (incharclass) { if (pchar == '\\') *r++ = '\\'; *r++ = pchar; if (pchar == ']') incharclass = false; } else if (pchar == '[') { *r++ = pchar; incharclass = true; } else if (pchar == '%') { *r++ = '.'; *r++ = '*'; } else if (pchar == '_') *r++ = '.'; else if (pchar == '(') { /* convert to non-capturing parenthesis */ *r++ = '('; *r++ = '?'; *r++ = ':'; } else if (pchar == '\\' || pchar == '.' || pchar == '^' || pchar == '$') { *r++ = '\\'; *r++ = pchar; } else *r++ = pchar; p++, plen--; } *r++ = ')'; *r++ = '$'; SET_VARSIZE(result, r - ((char *) result)); PG_RETURN_TEXT_P(result); }
Datum hstore_from_record(PG_FUNCTION_ARGS) { HeapTupleHeader rec; int32 buflen; HStore *out; Pairs *pairs; Oid tupType; int32 tupTypmod; TupleDesc tupdesc; HeapTupleData tuple; RecordIOData *my_extra; int ncolumns; int i, j; Datum *values; bool *nulls; if (PG_ARGISNULL(0)) { Oid argtype = get_fn_expr_argtype(fcinfo->flinfo, 0); /* * have no tuple to look at, so the only source of type info is the * argtype. The lookup_rowtype_tupdesc call below will error out if we * don't have a known composite type oid here. */ tupType = argtype; tupTypmod = -1; rec = NULL; } else { rec = PG_GETARG_HEAPTUPLEHEADER(0); /* Extract type info from the tuple itself */ tupType = HeapTupleHeaderGetTypeId(rec); tupTypmod = HeapTupleHeaderGetTypMod(rec); } tupdesc = lookup_rowtype_tupdesc(tupType, tupTypmod); ncolumns = tupdesc->natts; /* * We arrange to look up the needed I/O info just once per series of * calls, assuming the record type doesn't change underneath us. */ my_extra = (RecordIOData *) fcinfo->flinfo->fn_extra; if (my_extra == NULL || my_extra->ncolumns != ncolumns) { fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt, sizeof(RecordIOData) - sizeof(ColumnIOData) + ncolumns * sizeof(ColumnIOData)); my_extra = (RecordIOData *) fcinfo->flinfo->fn_extra; my_extra->record_type = InvalidOid; my_extra->record_typmod = 0; } if (my_extra->record_type != tupType || my_extra->record_typmod != tupTypmod) { MemSet(my_extra, 0, sizeof(RecordIOData) - sizeof(ColumnIOData) + ncolumns * sizeof(ColumnIOData)); my_extra->record_type = tupType; my_extra->record_typmod = tupTypmod; my_extra->ncolumns = ncolumns; } Assert(ncolumns <= MaxTupleAttributeNumber); /* thus, no overflow */ pairs = palloc(ncolumns * sizeof(Pairs)); if (rec) { /* Build a temporary HeapTuple control structure */ tuple.t_len = HeapTupleHeaderGetDatumLength(rec); ItemPointerSetInvalid(&(tuple.t_self)); tuple.t_tableOid = InvalidOid; tuple.t_data = rec; values = (Datum *) palloc(ncolumns * sizeof(Datum)); nulls = (bool *) palloc(ncolumns * sizeof(bool)); /* Break down the tuple into fields */ heap_deform_tuple(&tuple, tupdesc, values, nulls); } else { values = NULL; nulls = NULL; } for (i = 0, j = 0; i < ncolumns; ++i) { ColumnIOData *column_info = &my_extra->columns[i]; Oid column_type = tupdesc->attrs[i]->atttypid; char *value; /* Ignore dropped columns in datatype */ if (tupdesc->attrs[i]->attisdropped) continue; pairs[j].key = NameStr(tupdesc->attrs[i]->attname); pairs[j].keylen = hstoreCheckKeyLen(strlen(NameStr(tupdesc->attrs[i]->attname))); if (!nulls || nulls[i]) { pairs[j].val = NULL; pairs[j].vallen = 4; pairs[j].isnull = true; pairs[j].needfree = false; ++j; continue; } /* * Convert the column value to text */ if (column_info->column_type != column_type) { bool typIsVarlena; getTypeOutputInfo(column_type, &column_info->typiofunc, &typIsVarlena); fmgr_info_cxt(column_info->typiofunc, &column_info->proc, fcinfo->flinfo->fn_mcxt); column_info->column_type = column_type; } value = OutputFunctionCall(&column_info->proc, values[i]); pairs[j].val = value; pairs[j].vallen = hstoreCheckValLen(strlen(value)); pairs[j].isnull = false; pairs[j].needfree = false; ++j; } ncolumns = hstoreUniquePairs(pairs, j, &buflen); out = hstorePairs(pairs, ncolumns, buflen); ReleaseTupleDesc(tupdesc); PG_RETURN_POINTER(out); }
/* * The step function for aggregating the class counts while doing Reduce Error Pruning (REP). * * Parameters: * class_count_array The array used to store the accumulated information. * [0]: the total number of mis-classified cases * [i]: the number of cases belonging to the ith class * classified_class The predicted class based on our trained DT model. * original_class The real class value provided in the validation set. * max_num_of_classes The total number of distinct class values. * Return: * An updated state array. */ Datum rep_aggr_class_count_sfunc(PG_FUNCTION_ARGS) { ArrayType *class_count_array = NULL; int array_dim = 0; int *p_array_dim = NULL; int array_length = 0; int64 *class_count_data = NULL; int classified_class = PG_GETARG_INT32(1); int original_class = PG_GETARG_INT32(2); int max_num_of_classes = PG_GETARG_INT32(3); bool need_reconstruct_array = false; check_error_value ( max_num_of_classes >= 2, "invalid value: %d. The number of classes must be greater than or equal to 2", max_num_of_classes ); check_error_value ( original_class > 0 && original_class <= max_num_of_classes, "invalid real class value: %d. It must be in range from 1 to the number of classes", original_class ); check_error_value ( classified_class > 0 && classified_class <= max_num_of_classes, "invalid classified class value: %d. It must be in range from 1 to the number of classes", classified_class ); /* test if the first argument (class count array) is null */ if (PG_ARGISNULL(0)) { /* * We assume the maximum number of classes is limited (up to millions), * so that the allocated array won't break our memory limitation. */ class_count_data = palloc0(sizeof(int64) * (max_num_of_classes + 1)); array_length = max_num_of_classes + 1; need_reconstruct_array = true; } else { if (fcinfo->context && IsA(fcinfo->context, AggState)) class_count_array = PG_GETARG_ARRAYTYPE_P(0); else class_count_array = PG_GETARG_ARRAYTYPE_P_COPY(0); check_error ( class_count_array, "invalid aggregation state array" ); array_dim = ARR_NDIM(class_count_array); check_error_value ( array_dim == 1, "invalid array dimension: %d. The dimension of class count array must be equal to 1", array_dim ); p_array_dim = ARR_DIMS(class_count_array); array_length = ArrayGetNItems(array_dim,p_array_dim); class_count_data = (int64 *)ARR_DATA_PTR(class_count_array); check_error_value ( array_length == max_num_of_classes + 1, "invalid array length: %d. The length of class count array must be equal to the total number classes + 1", array_length ); } /* * If the condition is met, then the current record has been mis-classified. * Therefore, we will need to increase the first element. */ if(original_class != classified_class) ++class_count_data[0]; /* In any case, we will update the original class count */ ++class_count_data[original_class]; if( need_reconstruct_array ) { /* construct a new array to keep the aggr states. */ class_count_array = construct_array( (Datum *)class_count_data, array_length, INT8OID, sizeof(int64), true, 'd' ); } PG_RETURN_ARRAYTYPE_P(class_count_array); }
/* * The pre-function for REP. It takes two class count arrays * produced by the sfunc and combine them together. * * Parameters: * 1 arg: The array returned by sfun1 * 2 arg: The array returned by sfun2 * Return: * The array with the combined information */ Datum rep_aggr_class_count_prefunc(PG_FUNCTION_ARGS) { ArrayType *class_count_array = NULL; int array_dim = 0; int *p_array_dim = NULL; int array_length = 0; int64 *class_count_data = NULL; ArrayType *class_count_array2 = NULL; int array_dim2 = 0; int *p_array_dim2 = NULL; int array_length2 = 0; int64 *class_count_data2 = NULL; if (PG_ARGISNULL(0) && PG_ARGISNULL(1)) PG_RETURN_NULL(); else if (PG_ARGISNULL(1) || PG_ARGISNULL(0)) { /* If one of the two array is null, just return the non-null array directly */ PG_RETURN_ARRAYTYPE_P(PG_ARGISNULL(1) ? PG_GETARG_ARRAYTYPE_P(0) : PG_GETARG_ARRAYTYPE_P(1)); } else { /* * If both arrays are not null, we will merge them together. */ if (fcinfo->context && IsA(fcinfo->context, AggState)) class_count_array = PG_GETARG_ARRAYTYPE_P(0); else class_count_array = PG_GETARG_ARRAYTYPE_P_COPY(0); check_error ( class_count_array, "invalid aggregation state array" ); array_dim = ARR_NDIM(class_count_array); check_error_value ( array_dim == 1, "invalid array dimension: %d. The dimension of class count array must be equal to 1", array_dim ); p_array_dim = ARR_DIMS(class_count_array); array_length = ArrayGetNItems(array_dim,p_array_dim); class_count_data = (int64 *)ARR_DATA_PTR(class_count_array); class_count_array2 = PG_GETARG_ARRAYTYPE_P(1); array_dim2 = ARR_NDIM(class_count_array2); check_error_value ( array_dim2 == 1, "invalid array dimension: %d. The dimension of class count array must be equal to 1", array_dim2 ); p_array_dim2 = ARR_DIMS(class_count_array2); array_length2 = ArrayGetNItems(array_dim2,p_array_dim2); class_count_data2 = (int64 *)ARR_DATA_PTR(class_count_array2); check_error ( array_length == array_length2, "the size of the two array must be the same in prefunction" ); for (int index = 0; index < array_length; index++) class_count_data[index] += class_count_data2[index]; PG_RETURN_ARRAYTYPE_P(class_count_array); } }
/* * pg_prewarm(regclass, mode text, fork text, * first_block int8, last_block int8) * * The first argument is the relation to be prewarmed; the second controls * how prewarming is done; legal options are 'prefetch', 'read', and 'buffer'. * The third is the name of the relation fork to be prewarmed. The fourth * and fifth arguments specify the first and last block to be prewarmed. * If the fourth argument is NULL, it will be taken as 0; if the fifth argument * is NULL, it will be taken as the number of blocks in the relation. The * return value is the number of blocks successfully prewarmed. */ Datum pg_prewarm(PG_FUNCTION_ARGS) { Oid relOid; text *forkName; text *type; int64 first_block; int64 last_block; int64 nblocks; int64 blocks_done = 0; int64 block; Relation rel; ForkNumber forkNumber; char *forkString; char *ttype; PrewarmType ptype; AclResult aclresult; /* Basic sanity checking. */ if (PG_ARGISNULL(0)) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("relation cannot be null"))); relOid = PG_GETARG_OID(0); if (PG_ARGISNULL(1)) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), (errmsg("prewarm type cannot be null")))); type = PG_GETARG_TEXT_P(1); ttype = text_to_cstring(type); if (strcmp(ttype, "prefetch") == 0) ptype = PREWARM_PREFETCH; else if (strcmp(ttype, "read") == 0) ptype = PREWARM_READ; else if (strcmp(ttype, "buffer") == 0) ptype = PREWARM_BUFFER; else { ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("invalid prewarm type"), errhint("Valid prewarm types are \"prefetch\", \"read\", and \"buffer\"."))); PG_RETURN_INT64(0); /* Placate compiler. */ } if (PG_ARGISNULL(2)) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), (errmsg("relation fork cannot be null")))); forkName = PG_GETARG_TEXT_P(2); forkString = text_to_cstring(forkName); forkNumber = forkname_to_number(forkString); /* Open relation and check privileges. */ rel = relation_open(relOid, AccessShareLock); aclresult = pg_class_aclcheck(relOid, GetUserId(), ACL_SELECT); if (aclresult != ACLCHECK_OK) aclcheck_error(aclresult, ACL_KIND_CLASS, get_rel_name(relOid)); /* Check that the fork exists. */ RelationOpenSmgr(rel); if (!smgrexists(rel->rd_smgr, forkNumber)) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("fork \"%s\" does not exist for this relation", forkString))); /* Validate block numbers, or handle nulls. */ nblocks = RelationGetNumberOfBlocksInFork(rel, forkNumber); if (PG_ARGISNULL(3)) first_block = 0; else { first_block = PG_GETARG_INT64(3); if (first_block < 0 || first_block >= nblocks) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("starting block number must be between 0 and " INT64_FORMAT, nblocks - 1))); } if (PG_ARGISNULL(4)) last_block = nblocks - 1; else { last_block = PG_GETARG_INT64(4); if (last_block < 0 || last_block >= nblocks) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("ending block number must be between 0 and " INT64_FORMAT, nblocks - 1))); } /* Now we're ready to do the real work. */ if (ptype == PREWARM_PREFETCH) { #ifdef USE_PREFETCH /* * In prefetch mode, we just hint the OS to read the blocks, but we * don't know whether it really does it, and we don't wait for it to * finish. * * It would probably be better to pass our prefetch requests in chunks * of a megabyte or maybe even a whole segment at a time, but there's * no practical way to do that at present without a gross modularity * violation, so we just do this. */ for (block = first_block; block <= last_block; ++block) { CHECK_FOR_INTERRUPTS(); PrefetchBuffer(rel, forkNumber, block); ++blocks_done; } #else ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("prefetch is not supported by this build"))); #endif } else if (ptype == PREWARM_READ) { /* * In read mode, we actually read the blocks, but not into shared * buffers. This is more portable than prefetch mode (it works * everywhere) and is synchronous. */ for (block = first_block; block <= last_block; ++block) { CHECK_FOR_INTERRUPTS(); smgrread(rel->rd_smgr, forkNumber, block, blockbuffer); ++blocks_done; } } else if (ptype == PREWARM_BUFFER) { /* * In buffer mode, we actually pull the data into shared_buffers. */ for (block = first_block; block <= last_block; ++block) { Buffer buf; CHECK_FOR_INTERRUPTS(); buf = ReadBufferExtended(rel, forkNumber, block, RBM_NORMAL, NULL); ReleaseBuffer(buf); ++blocks_done; } } /* Close relation, release lock. */ relation_close(rel, AccessShareLock); PG_RETURN_INT64(blocks_done); }
/* * FUNCTION UTL_FILE.FOPEN(location text, * filename text, * open_mode text, * max_linesize integer) * RETURNS UTL_FILE.FILE_TYPE; * * The FOPEN function opens specified file and returns file handle. * open_mode: ['R', 'W', 'A'] * max_linesize: [1 .. 32767] * * Exceptions: * INVALID_MODE, INVALID_OPERATION, INVALID_PATH, INVALID_MAXLINESIZE */ Datum utl_file_fopen(PG_FUNCTION_ARGS) { text *open_mode; int max_linesize; int encoding; const char *mode = NULL; FILE *file; char *fullname; int d; NOT_NULL_ARG(0); NOT_NULL_ARG(1); NOT_NULL_ARG(2); NOT_NULL_ARG(3); open_mode = PG_GETARG_TEXT_P(2); NON_EMPTY_TEXT(open_mode); max_linesize = PG_GETARG_INT32(3); CHECK_LINESIZE(max_linesize); if (PG_NARGS() > 4 && !PG_ARGISNULL(4)) { const char *encname = NameStr(*PG_GETARG_NAME(4)); encoding = pg_char_to_encoding(encname); if (encoding < 0) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("invalid encoding name \"%s\"", encname))); } else encoding = GetDatabaseEncoding(); if (VARSIZE(open_mode) - VARHDRSZ != 1) CUSTOM_EXCEPTION(INVALID_MODE, "open mode is different than [R,W,A]"); switch (*((char*)VARDATA(open_mode))) { case 'a': case 'A': mode = "a"; break; case 'r': case 'R': mode = "r"; break; case 'w': case 'W': mode = "w"; break; default: CUSTOM_EXCEPTION(INVALID_MODE, "open mode is different than [R,W,A]"); } /* open file */ fullname = get_safe_path(PG_GETARG_TEXT_P(0), PG_GETARG_TEXT_P(1)); /* * We cannot use AllocateFile here because those files are automatically * closed at the end of (sub)transactions, but we want to keep them open * for oracle compatibility. */ #if NOT_USED fullname = convert_encoding_server_to_platform(fullname); #endif file = fopen(fullname, mode); if (!file) IO_EXCEPTION(); d = get_descriptor(file, max_linesize, encoding); if (d == INVALID_SLOTID) { fclose(file); ereport(ERROR, (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), errmsg("program limit exceeded"), errdetail("Too much concurent opened files"), errhint("You can only open a maximum of ten files for each session"))); } PG_RETURN_INT32(d); }
/* * 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)); PG_TRY(); { /* restart at slot's confirmed_flush */ ctx = CreateDecodingContext(InvalidXLogRecPtr, options, logical_read_local_xlog_page, LogicalOutputPrepareWrite, LogicalOutputWrite); 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; }
Datum array_mad(PG_FUNCTION_ARGS) { // The formal PostgreSQL array object ArrayType *array; // The array element type Oid arrayElementType; // The array element type width int16 arrayElementTypeWidth; // The array element type "is passed by value" flags (not used, should always be true) bool arrayElementTypeByValue; // The array element type alignment codes (not used) char arrayElementTypeAlignmentCode; // The array contents, as PostgreSQL "datum" objects Datum *arrayContent; // List of "is null" flags for the array contents bool *arrayNullFlags; // The size of each array int arrayLength; int i,j, nelem; double median, mad; double *inarray; if (PG_ARGISNULL(0)) ereport(ERROR, (errmsg("Null arrays not accepted"))); // Get array from input array = PG_GETARG_ARRAYTYPE_P(0); if (ARR_NDIM(array) != 1) ereport(ERROR, (errmsg("One-dimesional arrays are required"))); if (array_contains_nulls(array)) ereport(ERROR, (errmsg("Array contains null elements"))); arrayLength = (ARR_DIMS(array))[0]; arrayElementType = ARR_ELEMTYPE(array); get_typlenbyvalalign(arrayElementType, &arrayElementTypeWidth, &arrayElementTypeByValue, &arrayElementTypeAlignmentCode); deconstruct_array(array, arrayElementType, arrayElementTypeWidth, arrayElementTypeByValue, arrayElementTypeAlignmentCode, &arrayContent, &arrayNullFlags, &arrayLength); inarray = (double*)malloc(arrayLength*sizeof(double)); for (i=0; i<arrayLength; i++) { inarray[i] = DatumGetFloat4(arrayContent[i]); } gsl_sort (inarray, 1, arrayLength); median = gsl_stats_median_from_sorted_data (inarray, 1, arrayLength); for (i=0; i<arrayLength; i++) { inarray[i] = fabs(inarray[i]-median); } gsl_sort (inarray, 1, arrayLength); mad = 1.486 * gsl_stats_median_from_sorted_data (inarray, 1, arrayLength); PG_RETURN_FLOAT8(mad); }
Datum dbms_pipe_create_pipe (PG_FUNCTION_ARGS) { text *pipe_name = NULL; int limit = 0; bool is_private; bool limit_is_valid = false; bool created; float8 endtime; int cycle = 0; int timeout = 10; if (PG_ARGISNULL(0)) ereport(ERROR, (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED), errmsg("pipe name is NULL"), errdetail("Pipename may not be NULL."))); else pipe_name = PG_GETARG_TEXT_P(0); if (!PG_ARGISNULL(1)) { limit = PG_GETARG_INT32(1); limit_is_valid = true; } is_private = PG_ARGISNULL(2) ? false : PG_GETARG_BOOL(2); WATCH_PRE(timeout, endtime, cycle); if (ora_lock_shmem(SHMEMMSGSZ, MAX_PIPES,MAX_EVENTS,MAX_LOCKS,false)) { pipe *p; if (NULL != (p = find_pipe(pipe_name, &created, false))) { if (!created) { LWLockRelease(shmem_lockid); ereport(ERROR, (errcode(ERRCODE_DUPLICATE_OBJECT), errmsg("pipe creation error"), errdetail("Pipe is registered."))); } if (is_private) { char *user; p->uid = GetUserId(); #if PG_VERSION_NUM >= 90500 user = (char*)DirectFunctionCall1(namein, CStringGetDatum(GetUserNameFromId(p->uid, false))); #else user = (char*)DirectFunctionCall1(namein, CStringGetDatum(GetUserNameFromId(p->uid))); #endif p->creator = ora_sstrcpy(user); pfree(user); } p->limit = limit_is_valid ? limit : -1; p->registered = true; LWLockRelease(shmem_lockid); PG_RETURN_VOID(); } } WATCH_POST(timeout, endtime, cycle); LOCK_ERROR(); PG_RETURN_VOID(); }
Datum text_unpivot(PG_FUNCTION_ARGS) { FuncCallContext *funcctx; unpivot_fctx *fctx; MemoryContext oldcontext; Datum d[2]; bool isna[2]; /* stuff done only on the first call of the function */ if (SRF_IS_FIRSTCALL()) { TupleDesc tupdesc; ArrayType *labels; ArrayType *data; Oid eltype1; Oid eltype2; /* see if we were given an explicit step size */ if (PG_NARGS() != 2) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("number of parameters != 2"))); /* * Type check inputs: * 0: label text[] * 1: value anyarray */ eltype1 = get_fn_expr_argtype(fcinfo->flinfo, 0); eltype2 = get_fn_expr_argtype(fcinfo->flinfo, 1); if (!OidIsValid(eltype1)) elog(ERROR, "could not determine data type of input 'label'"); if (!OidIsValid(eltype2)) elog(ERROR, "could not determine data type of input 'value'"); /* Strict function, return null on null input */ if (PG_ARGISNULL(0) || PG_ARGISNULL(1)) PG_RETURN_NULL(); /* create a function context for cross-call persistence */ funcctx = SRF_FIRSTCALL_INIT(); /* switch to memory context appropriate for multiple function calls */ oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx); /* Build a tuple descriptor for our result type */ if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("function returning record called in context " "that cannot accept type record"))); funcctx->tuple_desc = BlessTupleDesc(tupdesc); /* allocate memory for user context */ fctx = (unpivot_fctx *) palloc(sizeof(unpivot_fctx)); /* Use fctx to keep state from call to call */ labels = PG_GETARG_ARRAYTYPE_P(0); data = PG_GETARG_ARRAYTYPE_P(1); array_loop(labels, ARR_LBOUND(labels)[0], &fctx->label_iter); array_loop(data, ARR_LBOUND(labels)[0], &fctx->data_iter); funcctx->user_fctx = fctx; MemoryContextSwitchTo(oldcontext); } /* stuff done on every call of the function */ funcctx = SRF_PERCALL_SETUP(); /* get the saved state and use current as the result for this iteration */ fctx = (unpivot_fctx*) funcctx->user_fctx; if (array_next(&fctx->label_iter, &d[0], &isna[0])) { HeapTuple tuple; array_next(&fctx->data_iter, &d[1], &isna[1]); tuple = heap_form_tuple(funcctx->tuple_desc, d, isna); SRF_RETURN_NEXT(funcctx, HeapTupleGetDatum(tuple)); } else { SRF_RETURN_DONE(funcctx); } }
/* * The step function for the aggregation of splitting criteria values. It accumulates * all the information for scv calculation and stores to a fourteen element array. * * Parameters: * scv_state_array The array used to accumulate all the information for the * calculation of splitting criteria values. Please refer to * the definition of SCV_STATE_ARRAY_INDEX. * split_criterion 1- infogain; 2- gainratio; 3- gini. * feature_val The feature value of current record under processing. * class The class of current record under processing. * is_cont_feature True- The feature is continuous. False- The feature is * discrete. * less Count of elements less than or equal to feature_val. * great Count of elements greater than feature_val. * true_total_count If there is any missing value, true_total_count is larger than * the total count computed in the aggregation. Thus, we should * multiply a ratio for the computed gain. * Return: * A fourteen element array. Please refer to the definition of * SCV_STATE_ARRAY_INDEX for the detailed information of this * array. */ Datum scv_aggr_sfunc(PG_FUNCTION_ARGS) { ArrayType* scv_state_array = NULL; if (fcinfo->context && IsA(fcinfo->context, AggState)) scv_state_array = PG_GETARG_ARRAYTYPE_P(0); else scv_state_array = PG_GETARG_ARRAYTYPE_P_COPY(0); check_error ( scv_state_array, "invalid aggregation state array" ); int array_dim = ARR_NDIM(scv_state_array); check_error_value ( array_dim == 1, "invalid array dimension: %d. The dimension of scv state array must be equal to 1", array_dim ); int* p_array_dim = ARR_DIMS(scv_state_array); int array_length = ArrayGetNItems(array_dim, p_array_dim); check_error_value ( array_length == SCV_STATE_MAX_CLASS_ELEM_COUNT + 1, "invalid array length: %d", array_length ); float8 *scv_state_data = (float8 *)ARR_DATA_PTR(scv_state_array); check_error ( scv_state_data, "invalid aggregation data array" ); int split_criterion = PG_GETARG_INT32(1); bool is_null_fval = PG_ARGISNULL(2); float8 feature_val = PG_GETARG_FLOAT8(2); float8 class = PG_ARGISNULL(3) ? -1 : PG_GETARG_FLOAT8(3); bool is_cont_feature = PG_ARGISNULL(4) ? false : PG_GETARG_BOOL(4); float8 less = PG_ARGISNULL(5) ? 0 : PG_GETARG_FLOAT8(5); float8 great = PG_ARGISNULL(6) ? 0 : PG_GETARG_FLOAT8(6); float8 true_total_count = PG_ARGISNULL(7) ? 0 : PG_GETARG_FLOAT8(7); check_error_value ( (SC_INFOGAIN == split_criterion || SC_GAINRATIO == split_criterion || SC_GINI == split_criterion), "invalid split criterion: %d. It must be 1(infogain), 2(gainratio) or 3(gini)", split_criterion ); /* * If the count for total element is still zero * it is the first time that step function is * invoked. In that case, we should initialize * several elements. */ if (is_float_zero(scv_state_data[SCV_STATE_TOTAL_ELEM_COUNT])) { scv_state_data[SCV_STATE_SPLIT_CRIT] = split_criterion; if (SC_GINI == split_criterion) { scv_state_data[SCV_STATE_INIT_SCV] = 1; } else { scv_state_data[SCV_STATE_INIT_SCV] = 0; } scv_state_data[SCV_STATE_IS_CONT] = is_cont_feature ? 1 : 0; scv_state_data[SCV_STATE_TRUE_TOTAL_COUNT] = true_total_count; dtelog(NOTICE,"true_total_count:%lf",true_total_count); } float8 temp_float = 0; if( is_null_fval ) { dtelog(NOTICE,"is_null_fval:%d",is_null_fval); if( !is_cont_feature ) { if( class <0 ) { scv_state_data[SCV_STATE_TOTAL_ELEM_COUNT] = less; dtelog(NOTICE,"SCV_STATE_TOTAL_ELEM_COUNT:%lf",less); } else { if (scv_state_data[SCV_STATE_MAX_CLASS_ELEM_COUNT] < less) { scv_state_data[SCV_STATE_MAX_CLASS_ELEM_COUNT] = less; scv_state_data[SCV_STATE_MAX_CLASS_ID] = class; } accumulate_pre_split_scv( scv_state_data, less, scv_state_data[SCV_STATE_TOTAL_ELEM_COUNT], split_criterion); } }
/* * Returns activity of PG backends. */ Datum pg_stat_get_activity(PG_FUNCTION_ARGS) { #define PG_STAT_GET_ACTIVITY_COLS 23 int num_backends = pgstat_fetch_stat_numbackends(); int curr_backend; int pid = PG_ARGISNULL(0) ? -1 : PG_GETARG_INT32(0); ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo; TupleDesc tupdesc; Tuplestorestate *tupstore; MemoryContext per_query_ctx; MemoryContext oldcontext; /* 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"))); /* Build a tuple descriptor for our result type */ if (get_call_result_type(fcinfo, NULL, &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); tupstore = tuplestore_begin_heap(true, false, work_mem); rsinfo->returnMode = SFRM_Materialize; rsinfo->setResult = tupstore; rsinfo->setDesc = tupdesc; MemoryContextSwitchTo(oldcontext); /* 1-based index */ for (curr_backend = 1; curr_backend <= num_backends; curr_backend++) { /* for each row */ Datum values[PG_STAT_GET_ACTIVITY_COLS]; bool nulls[PG_STAT_GET_ACTIVITY_COLS]; LocalPgBackendStatus *local_beentry; PgBackendStatus *beentry; PGPROC *proc; const char *wait_event_type; const char *wait_event; MemSet(values, 0, sizeof(values)); MemSet(nulls, 0, sizeof(nulls)); if (pid != -1) { /* Skip any which are not the one we're looking for. */ PgBackendStatus *be = pgstat_fetch_stat_beentry(curr_backend); if (!be || be->st_procpid != pid) continue; } /* Get the next one in the list */ local_beentry = pgstat_fetch_stat_local_beentry(curr_backend); if (!local_beentry) continue; beentry = &local_beentry->backendStatus; if (!beentry) { int i; for (i = 0; i < sizeof(nulls) / sizeof(nulls[0]); i++) nulls[i] = true; nulls[5] = false; values[5] = CStringGetTextDatum("<backend information not available>"); tuplestore_putvalues(tupstore, tupdesc, values, nulls); continue; } /* Values available to all callers */ values[0] = ObjectIdGetDatum(beentry->st_databaseid); values[1] = Int32GetDatum(beentry->st_procpid); values[2] = ObjectIdGetDatum(beentry->st_userid); if (beentry->st_appname) values[3] = CStringGetTextDatum(beentry->st_appname); else nulls[3] = true; if (TransactionIdIsValid(local_beentry->backend_xid)) values[15] = TransactionIdGetDatum(local_beentry->backend_xid); else nulls[15] = true; if (TransactionIdIsValid(local_beentry->backend_xmin)) values[16] = TransactionIdGetDatum(local_beentry->backend_xmin); else nulls[16] = true; if (beentry->st_ssl) { values[17] = BoolGetDatum(true); /* ssl */ values[18] = CStringGetTextDatum(beentry->st_sslstatus->ssl_version); values[19] = CStringGetTextDatum(beentry->st_sslstatus->ssl_cipher); values[20] = Int32GetDatum(beentry->st_sslstatus->ssl_bits); values[21] = BoolGetDatum(beentry->st_sslstatus->ssl_compression); values[22] = CStringGetTextDatum(beentry->st_sslstatus->ssl_clientdn); } else { values[17] = BoolGetDatum(false); /* ssl */ nulls[18] = nulls[19] = nulls[20] = nulls[21] = nulls[22] = true; } /* Values only available to role member */ if (has_privs_of_role(GetUserId(), beentry->st_userid)) { SockAddr zero_clientaddr; switch (beentry->st_state) { case STATE_IDLE: values[4] = CStringGetTextDatum("idle"); break; case STATE_RUNNING: values[4] = CStringGetTextDatum("active"); break; case STATE_IDLEINTRANSACTION: values[4] = CStringGetTextDatum("idle in transaction"); break; case STATE_FASTPATH: values[4] = CStringGetTextDatum("fastpath function call"); break; case STATE_IDLEINTRANSACTION_ABORTED: values[4] = CStringGetTextDatum("idle in transaction (aborted)"); break; case STATE_DISABLED: values[4] = CStringGetTextDatum("disabled"); break; case STATE_UNDEFINED: nulls[4] = true; break; } values[5] = CStringGetTextDatum(beentry->st_activity); proc = BackendPidGetProc(beentry->st_procpid); wait_event_type = pgstat_get_wait_event_type(proc->wait_event_info); if (wait_event_type) values[6] = CStringGetTextDatum(wait_event_type); else nulls[6] = true; wait_event = pgstat_get_wait_event(proc->wait_event_info); if (wait_event) values[7] = CStringGetTextDatum(wait_event); else nulls[7] = true; if (beentry->st_xact_start_timestamp != 0) values[8] = TimestampTzGetDatum(beentry->st_xact_start_timestamp); else nulls[8] = true; if (beentry->st_activity_start_timestamp != 0) values[9] = TimestampTzGetDatum(beentry->st_activity_start_timestamp); else nulls[9] = true; if (beentry->st_proc_start_timestamp != 0) values[10] = TimestampTzGetDatum(beentry->st_proc_start_timestamp); else nulls[10] = true; if (beentry->st_state_start_timestamp != 0) values[11] = TimestampTzGetDatum(beentry->st_state_start_timestamp); else nulls[11] = true; /* A zeroed client addr means we don't know */ memset(&zero_clientaddr, 0, sizeof(zero_clientaddr)); if (memcmp(&(beentry->st_clientaddr), &zero_clientaddr, sizeof(zero_clientaddr)) == 0) { nulls[12] = true; nulls[13] = true; nulls[14] = true; } else { if (beentry->st_clientaddr.addr.ss_family == AF_INET #ifdef HAVE_IPV6 || beentry->st_clientaddr.addr.ss_family == AF_INET6 #endif ) { char remote_host[NI_MAXHOST]; char remote_port[NI_MAXSERV]; int ret; remote_host[0] = '\0'; remote_port[0] = '\0'; ret = pg_getnameinfo_all(&beentry->st_clientaddr.addr, beentry->st_clientaddr.salen, remote_host, sizeof(remote_host), remote_port, sizeof(remote_port), NI_NUMERICHOST | NI_NUMERICSERV); if (ret == 0) { clean_ipv6_addr(beentry->st_clientaddr.addr.ss_family, remote_host); values[12] = DirectFunctionCall1(inet_in, CStringGetDatum(remote_host)); if (beentry->st_clienthostname && beentry->st_clienthostname[0]) values[13] = CStringGetTextDatum(beentry->st_clienthostname); else nulls[13] = true; values[14] = Int32GetDatum(atoi(remote_port)); } else { nulls[12] = true; nulls[13] = true; nulls[14] = true; } } else if (beentry->st_clientaddr.addr.ss_family == AF_UNIX) { /* * Unix sockets always reports NULL for host and -1 for * port, so it's possible to tell the difference to * connections we have no permissions to view, or with * errors. */ nulls[12] = true; nulls[13] = true; values[14] = DatumGetInt32(-1); } else { /* Unknown address type, should never happen */ nulls[12] = true; nulls[13] = true; nulls[14] = true; } } } else { /* No permissions to view data about this session */ values[5] = CStringGetTextDatum("<insufficient privilege>"); nulls[4] = true; nulls[6] = true; nulls[7] = true; nulls[8] = true; nulls[9] = true; nulls[10] = true; nulls[11] = true; nulls[12] = true; nulls[13] = true; nulls[14] = true; } tuplestore_putvalues(tupstore, tupdesc, values, nulls); /* If only a single backend was requested, and we found it, break. */ if (pid != -1) break; } /* clean up and return the tuplestore */ tuplestore_donestoring(tupstore); return (Datum) 0; }
Datum spell_init(PG_FUNCTION_ARGS) { DictISpell *d; Map *cfg, *pcfg; text *in; bool affloaded = false, dictloaded = false, stoploaded = false; if (PG_ARGISNULL(0) || PG_GETARG_POINTER(0) == NULL) ereport(ERROR, (errcode(ERRCODE_CONFIG_FILE_ERROR), errmsg("ISpell confguration error"))); d = (DictISpell *) malloc(sizeof(DictISpell)); if (!d) ereport(ERROR, (errcode(ERRCODE_OUT_OF_MEMORY), errmsg("out of memory"))); memset(d, 0, sizeof(DictISpell)); d->stoplist.wordop = lowerstr; in = PG_GETARG_TEXT_P(0); parse_cfgdict(in, &cfg); PG_FREE_IF_COPY(in, 0); pcfg = cfg; while (pcfg->key) { if (strcasecmp("DictFile", pcfg->key) == 0) { if (dictloaded) { freeDictISpell(d); ereport(ERROR, (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), errmsg("dictionary already loaded"))); } if (ImportDictionary(&(d->obj), pcfg->value)) { freeDictISpell(d); ereport(ERROR, (errcode(ERRCODE_CONFIG_FILE_ERROR), errmsg("could not load dictionary file \"%s\"", pcfg->value))); } dictloaded = true; } else if (strcasecmp("AffFile", pcfg->key) == 0) { if (affloaded) { freeDictISpell(d); ereport(ERROR, (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), errmsg("affixes already loaded"))); } if (ImportAffixes(&(d->obj), pcfg->value)) { freeDictISpell(d); ereport(ERROR, (errcode(ERRCODE_CONFIG_FILE_ERROR), errmsg("could not load affix file \"%s\"", pcfg->value))); } affloaded = true; } else if (strcasecmp("StopFile", pcfg->key) == 0) { text *tmp = char2text(pcfg->value); if (stoploaded) { freeDictISpell(d); ereport(ERROR, (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), errmsg("stop words already loaded"))); } readstoplist(tmp, &(d->stoplist)); sortstoplist(&(d->stoplist)); pfree(tmp); stoploaded = true; } else { freeDictISpell(d); ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("unrecognized option: %s => %s", pcfg->key, pcfg->value))); } pfree(pcfg->key); pfree(pcfg->value); pcfg++; } pfree(cfg); if (affloaded && dictloaded) { SortDictionary(&(d->obj)); SortAffixes(&(d->obj)); } else if (!affloaded) { freeDictISpell(d); ereport(ERROR, (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), errmsg("no affixes"))); } else { freeDictISpell(d); ereport(ERROR, (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), errmsg("no dictionary"))); } PG_RETURN_POINTER(d); }
Datum hstore_from_arrays(PG_FUNCTION_ARGS) { int32 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); /* see discussion in hstoreArrayToPairs() */ if (key_count > MaxAllocSize / sizeof(Pairs)) ereport(ERROR, (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), errmsg("number of pairs (%d) exceeds the maximum allowed (%d)", key_count, (int) (MaxAllocSize / sizeof(Pairs))))); /* 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 tuple_data_split(PG_FUNCTION_ARGS) { Oid relid; bytea *raw_data; uint16 t_infomask; uint16 t_infomask2; char *t_bits_str; bool do_detoast = false; bits8 *t_bits = NULL; Datum res; relid = PG_GETARG_OID(0); raw_data = PG_ARGISNULL(1) ? NULL : PG_GETARG_BYTEA_P(1); t_infomask = PG_GETARG_INT16(2); t_infomask2 = PG_GETARG_INT16(3); t_bits_str = PG_ARGISNULL(4) ? NULL : text_to_cstring(PG_GETARG_TEXT_PP(4)); if (PG_NARGS() >= 6) do_detoast = PG_GETARG_BOOL(5); if (!superuser()) ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), errmsg("must be superuser to use raw page functions"))); if (!raw_data) PG_RETURN_NULL(); /* * Convert t_bits string back to the bits8 array as represented in the * tuple header. */ if (t_infomask & HEAP_HASNULL) { int bits_str_len; int bits_len; bits_len = (t_infomask2 & HEAP_NATTS_MASK) / 8 + 1; if (!t_bits_str) ereport(ERROR, (errcode(ERRCODE_DATA_CORRUPTED), errmsg("argument of t_bits is null, but it is expected to be null and %d character long", bits_len * 8))); bits_str_len = strlen(t_bits_str); if ((bits_str_len % 8) != 0) ereport(ERROR, (errcode(ERRCODE_DATA_CORRUPTED), errmsg("length of t_bits is not a multiple of eight"))); if (bits_len * 8 != bits_str_len) ereport(ERROR, (errcode(ERRCODE_DATA_CORRUPTED), errmsg("unexpected length of t_bits %u, expected %d", bits_str_len, bits_len * 8))); /* do the conversion */ t_bits = text_to_bits(t_bits_str, bits_str_len); } else { if (t_bits_str) ereport(ERROR, (errcode(ERRCODE_DATA_CORRUPTED), errmsg("t_bits string is expected to be NULL, but instead it is %zu bytes length", strlen(t_bits_str)))); } /* Split tuple data */ res = tuple_data_split_internal(relid, (char *) raw_data + VARHDRSZ, VARSIZE(raw_data) - VARHDRSZ, t_infomask, t_infomask2, t_bits, do_detoast); if (t_bits) pfree(t_bits); PG_RETURN_ARRAYTYPE_P(res); }
Datum hstore_populate_record(PG_FUNCTION_ARGS) { Oid argtype = get_fn_expr_argtype(fcinfo->flinfo, 0); HStore *hs; HEntry *entries; char *ptr; HeapTupleHeader rec; Oid tupType; int32 tupTypmod; TupleDesc tupdesc; HeapTupleData tuple; HeapTuple rettuple; RecordIOData *my_extra; int ncolumns; int i; Datum *values; bool *nulls; if (!type_is_rowtype(argtype)) ereport(ERROR, (errcode(ERRCODE_DATATYPE_MISMATCH), errmsg("first argument must be a rowtype"))); if (PG_ARGISNULL(0)) { if (PG_ARGISNULL(1)) PG_RETURN_NULL(); rec = NULL; /* * have no tuple to look at, so the only source of type info is the * argtype. The lookup_rowtype_tupdesc call below will error out if we * don't have a known composite type oid here. */ tupType = argtype; tupTypmod = -1; } else { rec = PG_GETARG_HEAPTUPLEHEADER(0); if (PG_ARGISNULL(1)) PG_RETURN_POINTER(rec); /* Extract type info from the tuple itself */ tupType = HeapTupleHeaderGetTypeId(rec); tupTypmod = HeapTupleHeaderGetTypMod(rec); } hs = PG_GETARG_HS(1); entries = ARRPTR(hs); ptr = STRPTR(hs); /* * if the input hstore is empty, we can only skip the rest if we were * passed in a non-null record, since otherwise there may be issues with * domain nulls. */ if (HS_COUNT(hs) == 0 && rec) PG_RETURN_POINTER(rec); tupdesc = lookup_rowtype_tupdesc(tupType, tupTypmod); ncolumns = tupdesc->natts; if (rec) { /* Build a temporary HeapTuple control structure */ tuple.t_len = HeapTupleHeaderGetDatumLength(rec); ItemPointerSetInvalid(&(tuple.t_self)); tuple.t_tableOid = InvalidOid; tuple.t_data = rec; } /* * We arrange to look up the needed I/O info just once per series of * calls, assuming the record type doesn't change underneath us. */ my_extra = (RecordIOData *) fcinfo->flinfo->fn_extra; if (my_extra == NULL || my_extra->ncolumns != ncolumns) { fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt, sizeof(RecordIOData) - sizeof(ColumnIOData) + ncolumns * sizeof(ColumnIOData)); my_extra = (RecordIOData *) fcinfo->flinfo->fn_extra; my_extra->record_type = InvalidOid; my_extra->record_typmod = 0; } if (my_extra->record_type != tupType || my_extra->record_typmod != tupTypmod) { MemSet(my_extra, 0, sizeof(RecordIOData) - sizeof(ColumnIOData) + ncolumns * sizeof(ColumnIOData)); my_extra->record_type = tupType; my_extra->record_typmod = tupTypmod; my_extra->ncolumns = ncolumns; } values = (Datum *) palloc(ncolumns * sizeof(Datum)); nulls = (bool *) palloc(ncolumns * sizeof(bool)); if (rec) { /* Break down the tuple into fields */ heap_deform_tuple(&tuple, tupdesc, values, nulls); } else { for (i = 0; i < ncolumns; ++i) { values[i] = (Datum) 0; nulls[i] = true; } } for (i = 0; i < ncolumns; ++i) { ColumnIOData *column_info = &my_extra->columns[i]; Oid column_type = tupdesc->attrs[i]->atttypid; char *value; int idx; int vallen; /* Ignore dropped columns in datatype */ if (tupdesc->attrs[i]->attisdropped) { nulls[i] = true; continue; } idx = hstoreFindKey(hs, 0, NameStr(tupdesc->attrs[i]->attname), strlen(NameStr(tupdesc->attrs[i]->attname))); /* * we can't just skip here if the key wasn't found since we might have * a domain to deal with. If we were passed in a non-null record * datum, we assume that the existing values are valid (if they're * not, then it's not our fault), but if we were passed in a null, * then every field which we don't populate needs to be run through * the input function just in case it's a domain type. */ if (idx < 0 && rec) continue; /* * Prepare to convert the column value from text */ if (column_info->column_type != column_type) { getTypeInputInfo(column_type, &column_info->typiofunc, &column_info->typioparam); fmgr_info_cxt(column_info->typiofunc, &column_info->proc, fcinfo->flinfo->fn_mcxt); column_info->column_type = column_type; } if (idx < 0 || HS_VALISNULL(entries, idx)) { /* * need InputFunctionCall to happen even for nulls, so that domain * checks are done */ values[i] = InputFunctionCall(&column_info->proc, NULL, column_info->typioparam, tupdesc->attrs[i]->atttypmod); nulls[i] = true; } else { vallen = HS_VALLEN(entries, idx); value = palloc(1 + vallen); memcpy(value, HS_VAL(entries, ptr, idx), vallen); value[vallen] = 0; values[i] = InputFunctionCall(&column_info->proc, value, column_info->typioparam, tupdesc->attrs[i]->atttypmod); nulls[i] = false; } } rettuple = heap_form_tuple(tupdesc, values, nulls); ReleaseTupleDesc(tupdesc); PG_RETURN_DATUM(HeapTupleGetDatum(rettuple)); }
/* * Preliminary segment-level calculation function for multi-linear regression * aggregates. */ Datum float8_mregr_combine(PG_FUNCTION_ARGS) { ArrayType *state1, *state2, *result; float8 *state1Data, *state2Data, *resultData; uint32 len; uint64 statelen, i; Size size; /* We should be strict, but it doesn't hurt to be paranoid */ if (PG_ARGISNULL(0)) { if (PG_ARGISNULL(1)) PG_RETURN_NULL(); PG_RETURN_ARRAYTYPE_P(PG_GETARG_ARRAYTYPE_P(1)); } if (PG_ARGISNULL(1)) PG_RETURN_ARRAYTYPE_P(PG_GETARG_ARRAYTYPE_P(0)); state1 = PG_GETARG_ARRAYTYPE_P(0); state2 = PG_GETARG_ARRAYTYPE_P(1); /* Ensure that both arrays are single dimensional float8[] arrays */ if (ARR_NULLBITMAP(state1) || ARR_NULLBITMAP(state2) || ARR_NDIM(state1) != 1 || ARR_NDIM(state2) != 1 || ARR_ELEMTYPE(state1) != FLOAT8OID || ARR_ELEMTYPE(state2) != FLOAT8OID) { ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("preliminary segment-level calculation function \"%s\" called with invalid parameters", format_procedure(fcinfo->flinfo->fn_oid)))); } /* * Remember that we initialized to {0}, so if either array is still at * the initial value then just return the other one */ if (ARR_DIMS(state1)[0] == 1) PG_RETURN_ARRAYTYPE_P(state2); if (ARR_DIMS(state2)[0] == 1) PG_RETURN_ARRAYTYPE_P(state1); state1Data = (float8*) ARR_DATA_PTR(state1); state2Data = (float8*) ARR_DATA_PTR(state2); if (ARR_DIMS(state1)[0] != ARR_DIMS(state2)[0] || state1Data[0] != state2Data[0]) { ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("preliminary segment-level calculation function \"%s\" called with invalid parameters", format_procedure(fcinfo->flinfo->fn_oid)), errdetail("The independent-variable array is not of constant width."))); } len = state1Data[0]; statelen = STATE_LEN(len); /* * Violation of any of the following conditions indicates bogus inputs. */ if (state1Data[0] > UINT32_MAX || (uint64) ARR_DIMS(state1)[0] != statelen || !IS_FEASIBLE_STATE_LEN(statelen)) { ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("preliminary segment-level calculation function \"%s\" called with invalid parameters", format_procedure(fcinfo->flinfo->fn_oid)))); } /* Validations pass, allocate memory for result and do work */ /* * Precondition: * IS_FEASIBLE_STATE_LEN(statelen) */ size = statelen * sizeof(float8) + ARR_OVERHEAD_NONULLS(1); result = (ArrayType *) palloc(size); SET_VARSIZE(result, size); result->ndim = 1; result->dataoffset = 0; result->elemtype = FLOAT8OID; ARR_DIMS(result)[0] = statelen; ARR_LBOUND(result)[0] = 1; resultData = (float8*) ARR_DATA_PTR(result); memset(resultData, 0, statelen * sizeof(float8)); /* * Contents of 'state' are as follows: * [0] = len(X[]) * [1] = count * [2] = sum(y) * [3] = sum(y*y) * [4:N] = sum(X'[] * y) * [N+1:M] = sum(X[] * X'[]) * N = 3 + len(X) * M = N + len(X)*len(X) */ resultData[0] = len; for (i = 1; i < statelen; i++) resultData[i] = state1Data[i] + state2Data[i]; PG_RETURN_ARRAYTYPE_P(result); }
Datum RASTER_sameAlignment(PG_FUNCTION_ARGS) { const int set_count = 2; rt_pgraster *pgrast[2]; int pgrastpos[2] = {-1, -1}; rt_raster rast[2] = {NULL}; uint32_t i; uint32_t j; uint32_t k; int rtn; int aligned = 0; char *reason = NULL; for (i = 0, j = 0; i < set_count; i++) { /* pgrast is null, return null */ if (PG_ARGISNULL(j)) { for (k = 0; k < i; k++) { rt_raster_destroy(rast[k]); PG_FREE_IF_COPY(pgrast[k], pgrastpos[k]); } PG_RETURN_NULL(); } pgrast[i] = (rt_pgraster *) PG_DETOAST_DATUM_SLICE(PG_GETARG_DATUM(j), 0, sizeof(struct rt_raster_serialized_t)); pgrastpos[i] = j; j++; /* raster */ rast[i] = rt_raster_deserialize(pgrast[i], TRUE); if (!rast[i]) { for (k = 0; k <= i; k++) { if (k < i) rt_raster_destroy(rast[k]); PG_FREE_IF_COPY(pgrast[k], pgrastpos[k]); } elog(ERROR, "RASTER_sameAlignment: Could not deserialize the %s raster", i < 1 ? "first" : "second"); PG_RETURN_NULL(); } } rtn = rt_raster_same_alignment( rast[0], rast[1], &aligned, &reason ); for (k = 0; k < set_count; k++) { rt_raster_destroy(rast[k]); PG_FREE_IF_COPY(pgrast[k], pgrastpos[k]); } if (rtn != ES_NONE) { elog(ERROR, "RASTER_sameAlignment: Could not test for alignment on the two rasters"); PG_RETURN_NULL(); } /* only output reason if not aligned */ if (reason != NULL && !aligned) elog(NOTICE, "%s", reason); PG_RETURN_BOOL(aligned); }
static bool float8_mregr_accum_get_args(FunctionCallInfo fcinfo, MRegrAccumArgs *outArgs) { float8 *stateData; uint32 len, i; uint64 statelen; /* We should be strict, but it doesn't hurt to be paranoid */ if (PG_ARGISNULL(0) || PG_ARGISNULL(1) || PG_ARGISNULL(2)) return false; outArgs->stateAsArray = PG_GETARG_ARRAYTYPE_P(0); outArgs->newY = PG_GETARG_FLOAT8(1); outArgs->newXAsArray = PG_GETARG_ARRAYTYPE_P(2); outArgs->newX = (float8*) ARR_DATA_PTR(outArgs->newXAsArray); /* Ensure that both arrays are single dimensional float8[] arrays */ if (ARR_NULLBITMAP(outArgs->stateAsArray) || ARR_NDIM(outArgs->stateAsArray) != 1 || ARR_ELEMTYPE(outArgs->stateAsArray) != FLOAT8OID || ARR_NDIM(outArgs->newXAsArray) != 1 || ARR_ELEMTYPE(outArgs->newXAsArray) != FLOAT8OID) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("transition function \"%s\" called with invalid parameters", format_procedure(fcinfo->flinfo->fn_oid)))); /* Only callable as a transition function */ if (!(fcinfo->context && IsA(fcinfo->context, AggState))) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("transition function \"%s\" not called from aggregate", format_procedure(fcinfo->flinfo->fn_oid)))); /* newXAsArray with nulls will be ignored */ if (ARR_NULLBITMAP(outArgs->newXAsArray)) return false; /* See MPP-14102. Avoid overflow while initializing len */ if (ARR_DIMS(outArgs->newXAsArray)[0] > UINT32_MAX) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("number of independent variables cannot exceed %lu", (unsigned long) UINT32_MAX))); len = ARR_DIMS(outArgs->newXAsArray)[0]; /* * See MPP-13580. At least on certain platforms and with certain versions, * LAPACK will run into an infinite loop if pinv() is called for non-finite * matrices. We extend the check also to the dependent variables. */ for (i = 0; i < len; i++) if (!isfinite(outArgs->newX[i])) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("design matrix is not finite"))); if (!isfinite(outArgs->newY)) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("dependent variables are not finite"))); /* * See MPP-14102. We want to avoid (a) long int overflows and (b) making * oversized allocation requests. * We could compute the maximum number of variables so that the transition- * state length still fits into MaxAllocSize, but (assuming MaxAllocSize may * change in the future) this calculation requires taking the root out of a * 64-bit long int. Since there is no standard library function for that, and * displaying this number of merely of theoretical interest (the actual * limit is a lot lower), we simply report that the number of independent * variables is too large. * Precondition: * len < 2^32. */ statelen = STATE_LEN(len); if (!IS_FEASIBLE_STATE_LEN(statelen)) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("number of independent variables is too large"))); /* * If length(outArgs->stateAsArray) == 1 then it is an unitialized state. * We extend as needed. */ if (ARR_DIMS(outArgs->stateAsArray)[0] == 1) { /* * Precondition: * IS_FEASIBLE_STATE_LEN(statelen) */ Size size = statelen * sizeof(float8) + ARR_OVERHEAD_NONULLS(1); outArgs->stateAsArray = (ArrayType *) palloc(size); SET_VARSIZE(outArgs->stateAsArray, size); outArgs->stateAsArray->ndim = 1; outArgs->stateAsArray->dataoffset = 0; outArgs->stateAsArray->elemtype = FLOAT8OID; ARR_DIMS(outArgs->stateAsArray)[0] = statelen; ARR_LBOUND(outArgs->stateAsArray)[0] = 1; stateData = (float8*) ARR_DATA_PTR(outArgs->stateAsArray); memset(stateData, 0, statelen * sizeof(float8)); stateData[0] = len; } /* * Contents of 'state' are as follows: * [0] = len(X[]) * [1] = count * [2] = sum(y) * [3] = sum(y*y) * [4:N] = sum(X'[] * y) * [N+1:M] = sum(X[] * X'[]) * N = 3 + len(X) * M = N + len(X)*len(X) */ outArgs->len = (float8*) ARR_DATA_PTR(outArgs->stateAsArray); outArgs->count = outArgs->len + 1; outArgs->sumy = outArgs->len + 2; outArgs->sumy2 = outArgs->len + 3; outArgs->Xty = outArgs->len + 4; outArgs->XtX = outArgs->len + 4 + len; /* It is an error if the number of indepent variables is not constant */ if (*outArgs->len != len) { ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("transition function \"%s\" called with invalid parameters", format_procedure(fcinfo->flinfo->fn_oid)), errdetail("The independent-variable array is not of constant width."))); } /* Something is seriously fishy if our state has the wrong length */ if ((uint64) ARR_DIMS(outArgs->stateAsArray)[0] != statelen) { ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("transition function \"%s\" called with invalid parameters", format_procedure(fcinfo->flinfo->fn_oid)))); } /* Okay... All's good now do the work */ return true; }
/* * similar_escape() * Convert a SQL:2008 regexp pattern to POSIX style, so it can be used by * our regexp engine. */ Datum similar_escape(PG_FUNCTION_ARGS) { text *pat_text; text *esc_text; text *result; char *p, *e, *r; int plen, elen; bool afterescape = false; bool incharclass = false; int nquotes = 0; /* This function is not strict, so must test explicitly */ if (PG_ARGISNULL(0)) PG_RETURN_NULL(); pat_text = PG_GETARG_TEXT_PP(0); p = VARDATA_ANY(pat_text); plen = VARSIZE_ANY_EXHDR(pat_text); if (PG_ARGISNULL(1)) { /* No ESCAPE clause provided; default to backslash as escape */ e = "\\"; elen = 1; } else { esc_text = PG_GETARG_TEXT_PP(1); e = VARDATA_ANY(esc_text); elen = VARSIZE_ANY_EXHDR(esc_text); if (elen == 0) e = NULL; /* no escape character */ else if (elen != 1) ereport(ERROR, (errcode(ERRCODE_INVALID_ESCAPE_SEQUENCE), errmsg("invalid escape string"), errhint("Escape string must be empty or one character."))); } /*---------- * We surround the transformed input string with * ^(?: ... )$ * which requires some explanation. We need "^" and "$" to force * the pattern to match the entire input string as per SQL99 spec. * The "(?:" and ")" are a non-capturing set of parens; we have to have * parens in case the string contains "|", else the "^" and "$" will * be bound into the first and last alternatives which is not what we * want, and the parens must be non capturing because we don't want them * to count when selecting output for SUBSTRING. *---------- */ /* * We need room for the prefix/postfix plus as many as 3 output bytes per * input byte; since the input is at most 1GB this can't overflow */ result = (text *) palloc(VARHDRSZ + 6 + 3 * plen); r = VARDATA(result); *r++ = '^'; *r++ = '('; *r++ = '?'; *r++ = ':'; while (plen > 0) { char pchar = *p; if (afterescape) { if (pchar == '"' && !incharclass) /* for SUBSTRING patterns */ *r++ = ((nquotes++ % 2) == 0) ? '(' : ')'; else { *r++ = '\\'; *r++ = pchar; } afterescape = false; } else if (e && pchar == *e) { /* SQL99 escape character; do not send to output */ afterescape = true; } else if (incharclass) { if (pchar == '\\') *r++ = '\\'; *r++ = pchar; if (pchar == ']') incharclass = false; } else if (pchar == '[') { *r++ = pchar; incharclass = true; } else if (pchar == '%') { *r++ = '.'; *r++ = '*'; } else if (pchar == '_') *r++ = '.'; else if (pchar == '(') { /* convert to non-capturing parenthesis */ *r++ = '('; *r++ = '?'; *r++ = ':'; } else if (pchar == '\\' || pchar == '.' || pchar == '^' || pchar == '$') { *r++ = '\\'; *r++ = pchar; } else *r++ = pchar; p++, plen--; } *r++ = ')'; *r++ = '$'; SET_VARSIZE(result, r - ((char *) result)); PG_RETURN_TEXT_P(result); }
Datum pg_stat_get_activity(PG_FUNCTION_ARGS) { FuncCallContext *funcctx; if (SRF_IS_FIRSTCALL()) { MemoryContext oldcontext; TupleDesc tupdesc; int nattr = 13; funcctx = SRF_FIRSTCALL_INIT(); oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx); tupdesc = CreateTemplateTupleDesc(nattr, false); TupleDescInitEntry(tupdesc, (AttrNumber) 1, "datid", OIDOID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 2, "procpid", INT4OID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 3, "usesysid", OIDOID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 4, "application_name", TEXTOID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 5, "current_query", TEXTOID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 6, "waiting", BOOLOID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 7, "act_start", TIMESTAMPTZOID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 8, "query_start", TIMESTAMPTZOID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 9, "backend_start", TIMESTAMPTZOID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 10, "client_addr", INETOID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 11, "client_port", INT4OID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 12, "sess_id", INT4OID, -1, 0); /* GPDB */ if (nattr > 12) TupleDescInitEntry(tupdesc, (AttrNumber) 13, "waiting_for", TEXTOID, -1, 0); funcctx->tuple_desc = BlessTupleDesc(tupdesc); funcctx->user_fctx = palloc0(sizeof(int)); if (PG_ARGISNULL(0)) { /* Get all backends */ funcctx->max_calls = pgstat_fetch_stat_numbackends(); } else { /* * Get one backend - locate by pid. * * We lookup the backend early, so we can return zero rows if it * doesn't exist, instead of returning a single row full of NULLs. */ int pid = PG_GETARG_INT32(0); int i; int n = pgstat_fetch_stat_numbackends(); for (i = 1; i <= n; i++) { PgBackendStatus *be = pgstat_fetch_stat_beentry(i); if (be) { if (be->st_procpid == pid) { *(int *) (funcctx->user_fctx) = i; break; } } } if (*(int *) (funcctx->user_fctx) == 0) /* Pid not found, return zero rows */ funcctx->max_calls = 0; else funcctx->max_calls = 1; } MemoryContextSwitchTo(oldcontext); } /* stuff done on every call of the function */ funcctx = SRF_PERCALL_SETUP(); if (funcctx->call_cntr < funcctx->max_calls) { /* for each row */ Datum values[13]; bool nulls[13]; HeapTuple tuple; PgBackendStatus *beentry; SockAddr zero_clientaddr; MemSet(values, 0, sizeof(values)); MemSet(nulls, 0, sizeof(nulls)); if (*(int *) (funcctx->user_fctx) > 0) { /* Get specific pid slot */ beentry = pgstat_fetch_stat_beentry(*(int *) (funcctx->user_fctx)); } else { /* Get the next one in the list */ beentry = pgstat_fetch_stat_beentry(funcctx->call_cntr + 1); /* 1-based index */ } if (!beentry) { int i; for (i = 0; i < sizeof(nulls) / sizeof(nulls[0]); i++) nulls[i] = true; nulls[4] = false; values[4] = CStringGetTextDatum("<backend information not available>"); tuple = heap_form_tuple(funcctx->tuple_desc, values, nulls); SRF_RETURN_NEXT(funcctx, HeapTupleGetDatum(tuple)); } /* Values available to all callers */ values[0] = ObjectIdGetDatum(beentry->st_databaseid); values[1] = Int32GetDatum(beentry->st_procpid); values[2] = ObjectIdGetDatum(beentry->st_userid); if (beentry->st_appname) values[3] = CStringGetTextDatum(beentry->st_appname); else nulls[3] = true; /* Values only available to same user or superuser */ if (superuser() || beentry->st_userid == GetUserId()) { bool waiting; if (*(beentry->st_activity) == '\0') { values[4] = CStringGetTextDatum("<command string not enabled>"); } else { values[4] = CStringGetTextDatum(beentry->st_activity); } waiting = beentry->st_waiting != PGBE_WAITING_NONE; values[5] = BoolGetDatum(beentry->st_waiting); if (beentry->st_xact_start_timestamp != 0) values[6] = TimestampTzGetDatum(beentry->st_xact_start_timestamp); else nulls[6] = true; if (beentry->st_activity_start_timestamp != 0) values[7] = TimestampTzGetDatum(beentry->st_activity_start_timestamp); else nulls[7] = true; if (beentry->st_proc_start_timestamp != 0) values[8] = TimestampTzGetDatum(beentry->st_proc_start_timestamp); else nulls[8] = true; /* A zeroed client addr means we don't know */ memset(&zero_clientaddr, 0, sizeof(zero_clientaddr)); if (memcmp(&(beentry->st_clientaddr), &zero_clientaddr, sizeof(zero_clientaddr) == 0)) { nulls[9] = true; nulls[10] = true; } else { if (beentry->st_clientaddr.addr.ss_family == AF_INET #ifdef HAVE_IPV6 || beentry->st_clientaddr.addr.ss_family == AF_INET6 #endif ) { char remote_host[NI_MAXHOST]; char remote_port[NI_MAXSERV]; int ret; remote_host[0] = '\0'; remote_port[0] = '\0'; ret = pg_getnameinfo_all(&beentry->st_clientaddr.addr, beentry->st_clientaddr.salen, remote_host, sizeof(remote_host), remote_port, sizeof(remote_port), NI_NUMERICHOST | NI_NUMERICSERV); if (ret) { nulls[9] = true; nulls[10] = true; } else { clean_ipv6_addr(beentry->st_clientaddr.addr.ss_family, remote_host); values[9] = DirectFunctionCall1(inet_in, CStringGetDatum(remote_host)); values[10] = Int32GetDatum(atoi(remote_port)); } } else if (beentry->st_clientaddr.addr.ss_family == AF_UNIX) { /* * Unix sockets always reports NULL for host and -1 for * port, so it's possible to tell the difference to * connections we have no permissions to view, or with * errors. */ nulls[9] = true; values[10] = DatumGetInt32(-1); } else { /* Unknown address type, should never happen */ nulls[9] = true; nulls[10] = true; } } values[11] = Int32GetDatum(beentry->st_session_id); /* GPDB */ if (funcctx->tuple_desc->natts > 12) { char st_waiting = beentry->st_waiting; char *reason; reason = pgstat_waiting_string(st_waiting); if (reason != NULL) values[12] = CStringGetTextDatum(reason); else nulls[12] = true; } } else { /* No permissions to view data about this session */ values[4] = CStringGetTextDatum("<insufficient privilege>"); nulls[5] = true; nulls[6] = true; nulls[7] = true; nulls[8] = true; nulls[9] = true; nulls[10] = true; values[11] = Int32GetDatum(beentry->st_session_id); if (funcctx->tuple_desc->natts > 12) nulls[12] = true; } tuple = heap_form_tuple(funcctx->tuple_desc, values, nulls); SRF_RETURN_NEXT(funcctx, HeapTupleGetDatum(tuple)); } else { /* nothing left */ SRF_RETURN_DONE(funcctx); } }
Datum pseudoinverse(PG_FUNCTION_ARGS) { /* * A note on types: PostgreSQL array dimensions are of type int. See, e.g., * the macro definition ARR_DIMS */ int rows, columns; float8 *A, *Aplus; ArrayType *A_PG, *Aplus_PG; int lbs[2], dims[2]; /* * Perform all the error checking needed to ensure that no one is * trying to call this in some sort of crazy way. */ if (PG_NARGS() != 1) { ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("pseudoinverse called with %d arguments", PG_NARGS()))); } if (PG_ARGISNULL(0)) PG_RETURN_NULL(); A_PG = PG_GETARG_ARRAYTYPE_P(0); if (ARR_ELEMTYPE(A_PG) != FLOAT8OID) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("pseudoinverse only defined over float8[]"))); if (ARR_NDIM(A_PG) != 2) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("pseudoinverse only defined over 2 dimensional arrays")) ); if (ARR_NULLBITMAP(A_PG)) ereport(ERROR, (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED), errmsg("null array element not allowed in this context"))); /* Extract rows, columns, and data */ rows = ARR_DIMS(A_PG)[0]; columns = ARR_DIMS(A_PG)[1]; A = (float8 *) ARR_DATA_PTR(A_PG); /* Allocate a PG array for the result, "Aplus" is A+, the pseudo inverse of A */ lbs[0] = 1; lbs[1] = 1; dims[0] = columns; dims[1] = rows; Aplus_PG = construct_md_array(NULL, NULL, 2, dims, lbs, FLOAT8OID, sizeof(float8), true, 'd'); Aplus = (float8 *) ARR_DATA_PTR(Aplus_PG); pinv(rows,columns,A,Aplus); PG_RETURN_ARRAYTYPE_P(Aplus_PG); }
/* * pivot_accum() - Pivot and accumulate */ static Datum oid_pivot_accum(FunctionCallInfo fcinfo, Oid type) { ArrayType *data; ArrayType *labels; text *attr; int i; /* Simple argument validation */ if (PG_NARGS() != 4) ereport(ERROR, (errcode(ERRCODE_DATATYPE_MISMATCH), errmsg("pivot_accum called with %d input arguments", PG_NARGS()))); if (PG_ARGISNULL(1) || PG_ARGISNULL(2) || PG_ARGISNULL(3)) { if (PG_ARGISNULL(0)) PG_RETURN_NULL(); PG_RETURN_ARRAYTYPE_P(PG_GETARG_ARRAYTYPE_P(0)); } labels = PG_GETARG_ARRAYTYPE_P(1); attr = PG_GETARG_TEXT_P(2); /* Do nothing if the attr isn't in the labels array. */ if ((i = pivot_find(labels, attr)) < 0) { if (PG_ARGISNULL(0)) PG_RETURN_NULL(); PG_RETURN_ARRAYTYPE_P(PG_GETARG_ARRAYTYPE_P(0)); } /* Get the data array, or make it if null */ if (!PG_ARGISNULL(0)) { data = PG_GETARG_ARRAYTYPE_P(0); Assert(ARR_DIMS(labels)[0] == ARR_DIMS(data)[0]); } else { int elsize, size, nelem; switch (type) { case INT4OID: elsize = 4; break; case INT8OID: case FLOAT8OID: elsize = 8; break; default: elsize = 0; /* Fixes complier warnings */ Assert(false); } nelem = ARR_DIMS(labels)[0]; size = nelem * elsize + ARR_OVERHEAD_NONULLS(1); data = (ArrayType *) palloc(size); SET_VARSIZE(data, size); data->ndim = 1; data->dataoffset = 0; data->elemtype = type; ARR_DIMS(data)[0] = nelem; ARR_LBOUND(data)[0] = 1; memset(ARR_DATA_PTR(data), 0, nelem * elsize); } /* * Should we think about upconverting the arrays? Or is the assumption that * the pivot isn't usually doing much aggregation? */ switch (type) { case INT4OID: { int32 *datap = (int32*) ARR_DATA_PTR(data); int32 value = PG_GETARG_INT32(3); datap[i] += value; break; } case INT8OID: { int64 *datap = (int64*) ARR_DATA_PTR(data); int64 value = PG_GETARG_INT64(3); datap[i] += value; break; } case FLOAT8OID: { float8 *datap = (float8*) ARR_DATA_PTR(data); float8 value = PG_GETARG_FLOAT8(3); datap[i] += value; break; } default: Assert(false); } PG_RETURN_ARRAYTYPE_P(data); }
Datum count_distinct_elements_append(PG_FUNCTION_ARGS) { int i; element_set_t *eset = NULL; /* info for anyarray */ Oid input_type; Oid element_type; /* array data */ ArrayType *input; int ndims; int *dims; int nitems; bits8 *null_bitmap; char *arr_ptr; Datum element; /* memory contexts */ MemoryContext oldcontext; MemoryContext aggcontext; /* * If the new value is NULL, we simply return the current aggregate state * (it might be NULL, so check it). In this case we don't really care about * the types etc. * * We may still get NULL elements in the array, but to check that we would * have to walk the array, which does not qualify as cheap check. Also we * assume that there's at least one non-NULL element, and we'll walk the * array just once. It's possible we'll get empty set this way. */ if (PG_ARGISNULL(1) && PG_ARGISNULL(0)) PG_RETURN_NULL(); else if (PG_ARGISNULL(1)) PG_RETURN_DATUM(PG_GETARG_DATUM(0)); /* from now on we know the new value is not NULL */ /* get the type of array elements */ input_type = get_fn_expr_argtype(fcinfo->flinfo, 1); element_type = get_element_type(input_type); /* * parse the array contents (we know we got non-NULL value) */ input = PG_GETARG_ARRAYTYPE_P(1); ndims = ARR_NDIM(input); dims = ARR_DIMS(input); nitems = ArrayGetNItems(ndims, dims); null_bitmap = ARR_NULLBITMAP(input); arr_ptr = ARR_DATA_PTR(input); /* make sure we're running as part of aggregate function */ GET_AGG_CONTEXT("count_distinct_elements_append", fcinfo, aggcontext); oldcontext = MemoryContextSwitchTo(aggcontext); /* add all array elements to the set */ for (i = 0; i < nitems; i++) { /* ignore nulls */ if (null_bitmap && !(null_bitmap[i / 8] & (1 << (i % 8)))) continue; /* init the hash table, if needed */ if (eset == NULL) { if (PG_ARGISNULL(0)) { int16 typlen; bool typbyval; char typalign; /* get type information for the second parameter (anyelement item) */ get_typlenbyvalalign(element_type, &typlen, &typbyval, &typalign); /* we can't handle varlena types yet or values passed by reference */ if ((typlen < 0) || (! typbyval)) elog(ERROR, "count_distinct_elements handles only arrays of fixed-length types passed by value"); eset = init_set(typlen, typalign, aggcontext); } else eset = (element_set_t *)PG_GETARG_POINTER(0); } element = fetch_att(arr_ptr, true, eset->item_size); add_element(eset, (char*)&element); /* advance array pointer */ arr_ptr = att_addlength_pointer(arr_ptr, eset->item_size, arr_ptr); arr_ptr = (char *) att_align_nominal(arr_ptr, eset->typalign); } MemoryContextSwitchTo(oldcontext); if (eset == NULL) PG_RETURN_NULL(); else PG_RETURN_POINTER(eset); }
/** * Returns a mean from an array of numbers. * by Paul A. Jungwirth */ Datum array_to_mean(PG_FUNCTION_ARGS) { // Our arguments: ArrayType *vals; // The array element type: Oid valsType; // The array element type widths for our input array: int16 valsTypeWidth; // The array element type "is passed by value" flags (not really used): bool valsTypeByValue; // The array element type alignment codes (not really used): char valsTypeAlignmentCode; // The array contents, as PostgreSQL "Datum" objects: Datum *valsContent; // List of "is null" flags for the array contents (not used): bool *valsNullFlags; // The size of the input array: int valsLength; float8 v = 0; int i; if (PG_ARGISNULL(0)) { ereport(ERROR, (errmsg("Null arrays not accepted"))); } vals = PG_GETARG_ARRAYTYPE_P(0); if (ARR_NDIM(vals) == 0) { PG_RETURN_NULL(); } if (ARR_NDIM(vals) > 1) { ereport(ERROR, (errmsg("One-dimesional arrays are required"))); } if (array_contains_nulls(vals)) { ereport(ERROR, (errmsg("Array contains null elements"))); } // Determine the array element types. valsType = ARR_ELEMTYPE(vals); if (valsType != INT2OID && valsType != INT4OID && valsType != INT8OID && valsType != FLOAT4OID && valsType != FLOAT8OID) { ereport(ERROR, (errmsg("Mean subject must be SMALLINT, INTEGER, BIGINT, REAL, or DOUBLE PRECISION values"))); } valsLength = (ARR_DIMS(vals))[0]; if (valsLength == 0) PG_RETURN_NULL(); get_typlenbyvalalign(valsType, &valsTypeWidth, &valsTypeByValue, &valsTypeAlignmentCode); // Extract the array contents (as Datum objects). deconstruct_array(vals, valsType, valsTypeWidth, valsTypeByValue, valsTypeAlignmentCode, &valsContent, &valsNullFlags, &valsLength); // Iterate through the contents and sum things up, // then return the mean: // Watch out for overflow: // http://stackoverflow.com/questions/1930454/what-is-a-good-solution-for-calculating-an-average-where-the-sum-of-all-values-e/1934266#1934266 switch (valsType) { case INT2OID: for (i = 0; i < valsLength; i++) { v += (DatumGetInt16(valsContent[i]) - v) / (i + 1); } break; case INT4OID: for (i = 0; i < valsLength; i++) { v += (DatumGetInt32(valsContent[i]) - v) / (i + 1); } break; case INT8OID: for (i = 0; i < valsLength; i++) { v += (DatumGetInt64(valsContent[i]) - v) / (i + 1); } break; case FLOAT4OID: for (i = 0; i < valsLength; i++) { v += (DatumGetFloat4(valsContent[i]) - v) / (i + 1); } break; case FLOAT8OID: for (i = 0; i < valsLength; i++) { v += (DatumGetFloat8(valsContent[i]) - v) / (i + 1); } break; default: ereport(ERROR, (errmsg("Mean subject must be SMALLINT, INTEGER, BIGINT, REAL, or DOUBLE PRECISION values"))); break; } PG_RETURN_FLOAT8(v); }