PostgreSQL Source Code  git master
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros
txid.c
Go to the documentation of this file.
1 /*-------------------------------------------------------------------------
2  * txid.c
3  *
4  * Export internal transaction IDs to user level.
5  *
6  * Note that only top-level transaction IDs are ever converted to TXID.
7  * This is important because TXIDs frequently persist beyond the global
8  * xmin horizon, or may even be shipped to other machines, so we cannot
9  * rely on being able to correlate subtransaction IDs with their parents
10  * via functions such as SubTransGetTopmostTransaction().
11  *
12  *
13  * Copyright (c) 2003-2017, PostgreSQL Global Development Group
14  * Author: Jan Wieck, Afilias USA INC.
15  * 64-bit txids: Marko Kreen, Skype Technologies
16  *
17  * src/backend/utils/adt/txid.c
18  *
19  *-------------------------------------------------------------------------
20  */
21 
22 #include "postgres.h"
23 
24 #include "access/transam.h"
25 #include "access/xact.h"
26 #include "access/xlog.h"
27 #include "funcapi.h"
28 #include "miscadmin.h"
29 #include "libpq/pqformat.h"
30 #include "postmaster/postmaster.h"
31 #include "utils/builtins.h"
32 #include "utils/memutils.h"
33 #include "utils/snapmgr.h"
34 
35 
36 /* txid will be signed int8 in database, so must limit to 63 bits */
37 #define MAX_TXID ((uint64) PG_INT64_MAX)
38 
39 /* Use unsigned variant internally */
40 typedef uint64 txid;
41 
42 /* sprintf format code for uint64 */
43 #define TXID_FMT UINT64_FORMAT
44 
45 /*
46  * If defined, use bsearch() function for searching for txids in snapshots
47  * that have more than the specified number of values.
48  */
49 #define USE_BSEARCH_IF_NXIP_GREATER 30
50 
51 
52 /*
53  * Snapshot containing 8byte txids.
54  */
55 typedef struct
56 {
57  /*
58  * 4-byte length hdr, should not be touched directly.
59  *
60  * Explicit embedding is ok as we want always correct alignment anyway.
61  */
63 
64  uint32 nxip; /* number of txids in xip array */
67  /* in-progress txids, xmin <= xip[i] < xmax: */
68  txid xip[FLEXIBLE_ARRAY_MEMBER];
69 } TxidSnapshot;
70 
71 #define TXID_SNAPSHOT_SIZE(nxip) \
72  (offsetof(TxidSnapshot, xip) + sizeof(txid) * (nxip))
73 #define TXID_SNAPSHOT_MAX_NXIP \
74  ((MaxAllocSize - offsetof(TxidSnapshot, xip)) / sizeof(txid))
75 
76 /*
77  * Epoch values from xact.c
78  */
79 typedef struct
80 {
83 } TxidEpoch;
84 
85 
86 /*
87  * Fetch epoch data from xact.c.
88  */
89 static void
91 {
92  GetNextXidAndEpoch(&state->last_xid, &state->epoch);
93 }
94 
95 /*
96  * do a TransactionId -> txid conversion for an XID near the given epoch
97  */
98 static txid
100 {
101  uint64 epoch;
102 
103  /* return special xid's as-is */
104  if (!TransactionIdIsNormal(xid))
105  return (txid) xid;
106 
107  /* xid can be on either side when near wrap-around */
108  epoch = (uint64) state->epoch;
109  if (xid > state->last_xid &&
110  TransactionIdPrecedes(xid, state->last_xid))
111  epoch--;
112  else if (xid < state->last_xid &&
113  TransactionIdFollows(xid, state->last_xid))
114  epoch++;
115 
116  return (epoch << 32) | xid;
117 }
118 
119 /*
120  * txid comparator for qsort/bsearch
121  */
122 static int
123 cmp_txid(const void *aa, const void *bb)
124 {
125  txid a = *(const txid *) aa;
126  txid b = *(const txid *) bb;
127 
128  if (a < b)
129  return -1;
130  if (a > b)
131  return 1;
132  return 0;
133 }
134 
135 /*
136  * Sort a snapshot's txids, so we can use bsearch() later. Also remove
137  * any duplicates.
138  *
139  * For consistency of on-disk representation, we always sort even if bsearch
140  * will not be used.
141  */
142 static void
144 {
145  txid last = 0;
146  int nxip,
147  idx1,
148  idx2;
149 
150  if (snap->nxip > 1)
151  {
152  qsort(snap->xip, snap->nxip, sizeof(txid), cmp_txid);
153 
154  /* remove duplicates */
155  nxip = snap->nxip;
156  idx1 = idx2 = 0;
157  while (idx1 < nxip)
158  {
159  if (snap->xip[idx1] != last)
160  last = snap->xip[idx2++] = snap->xip[idx1];
161  else
162  snap->nxip--;
163  idx1++;
164  }
165  }
166 }
167 
168 /*
169  * check txid visibility.
170  */
171 static bool
173 {
174  if (value < snap->xmin)
175  return true;
176  else if (value >= snap->xmax)
177  return false;
178 #ifdef USE_BSEARCH_IF_NXIP_GREATER
179  else if (snap->nxip > USE_BSEARCH_IF_NXIP_GREATER)
180  {
181  void *res;
182 
183  res = bsearch(&value, snap->xip, snap->nxip, sizeof(txid), cmp_txid);
184  /* if found, transaction is still in progress */
185  return (res) ? false : true;
186  }
187 #endif
188  else
189  {
190  uint32 i;
191 
192  for (i = 0; i < snap->nxip; i++)
193  {
194  if (value == snap->xip[i])
195  return false;
196  }
197  return true;
198  }
199 }
200 
201 /*
202  * helper functions to use StringInfo for TxidSnapshot creation.
203  */
204 
205 static StringInfo
206 buf_init(txid xmin, txid xmax)
207 {
208  TxidSnapshot snap;
209  StringInfo buf;
210 
211  snap.xmin = xmin;
212  snap.xmax = xmax;
213  snap.nxip = 0;
214 
215  buf = makeStringInfo();
216  appendBinaryStringInfo(buf, (char *) &snap, TXID_SNAPSHOT_SIZE(0));
217  return buf;
218 }
219 
220 static void
222 {
223  TxidSnapshot *snap = (TxidSnapshot *) buf->data;
224 
225  /* do this before possible realloc */
226  snap->nxip++;
227 
228  appendBinaryStringInfo(buf, (char *) &xid, sizeof(xid));
229 }
230 
231 static TxidSnapshot *
233 {
234  TxidSnapshot *snap = (TxidSnapshot *) buf->data;
235 
236  SET_VARSIZE(snap, buf->len);
237 
238  /* buf is not needed anymore */
239  buf->data = NULL;
240  pfree(buf);
241 
242  return snap;
243 }
244 
245 /*
246  * simple number parser.
247  *
248  * We return 0 on error, which is invalid value for txid.
249  */
250 static txid
251 str2txid(const char *s, const char **endp)
252 {
253  txid val = 0;
254  txid cutoff = MAX_TXID / 10;
255  txid cutlim = MAX_TXID % 10;
256 
257  for (; *s; s++)
258  {
259  unsigned d;
260 
261  if (*s < '0' || *s > '9')
262  break;
263  d = *s - '0';
264 
265  /*
266  * check for overflow
267  */
268  if (val > cutoff || (val == cutoff && d > cutlim))
269  {
270  val = 0;
271  break;
272  }
273 
274  val = val * 10 + d;
275  }
276  if (endp)
277  *endp = s;
278  return val;
279 }
280 
281 /*
282  * parse snapshot from cstring
283  */
284 static TxidSnapshot *
285 parse_snapshot(const char *str)
286 {
287  txid xmin;
288  txid xmax;
289  txid last_val = 0,
290  val;
291  const char *str_start = str;
292  const char *endp;
293  StringInfo buf;
294 
295  xmin = str2txid(str, &endp);
296  if (*endp != ':')
297  goto bad_format;
298  str = endp + 1;
299 
300  xmax = str2txid(str, &endp);
301  if (*endp != ':')
302  goto bad_format;
303  str = endp + 1;
304 
305  /* it should look sane */
306  if (xmin == 0 || xmax == 0 || xmin > xmax)
307  goto bad_format;
308 
309  /* allocate buffer */
310  buf = buf_init(xmin, xmax);
311 
312  /* loop over values */
313  while (*str != '\0')
314  {
315  /* read next value */
316  val = str2txid(str, &endp);
317  str = endp;
318 
319  /* require the input to be in order */
320  if (val < xmin || val >= xmax || val < last_val)
321  goto bad_format;
322 
323  /* skip duplicates */
324  if (val != last_val)
325  buf_add_txid(buf, val);
326  last_val = val;
327 
328  if (*str == ',')
329  str++;
330  else if (*str != '\0')
331  goto bad_format;
332  }
333 
334  return buf_finalize(buf);
335 
336 bad_format:
337  ereport(ERROR,
338  (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
339  errmsg("invalid input syntax for type %s: \"%s\"",
340  "txid_snapshot", str_start)));
341  return NULL; /* keep compiler quiet */
342 }
343 
344 /*
345  * Public functions.
346  *
347  * txid_current() and txid_current_snapshot() are the only ones that
348  * communicate with core xid machinery. All the others work on data
349  * returned by them.
350  */
351 
352 /*
353  * txid_current() returns int8
354  *
355  * Return the current toplevel transaction ID as TXID
356  * If the current transaction does not have one, one is assigned.
357  */
358 Datum
360 {
361  txid val;
363 
364  /*
365  * Must prevent during recovery because if an xid is not assigned we try
366  * to assign one, which would fail. Programs already rely on this function
367  * to always return a valid current xid, so we should not change this to
368  * return NULL or similar invalid xid.
369  */
370  PreventCommandDuringRecovery("txid_current()");
371 
372  load_xid_epoch(&state);
373 
374  val = convert_xid(GetTopTransactionId(), &state);
375 
376  PG_RETURN_INT64(val);
377 }
378 
379 /*
380  * Same as txid_current() but doesn't assign a new xid if there isn't one
381  * yet.
382  */
383 Datum
385 {
386  txid val;
389 
390  if (topxid == InvalidTransactionId)
391  PG_RETURN_NULL();
392 
393  load_xid_epoch(&state);
394 
395  val = convert_xid(topxid, &state);
396 
397  PG_RETURN_INT64(val);
398 }
399 
400 /*
401  * txid_current_snapshot() returns txid_snapshot
402  *
403  * Return current snapshot in TXID format
404  *
405  * Note that only top-transaction XIDs are included in the snapshot.
406  */
407 Datum
409 {
410  TxidSnapshot *snap;
411  uint32 nxip,
412  i;
414  Snapshot cur;
415 
416  cur = GetActiveSnapshot();
417  if (cur == NULL)
418  elog(ERROR, "no active snapshot set");
419 
420  load_xid_epoch(&state);
421 
422  /*
423  * Compile-time limits on the procarray (MAX_BACKENDS processes plus
424  * MAX_BACKENDS prepared transactions) guarantee nxip won't be too large.
425  */
427  "possible overflow in txid_current_snapshot()");
428 
429  /* allocate */
430  nxip = cur->xcnt;
431  snap = palloc(TXID_SNAPSHOT_SIZE(nxip));
432 
433  /* fill */
434  snap->xmin = convert_xid(cur->xmin, &state);
435  snap->xmax = convert_xid(cur->xmax, &state);
436  snap->nxip = nxip;
437  for (i = 0; i < nxip; i++)
438  snap->xip[i] = convert_xid(cur->xip[i], &state);
439 
440  /*
441  * We want them guaranteed to be in ascending order. This also removes
442  * any duplicate xids. Normally, an XID can only be assigned to one
443  * backend, but when preparing a transaction for two-phase commit, there
444  * is a transient state when both the original backend and the dummy
445  * PGPROC entry reserved for the prepared transaction hold the same XID.
446  */
447  sort_snapshot(snap);
448 
449  /* set size after sorting, because it may have removed duplicate xips */
450  SET_VARSIZE(snap, TXID_SNAPSHOT_SIZE(snap->nxip));
451 
452  PG_RETURN_POINTER(snap);
453 }
454 
455 /*
456  * txid_snapshot_in(cstring) returns txid_snapshot
457  *
458  * input function for type txid_snapshot
459  */
460 Datum
462 {
463  char *str = PG_GETARG_CSTRING(0);
464  TxidSnapshot *snap;
465 
466  snap = parse_snapshot(str);
467 
468  PG_RETURN_POINTER(snap);
469 }
470 
471 /*
472  * txid_snapshot_out(txid_snapshot) returns cstring
473  *
474  * output function for type txid_snapshot
475  */
476 Datum
478 {
480  StringInfoData str;
481  uint32 i;
482 
483  initStringInfo(&str);
484 
485  appendStringInfo(&str, TXID_FMT ":", snap->xmin);
486  appendStringInfo(&str, TXID_FMT ":", snap->xmax);
487 
488  for (i = 0; i < snap->nxip; i++)
489  {
490  if (i > 0)
491  appendStringInfoChar(&str, ',');
492  appendStringInfo(&str, TXID_FMT, snap->xip[i]);
493  }
494 
495  PG_RETURN_CSTRING(str.data);
496 }
497 
498 /*
499  * txid_snapshot_recv(internal) returns txid_snapshot
500  *
501  * binary input function for type txid_snapshot
502  *
503  * format: int4 nxip, int8 xmin, int8 xmax, int8 xip
504  */
505 Datum
507 {
509  TxidSnapshot *snap;
510  txid last = 0;
511  int nxip;
512  int i;
513  txid xmin,
514  xmax;
515 
516  /* load and validate nxip */
517  nxip = pq_getmsgint(buf, 4);
518  if (nxip < 0 || nxip > TXID_SNAPSHOT_MAX_NXIP)
519  goto bad_format;
520 
521  xmin = pq_getmsgint64(buf);
522  xmax = pq_getmsgint64(buf);
523  if (xmin == 0 || xmax == 0 || xmin > xmax || xmax > MAX_TXID)
524  goto bad_format;
525 
526  snap = palloc(TXID_SNAPSHOT_SIZE(nxip));
527  snap->xmin = xmin;
528  snap->xmax = xmax;
529 
530  for (i = 0; i < nxip; i++)
531  {
532  txid cur = pq_getmsgint64(buf);
533 
534  if (cur < last || cur < xmin || cur >= xmax)
535  goto bad_format;
536 
537  /* skip duplicate xips */
538  if (cur == last)
539  {
540  i--;
541  nxip--;
542  continue;
543  }
544 
545  snap->xip[i] = cur;
546  last = cur;
547  }
548  snap->nxip = nxip;
549  SET_VARSIZE(snap, TXID_SNAPSHOT_SIZE(nxip));
550  PG_RETURN_POINTER(snap);
551 
552 bad_format:
553  ereport(ERROR,
554  (errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
555  errmsg("invalid external txid_snapshot data")));
556  PG_RETURN_POINTER(NULL); /* keep compiler quiet */
557 }
558 
559 /*
560  * txid_snapshot_send(txid_snapshot) returns bytea
561  *
562  * binary output function for type txid_snapshot
563  *
564  * format: int4 nxip, int8 xmin, int8 xmax, int8 xip
565  */
566 Datum
568 {
571  uint32 i;
572 
573  pq_begintypsend(&buf);
574  pq_sendint(&buf, snap->nxip, 4);
575  pq_sendint64(&buf, snap->xmin);
576  pq_sendint64(&buf, snap->xmax);
577  for (i = 0; i < snap->nxip; i++)
578  pq_sendint64(&buf, snap->xip[i]);
580 }
581 
582 /*
583  * txid_visible_in_snapshot(int8, txid_snapshot) returns bool
584  *
585  * is txid visible in snapshot ?
586  */
587 Datum
589 {
592 
593  PG_RETURN_BOOL(is_visible_txid(value, snap));
594 }
595 
596 /*
597  * txid_snapshot_xmin(txid_snapshot) returns int8
598  *
599  * return snapshot's xmin
600  */
601 Datum
603 {
605 
606  PG_RETURN_INT64(snap->xmin);
607 }
608 
609 /*
610  * txid_snapshot_xmax(txid_snapshot) returns int8
611  *
612  * return snapshot's xmax
613  */
614 Datum
616 {
618 
619  PG_RETURN_INT64(snap->xmax);
620 }
621 
622 /*
623  * txid_snapshot_xip(txid_snapshot) returns setof int8
624  *
625  * return in-progress TXIDs in snapshot.
626  */
627 Datum
629 {
630  FuncCallContext *fctx;
631  TxidSnapshot *snap;
632  txid value;
633 
634  /* on first call initialize snap_state and get copy of snapshot */
635  if (SRF_IS_FIRSTCALL())
636  {
638 
639  fctx = SRF_FIRSTCALL_INIT();
640 
641  /* make a copy of user snapshot */
643  memcpy(snap, arg, VARSIZE(arg));
644 
645  fctx->user_fctx = snap;
646  }
647 
648  /* return values one-by-one */
649  fctx = SRF_PERCALL_SETUP();
650  snap = fctx->user_fctx;
651  if (fctx->call_cntr < snap->nxip)
652  {
653  value = snap->xip[fctx->call_cntr];
654  SRF_RETURN_NEXT(fctx, Int64GetDatum(value));
655  }
656  else
657  {
658  SRF_RETURN_DONE(fctx);
659  }
660 }
static void load_xid_epoch(TxidEpoch *state)
Definition: txid.c:90
uint64 call_cntr
Definition: funcapi.h:65
#define PG_RETURN_POINTER(x)
Definition: fmgr.h:305
txid xip[FLEXIBLE_ARRAY_MEMBER]
Definition: txid.c:68
static struct @76 value
int32 __varsz
Definition: txid.c:62
static void buf_add_txid(StringInfo buf, txid xid)
Definition: txid.c:221
Definition: txid.c:79
bool TransactionIdFollows(TransactionId id1, TransactionId id2)
Definition: transam.c:334
uint32 TransactionId
Definition: c.h:393
static TxidSnapshot * buf_finalize(StringInfo buf)
Definition: txid.c:232
#define VARSIZE(PTR)
Definition: postgres.h:306
#define PG_RETURN_INT64(x)
Definition: fmgr.h:311
#define SRF_IS_FIRSTCALL()
Definition: funcapi.h:285
void pq_begintypsend(StringInfo buf)
Definition: pqformat.c:359
static void sort_snapshot(TxidSnapshot *snap)
Definition: txid.c:143
StringInfo makeStringInfo(void)
Definition: stringinfo.c:29
StringInfoData * StringInfo
Definition: stringinfo.h:46
#define USE_BSEARCH_IF_NXIP_GREATER
Definition: txid.c:49
Snapshot GetActiveSnapshot(void)
Definition: snapmgr.c:834
struct cursor * cur
Definition: ecpg.c:28
Datum txid_snapshot_xip(PG_FUNCTION_ARGS)
Definition: txid.c:628
int errcode(int sqlerrcode)
Definition: elog.c:575
Datum txid_snapshot_xmin(PG_FUNCTION_ARGS)
Definition: txid.c:602
#define PG_GETARG_POINTER(n)
Definition: fmgr.h:232
TransactionId GetTopTransactionId(void)
Definition: xact.c:388
#define PG_RETURN_BYTEA_P(x)
Definition: fmgr.h:313
txid xmin
Definition: txid.c:65
bytea * pq_endtypsend(StringInfo buf)
Definition: pqformat.c:379
TransactionId last_xid
Definition: txid.c:81
#define SRF_PERCALL_SETUP()
Definition: funcapi.h:289
signed int int32
Definition: c.h:253
#define SRF_RETURN_NEXT(_funcctx, _result)
Definition: funcapi.h:291
uint64 txid
Definition: txid.c:40
Datum txid_visible_in_snapshot(PG_FUNCTION_ARGS)
Definition: txid.c:588
Datum txid_snapshot_recv(PG_FUNCTION_ARGS)
Definition: txid.c:506
#define StaticAssertStmt(condition, errmessage)
Definition: c.h:752
static txid convert_xid(TransactionId xid, const TxidEpoch *state)
Definition: txid.c:99
void GetNextXidAndEpoch(TransactionId *xid, uint32 *epoch)
Definition: xlog.c:8243
#define TXID_FMT
Definition: txid.c:43
void pfree(void *pointer)
Definition: mcxt.c:992
#define PG_GETARG_VARLENA_P(n)
Definition: fmgr.h:242
void appendStringInfo(StringInfo str, const char *fmt,...)
Definition: stringinfo.c:110
#define ERROR
Definition: elog.h:43
void PreventCommandDuringRecovery(const char *cmdname)
Definition: utility.c:272
#define MAX_BACKENDS
Definition: postmaster.h:75
static TxidSnapshot * parse_snapshot(const char *str)
Definition: txid.c:285
Datum txid_current(PG_FUNCTION_ARGS)
Definition: txid.c:359
static char * buf
Definition: pg_test_fsync.c:65
static int cmp_txid(const void *aa, const void *bb)
Definition: txid.c:123
#define InvalidTransactionId
Definition: transam.h:31
unsigned int uint32
Definition: c.h:265
TransactionId xmax
Definition: snapshot.h:66
TransactionId xmin
Definition: snapshot.h:65
Datum txid_snapshot_xmax(PG_FUNCTION_ARGS)
Definition: txid.c:615
TransactionId GetTopTransactionIdIfAny(void)
Definition: xact.c:403
static bool is_visible_txid(txid value, const TxidSnapshot *snap)
Definition: txid.c:172
Datum Int64GetDatum(int64 X)
Definition: fmgr.c:2102
uint32 epoch
Definition: txid.c:82
static StringInfo buf_init(txid xmin, txid xmax)
Definition: txid.c:206
#define ereport(elevel, rest)
Definition: elog.h:122
Datum txid_current_snapshot(PG_FUNCTION_ARGS)
Definition: txid.c:408
bool TransactionIdPrecedes(TransactionId id1, TransactionId id2)
Definition: transam.c:300
TransactionId * xip
Definition: snapshot.h:76
void appendStringInfoChar(StringInfo str, char ch)
Definition: stringinfo.c:201
void initStringInfo(StringInfo str)
Definition: stringinfo.c:65
static txid str2txid(const char *s, const char **endp)
Definition: txid.c:251
#define PG_RETURN_BOOL(x)
Definition: fmgr.h:303
uintptr_t Datum
Definition: postgres.h:374
#define TXID_SNAPSHOT_SIZE(nxip)
Definition: txid.c:71
Datum txid_snapshot_out(PG_FUNCTION_ARGS)
Definition: txid.c:477
#define NULL
Definition: c.h:226
Definition: regguts.h:298
#define PG_RETURN_CSTRING(x)
Definition: fmgr.h:306
MemoryContext multi_call_memory_ctx
Definition: funcapi.h:109
#define MAX_TXID
Definition: txid.c:37
void * user_fctx
Definition: funcapi.h:90
Datum txid_snapshot_send(PG_FUNCTION_ARGS)
Definition: txid.c:567
uint32 xcnt
Definition: snapshot.h:77
void * palloc(Size size)
Definition: mcxt.c:891
int errmsg(const char *fmt,...)
Definition: elog.c:797
void pq_sendint(StringInfo buf, int i, int b)
Definition: pqformat.c:236
void * MemoryContextAlloc(MemoryContext context, Size size)
Definition: mcxt.c:749
Datum txid_current_if_assigned(PG_FUNCTION_ARGS)
Definition: txid.c:384
static const unsigned __int64 epoch
Definition: gettimeofday.c:34
int i
int64 pq_getmsgint64(StringInfo msg)
Definition: pqformat.c:486
uint32 nxip
Definition: txid.c:64
void * arg
#define PG_GETARG_CSTRING(n)
Definition: fmgr.h:233
#define PG_FUNCTION_ARGS
Definition: fmgr.h:150
unsigned int pq_getmsgint(StringInfo msg, int b)
Definition: pqformat.c:448
#define SET_VARSIZE(PTR, len)
Definition: postgres.h:330
#define elog
Definition: elog.h:219
txid xmax
Definition: txid.c:66
#define qsort(a, b, c, d)
Definition: port.h:440
#define TXID_SNAPSHOT_MAX_NXIP
Definition: txid.c:73
#define TransactionIdIsNormal(xid)
Definition: transam.h:42
#define PG_GETARG_INT64(n)
Definition: fmgr.h:238
void pq_sendint64(StringInfo buf, int64 i)
Definition: pqformat.c:271
void appendBinaryStringInfo(StringInfo str, const char *data, int datalen)
Definition: stringinfo.c:240
long val
Definition: informix.c:689
#define PG_RETURN_NULL()
Definition: fmgr.h:289
Datum txid_snapshot_in(PG_FUNCTION_ARGS)
Definition: txid.c:461
#define SRF_RETURN_DONE(_funcctx)
Definition: funcapi.h:309
#define SRF_FIRSTCALL_INIT()
Definition: funcapi.h:287