2 * contrib/tablefunc/tablefunc.c
7 * Sample to demonstrate C functions which return setof scalar
9 * Joe Conway <mail@joeconway.com>
11 * Nabil Sayegh <postgresql@e-trolley.de>
13 * Copyright (c) 2002-2025, PostgreSQL Global Development Group
15 * Permission to use, copy, modify, and distribute this software and its
16 * documentation for any purpose, without fee, and without a written agreement
17 * is hereby granted, provided that the above copyright notice and this
18 * paragraph and the following two paragraphs appear in all copies.
20 * IN NO EVENT SHALL THE AUTHORS OR DISTRIBUTORS BE LIABLE TO ANY PARTY FOR
21 * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING
22 * LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS
23 * DOCUMENTATION, EVEN IF THE AUTHOR OR DISTRIBUTORS HAVE BEEN ADVISED OF THE
24 * POSSIBILITY OF SUCH DAMAGE.
26 * THE AUTHORS AND DISTRIBUTORS SPECIFICALLY DISCLAIM ANY WARRANTIES,
27 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
28 * AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
29 * ON AN "AS IS" BASIS, AND THE AUTHOR AND DISTRIBUTORS HAS NO OBLIGATIONS TO
30 * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
97 #define xpfree(var_) \
106 #define xpstrdup(tgtvar_, srcvar_) \
109 tgtvar_ = pstrdup(srcvar_); \
114 #define xstreq(tgtvar_, srcvar_) \
115 (((tgtvar_ == NULL) && (srcvar_ == NULL)) || \
116 ((tgtvar_ != NULL) && (srcvar_ != NULL) && (strcmp(tgtvar_, srcvar_) == 0)))
118/* sign, 10 digits, '0円' */
119 #define INT32_STRLEN 12
121/* stored info for a crosstab category */
128 #define MAX_CATNAME_LEN NAMEDATALEN
131 #define crosstab_HashTableLookup(HASHTAB, CATNAME, CATDESC) \
133 crosstab_HashEnt *hentry; char key[MAX_CATNAME_LEN]; \
135 MemSet(key, 0, MAX_CATNAME_LEN); \
136 snprintf(key, MAX_CATNAME_LEN - 1, "%s", CATNAME); \
137 hentry = (crosstab_HashEnt*) hash_search(HASHTAB, \
138 key, HASH_FIND, NULL); \
140 CATDESC = hentry->catdesc; \
145 #define crosstab_HashTableInsert(HASHTAB, CATDESC) \
147 crosstab_HashEnt *hentry; bool found; char key[MAX_CATNAME_LEN]; \
149 MemSet(key, 0, MAX_CATNAME_LEN); \
150 snprintf(key, MAX_CATNAME_LEN - 1, "%s", CATDESC->catname); \
151 hentry = (crosstab_HashEnt*) hash_search(HASHTAB, \
152 key, HASH_ENTER, &found); \
155 (errcode(ERRCODE_DUPLICATE_OBJECT), \
156 errmsg("duplicate category name"))); \
157 hentry->catdesc = CATDESC; \
168 * normal_rand - return requested number of random values
169 * with a Gaussian (Normal) distribution.
171 * inputs are int numvals, float8 mean, and float8 stddev
172 * returns setof float8
188 /* stuff done only on the first call of the function */
193 /* create a function context for cross-call persistence */
197 * switch to memory context appropriate for multiple function calls
201 /* total number of tuples to be returned */
205 (
errcode(ERRCODE_INVALID_PARAMETER_VALUE),
206 errmsg(
"number of rows cannot be negative")));
209 /* allocate memory for user context */
213 * Use fctx to keep track of upper and lower bounds from call to call.
214 * It will also be used to carry over the spare value we get from the
215 * Box-Muller algorithm so that we only actually calculate a new value
228 /* stuff done on every call of the function */
239 if (call_cntr < max_calls)
/* do when there is more left to send */
246 * reset use_carry and use second value obtained on last pass
256 /* Get the next two normal values */
260 result = mean + (stddev * normval_1);
262 /* and save the second */
263 fctx->
carry_val = mean + (stddev * normval_2);
267 /* send the result */
271 /* do when there is no more left */
277 * Assigns normally distributed (Gaussian) values to a pair of provided
278 * parameters, with mean 0, standard deviation 1.
280 * This routine implements Algorithm P (Polar method for normal deviates)
281 * from Knuth's _The_Art_of_Computer_Programming_, Volume 2, 3rd ed., pages
282 * 122-126. Knuth cites his source as "The polar method", G. E. P. Box, M. E.
283 * Muller, and G. Marsaglia, _Annals_Math,_Stat._ 29 (1958), 610-611.
300 v1 = (2.0 * u1) - 1.0;
301 v2 = (2.0 * u2) - 1.0;
303 s = v1 * v1 + v2 * v2;
313 s = sqrt((-2.0 * log(s)) / s);
320 * crosstab - create a crosstab of rowids and values columns from a
321 * SQL statement returning one rowid column, one category column,
322 * and one value column.
324 * e.g. given sql which produces:
327 * ------+-------+-------
338 * <===== values columns =====>
339 * rowid cat1 cat2 cat3 cat4
340 * ------+-------+-------+-------+-------
341 * row1 val1 val2 val3 val4
342 * row2 val5 val6 val7 val8
345 * 1. SQL result must be ordered by 1,2.
346 * 2. The number of values columns depends on the tuple description
347 * of the function's declared return type. The return type's columns
348 * must match the datatypes of the SQL query's result. The datatype
349 * of the category column can be anything, however.
350 * 3. Missing values (i.e. not enough adjacent rows of same rowid to
351 * fill the number of result values columns) are filled in with nulls.
352 * 4. Extra values (i.e. too many adjacent rows of same rowid to fill
353 * the number of result values columns) are skipped.
354 * 5. Rows with all nulls in the values columns are skipped.
378 /* check to see if caller supports us returning a tuplestore */
381 (
errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
382 errmsg(
"set-valued function called in context that cannot accept a set")));
385 (
errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
386 errmsg(
"materialize mode required, but it is not allowed in this context")));
390 /* Connect to SPI manager */
393 /* Retrieve the desired rows */
397 /* If no qualifying tuples, fall out early */
406 spi_tupdesc = spi_tuptable->
tupdesc;
409 * The provided SQL query must always return three columns.
412 * the label or identifier for each row in the final result
414 * the label or identifier for each column in the final result
416 * the value for each column in the final result
419 if (spi_tupdesc->natts != 3)
421 (
errcode(ERRCODE_INVALID_PARAMETER_VALUE),
422 errmsg(
"invalid crosstab source data query"),
423 errdetail(
"The query must return 3 columns: row_name, category, and value.")));
425 /* get a tuple descriptor for our result type */
432 /* failed to determine actual type of RECORD */
434 (
errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
435 errmsg(
"function returning record called in context "
436 "that cannot accept type record")));
439 /* result type isn't composite */
441 (
errcode(ERRCODE_DATATYPE_MISMATCH),
442 errmsg(
"return type must be a row type")));
447 * Check that return tupdesc is compatible with the data we got from SPI,
448 * at least based on number and type of attributes
453 * switch to long-lived memory context
457 /* make sure we have a persistent copy of the result tupdesc */
460 /* initialize our tuplestore in long-lived context */
468 * Generate attribute metadata needed later to produce tuples from raw C
473 /* total number of tuples to be examined */
476 /* the return tuple always must have 1 rowid + num_categories columns */
477 num_categories = tupdesc->natts - 1;
482 for (call_cntr = 0; call_cntr < max_calls; call_cntr++)
484 bool skip_tuple =
false;
487 /* allocate and zero space */
488 values = (
char **)
palloc0((1 + num_categories) *
sizeof(
char *));
491 * now loop through the sql results and assign each value in sequence
492 * to the next category
494 for (
i = 0;
i < num_categories;
i++)
499 /* see if we've gone too far already */
500 if (call_cntr >= max_calls)
503 /* get the next sql result tuple */
504 spi_tuple = spi_tuptable->vals[call_cntr];
506 /* get the rowid from the current sql result tuple */
510 * If this is the first pass through the values for this rowid,
511 * set the first column to rowid
518 * Check to see if the rowid is the same as that of the last
519 * tuple sent -- if so, skip this tuple entirely
521 if (!firstpass &&
xstreq(lastrowid, rowid))
530 * If rowid hasn't changed on us, continue building the output
536 * Get the next category item value, which is always attribute
539 * Be careful to assign the value to the array index based on
540 * which category we are presently processing.
545 * increment the counter since we consume a row for each
546 * category, but not for last pass because the outer loop will
549 if (
i < (num_categories - 1))
556 * We'll fill in NULLs for the missing values, but we need to
557 * decrement the counter since this sql result row doesn't
558 * belong to the current output tuple.
570 /* build the tuple and store it */
576 /* Remember current rowid */
582 for (
i = 0;
i < num_categories + 1;
i++)
588 /* let the caller know we're sending back a tuplestore */
593 /* release SPI related resources (and return to caller's context) */
600 * crosstab_hash - reimplement crosstab as materialized function and
601 * properly deal with missing values (i.e. don't pack remaining
602 * values to the left)
604 * crosstab - create a crosstab of rowids and values columns from a
605 * SQL statement returning one rowid column, one category column,
606 * and one value column.
608 * e.g. given sql which produces:
611 * ------+-------+-------
621 * <===== values columns =====>
622 * rowid cat1 cat2 cat3 cat4
623 * ------+-------+-------+-------+-------
624 * row1 val1 val2 null val4
625 * row2 val5 val6 val7 val8
628 * 1. SQL result must be ordered by 1.
629 * 2. The number of values columns depends on the tuple description
630 * of the function's declared return type.
631 * 3. Missing values (i.e. missing category) are filled in with nulls.
632 * 4. Extra values (i.e. not in category results) are skipped.
646 /* check to see if caller supports us returning a tuplestore */
649 (
errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
650 errmsg(
"set-valued function called in context that cannot accept a set")));
654 (
errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
655 errmsg(
"materialize mode required, but it is not allowed in this context")));
660 /* get the requested return tuple description */
664 * Check to make sure we have a reasonable tuple descriptor
666 * Note we will attempt to coerce the values into whatever the return
667 * attribute type is and depend on the "in" function to complain if
670 if (tupdesc->natts < 2)
672 (
errcode(ERRCODE_DATATYPE_MISMATCH),
673 errmsg(
"invalid crosstab return type"),
674 errdetail(
"Return row must have at least two columns.")));
676 /* load up the categories hash table */
679 /* let the caller know we're sending back a tuplestore */
682 /* now go build it */
689 * SFRM_Materialize mode expects us to return a NULL Datum. The actual
690 * tuples are in our tuplestore and passed back through rsinfo->setResult.
691 * rsinfo->setDesc is set to the tuple description that we actually used
692 * to build our tuples with, so the caller can verify we did what it was
702 * load up the categories hash table
713 /* initialize the category hash table */
716 ctl.hcxt = per_query_ctx;
719 * use INIT_CATS, defined above as a guess of how many hash table entries
720 * to create, initially
727 /* Connect to SPI manager */
730 /* Retrieve the category name rows */
734 /* Check for qualifying tuples */
742 * The provided categories SQL query must always return one column:
743 * category - the label or identifier for each column
745 if (spi_tupdesc->
natts != 1)
747 (
errcode(ERRCODE_INVALID_PARAMETER_VALUE),
748 errmsg(
"invalid crosstab categories query"),
749 errdetail(
"The query must return one column.")));
751 for (
i = 0;
i < proc;
i++)
757 /* get the next sql result tuple */
758 spi_tuple = spi_tuptable->
vals[
i];
760 /* get the category from the current sql result tuple */
764 (
errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
765 errmsg(
"crosstab category value must not be null")));
773 /* Add the proc description block to the hashtable */
782 elog(
ERROR,
"load_categories_hash: SPI_finish() failed");
788 * create and populate the crosstab tuplestore using the provided source query
804 /* initialize our tuplestore (while still in query context!) */
807 /* Connect to SPI manager */
810 /* Now retrieve the crosstab source rows */
814 /* Check for qualifying tuples */
819 int ncols = spi_tupdesc->
natts;
821 char *lastrowid = NULL;
822 bool firstpass =
true;
827 if (num_categories == 0)
829 /* no qualifying category tuples */
831 (
errcode(ERRCODE_CARDINALITY_VIOLATION),
832 errmsg(
"crosstab categories query must return at least one row")));
836 * The provided SQL query must always return at least three columns:
838 * 1. rowname the label for each row - column 1 in the final result
839 * 2. category the label for each value-column in the final result 3.
840 * value the values used to populate the value-columns
842 * If there are more than three columns, the last two are taken as
843 * "category" and "values". The first column is taken as "rowname".
844 * Additional columns (2 thru N-2) are assumed the same for the same
845 * "rowname", and are copied into the result tuple from the first time
846 * we encounter a particular rowname.
850 (
errcode(ERRCODE_INVALID_PARAMETER_VALUE),
851 errmsg(
"invalid crosstab source data query"),
852 errdetail(
"The query must return at least 3 columns: row_name, category, and value.")));
854 result_ncols = (ncols - 2) + num_categories;
856 /* Recheck to make sure output tuple descriptor looks reasonable */
857 if (tupdesc->
natts != result_ncols)
859 (
errcode(ERRCODE_DATATYPE_MISMATCH),
860 errmsg(
"invalid crosstab return type"),
861 errdetail(
"Return row must have %d columns, not %d.",
862 result_ncols, tupdesc->
natts)));
864 /* allocate space and make sure it's clear */
867 for (
i = 0;
i < proc;
i++)
873 /* get the next sql result tuple */
874 spi_tuple = spi_tuptable->
vals[
i];
876 /* get the rowid from the current sql result tuple */
880 * if we're on a new output row, grab the column values up to
883 if (firstpass || !
xstreq(lastrowid, rowid))
886 * a new row means we need to flush the old one first, unless
887 * we're on the very first row
891 /* rowid changed, flush the previous output row */
896 for (
j = 0;
j < result_ncols;
j++)
901 for (
j = 1;
j < ncols - 2;
j++)
904 /* we're no longer on the first pass */
908 /* look up the category and fill in the appropriate column */
909 catname =
SPI_getvalue(spi_tuple, spi_tupdesc, ncols - 1);
924 /* flush the last output row */
932 elog(
ERROR,
"get_crosstab_tuplestore: SPI_finish() failed");
938 * connectby_text - produce a result set from a hierarchical (parent/child)
941 * e.g. given table foo:
943 * keyid parent_keyid pos
944 * ------+------------+--
956 * connectby(text relname, text keyid_fld, text parent_keyid_fld
957 * [, text orderby_fld], text start_with, int max_depth
958 * [, text branch_delim])
959 * connectby('foo', 'keyid', 'parent_keyid', 'pos', 'row2', 0, '~') returns:
961 * keyid parent_id level branch serial
962 * ------+-----------+--------+-----------------------
964 * row5 row2 1 row2~row5 2
965 * row9 row5 2 row2~row5~row9 3
966 * row4 row2 1 row2~row4 4
967 * row6 row4 2 row2~row4~row6 5
968 * row8 row6 3 row2~row4~row6~row8 6
973 #define CONNECTBY_NCOLS 4
974 #define CONNECTBY_NCOLS_NOBRANCH 3
984 char *branch_delim = NULL;
985 bool show_branch =
false;
986 bool show_serial =
false;
993 /* check to see if caller supports us returning a tuplestore */
996 (
errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
997 errmsg(
"set-valued function called in context that cannot accept a set")));
1001 (
errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1002 errmsg(
"materialize mode required, but it is not allowed in this context")));
1004 if (fcinfo->nargs == 6)
1010 /* default is no show, tilde for the delimiter */
1016 /* get the requested return tuple description */
1019 /* does it meet our needs */
1022 /* OK, use it then */
1025 /* OK, go to work */
1044 * SFRM_Materialize mode expects us to return a NULL Datum. The actual
1045 * tuples are in our tuplestore and passed back through rsinfo->setResult.
1046 * rsinfo->setDesc is set to the tuple description that we actually used
1047 * to build our tuples with, so the caller can verify we did what it was
1063 char *branch_delim = NULL;
1064 bool show_branch =
false;
1065 bool show_serial =
true;
1072 /* check to see if caller supports us returning a tuplestore */
1075 (
errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1076 errmsg(
"set-valued function called in context that cannot accept a set")));
1080 (
errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1081 errmsg(
"materialize mode required, but it is not allowed in this context")));
1083 if (fcinfo->nargs == 7)
1089 /* default is no show, tilde for the delimiter */
1095 /* get the requested return tuple description */
1098 /* does it meet our needs */
1101 /* OK, use it then */
1104 /* OK, go to work */
1123 * SFRM_Materialize mode expects us to return a NULL Datum. The actual
1124 * tuples are in our tuplestore and passed back through rsinfo->setResult.
1125 * rsinfo->setDesc is set to the tuple description that we actually used
1126 * to build our tuples with, so the caller can verify we did what it was
1134 * connectby - does the real work for connectby_text()
1139 char *parent_key_fld,
1154 /* Connect to SPI manager */
1157 /* switch to longer term context to create the tuple store */
1160 /* initialize our tuplestore */
1165 /* now go get the whole tree */
1172 start_with,
/* current_branch */
1173 0,
/* initial level is 0 */
1174 &serial,
/* initial serial is 1 */
1189 char *parent_key_fld,
1211 char *current_key_parent;
1214 char *current_branch;
1217 if (max_depth > 0 && level > max_depth)
1222 /* Build initial sql statement */
1225 appendStringInfo(&sql,
"SELECT %s, %s FROM %s WHERE %s = %s AND %s IS NOT NULL AND %s <> %s",
1231 key_fld, key_fld, parent_key_fld);
1236 appendStringInfo(&sql,
"SELECT %s, %s FROM %s WHERE %s = %s AND %s IS NOT NULL AND %s <> %s ORDER BY %s",
1242 key_fld, key_fld, parent_key_fld,
1252 /* First time through, do a little setup */
1255 /* root value is the one we initially start with */
1258 /* root value has no parent */
1261 /* root level is 0 */
1262 sprintf(current_level,
"%d", level);
1263 values[2] = current_level;
1265 /* root branch is just starting root value */
1269 /* root starts the serial with 1 */
1272 sprintf(serial_str,
"%d", (*serial)++);
1279 /* construct the tuple */
1285 /* increment level */
1289 /* Retrieve the desired rows */
1293 /* Check for qualifying tuples */
1305 * Check that return tupdesc is compatible with the one we got from
1314 for (
i = 0;
i < proc;
i++)
1316 /* initialize branch for this pass */
1318 appendStringInfo(&chk_branchstr,
"%s%s%s", branch_delim, branch, branch_delim);
1320 /* get the next sql result tuple */
1321 spi_tuple = tuptable->
vals[
i];
1323 /* get the current key (might be NULL) */
1326 /* get the parent key (might be NULL) */
1327 current_key_parent =
SPI_getvalue(spi_tuple, spi_tupdesc, 2);
1329 /* get the current level */
1330 sprintf(current_level,
"%d", level);
1332 /* check to see if this key is also an ancestor */
1336 branch_delim, current_key, branch_delim);
1337 if (strstr(chk_branchstr.
data, chk_current_key.
data))
1339 (
errcode(ERRCODE_INVALID_RECURSION),
1340 errmsg(
"infinite recursion detected")));
1343 /* OK, extend the branch */
1346 current_branch = branchstr.
data;
1350 values[1] = current_key_parent;
1351 values[2] = current_level;
1353 values[3] = current_branch;
1356 sprintf(serial_str,
"%d", (*serial)++);
1365 /* store the tuple for later use */
1370 /* recurse using current_key as the new start_with */
1389 xpfree(current_key_parent);
1391 /* reset branch for next pass */
1404 * Check expected (query runtime) tupdesc suitable for Connectby
1411 /* are there the correct number of columns */
1419 if (td->
natts != expected_cols)
1421 (
errcode(ERRCODE_DATATYPE_MISMATCH),
1422 errmsg(
"invalid connectby return type"),
1423 errdetail(
"Return row must have %d columns, not %d.",
1424 expected_cols, td->
natts)));
1426 /* the first two columns will be checked against the input tuples later */
1428 /* check that the type of the third column is INT4 */
1431 (
errcode(ERRCODE_DATATYPE_MISMATCH),
1432 errmsg(
"invalid connectby return type"),
1433 errdetail(
"Third return column (depth) must be type %s.",
1436 /* check that the type of the branch column is TEXT if applicable */
1437 if (show_branch &&
TupleDescAttr(td, 3)->atttypid != TEXTOID)
1439 (
errcode(ERRCODE_DATATYPE_MISMATCH),
1440 errmsg(
"invalid connectby return type"),
1441 errdetail(
"Fourth return column (branch) must be type %s.",
1444 /* check that the type of the serial column is INT4 if applicable */
1445 if (show_branch && show_serial &&
1448 (
errcode(ERRCODE_DATATYPE_MISMATCH),
1449 errmsg(
"invalid connectby return type"),
1450 errdetail(
"Fifth return column (serial) must be type %s.",
1452 if (!show_branch && show_serial &&
1455 (
errcode(ERRCODE_DATATYPE_MISMATCH),
1456 errmsg(
"invalid connectby return type"),
1457 errdetail(
"Fourth return column (serial) must be type %s.",
1460 /* OK, the tupdesc is valid for our purposes */
1464 * Check if output tupdesc and SQL query's tupdesc are compatible
1471 int32 ret_atttypmod;
1472 int32 sql_atttypmod;
1475 * Query result must have at least 2 columns.
1477 if (sql_tupdesc->
natts < 2)
1479 (
errcode(ERRCODE_INVALID_PARAMETER_VALUE),
1480 errmsg(
"invalid connectby source data query"),
1481 errdetail(
"The query must return at least two columns.")));
1484 * These columns must match the result type indicated by the calling
1491 if (ret_atttypid != sql_atttypid ||
1492 (ret_atttypmod >= 0 && ret_atttypmod != sql_atttypmod))
1494 (
errcode(ERRCODE_DATATYPE_MISMATCH),
1495 errmsg(
"invalid connectby return type"),
1496 errdetail(
"Source key type %s does not match return key type %s.",
1504 if (ret_atttypid != sql_atttypid ||
1505 (ret_atttypmod >= 0 && ret_atttypmod != sql_atttypmod))
1507 (
errcode(ERRCODE_DATATYPE_MISMATCH),
1508 errmsg(
"invalid connectby return type"),
1509 errdetail(
"Source parent key type %s does not match return parent key type %s.",
1513 /* OK, the two tupdescs are compatible for our purposes */
1517 * Check if crosstab output tupdesc agrees with input tupdesc
1525 int32 ret_atttypmod;
1526 int32 sql_atttypmod;
1528 if (ret_tupdesc->
natts < 2)
1530 (
errcode(ERRCODE_DATATYPE_MISMATCH),
1531 errmsg(
"invalid crosstab return type"),
1532 errdetail(
"Return row must have at least two columns.")));
1533 Assert(sql_tupdesc->
natts == 3);
/* already checked by caller */
1535 /* check the row_name types match */
1540 if (ret_atttypid != sql_atttypid ||
1541 (ret_atttypmod >= 0 && ret_atttypmod != sql_atttypmod))
1543 (
errcode(ERRCODE_DATATYPE_MISMATCH),
1544 errmsg(
"invalid crosstab return type"),
1545 errdetail(
"Source row_name datatype %s does not match return row_name datatype %s.",
1550 * attribute [1] of sql tuple is the category; no need to check it
1551 * attribute [2] of sql tuple should match attributes [1] to [natts - 1]
1552 * of the return tuple
1556 for (
i = 1;
i < ret_tupdesc->
natts;
i++)
1561 if (ret_atttypid != sql_atttypid ||
1562 (ret_atttypmod >= 0 && ret_atttypmod != sql_atttypmod))
1564 (
errcode(ERRCODE_DATATYPE_MISMATCH),
1565 errmsg(
"invalid crosstab return type"),
1566 errdetail(
"Source value datatype %s does not match return value datatype %s in column %d.",
1572 /* OK, the two tupdescs are compatible for our purposes */
static Datum values[MAXATTR]
HTAB * hash_create(const char *tabname, int64 nelem, const HASHCTL *info, int flags)
int64 hash_get_num_entries(HTAB *hashp)
int errdetail(const char *fmt,...)
int errcode(int sqlerrcode)
int errmsg(const char *fmt,...)
#define ereport(elevel,...)
HeapTuple BuildTupleFromCStrings(AttInMetadata *attinmeta, char **values)
AttInMetadata * TupleDescGetAttInMetadata(TupleDesc tupdesc)
@ SFRM_Materialize_Random
#define PG_GETARG_TEXT_PP(n)
#define PG_GETARG_FLOAT8(n)
#define PG_GETARG_INT32(n)
TypeFuncClass get_call_result_type(FunctionCallInfo fcinfo, Oid *resultTypeId, TupleDesc *resultTupleDesc)
#define SRF_IS_FIRSTCALL()
#define SRF_PERCALL_SETUP()
#define SRF_RETURN_NEXT(_funcctx, _result)
#define SRF_FIRSTCALL_INIT()
#define SRF_RETURN_DONE(_funcctx)
Assert(PointerIsAligned(start, uint64))
void heap_freetuple(HeapTuple htup)
if(TABLE==NULL||TABLE_index==NULL)
char * pstrdup(const char *in)
void pfree(void *pointer)
void * palloc0(Size size)
#define IsA(nodeptr, _type_)
static MemoryContext MemoryContextSwitchTo(MemoryContext context)
double pg_prng_double(pg_prng_state *state)
pg_prng_state pg_global_prng_state
static Datum Float8GetDatum(float8 X)
char * quote_literal_cstr(const char *rawstr)
SPITupleTable * SPI_tuptable
char * SPI_getvalue(HeapTuple tuple, TupleDesc tupdesc, int fnumber)
int SPI_execute(const char *src, bool read_only, long tcount)
void resetStringInfo(StringInfo str)
void appendStringInfo(StringInfo str, const char *fmt,...)
void appendStringInfoString(StringInfo str, const char *s)
void initStringInfo(StringInfo str)
MemoryContext ecxt_per_query_memory
MemoryContext multi_call_memory_ctx
SetFunctionReturnMode returnMode
Tuplestorestate * setResult
crosstab_cat_desc * catdesc
char internal_catname[MAX_CATNAME_LEN]
#define crosstab_HashTableInsert(HASHTAB, CATDESC)
#define xpstrdup(tgtvar_, srcvar_)
static void compatCrosstabTupleDescs(TupleDesc ret_tupdesc, TupleDesc sql_tupdesc)
struct crosstab_cat_desc crosstab_cat_desc
Datum connectby_text(PG_FUNCTION_ARGS)
Datum normal_rand(PG_FUNCTION_ARGS)
Datum crosstab(PG_FUNCTION_ARGS)
#define CONNECTBY_NCOLS_NOBRANCH
static void get_normal_pair(float8 *x1, float8 *x2)
#define crosstab_HashTableLookup(HASHTAB, CATNAME, CATDESC)
PG_MODULE_MAGIC_EXT(.name="tablefunc",.version=PG_VERSION)
static Tuplestorestate * get_crosstab_tuplestore(char *sql, HTAB *crosstab_hash, TupleDesc tupdesc, bool randomAccess)
Datum connectby_text_serial(PG_FUNCTION_ARGS)
static void build_tuplestore_recursively(char *key_fld, char *parent_key_fld, char *relname, char *orderby_fld, char *branch_delim, char *start_with, char *branch, int level, int *serial, int max_depth, bool show_branch, bool show_serial, MemoryContext per_query_ctx, AttInMetadata *attinmeta, Tuplestorestate *tupstore)
Datum crosstab_hash(PG_FUNCTION_ARGS)
static void compatConnectbyTupleDescs(TupleDesc ret_tupdesc, TupleDesc sql_tupdesc)
#define xstreq(tgtvar_, srcvar_)
static void validateConnectbyTupleDesc(TupleDesc td, bool show_branch, bool show_serial)
static Tuplestorestate * connectby(char *relname, char *key_fld, char *parent_key_fld, char *orderby_fld, char *branch_delim, char *start_with, int max_depth, bool show_branch, bool show_serial, MemoryContext per_query_ctx, bool randomAccess, AttInMetadata *attinmeta)
PG_FUNCTION_INFO_V1(normal_rand)
struct crosstab_hashent crosstab_HashEnt
static HTAB * load_categories_hash(char *cats_sql, MemoryContext per_query_ctx)
TupleDesc CreateTupleDescCopy(TupleDesc tupdesc)
static FormData_pg_attribute * TupleDescAttr(TupleDesc tupdesc, int i)
Tuplestorestate * tuplestore_begin_heap(bool randomAccess, bool interXact, int maxKBytes)
void tuplestore_puttuple(Tuplestorestate *state, HeapTuple tuple)
char * text_to_cstring(const text *t)