Proc *C/C++入门--C/C++操作Oracle数据库

Proc *C/C++入门

有点累啊 整理笔记!!!

基本概念

  • SQL
    构化查询语言(Structured Query Language)简称SQL,是一种数据库查询和程序设计语言,用于存取数据以及查询、更新和管理关系数据库系统;同时也是数据库脚本文件的扩展名。
    1986年10 月由美国国家标准局(ANSI)通过的数据库语言美国标准,接着,国际标准化组织(ISO)颁布了SQL正式国际标准。

  • Pro*C/C++
    在C/C++程序中嵌入SQL语句操作数据库,得到的应用程序叫做Proc*C/C++程序!优点是高效!

  • 嵌入式SQL

能在其他编程语言中混合使用的SQL语句,叫做嵌入式SQL语句!但是各厂商对嵌入式SQL的具体实现不一样!只是接口一样,但是具体的混合语法不一样,各厂商有自己的预编译工具!

SQL86(1986年发布)中定义了对于COBOL, FORTRAN, PI/L等语言的嵌入式SQL的规范。在SQL89(1989年发布)规范中,定义了对于C语言的嵌入式SQL的规范。

  • 宿主语言
    嵌入式SQL的载体是宿主语言,比如这里说的就是C/C++!
宿主语言 Pro程序
C/C++ Pro*C/C++
FORTRAN Pro*FORTRAN
PASCAL Pro*PASCAL
COBOL Pro*COBOL
PL/I Pro*PL/I
Ada Pro*Ada
  • 访问数据库的方法
    • 命令行:Sql*Plus
    • web界面:Enterprise Manager Console(在数据库主机开启一个服务器供B/S架构使用)
    • 第三方应用程序:自己开发的程序,可用C/C++或者Java(JDBC)

  • 程序结构

1.  Include 头文件 (c/c++ and pro*c/c++)
2.  定义变量
3.  定义函数
4.  main 
4.1     连结数据库: connect
4.2     SQL 操作语句:  EXEC SQL …….;
4.3     exception handler
4.4     断开连结:
4.5     EXEC SQL COMMIT / ROLLBACK WORK release

#include 
#include 
#include 
#include "sqlca.h"

EXEC SQL BEGIN DECLARE SECTION;
    char username[32];
    char password[32];
    char dname[25];
EXEC SQL END DECLARE SECTION;

EXEC SQL INCLUDE sqlca;

void sqlerror();

main()
{
   EXEC SQL WHENEVER SQLERROR DO sqlerror();
   strcpy(username,“scott");
   strcpy(password, “*****");
   EXEC SQL CONNECT:username IDENTIFIED BY:password;
   EXEC SQL select dname from dept where id=10;
   printf(“dname:%s \n”, dname);
}
void sqlerror()
{
   EXEC SQL WHENEVER SQLERROR CONTINUE;
   printf("\n---- oracle error detected:\n");
   printf("%.70s\n", sqlca.sqlerrm.sqlerrmc);
   EXEC SQL ROLLBACK WORK RELEASE;
   exit(1);
}

开发流程

  • 流程比较

Pro*C/C++应用程序的开发多了一个预编译的过程:pc转换成c或者cpp文件

  • 关于预编译工具–Proc
    • 位置:$ORACLE_HOME/bin目录下–拥有数据库访问权限的用户可以直接在终端使用,不需要绝对路径
    • 配置文件:$ORACLE_HOME/precomp/admin/pcscfg.cfg –头文件、库文件路径
    • 基本命令格式:proc iname=filename oname=outname [OptionName1=value1]…[OptionNameN=valueN](C++)或者直接proc xxx.pc
    • 基本选项:
选项 说明
INAME path and filename (name of the input file) 1.pc
ONAME path and filename (name of the output file) 1.c 1.cc
INCLUDE path (头文件所在路径)–INCLUDE 路径名 或 INCLUDE =(路径名1,路径名2)
PARSE FULL 、 PARTIA 、 NONE (default FULL for C, Others for C++)
CODE ANSI_C 、 CPP (default ansi_c)
USERID username/password(同时包含用户名和密码)

默认预编译得到的是C文件,使用下列选项得到C++文件

parse=none      告诉proc编译器 按照c++规范解析 dm02_hello.pc
code=cpp        告诉proc编译器   按照c++规范 生产文件
proc dm02_hello.pc parse=none code=cpp oname=dm02_hello.cc
  • 结合命令简述开发流程:
    1. vim hello.pc
    2. proc hello.pc
    3. gcc -g hello.c -o app -I ORACLEHOME/precomp/publicL {ORACLE_HOME}/lib -lclntsh
    4. ./app [args]
#include 
#include 
#include 

#include "sqlca.h"


//定义宿主变量(SQL变量)
//C语言可以直接使用
//嵌入式SQL语句里面使用的话需要加上EXEC SQL前缀

EXEC SQL BEGIN DECLARE SECTION;
char * serverid = "scott/lzj123529";
EXEC SQL END DECLARE SECTION;

int main()
{

    printf("HelloWorld\n");
    return 0;
}
#include 
#include 
#include 
#include 

#include "sqlca.h"
using namespace std;

EXEC SQL BEGIN DECLARE SECTION;
char * serverid = "scott/lzj123529";
EXEC SQL END DECLARE SECTION;

int main()
{

    cout<<"HelloWorld\n";
    return 0;
}

相关基础语法

连接数据库

用到的嵌入式SQL语句:

EXEC SQL CONNECT { :user IDENTIFIED BY :oldpswd | :usr_psw }
   [[ AT { dbname | :host_variable }] USING :connect_string ]
      [ {ALTER AUTHORIZATION :newpswd  |  IN { SYSDBA | SYSOPER } MODE} ] ;

连接之前开始lsnrctl以及启动数据库服务

  • 方法1
    EXEC SQL CONNECT :usr_pwd;

  • 方法2
    EXEC SQL CONNECT :username IDENTIFIED BY :password ;

  • 方法3
    通过db_name连接没有限定名字的数据库–可以修改数据库的名字–at选项 */
    EXEC SQL CONNECT :username IDENTIFIED BY :password AT :db_name USING :db_string;

/* declare needed host variables */
char  username[10]  = "scott"; 
char  password[10]  = "tiger";
char  db_string[20] = "NYNON"; 

/* give the database connection a unique name */ 
EXEC SQL DECLARE DB_NAME DATABASE; 

/* connect to the nondefault database  */
EXEC SQL CONNECT :username IDENTIFIED BY :password 
   AT DB_NAME USING :db_string;

1.代码里写死了用户名和密码

#include 
#include 
#include 

#include "sqlca.h"


//定义宿主变量(SQL变量)
//C语言可以直接使用
//嵌入式SQL语句里面使用的话需要加上EXEC SQL前缀

EXEC SQL BEGIN DECLARE SECTION;

char * serverid = "scott/lzj123529";

EXEC SQL END DECLARE SECTION;


int main()
{
    int ret = 0;

    printf("HelloWorld\n");

    EXEC SQL connect:serverid;

    if(sqlca.sqlcode != 0)
    {

        ret = sqlca.sqlcode ;
        printf("ret :%d\n",ret);

        return ret;
    }

    printf("connect OK!\n");

    EXEC SQL COMMIT RELEASE;//提交事务并且断开

    return 0;
}

2.交互式的连接数据库

EXEC SQL BEGIN DECLARE SECTION;

char user[32];
char passwd[32];
char sid[32];

EXEC SQL END DECLARE SECTION;

int main()
{
    int ret = 0;
    printf("user:");
    scanf("%s",user);

    printf("passwd:");
    scanf("%s",passwd);

    printf("sid:");
    scanf("%s",sid);

    EXEC SQL CONNECT:user IDENTIFIED BY:passwd USING:sid;

    if(sqlca.sqlcode != 0)
    {

        ret = sqlca.sqlcode ;
        printf("ret :%d\n",ret);

        return ret;
    }

    printf("connect OK!\n");

    EXEC SQL COMMIT RELEASE;//提交事务并且断开

    return 0;
}

3.连接多个数据库

3.1 通过宿主变量指定连接名字

//演示通过程序连接多个数据库
EXEC SQL BEGIN DECLARE SECTION;
    char        *usrname = "scott";
    char        *passwd = "tiger";
    char        *link1 = "link1";  //通过宿主变量指定连接名字
    char        *serverid = "orcl";

    char        *usrname2 = "scott";
    char        *passwd2 = "tiger";
    char        *link2 = "link2";
    char        *serverid2 = "orcl";
EXEC SQL END DECLARE SECTION;

int main()
{
    int ret = 0;    

    //第一个用户连接数据库
    EXEC SQL CONNECT:usrname IDENTIFIED BY:passwd AT:link1  USING:serverid  ;
    if (sqlca.sqlcode != 0)
    {
        ret = sqlca.sqlcode;
        printf("第一个用户连接数据库 失败sqlca.sqlcode: err:%d \n", sqlca.sqlcode);
        return ret;
    }
    else
    {
        printf("第一个用户连接数据库 成功connect ok...\n");
    }

    //第二个用户连接数据库
    EXEC SQL CONNECT:usrname2 IDENTIFIED BY:passwd2 AT:link2  USING:serverid2  ;
    if (sqlca.sqlcode != 0)
    {
        ret = sqlca.sqlcode;
        printf("第二个用户连接数据库 失败sqlca.sqlcode: err:%d \n", sqlca.sqlcode);
        return ret;
    }
    else
    {
        printf("第二个用户连接数据库 成功connect ok...\n");
    }


    //断开连接
    EXEC SQL AT:link1 COMMIT RELEASE;
    if (sqlca.sqlcode != 0)
    {
        ret = sqlca.sqlcode;
        printf("第1个用户断开数据库 失败sqlca.sqlcode: err:%d \n", sqlca.sqlcode);
        return ret;
    }
    else
    {
        printf("第1个用户断开数据库 成功 RELEASE ok...\n");
    }

    EXEC SQL AT:link2 COMMIT RELEASE;
    if (sqlca.sqlcode != 0)
    {
        ret = sqlca.sqlcode;
        printf("第二个用户断开数据库 失败sqlca.sqlcode: err:%d \n", sqlca.sqlcode);
        return ret;
    }
    else
    {
        printf("第二个用户断开数据库 成功 RELEASE ok...\n");
    }
    return ret ;
}

3.2 通过系统分配方式指定连接名字

EXEC SQL BEGIN DECLARE SECTION;
    char        *usrname = "myscott";
    char        *passwd = "22";
    //char      *link1 = "link1";
    char        *serverid = "orcl";

    char        *usrname2 = "myscott";
    char        *passwd2 = "22";
    //char      *link2 = "link2";
    char        *serverid2 = "orcl";
EXEC SQL END DECLARE SECTION;

int main()
{
    int ret = 0;    
    //使用oracle声明连接 EXEC SQL DECLARE link1 DATABASE; 
    EXEC SQL DECLARE link1 DATABASE; 
    EXEC SQL DECLARE link2 DATABASE; 

    //第一个用户连接数据库
    EXEC SQL CONNECT:usrname IDENTIFIED BY:passwd AT link1  USING:serverid  ;
    if (sqlca.sqlcode != 0)
    {
        ret = sqlca.sqlcode;
        printf("第一个用户连接数据库 失败sqlca.sqlcode: err:%d \n", sqlca.sqlcode);
        return ret;
    }
    else
    {
        printf("第一个用户连接数据库 成功connect ok...\n");
    }

    //第二个用户连接数据库
    EXEC SQL CONNECT:usrname2 IDENTIFIED BY:passwd2 AT link2  USING:serverid2  ;
    if (sqlca.sqlcode != 0)
    {
        ret = sqlca.sqlcode;
        printf("第二个用户连接数据库 失败sqlca.sqlcode: err:%d \n", sqlca.sqlcode);
        return ret;
    }
    else
    {
        printf("第二个用户连接数据库 成功connect ok...\n");
    }


    //断开连接
    EXEC SQL AT link1 COMMIT RELEASE;
    if (sqlca.sqlcode != 0)
    {
        ret = sqlca.sqlcode;
        printf("第1个用户断开数据库 失败sqlca.sqlcode: err:%d \n", sqlca.sqlcode);
        return ret;
    }
    else
    {
        printf("第1个用户断开数据库 成功 RELEASE ok...\n");
    }

    EXEC SQL AT link2 COMMIT RELEASE;
    if (sqlca.sqlcode != 0)
    {
        ret = sqlca.sqlcode;
        printf("第2个用户断开数据库 失败sqlca.sqlcode: err:%d \n", sqlca.sqlcode);
        return ret;
    }
    else
    {
        printf("第2个用户断开数据库 成功 RELEASE ok...\n");
    }
    return ret ;
}

宿主变量

C 的数据类型不同于ORACLE的数据类型,在数据传递时有一个数据类型转换的过程。

在Proc*C/C++程序中,能通是被C和SQL语句使用的变量!位于

EXEC SQL BEGIN DECLARE SECTION;
    ... ...
EXEC SQL END DECLARE SECTION;

之内!!

主要类型:

程序中形式 说明
char 单字符
char[n] N个定长字符数组
int 整数
Short 短整数
long 长整数
float 单精度浮点数
double 双精度浮点数
VARCHAR[n] 变长字符串

使用场景:

1.输入 — 将应用程序的数据传递到数据库中。

nt salary, emp_number;
cin>>salary; cin>>emp_number;
EXEC SQL update emp set sal=:salary where empno= :emp_number;

2.输出 — 将数据库的数据传递到应用程序中。

float v_salary;
char v_job;
EXEC SQL select sal,job INTO :v_salary, :v_job from emp where empno = 7788;
cout<

3.申明语法与普通C变量一致,但在CODE=CPP或 MODE=ANSI时变量必须放在申明区.

4.可使用pointer 作为宿主变量,使用前分配空间。

5.在DDL语句中不能用宿主变量。错误例子:

char table_name[30];
cin>>table_name;
EXEC SQL DROP TABLE :table_name;

6.预编译选项 CHAR_MAP

CHAR_MAP=CHARZ (默认设置): ‘\0’结尾,定长,空格补齐。
CHAR_MAP=CHARF | VARCHAR2:定长,空格补齐。
CHAR_MAP=STRING:     ‘\0’结尾,变长。

7.当‘\0’结尾,宿主变量长度要大于实际数据长度。
8.VARCHAR
* 变长, 不是‘\0’结尾。
* 结构体类型

    Struct{
        unsigned short len;
        unsigned char arr[ ]
    }variable_name;

数据类型

1.内部类型

就是在Sql*Plus使用到的数据类型!

Oracle数据就是Oracle数据库内部使用的数据类型:

类型 说明
VARCHAR2 变长字符串,最大4000字节
CHAR 定长字符串,最大2000字节
NUMBER(p,s) 数字类型,p精度,s标度
DATE 日期时间数据,7字节
RAW 变长二进制数据,最大2000字节
LONG 大批量数据,最大2G字节
LONG RAW 大二进制数据,最大2G字节
CLOB 大批量字符数据,最大4G
BLOB 大批量二进制数据,最大4G
BFILE OS文件数据
NCHAR,NVARCHAR2,NCLOB 本地字符集数据
ROWID 伪列——表行物理地址

数据库中的表和伪列使用这些数据类型

2.外部类型

Oracle外部数据类型是宿主程序所引用的数据类型,在运行Pro*C/C++程序的时候,Oracle会根据需要将宿主变量的数据类型映射成Oracle外部数据类型,在编写Pro*C/C++程序的时候不能直接使用Oracle外部数据类型来定义宿主变量。

类型 说明
VARCHAR2 变长字符串
NUMBER 数字值
INTEGER 有符号整数
FLOAT 浮点数
STRING 以NULL终止的变长字符串
VARNUM 数字值,但包含数字长度
LONG 长字符串
VARCHAR 变长字符串
ROWID 二进制值
DATE 日期
VARRAW 变长二进制
RAW 定长二进制
LONG RAW 定长二进制
UNSIGNED 无符号整数
LONG VARCHAR 变长字符串
LONG VARRAW 变长二进制
CHAR 定长字符
CHARZ NULL终止定长字符串
CHARF 等价CHAR的字符数据类型
MLSLABEL 操作系统标记

外部数据类型包括全部的内部数据类型和宿主语言中所提供的几个数据类型

3.宿主类型
宿主变量数据类型也是SQL数据类型

  • Oracle的外部数据类型与宿主变量的自动类型转换

  • Oracle的外部数据类型与宿主变量的人工类型转换

1.

EXEC SQL VAR host_variable IS type_name[(length)];
        char  emp_name[11];
        EXEC SQL VAR emp_name IS STRING(11);

2.用户定义类型等价

EXEC SQL TYPE user_type IS type_name[(length)];
typedef struct {
    short  len;
    char   buff[4000];
} graphics;
EXEC SQL TYPE graphics IS VARRAW(4000);

3.TO_DATE

例子:
typedef char dnameType[20];
typedef char locType[20];

EXEC SQL BEGIN DECLARE SECTION;
    EXEC SQL TYPE dnameType is string(20);
    EXEC SQL TYPE locType is string(20);
    char    *usrname = "myscott";
    char    *passwd = "22";
    char    *serverid = "orcl";

    int             deptno;
    dnameType       dname;
    short           dname_ind;
    locType         loc;
    short           loc_ind;    
EXEC SQL END DECLARE SECTION;

EXEC SQL BEGIN DECLARE SECTION;
    char    *usrname = "scott";
    char    *passwd = "tiger";
    char    *serverid = "orcl";

    int         count;

    int         deptno[100];
    char        dname[100][20];
    char        loc[100][20];

    int         deptno2[100];
    varchar     dname2[100][20]; //varchar类型 和 char 类型的区别
    varchar     loc2[100][20];

EXEC SQL END DECLARE SECTION;

关于数据的增删改查

预先定义了

EXEC SQL BEGIN DECLARE SECTION;

int deptid = 50;
char dname[32] = "20name";
char loc[32] = "20loc";

EXEC SQL END DECLARE SECTION;

1.插入数据

EXEC SQL insert into dept(DEPTNO,DNAME,LOC) values(:deptid,:dname,:loc);

2.删除数据

EXEC SQL delete from dept where deptno=:deptid;

3.修改数据

EXEC SQL update dept set loc = :loc where deptno=:deptid;

综合实例:

#include 
#include 
#include 
#include 
#include "sqlca.h"


//定义宿主变量(SQL变量)
//C语言可以直接使用
//嵌入式SQL语句里面使用的话需要加上EXEC SQL前缀

EXEC SQL BEGIN DECLARE SECTION;

//char * serverid = "scott/lzj123529";

char user[32];
char passwd[32];
char sid[32];

int deptid = 50;
char dname[32] = "20name";
char loc[32] = "20loc";

EXEC SQL END DECLARE SECTION;



int main()
{
    int ret = 0;

    printf("HelloWorld\n");

    printf("\nuser:");
    scanf("%s",user);

    printf("\npasswd:");
    scanf("%s",passwd);


    printf("sid:");
    scanf("%s",sid);

    EXEC SQL CONNECT:user IDENTIFIED BY:passwd USING:sid;

    if(sqlca.sqlcode != 0)
    {

        ret = sqlca.sqlcode ;
        printf("ret :%d\n",ret);

        return ret;
    }

    printf("connect OK!\n");

    EXEC SQL insert into dept(DEPTNO,DNAME,LOC) values(:deptid,:dname,:loc);

    EXEC SQL COMMIT;//提交事务不退出

    sleep(10);

    printf("delete ....\n");
    //EXEC SQL delete from dept where deptno=:deptid;

    strcpy(loc,"中国");
    EXEC SQL update dept set loc = :loc where deptno=:deptid;


    EXEC SQL COMMIT RELEASE;//提交事务并且断开

    return 0;
}

错误处理机制




EXEC SQL WHENEVER  
condition:
    SQLWARNING
    SQLERROR
    NOT FOUND   编译选项    MODE=ORACLE sqlca.sqlcode = 1403 
                编译选项    MODE=ANSI   sqlca.sqlcode = 100
aciton:
    CONTINUE
    DO
    GOTO label_name
    STOP
EXEC SQL WHENEVER SQLERROR GOTO connect_error; 
... 
connect_error: 
    EXEC SQL WHENEVER SQLERROR CONTINUE; 
    EXEC SQL ROLLBACK RELEASE; 
    printf("\nInvalid username/password\n"); 
    exit(1); 

完整示例:

#include 
#include  
#include 

#include "sqlca.h"

//连接数据

//先定义宿主变量 (SQL变量)
EXEC SQL BEGIN  DECLARE SECTION ;
    char * serverid = "scott/tiger2@orcl";
    int     deptid;
    char    DNAME[32];
    char    LOC[32];

EXEC SQL END  DECLARE SECTION ;



//错误SQL语言给打印出来
void sqlerr02()
{
    char    stm[120];
    size_t  sqlfc, stmlen=120;
    unsigned int ret = 0;
    printf("func sqlerr02() begin\n");

    //出错时,可以把错误SQL语言给打印出来
    EXEC SQL WHENEVER SQLERROR CONTINUE;

    ret = sqlgls(stm, &stmlen, &sqlfc);
    /*
    if (ret != 0)
    {
        printf("func sqlgls() err, %d \n", ret);
        return ;
    }*/
    printf("中国\n");

    printf("出错的SQL:%.*s\n", stmlen, stm);
    printf("出错原因:%.*s\n", sqlca.sqlerrm.sqlerrml, sqlca.sqlerrm.sqlerrmc);
    //printf("出错原因:%.70s\n", sqlca.sqlerrm.sqlerrml, sqlca.sqlerrm.sqlerrmc);
    EXEC SQL ROLLBACK WORK RELEASE;
    printf("func sqlerr02() end\n");
    exit(1);
}

//出错原因
void sqlerr()
{
    EXEC SQL WHENEVER SQLERROR CONTINUE; // 下一步
    printf("err reason:%.*s\n", sqlca.sqlerrm.sqlerrml, sqlca.sqlerrm.sqlerrmc);
    //printf("err reason:%.*s\n", sqlca.sqlerrm.sqlerrml, sqlca.sqlerrm.sqlerrmc);
    EXEC SQL ROLLBACK WORK RELEASE;//
    exit(1);
}



int main()
{
    int ret = 0;
    printf("hello....\n");

    printf("serverid:%s \n", serverid);

    deptid = 63;
    strcpy(DNAME, "50name");
    strcpy(LOC, "50loc");


    EXEC SQL WHENEVER SQLERROR DO sqlerr();

    EXEC SQL connect :serverid;
    if (sqlca.sqlcode != 0)
    {
        ret = sqlca.sqlcode ;
        printf("connect err:%d \n", ret);
        return ret;
    }
    else
    {
        printf("connect ok\n");
    }

    EXEC SQL insert into dept(DEPTNO, DNAME, LOC) values(:deptid, :DNAME, :LOC);


    EXEC SQL COMMIT; 


    printf("anter key continue ...delete...\n");
    getchar();
    getchar();

    //EXEC SQL delete from dept where deptno= :deptid;

    printf("anter key continue ...update...\n");
    getchar();
    getchar();

    strcpy(LOC, "50loclolo");
    strcpy(LOC, "中国");

    EXEC SQL update dept set loc= :LOC where deptno= :deptid; 



    EXEC SQL COMMIT RELEASE; //提交事务断开连接
    return ret; 
}


遇到的错误

  1. 找不到头文件stddef.h
$ locate stddef.h

把对应的头文件路径添加到Proc的配置文件(pcscfg.cfg)里面

  1. 看错误代码的含义:
$ oerr ora 错误码

你可能感兴趣的:(数据库)