+ DR_printtup *myState = (DR_printtup *) self;
+
+ if (PG_PROTOCOL_MAJOR(FrontendProtocol) < 3)
+ {
+ /*
+ * Send portal name to frontend (obsolete cruft, gone in proto 3.0)
+ *
+ * If portal name not specified, use "blank" portal.
+ */
+ if (portalName == NULL)
+ portalName = "blank";
+
+ pq_puttextmessage('P', portalName);
+ }
+
+ /*
+ * If this is a retrieve, and we are supposed to emit row descriptions,
+ * then we send back the tuple descriptor of the tuples.
+ */
+ if (operation == CMD_SELECT && myState->sendDescrip)
+ SendRowDescriptionMessage(typeinfo, targetlist);
+
+ /* ----------------
+ * We could set up the derived attr info at this time, but we postpone it
+ * until the first call of printtup, for 2 reasons:
+ * 1. We don't waste time (compared to the old way) if there are no
+ * tuples at all to output.
+ * 2. Checking in printtup allows us to handle the case that the tuples
+ * change type midway through (although this probably can't happen in
+ * the current executor).
+ * ----------------
+ */
+}
+
+/*
+ * SendRowDescriptionMessage --- send a RowDescription message to the frontend
+ *
+ * Notes: the TupleDesc has typically been manufactured by ExecTypeFromTL()
+ * or some similar function; it does not contain a full set of fields.
+ * The targetlist will be NIL when executing a utility function that does
+ * not have a plan. If the targetlist isn't NIL then it is a Plan node's
+ * targetlist; it is up to us to ignore resjunk columns in it.
+ */
+void
+SendRowDescriptionMessage(TupleDesc typeinfo, List *targetlist)
+{
+ Form_pg_attribute *attrs = typeinfo->attrs;
+ int natts = typeinfo->natts;
+ int proto = PG_PROTOCOL_MAJOR(FrontendProtocol);
+ int i;
+ StringInfoData buf;
+
+ pq_beginmessage(&buf, 'T'); /* tuple descriptor message type */
+ pq_sendint(&buf, natts, 2); /* # of attrs in tuples */
+
+ for (i = 0; i < natts; ++i)
+ {
+ pq_sendstring(&buf, NameStr(attrs[i]->attname));
+ /* column ID info appears in protocol 3.0 and up */
+ if (proto >= 3)
+ {
+ /* Do we have a non-resjunk tlist item? */
+ while (targetlist &&
+ ((TargetEntry *) lfirst(targetlist))->resdom->resjunk)
+ targetlist = lnext(targetlist);
+ if (targetlist)
+ {
+ Resdom *res = ((TargetEntry *) lfirst(targetlist))->resdom;
+
+ pq_sendint(&buf, res->resorigtbl, 4);
+ pq_sendint(&buf, res->resorigcol, 2);
+ targetlist = lnext(targetlist);
+ }
+ else
+ {
+ /* No info available, so send zeroes */
+ pq_sendint(&buf, 0, 4);
+ pq_sendint(&buf, 0, 2);
+ }
+ }
+ pq_sendint(&buf, (int) attrs[i]->atttypid,
+ sizeof(attrs[i]->atttypid));
+ pq_sendint(&buf, attrs[i]->attlen,
+ sizeof(attrs[i]->attlen));
+ /* typmod appears in protocol 2.0 and up */
+ if (proto >= 2)
+ pq_sendint(&buf, attrs[i]->atttypmod,
+ sizeof(attrs[i]->atttypmod));
+ }
+ pq_endmessage(&buf);
+}
+
+static void
+printtup_prepare_info(DR_printtup *myState, TupleDesc typeinfo, int numAttrs)
+{
+ int i;
+
+ if (myState->myinfo)
+ pfree(myState->myinfo); /* get rid of any old data */
+ myState->myinfo = NULL;
+ myState->attrinfo = typeinfo;
+ myState->nattrs = numAttrs;
+ if (numAttrs <= 0)
+ return;
+ myState->myinfo = (PrinttupAttrInfo *)
+ palloc(numAttrs * sizeof(PrinttupAttrInfo));
+ for (i = 0; i < numAttrs; i++)
+ {
+ PrinttupAttrInfo *thisState = myState->myinfo + i;
+
+ if (getTypeOutputInfo(typeinfo->attrs[i]->atttypid,
+ &thisState->typoutput, &thisState->typelem,
+ &thisState->typisvarlena))
+ fmgr_info(thisState->typoutput, &thisState->finfo);
+ }