Received: from malur.postgresql.org ([217.196.149.56]) by arkaria.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.96) (envelope-from ) id 1x12wU-004x2m-0d for pgsql-hackers@arkaria.postgresql.org; Mon, 31 Aug 2026 14:24:58 +0000 Received: from localhost ([127.0.0.1] helo=malur.postgresql.org) by malur.postgresql.org with esmtp (Exim 4.96) (envelope-from ) id 1x12wT-000iQJ-0a for pgsql-hackers@arkaria.postgresql.org; Mon, 31 Aug 2026 14:24:57 +0000 Received: from magus.postgresql.org ([2a02:c0:301:0:ffff::29]) by malur.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.96) (envelope-from ) id 1x12wS-000iQ8-2i for pgsql-hackers@lists.postgresql.org; Mon, 31 Aug 2026 14:24:56 +0000 Received: from mail-lf1-x134.google.com ([2a00:1450:4864:20::134]) by magus.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 (Exim 4.98.2) (envelope-from ) id 1x12wQ-00000002Cux-0MiO for pgsql-hackers@postgresql.org; Mon, 31 Aug 2026 14:24:56 +0000 Received: by mail-lf1-x134.google.com with SMTP id 2adb3069b0e04-5aeb98460c6so4801655e87.2 for ; Mon, 31 Aug 2026 07:24:54 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1788186293; x=1788791093; darn=postgresql.org; h=in-reply-to:content-disposition:content-type:mime-version :references:message-id:subject:cc:to:from:date:from:to:cc:subject :date:message-id:reply-to:content-type; bh=452YSkJbaREg2T28lUynARIBugUSswi6jXCCsxryFJo=; b=U0PE0vBtbymii8kJt8Q04GREYtJVDvki1peLfjB3oZCo2kwQscHYN5m69SQs2eCjWf d6jh78+LpOo+smBUBh2fWMbx4Zs73FnojnG5sIBqDL75n7tmd46d8Tx3UYyaxa7Q537Q QT6PnarqzGrmBwrIr+sAcAD5MSjbQ29Iyc6TuXFTigXFnG6Kb99OFjbvMCqCPOYXb9oM XHbresnL7WFOG+Z7TldKKKSS2fXeymjK/9TgpMLNCQxUD1/Gb7O42HGHJpHCr6Kv+lL3 PLZrHEsV+8Wn/eZgO0T0s0ehx8XmZiwcmZmrU0w4EDUH9qCQUH9+oqmPVgIIUEdcg6c8 BFaQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1788186293; x=1788791093; h=in-reply-to:content-disposition:content-type:mime-version :references:message-id:subject:cc:to:from:date:x-gm-gg :x-gm-message-state:from:to:cc:subject:date:message-id:reply-to :content-type; bh=452YSkJbaREg2T28lUynARIBugUSswi6jXCCsxryFJo=; b=iCO945g7bgwqnbUyoz7BtbNcfKvQ6/xmqm0W5u6Zsi2SBTv8PptwWS6F5m3x54EsoA jsha+Lh02lJTRaqJ3mUHHgBw+fY4FdAt08J5Zli43ucHUc7SlwvROa3XwDnjo3CVTUWQ DW0AyGGBPw/p4a0EjARaN15HbT1AWI7v9YMmkcuS+qeWh8uUhni3z/R3yxdYfSCgCNpp 1261UO6krdKoYoAQL8zY5LIh6tlDcgeSFPzlHYdsG6SZwMKEB4obe3QQMPub/Pu1kGWy sWkpgK7E3e+cbEv36IopEWmH6e8NaHt3c06j7sAZNG52J8wEZ2SjUynlK7NO9Jak854T WzZQ== X-Forwarded-Encrypted: i=1; AKwUvBxBxQlCvuOcncdKUwjkAgEKuVSMtu6/ke95FH41QQSqw6SgW4I2MisFf1/2+rvLoH1X33a41Qjl7O4xeAk5@postgresql.org X-Gm-Message-State: AFuF++nkf4irk+eltxFQOzUEzBupXJphFaboNiDk8exM1rPj7UQVynUE QIaG8oq1uxUMVPFojPxTUSwFubVXORv8tHYN2n+z+/OnNO/FEA7eDNS/ X-Gm-Gg: AYBFou33WCvbSI1vu635MeQHkjYIQm+spEoBwI2vuwexKw2vVaeRYXOy1Ct5fSjcRkP q0v8f0btIKvH3/JR04eBqcKoGZLj8d4FxisnuO7hGk34RWJz1OWENelBiriRu9soKzGV2tq8+em EcL3/JJjkzxa2zJxmJp2zRE+1qJYPAtStAhHNrzEp6zMiQk3iQzeqByMQ7I/H3hify4cDhiEw7D 1GyTdD9q9nKjR0vkVVwW6ZPHK5Fo+cgDSttt+Htk/4AoUBADtx1rJq7pFZoeiRM3HMHfnj5S9nw X48GYtQ1ByH6H5Di27h3eanUE2jmLdA+IpBbc/8jauuHfg64DbBITl7+kFEs2+jR+yGyqRgwHTB cOxPAlB4irEJ1CBwRgtOYWYtIfwPzjY2G/9Hi73SJSZOi1i801A79TWZqI0KwyqYVplrtLK1gTD LEg/NjrnEVoWhPW7ArTilz4DE33drDnW+DD0w6Fm0RwuIqEJCXzcGlBVJnWAzIqRtcBdtiNSYyt drtdABIDHo8vEKNut2nkKnzlvlN1tKlGSSES1yegkTeUkMA+df9DKApNmtcVJO8PBz5enf/of9r 2nTC1VhC55xf2sbRPH4W9n0yAg== X-Received: by 2002:ac2:568d:0:b0:5b5:e8f1:ee86 with SMTP id 2adb3069b0e04-5b5e8f1f2acmr7393766e87.2.1788186293178; Mon, 31 Aug 2026 07:24:53 -0700 (PDT) Received: from nathan (162-195-168-172.lightspeed.stlsmo.sbcglobal.net. [162.195.168.172]) by smtp.gmail.com with ESMTPSA id 2adb3069b0e04-5b5e8a11587sm2265856e87.66.2026.08.31.07.24.49 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 31 Aug 2026 07:24:51 -0700 (PDT) Date: Mon, 31 Aug 2026 09:24:47 -0500 From: Nathan Bossart To: Greg Burd Cc: Nazir Bilal Yavuz , Manni Wood , KAZAR Ayoub , Neil Conway , Andrew Dunstan , Shinya Kato , pgsql-hackers Subject: Re: Speed up COPY FROM text/CSV parsing using SIMD Message-ID: References: <43de48dc-701b-4735-881b-50bca6870f39@app.fastmail.com> MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="2cxon9lkp5TU8SCV" Content-Disposition: inline In-Reply-To: List-Id: List-Help: List-Subscribe: List-Post: List-Owner: List-Archive: Archived-At: Precedence: bulk --2cxon9lkp5TU8SCV Content-Type: text/plain; charset=us-ascii Content-Disposition: inline Claude found this bug: CREATE TABLE t (a TEXT); COPY t FROM PROGRAM $$printf 'abcdefghijklmnopqrst\n\\.\nxxxxxxxxxxxx\200'$$; fails with ERROR: invalid byte sequence for encoding "UTF8": 0x80 CONTEXT: COPY t, line 2 even though COPY should ignore everything after the \. The best fix I could find involves teaching CopyLoadInputBuf() to avoid erroring in the read-ahead path. AFAICT that doesn't meaningfully change the performance characteristics. Patch attached. -- nathan --2cxon9lkp5TU8SCV Content-Type: text/plain; charset=us-ascii Content-Disposition: attachment; filename=v1-0001-Make-COPY-FROM-s-SIMD-read-ahead-speculative.patch From d9a9466a2f986c24d996110ffd691c5965509e8b Mon Sep 17 00:00:00 2001 From: Nathan Bossart Date: Mon, 31 Aug 2026 09:08:23 -0500 Subject: [PATCH v1 1/1] Make COPY FROM's SIMD read-ahead speculative. Commit e0a3a3fd53 added a SIMD scan for COPY FROM (FORMAT {text,csv}) that refills the input buffer whenever fewer than sizeof(Vector8) bytes remain, rather than when it has actually run out. Presently, that read-ahead calls CopyLoadInputBuf() where the scalar loop would not, and CopyLoadInputBuf() is where a deferred encoding error is reported. So a file whose data ends with \. followed by bytes that are invalid in the encoding, a case CopyConvertBuf() goes out of its way to tolerate, now fails. Whether it fails depends on how much never-examined padding sits between the marker and the invalid byte: with 16-byte vectors, 12 bytes or fewer and the COPY errors out, 13 or more and it succeeds. To fix, teach CopyLoadInputBuf() to tell a speculative load, made on the chance that the caller will want the data, from one the caller needs in order to make progress. A speculative load leaves a pending encoding error pending, so long as the caller still has bytes to chew on. The SIMD path then hands those bytes to the scalar code, which asks only for what it needs and so never reads past the marker. Input that is really read is unaffected, except that its errors are once again reported against the line holding the bad byte, as they were before e0a3a3fd53. The obvious fix, declining to read ahead at all, is worse than it looks. The SIMD helper runs once per line, so refusing to refill hands the entire remainder of any line that straddles a buffer boundary to the scalar loop, and COPY of megabyte-wide lines slowed by about 80% in my testing. Consuming the sub-vector tail inside the helper instead cost the compiler its unrolling of the vector loop, which was worse again. Leaving that loop untouched, as this patch does, measures within noise of unpatched on every line width I tried. Oversight in commit e0a3a3fd53. Discussion: https://postgr.es/m/CAOzEurSW8cNr6TPKsjrstnPfhf4QyQqB4tnPXGGe8N4e_v7Jig%40mail.gmail.com Backpatch-through: 19 --- src/backend/commands/copyfromparse.c | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/src/backend/commands/copyfromparse.c b/src/backend/commands/copyfromparse.c index 37750cca13a..dc02838bc38 100644 --- a/src/backend/commands/copyfromparse.c +++ b/src/backend/commands/copyfromparse.c @@ -167,7 +167,7 @@ static int CopyGetData(CopyFromState cstate, void *databuf, int minread, int maxread); static inline bool CopyGetInt32(CopyFromState cstate, int32 *val); static inline bool CopyGetInt16(CopyFromState cstate, int16 *val); -static void CopyLoadInputBuf(CopyFromState cstate); +static void CopyLoadInputBuf(CopyFromState cstate, bool speculative); static int CopyReadBinaryData(CopyFromState cstate, char *dest, int nbytes); void @@ -651,9 +651,15 @@ CopyLoadRawBuf(CopyFromState cstate) * * If INPUT_BUF_BYTES(cstate) > 0, the unprocessed bytes are moved to the start * of the buffer and then we load more data after that. + * + * A speculative load is one made on the chance that the caller will want the + * data, not because it needs it yet. Such a load leaves a pending encoding + * error pending, so long as the caller still has something to chew on, since + * the input may never be read that far. NB: a speculative caller must cope + * with getting no additional data. */ static void -CopyLoadInputBuf(CopyFromState cstate) +CopyLoadInputBuf(CopyFromState cstate, bool speculative) { int nbytes = INPUT_BUF_BYTES(cstate); @@ -684,7 +690,11 @@ CopyLoadInputBuf(CopyFromState cstate) * conversion error. */ if (cstate->input_reached_error) + { + if (speculative && INPUT_BUF_BYTES(cstate) > 0) + return; CopyConversionError(cstate); + } /* no more input, and everything has been converted */ if (cstate->input_reached_eof) @@ -1381,7 +1391,7 @@ CopyReadLineTextSIMDHelper(CopyFromState cstate, bool is_csv, { REFILL_LINEBUF; - CopyLoadInputBuf(cstate); + CopyLoadInputBuf(cstate, true); /* update our local variables */ *hit_eof_p = cstate->input_reached_eof; input_buf_ptr = cstate->input_buf_index; @@ -1566,7 +1576,7 @@ CopyReadLineText(CopyFromState cstate, bool is_csv) { REFILL_LINEBUF; - CopyLoadInputBuf(cstate); + CopyLoadInputBuf(cstate, false); /* update our local variables */ hit_eof = cstate->input_reached_eof; input_buf_ptr = cstate->input_buf_index; -- 2.55.0 --2cxon9lkp5TU8SCV--