=== Applying patches on top of PostgreSQL commit ID 9018c7d37bb464cd53567c0b553a6f49b50bec78 === /etc/rc.d/jail: WARNING: Per-jail configuration via jail_* variables is obsolete. Please consider migrating to /etc/jail.conf. Sun Apr 19 21:44:30 UTC 2026 On branch cf/4681 nothing to commit, working tree clean === using 'git am' to apply patch ./0001-Separate-format-specific-fields-from-CopyToStateData.patch === Applying: Separate format-specific fields from CopyToStateData struct. Using index info to reconstruct a base tree... M src/backend/commands/copyto.c M src/include/commands/copy.h M src/include/commands/copyapi.h Falling back to patching base and 3-way merge... Auto-merging src/include/commands/copyapi.h Auto-merging src/include/commands/copy.h Auto-merging src/backend/commands/copyto.c CONFLICT (content): Merge conflict in src/backend/commands/copyto.c error: Failed to merge in the changes. hint: Use 'git am --show-current-patch=diff' to see the failed patch Patch failed at 0001 Separate format-specific fields from CopyToStateData struct. When you have resolved this problem, run "git am --continue". If you prefer to skip this patch, run "git am --skip" instead. To restore the original branch and stop patching, run "git am --abort". === using patch(1) to apply patch ./0001-Separate-format-specific-fields-from-CopyToStateData.patch === patching file src/backend/commands/copyto.c Hunk #1 succeeded at 23 with fuzz 1 (offset 1 line). Hunk #2 succeeded at 45 with fuzz 1 (offset 4 lines). Hunk #3 FAILED at 54. Hunk #4 succeeded at 118 (offset 12 lines). Hunk #5 FAILED at 121. Hunk #6 succeeded at 157 (offset 11 lines). Hunk #7 succeeded at 166 (offset 11 lines). Hunk #8 FAILED at 164. Hunk #9 succeeded at 319 (offset 32 lines). Hunk #10 succeeded at 425 with fuzz 1 (offset 121 lines). Hunk #11 succeeded at 541 with fuzz 2 (offset 135 lines). Hunk #12 FAILED at 453. Hunk #13 succeeded at 624 with fuzz 2 (offset 137 lines). Hunk #14 succeeded at 649 (offset 137 lines). Hunk #15 succeeded at 657 (offset 137 lines). Hunk #16 succeeded at 751 (offset 137 lines). Hunk #17 FAILED at 766. Hunk #18 succeeded at 920 (offset 137 lines). Hunk #19 succeeded at 1143 (offset 180 lines). Hunk #20 succeeded at 1440 (offset 181 lines). Hunk #21 succeeded at 1514 (offset 181 lines). Hunk #22 succeeded at 1574 (offset 181 lines). Hunk #23 succeeded at 1597 (offset 181 lines). Hunk #24 succeeded at 1652 (offset 181 lines). Hunk #25 succeeded at 1663 (offset 181 lines). Hunk #26 succeeded at 1673 (offset 181 lines). 5 out of 26 hunks FAILED -- saving rejects to file src/backend/commands/copyto.c.rej patching file src/include/commands/copy.h Hunk #1 succeeded at 99 (offset 12 lines). patching file src/include/commands/copyapi.h patching file src/include/commands/copystate.h Unstaged changes after reset: M src/backend/commands/copyto.c M src/include/commands/copy.h M src/include/commands/copyapi.h Removing src/backend/commands/copyto.c.rej Removing src/include/commands/copystate.h === using 'git apply' to apply patch ./0001-Separate-format-specific-fields-from-CopyToStateData.patch === Applied patch to 'src/backend/commands/copyto.c' with conflicts. Applied patch to 'src/include/commands/copy.h' cleanly. Applied patch to 'src/include/commands/copyapi.h' cleanly. Falling back to direct application... U src/backend/commands/copyto.c diff --cc src/backend/commands/copyto.c index 85d15353647,6a0a66507ba..00000000000 --- a/src/backend/commands/copyto.c +++ b/src/backend/commands/copyto.c @@@ -20,9 -20,10 +20,11 @@@ #include "access/table.h" #include "access/tableam.h" +#include "access/tupconvert.h" #include "catalog/pg_inherits.h" #include "commands/copyapi.h" + #include "commands/copystate.h" + #include "commands/defrem.h" #include "commands/progress.h" #include "executor/execdesc.h" #include "executor/executor.h" @@@ -40,22 -39,9 +42,10 @@@ #include "utils/memutils.h" #include "utils/rel.h" #include "utils/snapmgr.h" +#include "utils/wait_event.h" /* - * Represents the different dest cases we need to worry about at - * the bottom level - */ - typedef enum CopyDest - { - COPY_FILE, /* to file (or a piped program) */ - COPY_FRONTEND, /* to frontend */ - COPY_CALLBACK, /* to callback function */ - } CopyDest; - - /* - * This struct contains all the state variables used throughout a COPY TO - * operation. + * Struct used for text and CSV format. * * Multi-byte encodings: all supported client-side encodings encode multi-byte * characters by having the first byte's high bit set. Subsequent bytes of the @@@ -81,36 -61,7 +65,40 @@@ typedef struct CopyToStateTextLik int file_encoding; /* file or remote side's character encoding */ bool need_transcoding; /* file encoding diff from server? */ bool encoding_embeds_ascii; /* ASCII can be non-first byte? */ ++<<<<<<< ours + + /* parameters from the COPY command */ + Relation rel; /* relation to copy to */ + QueryDesc *queryDesc; /* executable query to copy from */ + List *attnumlist; /* integer list of attnums to copy */ + char *filename; /* filename, or NULL for STDOUT */ + bool is_program; /* is 'filename' a program to popen? */ + bool json_row_delim_needed; /* need delimiter before next row */ + StringInfo json_buf; /* reusable buffer for JSON output, + * initialized in BeginCopyTo */ + TupleDesc tupDesc; /* Descriptor for JSON output; for a column + * list this is a projected descriptor */ + Datum *json_projvalues; /* pre-allocated projection values, or + * NULL */ + bool *json_projnulls; /* pre-allocated projection nulls, or NULL */ + copy_data_dest_cb data_dest_cb; /* function for writing data */ + + CopyFormatOptions opts; + Node *whereClause; /* WHERE condition (or NULL) */ + List *partitions; /* OID list of partitions to copy data from */ + + /* + * Working state + */ + MemoryContext copycontext; /* per-copy execution context */ + + FmgrInfo *out_functions; /* lookup info for output functions */ + MemoryContext rowcontext; /* per-row evaluation context */ + uint64 bytes_processed; /* number of bytes processed so far */ +} CopyToStateData; ++======= + } CopyToStateTextLike; ++>>>>>>> theirs /* DestReceiver for COPY (query) TO */ typedef struct @@@ -142,8 -94,7 +131,12 @@@ static void CopyToCSVOneRow(CopyToStat static void CopyToTextLikeOneRow(CopyToState cstate, TupleTableSlot *slot, bool is_csv); static void CopyToTextLikeEnd(CopyToState cstate); ++<<<<<<< ours +static void CopyToJsonOneRow(CopyToState cstate, TupleTableSlot *slot); +static void CopyToJsonEnd(CopyToState cstate); ++======= + static Size CopyToEstimateStateBinary(void); ++>>>>>>> theirs static void CopyToBinaryStart(CopyToState cstate, TupleDesc tupDesc); static void CopyToBinaryOutFunc(CopyToState cstate, Oid atttypid, FmgrInfo *finfo); static void CopyToBinaryOneRow(CopyToState cstate, TupleTableSlot *slot); @@@ -180,41 -136,46 +175,66 @@@ static const CopyToRoutine CopyToRoutin .CopyToEnd = CopyToTextLikeEnd, }; +/* json format */ +static const CopyToRoutine CopyToRoutineJson = { + .CopyToStart = CopyToTextLikeStart, + .CopyToOutFunc = CopyToTextLikeOutFunc, + .CopyToOneRow = CopyToJsonOneRow, + .CopyToEnd = CopyToJsonEnd, +}; + /* binary format */ static const CopyToRoutine CopyToRoutineBinary = { + .CopyToEstimateStateSpace = CopyToEstimateStateBinary, .CopyToStart = CopyToBinaryStart, .CopyToOutFunc = CopyToBinaryOutFunc, .CopyToOneRow = CopyToBinaryOneRow, .CopyToEnd = CopyToBinaryEnd, }; - /* Return a COPY TO routine for the given options */ - static const CopyToRoutine * - CopyToGetRoutine(const CopyFormatOptions *opts) + static Size + CopyToEstimateStateTextLike(void) { ++<<<<<<< ours + if (opts->format == COPY_FORMAT_CSV) + return &CopyToRoutineCSV; + else if (opts->format == COPY_FORMAT_BINARY) + return &CopyToRoutineBinary; + else if (opts->format == COPY_FORMAT_JSON) + return &CopyToRoutineJson; + + /* default is text */ + return &CopyToRoutineText; ++======= + return sizeof(CopyToStateTextLike); ++>>>>>>> theirs } -/* Implementation of the start callback for text and CSV formats */ +/* Implementation of the start callback for text, CSV, and json formats */ static void - CopyToTextLikeStart(CopyToState cstate, TupleDesc tupDesc) + CopyToTextLikeStart(CopyToState ccstate, TupleDesc tupDesc) { + CopyToStateTextLike *cstate = (CopyToStateTextLike *) ccstate; + + /* Use client encoding when ENCODING option is not specified. */ + if (cstate->base.opts.file_encoding < 0) + cstate->file_encoding = pg_get_client_encoding(); + else + cstate->file_encoding = cstate->base.opts.file_encoding; + + /* + * Set up encoding conversion info if the file and server encodings differ + * (see also pg_server_to_any). + */ + if (cstate->file_encoding == GetDatabaseEncoding() || + cstate->file_encoding == PG_SQL_ASCII) + cstate->need_transcoding = false; + else + cstate->need_transcoding = true; + + /* See Multibyte encoding comment above */ + cstate->encoding_embeds_ascii = PG_ENCODING_IS_CLIENT_ONLY(cstate->file_encoding); + /* * For non-binary copy, we need to convert null_print to file encoding, * because it will be sent directly with CopySendString. @@@ -230,9 -191,7 +250,13 @@@ ListCell *cur; bool hdr_delim = false; ++<<<<<<< ours + Assert(cstate->opts.format != COPY_FORMAT_JSON); + + foreach(cur, cstate->attnumlist) ++======= + foreach(cur, cstate->base.attnumlist) ++>>>>>>> theirs { int attnum = lfirst_int(cur); char *colname; @@@ -243,23 -202,14 +267,27 @@@ colname = NameStr(TupleDescAttr(tupDesc, attnum - 1)->attname); ++<<<<<<< ours + if (cstate->opts.format == COPY_FORMAT_CSV) ++======= + if (cstate->base.opts.csv_mode) ++>>>>>>> theirs CopyAttributeOutCSV(cstate, colname, false); else CopyAttributeOutText(cstate, colname); } - CopySendTextLikeEndOfRow(cstate); + CopySendTextLikeEndOfRow(ccstate); } + + /* + * If FORCE_ARRAY has been specified, send the opening bracket. + */ + if (cstate->opts.format == COPY_FORMAT_JSON && cstate->opts.force_array) + { + CopySendChar(cstate, '['); + CopySendTextLikeEndOfRow(cstate); + } } /* @@@ -343,93 -293,11 +371,101 @@@ CopyToTextLikeEnd(CopyToState cstate /* Nothing to do here */ } ++<<<<<<< ours +/* Implementation of the end callback for json format */ +static void +CopyToJsonEnd(CopyToState cstate) +{ + if (cstate->opts.force_array) + { + CopySendChar(cstate, ']'); + CopySendTextLikeEndOfRow(cstate); + } +} + +/* Implementation of per-row callback for json format */ +static void +CopyToJsonOneRow(CopyToState cstate, TupleTableSlot *slot) +{ + Datum rowdata; + + resetStringInfo(cstate->json_buf); + + if (cstate->json_projvalues != NULL) + { + /* + * Column list case: project selected column values into sequential + * positions matching the custom TupleDesc, then form a new tuple. + */ + HeapTuple tup; + int i = 0; + + foreach_int(attnum, cstate->attnumlist) + { + cstate->json_projvalues[i] = slot->tts_values[attnum - 1]; + cstate->json_projnulls[i] = slot->tts_isnull[attnum - 1]; + i++; + } + + tup = heap_form_tuple(cstate->tupDesc, + cstate->json_projvalues, + cstate->json_projnulls); + + /* + * heap_form_tuple already stamps the datum-length, type-id, and + * type-mod fields on t_data, so we can use it directly as a composite + * Datum without the extra pallocmemcpy that heap_copy_tuple_as_datum + * would do. Any TOAST pointers in the projected values will be + * detoasted by the per-column output functions called from + * composite_to_json. + */ + rowdata = HeapTupleGetDatum(tup); + } + else + { + /* + * Full table or query without column list. For queries, the slot's + * TupleDesc may carry RECORDOID, which is not registered in the type + * cache and would cause composite_to_json's lookup_rowtype_tupdesc + * call to fail. Build a HeapTuple stamped with the blessed + * descriptor so the type can be looked up correctly. + */ + if (!cstate->rel && slot->tts_tupleDescriptor->tdtypeid == RECORDOID) + { + HeapTuple tup = heap_form_tuple(cstate->tupDesc, + slot->tts_values, + slot->tts_isnull); + + rowdata = HeapTupleGetDatum(tup); + } + else + rowdata = ExecFetchSlotHeapTupleDatum(slot); + } + + composite_to_json(rowdata, cstate->json_buf, false); + + if (cstate->opts.force_array) + { + if (cstate->json_row_delim_needed) + CopySendChar(cstate, ','); + else + { + /* first row needs no delimiter */ + CopySendChar(cstate, ' '); + cstate->json_row_delim_needed = true; + } + } + + CopySendData(cstate, cstate->json_buf->data, cstate->json_buf->len); + + CopySendTextLikeEndOfRow(cstate); ++======= + static Size + CopyToEstimateStateBinary(void) + { + /* Binary format doesn't require additional fields */ + return sizeof(CopyToStateData); ++>>>>>>> theirs } /* @@@ -523,25 -391,11 +559,25 @@@ SendCopyBegin(CopyToState cstate pq_beginmessage(&buf, PqMsg_CopyOutResponse); pq_sendbyte(&buf, format); /* overall format */ - pq_sendint16(&buf, natts); - for (i = 0; i < natts; i++) - pq_sendint16(&buf, format); /* per-column formats */ + if (cstate->opts.format != COPY_FORMAT_JSON) + { + pq_sendint16(&buf, natts); + for (i = 0; i < natts; i++) + pq_sendint16(&buf, format); /* per-column formats */ + } + else + { + /* + * For JSON format, report one text-format column. Each CopyData + * message contains one complete JSON object, not individual column + * values, so the per-column count is always 1. + */ + pq_sendint16(&buf, 1); + pq_sendint16(&buf, 0); + } + pq_endmessage(&buf); - cstate->copy_dest = COPY_FRONTEND; + cstate->copy_dest = COPY_DEST_FRONTEND; } static void @@@ -588,8 -442,7 +624,12 @@@ CopySendEndOfRow(CopyToState cstate switch (cstate->copy_dest) { ++<<<<<<< ours + case COPY_FILE: + pgstat_report_wait_start(WAIT_EVENT_COPY_TO_WRITE); ++======= + case COPY_DEST_FILE: ++>>>>>>> theirs if (fwrite(fe_msgbuf->data, fe_msgbuf->len, 1, cstate->copy_file) != 1 || ferror(cstate->copy_file)) @@@ -622,9 -475,8 +662,9 @@@ (errcode_for_file_access(), errmsg("could not write to COPY file: %m"))); } + pgstat_report_wait_end(); break; - case COPY_FRONTEND: + case COPY_DEST_FRONTEND: /* Dump the accumulated row as one CopyData message */ (void) pq_putmessage(PqMsg_CopyData, fe_msgbuf->data, fe_msgbuf->len); break; @@@ -855,9 -755,7 +943,13 @@@ BeginCopyTo(ParseState *pstate RelationGetRelationName(rel)))); } ++<<<<<<< ours + + /* Allocate workspace and zero all fields */ + cstate = palloc0_object(CopyToStateData); ++======= + cstate = create_copyto_state(pstate, options); ++>>>>>>> theirs /* * We allocate everything used by a cstate in a new memory context. This