Pro*C 作為 Oracle 嵌入式 SQL 的經典實現,在存量系統維護中仍佔重要地位。本文匯總環境變量、類型映射、數組 FETCH/INSERT、動態 SQL、預編譯選項等工程級知識點。
ORACLE_SID=ORA8
NLS_LANG=American_America.ZHS16CGB231280 # 字符集關鍵
ORA_NLS33=$ORACLE_HOME/ocommon/nls/admin/data
ORACLE_HOME=/home/oracle
LD_LIBRARY_PATH=$ORACLE_HOME/lib:/usr/lib:.
SHLIB_PATH=$ORACLE_HOME/lib
NLS_DATE_FORMAT='YYYY/MM/DD'
字符集不匹配 是亂碼首因,客戶端 NLS_LANG 必須與數據庫字符集兼容。
| 差異點 | Informix | Oracle Pro*C |
|---|---|---|
| 字符串界定符 | 雙引號 " | 單引號 ' (雙引號報錯) |
| 長字符串截斷 | 自動截斷 | 報錯 ORA-12899 |
| VARCHAR 等價聲明 | 無需 | EXEC SQL VAR name IS STRING(n); |
| 宏定義 | EXEC SQL DEFINE | 用 EXEC ORACLE DEFINE 或 C 常量 |
| 選項 | 推薦值 | 說明 | |
|---|---|---|---|
MODE | ANSI | 標準合規,游標語義嚴格 | |
CHAR_MAP | STRING | char[n] 自動視為以 \0 結尾,免去 VARCHAR 等價聲明 | |
RELEASE_CURSOR | YES | 提交/回滾後釋放游標,防洩漏 | |
HOLD_CURSOR | NO | 不長期持有游標 | |
MAXOPENCURSORS | 50+ | 根據併發游標數調整 | |
SQLCHECK | SEMANTICS | 連接數據庫做語義檢查(需有效 schema) | |
PARSE | FULL | 完整解析 C 代碼(支援 #include 等) | |
THREADS | YES | 多線程程序必開 |
| Oracle 類型 | C 類型 | Pro*C 外部類型 | 備註 |
|---|---|---|---|
| VARCHAR2(n) | char[n+1] | STRING / CHARZ | 需 +1 存 \0 |
| CHAR(n) | char[n] | CHARF | 定長,不自動補 \0 |
| NUMBER | int/long/float/double | INTEGER/FLOAT | 視精度選擇 |
| NUMBER(p,s) | double | FLOAT | 高精度建議字符串讀取 |
| DATE | char[11+] | STRING | 格式由 NLS_DATE_FORMAT 決定 |
| RAW(n) | unsigned char[n] | RAW | 二進制數據 |
| ROWID | char[19+] | STRING | 18字符+ \0 |
#define MAX_ROWS 1000 long id[MAX_ROWS]; char name[MAX_ROWS][31]; short id_ind[MAX_ROWS]; short name_ind[MAX_ROWS]; EXEC SQL FOR :MAX_ROWS FETCH cur INTO :id:id_ind, :name:name_ind; int fetched = sqlca.sqlerrd[2]; // 實際提取行數
int ids[100];
char names[100][31];
for (int i=0; i<100; i++) { ids[i]=i; sprintf(names[i], "name%d", i); }
EXEC SQL FOR :100 INSERT INTO t (id, name) VALUES (:ids, :names);
struct Emp { int id; char name[31]; } emps[100];
Emp *p = emps; // 必須用指針
EXEC SQL FOR :100 SELECT id, name INTO :p FROM emp WHERE rownum <= 100;
char *sql = "INSERT INTO t (id) VALUES (:v)"; EXEC SQL PREPARE stmt FROM :sql; EXEC SQL FOR :100 EXECUTE stmt USING :ids;
EXEC SQL EXECUTE BEGIN my_pkg.proc(:in_param, :out_param, :inout_param); END; END-EXEC;
EXEC SQL EXECUTE ... END-EXECORACLE_HOME = /oracle/app/oracle/product/19c PROC = $(ORACLE_HOME)/bin/proc PROCFLAGS = parse=FULL sqlcheck=SEMANTICS char_map=STRING release_cursor=YES userid=scott/tiger CC = gcc CFLAGS = -I$(ORACLE_HOME)/precomp/public -I$(ORACLE_HOME)/rdbms/public LDFLAGS = -L$(ORACLE_HOME)/lib -lclntsh .SUFFIXES: .pc .c .o .pc.c: $(PROC) $(PROCFLAGS) iname=$< .c.o: $(CC) $(CFLAGS) -c $< myapp: main.o db.o util.o $(CC) -o $@ $^ $(LDFLAGS)
sqlcode:0 成功,>0 異常 (1403 NOT FOUND),<0 錯誤sqlerrm.sqlerrmc:錯誤消息文本sqlerrd[2]:已處理行數 (INSERT/UPDATE/DELETE/FETCH 累積)sqlerrd[4]:游標緩存統計sqlwarn[0]:'W' 表示有警告Pro*C 核心在於宿主變量聲明區 (DECLARE SECTION)、類型等價 (VAR IS STRING)、游標生命週期管理、數組批量操作。現代化替代方案:OCCI (C++)、ODPI-C (輕量 C)、微服務化。維護舊系統時,嚴格遵循 MODE=ANSI、CHAR_MAP=STRING、RELEASE_CURSOR=YES 三大鐵律,能消除 90% 的游標洩漏與字符串截斷問題。