typedefstruct
{
float8 mean; /* mean of the distribution */
float8 stddev; /* stddev of the distribution */
float8 carry_val; /* hold second generated value */ bool use_carry; /* use second generated value */
} normal_rand_fctx;
#define xpfree(var_) \ do { \ if (var_ != NULL) \
{ \
pfree(var_); \
var_ = NULL; \
} \
} while (0)
#define xpstrdup(tgtvar_, srcvar_) \ do { \ if (srcvar_) \
tgtvar_ = pstrdup(srcvar_); \ else \
tgtvar_ = NULL; \
} while (0)
/* stored info for a crosstab category */ typedefstruct crosstab_cat_desc
{ char *catname; /* full category name */
uint64 attidx; /* zero based */
} crosstab_cat_desc;
/* total number of tuples to be returned */
num_tuples = PG_GETARG_INT32(0); if (num_tuples < 0)
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("number of rows cannot be negative")));
funcctx->max_calls = num_tuples;
/* allocate memory for user context */
fctx = (normal_rand_fctx *) palloc(sizeof(normal_rand_fctx));
/* 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")));
/* Retrieve the desired rows */
ret = SPI_execute(sql, true, 0);
proc = SPI_processed;
/* If no qualifying tuples, fall out early */ if (ret != SPI_OK_SELECT || proc == 0)
{
SPI_finish();
rsinfo->isDone = ExprEndResult;
PG_RETURN_NULL();
}
/*---------- *TheprovidedSQLquerymustalwaysreturnthreecolumns. * *1.rowname *thelabeloridentifierforeachrowinthefinalresult *2.category *thelabeloridentifierforeachcolumninthefinalresult *3.values *thevalueforeachcolumninthefinalresult *----------
*/ if (spi_tupdesc->natts != 3)
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("invalid crosstab source data query"),
errdetail("The query must return 3 columns: row_name, category, and value.")));
/* get a tuple descriptor for our result type */ switch (get_call_result_type(fcinfo, NULL, &tupdesc))
{ case TYPEFUNC_COMPOSITE: /* success */ break; case TYPEFUNC_RECORD: /* failed to determine actual type of RECORD */
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("function returning record called in context " "that cannot accept type record"))); break; default: /* result type isn't composite */
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
errmsg("return type must be a row type"))); break;
}
/* Clean up */ for (i = 0; i < num_categories + 1; i++) if (values[i] != NULL)
pfree(values[i]);
pfree(values);
}
/* let the caller know we're sending back a tuplestore */
rsinfo->returnMode = SFRM_Materialize;
rsinfo->setResult = tupstore;
rsinfo->setDesc = tupdesc;
/* release SPI related resources (and return to caller's context) */
SPI_finish();
/* 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) ||
rsinfo->expectedDesc == NULL)
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("materialize mode required, but it is not allowed in this context")));
/* get the requested return tuple description */
tupdesc = CreateTupleDescCopy(rsinfo->expectedDesc);
/* *Checktomakesurewehaveareasonabletupledescriptor * *Notewewillattempttocoercethevaluesintowhateverthereturn *attributetypeisanddependonthe"in"functiontocomplainif *needed.
*/ if (tupdesc->natts < 2)
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
errmsg("invalid crosstab return type"),
errdetail("Return row must have at least two columns.")));
/* load up the categories hash table */
crosstab_hash = load_categories_hash(cats_sql, per_query_ctx);
/* let the caller know we're sending back a tuplestore */
rsinfo->returnMode = SFRM_Materialize;
/* now go build it */
rsinfo->setResult = get_crosstab_tuplestore(sql,
crosstab_hash,
tupdesc,
rsinfo->allowedModes & SFRM_Materialize_Random);
/* Retrieve the category name rows */
ret = SPI_execute(cats_sql, true, 0);
proc = SPI_processed;
/* Check for qualifying tuples */ if ((ret == SPI_OK_SELECT) && (proc > 0))
{
SPITupleTable *spi_tuptable = SPI_tuptable;
TupleDesc spi_tupdesc = spi_tuptable->tupdesc;
uint64 i;
/* *TheprovidedcategoriesSQLquerymustalwaysreturnonecolumn: *category-thelabeloridentifierforeachcolumn
*/ if (spi_tupdesc->natts != 1)
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("invalid crosstab categories query"),
errdetail("The query must return one column.")));
for (i = 0; i < proc; i++)
{
crosstab_cat_desc *catdesc; char *catname;
HeapTuple spi_tuple;
/* get the next sql result tuple */
spi_tuple = spi_tuptable->vals[i];
/* get the category from the current sql result tuple */
catname = SPI_getvalue(spi_tuple, spi_tupdesc, 1); if (catname == NULL)
ereport(ERROR,
(errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
errmsg("crosstab category value must not be null")));
/* initialize our tuplestore (while still in query context!) */
tupstore = tuplestore_begin_heap(randomAccess, false, work_mem);
/* Connect to SPI manager */
SPI_connect();
/* Now retrieve the crosstab source rows */
ret = SPI_execute(sql, true, 0);
proc = SPI_processed;
/* Check for qualifying tuples */ if ((ret == SPI_OK_SELECT) && (proc > 0))
{
SPITupleTable *spi_tuptable = SPI_tuptable;
TupleDesc spi_tupdesc = spi_tuptable->tupdesc; int ncols = spi_tupdesc->natts; char *rowid; char *lastrowid = NULL; bool firstpass = true;
uint64 i; int j; int result_ncols;
if (num_categories == 0)
{ /* no qualifying category tuples */
ereport(ERROR,
(errcode(ERRCODE_CARDINALITY_VIOLATION),
errmsg("crosstab categories query must return at least one row")));
}
/* *TheprovidedSQLquerymustalwaysreturnatleastthreecolumns: * *1.rownamethelabelforeachrow-column1inthefinalresult *2.categorythelabelforeachvalue-columninthefinalresult3. *valuethevaluesusedtopopulatethevalue-columns * *Iftherearemorethanthreecolumns,thelasttwoaretakenas *"category"and"values".Thefirstcolumnistakenas"rowname". *Additionalcolumns(2thruN-2)areassumedthesameforthesame *"rowname",andarecopiedintotheresulttuplefromthefirsttime *weencounteraparticularrowname.
*/ if (ncols < 3)
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("invalid crosstab source data query"),
errdetail("The query must return at least 3 columns: row_name, category, and value.")));
result_ncols = (ncols - 2) + num_categories;
/* Recheck to make sure output tuple descriptor looks reasonable */ if (tupdesc->natts != result_ncols)
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
errmsg("invalid crosstab return type"),
errdetail("Return row must have %d columns, not %d.",
result_ncols, tupdesc->natts)));
/* allocate space and make sure it's clear */
values = (char **) palloc0(result_ncols * sizeof(char *));
for (i = 0; i < proc; i++)
{
HeapTuple spi_tuple;
crosstab_cat_desc *catdesc; char *catname;
/* get the next sql result tuple */
spi_tuple = spi_tuptable->vals[i];
/* get the rowid from the current sql result tuple */
rowid = SPI_getvalue(spi_tuple, spi_tupdesc, 1);
/* *ifwe'reonanewoutputrow,grabthecolumnvaluesupto *columnN-2now
*/ if (firstpass || !xstreq(lastrowid, rowid))
{ /* *anewrowmeansweneedtoflushtheoldonefirst,unless *we'reontheveryfirstrow
*/ if (!firstpass)
{ /* rowid changed, flush the previous output row */
tuple = BuildTupleFromCStrings(attinmeta, values);
tuplestore_puttuple(tupstore, tuple);
for (j = 0; j < result_ncols; j++)
xpfree(values[j]);
}
/* 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) ||
rsinfo->expectedDesc == NULL)
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("materialize mode required, but it is not allowed in this context")));
if (fcinfo->nargs == 6)
{
branch_delim = text_to_cstring(PG_GETARG_TEXT_PP(5));
show_branch = true;
} else /* default is no show, tilde for the delimiter */
branch_delim = pstrdup("~");
/* 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) ||
rsinfo->expectedDesc == NULL)
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("materialize mode required, but it is not allowed in this context")));
if (fcinfo->nargs == 7)
{
branch_delim = text_to_cstring(PG_GETARG_TEXT_PP(6));
show_branch = true;
} else /* default is no show, tilde for the delimiter */
branch_delim = pstrdup("~");
/* now go get the whole tree */
build_tuplestore_recursively(key_fld,
parent_key_fld,
relname,
orderby_fld,
branch_delim,
start_with,
start_with, /* current_branch */ 0, /* initial level is 0 */
&serial, /* initial serial is 1 */
max_depth,
show_branch,
show_serial,
per_query_ctx,
attinmeta,
tupstore);
/* root branch is just starting root value */ if (show_branch)
values[3] = start_with;
/* root starts the serial with 1 */ if (show_serial)
{
sprintf(serial_str, "%d", (*serial)++); if (show_branch)
values[4] = serial_str; else
values[3] = serial_str;
}
/* construct the tuple */
tuple = BuildTupleFromCStrings(attinmeta, values);
/* now store it */
tuplestore_puttuple(tupstore, tuple);
/* increment level */
level++;
}
/* Retrieve the desired rows */
ret = SPI_execute(sql.data, true, 0);
proc = SPI_processed;
for (i = 0; i < proc; i++)
{ /* initialize branch for this pass */
appendStringInfoString(&branchstr, branch);
appendStringInfo(&chk_branchstr, "%s%s%s", branch_delim, branch, branch_delim);
/* get the next sql result tuple */
spi_tuple = tuptable->vals[i];
/* get the current key (might be NULL) */
current_key = SPI_getvalue(spi_tuple, spi_tupdesc, 1);
/* get the parent key (might be NULL) */
current_key_parent = SPI_getvalue(spi_tuple, spi_tupdesc, 2);
/* get the current level */
sprintf(current_level, "%d", level);
/* check to see if this key is also an ancestor */ if (current_key)
{
appendStringInfo(&chk_current_key, "%s%s%s",
branch_delim, current_key, branch_delim); if (strstr(chk_branchstr.data, chk_current_key.data))
ereport(ERROR,
(errcode(ERRCODE_INVALID_RECURSION),
errmsg("infinite recursion detected")));
}
/* OK, extend the branch */ if (current_key)
appendStringInfo(&branchstr, "%s%s", branch_delim, current_key);
current_branch = branchstr.data;
/* build a tuple */
values[0] = current_key;
values[1] = current_key_parent;
values[2] = current_level; if (show_branch)
values[3] = current_branch; if (show_serial)
{
sprintf(serial_str, "%d", (*serial)++); if (show_branch)
values[4] = serial_str; else
values[3] = serial_str;
}
/* are there the correct number of columns */ if (show_branch)
expected_cols = CONNECTBY_NCOLS; else
expected_cols = CONNECTBY_NCOLS_NOBRANCH; if (show_serial)
expected_cols++;
if (td->natts != expected_cols)
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
errmsg("invalid connectby return type"),
errdetail("Return row must have %d columns, not %d.",
expected_cols, td->natts)));
/* the first two columns will be checked against the input tuples later */
/* check that the type of the third column is INT4 */ if (TupleDescAttr(td, 2)->atttypid != INT4OID)
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
errmsg("invalid connectby return type"),
errdetail("Third return column (depth) must be type %s.",
format_type_be(INT4OID))));
/* check that the type of the branch column is TEXT if applicable */ if (show_branch && TupleDescAttr(td, 3)->atttypid != TEXTOID)
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
errmsg("invalid connectby return type"),
errdetail("Fourth return column (branch) must be type %s.",
format_type_be(TEXTOID))));
/* check that the type of the serial column is INT4 if applicable */ if (show_branch && show_serial &&
TupleDescAttr(td, 4)->atttypid != INT4OID)
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
errmsg("invalid connectby return type"),
errdetail("Fifth return column (serial) must be type %s.",
format_type_be(INT4OID)))); if (!show_branch && show_serial &&
TupleDescAttr(td, 3)->atttypid != INT4OID)
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
errmsg("invalid connectby return type"),
errdetail("Fourth return column (serial) must be type %s.",
format_type_be(INT4OID))));
/* OK, the tupdesc is valid for our purposes */
}
/* *CheckifoutputtupdescandSQLquery'stupdescarecompatible
*/ staticvoid
compatConnectbyTupleDescs(TupleDesc ret_tupdesc, TupleDesc sql_tupdesc)
{
Oid ret_atttypid;
Oid sql_atttypid;
int32 ret_atttypmod;
int32 sql_atttypmod;
/* *Queryresultmusthaveatleast2columns.
*/ if (sql_tupdesc->natts < 2)
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("invalid connectby source data query"),
errdetail("The query must return at least two columns.")));
/* *Thesecolumnsmustmatchtheresulttypeindicatedbythecalling *query.
*/
ret_atttypid = TupleDescAttr(ret_tupdesc, 0)->atttypid;
sql_atttypid = TupleDescAttr(sql_tupdesc, 0)->atttypid;
ret_atttypmod = TupleDescAttr(ret_tupdesc, 0)->atttypmod;
sql_atttypmod = TupleDescAttr(sql_tupdesc, 0)->atttypmod; if (ret_atttypid != sql_atttypid ||
(ret_atttypmod >= 0 && ret_atttypmod != sql_atttypmod))
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
errmsg("invalid connectby return type"),
errdetail("Source key type %s does not match return key type %s.",
format_type_with_typemod(sql_atttypid, sql_atttypmod),
format_type_with_typemod(ret_atttypid, ret_atttypmod))));
ret_atttypid = TupleDescAttr(ret_tupdesc, 1)->atttypid;
sql_atttypid = TupleDescAttr(sql_tupdesc, 1)->atttypid;
ret_atttypmod = TupleDescAttr(ret_tupdesc, 1)->atttypmod;
sql_atttypmod = TupleDescAttr(sql_tupdesc, 1)->atttypmod; if (ret_atttypid != sql_atttypid ||
(ret_atttypmod >= 0 && ret_atttypmod != sql_atttypmod))
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
errmsg("invalid connectby return type"),
errdetail("Source parent key type %s does not match return parent key type %s.",
format_type_with_typemod(sql_atttypid, sql_atttypmod),
format_type_with_typemod(ret_atttypid, ret_atttypmod))));
/* OK, the two tupdescs are compatible for our purposes */
}
/* *Checkifcrosstaboutputtupdescagreeswithinputtupdesc
*/ staticvoid
compatCrosstabTupleDescs(TupleDesc ret_tupdesc, TupleDesc sql_tupdesc)
{ int i;
Oid ret_atttypid;
Oid sql_atttypid;
int32 ret_atttypmod;
int32 sql_atttypmod;
if (ret_tupdesc->natts < 2)
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
errmsg("invalid crosstab return type"),
errdetail("Return row must have at least two columns.")));
Assert(sql_tupdesc->natts == 3); /* already checked by caller */
/* check the row_name types match */
ret_atttypid = TupleDescAttr(ret_tupdesc, 0)->atttypid;
sql_atttypid = TupleDescAttr(sql_tupdesc, 0)->atttypid;
ret_atttypmod = TupleDescAttr(ret_tupdesc, 0)->atttypmod;
sql_atttypmod = TupleDescAttr(sql_tupdesc, 0)->atttypmod; if (ret_atttypid != sql_atttypid ||
(ret_atttypmod >= 0 && ret_atttypmod != sql_atttypmod))
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
errmsg("invalid crosstab return type"),
errdetail("Source row_name datatype %s does not match return row_name datatype %s.",
format_type_with_typemod(sql_atttypid, sql_atttypmod),
format_type_with_typemod(ret_atttypid, ret_atttypmod))));
/* *attribute[1]ofsqltupleisthecategory;noneedtocheckit *attribute[2]ofsqltupleshouldmatchattributes[1]to[natts-1] *ofthereturntuple
*/
sql_atttypid = TupleDescAttr(sql_tupdesc, 2)->atttypid;
sql_atttypmod = TupleDescAttr(sql_tupdesc, 2)->atttypmod; for (i = 1; i < ret_tupdesc->natts; i++)
{
ret_atttypid = TupleDescAttr(ret_tupdesc, i)->atttypid;
ret_atttypmod = TupleDescAttr(ret_tupdesc, i)->atttypmod;
if (ret_atttypid != sql_atttypid ||
(ret_atttypmod >= 0 && ret_atttypmod != sql_atttypmod))
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
errmsg("invalid crosstab return type"),
errdetail("Source value datatype %s does not match return value datatype %s in column %d.",
format_type_with_typemod(sql_atttypid, sql_atttypmod),
format_type_with_typemod(ret_atttypid, ret_atttypmod),
i + 1)));
}
/* OK, the two tupdescs are compatible for our purposes */
}
Messung V0.5 in Prozent
¤ Dauer der Verarbeitung: 0.48 Sekunden
(vorverarbeitet am 2026-08-08)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.