Kudu 는 생성 후 파티셔닝을 바꿀 수 없으므로 hash·range 를 고치려면 새 테이블을 만들어 옮겨야 한다. 이를 반복하기 위해 만든 세 도구 — 기존 DDL 을 읽어 파티션만 바꾼 신규 DDL 을 뽑는 generate_repartition_ddl.py, 명세서(txt) 또는 실제 테이블에서 Kudu + Parquet external DDL 을 만드는 make_ddl.sh, 기존 range 파티션을 새 레이아웃으로 옮기는 ch_layout.sh / ops_kudu.sh — 의 규칙과 알려진 결함을 정리한다.
입력은 스키마 테이블 만 적힌 목록 파일이고, impyla 또는 impala-shell(--conn-mode impyla|shell)로 SHOW CREATE TABLE 을 읽는다.
| 규칙 | 구현 |
|---|---|
| HASH 있음 | 컬럼 유지, 버킷 6 ↔ 3 토글. 그 외 값은 경고만 내고 유지 |
| HASH 없음 | RANGE 컬럼을 뺀 PK 앞에서 최대 2개, PARTITIONS 6. 1개만 남으면 1개 |
| RANGE 있음 | 컬럼 선언만 유지하고 경계는 SHOW RANGE PARTITIONS 로 재조회해 ALTER TABLE ... ADD RANGE PARTITION 으로 분리 |
| RANGE 없음 | RANGE 절을 붙이지 않음 |
| WHERE 조건 | RANGE 첫 컬럼(없으면 HASH 첫 컬럼)의 샘플값을 LIMIT 1 로 조회해 타입에 맞게 따옴표 처리 |
| COMMENT | PARTITION BY 다음, STORED AS KUDU 바로 위로 재배치 |
| 신규 테이블명 | --new-suffix (기본 _v2), --execute 로 실제 생성 |
| 출력 | result.txt(신규 DDL + ALTER + 조회조건), diff_result.txt(기존/신규 구분) |
알려진 취약점: TBLPROPERTIES 의 'kudu.table_name'='impala::schema.old_table' 을 그대로 복사하면 신규 생성이 실패하거나 기존 Kudu 테이블을 가리킨다. STRING range 경계가 따옴표 없이 나오는 경우가 있다. PRIMARY KEY (...) 별도 절만 인식하며 인라인 선언은 예외가 난다. --execute 에 동일명 존재 확인·IF NOT EXISTS·dry-run 이 없다.
테이블명을 주면 같은 폴더에 <테이블>.txt 명세서가 있으면 그것을 파싱(spec)하고, 없으면 SHOW CREATE TABLE 로 생성(live)한다. 두 경우 모두 Kudu 테이블과 <테이블>_ext(PARTITIONED BY (part_key STRING) STORED AS PARQUET LOCATION <base>/<테이블>_ext)를 만든다.
./make_ddl.sh <테이블> # 자동 판별
./make_ddl.sh -T list.txt # 목록 일괄
./make_ddl.sh -A # 폴더의 모든 .txt (spec)
# 옵션: -d DB -L external base -o 화면 출력 -F 구분자 -n 컬럼명 필드 -W 최대주차 -p HASH수 -k Kudu 타입 유지 -m spec|live
명세서 전처리에서 BOM 과 CR 을 제거하고(sed -e '1s/^\xEF\xBB\xBF//' -e 's/\r$//'), 타입은 INT8→TINYINT, INT32→INT, INT64→BIGINT, STRING/VARCHAR/BINARY→STRING 으로 매핑하며 DECIMAL 은 precision 38 상한으로 보정한다. 출력은 sql/<테이블>.sql 이다.
VALUES < ; 빈 문장 결함기존 테이블의 SHOW RANGE PARTITIONS 한 줄을 읽는 parse() 는 VALUE = 로 시작하면 단일값(V), 아니면 구간형(R)으로 분류한다. R 인데 그 줄에 <= VALUES 도 VALUES < 도 없으면 하한·상한이 모두 비고, 마지막 else 분기가 ALTER TABLE ... ADD RANGE PARTITION VALUES < ; 라는 빈 문장을 찍어 스크립트가 중단된다. 그 줄은 대개 Impala 가 출력하는 VALUES >= '202601'(하한만 있는 파티션)이거나 \r 만 남은 잡줄이다.
# parse() 안, hi[i] 처리 다음 줄에 추가
if(lo[i]=="" && index(s,"VALUES >=")>0){ lo[i]=substr(s,index(s,"VALUES >=")+9); gsub(/^ +| +$/,"",lo[i]) }
# 문장 생성부 교체
if(lo[0]=="" && hi[0]==""){ printf "SKIP\t%s\n", $0; next }
if(lo[0]!="" && hi[0]!="") printf "ALTER TABLE %s ADD IF NOT EXISTS RANGE PARTITION %s <= VALUES < %s;\n", NEWT, lo[0], hi[0]
else if(lo[0]!="") printf "ALTER TABLE %s ADD IF NOT EXISTS RANGE PARTITION %s <= VALUES;\n", NEWT, lo[0]
else printf "ALTER TABLE %s ADD IF NOT EXISTS RANGE PARTITION VALUES < %s;\n", NEWT, hi[0]
결과를 읽는 case 에 SKIP*) echo "[warn] 해석 못한 파티션 줄: ${line#SKIP }" >&2 ;; 를 넣어 무엇이 걸러졌는지 보이게 한다. 어떤 줄이 문제인지 보려면 printf '%s\n' "$oldparts" | cat -A >&2 를 넣는다.
PLANID range 에 202636MO 처럼 연도·주차·요일이 붙는 값을 만들도록 Remark 표기를 확장했다.
| Remark | 생성되는 값 |
|---|---|
202636, MO, TH |
'202636','202636MO','202636TH' |
202636, MX, 요일 |
'202636','202636_M','202636_P','202636_V' + MO~SU 7개 |
202636,202637, MON |
주차마다 '주차','주차MO' |
202636 |
예전과 동일 — 테이블명 코드로 _M/_P/_V |
요일 토큰은 MO/MON/월 어느 것이든 받아 2자로 통일하고, "적힌 것만 생성" 규칙이라 요일이 있으면 접미사를 유추하지 않는다. 구간형(~, 월단위/주단위/분기단위)을 처리하는 col_values() 는 손대지 않아 ACTUALDATE DATE RANGE 20260101~20270101,월단위 는 그대로 동작한다.