치환 변수 전량 이식 — 그리고 이 파일의 주석이 틀렸었다
25개였던 카탈로그를 51개로 늘렸다. 그 과정에서 이 파일이 근거로 삼던 규약 자체가
틀렸다는 것이 드러나 함께 고쳤다.
■ 엔진을 잘못 지목하고 있었다
주석은 bzDesignSheetLoader.ConvertQuery 의
sReserved.Substring(0, sReserved.LastIndexOf("."))
를 근거로 "DataRow 접근형(...PatInfDR.item("컬럼"))은 접두어를 깨뜨려 반드시 실패한다"고
단정하고 그런 변수를 목록에서 뺐다. 테스트도 그 규칙으로 검사하고 있었다.
실제 엔진은 clsMDataTable.ConvertQuery 다
(C:\MsystechHIS_Ver.2\[003]EMR\[002]UserControl\MDataTable\clsMDataTable.vb).
ucLoadSheetBase 가 이것을 부르고 레거시 쿼리 편집기(fmMDataTable)도 같은 것을 쓴다.
그리고 그 파일에서 LastIndexOf(".") 줄은 주석 처리되어 있다(:32). StartsWith(FullName) 로 대체됐고,
클래스명 뒤 경로는 정규식으로 해석한다(:113):
^([a-zA-Z_]\w*)(?:\.item\("([^"]+)"\)|\("([^"]+)"\))?$
즉 DataRow 컬럼 접근이 두 형태로 <b>동작한다</b>. 단 item 은 반드시 소문자다 —
정규식에 IgnoreCase 가 없다. 대문자로 쓰면 값을 못 찾고 빈 문자열로 치환된다.
■ 추가한 것
환자 16 + 외부연계 11 + 서식 13 + 작업자 3 = 스칼라 43종(레거시 세 클래스의 스칼라 전량),
여기에 DataRow 컬럼 채우기 틀 8종(P_PatInf·P_ComInf·P_CodInf·P_CoiInf·P_CowInf·E_ShtMst·E_SdgMst).
컬럼 틀은 삽입하면 '컬럼명' 자리가 선택돼 바로 덮어쓸 수 있다.
다른 biz 객체·컬렉션을 돌려주는 속성(PatientInfoBiz_Refer, SaveSheetInfo, PrintEmrKeyList,
UidMst/HspMst/DepMst, SctMst)은 넣지 않았다. 정규식이 한 단계 경로만 허용해 중첩 접근이 안 되고,
단독으로 쓰면 타입 이름 문자열이 SQL 에 박힌다.
■ 판정을 목록 대조에서 형태 검사로 바꿨다
DataRow 컬럼은 무한히 많아 목록에 담을 수 없다. LegacyQueryVariableCatalog.IsResolvable 이
접두어 StartsWith + 위 정규식으로 판정하고, 편집기 경고도 이것을 쓴다.
목록에 없다고 경고하던 종전 방식이었다면 정상적인 DataRow 사용이 전부 오탐이 됐을 것이다.
■ 토크나이저 결함 — 따옴표 안의 변수를 삼키고 있었다
테스트를 쓰다 발견했다. 엔진은 값에 따옴표를 붙여 주지 않고 Replace 로 원문을 박으므로,
문자열 비교에 쓰려면 SQL 쪽에서 '<<...>>' 로 감싸는 것이 정상 사용법이다.
그런데 토크나이저가 '...' 를 통째로 문자열로 잡아 그 안의 변수를 못 봤다 —
가장 흔한 형태의 변수가 색도 검증도 못 받고 있었다. 문자열 구간 안에서도 <<...>> 를 떼어 내도록 고쳤다.
전 구간 덮기 불변식은 유지된다(테스트로 고정).
단위 테스트 22건 추가/수정(카탈로그 자기일관성·DataRow 2형태·item 대문자 거부·축약 접두어 거부·
중첩 경로 거부·따옴표 안 변수·덮기 불변식). 옛 규칙을 박아 둔 기존 테스트 1건은 실제 규약으로 교체.
회귀: 테스트 196/196, 편집 스모크 실패 0,
DB 왕복 1,271건 diff 0/예외 0, 종이 렌더 P062 바이트 동일.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
de46f70035
commit
864f11b0e4
@@ -139,7 +139,11 @@ public static class SqlTokenizer
|
||||
continue;
|
||||
}
|
||||
|
||||
// 문자열 — Oracle 은 '' 로 작은따옴표를 이스케이프한다
|
||||
// 문자열 — Oracle 은 '' 로 작은따옴표를 이스케이프한다.
|
||||
//
|
||||
// 안에 든 치환 변수는 따로 떼어 낸다. 엔진이 값에 따옴표를 붙여 주지 않으므로
|
||||
// 문자열 비교에 쓰려면 '<<...>>' 처럼 감싸는 것이 정상 사용법이다 —
|
||||
// 통째로 문자열로 칠해 버리면 가장 흔한 형태의 변수가 색도 검증도 못 받는다.
|
||||
if (ch == '\'')
|
||||
{
|
||||
var j = i + 1;
|
||||
@@ -165,7 +169,7 @@ public static class SqlTokenizer
|
||||
j++;
|
||||
}
|
||||
FlushPlain(i);
|
||||
tokens.Add(new SqlToken(i, j - i, closed ? SqlTokenKind.Text : SqlTokenKind.Broken));
|
||||
AddStringRun(tokens, text, i, j, closed);
|
||||
i = j;
|
||||
plainStart = i;
|
||||
continue;
|
||||
@@ -213,6 +217,39 @@ public static class SqlTokenizer
|
||||
return tokens;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 문자열 구간 [start, end) 을 조각으로 넣는다 — 안에 든 <c><<...>></c> 는 변수로 떼어 낸다.
|
||||
/// 닫히지 않은 문자열이면 변수가 아닌 부분을 Broken 으로 표시한다.
|
||||
/// </summary>
|
||||
private static void AddStringRun(List<SqlToken> tokens, string text, int start, int end, bool closed)
|
||||
{
|
||||
var textKind = closed ? SqlTokenKind.Text : SqlTokenKind.Broken;
|
||||
var cursor = start;
|
||||
while (cursor < end)
|
||||
{
|
||||
var open = text.IndexOf("<<", cursor, StringComparison.Ordinal);
|
||||
if (open < 0 || open >= end)
|
||||
{
|
||||
break;
|
||||
}
|
||||
var close = text.IndexOf(">>", open + 2, StringComparison.Ordinal);
|
||||
if (close < 0 || close + 2 > end)
|
||||
{
|
||||
break;
|
||||
}
|
||||
if (open > cursor)
|
||||
{
|
||||
tokens.Add(new SqlToken(cursor, open - cursor, textKind));
|
||||
}
|
||||
tokens.Add(new SqlToken(open, close + 2 - open, SqlTokenKind.Variable));
|
||||
cursor = close + 2;
|
||||
}
|
||||
if (cursor < end)
|
||||
{
|
||||
tokens.Add(new SqlToken(cursor, end - cursor, textKind));
|
||||
}
|
||||
}
|
||||
|
||||
private static bool IsWordStart(char ch) => char.IsLetter(ch) || ch == '_';
|
||||
|
||||
private static bool IsWordChar(char ch) => char.IsLetterOrDigit(ch) || ch == '_' || ch == '$' || ch == '#';
|
||||
|
||||
Reference in New Issue
Block a user