当前位置导航:炫浪网>>网络学院>>编程开发>>Oracle教程

将Sybase C程序快速移植到Oracle Pro*C程序


  一、前言
  
  当前不少单位在进行系统改造升级时,面临着如何将各个独立部门开发的应用所采用的数据库统一,以便适应数据大集中的需求,方便将来进行数据挖掘等更高层次的应用。在数据库统一的过程中,如何快速、高效的将已有的基于异种数据库的程序,特别是采用C/C++开发的运行于linux/unix下的程序转换到现有数据库下,成为不少单位面临的问题。当然了,基于RAD开发的程序,基本上不需要进行修改,就能达到目的。
  
  针对如何快速地将Sybase C程序移植为Oracle Pro*C程序,本文将指出一个方向,并给出一个粗糙的框架。仅以此文抛砖引玉,希望各位指导。(计划编写一个转换系列,看看各位反应如何吧)
  
  二、原理
  
  Sybase的C程序转换到Oracle的Pro*C程序,相对于其他类型的转换有很多优势(原谅我挑了可能是最简单的一种转换):
  
  Sybase程序采用的是C函数调用的方式,这给将Sybase程序转换到其他任意一种数据库程序带来了很大的方便!仅仅需要使用目的数据库的语法,实现老程序使用到的Sybase函数就可以了!并且不需要对原来的C程序进行任何逻辑方面的修改!
  Oracle的Pro*C提供了动态sql的开发方式,这给移植带来了极大的方便。
  Oracle Pro*C与Sybase同样提供进程多连接方式。
  Oracle 9采用parse=full的编译选项时,C的定义变量可以直接在EXEC SQL中使用,这给程序的编写方便了不少。
  还有其他优势,不再一一列举。
  
  三、实现
  
  针对Sybase的几个主要函数,下面概述处理办法,具体的实现参照源代码:
  
  dbinit():该函数仅仅将针对数据库操作的一些变量进行初始化。
  一些用户名、密码的设置,没有太多可说的。比较简单。
  dbopen():该函数实际进行数据库的连接,在连接过程中,我设置了全局变量AllOpenProcess用于保存所有的连接指针,并产生了一个连接串用于标识该连接。保存于全局变量中是因为Sybase函数dbexit没有输入参数。
  dbcmd():该函数进行sql语句的分析,以便进一步进行判断。
  dbexec():该函数进行命令的执行(不太清楚为什么Sybase将该函数与dbcmd分开)。对无返回结果集的命令(insert、update、delete)直接通过EXEC SQL AT EXECUTE IMMEDIATE的语法执行。对与存在返回结果集的select,采用oracle动态sql4的方法进行处理。因为时间以及老的程序的关系,本函数的实现仅仅针对了传入完整sql语句的字符串。
  dbresults():判断返回结果。
  dbbind():绑定存储值的地址,并判断传入的类型是否正确。因为老程序仅仅使用了NTBSTRINGBIND类型,所以示例中也仅仅处理了此种类型。
  dbnumcols():返回数据列数目,在语句类型为select时有效。
  dbnextrow():取得下一条数据值,并填写到dbbind所绑定的地址空间内。此处需要根据源类型select_dp->T[]以及目标类型进行相关的数据转换。
  dbdead():判断数据库连接是否正常。采用从DUAL表中取值的方式来测试。
  dbexit():退出数据库连接。
  还有其他很多函数,但是因为我没有使用到,嘿嘿,就不作实现了。
  
  四.移植
  
  移植的方法很简单,将原来c/c++文件中#include "Sybfront.h" #include "sybdb.h"等引用Sybase头文件的地方去掉,更换为附件中的#include "database_op.h",同时将附件中database_op.cxx加入到工程中,加上适当的编译语句(PROC),然后去除原来连接sybase的lib,加入oracle的lib:orasql9.lib。就可以在基本不修改源程序的情况下完成程序的移植!当然,如果你的sybase程序采用了非NTBSTRINGBIND类型的话,自己在cxx文件中加入相关的处理方式吧!祝好运!欢迎探讨。
  
  五、技巧
  
  下面我总结一些开发过程中使用到的一些小的技巧,各位见笑。如何充分利用 Visual C++ 的开发环境进行 Pro*C 程序开发:
  
  相信各位对 Visual C++ 的集成开发环境使用比较熟吧,别告诉我你开发 Unix 下的 C/C++ 程序还在使用 vi、UltraEdit、EditPlus 吧!当然了,如果你坚持我也不反对,不过您老此时就不用浪费时间向下翻页了。
  
  oracle 的 Pro*C 程序一般以 PC 结尾。而以 PC 结尾的程序,没法按照 C/C++ 的语法在 Visual C++ 的集成开发环境下显示!只是白纸黑字一片。不像 UltraEdit,可以定制使得 PC 文件按照 C/C++ 的语法显示(在 UltraEdit 安装目录下的 WORDFILE.TXT 文件中包含 "C CPP CC CXX H HPP AWK" 内容的后面加上 "PC" 就可以了)(也许VC有这个功能,不过我没找到)。怎么办呢?简单,修改PC文件后缀为CXX,此时,文件就按照C/C++语法进行显示了!此时,设置该文件属性页(在VC开发环境的WORKSPACE中选中该文件,按 ALT+F7 )中 General 下的 Always use custom build step 为选中状态,Curstom Build 页下的 Commands/Outputs 下参考 oracle 的 example 输入相应的编译选项。记得将对应的PC后缀修改为 cxx 后缀。
  
  database_op.cxx
  
  /*
  database_op.cxx
  负责兼容 sybase 和 oracle对数据库的操作
  */
  #include "stdio.h"
  #include "stdlib.h"
  #include "string.h"
  #include "database_op.h"
  #ifdef SYBASE_PLATFORM
  /*
  * 不需要任何动作
  */
  #endif
  #ifdef MSSQL_PLATFORM
  #endif
  #ifdef ORACLE_PLATFORM
  EXEC SQL INCLUDE sqlca;
  EXEC SQL INCLUDE sqlda;
  EXEC SQL WHENEVER NOT FOUND CONTINUE ;
  EXEC SQL WHENEVER SQLWARNING CONTINUE ;
  EXEC SQL WHENEVER SQLERROR CONTINUE ;
  char  dyn_statement[1024];
  SQLDA *select_dp = NULL;
  #define MAXCOLNUM 126
  #define MAXCOLVALUE 1024
  char chValueBuff[MAXCOLNUM][MAXCOLVALUE]={0};
  /* Maximum lengths of the _names_ of the
  select-list items or indicator variables. */
  #define MAX_VNAME_LEN   80
  #define MAX_INAME_LEN   80
  typedef struct Securepair
  {
  struct Securepair *next;
  char *labelname;
  char *labelvalue;
  } SECLABELS;
  typedef struct loginrec
  {
  char chLogName[256];
  char chLogPass[256];
  } LOGINREC_STR;
  enum SQLTYPE_ENUM{SQL_NULL=0,SQL_SELECT,SQL_INSERT,SQL_UPDATE,SQL_DELETE};
  struct dbprocess {
  sql_context ctx;
  char connName[20];
  
  /*当前sql语句类型,包括查询select insert update delete,*/
  /*通过在sqldata中查找上述字符串获得*/
  enum SQLTYPE_ENUM sqltype ;
  long rowindex ; /*执行本次sql后,被取出的行的数目/下次需要取出的索引*/
  long rownum ; /*本次sql得到的行数目*/
  long colnum ; /*当前列数目*/
  long sqlerror ;/*SQL语句错误号*/
  void * ppbuff[MAXPOINTNUM] ; /*当前绑定了字段的内存地址*/
  long pbufftype[MAXPOINTNUM] ;
  /*当前sql语句值,在执行后更改上面的rowindex,rownum,colnum*/
  char sqldata[SQLDATALEN] ;
  };
  typedef struct dbprocess  DBPROC_STR;
  DBPROC_STR * AllOpenProcess[20]={0};
  EHANDLEFUNC ehandfunc = 0 ;
  MHANDLEFUNC mhandfunc = 0 ;
  RETCODE dbinit (void)
  {
  int i ;
  if( select_dp ) return SUCCEED ;
  if ((select_dp =sqlald (MAXPOINTNUM, MAX_VNAME_LEN, MAX_INAME_LEN)) == (SQLDA *) 0)
  {
  fprintf(stderr,"Cannot allocate memory for select descriptor.");
  return FAIL;
  }
  
  select_dp->N = MAXPOINTNUM;
  
  /* Allocate the pointers to the indicator variables, and the
  actual data. */
  for (i = 0; i < MAXPOINTNUM; i++) {
  select_dp->I[i] = (short *) malloc(sizeof(short));
  select_dp->V[i] = (char *) malloc(1);
  }
  
  return SUCCEED;
  }
  void errhandlefunc()
  {
  if( ehandfunc )
  (*ehandfunc)(0,0,0,0,"","") ;
  }
  void mhandlefunc()
  {
  if( mhandfunc )
  (*mhandfunc)(0,0,0,0,"","","",0) ;
  }
  EHANDLEFUNC dberrhandle (EHANDLEFUNC handler)
  {
  ehandfunc = handler ;
  /* EXEC SQL WHENEVER SQLERROR DO errhandlefunc() ;*/
  return 0 ;
  }
  MHANDLEFUNC dbmsghandle (MHANDLEFUNC handler)
  {
  mhandfunc = handler ;
  /* EXEC SQL WHENEVER SQLWARNING DO mhandlefunc() ;***/
  return 0 ;
  }
  void * dblogin (void)
  {
  LOGINREC_STR * rec ;
  rec = (LOGINREC_STR*)malloc(sizeof(LOGINREC_STR)) ;
  if( NULL == rec ) return rec ;
  memset(rec,0,sizeof(LOGINREC_STR));
  return rec;
  }
  RETCODE dbsetlname (LOGINREC_STR *lptr,char *values,int type)
  {
  if( NULL == lptr || NULL == values ) return FAIL ;
  if( (DBSETUSER != type) &&(DBSETPWD != type) ) return FAIL ;
  if( DBSETUSER == type )
  {
  if( strlen(values) >= sizeof(lptr->chLogName) ) return FAIL ;
  st
相关内容
赞助商链接