学习Oracle7的OCI(Oracle Call Interface)时,翻译了一部分Oracle7的OCI官方文档

  

学习Oracle7的OCI(Oracle Call Interface)时,翻译了一部分Oracle7的OCI官方文档

          这是第一开通博客,也是自己第一上传自己写的一些学习资料。刚开始学习Oracle数据库的时候,老师讲了Pro*C连接Oracle数据的方式,但是当时没有听懂,工作大概一两个月后,领导叫我学习Oracle的OCI,并给了我一些学习资料,后来我在Oracle官方网站上下载了从Oracle7到Oracle11的官方文档,开始学习Oracle7的OCI,以Oracle7的OCI官方文档为教材,看那本书的前两遍都看的很吃力,后来边看教材,边看文档中的示例程序,慢慢地就有了感觉,然后领导叫我写一份OCI的学习笔记,我觉得Oracle7的OCI官方文档就是最好的学习资料,就把它翻译成中文作为学习笔记交给领导。接着就开始翻译过程,我只是挑了Oracle7的OCI前四章的自己认为比较重要的部分来翻译,Oracle7的OCI官方文档一共有6章,前三章讲OCI的基础知识,第四章讲C语言环境下OCI,第五章讲COBOL语言环境下OCI,第六章讲FORTRAN语言环境下的OCI。翻译中错误的地方希望大家批评指正,谢谢大家!下面就是我翻译的内容:

OCI 学习(Oracle 7)

1 OCI定义

SQL结构化查询语言是一种非过程语言。用非过程语言编写的程序只说明其所操作的数据对象,但不说明其操作如何进行。SQL的非过程本质使其成为对执行简单数据库事务的易学易用的语言。

然而,第三代编程语言比如C/C++, COBOL和FORTRAN是过程性的。大多数语句的执行依靠前边的或者后边的语句还有循环及分支语句。这些语言的过程性本质使这些语言比SQL更复杂,但这也使它们非常灵活和强大。

OCI是应用程序开发者能同时利用SQL的非过程性能力和第三代语言的过程性能力。也可以利用PL/SQL。这样,可以使应用程序更强大和更灵活。

OCI(Oracle Call Interface, 即Oracle调用层接口)是Oracle公司提供的由头文件和库函数等组成的一个访问Oracle数据库的应用程序编程接口(application programming interface API),它允许开发人员在第三代编程语言(包括C,C++,COBOL与FORTRAN)中通过SQL(Structure Query Language)来操纵Oracle数据库。如图1所示编译和链接OCI程序同编译、链接非数据库应用程序的方式是一样的。无需单独的预处理或预编译过程。

 

 

   OCI由一组应用程序开发接口(API)组成的,ORACLE提供API的方式是提供一组库。这组库包含一系列的函数调用。这组函数包含了连接数据库、调用SQL和事务控制等。在安装DBMS SERVER或者客户端的时候,就安装了OCI。

OCI开发方法实际上是将结构化查询语言(SQL)和第三代程序设计语言相结合的一种方法。对数据库的访问是通过调用OCI库函数实现的,若将C语言作为宿主语言,那么ORACLE数据库调用其实就是C程序中的函数调用,一个含OCI调用的C程序其实就是用C语言编写的应用程序。这样的程序既具有SQL语言非过程性的优点又具有C语言过程性的优点,同时还可具有SQL语言的扩展,PL/SQL语言过程性和结构性的优点,因此使得开发出的应用程序具有高度灵活性。

2 编写OCI程序

2.1基本的程序结构

   我们编写OCI程序时按照一定的步骤能确保我们的程序运转正常。步骤如下:

1.    定义链接数据库和处理游标的数据结构。

2.    连接到一个或多个Oracle数据库。

3.    按程序需要打开一个或多个游标来处理SQL或者PL/SQL语句。

4.    对执行应用程序任务的SQL或PL/SQL语句进行处理。

5.    关闭游标。

6.    断开服务器连接。

2.2 OCI数据结构

在一个OCI应用程序中我们需要定义连接数据库和处理SQL及PL/SQL语句的数据结构。用于连接数据库的数据结构是登录数据区域LDA(logon data area)和HDA(host data area).为程序需要的每一个并发连接声明一对LDA-HAD,并且在连接数据库的olog( )函数中传递它们的引用。

为处理SQL或者PL/SQL语句,我们要定义一个游标(cursor)。使用游标数据区域cursor data area(CDA)和oopen()函数来定义一个游标。每一个并发的处于活动状态的游标需要一个单独的游标数据区域。在我们关闭一个游标之后,我们可以用这个曾经与一个旧游标连接的游标数据区域连接到一个新游标。

登录数据区域和游标数据区域都有一个叫做返回代码的字段(return code field)。这个字段保存一个二进制的16位数值。如果执行一个引用了登录数据区域LDA和游标数据区域CDA的OCI函数没有错误发生,则这个返回代码为0。否则,这个返回代码字段将保存Oracle错误代码。在Oracle7 Server Messages中有错误代码和与之联系的错误信息的列表。在程序中,我们可以调用oerhms()函数来获得某个错误返回代码对应的错误信息。

2.2.1登录数据区域LDA

       登录数据区域LDA是通过olog()函数与一个活动的Oracle连接联合的数据结构。对于32位的系统,登录数据区域LDA的格式如下图所示:

登录数据区域LDA的字段的长度和字节偏移量依赖于系统。但是,所有的字段都存在于任何系统上。C程序员可以使用ocidfn.h中登录数据区域LDA的定义。在登录数据区域LDA中最常用的字段是 return code。在登录数据区域LDA中命名的字段同游标数据区域CDA中对应的字段相同。在新的OCI程序中,不要通过检查V2返回代码字段(V2 return code)来获取错误信息。这个字段仅仅可以向后兼容。

注意:在设立连接之后,不能改变登录数据区域LDA或host data area(HAD) 的地址。在处理OCI调用时,Oracle服务器使用这些数据区域的地址,在连接的生命期内它们的地址必须保持不变。

2.2.2 Host Data Area(HDA)

       在程序中当我们连接Oracle服务器时,必须分配一个host data area(HDA),它通常是256字节。我们要为每一个同时发生的Oracle连接分配一个登录数据区域LDA和host data area(HDA),当我们使用olog()函数来登录Oracle数据库时,这些数据区域所属的连接会将这两个数据区域的地址传给数据库。

   在32位的操作系统上,HAD通常是256字节。在64位的操作系统上,HAD通常是512字节。如果存储空间允许,即使在32位的操作系统上也可能分配512字节的HAD。

   注意:在HAD被使用前,它必须被正确的声明和初始化。在第一次调用olog前,HDA的所有位都必须被初始化为0(二进制的0,而非字符’0’),否则将会发生运行时错误。

2.2.3 游标数据区域CDA

游标数据区域为我们程序中的用户游标和服务器中SQL语句代表建立一个映射。有关SQL和PL/SQL语句的信息保存在系统全局区(SGA)和私有的SQL区域。当Oracle处理我们程序中的SQL语句时,它将会更新游标数据区域CDA中的字段来显示语句处理的进展和状态。

如图2-2显示了一个32位系统上的游标数据区域CDA的结构。游标数据区域CDA中的各个字段的长度和字段的偏移量依赖于系统。但是,所有的字段存在于任何系统中。游标数据区域CDA总是64字节,但是检查Oracle系统说明文档获取准确的游标数据区域CDA配置。

2.2.3.1 V2 Return Code(Oracle version2 返回代码)

在C语言中,OCI调用返回一个2字节的2进制整数。如果V2 return code 为0,则没有错误发生,非0则有错误发生。现在这个返回代码与Oracle V2(version2) Return Code相同。V2 Return Code字段含有相同的数字。

       注意:这个字段仅仅向后兼容。它可能不被将来的OCI版本兼容。在新的程序中应使用return code。

2.2.3.2 SQL Function Code(SQL函数代码)

SQL函数代码是Oracle内部使用的一个2字节的二进制整数。对每一个SQL命令都有一个SQL函数代码。SQL函数代码在OCI版本间经常变化。它们如表2-1所示。

注意:SQL函数代码直到解析执行后才有效。除非解析已经被延迟了,这将发生在调用oparse()函数的时候。在延迟的情况下,这将发生在 下一个描述或执行调用中。

2.2.3.3 Rows Processed Count(被处理行数计数)

这个字段包含一个4字节的二进制整数,它计算被一个SQL语句处理的行数。这个数量说明被一个数据操作语句插入、更新或删除的行数之和以及一个查询结果集的被捕获的行数。

Rows processed count字段只有在经过oexec(), oexn(), oexfet(), ofen() 或者ofetch() 调用后才为有效。对于查询,当oexec()或者oexn()被调用后它被重置为0并且在ofetch()或ofen()调用后递增。对于oexfet(), 在调用的执行部分数值被重置为0,,当捕获结束后进行设置。

注意:如果一个查询返回的行数超过4字节整数的计数范围,rows processed 字段的内存是未定义的。另外,由于Oracle执行产生很大数量行数的查询非常耗费时间,我们要避免进行这种查询。

2.2.3.4 Parse Error Offset(解析错误偏移量)

这个字段包含一个2字节的二进制整数。用以说明当一个解析错误被侦测到时,这个解析错误在此SQL语句中的首字节的位置。如果SQL语句的长度超过64k 字节,解析错误偏移量为未定义。

解析错误由多种原因引起。其中由语句中的语法错误、安全错误或者不存在的表或字段。只有在调用oparse()后,解析错误偏移量才具有有效值。其他的OCI函数可能会使此字段产生一个数值,但其数值没有意义。

注意:解析错误偏移量(parse error offset)字段只有解析执行后才有效。在解析被延迟的情况下,其在随后的描述或只执行调用后成为有效。在解析没有被延迟的情况下,调用oparse()就成为有效。

2.2.3.5 OCI Function Code(OCI函数代码)

OCI函数代码字段包含一个1字节二进制整数,其说明最近完成的OCI函数。对于每一个使用游标数据区域CDA的OCI函数,有一个函数代码与之对应。只引用登录数据区域LDA的函数(比如olog() ),没有函数代码。

表2-2列出了使用油表数据区域CDA的OCI函数代码。没有列出OCI函数的代码为不使用的代码。

       2.2.3.6 Return Code(返回代码)

       返回代码是一个2字节的正的二进制整数,其包含最近执行的语句的Oracle错误代码。错误代码和信息列示在Oracle7 Server Messages一书中。使用oerhms()函数与返回代码联结的错误信息。

       2.2.3.7 Warning Flags(警告标志)

       警告标志(Warning Flags)包含位警告标志。可以设置多位。下表列出了位标志。

      

       2.2.3.8 Oracle ROWID

       这个字段包含ROWID,并且是以Oracle的内部二进制格式(等价于外部数据类型11)。只有在INSERT, UPDATE, DELETE和SELECT FOR UPDATE操作后才有效。在不包含 FOR UPDATE子句的SELECT之后不能成为有效。

       如果 执行多行操作,Oracle ROWID字段被设置为最后被操作的行。

       注意:对于使用Oracle Open Gateway连接到非Oracle数据库的OCI程序,Oracle ROWID字段是未定义的。这些程序必须在SELECT语句中直接使用ROWID伪字段,并在其后在INSERT, UPDATE和DELETE语句中使用返回的ROWID来识别非Oracle数据库中的指定行。Oracle ROWID字段的大小是依赖于系统的。

       2.2.3.9 OSD Error Code(操作系统依赖错误代码)

       这个字段包含与一个Oracle错误代码相连接的system-dependent(OSD)错误代码。比如,如果Oracle7尝试执行硬盘I/O时获得一个错误,则OSD error code被设置成operating system-dependent I/O系统错误代码。

       2.3 处理SQL语句的步骤

       当我们编写OCI代码去处理一个SQL语句的时候,有几个不同的步骤我们必须完成。一些SQL语句只要一步就行,但是其他就需要4步或者5步。分段操作(Piecewise operations)还需要额外的步骤。

       当我们连接到一个Oracle并且打开了游标,处理SQL语句的步骤如下:

       1. 使用oparse()函数来解析语句。如果程序以非延迟模式连接或者以延迟模式连接且oparse()函数的参数defflg为0,可以使用oparse()函数直接执行不接受输入值或返回结果的数据定义语句(DDL)。不需要进一步的处理。事务控制语句必须在解析后执行。

 

       2.对数据操作语句(DML)和查询(queries),调用obndra(), obndrv(), obndrn()或者obindps()函数来绑定各个输入变量(或者PL/SQL输出变量)或数组与语句中的各个占位符绑定到一起。只有在延迟模式并且有进行分段操作的必要或者使用结构数组时,Obindps()函数才有效。

       3.对于查询,使用odescr()函数来描述其select-list items(选择项目)。这是一个可选步骤;如果在编译时已经知道选择项目的数目和各项的属性(比如它的长度和数据类型)则不需要此步骤。如果解析采用延迟方式,则它在查询被描述时执行。如果被处理的SQL语句不是一个查询,则odescr()函数将产生一个错误。

       4.对于查询,调用odefin()函数或odefinps()函数为SQL语句中的各个select-list item(查询项目)定义一个输出变量。在匿名的PL/SQL语句块中,我们须使用obndrv()函数或者obndra()函数,而不能使用odefin()函数或odefinps()函数。只有在延迟方式并且进行分段操作或者使用结构数组时Odefinps()函数才有效。

       5.对于DML和事务控制语句,调用oexn()函数去执行语句。如果解析是延迟方式,它将在此时执行。

       6.对于查询,调用oexfet()函数或者一起调用oexec()函数与ofetch()函数来执行语句并获取满足语句的行。如果oexfet()函数不能获取结果集中所有的行,就需要调用ofen()函数,可能还需要调用多次ofen()函数才能获取剩下的行。如果解析是延迟方式,并且没有调用odescr()函数,则解析在此时进行。

       跟随这些步骤,应用程序可以关闭游标并断开Oracle的连接。

       2.4 延迟语句执行

       在Oracle7 Server, Release 7.0之前,每一个OCI函数调用都需要一个相应的对服务器的调用。例如,当我们调用osql3()函数(osql3()函数是旧的OCI函数,它用来解析SQL语句,Oracle7中代替osql3()函数的是oparse(),下表列出了新旧OCI函数的对应关系) 来解析SQL语句时,SQL语句的文本被传送到服务器,而且语句被解析和储存在进程全局区(process global area )PGA中。如果SQL语句中有占位符,与占位符绑定的输入变量的地址也传送至服务器。对于每一个输入变量都需要调用一次服务器。查询需要额外的服务器调用来定义保存select-list item(所选字段)的程序变量(program variables)的地址。最终,当SQL语句被执行的时候,Oracle需要获取所有输入变量的值,对于查询语句则需要返回结果。这有需要一次服务器调用。

       当OCI应用程序和Oracle服务器运行在同一台机器上时,对Oracle服务器的多次调用对性能只有轻微的影响。在网络的客户/服务性环境下,OCI程序运行在一个客户机上而应用程序正在连接的Oracle服务器运行在另外的机器上(有可能距离数千公里),分散而单独的数据库服务器调用会降低性能。

       为了提高性能,OCI和Oracle7现在允许我们延迟SQL语句处理中的一个或多个步骤。

例如,我们可以将解析SQL语句的步骤和绑定输入变量和定义输出变量的步骤延迟至SQL语句实际执行的时候。

       如果没有调用odescr()函数,一个完整的查询和结果获取可以在一个服务器调用中完成而只需一个单独的网络往返。

       2.4.1 控制延迟执行

       两个因素控制解析、绑定和定义步骤的延迟:

1. 我们连接OCI程序的方式

2. 我们如何设置oparse()函数中的defflg参数

       2.4.1.1 延迟模式连接Deferred Mode Linking

       当我们使用延迟模式连接OCI程序并且我们的OCI程序连接至Oracle 7 Server时,绑定(bind)和定义(define)步骤总是被延迟至语句执行时进行。这种行为不依赖于我们的应用程序所选择的特定的OCI函数,而只依赖于我们选择的连接选项。例如,如果我们使用延迟模式连接选项重新连接现存的Version 6的OCI程序,绑定(bind)和定义(define)将被延迟(但解析不一定延迟)。

       延迟模式连接选择新的Oracle 7 OCI库将绑定(bind)和定义(define)变量使用动态分配内存的方式保存在客户系统上,知道其信息被Oracle获取去处理一个SQL语句。这种方法需要客户系统中额外的内存。

       延迟模式连接是默认方式。

       2.4.1.2 延迟解析步骤 Deferring the Parse Step

       当我们用延迟模式连接我们的程序,并且使用Oracle 7的oparse()函数解析SQL语句,并且oparse()函数的defflg参数被设置为非零值时,解析步骤也被延迟。这样我们可以将所有的OCI操作延迟至SQL语句实际执行时。Version 6的OCI程序必须使用oparse()函数(代替osql3()函数)重写以获得延迟的解析行为。

       注意:如果我们使用odescr()函数来获取一个查询的select-list item(所选字段),任何未决的绑定(bind)和定义(define)调用信息将被立即传送至Oracle。如需解析就执行解析,然后执行描述操作。

       2.4.1.3 非延迟连接 Non-Deferred Linking

       非延迟连接所产生的行为与Version 6的OCI应用程序的行为相同。Version 6程序可以使用非延迟连接,无需改动即可与Oracle 7 服务器连接。

       延迟模式连接需要在客户系统中占用额外的内存以缓存调用数据。如果客户系统上的内存资源比较稀有,则我们可能需要使用非延迟模式连接。

       新的Release 7.3 中的为绑定(bind)和定义(define)变量的obindps()函数和odefinps()函数在非延迟模式下是无效的。想利用这两个函数提供的分段操作和结构数组的功能,就必须使用延迟模式连接。

       2.4.1.4 比较延迟连接模式与非延迟连接

       下面的图表显示了解析、绑定、定义和执行/获取 的OCI调用如何以延迟模式连接和费延迟模式连接同Oracle通信。

       图表中的数字说明特定的函数调用的顺序。图2-4中的空心圆表明在执行SQL语句前的OCI调用(如图2-3所示)被延迟(不被发送至服务器)至步骤4。

       在此例中,oparse()函数、obndra()函数和odefin()函数调用被延迟至执行(如oexec()函数)或者描述(odescr()函数)调用被执行的时候。

 

 

          图2-3  语句处理延迟解析(执行前) oparse()函数、obndra()函数和odefin()函数已经被调用但延迟至oexfet()函数、odescr()函数、oexec()函数和oexn()函数调用的时候

                图2-4 语句处理延迟解析(执行后) 客户系统上有关绑定和定义变量的数据及oparse()函数相关数据被传送至Oracle服务器。

 

 

       图2-5 当解析不是延迟模式时,当做出每个OCI调用时错误代码都会立即返回。但是当解析被延迟时,解析、绑定和定义步骤的错误指导描述或者执行调用被执行时才被返回。

       2.5 开发一个OCI程序

           开发一个OCI程序的步骤

            1. 定义OCI数据结构

               2. 连接至Oracle服务器

               3. 打开游标

               4. 解析语句

               5. 绑定输入变量的地址

              6. 描述所选字段(select-list item)

               7. 执行语句

               8. 定义所选字段(select-list item)

               9. 获取查询的行

               10. 关闭游标

               11. 提交事务或者回滚事务

               12. 与Oracle断开连接

2.5.1 定义OCI数据结构

        在连接至Oracle之前,我们必须定义至少一个登录数据区域LDA。如果程序需要多个同时连接,则需为每个同时连接定义一个LDA。定义这些数据区域的方式依赖于我们使用的语言。我们必须为每个LDA定义一个HDA。每个数据库连接必须有一个HDA/LDA对。

       为了处理SQL语句,我们需要定义一个或多个游标数据区域CDAs,每一个活动状态下的SQL语句都必须有一个CDA。如果我们的程序以连续的方式处理SQL语句,我们只需要一个CDA。CDA的定义方式同LDA的定义方式相同。

       2.5.2连接至Oracle服务器

       通过调用olog()函数,我们的程序同一个或者多个Oracle数据库建立连接。为了连接至Oracle,首先需要在我们的程序中定义一个LDA和HAD。同Oracle的通信建立在连接时;它使用我们定义的LDA和HAD发生。

       如果我们的程序在一个时刻只需要一个数据库连接,我们可以交替使用olog()函数和ologof()函数来设立和断开连接。

       使用olog()函数可以设立任意数量的同步连接。

       连接的限制Restrictions on Connections

       当我们使用single-task driver方式连接一个程序时,它只能在一个时间连接一个数据库,并且这个数据库时隐式的默认数据库。如果想连接至其他的数据库,则必须在olog()函数中显式的引用。如果需要对于同一数据库的额外连接,则必须使用olog()函数。

       有阻塞(blocking)和非阻塞(non-blocking)两种通信方式用于连接至Oracle数据库。使用阻塞方式,一个OCI调用不论其成功还是失败,它只有在完成后才返回。使用非阻塞模式,如果OCI调用不能够完成(with ORA-03123 message),控制将立即返回至OCI程序。在这种情况下,OCI客户可以等待再次尝试OCI调用的同时继续去处理其他语句。

       2.5.3 打开游标Open the Cursors

       处理一个SQL语句,我们需要一个打开的游标。通过调用oopen()函数,我们在Oracle维护的代表一个有效游标的数据结构和我们程序中的CDA形成联结。因为一个有效的LDA是oopen()函数的必需参数,必须在连接至Oracle后执行此步骤。在CDA可以被用来解析一个SQL语句前,我们必需执行oopen()函数。

       在一个OCI应用程序中每个打开的游标联结着一个特定的服务器/数据库。如果一个OCI程序连接至不止一个数据库,则在此程序中可以同时打开的游标总和为两个数据库的OPEN_CURSORS参数之和。例如,如果一个OCI客户程序连接至 db1,其OPEN_CURSORS为50,和db2,其OPEN_CURSORS参数为100,则此OCI客户程序最多可有150打开的游标。也要记住,不能有超过50游标连接至db1,也不能由100游标连接至db2。

       我们可以使用游标来重复执行一个SQL语句或者执行一个新的SQL语句。当一个游标被重用时,在一个新的SQL语句被解析时,我们程序中与游标对应的CDA的内容会被重置。在再次使用一个游标前没有必要去关闭和重新打开一个游标。

       2.5.4 解析语句 Parse the Statement

       每个SQL语句必须用oparse()函数来解析。

       如果我们以非延迟模式连接或者以延迟模式连接但是oparse()函数的defflg参数为0,数据定义语言DDL语句在解析时执行。如果我们以延迟模式连接并且defflg参数为非0,我们必须调用oexn()函数或者oexec()函数来执行语句。

       Oracle建议我们尽可能地使用延迟模式。这会提高性能,特别是在网络环境下。当oparse()函数在以非延迟模式下调用时检测到的SQL语句中的错误在延迟模式下时只有在第一次非延迟调用发生时才能检测到(通常是一个执行或者描述调用)。

       所有的DML语句,PL/SQL块,和查询需要在解析步骤后进行,进一步的处理。

       2.5.5 绑定输入变量的地址Bind the Addresses of Input Variables

       大多数DML语句和一些查询(比如那些具有where子句的),需要程序中的数据作为SQL和PL/SQL语句的一部分传送给Oracle。

       这些数据可以是在编译时就已经知道的常量。例如,如下的SQL语句

       INSERT INTO wine_list (name, type, year, bin_no) VALUES

                     (‘Joseph Swan Vineyard’, ‘ZINFANDEL’, NULL, 112)

       包含有几个常量,比如 ‘ZINFANDEL’ 和112。

       这种语句的局限性比较强。我们不能每次往酒窖中添加一瓶新酒时都改变程序并重新编译。作为替代,我们可以写程序以使输入数据在运行时提供。

       2.5.5.1 占位符 Placeholders

       当我们定义一个包含有运行时提供的输入变量的SQL语句或者PL/SQL语句块时,在SQL语句或者PL/SQL语句块中的占位符就标记着所需提供的数据的位置。例如,如下SQL语句

       INSERT INTO wine_list (name, type, year, bin_no) VALUES

                     (:Name, :Type, :Year, :Bin_Number)

       包含有四个占位符,由第一位的冒号提示,这些占位符就显示了由程序提供的输入变量的位置。

       如下的PL/SQL块包含有两个占位符:

       Char plsql_statement[] = “BEGIN\

                                              RAISE_SALARY(:EMP_NUMBER, :NEW_SAL);\

                                            END;”;

       我们可以在DELETE, INSERT, SELECT 或者 UPDATE语句及PL/SQL语句块中可以使用表达式或常量的位置使用占位符。

       注意:占位符不能用来命名其他的Oracle对象,比如表或字段。

       对SQL语句或PL/SQL语句块中的每个占位符,我们都要调用一个OCI函数来将我们程序中的一个变量绑定至占位符。这样当SQL语句执行时,Oracle就能获得我们程序所绑定变量的数据。

       当我们执行绑定步骤时,数据不一定必须已经在所绑定的变量中。在绑定步骤时,我们仅仅是告诉Oracle绑定变量的地址、数据类型和变量长度。但是,必须确保在执行SQL语句或者PL/SQL语句块时绑定变量包含有效值。

       注意:如果我们仅仅改变了绑定变量的值,为了再次执行SQL语句,则没有必要进行重新绑定。绑定是引用绑定,所以只要绑定变量的地址保持有效,可以不经重新绑定而重新执行引用该绑定变量的语句。

2.5.5.2    绑定地址的函数 Routines that Bind Addresses

有四个函数可以用来绑定地址至占位符:obndrv(), obndrn(), obndra()和obindps()。

Obndrv()函数

当我们使用obndrv()函数时,我们必须说明占位符的名字。在上面的SQL语句中,我

们指定”:Year”作为年值的占位符的名字。

       Obndrv()函数可以用在用户将在运行时提供数据的交互式应用程序中。在这种情况下,我们的程序必须浏览SQL语句来获取占位符的名字。

       Obndrv()函数的占位符的名字不能为保留词。例如,如下的SQL语句是不合法的,原因是ROWID是一个保留词。

       SELECT ename FROM emp WHERE rowed = :ROWID;

       Obndrn()函数

       为使用obndrn()函数,每个占位符都必须以 :N的形式提供,其中N为1到255之间的常量整数。如下SQL语句就可作为一个有效的SQL语句提供给obndrv()函数。

       SELECT ename, sal FROM emp

                     WHERE (job = :1 AND sal > :2)

                     OR

                     ( job != :1 AND sal < :2 )

       在此语句中有四个占位符的实例,但实际上只有两个占位符。这样只需要两个绑定变量。为此SQL语句,我们只需要调用两次obndrn()函数。一个占位符在一个SQL语句中的所有出现都在一个绑定函数中绑定。

       注意:我们必须使用obndra()函数或者obndrv()函数来绑定变量到PL/SQL语句块,而不能使用obndrn()函数。

       Obndra()函数

       Obndra()函数绑定我们程序中的数组的地址到SQL语句或者PL/SQL块中的占位符。Obndra()函数类似于obndrv()函数,但是它提供了用于说明数组最大长度的参数、返回的数组元素的数量和长度,并且返回的错误以字段为基础。

       Obindps()函数

       Obindps()函数提供了obndra()函数和obndrn()函数的大多数功能。Obindps()函数的opcode参数标志一个应用程序将提供运行时的递增的插入或者更新数据。当应用程序将插入储存在结构数组中的数据时也可以使用obindps()函数。

       只有应用程序以延迟方式连接并且连接至Oracle Server release 7.3及以后版本才能获得支持。

2.5.6描述所选项 Describe Select-List Items

       如果SQL语句为一个查询语句,我们可能想要获取更多的有关所选项的信息。这对动态查询(在运行时得到查询语句的内容)来说是十分有用的。在这种情况下,程序没有关于所选项的数据类型、字段长度等预知信息。

       例如,一个用户可能输入这样一个查询

       SELECT * FROM wine_list

       而程序没有关于WINE_LIST的预知信息。

       我们可以使用odescr()函数来获得这些信息。Odescr()函数返回关于第n项所选项的信息,其中n是一个输入参数(其值会传送至Oracle)。我们可以使用这些信息来决定如何转换、显示或者储存那些执行查询所获取的行的数据。

       在一个循环中调用odescr()函数来处理动态的选项。在一个循环的开始处设置一个数值为1的索引变量,然后递增此变量,重复执行odescr()函数,直到在CDA的返回代码字段中有“所选项中无变量”的错误(ORA-01007)返回。如下的C语言代码片段说明了此过程。

       For (pos =1; pos < = NPOS; pos++ ){

              cbuf1[pos] = sizeof cbuf[pos];

              if ( odescr(&cda, pos, &dbsize[pos], &dbtype[pos],

&cbuf[pos], &cbuf1[pos], &dsize[pos],

&prec[pos], &scale[pos], &nullok[pos]) ){

                            If (cda.rc = = 1007 )

                            break;

                            oci_error( );

                            continue( );

                     }

       }

2.5.7执行语句 Execute the Statement

       如果SQL语句为一个DML语句,则我们必须执行该语句。执行操作将把所有的绑定变量的值传送至Oracle。

       传送数据至Oracle有不同的方式。我们可以使用oexec()函数重复地执行一个SQL语句并在每次重复时提供不同的输入值。我们也可以使用Oracle的数组接口并通过使用oexn()函数对一个SQL语句输入很多数值。

       Oracle的数组接口显著降低了当我们需要更新或者插入大量数据时同Oracle的通信量。这可以产生大幅度的性能提升,尤其在一个客户/服务器环境中。例如,一个应用程序需要向数据库中插入10行数值时。需要用不同的数值调用十次oexec()函数产生10个网络往返。而调用一个oexn()函数就可完成,并且只有一个网络往返。

       2.5.8定义所选项 Define Select-List Items

       对于查询,我们需要使用odefin()函数和odefinps()函数来联结我们程序中的输出变量的地址同查询中的每个所选项。如果我们预先不知道所选项的数目,例如在

       SELECT * FROM wine_list

中,我们可以先重复调用odescr()函数来确定所选项的数目。我们也可以在同一个循环中调用odescr()函数和odefin()函数或odefinps()函数,当odescr()函数返回”变量不在所选项”中的错误时,退出循环。

       我们可以再次调用odefin()函数或者odefinps()函数来重新定义输出变量而无需重新解析或者重新执行SQL语句。

       2.5.9 获取查询的行 Fetch the Rows for the Query

       在我们定义输出变量的地址之后,我们可以通过调用以下函数来获取满足查询的行

       Ofetch()函数可以获取一单行。在调用ofetch()函数之前,我们必须调用oexec()函数来执行语句。我们也可以在一个循环中调用ofetch()函数来获取多行,但是其效率不如选择至数组的oexfet()函数或者ofen()函数。

       Ofen()函数,在一次调用中获取一单行或者多行至数组中。但是,我们必须首先通过调用oexec()函数来执行语句。

       Oexfet()函数,联合oexec()函数和ofen()/ofetch()函数的功能至一个语句中。Oexfet()函数可以执行语句并且获取一行或者多行数据到我们程序中的数组变量,并且可以选择在获取结束后释放游标资源。我们可以将延迟解析(使用oparse()函数)、延迟绑定、定义和oexfet()函数联合在一起来执行一个完整的查询操作,在对Oracle服务器的一次调用可以完成语句解析到游标释放。

       如果我们计划使用oexfet()函数和ofen()函数来获取多行,我们必须确保我们为所选项所确定的输出变量是数组。如果我们使用数组接口,有一些可选的输出参数也必须为数组。例如,指示器变量和字段返回代码也必须为数组。

       2.5.10关闭游标 Close the Cursors

       在我们的程序推出前,需要调用oclose()函数来关闭每个打开的游标。当一个游标被关闭后,其CDA不再与Oracle服务器相联结,在服务器上被该游标使用的内存资源将被释放。

       如果有必要对一个已经被我们的应用程序打开并且执行过SQL语句或者PL/SQL语句块的游标调用oopen()函数,必须确保在调用oopen()函数之前调用oclose()函数来关闭游标。

       2.5.11提交或者回滚 Commit or Rollback

       使用ologof()函数与oracle服务器断开连接会引起一个隐式的提交。我们可以使用ocom()函数来强制提交。我们可以使用orol()函数来回滚事务。

       注意:如果一个应用程序以调用ologof()函数之外的方式同Oracle断开连接(例如,网络中断),并且没有调用ocom()函数,则是事务自动回滚。

       2.5.12 与服务器断开连接 Disconnect from Oracle

       在程序退出前调用ologof()函数关闭与Oracle的连接。为一个olog()调用所引用的LDA调用ologof()函数。

2.6编码规则 Coding Rules

       2.6.1指示器变量 Indicator Variables

       绑定和定义OCI调用(obndra()函数、obndrv()函数、obndrn()函数、obindps()函数、odefinps()函数和odefin()函数)都有一个允许我们连接一个指示器变量或者一个指示器变量数组(如果我们使用数组的话)到DML语句、PL/SQL语句或者一个查询(query)的参数。

       由于数组语言没有NULL的概念,我们需要联结指示器变量与输入变量来说明与输入变量相关联的占位符是否可以为NULL空值。

       对于输出变量,指示器变量用来说明Oracle返回的值是否为NULL或者为一个被截短的值。

       对输入的宿主变量,OCI程序可以赋给指示器变量的值由如下含义:

       -1           Oracle赋给字段空值,忽略输入变量的值。

       >=0     Oracle将输入变量的值赋给字段

       在输出方面,Oracle可以赋给指示器变量的值有如下含义

       -2      所选项的长度大于输出变量的长度,所选项已经被截短。另外,原始长度大于

                     可以返回的最大的数据长度,最大的的数据长度为unsigned short integer(通常

为2的16次方-1)。

      -1         被选值为NULL,并且输出变量的值没有被修改。

       0            Oracle将完整无缺的值赋给宿主变量。

       >0      所选项的长度大于输出变量的长度;项目已经被截短。指示器变量中所返回

的正整数为截短前的实际长度。

       2.6.2空值NULLS

       我们可以以多种方式向一个数据库字段中插入空值NULL。一种方法是在一个INSERT或者UPDATE语句中使用NULL字符串。例如,如下SQL语句

       INSERT INTO emp (ename, empno, deptno)

                     VALUES (NULL, 8010, 20)

将使ename字段为NULL。

       另一种方法就是在OCI绑定调用中使用指示器变量。

       2.6.3 取消调用 Canceling Calls

       当我们使用操作系统终端来取消长时间运行的或者重复的调用是,错误代码为ORA-01013(“用户请求取消当前操作”)

       一旦得到了我们所需要数量的查询,我们可以使用ocan()函数来取消一个查询。

       如果OCI程序想用诸如定时器的工具来取消一个长时间运行的函数调用,我们能够使用obreak()函数。

       2.6.4最大数组大小 Maximum Array Size

       一个数组的最大长度是32512项。这意味着oexn()函数、ofen()函数或者oexfet()函数的iters或者nrows参数不能超过这个限制,不论这些参数的数据类型是什么。

       2.7线程安全性

       2.8分段插入、更新和获取

       2.9结构数组

       3. 数据类型

       这是我们对odefin()函数、obndra()函数、obndrv()函数和obndrn()函数所需的外部数据类型代码和当我们的程序与Oracle传输数据进行数据转换时的主要参考。

       3.1Oracle数据类型

       当我们在一个Oracle表中修改或者插入数据时,还有当我们用一个Query查询语句从Oracle中获取数据时,数据在我们的程序和Oracle表中的字段进行传输。Oracle内部以多种格式表示数据。其中有number, char, date和raw。

       我们的OCI程序将数据保存在我们所使用的语言预先定义的数据类型中。当我们的程序和Oracle交换数据时,我们需要去说明我们程序中数据的格式。例如,如果你将要处理如下SQL语句

       SELECT sal FROM emp WHERE empno = :employee_number

并且我们想要让薪水以字符数值返回,就需要在odefin()函数的ftype参数中指明一个Oracle外部字符串数据类型,比如varchar2(code = 1)或者char(code = 96)。我们也需要在程序中声明一个字符串常量,并在buf参数中指明它的地址。

       如果我们想要让薪水作为二进制浮点值返回,就需要指明float(code=4)的外部数据类型。我们也要为buf参数定义一个正确类型的变量。

       在绑定和定义函数中,我们必须使用恰当的外部数据类型代码。我们也必须要告诉Oracle我们程序变量中的输入变量和输出变量在什么地方以及他们的数据类型和长度。

       3.2内部数据类型代码

       Oracle内部表示数据的方式以内部数据类型代码的形式传送至我们的应用程序。例如,如果我们不知道一个查询所返回的数据类型,可以调用odescr()函数,其为每一个所选项返回一个内部数据类型代码。内部数据类型对查询非常重要,因为程序用它们来决定如何转换和格式化输出数据。

       3.3外部数据类型代码

       外部数据类型代码告诉Oracle,我们程序中的宿主变量如何表示数据。这决定了当数据被返回到我们程序中的输出变量是数据如何转换还有输入变量如何转换成Oracle字段值。例如,如果我们想要将一个Oracle字段中的number转换成变长数组时,我们需要在定义输出变量的odefin()函数中指明varchar2外部数据类型(1)。

       将一个绑定变量转换成一个Oracle字段中的值时,需要指明与绑定变量的数据类型相对应的外部数据类型代码。例如,如果我们想要将”25-JAN-64”转换成一个date字段,就需要将数据类型指定为character string(1),并且将长度参数设置为9。

       程序员负责确保数值是可以转换的。如果我们尝试将字符串”MY BIRTHDAY”转换成一个date字段,当执行这个语句时,我们将会得到一个错误。

       在oci目录下的include/ocidfn.h头文件中有关于外部数据类型代码的宏,分别是

/* input data types */

#define SQLT_CHR  1                        /* (ORANET TYPE) character string */

#define SQLT_NUM  2                          /* (ORANET TYPE) oracle numeric */

#define SQLT_INT  3                                 /* (ORANET TYPE) integer */

#define SQLT_FLT  4                   /* (ORANET TYPE) Floating point number */

#define SQLT_STR  5                                /* zero terminated string */

#define SQLT_VNU  6                        /* NUM with preceding length byte */

#define SQLT_PDN  7                  /* (ORANET TYPE) Packed Decimal Numeric */

#define SQLT_LNG  8                                                  /* long */

#define SQLT_VCS  9                             /* Variable character string */

#define SQLT_NON  10                      /* Null/empty PCC Descriptor entry */

#define SQLT_RID  11                                                /* rowid */

#define SQLT_DAT  12                                /* date in oracle format */

#define SQLT_VBI  15                                 /* binary in VCS format */

#define SQLT_BIN  23                                  /* binary data(DTYBIN) */

#define SQLT_LBI  24                                          /* long binary */

#define SQLT_UIN  68                                     /* unsigned integer */

#define SQLT_SLS  91                        /* Display sign leading separate */

#define SQLT_LVC  94                                  /* Longer longs (char) */

#define SQLT_LVB  95                                  /* Longer long binary */

#define SQLT_AFC  96                                      /* Ansi fixed char */

#define SQLT_AVC  97                                        /* Ansi Var char */

#define SQLT_CUR  102                                        /* cursor  type */

#define SQLT_RDD  104                                    /* rowid descriptor */

#define SQLT_LAB  105                                          /* label type */

#define SQLT_OSL  106                                        /* oslabel type */

       4.C语言中的OCI函数

       4.1参数类别

       有三种参数:必要参数、可选参数和不使用的参数。

       必要参数是Oracle使用的,OCI程序必须为他们提供有效值。

       可选参数的使用依赖与我们的应用程序,我们用方括号[ ]来表示可选参数。大多数情况下,如果一个可选参数为integer型,并且我们不使用它时,我们向其传-1。如果一个可选参数为地址参数时,我们向其传空指针即0。

       不使用的参数是不被Oracle使用的参数。但是我们必须要传递不使用的参数。在C语言中,传递不使用参数的方式和向省略的可选参数传递参数的方式一样。

              Integer      对应   -1

              地址参数    对应   0

       4.2 OCI函数的返回值

       当OCI函数被一个C程序调用时,其返回一个integer value。如果函数没有错误发生则返回0,否则有错误发生并返回一个非0数值。这种情况下,我们需要CDA中的返回代码return code字段来获取错误数字。

       4.3常用函数

       4.3.1 olog()函数

       目的:olog()函数在一个OCI程序和一个Oracle数据之间建立一个连接。

       原型:olog(Lda_Def *lda, ub1 *hda, text * uid, sword uidl, text * pswd, sword pswdl,

                            text *conn, sword connl, ub4 mode)

       参数: lda是指向一个LDA的指针,用来连接Oracle

                 hda 指向HDA的指针。

                 uid 指明一个包含username的字符串。

                 uidl uid指向的字符串的长度。如果那个字符串是空值结尾的字符串,则该参数应该被传为-1。

       pswd 指向password的字符串的指针。

pswdl pswd指向的字符串的长度。如果该为空值结尾的字符串,应该向其传递-1。

conn 指向包含SQL*Net V2连接说明符的一个指针。

connl conn指针指向的字符串的长度。如果该字符串为空值结尾的字符按,应该向其传

递-1。

    mode

       说明:HDA是一个与每个olog()函数相联结的程序分配的数据区域。对Oracle HDA的内容是完全私有的,但是HDA必须有OCI程序来分配。每个并发的连接需要一对LDA-HDA。

       注意:在调用olog()函数之前,HDA必须被初始化为全部为0(二进制的0,而不是字符’0’),否则会发生运行时错误。这意味着在C语言中,HAD必须被声明为全局或者静态变量,而不能是局部或者自动字符数组。

       调用olog()函数之后,HDA和LDA一定要保持和调用olog()函数是同样的地址。当一个OCI程序已经调用了olog()函数,其后的ologof()函数使用相同的LDA来提交该连接的事务。如果一个程序不能断开连接或者异常终止,则所有的事务都会被回滚。LDA的返回代码return code表明olog()函数执行的结果。0表示连接成功。

       最后一个参数mode用来说明此连接时阻塞或者非阻塞。

       4.3.2 oopen()函数

目的:打开指定的游标

       原型:oopen(Cda_Def * cursor,  Lda_Def *lda,  <text *dbn>,  <sword dbnl>,

<sword arsize>,  <text *uid>,  <sword uidl> )

参数:cursor 指向与程序相连的游标数据区域CDA的指针。

          lda  指向olog()函数中指明的LDA的指针

       dbn  这个参数只用于Oracle Version 2。V2版本后的Oracle都应传0

          dbnl这个参数只用于Oracle Version 2。V2版本后的Oracle都应传-1

          arsize Oracle7不适用该参数。游标在Oracle服务器中所占用的空间可以自动增

           uid 指向包含有userid和password的字符串的指针。必须用一个’/’将userid和

password 隔开

uidl uid指向的字符串的长度,如果该字符串为空值结尾的字符串,则此参数可

以被忽略。

       4.3.3 oparse()函数

       目的:oparse()函数解析一个SQL语句或者一个PL/SQL语句块,并将它连接至一个游

标。解析可以选择为延迟deferred模式

       原型:oparse( Cda_Def *cursor,   text *sqlstm,  [ sb4 sqll ], sword defflg, ub4 lngflg )

       参数:cursor 指向oopen()函数中指明的CDA

                sqlstm  指向包含有SQL语句的指针

                sqll     说明SQL语句的长度。如果sqlstm指向的字符串以空值结尾,则此参

数可以忽略。

          defflg  如果该应用程序以延迟方式连接至Oracle,并且该参数为非0值,则SQL

语句的解析延迟至调用odescr()函数、oexec()函数、oexn()函数或者oexfet()

函数时。

                lngflg  lngflg参数决定Oracle如何处理SQL语句和PL/SQL语句块。为保证与ANSI一致,对一些数据类型和操作的定义,Oracle 7 同Oracle 6有一些细微的差异。下表列出了lngflg参数对Oracle 7 和Oracle 6的不同影响

                 行为                              V6       V7

       char型字段为固定长度                     否       是

  为一个没有与指示器变量相连的输出变量

获取一个NULL值时,发出一个错误         否       是

当获取到的一个数值被截短并且没有

与之相关联的指示器变量时发出一个错误        是       否

描述(odescr()函数)函数为固定长度返回的

内部数据类型为1                              是        否

       描述(odescr()函数)函数为一个固定长度的

字符串返回的数据类型为96                  n/a不适用    是

       lngflg参数有三种不同的设置:

       0     指明为Oracle 6行为(我们连接的数据库可以是任何Version 6的Oracle,或者之

后的版本)

1     指定为我们的OCI程序所连接的服务器的正常行为,或者为Version 6行为或者

为Version 7行为

2   指定为Oracle 7 行为。如果我们使用2作为参数值连接的数据库不是Oracle 7,

    Oracle将会发出错误ORA-01011:不能使用这个语言类型同V6数据库对话。

 

       4.3.4 odefin()函数

       目的:odefin()函数为一个指定的SQL查询语句的所选项定义一个输出变量。

       原型: odefin( Cda_Def * cursor, sword pos, ub1 * buf, sword buf1, sword ftype,

                            <sword scale>, [sb2 *indp], <text *fmt>, <sword fmtl>,

                            <sword fmtt>,  [ub2 *rlen], [ub2 *rcode] );

       参数:cursor  指向oparse()函数中用到的游标数据区域CDA指针。

                pos    pos为查询语句中的所选项的索引。最左侧的所选项的索引值为1。

odefin()函数使用这个位置索引来连接输出变量和一个给定的所选项。

                我们可以使用odescr()函数来得到一个查询语言的所选项的数目。

          buf    指向程序中在执行ofetch()函数、ofen()函数或者oexfet()函数时获取接收

                 数据的变量的指针。该变量可以为Oracle字段能够被转换为的任意类型。

          bufl  被定义的变量的字节数。如果buf为一个数组,则该字节数为该数组中一

个元素的字节数。

          ftype  指定当一个所选项被传送至输出变量前的外部变量类型。

          scale  附带的小数位的个数。在C语言的OCI程序中不常用。

          indp  indp用来说明获取变量后,被获取的所选项为NULL、被截短或

完整返回。

          fmt   在C语言中不常用。

          fmtl   在C语言中不常用。

          rlen   Oracle将获取操作完成后的数据字节数存放至该ub2型指针指向的变量

                            当中。

          rcode  指向获取操作完成之后接收字段代码的变量的指针。Rcode中可以返回的

                 错误代码表明字段中的数据被截短或者有一个空值,例如,ORA-1405

或ORA-1406。

4.3.5obndrn()函数和obndrv()函数

目的:obndrn()函数和obndrv()函数联结程序中的变量和SQL语句中指定的占位符。

obndrv()函数以名称来是被占位符,而obndrn()函数以数字来识别占位符。

原型:obndrn(Cda_Def *cursor, sword sqlvn, ub1 *progv, sword progvl, sword ftype ,

<sword scale>, [sb2 *indp], <text *fmt>, <sword fmtl>, <sword fmtt >)

          obndrv(Cda_Def *cursor, text *sqlvar, [sword sqlvl], ub1 *progv, sword progvl,

                 sword ftype, <sword scale>, [sb2 *indp], <text *fmt>, <sword fmtl>,

                 <sword fmtt>);

说明:我们可以使用obndrv()函数或者obndrn()函数来绑定我们程序中的变量到一个

        SQL语句中的占位符。如果我们的程序需要进行分段操作或者使用结构数组。

        我们必须使用obindps()函数来绑定我们的变量。

     如果我们使用obndrv()函数,SQL语句中的占位符由一个冒号 : 和后面的SQL

标识符构成。占位符不是一个程序变量。例如,这个SQL语句

        SELECT ename, sal, comm. FROM emp WHERE deptno =  :Dept AND

                      Comm > :Min_com

由两个占位符,:Dept 和 :Min_com。

     如果使用obndrn()函数,SQL语句中的占位符有一个冒号 :  和后面的范围为从1到255的常量构成。这个SQL语句

SELECT ename, sal, comm FROM emp WHERE deptno = :2 AND comm > :1

有两个占位符::1 和 ;2。

绑定上文中第一个SQL语句中的占位符的obndrv()函数调用是

#define INT 3                   //为integer型的外部数据类型代码定义一个宏观

Cda_Def cursor;          //定义一个游标数据区域

Obndrv(&cursor, “:Dept”, -1, (ub1 *)&dept_num, (sword) sizeof(sword), INT, -1, (sb2*)0,

              (text *)0, -1, -1);

              由于”:Dept”是一个NULL结尾的字符串,不需要sqlvl参数,向其传递一个-1。

       如果调用obndrn()函数,sqlvn参数通过数字来识别sqlvn参数。如果sqlvn被设置为1,程序变量将被绑定至占位符 :1 。例如,将绑定程序变量上文中的第二个SQL语句中的占位符 :2 的obndrn()函数为

Obndrn(  &cursor,  2,  (ub1*) &dept_num,  (sword) sizeof(sword),  INT,

 -1,  (sb2*)0,  (text*) 0,  -1,  -1)

给sqlvn传递2来指明占位符 :2。由于PL/SQL语句块不识别数字占位符,在PL/SQL语句块中我们不能使用obndrn()函数来绑定程序变量到占位符。在PL/SQL语句块中使用obndra()函数和obndrv()函数来绑定变量至命名的占位符。

我们必须在调用oparse()函数来解析SQL语句之后和调用oexn()函数、oexec()函数与oexfet()函数之前来调用obndrv()函数和obndrn()函数。

在绑定时,Oracle就保存程序变量的地址。如果一个占位符在一个SQL语句中出现多次,一个单独的obndrv()函数或obndrn()函数调用就将该占位符的所有出现绑定至绑定变量。

参数:cursor 指向与oparse()函数中的SQL语句相关联的游标数据区域的指针。

         sqlvar 仅在obndrv()函数中使用。该参数标识SQL语句中的包含占位符(包含其前置的冒号 : )的字符串的地址。

         sqlvl  仅在obndrv()函数中使用,该参数为字符串sqlvar的长度。例如,占位符:Employee的长度为9。如果占位符的名字为NULL结尾的字符串,这个参数可以通过传-1来省略。

             progv  指向OCI程序中的一个变量或者数组变量的指针。当oexec()函数或者oexn()函数执行时,数据被传至Oracle,当执行oexfet()函数、ofen()函数或者ofetch()函数时,接收数据。

                progvl  OCI程序中的变量或者数组元素的字节数。对于成功的执行或者获取调用,obndrv()函数或者obndrn()函数可能仅仅被调用一次,progvl必须包含progv的最大长度。

                ftype  OCI程序中的程序变量的Oracle外部数据类型代码。当数据传至Oracle或者从Oracle接收时,Oracle对OCI程序变量在外部格式和内部格式之间进行转换。

                scale  scale参数只对附带有小数的变量有效。在C语言的OCI程序中不常用。将其设置为-1,以表示它不被使用。

                indp  指向指示器变量的指针。

                fmt   在C语言的OCI程序中不常用。

                fmtl   在C语言的OCI程序中不常用。

                fmtt   在C语言的OCI程序中不常用。

       4.3.6 oexec()函数

       目的:oexec()函数与一个cursor关联的SQL语句。

       原型:oexec(Cda_Def *cursor );

       参数:cursor  指向oparse()函数中指明的游标数据区域CDA的指针。

       在调用oexec()函数之前,我们必须调用oparse()函数来解析SQL语句,并且其必须成功完成。如果SQL语句中含有绑定变量的占位符,我们在调用oexec()函数之前调用obndrv()函数、obindps()函数、obndra()函数或者obndrn()来绑定各个占位符至程序变量。

       对于查询Query,在调用oexec()函数后,程序必须使用ofen()函数或者ofetch()函数来获取结果集中的行。

       4.3.7 ofetch()函数

       目的:ofetch()函数返回一个查询中的行,每次返回一行。

       原型:ofetch(Cda_Def * cursor)

       参数:cursor 指向与一个SQL语句相联结的游标数据区域的指针。

       说明:查询的每个字段值(所选项)都被存放至先前调用odefin()函数标识的缓冲区中。当运行在Oracle7 服务器上,并且先前的oparse()解析函数将其lngflg参数设置为1或2时,对于其缓冲区太大的字符串会被截短,此时,字段返回代码被设置为错误

       ORA-01406: 被获取的字段值被截短。

       并且其指示器参数被设置为该字符串的原始长度。但是,ofetch()函数并不提供一个错误指示。当一个字段值为NULL时,与该字段相关联的字段返回代码(rcode)被设置为错误

       ORA-01405: 获取的字段值为NULL。

       并且其指示器变量被设置为-1。但ofetch()函数并不返回一个错误。

       然而,如果没有定义指示器变量并且连接的是Oracle 7的服务器,ofetch()函数就返回错误1405。也就是说,如果一个字段值为NULL并且没有定义指示器变量,该函数总返回错误,即使已经定义了字段返回代码和返回长度。

       每个ofetch()函数返回满足查询的结果集中的下一行。每次调用ofetch()函数后,游标数据区域CDA中的 被处理行数 rows processed count 被递增。

       4.3.8 oclose()函数

       目的:oclose()函数将一个游标从它所连接的Oracle服务器的数据区域断开连接。

       原型: oclose( Cda_Def * cursor)

       参数:cursor 指向与其相关的oopen()函数中指明的游标数据区域CDA的指针。

       说明:oclose()函数释放所有通过调用oopen()函数、解析、执行和使用该游标进行获取操作所得到的资源。如果oclose()函数失败,游标数据区域中的返回代码 return code字段包含错误代码 error code。

       4.3.9 ologof()函数

       目的:ologof()函数断开一个LDA同Oracle程序全局区的连接并释放在Oracle用户程

序所占用的所有资源。

原型:ologof(Lda_Def * lda )

参数:指向用来连接Oracle的LDA的指针。

说明:当ologof()函数成功执行后,自动提交事务,并且当前所有打开的游标都被关闭。

如果没有成功地断开连接或者异常终止,所有的事务都被回滚。

翻译的文章里边一定有不少的错误,欢迎大家批评指正,谢谢大家!转载请注明 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
以下是一个使用 Oracle OCIOracle Call Interface)接口连接数据库、创建表和插入数据的简单示例代码。请注意,这个示例只是一个基本的样本,您可能需要根据您的具体情况进行修改。 ```c #include <stdio.h> #include <stdlib.h> #include <oci.h> int main() { OCIEnv *envhp; OCIError *errhp; OCIServer *srvhp; OCISession *usrhp; OCISvcCtx *svchp; OCIDefine *defhp; OCIStmt *stmthp; sword status; text sqlstmt[1024]; int empno; text ename[20]; int sal; // 初始化 OCI 环境 status = OCIEnvCreate(&envhp, OCI_THREADED | OCI_OBJECT, NULL, NULL, NULL, NULL, 0, NULL); if (status != OCI_SUCCESS) { printf("OCIEnvCreate failed\n"); return 1; } // 分配错误句柄 status = OCIHandleAlloc(envhp, (void **)&errhp, OCI_HTYPE_ERROR, 0, NULL); if (status != OCI_SUCCESS) { printf("OCIHandleAlloc failed for error handle\n"); return 1; } // 分配服务器句柄 status = OCIHandleAlloc(envhp, (void **)&srvhp, OCI_HTYPE_SERVER, 0, NULL); if (status != OCI_SUCCESS) { printf("OCIHandleAlloc failed for server handle\n"); return 1; } // 创建服务器上下文 status = OCIServerAttach(srvhp, errhp, (text *)"ORCL", strlen("ORCL"), 0); if (status != OCI_SUCCESS) { printf("OCIServerAttach failed\n"); return 1; } // 分配服务句柄 status = OCIHandleAlloc(envhp, (void **)&svchp, OCI_HTYPE_SVCCTX, 0, NULL); if (status != OCI_SUCCESS) { printf("OCIHandleAlloc failed for service context handle\n"); return 1; } // 设置服务上下文服务器句柄 status = OCIAttrSet(svchp, OCI_HTYPE_SVCCTX, srvhp, 0, OCI_ATTR_SERVER, errhp); if (status != OCI_SUCCESS) { printf("OCIAttrSet failed for service context handle\n"); return 1; } // 分配用户句柄 status = OCIHandleAlloc(envhp, (void **)&usrhp, OCI_HTYPE_SESSION, 0, NULL); if (status != OCI_SUCCESS) { printf("OCIHandleAlloc failed for user handle\n"); return 1; } // 设置用户句柄的用户名和密码 status = OCIAttrSet(usrhp, OCI_HTYPE_SESSION, (void *)"scott", strlen("scott"), OCI_ATTR_USERNAME, errhp); if (status != OCI_SUCCESS) { printf("OCIAttrSet failed for user handle\n"); return 1; } status = OCIAttrSet(usrhp, OCI_HTYPE_SESSION, (void *)"tiger", strlen("tiger"), OCI_ATTR_PASSWORD, errhp); if (status != OCI_SUCCESS) { printf("OCIAttrSet failed for user handle\n"); return 1; } // 开始会话

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值