2626#include "executor/execdesc.h"
2727#include "executor/executor.h"
2828#include "executor/tuptable.h"
29+ #include "funcapi.h"
2930#include "libpq/libpq.h"
3031#include "libpq/pqformat.h"
3132#include "mb/pg_wchar.h"
3233#include "miscadmin.h"
3334#include "pgstat.h"
3435#include "storage/fd.h"
3536#include "tcop/tcopprot.h"
37+ #include "utils/json.h"
3638#include "utils/lsyscache.h"
3739#include "utils/memutils.h"
3840#include "utils/rel.h"
@@ -130,6 +132,7 @@ static void CopyToCSVOneRow(CopyToState cstate, TupleTableSlot *slot);
130132static void CopyToTextLikeOneRow (CopyToState cstate , TupleTableSlot * slot ,
131133 bool is_csv );
132134static void CopyToTextLikeEnd (CopyToState cstate );
135+ static void CopyToJsonOneRow (CopyToState cstate , TupleTableSlot * slot );
133136static void CopyToBinaryStart (CopyToState cstate , TupleDesc tupDesc );
134137static void CopyToBinaryOutFunc (CopyToState cstate , Oid atttypid , FmgrInfo * finfo );
135138static void CopyToBinaryOneRow (CopyToState cstate , TupleTableSlot * slot );
@@ -149,7 +152,7 @@ static void CopySendInt16(CopyToState cstate, int16 val);
149152/*
150153 * COPY TO routines for built-in formats.
151154 *
152- * CSV and text formats share the same TextLike routines except for the
155+ * CSV and text, json formats share the same TextLike routines except for the
153156 * one-row callback.
154157 */
155158
@@ -169,6 +172,14 @@ static const CopyToRoutine CopyToRoutineCSV = {
169172 .CopyToEnd = CopyToTextLikeEnd ,
170173};
171174
175+ /* json format */
176+ static const CopyToRoutine CopyToRoutineJson = {
177+ .CopyToStart = CopyToTextLikeStart ,
178+ .CopyToOutFunc = CopyToTextLikeOutFunc ,
179+ .CopyToOneRow = CopyToJsonOneRow ,
180+ .CopyToEnd = CopyToTextLikeEnd ,
181+ };
182+
172183/* binary format */
173184static const CopyToRoutine CopyToRoutineBinary = {
174185 .CopyToStart = CopyToBinaryStart ,
@@ -185,12 +196,14 @@ CopyToGetRoutine(const CopyFormatOptions *opts)
185196 return & CopyToRoutineCSV ;
186197 else if (opts -> format == COPY_FORMAT_BINARY )
187198 return & CopyToRoutineBinary ;
199+ else if (opts -> format == COPY_FORMAT_JSON )
200+ return & CopyToRoutineJson ;
188201
189202 /* default is text */
190203 return & CopyToRoutineText ;
191204}
192205
193- /* Implementation of the start callback for text and CSV formats */
206+ /* Implementation of the start callback for text, CSV, and json formats */
194207static void
195208CopyToTextLikeStart (CopyToState cstate , TupleDesc tupDesc )
196209{
@@ -209,6 +222,8 @@ CopyToTextLikeStart(CopyToState cstate, TupleDesc tupDesc)
209222 ListCell * cur ;
210223 bool hdr_delim = false;
211224
225+ Assert (cstate -> opts .format != COPY_FORMAT_JSON );
226+
212227 foreach (cur , cstate -> attnumlist )
213228 {
214229 int attnum = lfirst_int (cur );
@@ -231,7 +246,7 @@ CopyToTextLikeStart(CopyToState cstate, TupleDesc tupDesc)
231246}
232247
233248/*
234- * Implementation of the outfunc callback for text and CSV formats. Assign
249+ * Implementation of the outfunc callback for text, CSV, and json formats. Assign
235250 * the output function data to the given *finfo.
236251 */
237252static void
@@ -304,13 +319,46 @@ CopyToTextLikeOneRow(CopyToState cstate,
304319 CopySendTextLikeEndOfRow (cstate );
305320}
306321
307- /* Implementation of the end callback for text and CSV formats */
322+ /* Implementation of the end callback for text, CSV, and json formats */
308323static void
309324CopyToTextLikeEnd (CopyToState cstate )
310325{
311326 /* Nothing to do here */
312327}
313328
329+ /* Implementation of per-row callback for json format */
330+ static void
331+ CopyToJsonOneRow (CopyToState cstate , TupleTableSlot * slot )
332+ {
333+ Datum rowdata ;
334+ StringInfo result ;
335+
336+ /*
337+ * If COPY TO source data come from query rather than plain table, we need
338+ * copy CopyToState->QueryDesc->TupleDesc to slot->tts_tupleDescriptor.
339+ * This is necessary because the slot's TupleDesc may change during query
340+ * execution, and we depend on it when calling composite_to_json.
341+ */
342+ if (!cstate -> rel )
343+ {
344+ memcpy (TupleDescAttr (slot -> tts_tupleDescriptor , 0 ),
345+ TupleDescAttr (cstate -> queryDesc -> tupDesc , 0 ),
346+ cstate -> queryDesc -> tupDesc -> natts * sizeof (FormData_pg_attribute ));
347+
348+ for (int i = 0 ; i < cstate -> queryDesc -> tupDesc -> natts ; i ++ )
349+ populate_compact_attribute (slot -> tts_tupleDescriptor , i );
350+
351+ BlessTupleDesc (slot -> tts_tupleDescriptor );
352+ }
353+ rowdata = ExecFetchSlotHeapTupleDatum (slot );
354+ result = makeStringInfo ();
355+ composite_to_json (rowdata , result , false);
356+
357+ CopySendData (cstate , result -> data , result -> len );
358+
359+ CopySendTextLikeEndOfRow (cstate );
360+ }
361+
314362/*
315363 * Implementation of the start callback for binary format. Send a header
316364 * for a binary copy.
@@ -402,9 +450,21 @@ SendCopyBegin(CopyToState cstate)
402450
403451 pq_beginmessage (& buf , PqMsg_CopyOutResponse );
404452 pq_sendbyte (& buf , format ); /* overall format */
405- pq_sendint16 (& buf , natts );
406- for (i = 0 ; i < natts ; i ++ )
407- pq_sendint16 (& buf , format ); /* per-column formats */
453+ if (cstate -> opts .format != COPY_FORMAT_JSON )
454+ {
455+ pq_sendint16 (& buf , natts );
456+ for (i = 0 ; i < natts ; i ++ )
457+ pq_sendint16 (& buf , format ); /* per-column formats */
458+ }
459+ else
460+ {
461+ /*
462+ * JSON format is always one non-binary column
463+ */
464+ pq_sendint16 (& buf , 1 );
465+ pq_sendint16 (& buf , 0 );
466+ }
467+
408468 pq_endmessage (& buf );
409469 cstate -> copy_dest = COPY_FRONTEND ;
410470}
@@ -504,7 +564,7 @@ CopySendEndOfRow(CopyToState cstate)
504564}
505565
506566/*
507- * Wrapper function of CopySendEndOfRow for text and CSV formats. Sends the
567+ * Wrapper function of CopySendEndOfRow for text, CSV, and json formats. Sends the
508568 * line termination and do common appropriate things for the end of row.
509569 */
510570static inline void
0 commit comments