目录
[一、Pro*C 基础与 KingbaseES 兼容机制](#一、Pro*C 基础与 KingbaseES 兼容机制)
[二、KingbaseES Pro*C 对 Oracle 功能兼容范围](#二、KingbaseES Pro*C 对 Oracle 功能兼容范围)
[1. 连接管理](#1. 连接管理)
[2. 数据类型与变量](#2. 数据类型与变量)
[3. 嵌入式 SQL 与游标](#3. 嵌入式 SQL 与游标)
[4. 动态 SQL](#4. 动态 SQL)
[5. 嵌入式 PL/SQL](#5. 嵌入式 PL/SQL)
[6. 错误处理](#6. 错误处理)
[7. 高级特性](#7. 高级特性)
[1. 适配度评估](#1. 适配度评估)
[2. 系统环境评估](#2. 系统环境评估)
[1. 运行库部署](#1. 运行库部署)
[2. 环境变量配置](#2. 环境变量配置)
[3. Makefile 编译配置](#3. Makefile 编译配置)
[4. 数据库连接配置](#4. 数据库连接配置)
[5. 迁移后验证](#5. 迁移后验证)
[1. 数据库连接示例](#1. 数据库连接示例)
[2. 数组批量插入示例](#2. 数组批量插入示例)
[3. 带指示器变量的查询](#3. 带指示器变量的查询)
[4. LOB 大字段读写](#4. LOB 大字段读写)
[5. Oracle 动态 SQL 方法 4](#5. Oracle 动态 SQL 方法 4)
[6. 嵌入式 PL/SQL 块](#6. 嵌入式 PL/SQL 块)
正文开始------
现如今国产化数据库替换已经是行业常态,很多企业早年上线的业务系统,大量采用 Oracle Pro*C 开发。这类嵌入式C语言数据库程序运行稳定、执行效率高,长期承载金融、政务、电信等行业的核心业务。但也正因年代久远、代码体量庞大,迁移改造一直是开发人员比较头疼的问题。金仓数据库KingbaseES专门针对这类存量程序,推出了Oracle兼容版Pro*C运行组件,不用大规模改写源码,仅调整环境依赖就能完成迁移,整体改造成本很低。下面这篇文章包括兼容特性、环境硬性要求、工程部署步骤、配置写法以及测试代码。
一、Pro*C 基础与 KingbaseES 兼容机制
简单来说,Pro*C就是在C语言代码中嵌入SQL语句。这种写法开发便捷、事务可控,不过普通C编译器无法识别内嵌的SQL语法,必须依靠专门的预编译工具,把后缀为.pc的文件转成标准C代码,之后再链接数据库运行库,程序才能正常访问数据库。
在传统Oracle环境里,我们一般使用Oracle自带的proc工具完成预编译。迁移到金仓数据库之后,很多人会误以为需要更换编译工具,实际并不是。金仓目前没有自研的Pro*C预编译程序,预编译环节依旧沿用Oracle的proc工具,改动只集中在运行阶段:把Oracle运行库替换成金仓提供的Pro*C运行库,以此做到语法、逻辑、调用方式全方位兼容。
我整理了金仓Pro*C的硬性部署规范,实际部署时必须遵守:
-
操作系统仅支持64位Linux,适配x86_64、arm、mipsel、loongarch64常见服务器架构,Windows平台无法部署使用;
-
驱动包结构简单,核心只有lib文件夹,存放所有运行依赖库文件;
-
金仓不单独提供开发头文件,直接复用Oracle原有头文件,若业务代码不涉及SQLDA复杂操作,甚至可以不用引入头文件;
-
预编译命令完全沿用Oracle写法,不需要修改原有编译脚本。
日常开发中常用的预编译命令不多,下面两种通用写法,适配普通SQL文件和带PL/SQL的代码文件:
bash
# 常规SQL文件预编译
$ORACLE/bin/proc iname=sample.pc oname=sample.c dynamic=ORACLE
# 简化写法
proc sample.pc
# 含PL/SQL语义检查的预编译(需连接数据库校验语法)
proc sample.pc SQLCHECK=SEMANTICS userid=user/password
一个常见问题:预编译过程中如果提示系统头文件缺失,直接把系统头文件路径补充到pcscfg.cfg配置文件即可,该文件路径一般在$ORACLE_HOME/precomp/admin/目录下。
二、KingbaseES Pro*C 对 Oracle 功能兼容范围
兼容性永远是迁移工作的重中之重。兼容对照表,区分常用兼容功能和不支持特性,方便迁移前快速评估,避免后期踩坑。
1. 连接管理
常规数据库连接方式基本全部兼容,包含标准CONNECT连接、Oracle Net Services远程连接。同时支持多并发登录、显式连接、隐式连接,适合多会话、多数据库切换的业务场景,原有连接逻辑不用改动。
2. 数据类型与变量
开发中常用的宿主变量、指示器变量、VARCHAR、游标变量、结构体、指针变量都能正常使用,数据类型转换、常量求值也没有限制。需要注意,Universal ROWIDs、NCHAR字符类型以及国际化相关的全球化功能,金仓当前版本不兼容。
3. 嵌入式 SQL 与游标
基础增删改查、DDL语句、DML返回子句都能正常执行。游标、滚动游标、CURRENT OF行定位语法完全兼容,数组、结构体数组批量操作语法也没有改动。唯一需要注意,Oracle专属的优化器Hint、模拟CURRENT OF语法、隐式缓冲区插入等扩展功能不支持,迁移时需要手动剔除。
4. 动态 SQL
动态SQL是老项目高频使用的功能,金仓对Oracle动态方法4、ANSI动态方法4做到了完整兼容。不管是未知参数、未知查询列的动态语句,还是动态SQL混合PL/SQL的写法,都能正常运行。唯一限制是不支持动态SQL语句缓存机制。
5. 嵌入式 PL/SQL
代码里内嵌的PL/SQL匿名块、存储过程、函数调用无需改写,主变量、指示器变量传参逻辑保持不变。仅外部C程序调用PL/SQL的特殊写法不兼容,普通业务代码基本不受影响。
6. 错误处理
生产环境必备的错误捕获功能全部兼容,包括SQLCODE错误码、SQLSTATE状态位、SQLCA通信区以及WHENEVER异常跳转。为缩减适配成本,ORACA通信区、原始SQL语句抓取、完整错误详情查询这类小众功能没有适配。
7. 高级特性
针对生产级项目,金仓支持多线程运行、连接池、C++编译、LOB大字段读写。为了减少冗余适配,OCI高低版本接口、嵌入式OCI调用、X/Open开发规范这类Oracle底层接口全部不兼容。
这里提醒一句,官方文档明确规定:兼容表里没有标注的功能,一律默认不支持,评估阶段不要主观判定兼容,严格对照文档执行。
三、迁移前准备:适配度与系统环境评估
迁移不能直接上手部署,必须提前做两层评估,一层是代码功能适配评估,另一层是服务器环境评估,这两步能规避绝大多数报错问题。
1. 适配度评估
适配评估的方式很简单,对照官方兼容表逐条筛查业务代码。如果代码里包含不支持特性,提前做替换改造:删除SQL优化Hint、把嵌入式OCI调用改成标准嵌入式SQL、NCHAR统一替换为NVARCHAR2。从实测情况来看,市面上绝大多数常规业务Pro*C代码,基本不用改动就能迁移。
2. 系统环境评估
服务器环境的坑主要集中在编译器版本,官方文档对环境要求标注得十分严格:
-
硬性要求64位Linux系统,拒绝Windows平台;
-
金仓驱动默认基于gcc 4.8.5编译;
-
gcc5.1以上版本重构了C++底层库,新旧ABI不互通,混用会出现undefined symbol符号未定义报错。
高版本gcc编译程序时,增加宏定义强制使用旧版ABI,就能规避兼容问题:
bash
-D_GLIBCXX_USE_CXX11_ABI=0
一定要保证应用编译环境、驱动编译环境的gcc版本和ABI模式一致,这是链接报错最常见的原因。
四、工程搭建与迁移全流程
整套迁移流程并不复杂,不需要改动预编译生成的C代码,只需要部署运行库、配置环境变量、修改编译脚本、填写数据库连接信息即可。
1. 运行库部署
解压官方提供的kbproc压缩包,自定义放置服务器目录,解压后仅包含lib库文件夹和sys_service.conf配置文件,没有多余冗余文件。
2. 环境变量配置
需要配置两个核心环境变量,一个指向配置文件目录,一个用于系统识别库文件路径,一般直接写入.bashrc永久生效:
bash
# 配置文件所在路径
export KINGBASE_CONFDIR=/home/kingbase/kbproc/
# 运行时库搜索路径
export LD_LIBRARY_PATH=/home/kingbase/kbproc/lib:$LD_LIBRARY_PATH
配置完成执行source命令刷新,用echo打印变量,确认配置无误。
3. Makefile 编译配置
编译脚本只需要改动库依赖部分,删除原有Oracle库路径,替换为金仓运行库,官方标准模板如下:
bash
PROC_LIB_DIR = /home/kingbase/kbproc/lib
DEST_LIB = -L$(PROC_LIB_DIR)
CC = gcc
CFLAGS = -O2 -Wall
test: test.c
CC) $(CFLAGS) test.c -o test $(DEST_LIB) -lclntsh -lsqllib -ldl -Wl,-rpath=$(PROC_LIB_DIR) $(
注意必须链接lclntsh、lsqllib两个库文件,彻底替换Oracle运行库,防止库冲突。
4. 数据库连接配置
Pro*C程序不再硬编码连接参数,统一读取sys_service.conf,配置格式和金仓DCI接口完全一致,写法简单直观:
bash
[kingbase]
name = kingbase
drivername = kingbase
ip = 192.168.1.100
port = 54321
username = system
password = 123456
dbname = test
5. 迁移后验证
部署完成不要直接投产,按照官方要求做功能校验:先测试数据库连通性,再依次测试普通SQL、批量插入、动态SQL、PL/SQL代码、LOB大字段读写。对比Oracle环境下的执行结果、事务提交状态、错误返回码,确保业务行为完全一致。
五、标准开发与测试代码示例
下面代码是官方给出的标准测试demo,覆盖日常开发高频场景,可直接编译运行用于环境校验。
1. 数据库连接示例
cpp
#include <stdio.h>
#include <stdlib.h>
EXEC SQL INCLUDE SQLCA;
void do_connect() {
EXEC SQL BEGIN DECLARE SECTION;
char user[100] = "system";
char pwd[100] = "123456";
char dbname[100] = "kingbase";
EXEC SQL END DECLARE SECTION;
EXEC SQL CONNECT :user IDENTIFIED BY :pwd USING :dbname;
if (SQLCODE != 0) {
printf("connect error\n");
exit(1);
}
printf("connect success\n");
}
2. 数组批量插入示例
cpp
void do_batch_insert() {
EXEC SQL BEGIN DECLARE SECTION;
int ids[7] = {1,2,3,9,95,2022,43001};
char names[10][10] = {"ababab","abcabc","3d3d3d3d","","xkhxx","dynamic","dynamic3"};
EXEC SQL END DECLARE SECTION;
EXEC SQL INSERT INTO tab01 VALUES (:ids, :names);
EXEC SQL COMMIT;
printf("insert rows: %d\n", sqlca.sqlerrd[2]);
}
3. 带指示器变量的查询
cpp
void do_select_with_indicator() {
EXEC SQL BEGIN DECLARE SECTION;
int id = 1;
char name[50];
short ind_name;
EXEC SQL END DECLARE SECTION;
EXEC SQL SELECT name INTO :name:ind_name FROM tab01 WHERE id=:id;
if (ind_name == -1) {
printf("name is NULL\n");
} else {
printf("name: %s\n", name);
}
}
4. LOB 大字段读写
cpp
#include <oci.h>
void do_lob_test() {
OCIBlobLocator *blob;
unsigned char buf[128] = "TEST_LOB_DATA";
unsigned int amt = 12, offset = 1;
EXEC SQL ALLOCATE :blob;
EXEC SQL SELECT b INTO :blob FROM t_proc_lob FOR UPDATE;
EXEC SQL LOB WRITE ONE :amt FROM :buf INTO :blob AT :offset;
EXEC SQL FREE :blob;
}
5. Oracle 动态 SQL 方法 4
cpp
void do_dynamic_sql4() {
char *sql = "select id,name from tab01 where id>=:id";
SQLDA *bind_dp, *select_dp;
bind_dp = SQLSQLDAAlloc(SQL_SINGLE_RCTX, 20, 10, 10);
select_dp = SQLSQLDAAlloc(SQL_SINGLE_RCTX, 20, 10, 10);
EXEC SQL PREPARE stmt FROM :sql;
EXEC SQL DECLARE C CURSOR FOR stmt;
EXEC SQL DESCRIBE BIND VARIABLES FOR stmt INTO bind_dp;
EXEC SQL OPEN C USING DESCRIPTOR bind_dp;
EXEC SQL DESCRIBE SELECT LIST FOR stmt INTO select_dp;
for (;;) {
EXEC SQL FETCH C USING DESCRIPTOR select_dp;
if (SQLCODE != 0) break;
}
EXEC SQL CLOSE C;
SQLSQLDAFree(SQL_SINGLE_RCTX, bind_dp);
SQLSQLDAFree(SQL_SINGLE_RCTX, select_dp);
}
6. 嵌入式 PL/SQL 块
cpp
void do_plsql_block() {
EXEC SQL BEGIN DECLARE SECTION;
int acct = 1;
double debit = 100;
VARCHAR status[20];
EXEC SQL END DECLARE SECTION;
EXEC SQL EXECUTE
DECLARE
old_bal NUMBER;
BEGIN
SELECT bal INTO old_bal FROM accounts WHERE account_id=:acct;
:status := 'PL/SQL EXEC SUCCESS';
END;
END-EXEC;
status.arr[status.len] = '\0';
printf("status: %s\n", status.arr);
}
六、迁移常见问题与处理建议
-
预编译提示头文件缺失:直接把系统头文件路径写入pcscfg.cfg配置文件;
-
编译链接出现undefined symbol:核对gcc版本,统一ABI模式,添加旧ABI兼容宏;
-
数据库连接失败:优先检查两个环境变量,核对配置文件内IP、端口、账号密码;
-
存在不兼容语法:提前修改代码,替换Oracle专属扩展写法;
-
运行时提示库文件加载失败:确认LD_LIBRARY_PATH包含金仓lib目录。
七、迁移总结
结合官方文档以及实操部署经验来看,金仓KingbaseES对Oracle Pro*C的兼容方案十分成熟。整套迁移不用改动业务源代码,预编译流程完全沿用Oracle工具,仅替换运行库、调整环境配置就能完成适配。常规连接、SQL执行、事务控制、动态SQL、PL/SQL、LOB字段等核心功能,都能稳定运行,完全满足企业生产要求。