From 95bf1258fb194c7e301902b6c05f957cc048af79 Mon Sep 17 00:00:00 2001
From: SungJun Jang <sjjang112233@gmail.com>
Date: Tue, 12 May 2026 10:54:27 +0900
Subject: [PATCH v2] Make EUC-KR encoding routines self-contained

Per KS X 2901 (formerly KS C 5861-1992), EUC-KR designates only G0
(ASCII) and G1 (KS X 1001).  G2 and G3 are not designated, so SS2
(0x8E) and SS3 (0x8F) cannot appear as lead bytes and no 3-byte
sequence is ever valid in EUC-KR.

pg_euckr_verifychar() already reflects this: it has no SS2/SS3 case.
But pg_euckr_mblen(), pg_euckr_dsplen(), and pg_euckr2wchar_with_len()
delegated to the shared pg_euc_* helpers, which include SS2/SS3
handling for encodings that designate G2/G3.  Replace these with
EUC-KR-specific implementations, change pg_wchar_table[PG_EUC_KR]
maxmblen from 3 to 2, and update Table 23.3 from "1-3" to "1-2".
Add a release note for the maxmblen change, which is the only
user-visible effect (via pg_encoding_max_length('EUC_KR')).

After this patch, EUC-KR's mb routines are structurally identical to
UHC's: IS_HIGHBIT_SET-only dispatch, 1-2 byte sequences, maxmblen=2.

No behavior change for valid EUC-KR data, since the verifier never
admits SS2 or SS3 as lead bytes.
---
 doc/src/sgml/charset.sgml    |  2 +-
 doc/src/sgml/release-19.sgml | 17 +++++++++++++++++
 src/common/wchar.c           | 36 ++++++++++++++++++++++++++++++++----
 3 files changed, 50 insertions(+), 5 deletions(-)

diff --git a/doc/src/sgml/charset.sgml b/doc/src/sgml/charset.sgml
index 746e40bb9d2..5edaf51b56c 100644
--- a/doc/src/sgml/charset.sgml
+++ b/doc/src/sgml/charset.sgml
@@ -1862,7 +1862,7 @@ ORDER BY c COLLATE ebcdic;
          <entry>Korean</entry>
          <entry>Yes</entry>
          <entry>Yes</entry>
-         <entry>1&ndash;3</entry>
+         <entry>1&ndash;2</entry>
          <entry></entry>
         </row>
         <row>
diff --git a/doc/src/sgml/release-19.sgml b/doc/src/sgml/release-19.sgml
index 96f6c4377c9..94d477d2441 100644
--- a/doc/src/sgml/release-19.sgml
+++ b/doc/src/sgml/release-19.sgml
@@ -283,6 +283,23 @@ Remove MULE_INTERNAL encoding (Thomas Munro)
 <para>
 This encoding was complex and rarely used.  Databases using it will need to be dumped and restored with a different encoding.
 </para>
+</listitem>
+
+<!--
+Author: SungJun Jang <sjjang112233@gmail.com>
+2026-05-12 [3b5ad36bb] Make EUC-KR encoding routines self-contained
+-->
+
+<listitem>
+<para>
+Correct <function>pg_encoding_max_length()</function> for <literal>EUC_KR</literal> from 3 to 2 (SungJun Jang)
+</para>
+
+<para>
+Per KS X 2901, EUC-KR designates only G0 (ASCII, 1 byte) and G1
+(KS X 1001, 2 bytes).  G2 and G3 are undefined in EUC-KR, so no
+3-byte sequence is valid and the previous value of 3 was incorrect.
+</para>
 </listitem>
 
    </itemizedlist>
diff --git a/src/common/wchar.c b/src/common/wchar.c
index 4c77e3e1dc8..3b8e1da862a 100644
--- a/src/common/wchar.c
+++ b/src/common/wchar.c
@@ -210,23 +210,51 @@ pg_eucjp_dsplen(const unsigned char *s)
 
 /*
  * EUC_KR
+ *
+ * Per KS X 2901 (formerly KS C 5861-1992), EUC-KR designates only G0
+ * (ASCII) and G1 (KS X 1001).  G2 and G3 are not designated, so the
+ * single-shift codes SS2 (0x8E) and SS3 (0x8F) never appear as lead
+ * bytes and no 3-byte sequence is ever valid.  These routines therefore
+ * implement EUC-KR directly rather than delegating to the shared
+ * pg_euc_* helpers, which include SS2/SS3 handling for encodings that
+ * designate G2/G3.
  */
 static int
 pg_euckr2wchar_with_len(const unsigned char *from, pg_wchar *to, int len)
 {
-	return pg_euc2wchar_with_len(from, to, len);
+	int			cnt = 0;
+
+	while (len > 0 && *from)
+	{
+		if (IS_HIGHBIT_SET(*from))	/* G1: KS X 1001, 2 bytes */
+		{
+			MB2CHAR_NEED_AT_LEAST(len, 2);
+			*to = *from++ << 8;
+			*to |= *from++;
+			len -= 2;
+		}
+		else					/* G0: ASCII */
+		{
+			*to = *from++;
+			len--;
+		}
+		to++;
+		cnt++;
+	}
+	*to = 0;
+	return cnt;
 }
 
 static int
 pg_euckr_mblen(const unsigned char *s)
 {
-	return pg_euc_mblen(s);
+	return IS_HIGHBIT_SET(*s) ? 2 : 1;
 }
 
 static int
 pg_euckr_dsplen(const unsigned char *s)
 {
-	return pg_euc_dsplen(s);
+	return IS_HIGHBIT_SET(*s) ? 2 : pg_ascii_dsplen(s);
 }
 
 /*
@@ -1865,7 +1893,7 @@ const pg_wchar_tbl pg_wchar_table[] = {
 	[PG_SQL_ASCII] = {pg_ascii2wchar_with_len, pg_wchar2single_with_len, pg_ascii_mblen, pg_ascii_dsplen, pg_ascii_verifychar, pg_ascii_verifystr, 1},
 	[PG_EUC_JP] = {pg_eucjp2wchar_with_len, pg_wchar2euc_with_len, pg_eucjp_mblen, pg_eucjp_dsplen, pg_eucjp_verifychar, pg_eucjp_verifystr, 3},
 	[PG_EUC_CN] = {pg_euccn2wchar_with_len, pg_wchar2euc_with_len, pg_euccn_mblen, pg_euccn_dsplen, pg_euccn_verifychar, pg_euccn_verifystr, 3},
-	[PG_EUC_KR] = {pg_euckr2wchar_with_len, pg_wchar2euc_with_len, pg_euckr_mblen, pg_euckr_dsplen, pg_euckr_verifychar, pg_euckr_verifystr, 3},
+	[PG_EUC_KR] = {pg_euckr2wchar_with_len, pg_wchar2euc_with_len, pg_euckr_mblen, pg_euckr_dsplen, pg_euckr_verifychar, pg_euckr_verifystr, 2},
 	[PG_EUC_TW] = {pg_euctw2wchar_with_len, pg_wchar2euc_with_len, pg_euctw_mblen, pg_euctw_dsplen, pg_euctw_verifychar, pg_euctw_verifystr, 4},
 	[PG_EUC_JIS_2004] = {pg_eucjp2wchar_with_len, pg_wchar2euc_with_len, pg_eucjp_mblen, pg_eucjp_dsplen, pg_eucjp_verifychar, pg_eucjp_verifystr, 3},
 	[PG_UTF8] = {pg_utf2wchar_with_len, pg_wchar2utf_with_len, pg_utf_mblen, pg_utf_dsplen, pg_utf8_verifychar, pg_utf8_verifystr, 4},
-- 
2.48.1.windows.1

