You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

printtup.c 19KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717
  1. /*-------------------------------------------------------------------------
  2. *
  3. * printtup.c
  4. * Routines to print out tuples to the destination (both frontend
  5. * clients and standalone backends are supported here).
  6. *
  7. *
  8. * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
  9. * Portions Copyright (c) 1994, Regents of the University of California
  10. *
  11. * IDENTIFICATION
  12. * src/backend/access/common/printtup.c
  13. *
  14. *-------------------------------------------------------------------------
  15. */
  16. #include "postgres.h"
  17. #include "access/printtup.h"
  18. #include "libpq/libpq.h"
  19. #include "libpq/pqformat.h"
  20. #include "tcop/pquery.h"
  21. #include "utils/lsyscache.h"
  22. #include "utils/memdebug.h"
  23. #include "utils/memutils.h"
  24. static void printtup_startup(DestReceiver *self, int operation,
  25. TupleDesc typeinfo);
  26. static bool printtup(TupleTableSlot *slot, DestReceiver *self);
  27. static bool printtup_20(TupleTableSlot *slot, DestReceiver *self);
  28. static bool printtup_internal_20(TupleTableSlot *slot, DestReceiver *self);
  29. static void printtup_shutdown(DestReceiver *self);
  30. static void printtup_destroy(DestReceiver *self);
  31. static void SendRowDescriptionCols_2(StringInfo buf, TupleDesc typeinfo,
  32. List *targetlist, int16 *formats);
  33. static void SendRowDescriptionCols_3(StringInfo buf, TupleDesc typeinfo,
  34. List *targetlist, int16 *formats);
  35. /* ----------------------------------------------------------------
  36. * printtup / debugtup support
  37. * ----------------------------------------------------------------
  38. */
  39. /* ----------------
  40. * Private state for a printtup destination object
  41. *
  42. * NOTE: finfo is the lookup info for either typoutput or typsend, whichever
  43. * we are using for this column.
  44. * ----------------
  45. */
  46. typedef struct
  47. { /* Per-attribute information */
  48. Oid typoutput; /* Oid for the type's text output fn */
  49. Oid typsend; /* Oid for the type's binary output fn */
  50. bool typisvarlena; /* is it varlena (ie possibly toastable)? */
  51. int16 format; /* format code for this column */
  52. FmgrInfo finfo; /* Precomputed call info for output fn */
  53. } PrinttupAttrInfo;
  54. typedef struct
  55. {
  56. DestReceiver pub; /* publicly-known function pointers */
  57. StringInfoData buf; /* output buffer */
  58. Portal portal; /* the Portal we are printing from */
  59. bool sendDescrip; /* send RowDescription at startup? */
  60. TupleDesc attrinfo; /* The attr info we are set up for */
  61. int nattrs;
  62. PrinttupAttrInfo *myinfo; /* Cached info about each attr */
  63. MemoryContext tmpcontext; /* Memory context for per-row workspace */
  64. } DR_printtup;
  65. /* ----------------
  66. * Initialize: create a DestReceiver for printtup
  67. * ----------------
  68. */
  69. DestReceiver *
  70. printtup_create_DR(CommandDest dest)
  71. {
  72. DR_printtup *self = (DR_printtup *) palloc0(sizeof(DR_printtup));
  73. self->pub.receiveSlot = printtup; /* might get changed later */
  74. self->pub.rStartup = printtup_startup;
  75. self->pub.rShutdown = printtup_shutdown;
  76. self->pub.rDestroy = printtup_destroy;
  77. self->pub.mydest = dest;
  78. /*
  79. * Send T message automatically if DestRemote, but not if
  80. * DestRemoteExecute
  81. */
  82. self->sendDescrip = (dest == DestRemote);
  83. self->attrinfo = NULL;
  84. self->nattrs = 0;
  85. self->myinfo = NULL;
  86. self->tmpcontext = NULL;
  87. return (DestReceiver *) self;
  88. }
  89. /*
  90. * Set parameters for a DestRemote (or DestRemoteExecute) receiver
  91. */
  92. void
  93. SetRemoteDestReceiverParams(DestReceiver *self, Portal portal)
  94. {
  95. DR_printtup *myState = (DR_printtup *) self;
  96. Assert(myState->pub.mydest == DestRemote ||
  97. myState->pub.mydest == DestRemoteExecute);
  98. myState->portal = portal;
  99. if (PG_PROTOCOL_MAJOR(FrontendProtocol) < 3)
  100. {
  101. /*
  102. * In protocol 2.0 the Bind message does not exist, so there is no way
  103. * for the columns to have different print formats; it's sufficient to
  104. * look at the first one.
  105. */
  106. if (portal->formats && portal->formats[0] != 0)
  107. myState->pub.receiveSlot = printtup_internal_20;
  108. else
  109. myState->pub.receiveSlot = printtup_20;
  110. }
  111. }
  112. static void
  113. printtup_startup(DestReceiver *self, int operation, TupleDesc typeinfo)
  114. {
  115. DR_printtup *myState = (DR_printtup *) self;
  116. Portal portal = myState->portal;
  117. /* create buffer to be used for all messages */
  118. initStringInfo(&myState->buf);
  119. /*
  120. * Create a temporary memory context that we can reset once per row to
  121. * recover palloc'd memory. This avoids any problems with leaks inside
  122. * datatype output routines, and should be faster than retail pfree's
  123. * anyway.
  124. */
  125. myState->tmpcontext = AllocSetContextCreate(CurrentMemoryContext,
  126. "printtup",
  127. ALLOCSET_DEFAULT_SIZES);
  128. if (PG_PROTOCOL_MAJOR(FrontendProtocol) < 3)
  129. {
  130. /*
  131. * Send portal name to frontend (obsolete cruft, gone in proto 3.0)
  132. *
  133. * If portal name not specified, use "blank" portal.
  134. */
  135. const char *portalName = portal->name;
  136. if (portalName == NULL || portalName[0] == '\0')
  137. portalName = "blank";
  138. pq_puttextmessage('P', portalName);
  139. }
  140. /*
  141. * If we are supposed to emit row descriptions, then send the tuple
  142. * descriptor of the tuples.
  143. */
  144. if (myState->sendDescrip)
  145. SendRowDescriptionMessage(&myState->buf,
  146. typeinfo,
  147. FetchPortalTargetList(portal),
  148. portal->formats);
  149. /* ----------------
  150. * We could set up the derived attr info at this time, but we postpone it
  151. * until the first call of printtup, for 2 reasons:
  152. * 1. We don't waste time (compared to the old way) if there are no
  153. * tuples at all to output.
  154. * 2. Checking in printtup allows us to handle the case that the tuples
  155. * change type midway through (although this probably can't happen in
  156. * the current executor).
  157. * ----------------
  158. */
  159. }
  160. /*
  161. * SendRowDescriptionMessage --- send a RowDescription message to the frontend
  162. *
  163. * Notes: the TupleDesc has typically been manufactured by ExecTypeFromTL()
  164. * or some similar function; it does not contain a full set of fields.
  165. * The targetlist will be NIL when executing a utility function that does
  166. * not have a plan. If the targetlist isn't NIL then it is a Query node's
  167. * targetlist; it is up to us to ignore resjunk columns in it. The formats[]
  168. * array pointer might be NULL (if we are doing Describe on a prepared stmt);
  169. * send zeroes for the format codes in that case.
  170. */
  171. void
  172. SendRowDescriptionMessage(StringInfo buf, TupleDesc typeinfo,
  173. List *targetlist, int16 *formats)
  174. {
  175. int natts = typeinfo->natts;
  176. int proto = PG_PROTOCOL_MAJOR(FrontendProtocol);
  177. /* tuple descriptor message type */
  178. pq_beginmessage_reuse(buf, 'T');
  179. /* # of attrs in tuples */
  180. pq_sendint16(buf, natts);
  181. if (proto >= 3)
  182. SendRowDescriptionCols_3(buf, typeinfo, targetlist, formats);
  183. else
  184. SendRowDescriptionCols_2(buf, typeinfo, targetlist, formats);
  185. pq_endmessage_reuse(buf);
  186. }
  187. /*
  188. * Send description for each column when using v3+ protocol
  189. */
  190. static void
  191. SendRowDescriptionCols_3(StringInfo buf, TupleDesc typeinfo, List *targetlist, int16 *formats)
  192. {
  193. int natts = typeinfo->natts;
  194. int i;
  195. ListCell *tlist_item = list_head(targetlist);
  196. /*
  197. * Preallocate memory for the entire message to be sent. That allows to
  198. * use the significantly faster inline pqformat.h functions and to avoid
  199. * reallocations.
  200. *
  201. * Have to overestimate the size of the column-names, to account for
  202. * character set overhead.
  203. */
  204. enlargeStringInfo(buf, (NAMEDATALEN * MAX_CONVERSION_GROWTH /* attname */
  205. + sizeof(Oid) /* resorigtbl */
  206. + sizeof(AttrNumber) /* resorigcol */
  207. + sizeof(Oid) /* atttypid */
  208. + sizeof(int16) /* attlen */
  209. + sizeof(int32) /* attypmod */
  210. + sizeof(int16) /* format */
  211. ) * natts);
  212. for (i = 0; i < natts; ++i)
  213. {
  214. Form_pg_attribute att = TupleDescAttr(typeinfo, i);
  215. Oid atttypid = att->atttypid;
  216. int32 atttypmod = att->atttypmod;
  217. Oid resorigtbl;
  218. AttrNumber resorigcol;
  219. int16 format;
  220. /*
  221. * If column is a domain, send the base type and typmod instead.
  222. * Lookup before sending any ints, for efficiency.
  223. */
  224. atttypid = getBaseTypeAndTypmod(atttypid, &atttypmod);
  225. /* Do we have a non-resjunk tlist item? */
  226. while (tlist_item &&
  227. ((TargetEntry *) lfirst(tlist_item))->resjunk)
  228. tlist_item = lnext(tlist_item);
  229. if (tlist_item)
  230. {
  231. TargetEntry *tle = (TargetEntry *) lfirst(tlist_item);
  232. resorigtbl = tle->resorigtbl;
  233. resorigcol = tle->resorigcol;
  234. tlist_item = lnext(tlist_item);
  235. }
  236. else
  237. {
  238. /* No info available, so send zeroes */
  239. resorigtbl = 0;
  240. resorigcol = 0;
  241. }
  242. if (formats)
  243. format = formats[i];
  244. else
  245. format = 0;
  246. pq_writestring(buf, NameStr(att->attname));
  247. pq_writeint32(buf, resorigtbl);
  248. pq_writeint16(buf, resorigcol);
  249. pq_writeint32(buf, atttypid);
  250. pq_writeint16(buf, att->attlen);
  251. pq_writeint32(buf, atttypmod);
  252. pq_writeint16(buf, format);
  253. }
  254. }
  255. /*
  256. * Send description for each column when using v2 protocol
  257. */
  258. static void
  259. SendRowDescriptionCols_2(StringInfo buf, TupleDesc typeinfo, List *targetlist, int16 *formats)
  260. {
  261. int natts = typeinfo->natts;
  262. int i;
  263. for (i = 0; i < natts; ++i)
  264. {
  265. Form_pg_attribute att = TupleDescAttr(typeinfo, i);
  266. Oid atttypid = att->atttypid;
  267. int32 atttypmod = att->atttypmod;
  268. /* If column is a domain, send the base type and typmod instead */
  269. atttypid = getBaseTypeAndTypmod(atttypid, &atttypmod);
  270. pq_sendstring(buf, NameStr(att->attname));
  271. /* column ID only info appears in protocol 3.0 and up */
  272. pq_sendint32(buf, atttypid);
  273. pq_sendint16(buf, att->attlen);
  274. pq_sendint32(buf, atttypmod);
  275. /* format info only appears in protocol 3.0 and up */
  276. }
  277. }
  278. /*
  279. * Get the lookup info that printtup() needs
  280. */
  281. static void
  282. printtup_prepare_info(DR_printtup *myState, TupleDesc typeinfo, int numAttrs)
  283. {
  284. int16 *formats = myState->portal->formats;
  285. int i;
  286. /* get rid of any old data */
  287. if (myState->myinfo)
  288. pfree(myState->myinfo);
  289. myState->myinfo = NULL;
  290. myState->attrinfo = typeinfo;
  291. myState->nattrs = numAttrs;
  292. if (numAttrs <= 0)
  293. return;
  294. myState->myinfo = (PrinttupAttrInfo *)
  295. palloc0(numAttrs * sizeof(PrinttupAttrInfo));
  296. for (i = 0; i < numAttrs; i++)
  297. {
  298. PrinttupAttrInfo *thisState = myState->myinfo + i;
  299. int16 format = (formats ? formats[i] : 0);
  300. Form_pg_attribute attr = TupleDescAttr(typeinfo, i);
  301. thisState->format = format;
  302. if (format == 0)
  303. {
  304. getTypeOutputInfo(attr->atttypid,
  305. &thisState->typoutput,
  306. &thisState->typisvarlena);
  307. fmgr_info(thisState->typoutput, &thisState->finfo);
  308. }
  309. else if (format == 1)
  310. {
  311. getTypeBinaryOutputInfo(attr->atttypid,
  312. &thisState->typsend,
  313. &thisState->typisvarlena);
  314. fmgr_info(thisState->typsend, &thisState->finfo);
  315. }
  316. else
  317. ereport(ERROR,
  318. (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
  319. errmsg("unsupported format code: %d", format)));
  320. }
  321. }
  322. /* ----------------
  323. * printtup --- print a tuple in protocol 3.0
  324. * ----------------
  325. */
  326. static bool
  327. printtup(TupleTableSlot *slot, DestReceiver *self)
  328. {
  329. TupleDesc typeinfo = slot->tts_tupleDescriptor;
  330. DR_printtup *myState = (DR_printtup *) self;
  331. MemoryContext oldcontext;
  332. StringInfo buf = &myState->buf;
  333. int natts = typeinfo->natts;
  334. int i;
  335. /* Set or update my derived attribute info, if needed */
  336. if (myState->attrinfo != typeinfo || myState->nattrs != natts)
  337. printtup_prepare_info(myState, typeinfo, natts);
  338. /* Make sure the tuple is fully deconstructed */
  339. slot_getallattrs(slot);
  340. /* Switch into per-row context so we can recover memory below */
  341. oldcontext = MemoryContextSwitchTo(myState->tmpcontext);
  342. /*
  343. * Prepare a DataRow message (note buffer is in per-row context)
  344. */
  345. pq_beginmessage_reuse(buf, 'D');
  346. pq_sendint16(buf, natts);
  347. /*
  348. * send the attributes of this tuple
  349. */
  350. for (i = 0; i < natts; ++i)
  351. {
  352. PrinttupAttrInfo *thisState = myState->myinfo + i;
  353. Datum attr = slot->tts_values[i];
  354. if (slot->tts_isnull[i])
  355. {
  356. pq_sendint32(buf, -1);
  357. continue;
  358. }
  359. /*
  360. * Here we catch undefined bytes in datums that are returned to the
  361. * client without hitting disk; see comments at the related check in
  362. * PageAddItem(). This test is most useful for uncompressed,
  363. * non-external datums, but we're quite likely to see such here when
  364. * testing new C functions.
  365. */
  366. if (thisState->typisvarlena)
  367. VALGRIND_CHECK_MEM_IS_DEFINED(DatumGetPointer(attr),
  368. VARSIZE_ANY(attr));
  369. if (thisState->format == 0)
  370. {
  371. /* Text output */
  372. char *outputstr;
  373. outputstr = OutputFunctionCall(&thisState->finfo, attr);
  374. pq_sendcountedtext(buf, outputstr, strlen(outputstr), false);
  375. }
  376. else
  377. {
  378. /* Binary output */
  379. bytea *outputbytes;
  380. outputbytes = SendFunctionCall(&thisState->finfo, attr);
  381. pq_sendint32(buf, VARSIZE(outputbytes) - VARHDRSZ);
  382. pq_sendbytes(buf, VARDATA(outputbytes),
  383. VARSIZE(outputbytes) - VARHDRSZ);
  384. }
  385. }
  386. pq_endmessage_reuse(buf);
  387. /* Return to caller's context, and flush row's temporary memory */
  388. MemoryContextSwitchTo(oldcontext);
  389. MemoryContextReset(myState->tmpcontext);
  390. return true;
  391. }
  392. /* ----------------
  393. * printtup_20 --- print a tuple in protocol 2.0
  394. * ----------------
  395. */
  396. static bool
  397. printtup_20(TupleTableSlot *slot, DestReceiver *self)
  398. {
  399. TupleDesc typeinfo = slot->tts_tupleDescriptor;
  400. DR_printtup *myState = (DR_printtup *) self;
  401. MemoryContext oldcontext;
  402. StringInfo buf = &myState->buf;
  403. int natts = typeinfo->natts;
  404. int i,
  405. j,
  406. k;
  407. /* Set or update my derived attribute info, if needed */
  408. if (myState->attrinfo != typeinfo || myState->nattrs != natts)
  409. printtup_prepare_info(myState, typeinfo, natts);
  410. /* Make sure the tuple is fully deconstructed */
  411. slot_getallattrs(slot);
  412. /* Switch into per-row context so we can recover memory below */
  413. oldcontext = MemoryContextSwitchTo(myState->tmpcontext);
  414. /*
  415. * tell the frontend to expect new tuple data (in ASCII style)
  416. */
  417. pq_beginmessage_reuse(buf, 'D');
  418. /*
  419. * send a bitmap of which attributes are not null
  420. */
  421. j = 0;
  422. k = 1 << 7;
  423. for (i = 0; i < natts; ++i)
  424. {
  425. if (!slot->tts_isnull[i])
  426. j |= k; /* set bit if not null */
  427. k >>= 1;
  428. if (k == 0) /* end of byte? */
  429. {
  430. pq_sendint8(buf, j);
  431. j = 0;
  432. k = 1 << 7;
  433. }
  434. }
  435. if (k != (1 << 7)) /* flush last partial byte */
  436. pq_sendint8(buf, j);
  437. /*
  438. * send the attributes of this tuple
  439. */
  440. for (i = 0; i < natts; ++i)
  441. {
  442. PrinttupAttrInfo *thisState = myState->myinfo + i;
  443. Datum attr = slot->tts_values[i];
  444. char *outputstr;
  445. if (slot->tts_isnull[i])
  446. continue;
  447. Assert(thisState->format == 0);
  448. outputstr = OutputFunctionCall(&thisState->finfo, attr);
  449. pq_sendcountedtext(buf, outputstr, strlen(outputstr), true);
  450. }
  451. pq_endmessage_reuse(buf);
  452. /* Return to caller's context, and flush row's temporary memory */
  453. MemoryContextSwitchTo(oldcontext);
  454. MemoryContextReset(myState->tmpcontext);
  455. return true;
  456. }
  457. /* ----------------
  458. * printtup_shutdown
  459. * ----------------
  460. */
  461. static void
  462. printtup_shutdown(DestReceiver *self)
  463. {
  464. DR_printtup *myState = (DR_printtup *) self;
  465. if (myState->myinfo)
  466. pfree(myState->myinfo);
  467. myState->myinfo = NULL;
  468. myState->attrinfo = NULL;
  469. if (myState->tmpcontext)
  470. MemoryContextDelete(myState->tmpcontext);
  471. myState->tmpcontext = NULL;
  472. }
  473. /* ----------------
  474. * printtup_destroy
  475. * ----------------
  476. */
  477. static void
  478. printtup_destroy(DestReceiver *self)
  479. {
  480. pfree(self);
  481. }
  482. /* ----------------
  483. * printatt
  484. * ----------------
  485. */
  486. static void
  487. printatt(unsigned attributeId,
  488. Form_pg_attribute attributeP,
  489. char *value)
  490. {
  491. printf("\t%2d: %s%s%s%s\t(typeid = %u, len = %d, typmod = %d, byval = %c)\n",
  492. attributeId,
  493. NameStr(attributeP->attname),
  494. value != NULL ? " = \"" : "",
  495. value != NULL ? value : "",
  496. value != NULL ? "\"" : "",
  497. (unsigned int) (attributeP->atttypid),
  498. attributeP->attlen,
  499. attributeP->atttypmod,
  500. attributeP->attbyval ? 't' : 'f');
  501. }
  502. /* ----------------
  503. * debugStartup - prepare to print tuples for an interactive backend
  504. * ----------------
  505. */
  506. void
  507. debugStartup(DestReceiver *self, int operation, TupleDesc typeinfo)
  508. {
  509. int natts = typeinfo->natts;
  510. int i;
  511. /*
  512. * show the return type of the tuples
  513. */
  514. for (i = 0; i < natts; ++i)
  515. printatt((unsigned) i + 1, TupleDescAttr(typeinfo, i), NULL);
  516. printf("\t----\n");
  517. }
  518. /* ----------------
  519. * debugtup - print one tuple for an interactive backend
  520. * ----------------
  521. */
  522. bool
  523. debugtup(TupleTableSlot *slot, DestReceiver *self)
  524. {
  525. TupleDesc typeinfo = slot->tts_tupleDescriptor;
  526. int natts = typeinfo->natts;
  527. int i;
  528. Datum attr;
  529. char *value;
  530. bool isnull;
  531. Oid typoutput;
  532. bool typisvarlena;
  533. for (i = 0; i < natts; ++i)
  534. {
  535. attr = slot_getattr(slot, i + 1, &isnull);
  536. if (isnull)
  537. continue;
  538. getTypeOutputInfo(TupleDescAttr(typeinfo, i)->atttypid,
  539. &typoutput, &typisvarlena);
  540. value = OidOutputFunctionCall(typoutput, attr);
  541. printatt((unsigned) i + 1, TupleDescAttr(typeinfo, i), value);
  542. }
  543. printf("\t----\n");
  544. return true;
  545. }
  546. /* ----------------
  547. * printtup_internal_20 --- print a binary tuple in protocol 2.0
  548. *
  549. * We use a different message type, i.e. 'B' instead of 'D' to
  550. * indicate a tuple in internal (binary) form.
  551. *
  552. * This is largely same as printtup_20, except we use binary formatting.
  553. * ----------------
  554. */
  555. static bool
  556. printtup_internal_20(TupleTableSlot *slot, DestReceiver *self)
  557. {
  558. TupleDesc typeinfo = slot->tts_tupleDescriptor;
  559. DR_printtup *myState = (DR_printtup *) self;
  560. MemoryContext oldcontext;
  561. StringInfo buf = &myState->buf;
  562. int natts = typeinfo->natts;
  563. int i,
  564. j,
  565. k;
  566. /* Set or update my derived attribute info, if needed */
  567. if (myState->attrinfo != typeinfo || myState->nattrs != natts)
  568. printtup_prepare_info(myState, typeinfo, natts);
  569. /* Make sure the tuple is fully deconstructed */
  570. slot_getallattrs(slot);
  571. /* Switch into per-row context so we can recover memory below */
  572. oldcontext = MemoryContextSwitchTo(myState->tmpcontext);
  573. /*
  574. * tell the frontend to expect new tuple data (in binary style)
  575. */
  576. pq_beginmessage_reuse(buf, 'B');
  577. /*
  578. * send a bitmap of which attributes are not null
  579. */
  580. j = 0;
  581. k = 1 << 7;
  582. for (i = 0; i < natts; ++i)
  583. {
  584. if (!slot->tts_isnull[i])
  585. j |= k; /* set bit if not null */
  586. k >>= 1;
  587. if (k == 0) /* end of byte? */
  588. {
  589. pq_sendint8(buf, j);
  590. j = 0;
  591. k = 1 << 7;
  592. }
  593. }
  594. if (k != (1 << 7)) /* flush last partial byte */
  595. pq_sendint8(buf, j);
  596. /*
  597. * send the attributes of this tuple
  598. */
  599. for (i = 0; i < natts; ++i)
  600. {
  601. PrinttupAttrInfo *thisState = myState->myinfo + i;
  602. Datum attr = slot->tts_values[i];
  603. bytea *outputbytes;
  604. if (slot->tts_isnull[i])
  605. continue;
  606. Assert(thisState->format == 1);
  607. outputbytes = SendFunctionCall(&thisState->finfo, attr);
  608. pq_sendint32(buf, VARSIZE(outputbytes) - VARHDRSZ);
  609. pq_sendbytes(buf, VARDATA(outputbytes),
  610. VARSIZE(outputbytes) - VARHDRSZ);
  611. }
  612. pq_endmessage_reuse(buf);
  613. /* Return to caller's context, and flush row's temporary memory */
  614. MemoryContextSwitchTo(oldcontext);
  615. MemoryContextReset(myState->tmpcontext);
  616. return true;
  617. }