오라클 Pro*C 프로그램을 컴파일하다 보면, 일부 분석함수(Analytic function)들을 제대로 인식하지 못해서 에러가 나는 경우가 있습니다.
분명히 SQL*Plus 상에서는 제대로 수행되는 SQL 인데, Pro*C 에서 Embeded SQL 형태로 사용하면 PCC-S-02201 에러가 발생합니다.
$ make -f multirows.mk
proc multirows INCLUDE=include/ include=/u02/app/oracle/product/19.12.0/dbhome_1/precomp/public/ include=/u02/app/oracle/product/19.12.0/dbhome_1/rdbms/demo/ include=/u02/app/oracle/product/19.12.0/dbhome_1/rdbms/public/ include=/u02/app/oracle/product/19.12.0/dbhome_1/network/public/ PARSE=NONE RELEASE_CURSOR=YES MODE=ANSI
Pro*C/C++: Release 19.0.0.0.0 - Production on Sat Jan 21 13:23:57 2023
Version 19.12.0.0.0
Copyright (c) 1982, 2019, Oracle and/or its affiliates. All rights reserved.
System default option values taken from: /u02/app/oracle/product/19.12.0/dbhome_1/precomp/admin/pcscfg.cfg
Syntax error at line 16, column 52, file multirows.pc:
Error at line 16, column 52 in file multirows.pc
select job, sum(sal) as ret2, sum(sal) over() as ret3
...................................................1
PCC-S-02201, Encountered the symbol "(" when expecting one of the following:
, into, from,
Error at line 0, column 0 in file multirows.pc
PCC-F-02102, Fatal error while doing C preprocessing
make: *** [multirows.mk:23: multirows.c] Error 1
소스상에서 에러가 발생하는 위치를 보면 Embeded SQL 에서 over() 구문을 사용한 위치에서 발생합니다.
이 부분을 제거하면 에러가 발생하지 않습니다.
이 경우는 Pro*C 컴파일러가 over() 와 같은 분석함수를 제대로 인식하지 못해서 발생한 것입니다.
여기에는 2가지 해결방법이 있습니다.
1) common_parser=yes 옵션 사용
2) Dynamic SQL 사용
아래와 같이 make file 에서 proc 커맨드라인 옵션상에 common_parser=yes 옵션을 추가하면 에러가 발생하지 않습니다.
common_parser 옵션은 SQL 표준규약인 SQL:1999 Syntax 를 처리할 수 있도록 하는 옵션입니다. 이게 왜 디폴트가 no 인지 모르겠는데, 아무튼 이걸 yes 로 해서 컴파일하면 에러가 없어지고 실행도 잘 됩니다.
이렇게 했는데도 에러가 나는 경우가 있다면, 그건 버그라고 보고 SR을 생성해서 패치를 만들어야 합니다.
비슷한 케이스로 아래 Oracle MOS 문서가 있습니다.
Precompiler Parsing SQL or PL/SQL Syntax Fails With PCC-02201 or PCB-00400 (Doc ID 230115.1)
하지만, SR 을 생성해서 패치를 만드는 과정은 시간이 많이 걸리기 때문에 가장 손쉽게 이런 에러를 피해갈 수 있는 방법은 Dynamic SQL 을 사용하는 것입니다.
Dynamic SQL 을 사용하면 Pro*C Precompiler 는 컴파일 단계에서 SQL Syntax 체크를 하지 않고, SQL 문장이 들어있는 문자열을 DBMS 에 통째로 있는 그대로 전달하기 때문에 SQL*Plus 상에서 실행하는 것과 똑같이 실행됩니다.