項目需在 Oracle 存儲過程中複用現有 C++ 算法庫(如加密、編碼、複雜計算),避免重寫邏輯。本文記錄 Linux x86_64 + Oracle 11g/19c 環境下,外部過程調用完整落地流程。
# 1. 源碼:LibDialPlan.cpp / LibDialPlan.h
# 假設導出函數:extern "C" char* Air2Gsi(int x, const char* y);
# 2. 編譯位置無關代碼
g++ -fPIC -c LibDialPlan.cpp -o LibDialPlan.o -std=c++11
# 3. 鏈接生成 .so(需鏈接 libstdc++)
g++ -shared -o libDialPlan.so LibDialPlan.o -lstdc++
# 4. 放置 Oracle 可訪問路徑(建議專用目錄)
mkdir -p /opt/oracle/extlib
cp libDialPlan.so /opt/oracle/extlib/
chown oracle:oinstall /opt/oracle/extlib/libDialPlan.so
chmod 755 /opt/oracle/extlib/libDialPlan.so
關鍵點:
extern "C" 防止 C++ 名字修飾,保證 Oracle 能按符號名找到函數-fPIC 生成位置無關代碼,共享庫必需-lstdc++ 鏈接 C++ 標準庫,否則運行時找不到 std::string 等符號lib 前綴,Oracle LIBRARY 對象引用時需完整路徑1. listener.ora(關鍵:SID_LIST_LISTENER 新增 PLSExtProc)
LISTENER =
(DESCRIPTION_LIST =
(DESCRIPTION =
(ADDRESS = (PROTOCOL = IPC)(KEY = EXTPROC1521))
(ADDRESS = (PROTOCOL = TCP)(HOST = 192.168.69.166)(PORT = 1521))
)
)
ADR_BASE_LISTENER = /home/oracle/db/app
SID_LIST_LISTENER =
(SID_LIST =
(SID_DESC =
(GLOBAL_DBNAME = oradb)
(SID_NAME = oradb)
)
(SID_DESC =
(SID_NAME = PLSExtProc)
(ORACLE_HOME = /opt/local/oracle/app/product/11.2.0/dbhome_2)
(ENVS = EXTPROC_DLLS=ANY) -- 關鍵:允許加載任意共享庫,生產建議限制具體路徑
(PROGRAM = extproc)
)
)
2. tnsnames.ora(新增 EXTPROC_CONNECTION_DATA)
ORADB =
(DESCRIPTION =
(ADDRESS = (PROTOCOL = TCP)(HOST = 192.168.69.166)(PORT = 1521))
(CONNECT_DATA =
(SERVER = DEDICATED)
(SERVICE_NAME = oradb)
)
)
EXTPROC_CONNECTION_DATA =
(DESCRIPTION =
(ADDRESS_LIST =
(ADDRESS = (PROTOCOL = IPC)(KEY = EXTPROC1521))
)
(CONNECT_DATA =
(SID = PLSExtProc)
(PRESENTATION = RO) -- 只讀會話
)
)
3. 重啟監聽並驗證
lsnrctl stop
lsnrctl start
lsnrctl status
# 輸出必須包含:
# Service "PLSExtProc" has 1 instance(s).
# Instance "PLSExtProc", status UNKNOWN, has 1 handler(s)...
-- 1. 創建 Library 對象(需 CREATE LIBRARY 權限,通常由 DBA 執行)
CREATE OR REPLACE LIBRARY hytpdtbilldb.Fun_Ext_Proc
AS '/opt/oracle/extlib/libDialPlan.so';
/
-- 2. 創建封裝函數(參數映射:C int → BINARY_INTEGER, C char* → VARCHAR2)
CREATE OR REPLACE FUNCTION hytpdtbilldb.Air2Gsi (
x IN BINARY_INTEGER,
y IN VARCHAR2
) RETURN VARCHAR2
AS
LANGUAGE C
LIBRARY hytpdtbilldb.Fun_Ext_Proc
NAME "Air2Gsi"; -- 必須與 C 導出符號完全一致,區分大小寫
/
-- 3. 授權執行權限
GRANT EXECUTE ON hytpdtbilldb.Air2Gsi TO app_user;
參數映射規則:
| C/C++ 類型 | Oracle 類型 | 備註 |
|---|---|---|
int / short / long | BINARY_INTEGER / PLS_INTEGER | 32 位有符號整數 |
float | BINARY_FLOAT | |
double | BINARY_DOUBLE / NUMBER | |
char* / const char* | VARCHAR2 / CLOB | 字符串以 null 結尾,Oracle 自動轉換字符集 |
struct* / 複雜類型 | 不支持直接映射 | 需拆解為基礎類型數組或 JSON 字符串傳遞 |
-- SQL*Plus / SQL Developer 測試
SET SERVEROUTPUT ON
DECLARE
v_result VARCHAR2(4000);
BEGIN
v_result := hytpdtbilldb.Air2Gsi(123, 'TEST_INPUT');
DBMS_OUTPUT.PUT_LINE('Result: ' || v_result);
END;
/
| 現象 | 原因 | 解決 |
|---|---|---|
| ORA-28575: unable to open RPC connection to external procedure agent | Listener 未啟用 PLSExtProc / IPC Key 不匹配 | 檢查 lsnrctl status 確認 PLSExtProc 服務;tnsping EXTPROC_CONNECTION_DATA 測試連通性 |
| ORA-06520: PL/SQL: Error loading external library | .so 路徑錯誤 / 權限不足 / 依賴庫缺失 | ldd /opt/oracle/extlib/libDialPlan.so 檢查依賴;確保 oracle 用戶可讀執行;EXTPROC_DLLS=ANY 或具體路徑 |
| ORA-06521: PL/SQL: Error mapping function | 函數名不匹配 / 參數個數/類型不符 | 用 nm -D libDialPlan.so | grep Air2Gsi 確認導出符號;核對 PL/SQL 參數順序類型 |
| 調用後需重啟數據庫才生效 | extproc 進程緩存了舊庫版本 | 修改庫後執行 ALTER SYSTEM FLUSH SHARED_POOL 或重啟 Listener;生產建議版本化庫文件名 |
| 中文亂碼 | 字符集不一致(C 端 UTF-8,Oracle AL32UTF8/WE8ISO8859P1) | 統一字符集,或在 C 層手動 iconv 轉換 |
ENVS = EXTPROC_DLLS=/opt/oracle/extlib/libDialPlan.so 限制可加載庫libDialPlan_v1.2.so,Library 對象指向新版,平滑升級通過標準化的「編譯 → 部署 → Listener 配置 → Library 創建 → PL/SQL 封裝 → 測試驗證 → 加固」流程,可安全、穩定地在 Oracle 中複用高性能 C/C++ 算法庫,實現異構語言能力無縫融合。