一、简介
单纯用 SQL 语言很难实现这样的应用。为此,DM 数据库提供了 SQL 的两种使用方式:一种是交互方式,另一
种是嵌入方式。
嵌入方式是将 SQL 语言嵌入到高级语言中,这样一来,既发挥了高级语言数据类型丰富、处理方便灵活的优势,又以 SQL 语言弥补了高级语言难以描述数据库操作的不足,从而为用户提供了建立大型管理信息系统和处理复杂事务所需要的工作环境。
DM 数据库允许 C 作为嵌入方式的主语言。在 DM 系统中,我们将嵌有 SQL 语句的 C 语言程序称为PRO*C 程序。
二、预编译系统的结构与功能
对嵌入的 SQL 语句段和 SQL 声明节进行全面的词法、语法检查,然后将 SQL 语句翻译成主语言语句(即 DPI 函数调用)写入目标文件中,使目标文件成为一个纯由主语言组成的程序。
三、预编译系统配置环境+库文件
预编译系统提供的软件有:
- 预编译命令行运行程序 dpc_new.exe;
- 编译时要使用的文件 dpc_dll.h、DPI.h、DPItypes.h、sqlca.h;若兼容ORACLE,则需另外添加 sqlca_ora.h、sqlda_ora.h、dpc_ora_dll.h;若兼容 DB2,则需另外添加 sqlca_db2.h、sqlda_db2.h;
- 连接时需要的库文件 dmdpc.lib(Windows 操作系统)或者 libdmdpc.a (Linux);
- 执行时需要的动态库文件 dmdpc.dll(Windows 操作系统)或者 libdmdpc.so(Linux)。
四、dpc_new命令汇总
[dmdba@localhost bin]$ ./dpc_new FILE=/opt/test.pc
## 收集的命令
./dpc_new FILE=$file MODE=ORACLE MACRO=y TYPE=CPP CHAR_MAP=string
./dpc_new FILE=main.pc MODE=ORACLE TYPE=CPP
g++ -m64 -o main main.cpp -I /opt/dmdbms/include -L /opt/dmdbms/bin -Wall -O -ldmdcp -ldmdpc
五、操作步骤
1、dmdba配置用户的环境变量
## 修改
vim ~/.bash_profile
export LD_LIBRARY_PATH="$LD_LIBRARY_PATH:/opt/dmdbms/bin:/opt/dmdbms/include:/opt/dmdbms/drivers/dci"
export DM_HOME="/opt/dmdbms"
export PATH=$PATH:/opt/dmdbms/bin
## 生效:
source ~/.bash_profile
2、创建表结构
CREATE TABLE "REGISTRY"
(
"REGKEY_ID" VARCHAR2(32) NOT NULL,
"REGKEY_TYPE" CHAR(1) NOT NULL,
"REGKEY_NAME" VARCHAR2(128) NOT NULL,
"REGKEY_STATUS" CHAR(1) NOT NULL,
"REGKEY_PARENT" VARCHAR2(32) NOT NULL,
"REGKEY_VAL" VARCHAR2(256) NOT NULL,
"DATA_TYPE" VARCHAR2(16) NOT NULL,
"VALID_LEN" NUMBER(5,0) DEFAULT 0 NOT NULL,
"VALID_DEC" NUMBER(5,0) DEFAULT 0 NOT NULL,
"MAX_VAL" VARCHAR2(32) NOT NULL,
"MIN_VAL" VARCHAR2(32) NOT NULL,
"DFT_VAL" VARCHAR2(32) NOT NULL,
"VALID_CHAR" VARCHAR2(256) NOT NULL,
"CTRL_ID" VARCHAR2(32) NOT NULL,
"CTRL_DATA" VARCHAR2(256) NOT NULL,
"SUBSYS" NUMBER(5,0) DEFAULT 0 NOT NULL) STORAGE(ON "MAIN", CLUSTERBTR) ;
CREATE UNIQUE INDEX "REGISTRY_UI1" ON "REGISTRY"("REGKEY_ID" ASC) STORAGE(ON "MAIN", CLUSTERBTR) ;
insert into "REGISTRY" ("REGKEY_ID","REGKEY_TYPE","REGKEY_NAME","REGKEY_STATUS","REGKEY_PARENT","REGKEY_VAL","DATA_TYPE","VALID_LEN","VALID_DEC","MAX_VAL","MIN_VAL","DFT_VAL","VALID_CHAR","CTRL_ID","CTRL_DATA","SUBSYS") values ('VERSION', '1', '1', '1', '1', '1', '1', 1, 1, '1', '1', '1', '1', '1', '1', 1);
3、demo用户
main.pc
#include "main.h"
// gcc -m64 -o ./main -I ./../../ref-linux/include/dm/ ./main.cpp -ldl -lstdc++ -Wl,-rpath,. -L. -ldl -L./../../ref-linux/lib-x86 -ldmdcp -ldmdpc
// dpc_new FILE=main.sqx MODE=ORACLE TYPE=CPP
int main(int argc, char **argv)
{
signal(SIGSEGV, server_backtrace); // SIGSEGV 11 Core Invalid memory reference
signal(SIGABRT, server_backtrace); // SIGABRT 6 Core Abort signal from
testDM();
return 0;
}
int testDM()
{
EXEC SQL BEGIN DECLARE SECTION;
varchar server[32];
varchar username[32];
varchar password[32];
EXEC SQL END DECLARE SECTION;
strncpy((char *)server.arr, "127.0.0.1:5236", sizeof(server.arr)-1);
server.len = strlen((const char *)server.arr);
strncpy((char *)username.arr, "SYSDBA", sizeof(username.arr)-1);
username.len = strlen((const char *)username.arr);
strncpy((char *)password.arr, "SYSDBA", sizeof(password.arr)-1);
password.len = strlen((const char *)password.arr);
EXEC SQL CONNECT :username IDENTIFIED BY :password USING :server;
if (sqlca.sqlcode != 0)
{
printf("connetDM fail!\n");
printf("file=[%s:%d] func=[%s] sqlca.sqlcode=%d\n",__FILE__,__LINE__,__FUNCTION__, sqlca.sqlcode);
return -1;
}
printf("connetDM success!\n");
printf("file _ success =[%s:%d] func=[%s] sqlca.sqlcode=%d\n",__FILE__,__LINE__,__FUNCTION__, sqlca.sqlcode);
ST_REGISTRY stRegistry;
ST_REGISTRY_UIDX1 stRegistryUidx1;
DAO_MEMSET(stRegistry);
DAO_MEMSET(stRegistryUidx1);
DAO_SET_VCHAR(stRegistryUidx1, szRegkeyId, "VERSION");
Select_ESQL(stRegistry, stRegistryUidx1);
printf("VERSION=[%s]\n", stRegistry.szRegkeyVal);
return 0;
}
int Select_ESQL(ST_REGISTRY &p_refstRegistry, const ST_REGISTRY_UIDX1 &p_refstRegistryUidx1)
{
EXEC SQL INCLUDE SQLCA;
EXEC SQL BEGIN DECLARE SECTION;
char szRegkeyId_UI1[32 + 1]; // 注册项标识
char szRegkeyType_UI1[1 + 1]; // 注册项类型
char szRegkeyName_UI1[128 + 1]; // 注册项名称
char szRegkeyStatus_UI1[1 + 1]; // 注册项状态
char szRegkeyParent_UI1[32 + 1]; // 注册项上级
char szRegkeyVal_UI1[256 + 1]; // 注册项数值
char szDataType_UI1[16 + 1]; // 数据类型
short siValidLen_UI1; // 有效长度
short siValidDec_UI1; // 有效精度
char szMaxVal_UI1[32 + 1]; // 最大数值
char szMinVal_UI1[32 + 1]; // 最小数值
char szDftVal_UI1[32 + 1]; // 缺省数值
char szValidChar_UI1[256 + 1]; // 有效字符
char szCtrlId_UI1[32 + 1]; // 控件标识
char szCtrlData_UI1[256 + 1]; // 控件数据
short siSubsys_UI1; // 子系统
EXEC SQL END DECLARE SECTION;
int iRetCode = RTMSG_OK;
// 输出数据结构复位
memset((void *)&p_refstRegistry, 0x00, sizeof(p_refstRegistry));
// 主键宿主变量赋值
DAO_STRNCPY(szRegkeyId_UI1, p_refstRegistryUidx1.szRegkeyId, sizeof(szRegkeyId_UI1));
// 执行SELECT语句
EXEC SQL SELECT REGKEY_TYPE, REGKEY_NAME, REGKEY_STATUS, REGKEY_PARENT, REGKEY_VAL,
DATA_TYPE, VALID_LEN, VALID_DEC, MAX_VAL, MIN_VAL,
DFT_VAL, VALID_CHAR, CTRL_ID, CTRL_DATA, SUBSYS
INTO :szRegkeyType_UI1, :szRegkeyName_UI1, :szRegkeyStatus_UI1, :szRegkeyParent_UI1, :szRegkeyVal_UI1,
:szDataType_UI1, :siValidLen_UI1, :siValidDec_UI1, :szMaxVal_UI1, :szMinVal_UI1,
:szDftVal_UI1, :szValidChar_UI1, :szCtrlId_UI1, :szCtrlData_UI1, :siSubsys_UI1
FROM REGISTRY
WHERE REGKEY_ID = :szRegkeyId_UI1;
if (SQLCODE == SQLCODE_NO_DATA)
{
iRetCode = RTMSG_NO_DATA;
printf("file _ 1=[%s:%d] func=[%s] sqlca.sqlcode=%d\n",__FILE__,__LINE__,__FUNCTION__, sqlca.sqlcode);
goto __end;
}
else if (SQLCODE != SQLCODE_OK)
{
printf("file _ != sqlcode_ok =[%s:%d] func=[%s] sqlca.sqlcode=%d\n",__FILE__,__LINE__,__FUNCTION__, sqlca.sqlcode);
return -1;
}
// 输出数据结构赋值
DAO_SET_VCHAR(p_refstRegistry, szRegkeyId, szRegkeyId_UI1);
DAO_SET_CHAR1(p_refstRegistry, chRegkeyType, szRegkeyType_UI1[0]);
DAO_SET_VCHAR(p_refstRegistry, szRegkeyName, szRegkeyName_UI1);
DAO_SET_CHAR1(p_refstRegistry, chRegkeyStatus, szRegkeyStatus_UI1[0]);
DAO_SET_VCHAR(p_refstRegistry, szRegkeyParent, szRegkeyParent_UI1);
DAO_SET_VCHAR(p_refstRegistry, szRegkeyVal, szRegkeyVal_UI1);
DAO_SET_VCHAR(p_refstRegistry, szDataType, szDataType_UI1);
DAO_SET_VALUE(p_refstRegistry, siValidLen, siValidLen_UI1);
DAO_SET_VALUE(p_refstRegistry, siValidDec, siValidDec_UI1);
DAO_SET_VCHAR(p_refstRegistry, szMaxVal, szMaxVal_UI1);
DAO_SET_VCHAR(p_refstRegistry, szMinVal, szMinVal_UI1);
DAO_SET_VCHAR(p_refstRegistry, szDftVal, szDftVal_UI1);
DAO_SET_VCHAR(p_refstRegistry, szValidChar, szValidChar_UI1);
DAO_SET_VCHAR(p_refstRegistry, szCtrlId, szCtrlId_UI1);
DAO_SET_VCHAR(p_refstRegistry, szCtrlData, szCtrlData_UI1);
DAO_SET_VALUE(p_refstRegistry, siSubsys, siSubsys_UI1);
p_refstRegistry.bInitialized = true;
__end:
return iRetCode;
}
static void WidebrightSegvHandler(int signum)
{
void *array[512];
size_t size;
char **strings;
size_t i, j;
signal(signum, SIG_DFL); /* 还原默认的信号处理handler */
size = backtrace (array, 512);
strings = (char **)backtrace_symbols (array, size);
fprintf(stderr, "widebright received SIGSEGV! Stack trace:\n");
for (i = 0; i < size; i++) {
fprintf(stderr, "%d %s \n",i,strings[i]);
}
free (strings);
}
void server_backtrace(int sig)
{
WidebrightSegvHandler(sig);
//打开文件
time_t tSetTime;
time(&tSetTime);
struct tm* ptm = localtime(&tSetTime);
char fname[256] = {0};
sprintf(fname, "./dump.%04d-%02d-%02d_%02d_%02d_%02d.log",
ptm->tm_year+1900, ptm->tm_mon+1, ptm->tm_mday,
ptm->tm_year+1900, ptm->tm_mon+1, ptm->tm_mday,
ptm->tm_hour, ptm->tm_min, ptm->tm_sec);
FILE* f = fopen(fname, "a");
if (f == NULL){
return;
}
int fd = fileno(f);
//锁定文件
struct flock fl;
fl.l_type = F_WRLCK;
fl.l_start = 0;
fl.l_whence = SEEK_SET;
fl.l_len = 0;
fl.l_pid = getpid();
fcntl(fd, F_SETLKW, &fl);
//输出程序的绝对路径
char buffer[4096];
memset(buffer, 0, sizeof(buffer));
int count = readlink("/proc/self/exe", buffer, sizeof(buffer));
if(count > 0){
buffer[count] = '\n';
buffer[count + 1] = 0;
fwrite(buffer, 1, count+1, f);
}
//输出信息的时间
memset(buffer, 0, sizeof(buffer));
sprintf(buffer, "Dump Time: %d-%d-%d %d:%d:%d\n",
ptm->tm_year+1900, ptm->tm_mon+1, ptm->tm_mday,
ptm->tm_hour, ptm->tm_min, ptm->tm_sec);
fwrite(buffer, 1, strlen(buffer), f);
//线程和信号
sprintf(buffer, "Curr thread: %u, Catch signal:%d\n",
(int)pthread_self(), sig);
fwrite(buffer, 1, strlen(buffer), f);
//堆栈
void* DumpArray[256];
int nSize = backtrace(DumpArray, 256);
sprintf(buffer, "backtrace rank = %d\n", nSize);
fwrite(buffer, 1, strlen(buffer), f);
if (nSize > 0){
char** symbols = backtrace_symbols(DumpArray, nSize);
if (symbols != NULL){
for (int i=0; i<nSize; i++){
fwrite(symbols[i], 1, strlen(symbols[i]), f);
fwrite("\n", 1, 1, f);
}
free(symbols);
}
}
//文件解锁后关闭
fl.l_type = F_UNLCK;
fcntl(fd, F_SETLK, &fl);
fclose(f);
exit(1);
}
main.h
#include <stdlib.h>
#include <dlfcn.h>
#include <stdio.h>
#include <stdarg.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <string.h>
#include <string>
#include <unistd.h>
#include <signal.h>
#include <execinfo.h>
#include <dirent.h>
#include <fcntl.h>
#include <sqlca.h>
using namespace std;
#define RTMSG_OK 0
#define RTMSG_NO_DATA 100
#define RTMSG_NO_DATA 100
#define SQLCODE_OK 0
#define SQLCODE_NO_DATA 100
#define SQLCODE sqlca.sqlcode
#define DAO_STRNCPY(szDest, szSource, iCount) \
{\
if ((szDest) != NULL && (szSource) != NULL) \
{ \
memset((void *)(szDest), 0x00, (iCount)); \
strncpy((szDest), (szSource)[0] == 0x00 ? " " : (szSource), (iCount) - 1); \
} \
}
#define DAO_SET_CHAR1(vTable, vColumn, vValue) \
{ \
(vTable).vColumn = (vValue) == ' ' ? 0x00 : (vValue); \
(vTable).siInd_##vColumn = 1; \
}
#define DAO_SET_VCHAR(vTable, vColumn, vValue) \
{ \
strncpy((vTable).vColumn, (vValue) == NULL || ((vValue)[0] == ' ' && (vValue)[1] == 0x00) ? "" : (vValue), sizeof((vTable).vColumn) - 1); \
(vTable).vColumn[sizeof((vTable).vColumn) - 1] = 0x00; \
(vTable).siInd_##vColumn = 1; \
}
#define DAO_SET_VALUE(vTable, vColumn, vValue) \
{ \
(vTable).vColumn = (vValue); \
(vTable).siInd_##vColumn = 1; \
}
#define DAO_MEMSET(vTable) \
{\
memset((void *)&(vTable), 0x00, sizeof(vTable)); \
}
// 定义数据结构
struct ST_REGISTRY
{
bool bInitialized;
char szRegkeyId[32 + 1]; short siInd_szRegkeyId; // 注册项标识
char chRegkeyType; short siInd_chRegkeyType; // 注册项类型
char szRegkeyName[128 + 1]; short siInd_szRegkeyName; // 注册项名称
char chRegkeyStatus; short siInd_chRegkeyStatus; // 注册项状态
char szRegkeyParent[32 + 1]; short siInd_szRegkeyParent; // 注册项上级
char szRegkeyVal[256 + 1]; short siInd_szRegkeyVal; // 注册项数值
char szDataType[16 + 1]; short siInd_szDataType; // 数据类型
short siValidLen; short siInd_siValidLen; // 有效长度
short siValidDec; short siInd_siValidDec; // 有效精度
char szMaxVal[32 + 1]; short siInd_szMaxVal; // 最大数值
char szMinVal[32 + 1]; short siInd_szMinVal; // 最小数值
char szDftVal[32 + 1]; short siInd_szDftVal; // 缺省数值
char szValidChar[256 + 1]; short siInd_szValidChar; // 有效字符
char szCtrlId[32 + 1]; short siInd_szCtrlId; // 控件标识
char szCtrlData[256 + 1]; short siInd_szCtrlData; // 控件数据
short siSubsys; short siInd_siSubsys; // 子系统
};
struct ST_REGISTRY_UIDX1
{
char szRegkeyId[32 + 1]; short siInd_szRegkeyId; // 注册项标识
};
int testDM();
void server_backtrace(int sig);
int Select_ESQL(ST_REGISTRY &p_refstRegistry, const ST_REGISTRY_UIDX1 &p_refstRegistryUidx1);
4、编译并运行文件
[dmdba@localhost bin]$ ./dpc_new FILE=main.pc MODE=ORACLE TYPE=CPP
[dmdba@localhost bin]$ g++ -m64 -o main main.cpp -I /opt/dmdbms/include -L /opt/dmdbms/bin -Wall -O -ldmdcp -ldmdpc
[dmdba@localhost bin]$ ./main
connetDM success!
file _ success =[main.cpp:58] func=[testDM] sqlca.sqlcode=0
VERSION=[1
六、Q&A
1. proc编译为c++报出很多警告
先编译加-Wno-write-strings取消警告,避免警告太多无法定位err
2. proc 预编译报错:cannot execute binary file
碰到这种错误第一反应可能是数据库版本和操作系统版本不匹配,或者安装包不完整,然后查操作系统版本和数据库版本,发现是版本和安装包的 MD5 都是匹配的。
其他例子
达梦Proc*操作达梦数据库-Fetch例子 https://blog.csdn.net/qq_35349982/article/details/127211857