PostgreSQL Source Code  git master
tablefunc.c File Reference
#include "postgres.h"
#include <math.h>
#include "access/htup_details.h"
#include "catalog/pg_type.h"
#include "common/pg_prng.h"
#include "executor/spi.h"
#include "funcapi.h"
#include "lib/stringinfo.h"
#include "miscadmin.h"
#include "tablefunc.h"
#include "utils/builtins.h"
Include dependency graph for tablefunc.c:

Go to the source code of this file.

Data Structures

struct  normal_rand_fctx
 
struct  crosstab_cat_desc
 
struct  crosstab_hashent
 

Macros

#define xpfree(var_)
 
#define xpstrdup(tgtvar_, srcvar_)
 
#define xstreq(tgtvar_, srcvar_)
 
#define INT32_STRLEN   12
 
#define MAX_CATNAME_LEN   NAMEDATALEN
 
#define INIT_CATS   64
 
#define crosstab_HashTableLookup(HASHTAB, CATNAME, CATDESC)
 
#define crosstab_HashTableInsert(HASHTAB, CATDESC)
 
#define CONNECTBY_NCOLS   4
 
#define CONNECTBY_NCOLS_NOBRANCH   3
 

Typedefs

typedef struct crosstab_cat_desc crosstab_cat_desc
 
typedef struct crosstab_hashent crosstab_HashEnt
 

Functions

static HTABload_categories_hash (char *cats_sql, MemoryContext per_query_ctx)
 
static Tuplestorestateget_crosstab_tuplestore (char *sql, HTAB *crosstab_hash, TupleDesc tupdesc, bool randomAccess)
 
static void validateConnectbyTupleDesc (TupleDesc td, bool show_branch, bool show_serial)
 
static void compatCrosstabTupleDescs (TupleDesc ret_tupdesc, TupleDesc sql_tupdesc)
 
static void compatConnectbyTupleDescs (TupleDesc ret_tupdesc, TupleDesc sql_tupdesc)
 
static void get_normal_pair (float8 *x1, float8 *x2)
 
static Tuplestorestateconnectby (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)
 
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)
 
 PG_FUNCTION_INFO_V1 (normal_rand)
 
Datum normal_rand (PG_FUNCTION_ARGS)
 
 PG_FUNCTION_INFO_V1 (crosstab)
 
Datum crosstab (PG_FUNCTION_ARGS)
 
 PG_FUNCTION_INFO_V1 (crosstab_hash)
 
Datum crosstab_hash (PG_FUNCTION_ARGS)
 
 PG_FUNCTION_INFO_V1 (connectby_text)
 
Datum connectby_text (PG_FUNCTION_ARGS)
 
 PG_FUNCTION_INFO_V1 (connectby_text_serial)
 
Datum connectby_text_serial (PG_FUNCTION_ARGS)
 

Variables

 PG_MODULE_MAGIC
 

Macro Definition Documentation

◆ CONNECTBY_NCOLS

#define CONNECTBY_NCOLS   4

Definition at line 970 of file tablefunc.c.

◆ CONNECTBY_NCOLS_NOBRANCH

#define CONNECTBY_NCOLS_NOBRANCH   3

Definition at line 971 of file tablefunc.c.

◆ crosstab_HashTableInsert

#define crosstab_HashTableInsert (   HASHTAB,
  CATDESC 
)
Value:
do { \
crosstab_HashEnt *hentry; bool found; char key[MAX_CATNAME_LEN]; \
snprintf(key, MAX_CATNAME_LEN - 1, "%s", CATDESC->catname); \
hentry = (crosstab_HashEnt*) hash_search(HASHTAB, \
key, HASH_ENTER, &found); \
if (found) \
ereport(ERROR, \
errmsg("duplicate category name"))); \
hentry->catdesc = CATDESC; \
} while(0)
#define MemSet(start, val, len)
Definition: c.h:1023
void * hash_search(HTAB *hashp, const void *keyPtr, HASHACTION action, bool *foundPtr)
Definition: dynahash.c:955
int errcode(int sqlerrcode)
Definition: elog.c:853
int errmsg(const char *fmt,...)
Definition: elog.c:1070
#define ERROR
Definition: elog.h:39
@ HASH_ENTER
Definition: hsearch.h:114
#define ERRCODE_DUPLICATE_OBJECT
Definition: streamutil.c:32
#define MAX_CATNAME_LEN
Definition: tablefunc.c:125

Definition at line 142 of file tablefunc.c.

◆ crosstab_HashTableLookup

#define crosstab_HashTableLookup (   HASHTAB,
  CATNAME,
  CATDESC 
)
Value:
do { \
crosstab_HashEnt *hentry; char key[MAX_CATNAME_LEN]; \
snprintf(key, MAX_CATNAME_LEN - 1, "%s", CATNAME); \
hentry = (crosstab_HashEnt*) hash_search(HASHTAB, \
key, HASH_FIND, NULL); \
if (hentry) \
CATDESC = hentry->catdesc; \
else \
CATDESC = NULL; \
} while(0)
@ HASH_FIND
Definition: hsearch.h:113

Definition at line 128 of file tablefunc.c.

◆ INIT_CATS

#define INIT_CATS   64

Definition at line 126 of file tablefunc.c.

◆ INT32_STRLEN

#define INT32_STRLEN   12

Definition at line 116 of file tablefunc.c.

◆ MAX_CATNAME_LEN

#define MAX_CATNAME_LEN   NAMEDATALEN

Definition at line 125 of file tablefunc.c.

◆ xpfree

#define xpfree (   var_)
Value:
do { \
if (var_ != NULL) \
{ \
pfree(var_); \
var_ = NULL; \
} \
} while (0)

Definition at line 94 of file tablefunc.c.

◆ xpstrdup

#define xpstrdup (   tgtvar_,
  srcvar_ 
)
Value:
do { \
if (srcvar_) \
tgtvar_ = pstrdup(srcvar_); \
else \
tgtvar_ = NULL; \
} while (0)
char * pstrdup(const char *in)
Definition: mcxt.c:1696

Definition at line 103 of file tablefunc.c.

◆ xstreq

#define xstreq (   tgtvar_,
  srcvar_ 
)
Value:
(((tgtvar_ == NULL) && (srcvar_ == NULL)) || \
((tgtvar_ != NULL) && (srcvar_ != NULL) && (strcmp(tgtvar_, srcvar_) == 0)))

Definition at line 111 of file tablefunc.c.

Typedef Documentation

◆ crosstab_cat_desc

◆ crosstab_HashEnt

Function Documentation

◆ build_tuplestore_recursively()

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 
)
static

Definition at line 1185 of file tablefunc.c.

1200 {
1201  TupleDesc tupdesc = attinmeta->tupdesc;
1202  int ret;
1203  uint64 proc;
1204  int serial_column;
1205  StringInfoData sql;
1206  char **values;
1207  char *current_key;
1208  char *current_key_parent;
1209  char current_level[INT32_STRLEN];
1210  char serial_str[INT32_STRLEN];
1211  char *current_branch;
1212  HeapTuple tuple;
1213 
1214  if (max_depth > 0 && level > max_depth)
1215  return;
1216 
1217  initStringInfo(&sql);
1218 
1219  /* Build initial sql statement */
1220  if (!show_serial)
1221  {
1222  appendStringInfo(&sql, "SELECT %s, %s FROM %s WHERE %s = %s AND %s IS NOT NULL AND %s <> %s",
1223  key_fld,
1224  parent_key_fld,
1225  relname,
1226  parent_key_fld,
1227  quote_literal_cstr(start_with),
1228  key_fld, key_fld, parent_key_fld);
1229  serial_column = 0;
1230  }
1231  else
1232  {
1233  appendStringInfo(&sql, "SELECT %s, %s FROM %s WHERE %s = %s AND %s IS NOT NULL AND %s <> %s ORDER BY %s",
1234  key_fld,
1235  parent_key_fld,
1236  relname,
1237  parent_key_fld,
1238  quote_literal_cstr(start_with),
1239  key_fld, key_fld, parent_key_fld,
1240  orderby_fld);
1241  serial_column = 1;
1242  }
1243 
1244  if (show_branch)
1245  values = (char **) palloc((CONNECTBY_NCOLS + serial_column) * sizeof(char *));
1246  else
1247  values = (char **) palloc((CONNECTBY_NCOLS_NOBRANCH + serial_column) * sizeof(char *));
1248 
1249  /* First time through, do a little setup */
1250  if (level == 0)
1251  {
1252  /* root value is the one we initially start with */
1253  values[0] = start_with;
1254 
1255  /* root value has no parent */
1256  values[1] = NULL;
1257 
1258  /* root level is 0 */
1259  sprintf(current_level, "%d", level);
1260  values[2] = current_level;
1261 
1262  /* root branch is just starting root value */
1263  if (show_branch)
1264  values[3] = start_with;
1265 
1266  /* root starts the serial with 1 */
1267  if (show_serial)
1268  {
1269  sprintf(serial_str, "%d", (*serial)++);
1270  if (show_branch)
1271  values[4] = serial_str;
1272  else
1273  values[3] = serial_str;
1274  }
1275 
1276  /* construct the tuple */
1277  tuple = BuildTupleFromCStrings(attinmeta, values);
1278 
1279  /* now store it */
1280  tuplestore_puttuple(tupstore, tuple);
1281 
1282  /* increment level */
1283  level++;
1284  }
1285 
1286  /* Retrieve the desired rows */
1287  ret = SPI_execute(sql.data, true, 0);
1288  proc = SPI_processed;
1289 
1290  /* Check for qualifying tuples */
1291  if ((ret == SPI_OK_SELECT) && (proc > 0))
1292  {
1293  HeapTuple spi_tuple;
1294  SPITupleTable *tuptable = SPI_tuptable;
1295  TupleDesc spi_tupdesc = tuptable->tupdesc;
1296  uint64 i;
1297  StringInfoData branchstr;
1298  StringInfoData chk_branchstr;
1299  StringInfoData chk_current_key;
1300 
1301  /*
1302  * Check that return tupdesc is compatible with the one we got from
1303  * the query.
1304  */
1305  compatConnectbyTupleDescs(tupdesc, spi_tupdesc);
1306 
1307  initStringInfo(&branchstr);
1308  initStringInfo(&chk_branchstr);
1309  initStringInfo(&chk_current_key);
1310 
1311  for (i = 0; i < proc; i++)
1312  {
1313  /* initialize branch for this pass */
1314  appendStringInfoString(&branchstr, branch);
1315  appendStringInfo(&chk_branchstr, "%s%s%s", branch_delim, branch, branch_delim);
1316 
1317  /* get the next sql result tuple */
1318  spi_tuple = tuptable->vals[i];
1319 
1320  /* get the current key (might be NULL) */
1321  current_key = SPI_getvalue(spi_tuple, spi_tupdesc, 1);
1322 
1323  /* get the parent key (might be NULL) */
1324  current_key_parent = SPI_getvalue(spi_tuple, spi_tupdesc, 2);
1325 
1326  /* get the current level */
1327  sprintf(current_level, "%d", level);
1328 
1329  /* check to see if this key is also an ancestor */
1330  if (current_key)
1331  {
1332  appendStringInfo(&chk_current_key, "%s%s%s",
1333  branch_delim, current_key, branch_delim);
1334  if (strstr(chk_branchstr.data, chk_current_key.data))
1335  ereport(ERROR,
1336  (errcode(ERRCODE_INVALID_RECURSION),
1337  errmsg("infinite recursion detected")));
1338  }
1339 
1340  /* OK, extend the branch */
1341  if (current_key)
1342  appendStringInfo(&branchstr, "%s%s", branch_delim, current_key);
1343  current_branch = branchstr.data;
1344 
1345  /* build a tuple */
1346  values[0] = current_key;
1347  values[1] = current_key_parent;
1348  values[2] = current_level;
1349  if (show_branch)
1350  values[3] = current_branch;
1351  if (show_serial)
1352  {
1353  sprintf(serial_str, "%d", (*serial)++);
1354  if (show_branch)
1355  values[4] = serial_str;
1356  else
1357  values[3] = serial_str;
1358  }
1359 
1360  tuple = BuildTupleFromCStrings(attinmeta, values);
1361 
1362  /* store the tuple for later use */
1363  tuplestore_puttuple(tupstore, tuple);
1364 
1365  heap_freetuple(tuple);
1366 
1367  /* recurse using current_key as the new start_with */
1368  if (current_key)
1370  parent_key_fld,
1371  relname,
1372  orderby_fld,
1373  branch_delim,
1374  current_key,
1375  current_branch,
1376  level + 1,
1377  serial,
1378  max_depth,
1379  show_branch,
1380  show_serial,
1381  per_query_ctx,
1382  attinmeta,
1383  tupstore);
1384 
1385  xpfree(current_key);
1386  xpfree(current_key_parent);
1387 
1388  /* reset branch for next pass */
1389  resetStringInfo(&branchstr);
1390  resetStringInfo(&chk_branchstr);
1391  resetStringInfo(&chk_current_key);
1392  }
1393 
1394  xpfree(branchstr.data);
1395  xpfree(chk_branchstr.data);
1396  xpfree(chk_current_key.data);
1397  }
1398 }
static Datum values[MAXATTR]
Definition: bootstrap.c:150
#define ereport(elevel,...)
Definition: elog.h:149
HeapTuple BuildTupleFromCStrings(AttInMetadata *attinmeta, char **values)
Definition: execTuples.c:2222
void heap_freetuple(HeapTuple htup)
Definition: heaptuple.c:1434
int i
Definition: isn.c:73
void * palloc(Size size)
Definition: mcxt.c:1317
NameData relname
Definition: pg_class.h:38
#define sprintf
Definition: port.h:240
char * quote_literal_cstr(const char *rawstr)
Definition: quote.c:103
uint64 SPI_processed
Definition: spi.c:44
SPITupleTable * SPI_tuptable
Definition: spi.c:45
char * SPI_getvalue(HeapTuple tuple, TupleDesc tupdesc, int fnumber)
Definition: spi.c:1217
int SPI_execute(const char *src, bool read_only, long tcount)
Definition: spi.c:593
#define SPI_OK_SELECT
Definition: spi.h:86
void resetStringInfo(StringInfo str)
Definition: stringinfo.c:78
void appendStringInfo(StringInfo str, const char *fmt,...)
Definition: stringinfo.c:97
void appendStringInfoString(StringInfo str, const char *s)
Definition: stringinfo.c:182
void initStringInfo(StringInfo str)
Definition: stringinfo.c:59
TupleDesc tupdesc
Definition: funcapi.h:38
TupleDesc tupdesc
Definition: spi.h:25
HeapTuple * vals
Definition: spi.h:26
#define CONNECTBY_NCOLS_NOBRANCH
Definition: tablefunc.c:971
#define INT32_STRLEN
Definition: tablefunc.c:116
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)
Definition: tablefunc.c:1185
#define CONNECTBY_NCOLS
Definition: tablefunc.c:970
static void compatConnectbyTupleDescs(TupleDesc ret_tupdesc, TupleDesc sql_tupdesc)
Definition: tablefunc.c:1464
#define xpfree(var_)
Definition: tablefunc.c:94
void tuplestore_puttuple(Tuplestorestate *state, HeapTuple tuple)
Definition: tuplestore.c:764

References appendStringInfo(), appendStringInfoString(), BuildTupleFromCStrings(), compatConnectbyTupleDescs(), CONNECTBY_NCOLS, CONNECTBY_NCOLS_NOBRANCH, StringInfoData::data, ereport, errcode(), errmsg(), ERROR, heap_freetuple(), i, initStringInfo(), INT32_STRLEN, palloc(), quote_literal_cstr(), relname, resetStringInfo(), SPI_execute(), SPI_getvalue(), SPI_OK_SELECT, SPI_processed, SPI_tuptable, sprintf, SPITupleTable::tupdesc, AttInMetadata::tupdesc, tuplestore_puttuple(), SPITupleTable::vals, values, and xpfree.

Referenced by connectby().

◆ compatConnectbyTupleDescs()

static void compatConnectbyTupleDescs ( TupleDesc  ret_tupdesc,
TupleDesc  sql_tupdesc 
)
static

Definition at line 1464 of file tablefunc.c.

1465 {
1466  Oid ret_atttypid;
1467  Oid sql_atttypid;
1468  int32 ret_atttypmod;
1469  int32 sql_atttypmod;
1470 
1471  /*
1472  * Query result must have at least 2 columns.
1473  */
1474  if (sql_tupdesc->natts < 2)
1475  ereport(ERROR,
1476  (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
1477  errmsg("invalid connectby source data query"),
1478  errdetail("The query must return at least two columns.")));
1479 
1480  /*
1481  * These columns must match the result type indicated by the calling
1482  * query.
1483  */
1484  ret_atttypid = TupleDescAttr(ret_tupdesc, 0)->atttypid;
1485  sql_atttypid = TupleDescAttr(sql_tupdesc, 0)->atttypid;
1486  ret_atttypmod = TupleDescAttr(ret_tupdesc, 0)->atttypmod;
1487  sql_atttypmod = TupleDescAttr(sql_tupdesc, 0)->atttypmod;
1488  if (ret_atttypid != sql_atttypid ||
1489  (ret_atttypmod >= 0 && ret_atttypmod != sql_atttypmod))
1490  ereport(ERROR,
1491  (errcode(ERRCODE_DATATYPE_MISMATCH),
1492  errmsg("invalid connectby return type"),
1493  errdetail("Source key type %s does not match return key type %s.",
1494  format_type_with_typemod(sql_atttypid, sql_atttypmod),
1495  format_type_with_typemod(ret_atttypid, ret_atttypmod))));
1496 
1497  ret_atttypid = TupleDescAttr(ret_tupdesc, 1)->atttypid;
1498  sql_atttypid = TupleDescAttr(sql_tupdesc, 1)->atttypid;
1499  ret_atttypmod = TupleDescAttr(ret_tupdesc, 1)->atttypmod;
1500  sql_atttypmod = TupleDescAttr(sql_tupdesc, 1)->atttypmod;
1501  if (ret_atttypid != sql_atttypid ||
1502  (ret_atttypmod >= 0 && ret_atttypmod != sql_atttypmod))
1503  ereport(ERROR,
1504  (errcode(ERRCODE_DATATYPE_MISMATCH),
1505  errmsg("invalid connectby return type"),
1506  errdetail("Source parent key type %s does not match return parent key type %s.",
1507  format_type_with_typemod(sql_atttypid, sql_atttypmod),
1508  format_type_with_typemod(ret_atttypid, ret_atttypmod))));
1509 
1510  /* OK, the two tupdescs are compatible for our purposes */
1511 }
signed int int32
Definition: c.h:497
int errdetail(const char *fmt,...)
Definition: elog.c:1203
char * format_type_with_typemod(Oid type_oid, int32 typemod)
Definition: format_type.c:362
unsigned int Oid
Definition: postgres_ext.h:31
#define TupleDescAttr(tupdesc, i)
Definition: tupdesc.h:92

References ereport, errcode(), errdetail(), errmsg(), ERROR, format_type_with_typemod(), TupleDescData::natts, and TupleDescAttr.

Referenced by build_tuplestore_recursively().

◆ compatCrosstabTupleDescs()

static void compatCrosstabTupleDescs ( TupleDesc  ret_tupdesc,
TupleDesc  sql_tupdesc 
)
static

Definition at line 1517 of file tablefunc.c.

1518 {
1519  int i;
1520  Oid ret_atttypid;
1521  Oid sql_atttypid;
1522  int32 ret_atttypmod;
1523  int32 sql_atttypmod;
1524 
1525  if (ret_tupdesc->natts < 2)
1526  ereport(ERROR,
1527  (errcode(ERRCODE_DATATYPE_MISMATCH),
1528  errmsg("invalid crosstab return type"),
1529  errdetail("Return row must have at least two columns.")));
1530  Assert(sql_tupdesc->natts == 3); /* already checked by caller */
1531 
1532  /* check the row_name types match */
1533  ret_atttypid = TupleDescAttr(ret_tupdesc, 0)->atttypid;
1534  sql_atttypid = TupleDescAttr(sql_tupdesc, 0)->atttypid;
1535  ret_atttypmod = TupleDescAttr(ret_tupdesc, 0)->atttypmod;
1536  sql_atttypmod = TupleDescAttr(sql_tupdesc, 0)->atttypmod;
1537  if (ret_atttypid != sql_atttypid ||
1538  (ret_atttypmod >= 0 && ret_atttypmod != sql_atttypmod))
1539  ereport(ERROR,
1540  (errcode(ERRCODE_DATATYPE_MISMATCH),
1541  errmsg("invalid crosstab return type"),
1542  errdetail("Source row_name datatype %s does not match return row_name datatype %s.",
1543  format_type_with_typemod(sql_atttypid, sql_atttypmod),
1544  format_type_with_typemod(ret_atttypid, ret_atttypmod))));
1545 
1546  /*
1547  * attribute [1] of sql tuple is the category; no need to check it
1548  * attribute [2] of sql tuple should match attributes [1] to [natts - 1]
1549  * of the return tuple
1550  */
1551  sql_atttypid = TupleDescAttr(sql_tupdesc, 2)->atttypid;
1552  sql_atttypmod = TupleDescAttr(sql_tupdesc, 2)->atttypmod;
1553  for (i = 1; i < ret_tupdesc->natts; i++)
1554  {
1555  ret_atttypid = TupleDescAttr(ret_tupdesc, i)->atttypid;
1556  ret_atttypmod = TupleDescAttr(ret_tupdesc, i)->atttypmod;
1557 
1558  if (ret_atttypid != sql_atttypid ||
1559  (ret_atttypmod >= 0 && ret_atttypmod != sql_atttypmod))
1560  ereport(ERROR,
1561  (errcode(ERRCODE_DATATYPE_MISMATCH),
1562  errmsg("invalid crosstab return type"),
1563  errdetail("Source value datatype %s does not match return value datatype %s in column %d.",
1564  format_type_with_typemod(sql_atttypid, sql_atttypmod),
1565  format_type_with_typemod(ret_atttypid, ret_atttypmod),
1566  i + 1)));
1567  }
1568 
1569  /* OK, the two tupdescs are compatible for our purposes */
1570 }
#define Assert(condition)
Definition: c.h:861

References Assert, ereport, errcode(), errdetail(), errmsg(), ERROR, format_type_with_typemod(), i, TupleDescData::natts, and TupleDescAttr.

Referenced by crosstab().

◆ connectby()

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 
)
static

Definition at line 1134 of file tablefunc.c.

1146 {
1147  Tuplestorestate *tupstore = NULL;
1148  MemoryContext oldcontext;
1149  int serial = 1;
1150 
1151  /* Connect to SPI manager */
1152  SPI_connect();
1153 
1154  /* switch to longer term context to create the tuple store */
1155  oldcontext = MemoryContextSwitchTo(per_query_ctx);
1156 
1157  /* initialize our tuplestore */
1158  tupstore = tuplestore_begin_heap(randomAccess, false, work_mem);
1159 
1160  MemoryContextSwitchTo(oldcontext);
1161 
1162  /* now go get the whole tree */
1164  parent_key_fld,
1165  relname,
1166  orderby_fld,
1167  branch_delim,
1168  start_with,
1169  start_with, /* current_branch */
1170  0, /* initial level is 0 */
1171  &serial, /* initial serial is 1 */
1172  max_depth,
1173  show_branch,
1174  show_serial,
1175  per_query_ctx,
1176  attinmeta,
1177  tupstore);
1178 
1179  SPI_finish();
1180 
1181  return tupstore;
1182 }
int work_mem
Definition: globals.c:130
MemoryContextSwitchTo(old_ctx)
int SPI_connect(void)
Definition: spi.c:94
int SPI_finish(void)
Definition: spi.c:182
Tuplestorestate * tuplestore_begin_heap(bool randomAccess, bool interXact, int maxKBytes)
Definition: tuplestore.c:330

References build_tuplestore_recursively(), MemoryContextSwitchTo(), relname, SPI_connect(), SPI_finish(), tuplestore_begin_heap(), and work_mem.

Referenced by connectby_text(), and connectby_text_serial().

◆ connectby_text()

Datum connectby_text ( PG_FUNCTION_ARGS  )

Definition at line 974 of file tablefunc.c.

975 {
977  char *key_fld = text_to_cstring(PG_GETARG_TEXT_PP(1));
978  char *parent_key_fld = text_to_cstring(PG_GETARG_TEXT_PP(2));
979  char *start_with = text_to_cstring(PG_GETARG_TEXT_PP(3));
980  int max_depth = PG_GETARG_INT32(4);
981  char *branch_delim = NULL;
982  bool show_branch = false;
983  bool show_serial = false;
984  ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
985  TupleDesc tupdesc;
986  AttInMetadata *attinmeta;
987  MemoryContext per_query_ctx;
988  MemoryContext oldcontext;
989 
990  /* check to see if caller supports us returning a tuplestore */
991  if (rsinfo == NULL || !IsA(rsinfo, ReturnSetInfo))
992  ereport(ERROR,
993  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
994  errmsg("set-valued function called in context that cannot accept a set")));
995  if (!(rsinfo->allowedModes & SFRM_Materialize) ||
996  rsinfo->expectedDesc == NULL)
997  ereport(ERROR,
998  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
999  errmsg("materialize mode required, but it is not allowed in this context")));
1000 
1001  if (fcinfo->nargs == 6)
1002  {
1003  branch_delim = text_to_cstring(PG_GETARG_TEXT_PP(5));
1004  show_branch = true;
1005  }
1006  else
1007  /* default is no show, tilde for the delimiter */
1008  branch_delim = pstrdup("~");
1009 
1010  per_query_ctx = rsinfo->econtext->ecxt_per_query_memory;
1011  oldcontext = MemoryContextSwitchTo(per_query_ctx);
1012 
1013  /* get the requested return tuple description */
1014  tupdesc = CreateTupleDescCopy(rsinfo->expectedDesc);
1015 
1016  /* does it meet our needs */
1017  validateConnectbyTupleDesc(tupdesc, show_branch, show_serial);
1018 
1019  /* OK, use it then */
1020  attinmeta = TupleDescGetAttInMetadata(tupdesc);
1021 
1022  /* OK, go to work */
1023  rsinfo->returnMode = SFRM_Materialize;
1024  rsinfo->setResult = connectby(relname,
1025  key_fld,
1026  parent_key_fld,
1027  NULL,
1028  branch_delim,
1029  start_with,
1030  max_depth,
1031  show_branch,
1032  show_serial,
1033  per_query_ctx,
1035  attinmeta);
1036  rsinfo->setDesc = tupdesc;
1037 
1038  MemoryContextSwitchTo(oldcontext);
1039 
1040  /*
1041  * SFRM_Materialize mode expects us to return a NULL Datum. The actual
1042  * tuples are in our tuplestore and passed back through rsinfo->setResult.
1043  * rsinfo->setDesc is set to the tuple description that we actually used
1044  * to build our tuples with, so the caller can verify we did what it was
1045  * expecting.
1046  */
1047  return (Datum) 0;
1048 }
AttInMetadata * TupleDescGetAttInMetadata(TupleDesc tupdesc)
Definition: execTuples.c:2173
@ SFRM_Materialize_Random
Definition: execnodes.h:321
@ SFRM_Materialize
Definition: execnodes.h:320
#define PG_GETARG_TEXT_PP(n)
Definition: fmgr.h:309
#define PG_GETARG_INT32(n)
Definition: fmgr.h:269
if(TABLE==NULL||TABLE_index==NULL)
Definition: isn.c:77
#define IsA(nodeptr, _type_)
Definition: nodes.h:158
uintptr_t Datum
Definition: postgres.h:64
MemoryContext ecxt_per_query_memory
Definition: execnodes.h:265
SetFunctionReturnMode returnMode
Definition: execnodes.h:339
ExprContext * econtext
Definition: execnodes.h:335
TupleDesc setDesc
Definition: execnodes.h:343
Tuplestorestate * setResult
Definition: execnodes.h:342
TupleDesc expectedDesc
Definition: execnodes.h:336
int allowedModes
Definition: execnodes.h:337
static void validateConnectbyTupleDesc(TupleDesc td, bool show_branch, bool show_serial)
Definition: tablefunc.c:1404
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)
Definition: tablefunc.c:1134
TupleDesc CreateTupleDescCopy(TupleDesc tupdesc)
Definition: tupdesc.c:133
char * text_to_cstring(const text *t)
Definition: varlena.c:217

References ReturnSetInfo::allowedModes, connectby(), CreateTupleDescCopy(), ReturnSetInfo::econtext, ExprContext::ecxt_per_query_memory, ereport, errcode(), errmsg(), ERROR, ReturnSetInfo::expectedDesc, if(), IsA, MemoryContextSwitchTo(), PG_GETARG_INT32, PG_GETARG_TEXT_PP, pstrdup(), relname, ReturnSetInfo::returnMode, ReturnSetInfo::setDesc, ReturnSetInfo::setResult, SFRM_Materialize, SFRM_Materialize_Random, text_to_cstring(), TupleDescGetAttInMetadata(), and validateConnectbyTupleDesc().

◆ connectby_text_serial()

Datum connectby_text_serial ( PG_FUNCTION_ARGS  )

Definition at line 1052 of file tablefunc.c.

1053 {
1055  char *key_fld = text_to_cstring(PG_GETARG_TEXT_PP(1));
1056  char *parent_key_fld = text_to_cstring(PG_GETARG_TEXT_PP(2));
1057  char *orderby_fld = text_to_cstring(PG_GETARG_TEXT_PP(3));
1058  char *start_with = text_to_cstring(PG_GETARG_TEXT_PP(4));
1059  int max_depth = PG_GETARG_INT32(5);
1060  char *branch_delim = NULL;
1061  bool show_branch = false;
1062  bool show_serial = true;
1063  ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
1064  TupleDesc tupdesc;
1065  AttInMetadata *attinmeta;
1066  MemoryContext per_query_ctx;
1067  MemoryContext oldcontext;
1068 
1069  /* check to see if caller supports us returning a tuplestore */
1070  if (rsinfo == NULL || !IsA(rsinfo, ReturnSetInfo))
1071  ereport(ERROR,
1072  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1073  errmsg("set-valued function called in context that cannot accept a set")));
1074  if (!(rsinfo->allowedModes & SFRM_Materialize) ||
1075  rsinfo->expectedDesc == NULL)
1076  ereport(ERROR,
1077  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1078  errmsg("materialize mode required, but it is not allowed in this context")));
1079 
1080  if (fcinfo->nargs == 7)
1081  {
1082  branch_delim = text_to_cstring(PG_GETARG_TEXT_PP(6));
1083  show_branch = true;
1084  }
1085  else
1086  /* default is no show, tilde for the delimiter */
1087  branch_delim = pstrdup("~");
1088 
1089  per_query_ctx = rsinfo->econtext->ecxt_per_query_memory;
1090  oldcontext = MemoryContextSwitchTo(per_query_ctx);
1091 
1092  /* get the requested return tuple description */
1093  tupdesc = CreateTupleDescCopy(rsinfo->expectedDesc);
1094 
1095  /* does it meet our needs */
1096  validateConnectbyTupleDesc(tupdesc, show_branch, show_serial);
1097 
1098  /* OK, use it then */
1099  attinmeta = TupleDescGetAttInMetadata(tupdesc);
1100 
1101  /* OK, go to work */
1102  rsinfo->returnMode = SFRM_Materialize;
1103  rsinfo->setResult = connectby(relname,
1104  key_fld,
1105  parent_key_fld,
1106  orderby_fld,
1107  branch_delim,
1108  start_with,
1109  max_depth,
1110  show_branch,
1111  show_serial,
1112  per_query_ctx,
1114  attinmeta);
1115  rsinfo->setDesc = tupdesc;
1116 
1117  MemoryContextSwitchTo(oldcontext);
1118 
1119  /*
1120  * SFRM_Materialize mode expects us to return a NULL Datum. The actual
1121  * tuples are in our tuplestore and passed back through rsinfo->setResult.
1122  * rsinfo->setDesc is set to the tuple description that we actually used
1123  * to build our tuples with, so the caller can verify we did what it was
1124  * expecting.
1125  */
1126  return (Datum) 0;
1127 }

References ReturnSetInfo::allowedModes, connectby(), CreateTupleDescCopy(), ReturnSetInfo::econtext, ExprContext::ecxt_per_query_memory, ereport, errcode(), errmsg(), ERROR, ReturnSetInfo::expectedDesc, if(), IsA, MemoryContextSwitchTo(), PG_GETARG_INT32, PG_GETARG_TEXT_PP, pstrdup(), relname, ReturnSetInfo::returnMode, ReturnSetInfo::setDesc, ReturnSetInfo::setResult, SFRM_Materialize, SFRM_Materialize_Random, text_to_cstring(), TupleDescGetAttInMetadata(), and validateConnectbyTupleDesc().

◆ crosstab()

Datum crosstab ( PG_FUNCTION_ARGS  )

Definition at line 355 of file tablefunc.c.

356 {
357  char *sql = text_to_cstring(PG_GETARG_TEXT_PP(0));
358  ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
359  Tuplestorestate *tupstore;
360  TupleDesc tupdesc;
361  uint64 call_cntr;
362  uint64 max_calls;
363  AttInMetadata *attinmeta;
364  SPITupleTable *spi_tuptable;
365  TupleDesc spi_tupdesc;
366  bool firstpass;
367  char *lastrowid;
368  int i;
369  int num_categories;
370  MemoryContext per_query_ctx;
371  MemoryContext oldcontext;
372  int ret;
373  uint64 proc;
374 
375  /* check to see if caller supports us returning a tuplestore */
376  if (rsinfo == NULL || !IsA(rsinfo, ReturnSetInfo))
377  ereport(ERROR,
378  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
379  errmsg("set-valued function called in context that cannot accept a set")));
380  if (!(rsinfo->allowedModes & SFRM_Materialize))
381  ereport(ERROR,
382  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
383  errmsg("materialize mode required, but it is not allowed in this context")));
384 
385  per_query_ctx = rsinfo->econtext->ecxt_per_query_memory;
386 
387  /* Connect to SPI manager */
388  SPI_connect();
389 
390  /* Retrieve the desired rows */
391  ret = SPI_execute(sql, true, 0);
392  proc = SPI_processed;
393 
394  /* If no qualifying tuples, fall out early */
395  if (ret != SPI_OK_SELECT || proc == 0)
396  {
397  SPI_finish();
398  rsinfo->isDone = ExprEndResult;
399  PG_RETURN_NULL();
400  }
401 
402  spi_tuptable = SPI_tuptable;
403  spi_tupdesc = spi_tuptable->tupdesc;
404 
405  /*----------
406  * The provided SQL query must always return three columns.
407  *
408  * 1. rowname
409  * the label or identifier for each row in the final result
410  * 2. category
411  * the label or identifier for each column in the final result
412  * 3. values
413  * the value for each column in the final result
414  *----------
415  */
416  if (spi_tupdesc->natts != 3)
417  ereport(ERROR,
418  (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
419  errmsg("invalid crosstab source data query"),
420  errdetail("The query must return 3 columns: row_name, category, and value.")));
421 
422  /* get a tuple descriptor for our result type */
423  switch (get_call_result_type(fcinfo, NULL, &tupdesc))
424  {
425  case TYPEFUNC_COMPOSITE:
426  /* success */
427  break;
428  case TYPEFUNC_RECORD:
429  /* failed to determine actual type of RECORD */
430  ereport(ERROR,
431  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
432  errmsg("function returning record called in context "
433  "that cannot accept type record")));
434  break;
435  default:
436  /* result type isn't composite */
437  ereport(ERROR,
438  (errcode(ERRCODE_DATATYPE_MISMATCH),
439  errmsg("return type must be a row type")));
440  break;
441  }
442 
443  /*
444  * Check that return tupdesc is compatible with the data we got from SPI,
445  * at least based on number and type of attributes
446  */
447  compatCrosstabTupleDescs(tupdesc, spi_tupdesc);
448 
449  /*
450  * switch to long-lived memory context
451  */
452  oldcontext = MemoryContextSwitchTo(per_query_ctx);
453 
454  /* make sure we have a persistent copy of the result tupdesc */
455  tupdesc = CreateTupleDescCopy(tupdesc);
456 
457  /* initialize our tuplestore in long-lived context */
458  tupstore =
460  false, work_mem);
461 
462  MemoryContextSwitchTo(oldcontext);
463 
464  /*
465  * Generate attribute metadata needed later to produce tuples from raw C
466  * strings
467  */
468  attinmeta = TupleDescGetAttInMetadata(tupdesc);
469 
470  /* total number of tuples to be examined */
471  max_calls = proc;
472 
473  /* the return tuple always must have 1 rowid + num_categories columns */
474  num_categories = tupdesc->natts - 1;
475 
476  firstpass = true;
477  lastrowid = NULL;
478 
479  for (call_cntr = 0; call_cntr < max_calls; call_cntr++)
480  {
481  bool skip_tuple = false;
482  char **values;
483 
484  /* allocate and zero space */
485  values = (char **) palloc0((1 + num_categories) * sizeof(char *));
486 
487  /*
488  * now loop through the sql results and assign each value in sequence
489  * to the next category
490  */
491  for (i = 0; i < num_categories; i++)
492  {
493  HeapTuple spi_tuple;
494  char *rowid;
495 
496  /* see if we've gone too far already */
497  if (call_cntr >= max_calls)
498  break;
499 
500  /* get the next sql result tuple */
501  spi_tuple = spi_tuptable->vals[call_cntr];
502 
503  /* get the rowid from the current sql result tuple */
504  rowid = SPI_getvalue(spi_tuple, spi_tupdesc, 1);
505 
506  /*
507  * If this is the first pass through the values for this rowid,
508  * set the first column to rowid
509  */
510  if (i == 0)
511  {
512  xpstrdup(values[0], rowid);
513 
514  /*
515  * Check to see if the rowid is the same as that of the last
516  * tuple sent -- if so, skip this tuple entirely
517  */
518  if (!firstpass && xstreq(lastrowid, rowid))
519  {
520  xpfree(rowid);
521  skip_tuple = true;
522  break;
523  }
524  }
525 
526  /*
527  * If rowid hasn't changed on us, continue building the output
528  * tuple.
529  */
530  if (xstreq(rowid, values[0]))
531  {
532  /*
533  * Get the next category item value, which is always attribute
534  * number three.
535  *
536  * Be careful to assign the value to the array index based on
537  * which category we are presently processing.
538  */
539  values[1 + i] = SPI_getvalue(spi_tuple, spi_tupdesc, 3);
540 
541  /*
542  * increment the counter since we consume a row for each
543  * category, but not for last pass because the outer loop will
544  * do that for us
545  */
546  if (i < (num_categories - 1))
547  call_cntr++;
548  xpfree(rowid);
549  }
550  else
551  {
552  /*
553  * We'll fill in NULLs for the missing values, but we need to
554  * decrement the counter since this sql result row doesn't
555  * belong to the current output tuple.
556  */
557  call_cntr--;
558  xpfree(rowid);
559  break;
560  }
561  }
562 
563  if (!skip_tuple)
564  {
565  HeapTuple tuple;
566 
567  /* build the tuple and store it */
568  tuple = BuildTupleFromCStrings(attinmeta, values);
569  tuplestore_puttuple(tupstore, tuple);
570  heap_freetuple(tuple);
571  }
572 
573  /* Remember current rowid */
574  xpfree(lastrowid);
575  xpstrdup(lastrowid, values[0]);
576  firstpass = false;
577 
578  /* Clean up */
579  for (i = 0; i < num_categories + 1; i++)
580  if (values[i] != NULL)
581  pfree(values[i]);
582  pfree(values);
583  }
584 
585  /* let the caller know we're sending back a tuplestore */
586  rsinfo->returnMode = SFRM_Materialize;
587  rsinfo->setResult = tupstore;
588  rsinfo->setDesc = tupdesc;
589 
590  /* release SPI related resources (and return to caller's context) */
591  SPI_finish();
592 
593  return (Datum) 0;
594 }
@ ExprEndResult
Definition: execnodes.h:308
#define PG_RETURN_NULL()
Definition: fmgr.h:345
TypeFuncClass get_call_result_type(FunctionCallInfo fcinfo, Oid *resultTypeId, TupleDesc *resultTupleDesc)
Definition: funcapi.c:276
@ TYPEFUNC_COMPOSITE
Definition: funcapi.h:149
@ TYPEFUNC_RECORD
Definition: funcapi.h:151
void pfree(void *pointer)
Definition: mcxt.c:1521
void * palloc0(Size size)
Definition: mcxt.c:1347
ExprDoneCond isDone
Definition: execnodes.h:340
#define xpstrdup(tgtvar_, srcvar_)
Definition: tablefunc.c:103
static void compatCrosstabTupleDescs(TupleDesc ret_tupdesc, TupleDesc sql_tupdesc)
Definition: tablefunc.c:1517
#define xstreq(tgtvar_, srcvar_)
Definition: tablefunc.c:111

References ReturnSetInfo::allowedModes, BuildTupleFromCStrings(), compatCrosstabTupleDescs(), CreateTupleDescCopy(), ReturnSetInfo::econtext, ExprContext::ecxt_per_query_memory, ereport, errcode(), errdetail(), errmsg(), ERROR, ExprEndResult, get_call_result_type(), heap_freetuple(), i, if(), IsA, ReturnSetInfo::isDone, MemoryContextSwitchTo(), palloc0(), pfree(), PG_GETARG_TEXT_PP, PG_RETURN_NULL, ReturnSetInfo::returnMode, ReturnSetInfo::setDesc, ReturnSetInfo::setResult, SFRM_Materialize, SFRM_Materialize_Random, SPI_connect(), SPI_execute(), SPI_finish(), SPI_getvalue(), SPI_OK_SELECT, SPI_processed, SPI_tuptable, text_to_cstring(), SPITupleTable::tupdesc, TupleDescGetAttInMetadata(), tuplestore_begin_heap(), tuplestore_puttuple(), TYPEFUNC_COMPOSITE, TYPEFUNC_RECORD, values, work_mem, xpfree, xpstrdup, and xstreq.

◆ crosstab_hash()

Datum crosstab_hash ( PG_FUNCTION_ARGS  )

Definition at line 633 of file tablefunc.c.

634 {
635  char *sql = text_to_cstring(PG_GETARG_TEXT_PP(0));
636  char *cats_sql = text_to_cstring(PG_GETARG_TEXT_PP(1));
637  ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
638  TupleDesc tupdesc;
639  MemoryContext per_query_ctx;
640  MemoryContext oldcontext;
642 
643  /* check to see if caller supports us returning a tuplestore */
644  if (rsinfo == NULL || !IsA(rsinfo, ReturnSetInfo))
645  ereport(ERROR,
646  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
647  errmsg("set-valued function called in context that cannot accept a set")));
648  if (!(rsinfo->allowedModes & SFRM_Materialize) ||
649  rsinfo->expectedDesc == NULL)
650  ereport(ERROR,
651  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
652  errmsg("materialize mode required, but it is not allowed in this context")));
653 
654  per_query_ctx = rsinfo->econtext->ecxt_per_query_memory;
655  oldcontext = MemoryContextSwitchTo(per_query_ctx);
656 
657  /* get the requested return tuple description */
658  tupdesc = CreateTupleDescCopy(rsinfo->expectedDesc);
659 
660  /*
661  * Check to make sure we have a reasonable tuple descriptor
662  *
663  * Note we will attempt to coerce the values into whatever the return
664  * attribute type is and depend on the "in" function to complain if
665  * needed.
666  */
667  if (tupdesc->natts < 2)
668  ereport(ERROR,
669  (errcode(ERRCODE_DATATYPE_MISMATCH),
670  errmsg("invalid crosstab return type"),
671  errdetail("Return row must have at least two columns.")));
672 
673  /* load up the categories hash table */
674  crosstab_hash = load_categories_hash(cats_sql, per_query_ctx);
675 
676  /* let the caller know we're sending back a tuplestore */
677  rsinfo->returnMode = SFRM_Materialize;
678 
679  /* now go build it */
680  rsinfo->setResult = get_crosstab_tuplestore(sql,
682  tupdesc,
684 
685  /*
686  * SFRM_Materialize mode expects us to return a NULL Datum. The actual
687  * tuples are in our tuplestore and passed back through rsinfo->setResult.
688  * rsinfo->setDesc is set to the tuple description that we actually used
689  * to build our tuples with, so the caller can verify we did what it was
690  * expecting.
691  */
692  rsinfo->setDesc = tupdesc;
693  MemoryContextSwitchTo(oldcontext);
694 
695  return (Datum) 0;
696 }
Definition: dynahash.c:220
static Tuplestorestate * get_crosstab_tuplestore(char *sql, HTAB *crosstab_hash, TupleDesc tupdesc, bool randomAccess)
Definition: tablefunc.c:788
Datum crosstab_hash(PG_FUNCTION_ARGS)
Definition: tablefunc.c:633
static HTAB * load_categories_hash(char *cats_sql, MemoryContext per_query_ctx)
Definition: tablefunc.c:702

References ReturnSetInfo::allowedModes, CreateTupleDescCopy(), ReturnSetInfo::econtext, ExprContext::ecxt_per_query_memory, ereport, errcode(), errdetail(), errmsg(), ERROR, ReturnSetInfo::expectedDesc, get_crosstab_tuplestore(), if(), IsA, load_categories_hash(), MemoryContextSwitchTo(), PG_GETARG_TEXT_PP, ReturnSetInfo::returnMode, ReturnSetInfo::setDesc, ReturnSetInfo::setResult, SFRM_Materialize, SFRM_Materialize_Random, and text_to_cstring().

Referenced by get_crosstab_tuplestore(), and load_categories_hash().

◆ get_crosstab_tuplestore()

static Tuplestorestate * get_crosstab_tuplestore ( char *  sql,
HTAB crosstab_hash,
TupleDesc  tupdesc,
bool  randomAccess 
)
static

Definition at line 788 of file tablefunc.c.

792 {
793  Tuplestorestate *tupstore;
794  int num_categories = hash_get_num_entries(crosstab_hash);
795  AttInMetadata *attinmeta = TupleDescGetAttInMetadata(tupdesc);
796  char **values;
797  HeapTuple tuple;
798  int ret;
799  uint64 proc;
800 
801  /* initialize our tuplestore (while still in query context!) */
802  tupstore = tuplestore_begin_heap(randomAccess, false, work_mem);
803 
804  /* Connect to SPI manager */
805  SPI_connect();
806 
807  /* Now retrieve the crosstab source rows */
808  ret = SPI_execute(sql, true, 0);
809  proc = SPI_processed;
810 
811  /* Check for qualifying tuples */
812  if ((ret == SPI_OK_SELECT) && (proc > 0))
813  {
814  SPITupleTable *spi_tuptable = SPI_tuptable;
815  TupleDesc spi_tupdesc = spi_tuptable->tupdesc;
816  int ncols = spi_tupdesc->natts;
817  char *rowid;
818  char *lastrowid = NULL;
819  bool firstpass = true;
820  uint64 i;
821  int j;
822  int result_ncols;
823 
824  if (num_categories == 0)
825  {
826  /* no qualifying category tuples */
827  ereport(ERROR,
828  (errcode(ERRCODE_CARDINALITY_VIOLATION),
829  errmsg("crosstab categories query must return at least one row")));
830  }
831 
832  /*
833  * The provided SQL query must always return at least three columns:
834  *
835  * 1. rowname the label for each row - column 1 in the final result
836  * 2. category the label for each value-column in the final result 3.
837  * value the values used to populate the value-columns
838  *
839  * If there are more than three columns, the last two are taken as
840  * "category" and "values". The first column is taken as "rowname".
841  * Additional columns (2 thru N-2) are assumed the same for the same
842  * "rowname", and are copied into the result tuple from the first time
843  * we encounter a particular rowname.
844  */
845  if (ncols < 3)
846  ereport(ERROR,
847  (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
848  errmsg("invalid crosstab source data query"),
849  errdetail("The query must return at least 3 columns: row_name, category, and value.")));
850 
851  result_ncols = (ncols - 2) + num_categories;
852 
853  /* Recheck to make sure output tuple descriptor looks reasonable */
854  if (tupdesc->natts != result_ncols)
855  ereport(ERROR,
856  (errcode(ERRCODE_DATATYPE_MISMATCH),
857  errmsg("invalid crosstab return type"),
858  errdetail("Return row must have %d columns, not %d.",
859  result_ncols, tupdesc->natts)));
860 
861  /* allocate space and make sure it's clear */
862  values = (char **) palloc0(result_ncols * sizeof(char *));
863 
864  for (i = 0; i < proc; i++)
865  {
866  HeapTuple spi_tuple;
867  crosstab_cat_desc *catdesc;
868  char *catname;
869 
870  /* get the next sql result tuple */
871  spi_tuple = spi_tuptable->vals[i];
872 
873  /* get the rowid from the current sql result tuple */
874  rowid = SPI_getvalue(spi_tuple, spi_tupdesc, 1);
875 
876  /*
877  * if we're on a new output row, grab the column values up to
878  * column N-2 now
879  */
880  if (firstpass || !xstreq(lastrowid, rowid))
881  {
882  /*
883  * a new row means we need to flush the old one first, unless
884  * we're on the very first row
885  */
886  if (!firstpass)
887  {
888  /* rowid changed, flush the previous output row */
889  tuple = BuildTupleFromCStrings(attinmeta, values);
890 
891  tuplestore_puttuple(tupstore, tuple);
892 
893  for (j = 0; j < result_ncols; j++)
894  xpfree(values[j]);
895  }
896 
897  values[0] = rowid;
898  for (j = 1; j < ncols - 2; j++)
899  values[j] = SPI_getvalue(spi_tuple, spi_tupdesc, j + 1);
900 
901  /* we're no longer on the first pass */
902  firstpass = false;
903  }
904 
905  /* look up the category and fill in the appropriate column */
906  catname = SPI_getvalue(spi_tuple, spi_tupdesc, ncols - 1);
907 
908  if (catname != NULL)
909  {
910  crosstab_HashTableLookup(crosstab_hash, catname, catdesc);
911 
912  if (catdesc)
913  values[catdesc->attidx + ncols - 2] =
914  SPI_getvalue(spi_tuple, spi_tupdesc, ncols);
915  }
916 
917  xpfree(lastrowid);
918  xpstrdup(lastrowid, rowid);
919  }
920 
921  /* flush the last output row */
922  tuple = BuildTupleFromCStrings(attinmeta, values);
923 
924  tuplestore_puttuple(tupstore, tuple);
925  }
926 
927  if (SPI_finish() != SPI_OK_FINISH)
928  /* internal error */
929  elog(ERROR, "get_crosstab_tuplestore: SPI_finish() failed");
930 
931  return tupstore;
932 }
long hash_get_num_entries(HTAB *hashp)
Definition: dynahash.c:1341
#define elog(elevel,...)
Definition: elog.h:225
int j
Definition: isn.c:74
#define SPI_OK_FINISH
Definition: spi.h:83
#define crosstab_HashTableLookup(HASHTAB, CATNAME, CATDESC)
Definition: tablefunc.c:128

References crosstab_cat_desc::attidx, BuildTupleFromCStrings(), crosstab_hash(), crosstab_HashTableLookup, elog, ereport, errcode(), errdetail(), errmsg(), ERROR, hash_get_num_entries(), i, j, TupleDescData::natts, palloc0(), SPI_connect(), SPI_execute(), SPI_finish(), SPI_getvalue(), SPI_OK_FINISH, SPI_OK_SELECT, SPI_processed, SPI_tuptable, SPITupleTable::tupdesc, TupleDescGetAttInMetadata(), tuplestore_begin_heap(), tuplestore_puttuple(), SPITupleTable::vals, values, work_mem, xpfree, xpstrdup, and xstreq.

Referenced by crosstab_hash().

◆ get_normal_pair()

static void get_normal_pair ( float8 x1,
float8 x2 
)
static

Definition at line 284 of file tablefunc.c.

285 {
286  float8 u1,
287  u2,
288  v1,
289  v2,
290  s;
291 
292  do
293  {
296 
297  v1 = (2.0 * u1) - 1.0;
298  v2 = (2.0 * u2) - 1.0;
299 
300  s = v1 * v1 + v2 * v2;
301  } while (s >= 1.0);
302 
303  if (s == 0)
304  {
305  *x1 = 0;
306  *x2 = 0;
307  }
308  else
309  {
310  s = sqrt((-2.0 * log(s)) / s);
311  *x1 = v1 * s;
312  *x2 = v2 * s;
313  }
314 }
double float8
Definition: c.h:633
double pg_prng_double(pg_prng_state *state)
Definition: pg_prng.c:268
pg_prng_state pg_global_prng_state
Definition: pg_prng.c:34

References pg_global_prng_state, and pg_prng_double().

Referenced by normal_rand().

◆ load_categories_hash()

static HTAB * load_categories_hash ( char *  cats_sql,
MemoryContext  per_query_ctx 
)
static

Definition at line 702 of file tablefunc.c.

703 {
705  HASHCTL ctl;
706  int ret;
707  uint64 proc;
708  MemoryContext SPIcontext;
709 
710  /* initialize the category hash table */
711  ctl.keysize = MAX_CATNAME_LEN;
712  ctl.entrysize = sizeof(crosstab_HashEnt);
713  ctl.hcxt = per_query_ctx;
714 
715  /*
716  * use INIT_CATS, defined above as a guess of how many hash table entries
717  * to create, initially
718  */
719  crosstab_hash = hash_create("crosstab hash",
720  INIT_CATS,
721  &ctl,
723 
724  /* Connect to SPI manager */
725  SPI_connect();
726 
727  /* Retrieve the category name rows */
728  ret = SPI_execute(cats_sql, true, 0);
729  proc = SPI_processed;
730 
731  /* Check for qualifying tuples */
732  if ((ret == SPI_OK_SELECT) && (proc > 0))
733  {
734  SPITupleTable *spi_tuptable = SPI_tuptable;
735  TupleDesc spi_tupdesc = spi_tuptable->tupdesc;
736  uint64 i;
737 
738  /*
739  * The provided categories SQL query must always return one column:
740  * category - the label or identifier for each column
741  */
742  if (spi_tupdesc->natts != 1)
743  ereport(ERROR,
744  (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
745  errmsg("invalid crosstab categories query"),
746  errdetail("The query must return one column.")));
747 
748  for (i = 0; i < proc; i++)
749  {
750  crosstab_cat_desc *catdesc;
751  char *catname;
752  HeapTuple spi_tuple;
753 
754  /* get the next sql result tuple */
755  spi_tuple = spi_tuptable->vals[i];
756 
757  /* get the category from the current sql result tuple */
758  catname = SPI_getvalue(spi_tuple, spi_tupdesc, 1);
759  if (catname == NULL)
760  ereport(ERROR,
761  (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
762  errmsg("crosstab category value must not be null")));
763 
764  SPIcontext = MemoryContextSwitchTo(per_query_ctx);
765 
766  catdesc = (crosstab_cat_desc *) palloc(sizeof(crosstab_cat_desc));
767  catdesc->catname = catname;
768  catdesc->attidx = i;
769 
770  /* Add the proc description block to the hashtable */
772 
773  MemoryContextSwitchTo(SPIcontext);
774  }
775  }
776 
777  if (SPI_finish() != SPI_OK_FINISH)
778  /* internal error */
779  elog(ERROR, "load_categories_hash: SPI_finish() failed");
780 
781  return crosstab_hash;
782 }
HTAB * hash_create(const char *tabname, long nelem, const HASHCTL *info, int flags)
Definition: dynahash.c:352
#define HASH_STRINGS
Definition: hsearch.h:96
#define HASH_CONTEXT
Definition: hsearch.h:102
#define HASH_ELEM
Definition: hsearch.h:95
tree ctl
Definition: radixtree.h:1853
#define crosstab_HashTableInsert(HASHTAB, CATDESC)
Definition: tablefunc.c:142
#define INIT_CATS
Definition: tablefunc.c:126
struct crosstab_hashent crosstab_HashEnt

References crosstab_cat_desc::attidx, crosstab_cat_desc::catname, crosstab_hash(), crosstab_HashTableInsert, ctl, elog, ereport, errcode(), errdetail(), errmsg(), ERROR, HASH_CONTEXT, hash_create(), HASH_ELEM, HASH_STRINGS, i, INIT_CATS, MAX_CATNAME_LEN, MemoryContextSwitchTo(), TupleDescData::natts, palloc(), SPI_connect(), SPI_execute(), SPI_finish(), SPI_getvalue(), SPI_OK_FINISH, SPI_OK_SELECT, SPI_processed, SPI_tuptable, SPITupleTable::tupdesc, and SPITupleTable::vals.

Referenced by crosstab_hash().

◆ normal_rand()

Datum normal_rand ( PG_FUNCTION_ARGS  )

Definition at line 173 of file tablefunc.c.

174 {
175  FuncCallContext *funcctx;
176  uint64 call_cntr;
177  uint64 max_calls;
178  normal_rand_fctx *fctx;
179  float8 mean;
180  float8 stddev;
181  float8 carry_val;
182  bool use_carry;
183  MemoryContext oldcontext;
184 
185  /* stuff done only on the first call of the function */
186  if (SRF_IS_FIRSTCALL())
187  {
188  int32 num_tuples;
189 
190  /* create a function context for cross-call persistence */
191  funcctx = SRF_FIRSTCALL_INIT();
192 
193  /*
194  * switch to memory context appropriate for multiple function calls
195  */
196  oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
197 
198  /* total number of tuples to be returned */
199  num_tuples = PG_GETARG_INT32(0);
200  if (num_tuples < 0)
201  ereport(ERROR,
202  (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
203  errmsg("number of rows cannot be negative")));
204  funcctx->max_calls = num_tuples;
205 
206  /* allocate memory for user context */
207  fctx = (normal_rand_fctx *) palloc(sizeof(normal_rand_fctx));
208 
209  /*
210  * Use fctx to keep track of upper and lower bounds from call to call.
211  * It will also be used to carry over the spare value we get from the
212  * Box-Muller algorithm so that we only actually calculate a new value
213  * every other call.
214  */
215  fctx->mean = PG_GETARG_FLOAT8(1);
216  fctx->stddev = PG_GETARG_FLOAT8(2);
217  fctx->carry_val = 0;
218  fctx->use_carry = false;
219 
220  funcctx->user_fctx = fctx;
221 
222  MemoryContextSwitchTo(oldcontext);
223  }
224 
225  /* stuff done on every call of the function */
226  funcctx = SRF_PERCALL_SETUP();
227 
228  call_cntr = funcctx->call_cntr;
229  max_calls = funcctx->max_calls;
230  fctx = funcctx->user_fctx;
231  mean = fctx->mean;
232  stddev = fctx->stddev;
233  carry_val = fctx->carry_val;
234  use_carry = fctx->use_carry;
235 
236  if (call_cntr < max_calls) /* do when there is more left to send */
237  {
238  float8 result;
239 
240  if (use_carry)
241  {
242  /*
243  * reset use_carry and use second value obtained on last pass
244  */
245  fctx->use_carry = false;
246  result = carry_val;
247  }
248  else
249  {
250  float8 normval_1;
251  float8 normval_2;
252 
253  /* Get the next two normal values */
254  get_normal_pair(&normval_1, &normval_2);
255 
256  /* use the first */
257  result = mean + (stddev * normval_1);
258 
259  /* and save the second */
260  fctx->carry_val = mean + (stddev * normval_2);
261  fctx->use_carry = true;
262  }
263 
264  /* send the result */
265  SRF_RETURN_NEXT(funcctx, Float8GetDatum(result));
266  }
267  else
268  /* do when there is no more left */
269  SRF_RETURN_DONE(funcctx);
270 }
Datum Float8GetDatum(float8 X)
Definition: fmgr.c:1816
#define PG_GETARG_FLOAT8(n)
Definition: fmgr.h:282
#define SRF_IS_FIRSTCALL()
Definition: funcapi.h:304
#define SRF_PERCALL_SETUP()
Definition: funcapi.h:308
#define SRF_RETURN_NEXT(_funcctx, _result)
Definition: funcapi.h:310
#define SRF_FIRSTCALL_INIT()
Definition: funcapi.h:306
#define SRF_RETURN_DONE(_funcctx)
Definition: funcapi.h:328
void * user_fctx
Definition: funcapi.h:82
uint64 max_calls
Definition: funcapi.h:74
uint64 call_cntr
Definition: funcapi.h:65
MemoryContext multi_call_memory_ctx
Definition: funcapi.h:101
float8 carry_val
Definition: tablefunc.c:90
static void get_normal_pair(float8 *x1, float8 *x2)
Definition: tablefunc.c:284

References FuncCallContext::call_cntr, normal_rand_fctx::carry_val, ereport, errcode(), errmsg(), ERROR, Float8GetDatum(), get_normal_pair(), FuncCallContext::max_calls, normal_rand_fctx::mean, MemoryContextSwitchTo(), FuncCallContext::multi_call_memory_ctx, palloc(), PG_GETARG_FLOAT8, PG_GETARG_INT32, SRF_FIRSTCALL_INIT, SRF_IS_FIRSTCALL, SRF_PERCALL_SETUP, SRF_RETURN_DONE, SRF_RETURN_NEXT, normal_rand_fctx::stddev, normal_rand_fctx::use_carry, and FuncCallContext::user_fctx.

◆ PG_FUNCTION_INFO_V1() [1/5]

PG_FUNCTION_INFO_V1 ( connectby_text  )

◆ PG_FUNCTION_INFO_V1() [2/5]

PG_FUNCTION_INFO_V1 ( connectby_text_serial  )

◆ PG_FUNCTION_INFO_V1() [3/5]

PG_FUNCTION_INFO_V1 ( crosstab  )

◆ PG_FUNCTION_INFO_V1() [4/5]

PG_FUNCTION_INFO_V1 ( crosstab_hash  )

◆ PG_FUNCTION_INFO_V1() [5/5]

PG_FUNCTION_INFO_V1 ( normal_rand  )

◆ validateConnectbyTupleDesc()

static void validateConnectbyTupleDesc ( TupleDesc  td,
bool  show_branch,
bool  show_serial 
)
static

Definition at line 1404 of file tablefunc.c.

1405 {
1406  int expected_cols;
1407 
1408  /* are there the correct number of columns */
1409  if (show_branch)
1410  expected_cols = CONNECTBY_NCOLS;
1411  else
1412  expected_cols = CONNECTBY_NCOLS_NOBRANCH;
1413  if (show_serial)
1414  expected_cols++;
1415 
1416  if (td->natts != expected_cols)
1417  ereport(ERROR,
1418  (errcode(ERRCODE_DATATYPE_MISMATCH),
1419  errmsg("invalid connectby return type"),
1420  errdetail("Return row must have %d columns, not %d.",
1421  expected_cols, td->natts)));
1422 
1423  /* the first two columns will be checked against the input tuples later */
1424 
1425  /* check that the type of the third column is INT4 */
1426  if (TupleDescAttr(td, 2)->atttypid != INT4OID)
1427  ereport(ERROR,
1428  (errcode(ERRCODE_DATATYPE_MISMATCH),
1429  errmsg("invalid connectby return type"),
1430  errdetail("Third return column (depth) must be type %s.",
1431  format_type_be(INT4OID))));
1432 
1433  /* check that the type of the branch column is TEXT if applicable */
1434  if (show_branch && TupleDescAttr(td, 3)->atttypid != TEXTOID)
1435  ereport(ERROR,
1436  (errcode(ERRCODE_DATATYPE_MISMATCH),
1437  errmsg("invalid connectby return type"),
1438  errdetail("Fourth return column (branch) must be type %s.",
1439  format_type_be(TEXTOID))));
1440 
1441  /* check that the type of the serial column is INT4 if applicable */
1442  if (show_branch && show_serial &&
1443  TupleDescAttr(td, 4)->atttypid != INT4OID)
1444  ereport(ERROR,
1445  (errcode(ERRCODE_DATATYPE_MISMATCH),
1446  errmsg("invalid connectby return type"),
1447  errdetail("Fifth return column (serial) must be type %s.",
1448  format_type_be(INT4OID))));
1449  if (!show_branch && show_serial &&
1450  TupleDescAttr(td, 3)->atttypid != INT4OID)
1451  ereport(ERROR,
1452  (errcode(ERRCODE_DATATYPE_MISMATCH),
1453  errmsg("invalid connectby return type"),
1454  errdetail("Fourth return column (serial) must be type %s.",
1455  format_type_be(INT4OID))));
1456 
1457  /* OK, the tupdesc is valid for our purposes */
1458 }
char * format_type_be(Oid type_oid)
Definition: format_type.c:343

References CONNECTBY_NCOLS, CONNECTBY_NCOLS_NOBRANCH, ereport, errcode(), errdetail(), errmsg(), ERROR, format_type_be(), TupleDescData::natts, and TupleDescAttr.

Referenced by connectby_text(), and connectby_text_serial().

Variable Documentation

◆ PG_MODULE_MAGIC

PG_MODULE_MAGIC

Definition at line 47 of file tablefunc.c.