(KO) PostgreSQL 콜레이션 프로바이더 — libc, ICU, 그리고 내장 프로바이더
이론적 배경
섹션 제목: “이론적 배경”관계형 데이터베이스는 정렬 가능한 모든 타입의 값에 **전순서(total order)**를 부여해야 한다. ORDER BY, B-트리 인덱스, MIN/MAX, 병합 조인, GROUP BY가 모두 두 값을 받아 작다/같다/크다를 반환하는 비교 함수에 의존하기 때문이다. 숫자와 날짜는 순서가 내재되어 있다. 문자열은 그렇지 않다. 'Z' < 'a'인가? ASCII 기준으로는 그렇지만(대문자가 앞), 독일 전화번호부 정렬에서는 ä를 ae처럼 취급하고, 스웨덴 정렬에서는 ä가 z 뒤에 온다. 이 판단을 내리는 함수가 **콜레이션(collation)**이며, 이는 수학적 사실이 아니라 문화적 규약이다.
Database System Concepts(Silberschatz 외)는 SQL COLLATE 절을 설명하며 두 가지 핵심을 강조한다. 첫째, 콜레이션은 인코딩(encoding)과 독립적이다. UTF-8이나 LATIN1은 어떤 바이트 시퀀스가 유효한 문자인지를 결정하고, 콜레이션은 그 문자들을 어떻게 정렬할지를 결정한다. 둘째, 콜레이션은 순서뿐 아니라 **동등성(equality)**에도 영향을 미친다. 대소문자 무시 콜레이션은 'A' = 'a'를 참으로 처리해야 하며, 이는 유니크 인덱스, DISTINCT, 해시 조인 전반에 파급된다.
구현 공간을 정의하는 세 가지 설계 축이 있다.
-
규칙은 어디서 오는가? OS의 C 라이브러리(
strcoll(3))를 사용하거나, 전용 유니코드 라이브러리(ICU/CLDR)를 연결하거나, 순서 테이블을 바이너리에 내장할 수 있다. 각 선택은 커버리지(지원 로케일 수)와 안정성(OS 업그레이드 후에도 순서가 바뀌지 않는가) 사이의 트레이드오프다. -
비교는 결정론적인가? 유니코드 콜레이션 알고리즘(UCA, Unicode Technical Standard #10)은 다단계 비교를 정의한다. 1차 레벨은 대소문자와 악센트를 무시하고, 2차는 악센트를 반영하며, 3차는 대소문자를 반영한다. 선택한 레벨에서 두 문자열이 같다고 나왔을 때 최종 바이트 비교로 타이를 깨는 방식이 **결정론적(deterministic)**이고, 타이를 그대로 두는 방식이 **비결정론적(nondeterministic)**이다. 이 선택은
=의 의미, 유니크 제약, 축약 키(abbreviated keys) 건전성까지 바꾼다. -
순서는 어떻게 버전 관리되는가? 콜레이션 테이블은 데이터이고 변한다. CLDR 새 버전이 나오거나, 악명 높은 glibc 2.28이 많은 로케일 순서를 바꿨다. B-트리 아래의 규칙이 바뀌면 인덱스는 조용히 깨진다. 성숙한 엔진은 인덱스를 만들 때 사용한 콜레이션 버전을 기록하고, 라이브러리와 불일치할 때 경고를 낸다.
PostgreSQL의 답은 세 가지다. 축 1에 대해서는 프로바이더 추상화를 제공한다. 모든 콜레이션은 한 글자 collprovider 코드를 가지며, 얇은 디스패치 레이어가 세 백엔드 중 하나로 라우팅한다. 축 2는 콜레이션별 불리언 collisdeterministic으로 표현하며 ICU만 지원한다. 축 3은 pg_collation.collversion / pg_database.datcollversion에 버전 문자열을 저장하고 첫 사용 시 재확인한다.
DBMS 공통 설계
섹션 제목: “DBMS 공통 설계”문화적으로 올바른 문자열 정렬을 지원하는 모든 엔진은 어떤 라이브러리에 위임하든 동일한 공학적 패턴으로 수렴한다. 여기서 이를 먼저 정리해두면 PostgreSQL의 구체적인 심볼이 공통 공간 안의 선택으로 읽힌다.
비교자 위의 프로바이더/전략 간접 참조
섹션 제목: “비교자 위의 프로바이더/전략 간접 참조”진지한 엔진은 콜레이션 라이브러리를 하드코딩하지 않는다. 비교자는 함수 포인터 테이블, 가상 메서드, 또는 프로바이더 코드 기반 태그드 디스패치로 접근한다. ORDER BY나 인덱스 빌드 같은 호출 지점은 규칙이 libc에서 오든 ICU에서 오든 동일하게 동작한다. 간접 참조는 (콜레이션, 세션) 쌍당 한 번 해석되고 캐시된다. 로케일 핸들(newlocale(3), ucol_open())을 여는 비용이 크기 때문이다.
로케일 핸들과 해석된 비교자의 분리
섹션 제목: “로케일 핸들과 해석된 비교자의 분리”로케일 이름("en_US.utf8", "und-x-icu")은 카탈로그 데이터다. 로케일 핸들(locale_t, UCollator *)은 카탈로그에 저장할 수 없는 살아있는 OS/라이브러리 객체로, 백엔드마다 재구성해야 한다. 엔진은 둘을 엄격히 분리한다. 카탈로그 행이 콜레이션을 명명하고, 프로세스별 캐시가 필요할 때 핸들을 구체화한다.
한 번 변환, 여러 번 비교 — 변환 키
섹션 제목: “한 번 변환, 여러 번 비교 — 변환 키”strcoll 방식 비교는 호출마다 두 문자열을 재파싱하므로 비용이 크다. 표준 최적화는 변환(transform)(strxfrm(3), ucol_getSortKey())으로, 문자열을 한 번 바이트 블롭으로 바꾸고 이후 memcmp가 콜레이션 순서를 재현한다. 정렬과 축약 키 최적화가 변환을 활용하지만, 일부 라이브러리의 변환이 신뢰할 수 없는 경우가 있어 엔진은 능력 플래그 뒤에 이를 가두어 놓는다.
문화 순서에 붙이는 결정론적 타이 브레이크
섹션 제목: “문화 순서에 붙이는 결정론적 타이 브레이크”문화 순서는 전순서가 아니다. 수많은 바이트 시퀀스가 모든 UCA 레벨에서 타이를 이룬다. B-트리 키나 유니크 인덱스에서 타이는 받아들일 수 없다. 보편적인 해법은 라이브러리가 “같다”를 반환한 후 memcmp(이후 길이 비교)로 전순서를 강제하는 것이다. 이 타이 브레이크를 건너뛰는 콜레이션이 비결정론적이며, 바이트-동일-iff-같음을 가정하는 최적화(중복 제거, 축약 키, 해시 동등)에서 제외해야 한다.
인덱스 유효성을 위한 버전 스탬핑
섹션 제목: “인덱스 유효성을 위한 버전 스탬핑”콜레이션 테이블이 변하므로 엔진은 콜레이션 의존 온디스크 구조를 만든 라이브러리 버전으로 스탬핑하고, 불일치를 경고와 명시적인 “재빌드 + 버전 갱신” 워크플로로 노출한다. 이 없이는 OS 업그레이드가 모든 텍스트 인덱스를 진단 없이 조용히 깨뜨린다.
이론 ↔ PostgreSQL 대응
섹션 제목: “이론 ↔ PostgreSQL 대응”| 이론 / 패턴 | PostgreSQL 이름 |
|---|---|
| 프로바이더/전략 간접 참조 | collprovider 코드 + create_pg_locale() 디스패치 |
| 프로바이더 코드 | COLLPROVIDER_LIBC 'c', _ICU 'i', _BUILTIN 'b', _DEFAULT 'd' |
| 해석된 비교자 핸들 | pg_locale_t (struct pg_locale_struct) |
| 비교자 메서드 테이블 | struct collate_methods (strncoll, strnxfrm, …) |
| 로케일 이름 (카탈로그 데이터) | collcollate/collctype (libc), colllocale (ICU/builtin) |
| 세션별 핸들 캐시 | CollationCache + last_collation_cache_* |
| 변환 키 | pg_strnxfrm / strxfrm_is_safe 플래그 |
| 결정론적 타이 브레이크 | mylocale->deterministic 분기 in varstr_cmp |
| 버전 스탬핑 | collversion 컬럼 + get_collation_actual_version() |
| 생성 시 고정되는 데이터베이스 로케일 | datcollate/datctype/datlocale in pg_database |
PostgreSQL의 접근 방식
섹션 제목: “PostgreSQL의 접근 방식”PostgreSQL은 모든 로케일 민감 문자열 작업을 단일 불투명 핸들 pg_locale_t로 라우팅한다. 이 핸들은 콜레이션 OID로부터 얻으며, 첫 번째 필드가 프로바이더 코드다. 모든 공개 진입점(pg_strcoll, pg_strnxfrm, pg_strlower 등)은 그 코드로 분기해 pg_locale_libc.c, pg_locale_icu.c, pg_locale_builtin.c 중 하나의 구현으로 넘기는 얇은 디스패처다.
해석된 핸들: pg_locale_t
섹션 제목: “해석된 핸들: pg_locale_t”// struct pg_locale_struct — src/include/utils/pg_locale.hstruct pg_locale_struct{ char provider; bool deterministic; bool collate_is_c; bool ctype_is_c; bool is_default;
const struct collate_methods *collate; /* NULL if collate_is_c */
union { struct { const char *locale; bool casemap_full; } builtin; locale_t lt; /* libc */#ifdef USE_ICU struct { const char *locale; UCollator *ucol; } icu;#endif } info;};이 구조체는 **태그드 유니온(discriminated union)**이다. provider가 판별자고, info 유니온은 내장 로케일 문자열, libc locale_t, ICU UCollator * 중 정확히 하나를 담는다. collate_is_c와 ctype_is_c는 유니온 밖으로 꺼내져 있다. C/POSIX 경로는 memcmp와 ASCII 대소문자 처리로 적극 최적화되기 때문에, 프로바이더별 호출 없이 빠르게 검사해야 한다. deterministic 역시 수십 개의 호출 지점에서 정확성 분기에 쓰이므로 유니온 밖에 있다.
비교 메서드 자체도 포인터 테이블이므로, 한 프로바이더 안에서도 PostgreSQL은 구현을 교체할 수 있다(예: Windows UTF-8 변형):
// struct collate_methods — src/include/utils/pg_locale.hstruct collate_methods{ int (*strncoll) (const char *arg1, ssize_t len1, const char *arg2, ssize_t len2, pg_locale_t locale); /* required */ size_t (*strnxfrm) (char *dest, size_t destsize, const char *src, ssize_t srclen, pg_locale_t locale); /* required */ size_t (*strnxfrm_prefix) (char *dest, size_t destsize, const char *src, ssize_t srclen, pg_locale_t locale); /* optional */ bool strxfrm_is_safe;};프로바이더 디스패치
섹션 제목: “프로바이더 디스패치”콜레이션 OID는 create_pg_locale()에서 핸들이 된다. 함수는 pg_collation 행을 읽어 collprovider로 분기한 뒤 각 생성자를 호출한다:
// create_pg_locale — src/backend/utils/adt/pg_locale.ccollform = (Form_pg_collation) GETSTRUCT(tp);
if (collform->collprovider == COLLPROVIDER_BUILTIN) result = create_pg_locale_builtin(collid, context);else if (collform->collprovider == COLLPROVIDER_ICU) result = create_pg_locale_icu(collid, context);else if (collform->collprovider == COLLPROVIDER_LIBC) result = create_pg_locale_libc(collid, context);else PGLOCALE_SUPPORT_ERROR(collform->collprovider); /* shouldn't happen */프로바이더 코드는 단일 문자로, char 컬럼에 저장해 카탈로그를 콤팩트하게 유지한다:
// pg_collation.h — provider codes#define COLLPROVIDER_DEFAULT 'd'#define COLLPROVIDER_BUILTIN 'b'#define COLLPROVIDER_ICU 'i'#define COLLPROVIDER_LIBC 'c'COLLPROVIDER_DEFAULT('d')는 실제 콜레이션 행에 나타나지 않는다. “데이터베이스 기본값을 사용하라”는 센티넬로, 디스패처는 DEFAULT_COLLATION_OID로 처리한다. 구체적인 프로바이더는 libc, ICU, 내장 세 가지다.
모든 공개 문자열 연산은 같은 디스패치 패턴을 따른다. pg_strlower가 대표적이다:
// pg_strlower — src/backend/utils/adt/pg_locale.csize_tpg_strlower(char *dst, size_t dstsize, const char *src, ssize_t srclen, pg_locale_t locale){ if (locale->provider == COLLPROVIDER_BUILTIN) return strlower_builtin(dst, dstsize, src, srclen, locale);#ifdef USE_ICU else if (locale->provider == COLLPROVIDER_ICU) return strlower_icu(dst, dstsize, src, srclen, locale);#endif else if (locale->provider == COLLPROVIDER_LIBC) return strlower_libc(dst, dstsize, src, srclen, locale); else PGLOCALE_SUPPORT_ERROR(locale->provider);
return 0; /* keep compiler quiet */}#ifdef USE_ICU에 주목하자. ICU는 빌드 타임 옵션이다. ICU 없이 컴파일된 서버는 카탈로그의 'i' 코드를 이해하므로(pg_dump/복원이 충돌하지 않는다) 깨끗하게 오류를 낸다. 비교와 변환 진입점은 훨씬 단순하다. 프로바이더 스위치 없이 메서드 테이블을 바로 통과한다:
// pg_strncoll / pg_strnxfrm — src/backend/utils/adt/pg_locale.cintpg_strncoll(const char *arg1, ssize_t len1, const char *arg2, ssize_t len2, pg_locale_t locale){ return locale->collate->strncoll(arg1, len1, arg2, len2, locale);}
size_tpg_strnxfrm(char *dest, size_t destsize, const char *src, ssize_t srclen, pg_locale_t locale){ return locale->collate->strnxfrm(dest, destsize, src, srclen, locale);}정리하면: provider가 생성자를 선택하고, 생성자가 collate_methods 테이블을 설치하고, 비교와 변환은 테이블을 통과하고, 대소문자 매핑은 함수별 프로바이더 스위치를 통과한다.
flowchart TD
A["호출자: ORDER BY / 인덱스 빌드 / texteq<br/>콜레이션 OID 보유"] --> B["pg_newlocale_from_collation(collid)"]
B --> C{"collid?"}
C -->|"DEFAULT_COLLATION_OID"| D["default_locale 반환<br/>(init_database_collation에서 설정)"]
C -->|"C_COLLATION_OID"| E["&c_locale 반환<br/>(정적, provider=LIBC, collate_is_c)"]
C -->|"기타"| F{"CollationCache에 있는가?"}
F -->|"있음"| G["캐시된 pg_locale_t 반환"]
F -->|"없음"| H["create_pg_locale(collid)"]
H --> I{"collprovider"}
I -->|"'b' BUILTIN"| J["create_pg_locale_builtin"]
I -->|"'i' ICU"| K["create_pg_locale_icu"]
I -->|"'c' LIBC"| L["create_pg_locale_libc"]
J --> M["collate_methods + info.builtin 설치"]
K --> M2["collate_methods + info.icu.ucol 설치"]
L --> M3["collate_methods + info.lt 설치"]
M --> N["캐시 저장 + 버전 확인"]
M2 --> N
M3 --> N
N --> G
그림 1 — 콜레이션 OID에서 pg_locale_t로 해석하는 과정. DEFAULT_COLLATION_OID와 C_COLLATION_OID 두 빠른 경로는 카탈로그 접근 전에 단락된다. 나머지는 백엔드별 캐시를 거쳐, 캐시 미스 시 프로바이더 디스패치 생성자로 간다. 버전 확인(그림 4)은 생성자가 반환된 후 create_pg_locale 안에서 실행된다.
libc 프로바이더: CREATE DATABASE 시 고정되는 lc_collate / lc_ctype
섹션 제목: “libc 프로바이더: CREATE DATABASE 시 고정되는 lc_collate / lc_ctype”libc 프로바이더는 역사적 기본값으로, LC_COLLATE / LC_CTYPE 설정에 연결된다. pg_locale.c 파일 헤더 주석이 핵심 제약을 명시한다:
// pg_locale.c — file header comment/*---------- * Here is how the locale stuff is handled: LC_COLLATE and LC_CTYPE * are fixed at CREATE DATABASE time, stored in pg_database, and cannot * be changed. Thus, the effects of strcoll(), strxfrm(), isupper(), * toupper(), etc. are always in the same fixed locale. * ... *---------- */LC_COLLATE와 LC_CTYPE은 세션별로 SET할 수 있는 GUC가 아니다. 데이터베이스 생성 시 pg_database.datcollate / datctype에 구워진다. 변경 가능한 콜레이션은 모든 기존 B-트리 인덱스를 모호하게 만들기 때문이다. 반면 LC_MESSAGES, LC_MONETARY, LC_NUMERIC, LC_TIME은 런타임 GUC로, 같은 파일에서 check_locale_* / assign_locale_* 훅과 PGLC_localeconv / cache_locale_time 캐시로 처리된다. 이들은 형식에만 영향을 미치며, 정렬 순서와는 무관하다.
libc 생성자는 카탈로그에서 두 로케일 문자열을 꺼내 OS locale_t를 만든다:
// create_pg_locale_libc — src/backend/utils/adt/pg_locale_libc.cloc = make_libc_collator(collate, ctype);
result = MemoryContextAllocZero(context, sizeof(struct pg_locale_struct));result->provider = COLLPROVIDER_LIBC;result->deterministic = true; /* libc is always deterministic */result->collate_is_c = (strcmp(collate, "C") == 0) || (strcmp(collate, "POSIX") == 0);result->ctype_is_c = (strcmp(ctype, "C") == 0) || (strcmp(ctype, "POSIX") == 0);result->info.lt = loc;if (!result->collate_is_c){#ifdef WIN32 if (GetDatabaseEncoding() == PG_UTF8) result->collate = &collate_methods_libc_win32_utf8; else#endif result->collate = &collate_methods_libc;}두 가지가 눈에 띈다. 첫째, deterministic은 true로 하드와이어된다. libc에는 비결정론적 모드가 없다. 둘째, C/POSIX 로케일의 경우 make_libc_collator가 NULL을 반환해 result->collate가 NULL로 남는다. 엔진은 C 로케일에서 strcoll_l을 전혀 호출하지 않고 memcmp를 사용한다. 이는 더 빠르고 OS 콜레이션 드리프트에 면역이다. 내부적으로 libc 메서드는 스레드 안전한 로케일 매개변수 형식인 strncoll_l / strnxfrm_l을 호출한다.
ICU 프로바이더: BCP-47 언어 태그와 비결정론적 콜레이션
섹션 제목: “ICU 프로바이더: BCP-47 언어 태그와 비결정론적 콜레이션”ICU는 유니코드 CLDR 데이터 기반으로 구축된 현대적, OS 독립적 프로바이더다. 로케일 이름은 BCP-47 언어 태그(en-US, 루트 로케일의 경우 und)이며, ucol_open() 전에 정규화된다:
// icu_language_tag — src/backend/utils/adt/pg_locale.c (condensed)char *icu_language_tag(const char *loc_str, int elevel){#ifdef USE_ICU /* ... grow buffer in a loop ... */ uloc_toLanguageTag(loc_str, langtag, buflen, strict, &status); /* ... on U_FAILURE, ereport(elevel) or return NULL ... */ return langtag;#else ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("ICU is not supported in this build"))); return NULL;#endif}uloc_toLanguageTag가 “2단계 정규화”를 수행한다. POSIX 방식이나 .NET 방식 로케일 표기를 받아 일관된 언어 태그를 생성한다. 따라서 CREATE COLLATION ... (locale = 'en_US')와 'en-US-x-icu'는 동일한 ICU 콜레이터로 해석된다. 이후 icu_validate_locale()이 베스트 에포트로 언어 존재 여부를 확인하고, 마지막으로 ucol_open()이 UCollator를 생성한다.
ICU는 비결정론적 콜레이션을 지원하는 유일한 프로바이더다. 다른 바이트 시퀀스가 UCA 레벨에서 합법적으로 동일하게 취급될 수 있는 유일한 경우이기 때문이다. DefineCollation이 이를 강제한다:
// DefineCollation — src/backend/commands/collationcmds.c/* * Nondeterministic collations are currently only supported with ICU * because that's the only case where it can actually make a * difference. ... */if (!collisdeterministic && collprovider != COLLPROVIDER_ICU) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("nondeterministic collations not supported with this provider")));내장(builtin) 프로바이더: C, C.UTF-8, PG_UNICODE_FAST
섹션 제목: “내장(builtin) 프로바이더: C, C.UTF-8, PG_UNICODE_FAST”내장 프로바이더는 호스트 libc나 링크된 ICU에 의존하지 않는 빠르고 버전 안정적인 유니코드 옵션을 PostgreSQL에 제공하기 위해 추가되었다. pg_locale.c에서 검증되고 정규화되는 로케일 이름은 정확히 세 가지다:
// builtin_validate_locale — src/backend/utils/adt/pg_locale.cif (strcmp(locale, "C") == 0) canonical_name = "C";else if (strcmp(locale, "C.UTF-8") == 0 || strcmp(locale, "C.UTF8") == 0) canonical_name = "C.UTF-8";else if (strcmp(locale, "PG_UNICODE_FAST") == 0) canonical_name = "PG_UNICODE_FAST";
if (!canonical_name) ereport(ERROR, (errcode(ERRCODE_WRONG_OBJECT_TYPE), errmsg("invalid locale name \"%s\" for builtin provider", locale)));각 내장 로케일은 builtin_locale_encoding이 반환하는 인코딩 제약을 갖는다. "C"는 인코딩 무관(-1)이고, "C.UTF-8"과 "PG_UNICODE_FAST"는 UTF-8을 요구한다. 순서는 순수 코드 포인트 순서(memcmp)이므로 빠르고 PostgreSQL 릴리스 간에 변하지 않는다. 외부 라이브러리 드리프트가 없기 때문이다. PG_UNICODE_FAST는 C.UTF-8과 ctype 동작에서 다르다. 코드 포인트 정렬 순서는 동일하지만, 전체 유니코드 대소문자 매핑(레퍼토리 전반의 upper/lower/title)을 적용하는 반면 C.UTF-8은 ASCII 대소문자 매핑만 한다.
결정론적 vs. 비결정론적 동등 비교
섹션 제목: “결정론적 vs. 비결정론적 동등 비교”결정론이 중요한 이유는 이것이 콜레이션을 “단순한 정렬”에서 =의 의미를 바꾸는 무언가로 만들기 때문이다. 비교 핵심인 varstr_cmp는 프로바이더를 호출한 뒤, 콜레이션이 결정론적일 때만 타이를 깬다:
// varstr_cmp tie-break — src/backend/utils/adt/varlena.c (condensed)result = pg_strncoll(arg1, len1, arg2, len2, mylocale);
/* Break tie if necessary. */if (result == 0 && mylocale->deterministic){ result = memcmp(arg1, arg2, Min(len1, len2)); if ((result == 0) && (len1 != len2)) result = (len1 < len2) ? -1 : 1;}결정론적 콜레이션에서 두 문자열은 바이트가 동일할 때만 같다. 문화 비교가 순서를 결정하고, 남은 타이는 바이트로 풀리므로 =는 memcmp로 귀결된다. texteq는 이를 활용해 결정론적 로케일에서 strcoll을 아예 건너뛴다:
// texteq — src/backend/utils/adt/varlena.c (condensed)mylocale = pg_newlocale_from_collation(collid);
if (mylocale->deterministic){ /* ... equality is pure length check + memcmp, no strcoll ... */ if (len1 == len2 && memcmp(arg1, arg2, len1) == 0) /* equal */ ;}else{ /* must run the collation: 'café' may equal 'café' */}비결정론적 콜레이션에서는 타이 브레이크가 생략된다. 선택한 UCA 레벨에서 같다고 판정된 두 문자열은 바이트가 달라도 같다. 대소문자 무시 또는 악센트 무시 유니크 인덱스가 가능해지지만, 바이트-동일-iff-같음을 가정하는 최적화는 비활성화된다. PostgreSQL은 중복 제거를 거부하고, 축약 키가 안전하지 않으며, 일부 부분 문자열 연산은 오류를 낸다("nondeterministic collations are not supported for substring searches").
flowchart TD
A["텍스트 동등 / 비교<br/>varstr_cmp"] --> B{"locale->deterministic?"}
B -->|"true (libc, builtin,<br/>대부분의 ICU)"| C["프로바이더 비교 실행"]
C --> D{"동등 판정?"}
D -->|"아니오"| E["문화 순서 반환"]
D -->|"예 (타이)"| F["memcmp 타이 브레이크<br/>이후 길이 비교"]
F --> G["전순서:<br/>바이트 동일일 때만 같음"]
B -->|"false (ICU 전용)"| H["UCA 레벨에서<br/>프로바이더 비교 실행"]
H --> I["같음은 같음<br/>바이트 타이 브레이크 없음"]
I --> J["비활성화: 중복 제거,<br/>축약 키, 부분 문자열 검색"]
그림 2 — 결정론 분기. 결정론적 콜레이션(기본값, libc와 내장 모두 지원)은 memcmp 타이 브레이크를 붙여 동등성이 바이트 동일성으로 귀결되게 한다. B-트리 중복 제거와 축약 키가 건전하게 유지된다. 비결정론적 콜레이션(ICU 전용)은 문화 판정을 최종으로 받아들여, 악센트/대소문자 무시 유니크 제약을 가능하게 하되 그 최적화들을 희생한다.
변환 키와 안전성 플래그
섹션 제목: “변환 키와 안전성 플래그”정렬은 비교자를 O(n log n)번 호출하며 매번 두 문자열을 재파싱한다. 표준 최적화는 각 문자열을 한 번 변환해 memcmp가 콜레이션 순서를 재현하는 바이트 블롭으로 만드는 것이다. PostgreSQL은 이를 pg_strnxfrm으로 노출하지만, 모든 프로바이더의 변환이 모든 플랫폼에서 신뢰할 수 있는 것은 아니므로, 가정하지 않고 능력 플래그 뒤에 가둔다:
// pg_strxfrm_enabled — src/backend/utils/adt/pg_locale.cboolpg_strxfrm_enabled(pg_locale_t locale){ /* * locale->collate->strnxfrm is still a required method, even if it may * have the wrong behavior, because the planner uses it for estimates in * some cases. */ return locale->collate->strxfrm_is_safe;}주석의 미묘함이 중요하다. strnxfrm은 항상 존재한다. 플래너가 선택도 추정에 이를 호출하기 때문이다. 잘못된 결과는 계획 품질에만 영향을 미치고 정확성에는 영향을 주지 않는다. 그러나 실제 정렬에는 strxfrm_is_safe가 참일 때만 사용된다. 일부 glibc 버전이 strcoll과 일치하지 않는 strxfrm 결과를 냈던 적이 있어, 이 플래그로 해당 플랫폼에서는 직접 비교로 폴백한다. tuplesort의 축약 키 최적화(postgres-tuplesort.md 참조)가 이 변환 위에 구축된다.
콜레이션 버전 추적
섹션 제목: “콜레이션 버전 추적”콜레이션 규칙은 외부 데이터이며 변한다. 새 ICU/CLDR 릴리스나 악명 높은 glibc 2.28 재정렬이 기존 B-트리 인덱스의 순서를 조용히 바꿀 수 있다. PostgreSQL은 pg_collation.collversion(그리고 데이터베이스 기본값의 경우 pg_database.datcollversion)에 생성 시 프로바이더가 보고한 버전 문자열을 기록하고, 백엔드에서 콜레이션이 처음 해석될 때마다 재확인한다.
get_collation_actual_version은 현재 라이브 프로바이더가 보고하는 버전을 조회한다:
// get_collation_actual_version — src/backend/utils/adt/pg_locale.cchar *get_collation_actual_version(char collprovider, const char *collcollate){ char *collversion = NULL;
if (collprovider == COLLPROVIDER_BUILTIN) collversion = get_collation_actual_version_builtin(collcollate);#ifdef USE_ICU else if (collprovider == COLLPROVIDER_ICU) collversion = get_collation_actual_version_icu(collcollate);#endif else if (collprovider == COLLPROVIDER_LIBC) collversion = get_collation_actual_version_libc(collcollate);
return collversion;}create_pg_locale은 카탈로그 행에서 읽은 기록된 버전과 실제 버전을 비교하고, 불일치 시 정확한 해결책을 명시한 경고를 낸다:
// create_pg_locale — src/backend/utils/adt/pg_locale.c (condensed)collversionstr = TextDatumGetCString(datum); /* recorded */
actual_versionstr = get_collation_actual_version(collform->collprovider, ...);
if (strcmp(actual_versionstr, collversionstr) != 0) ereport(WARNING, (errmsg("collation \"%s\" has version mismatch", ...), errdetail("The collation in the database was created using version %s, " "but the operating system provides version %s.", collversionstr, actual_versionstr), errhint("Rebuild all objects affected by this collation and run " "ALTER COLLATION %s REFRESH VERSION, ...")));불일치는 경고이지 오류가 아니다. PostgreSQL은 재정렬이 인덱스의 어떤 행에 영향을 주는지 알 수 없으므로, 위험을 알리고 DBA가 결정하게 한다. 해결책은 두 단계다. 영향받은 모든 인덱스를 REINDEX하고(새 순서로 재빌드), ALTER COLLATION ... REFRESH VERSION으로 새 버전을 스탬핑해 경고가 멈추게 한다. AlterCollation은 실제 버전을 다시 읽어 collversion에 쓰는 방식으로 갱신을 구현한다:
// AlterCollation — src/backend/commands/collationcmds.c (condensed)newversion = get_collation_actual_version(collForm->collprovider, TextDatumGetCString(datum));
if ((!oldversion && newversion) || (oldversion && !newversion)) elog(ERROR, "invalid collation version change");else if (oldversion && newversion && strcmp(newversion, oldversion) != 0){ ereport(NOTICE, (errmsg("changing version from %s to %s", oldversion, newversion))); /* ... write newversion into Anum_pg_collation_collversion ... */}else ereport(NOTICE, (errmsg("version has not changed")));기본 콜레이션은 pg_database에서 버전을 가져오므로 ALTER COLLATION이 거부되고, 대신 ALTER DATABASE ... REFRESH COLLATION VERSION을 사용하라는 힌트가 표시된다. SQL 함수 pg_collation_actual_version(oid)는 사용자가 라이브 버전을 확인할 수 있도록 노출한다. DEFAULT_COLLATION_OID의 경우 pg_database에서, 나머지는 pg_collation에서 읽는다.
flowchart TD
A["CREATE COLLATION / CREATE DATABASE"] --> B["get_collation_actual_version()"]
B --> C["버전 문자열 저장:<br/>pg_collation.collversion /<br/>pg_database.datcollversion"]
C --> D["이 규칙으로 인덱스 빌드"]
D --> E["OS 업그레이드: glibc 2.28 /<br/>새 ICU CLDR 릴리스"]
E --> F["첫 사용: create_pg_locale()<br/>기록 vs 실제 비교"]
F --> G{"strcmp 불일치?"}
G -->|"아니오"| H["조용함 — 인덱스 신뢰"]
G -->|"예"| I["WARNING: 버전 불일치<br/>errhint: REINDEX + REFRESH"]
I --> J["DBA: 영향받은 인덱스 REINDEX"]
J --> K["ALTER COLLATION ... REFRESH VERSION<br/>실제 버전을 collversion에 기록"]
K --> H
그림 3 — 콜레이션 버전 라이프사이클. 생성 시 스탬핑된 버전이 계약이다. 라이브러리 업그레이드가 이를 깰 수 있다. 불일치는 경고로 노출되고(PostgreSQL은 어떤 행이 영향받는지 증명할 수 없다), 두 단계 REINDEX + REFRESH 워크플로가 계약을 복원한다.
CREATE COLLATION: 옵션 파싱과 프로바이더 선택
섹션 제목: “CREATE COLLATION: 옵션 파싱과 프로바이더 선택”DefineCollation은 SQL 구문을 카탈로그 데이터로 변환하는 곳이다. provider, locale / lc_collate / lc_ctype, deterministic, rules, version 옵션을 파싱하고 프로바이더별로 정규화한다. locale 축약은 프로바이더에 따라 다르게 확장된다. libc에서는 lc_collate와 lc_ctype 모두를 채우고, ICU/내장에서는 colllocale을 채운다:
// DefineCollation — src/backend/commands/collationcmds.c (condensed)if (localeEl){ if (collprovider == COLLPROVIDER_LIBC) { collcollate = defGetString(localeEl); collctype = defGetString(localeEl); } else colllocale = defGetString(localeEl);}프로바이더 기본값은 명시하지 않으면 libc다. ICU 전용 옵션(rules, 비결정론)은 다른 프로바이더에서 거부된다. ICU 로케일은 언어 태그로 정규화되고(바이너리 업그레이드는 원본 문자열 보존), 마지막으로 CollationCreate가 행을 기록한다. 생성 후 코드는 pg_newlocale_from_collation(newoid)을 한 번 호출해 스모크 테스트를 한다. 잘못 입력된 로케일이 첫 쿼리가 아닌 CREATE COLLATION 시점에 실패하도록 하기 위해서다.
시스템 콜레이션 임포트
섹션 제목: “시스템 콜레이션 임포트”새 데이터베이스는 모든 OS 로케일을 직접 열거하지 않는다. pg_import_system_collations()(initdb가 호출하고 SQL 함수로도 노출)가 처리한다. 비Windows에서는 locale -a를 실행하고 각 이름을 정규화한 뒤(.utf8 방식 인코딩 태그 제거 및 짧은 별칭 생성), 유효한 것마다 COLLPROVIDER_LIBC로 CollationCreate를 호출한다:
// create_collation_from_locale — src/backend/commands/collationcmds.c (condensed)collid = CollationCreate(locale, nspid, GetUserId(), COLLPROVIDER_LIBC, true, enc, locale, locale, NULL, NULL, get_collation_actual_version(COLLPROVIDER_LIBC, locale), true, true);ICU와 함께 빌드된 경우, 같은 함수가 uloc_getAvailable()을 순회해 <langtag>-x-icu 콜레이션을 생성한다(루트 로케일은 인덱스 -1에서 추가). 각 콜레이션은 실제 버전이 이미 스탬핑된 상태로 생성되므로, 새로 임포트된 콜레이션은 처음부터 버전 일관성을 가진다.
소스 워크스루
섹션 제목: “소스 워크스루”콜레이션 프로바이더 기계장치는 카탈로그(pg_collation, pg_database), 런타임 해석기(pg_locale.c + 세 프로바이더 파일), DDL 레이어(collationcmds.c)에 걸쳐 있다. 아래 심볼들은 호출 흐름으로 그룹화했다.
카탈로그 및 타입 정의
섹션 제목: “카탈로그 및 타입 정의”struct pg_locale_struct/pg_locale_t(pg_locale.h) — 해석된 핸들. 프로바이더 판별자,deterministic/collate_is_c/ctype_is_c플래그,collate메서드 포인터, 내장/libclocale_t/ICUUCollator에 대한info유니온.struct collate_methods(pg_locale.h) — 콜레이션별 메서드 테이블.strncoll(필수),strnxfrm(필수),strnxfrm_prefix(선택),strxfrm_is_safe플래그.COLLPROVIDER_DEFAULT/_BUILTIN/_ICU/_LIBC(pg_collation.h) — 단일 문자 프로바이더 코드'd'/'b'/'i'/'c'.Form_pg_collation필드collprovider,collisdeterministic,collencoding,collcollate,collctype,colllocale,collicurules,collversion— 해석기가 읽는 카탈로그 컬럼.Form_pg_database필드datlocprovider,datcollate,datctype,datlocale,datcollversion— CREATE DATABASE 시 고정되는 데이터베이스 기본 로케일.
핸들 해석 및 캐싱
섹션 제목: “핸들 해석 및 캐싱”pg_newlocale_from_collation(pg_locale.c) — 공개 진입점.DEFAULT_COLLATION_OID→default_locale,C_COLLATION_OID→&c_locale를 단락시키고 캐시를 조회.c_locale(pg_locale.c) — C/POSIX용 정적pg_locale_struct. 카탈로그 접근 없이 사용 가능.collation_cache_entry/CollationCache/last_collation_cache_oid/last_collation_cache_locale(pg_locale.c) — 백엔드별 simplehash 캐시와 동일 콜레이션 연속 요청을 위한 1-엔트리 빠른 경로.create_pg_locale(pg_locale.c) — 카탈로그 행 읽기,collprovider로 프로바이더 생성자 디스패치, 버전 확인 실행.init_database_collation(pg_locale.c) — 백엔드 시작 시pg_database에서default_locale빌드.datlocprovider로 디스패치.
프로바이더 생성자
섹션 제목: “프로바이더 생성자”create_pg_locale_libc/make_libc_collator(pg_locale_libc.c) —newlocale로locale_t빌드.deterministic = true와collate_is_c/ctype_is_c플래그 설정. C/POSIX의 경우collate를 NULL로 둠.create_pg_locale_icu(pg_locale_icu.c) —UCollator열기.deterministic = false를 설정할 수 있는 유일한 생성자.create_pg_locale_builtin(pg_locale_builtin.c) —C/C.UTF-8/PG_UNICODE_FAST용 컴파일 내장 유니코드 테이블 설치.
문자열 연산 (디스패치)
섹션 제목: “문자열 연산 (디스패치)”pg_strcoll/pg_strncoll(pg_locale.c) —locale->collate->strncoll을 통한 비교.pg_strnxfrm/pg_strxfrm/pg_strxfrm_enabled/pg_strnxfrm_prefix/pg_strxfrm_prefix_enabled(pg_locale.c) — 변환 키와 안전성/가용성 게이트.pg_strlower/pg_strtitle/pg_strupper/pg_strfold(pg_locale.c) — 대소문자 매핑. 각각 프로바이더 스위치(pg_strfold는 libc 프로바이더에서strlower_libc로 폴백).varstr_cmp/text_cmp/texteq(varlena.c) — 결정론 타이 브레이크와 결정론적 동등성 빠른 경로.
내장/ICU 로케일 검증
섹션 제목: “내장/ICU 로케일 검증”builtin_validate_locale/builtin_locale_encoding(pg_locale.c) — 세 가지 내장 로케일 이름 정규화 및 인코딩 확인.icu_language_tag/icu_validate_locale(pg_locale.c) — ICU 로케일의 BCP-47 정규화 및 베스트 에포트 검증.
버전 추적 및 DDL
섹션 제목: “버전 추적 및 DDL”get_collation_actual_version(pg_locale.c) — 라이브 프로바이더에 현재 버전 문자열 조회.DefineCollation(collationcmds.c) — CREATE COLLATION: 옵션 파싱, 프로바이더별 정규화, 행 기록, 로케일 스모크 테스트.AlterCollation(collationcmds.c) — ALTER COLLATION … REFRESH VERSION. 기본 콜레이션 거부.pg_collation_actual_version(collationcmds.c) — SQL로 노출된 라이브 버전.pg_database또는pg_collation에서 읽음.pg_import_system_collations/create_collation_from_locale/normalize_libc_locale_name(collationcmds.c) — OS 로케일 일괄 임포트(libc:locale -a, ICU:uloc_getAvailable).
위치 힌트 (2026-06-05, REL_18 273fe94 기준)
섹션 제목: “위치 힌트 (2026-06-05, REL_18 273fe94 기준)”| 심볼 | 파일 | 줄 |
|---|---|---|
struct collate_methods | src/include/utils/pg_locale.h | 73 |
struct pg_locale_struct | src/include/utils/pg_locale.h | 115 |
COLLPROVIDER_DEFAULT | src/include/catalog/pg_collation.h | 70 |
COLLPROVIDER_BUILTIN | src/include/catalog/pg_collation.h | 71 |
COLLPROVIDER_ICU | src/include/catalog/pg_collation.h | 72 |
COLLPROVIDER_LIBC | src/include/catalog/pg_collation.h | 73 |
c_locale | src/backend/utils/adt/pg_locale.c | 137 |
collation_cache_entry | src/backend/utils/adt/pg_locale.c | 146 |
last_collation_cache_oid | src/backend/utils/adt/pg_locale.c | 176 |
check_locale | src/backend/utils/adt/pg_locale.c | 301 |
create_pg_locale | src/backend/utils/adt/pg_locale.c | 1075 |
init_database_collation | src/backend/utils/adt/pg_locale.c | 1154 |
pg_newlocale_from_collation | src/backend/utils/adt/pg_locale.c | 1196 |
get_collation_actual_version | src/backend/utils/adt/pg_locale.c | 1254 |
pg_strlower | src/backend/utils/adt/pg_locale.c | 1270 |
pg_strcoll | src/backend/utils/adt/pg_locale.c | 1353 |
pg_strncoll | src/backend/utils/adt/pg_locale.c | 1373 |
pg_strxfrm_enabled | src/backend/utils/adt/pg_locale.c | 1387 |
pg_strnxfrm | src/backend/utils/adt/pg_locale.c | 1428 |
builtin_locale_encoding | src/backend/utils/adt/pg_locale.c | 1486 |
builtin_validate_locale | src/backend/utils/adt/pg_locale.c | 1510 |
icu_language_tag | src/backend/utils/adt/pg_locale.c | 1550 |
icu_validate_locale | src/backend/utils/adt/pg_locale.c | 1608 |
create_pg_locale_libc | src/backend/utils/adt/pg_locale_libc.c | 421 |
make_libc_collator | src/backend/utils/adt/pg_locale_libc.c | 488 |
varstr_cmp (타이 브레이크) | src/backend/utils/adt/varlena.c | 1698 |
texteq | src/backend/utils/adt/varlena.c | 1738 |
DefineCollation | src/backend/commands/collationcmds.c | 53 |
AlterCollation | src/backend/commands/collationcmds.c | 424 |
pg_collation_actual_version | src/backend/commands/collationcmds.c | 507 |
normalize_libc_locale_name | src/backend/commands/collationcmds.c | 596 |
create_collation_from_locale | src/backend/commands/collationcmds.c | 696 |
pg_import_system_collations | src/backend/commands/collationcmds.c | 836 |
소스 검증 (2026-06-05 기준)
섹션 제목: “소스 검증 (2026-06-05 기준)”검증된 사실
섹션 제목: “검증된 사실”-
프로바이더 코드는 네 가지, 구체적 프로바이더는 세 가지다.
pg_collation.h확인:COLLPROVIDER_DEFAULT'd',COLLPROVIDER_BUILTIN'b',COLLPROVIDER_ICU'i',COLLPROVIDER_LIBC'c'.'d'는 “데이터베이스 기본값 사용” 센티넬로, 프로바이더 생성자에 도달하지 않는다.create_pg_locale은 나머지 셋으로만 디스패치하고 나머지는PGLOCALE_SUPPORT_ERROR를 낸다. -
비결정론적 콜레이션은 ICU 전용이며 CREATE 시 강제된다.
collationcmds.c의DefineCollation확인:if (!collisdeterministic && collprovider != COLLPROVIDER_ICU) ereport(ERROR, ...). libc 생성자는result->deterministic = true를 하드와이어하고, 내장 생성자도 동일하다.create_pg_locale_icu만 비결정론적 핸들을 생성할 수 있다. -
C/POSIX 경로는
strcoll을 절대 호출하지 않으며 메서드 테이블을 갖지 않는다.pg_locale_libc.c의create_pg_locale_libc와make_libc_collator확인:"C"/"POSIX"의 경우make_libc_collator가NULL을 반환하고,collate_is_c가 참으로 설정되며,result->collate가 NULL로 남는다. 비교는memcmp로 폴백하므로 C 로케일은 OS 콜레이션 드리프트에 면역이다. -
콜레이션 버전 불일치는 오류가 아닌 경고다.
pg_locale.c의create_pg_locale확인:strcmp(actual, recorded) != 0분기는ereport(WARNING, ...)로REINDEX와ALTER COLLATION ... REFRESH VERSION을 명시한 errhint를 낸다. PostgreSQL은 어떤 행이 재정렬에 영향받는지 증명할 수 없으므로 접근을 막지 않고 위험을 알린다. -
LC_COLLATE와 LC_CTYPE은 CREATE DATABASE 시 고정되며 GUC가 아니다.
pg_locale.c파일 헤더 주석 및 libc 생성자가pg_database에서datcollate/datctype을 읽는 방식으로 확인. 같은 파일의 런타임 로케일 GUC(locale_messages/_monetary/_numeric/_time)는 형식(PGLC_localeconv,cache_locale_time)에만 영향을 미치며 정렬 순서와는 무관하다. -
strnxfrm은 항상 존재하지만strxfrm_is_safe일 때만 사용된다.pg_locale.c의pg_strxfrm_enabled와 주석 확인: 메서드 포인터는 플래너가 추정에 사용하므로 필수이지만, 실제 정렬은strxfrm_is_safe플래그를 먼저 확인한다. 일부 glibc 버전의 깨진strxfrm은 이 플래그로 비활성화된다. -
내장 프로바이더는 정확히 세 가지 로케일 이름을 받는다.
pg_locale.c의builtin_validate_locale확인:C,C.UTF-8(별칭C.UTF8),PG_UNICODE_FAST. 그 외는 오류.C는 인코딩 무관(-1), 나머지 둘은builtin_locale_encoding에 따라 UTF-8 필수. -
백엔드별 콜레이션 캐시는 1-엔트리 빠른 경로를 갖는다.
pg_locale.c의pg_newlocale_from_collation확인:last_collation_cache_oid/last_collation_cache_locale가 동일 콜레이션 연속 요청 시 simplehash 조회를 단락시킨다. 한 쿼리 안에서 동일 콜레이션이 반복 요청되는 일반적인 경우에 최적화된다.
미해결 질문
섹션 제목: “미해결 질문”-
PG_UNICODE_FAST가 런타임에서C.UTF-8과 정확히 어떻게 다른가? 두 UTF-8 내장 로케일 모두 코드 포인트 정렬 순서를 사용한다. 차이는 ctype/대소문자 매핑(builtininfo 유니온의casemap_full— 전체 유니코드 대소문자 매핑 vs. ASCII 전용)에 있지만, 정확한 테이블 소스와casemap_full으로 분기하는 대소문자 매핑 함수 목록은 여기서 추적하지 않았다. 조사 경로:pg_locale_builtin.c와strlower_builtin/strupper_builtin/strtitle_builtin/strfold_builtin구현, 그리고src/common/unicode/아래 생성된 유니코드 테이블. -
REL_18에서
strxfrm_is_safe가 실제로 false로 설정되는 경우는 무엇인가? 플래그는 존재하고 준수되지만, 어떤 프로바이더/플랫폼이 실제로 이를 해제하는지(현대 glibc에서 기본값이 그런지)는 여기서 읽은 생성자에서 확인하지 않았다. 조사 경로:pg_locale_libc.c/pg_locale_icu.c/pg_locale_builtin.c에서strxfrm_is_safe를 grep하고collate_methods테이블 초기화 코드를 추적.
PostgreSQL 너머 — 비교 설계와 연구 방향
섹션 제목: “PostgreSQL 너머 — 비교 설계와 연구 방향”-
유니코드 콜레이션 알고리즘(UTS #10)과 CLDR. PostgreSQL ICU 프로바이더가 의존하는 다단계 1차/2차/3차 비교는 Unicode Technical Standard #10에 명시되고, 로케일별 세부 조정은 CLDR에 있다. UCA 레벨을 PostgreSQL의 결정론적/비결정론적 구분 및
rules(조정) 옵션에 매핑하는 집중 노트를 작성하면 ICU 프로바이더 동작이 구체화될 것이다. -
glibc 2.28 콜레이션 파괴 사건. 2018년 glibc 릴리스는 대부분의 로케일 순서를 바꿔, 업그레이드된 호스트의 B-트리 인덱스를 조용히 깨뜨렸다. 이 사건은 PostgreSQL의 버전 추적 기계장치와 ICU 및 내장 프로바이더로의 전환을 가속시켰다. 해당 사건에 대한 짧은 케이스 스터디는
collversion메커니즘이 존재하는 이유를 구체적으로 보여줄 것이다. -
ICU 대 libc 안정성 트레이드오프. ICU는 콜레이션 동작을 링크된 라이브러리 버전에 고정해 호스트 OS와 독립적이지만, 자체 버전 관리 표면을 도입한다. SQL Server(Windows 콜레이션 + UCA), Oracle(
NLS_SORT/ 언어 콜레이션), MySQL(문자 집합별 콜레이션,utf8mb4_0900_ai_ci)은 각각 커버리지/안정성 곡선의 다른 지점을 선택한다. 나란히 비교하면 PostgreSQL의 세 프로바이더 설계가 무엇을 얻는지 명확해진다. -
인코딩 대 콜레이션 분리. 인코딩 측면(
pg_wchar, utils/mb, 클라이언트 변환,collencoding이 특정 데이터베이스에서 어떤 콜레이션을 사용할 수 있는지 제한하는 방식)은postgres-encoding.md에서 다룬다. 두 서브시스템은pg_get_encoding_from_locale과check_encoding_locale_matches에서 만난다. -
콜레이션이 소비되는 곳. 비교자와 변환 키는 정렬(
postgres-tuplesort.md, 축약 키), B-트리 정렬(postgres-nbtree.md, 비결정론적 콜레이션에서 비활성화되는 중복 제거), 텍스트 타입 함수(postgres-datatypes-adt.md)에서 사용된다. 파싱 타임 콜레이션 할당에서strncoll호출까지ORDER BY하나를 끝까지 추적하는 후속 문서를 작성할 수 있다.
원시 분석 자료
섹션 제목: “원시 분석 자료”- 없음. 이 문서는 REL_18 소스 트리에서 직접 합성되었다(
sources: []).
소스 코드 (REL_18, 커밋 273fe94)
섹션 제목: “소스 코드 (REL_18, 커밋 273fe94)”src/backend/utils/adt/pg_locale.c— 프로바이더 디스패치, 핸들 캐시, 버전 확인, 내장/ICU 검증, 런타임 로케일 GUC 훅.src/backend/commands/collationcmds.c— CREATE / ALTER COLLATION, 버전 갱신, 시스템 콜레이션 임포트.src/include/utils/pg_locale.h—pg_locale_t,collate_methods,pg_locale_struct, 공개 문자열 연산 프로토타입.src/include/catalog/pg_collation.h—COLLPROVIDER_*코드,pg_collation카탈로그 폼.src/include/catalog/pg_database.h—datlocprovider/datcollate/datctype/datlocale/datcollversion.src/backend/utils/adt/pg_locale_libc.c— libc 프로바이더 생성자와make_libc_collator.src/backend/utils/adt/varlena.c—varstr_cmp결정론 타이 브레이크,texteq결정론적 빠른 경로.
교재 참고문헌
섹션 제목: “교재 참고문헌”- Database System Concepts (Silberschatz, Korth, Sudarshan, 7판) — SQL
COLLATE절, 비교 및 정렬 규칙으로서의 콜레이션, 인코딩 대 콜레이션 구분.knowledge/research/dbms-general/database-system-concepts.md참조.
외부 표준
섹션 제목: “외부 표준”- Unicode Technical Standard #10 (유니코드 콜레이션 알고리즘)과 CLDR 데이터 집합 — ICU 프로바이더의 다단계 비교 및 조정 규칙의 기반.
교차 참조
섹션 제목: “교차 참조”postgres-encoding.md— 문자 인코딩과 인코딩/콜레이션 경계.postgres-datatypes-adt.md— 이 비교자를 소비하는text타입 함수.postgres-tuplesort.md— 변환 기계장치 위에 구축된 축약 키.postgres-nbtree.md— B-트리 정렬과 결정론과의 중복 제거 상호작용.