+/*
+ * 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);
+}
+