人大金仓数据库KingbaseES DCI中的大对象处理

金仓数据库KingbaseES———DCI中的大对象处理

关键字:

DCILobLocator、CLOB、BLOB、人大金仓、KingbaseES

DCI中的DCILobLocator定义

struct DCILobLocator

{

DCIHandleHead head; // 结构体头部,head type= DCI_DTYPE_LOB

ub4 loid;

lobType type; // 初始为0,1 for BLOB(lobType_BLOB)0, 2 for CLOB(lobType_CLOB)

int len; // buf数组中已有数据的长度bytes for BLOB, characters for CLOB

int lobj_fd;//判断定位器是否打开的标志,标记是否为第一次读取或者写入,初始值为-1,表示第一次读取{-1,1}

char open_manually;//是否手动开启

char doBegin;

char enable_buffer;//是否打开lob定位器缓冲功能

char *buf;//初始大小为1048576个字节,即1M,后续加上偏移量offset,始终指向首地址

char updated;//

char status_piece; //piece flag.Must be use DCI_FIRST_PIECE,then you can use DCI_NEXT_PIECE and DCI_LAST_PIECE

char status_offset; //offset flag.LOB is opend,you can set offset.

int real_wlen; //actual data length to write实际写入的数据量

int need_wlen; //data length needed to write存储需要写入的数据量,在读取数据量大于真实传入数据量时使用

int remain_rlen; //未读取的数据长度data length not read

char is_init;//定位器是否已经初始化

DCIEnv *pEnv; //环境句柄

char *cur;//初始与buf地址相同,随着数据量的增大,指向buf数组的已有内容的最后一个字符

int len_in_byte;//初始值为-1,按照字节计数的长度

int allocated;//已分配空间大小,对应buf数组申请的空间大小,最大支持2G的空间分配,初始值为-1

char *backup_buf;//备份区域,大小为backup_len

int backup_len;//备份区域的大小

int backup_len_in_byte;//以字节长度衡量的备份区域的大小

int is_temporary;//是否为临时LOB,临时大对象无需执行写入数据库操作

char *table_name; //表名称need to invoke free() when dealloc

char *column_name; //列名称need to invoke free() when dealloc

CtidXminListNode *pCtidXminListNode; //存储Ctid、Xmin,need to invoke free() when dealloc

unsigned char mode; //DCILobOpen()调用设置LOB定位器的执行模式READ WRITE FLAG DCI_LOB_READONLY DCI_LOB_READWRITE

};

DCI中的大对象读操作

在大对象读方面,DCILobRead( )支持回调,BLOB与CLOB的长度用同一个参数amtp传入,对于CLOB类型,长度为字节数或者字符数,仅支持UTF8,GB18030,GBK三种编码方式;对于BLOB类型,长度为字节数。DCILobRead2( )暂时不支持回调,BLOB与CLOB的长度用不同的参数表示,大对象占用的字节数用byte_amtp表示,对于BLOB,该参数始终生效,对于CLOB,只有当char_amtp为0时才生效,字符数用参数char_amtp表示,仅用于CLOB,对BLOB应当忽略。

大对象的查询过程同样遵循OCI操作的一般过程,经历建立连接、语句句柄分配DCIHandleAlloc( )、查询语句准备DCIStmtpPrepare( )、查询语句执行DCIStmtExecute( )、获取查询结果DCIStmtFetch( )、查询事务提交DCITransCommit( )的过程,有所区别的是对于大对象类型,需要调用DCIDescriptorAlloc( )分配OCI_DTYPE_LOB类型的大对象句柄,并调用DCIDefineByPos( )对各个句柄与对应的类型SQLT_CLOB与SQLT_BLOB进行位置绑定,将大对象句柄关联到语句句柄stmtp对应的pDefine的valuep成员上:

err = OCIDescriptorAlloc(pEnv, (void**)&pBlob, OCI_DTYPE_LOB, 0, NULL);

err = OCIDefineByPos(pStmt,

                        &pDefine,

                        pError,

                        1,

                        (dvoid *) &pBlob,

                        sizeof(OCILobLocator *),

                        SQLT_BLOB,

                        (void *)0,

                        (ub2 *)0,

                        (ub2 *)0,

                        OCI_DEFAULT);

//在OCIDefineByPos内部:

stmtp->pDefine[position - 1].valuep = valuep;

在DCIStmtpFetch( )内部,需要调用KSAPI_BindCol( )再次将用户缓冲区pDefine[i].pBuffer->buf与数据库的列进行绑定:

ret = KSAPI_BindCol(stmtp->hstmt, pDefine[i].position, KSQL_C_BINARY, (void *)&pDefine[i].pBuffer->buf, SQLT_BLOB_LENGTH, ind);

ret = KSAPI_BindCol(stmtp->hstmt, pDefine[i].position, KSQL_C_CHAR, (void *)&pDefine[i].pBuffer->buf, SQLT_CLOB_LENGTH, ind);

最后将pDefine[i].pBuffer->buf中的数据存入DCILobLocator的buf成员变量中,以便后续从中读取。特别的,针对select…for update语句,需要额外关联该行数据的唯一标识ctid与xmin,因此需要申请空间并将其关联到DCILobLocator的pCtidXminListNode成员中,便于后续的写或者更新操作。针对大对象,DCIStmtFetch()的处理流程如图1所示。

图1 DCIStmtFetch( )处理大对象的流程

大对象因为数据体量比结构化数据大,且数据组织方式不规范,需要依次提取到相应的文件并做其他的处理。因此将数据读取到DCILobLocator中之后,还需要调用DCILobRead( )分批次进行读取。由于存放数据的缓冲区大小受限(READ_BUFSIZE=1024),因此每次只能读取一部分数据,需要采用循环结构,读取返回OCI_NEED_DATA时表示本次读取成功,数据还未读取完成,需进入循环重新读取,返回OCI_SUCCESS表明所有数据都已经读取成功。特别地,对于CLOB类型的数据,其读取缓冲区大小为READ_BUFSIZE + 1。DCILobRead( )其具体的实现思路为:

Step1:参数状态判断:svchp、errhp、locp以及locp中已有数据的长度;

Step2:判断bufp;

Step3:同步svchp->pEnv->pSveCtx与当前svchp句柄,加线程锁;

Step4:根据locp->type判断是读取BLOB还是CLOB;

Step5:读取调用的返回值处理。

在DCILobRead( )函数内部,由于BLOB与CLOB对应的计量方式不同,需要采取不同的方式从DCILobLocator中读取,对应的函数分别为DCILobReadBLOB( )与DCILobReadCLOB( ),其具体的实现思路分别如图2所示。相较于DCILobReadCLOB( ),DCILobReadBLOB( )没有字符编码判断这一步骤,其余实现步骤基本一致。

C:\Users\yangli\Desktop\OCI接口函数调用\DCILobRead().png

图2 DCILobRead( )实现过程

DCI中的大对象写操作

在DCI中,LOB大对象的写操作需要借助DCILobLocator,调用DCILobWrite( )写入,一次只可写入一列数据。而对于LOB大对象的更新操作,需要使用select…for update语句获取到该行,在本地修改之后,调用DCILobWrite( )重新写入,在此期间,ctid与xmin作为该行数据的唯一标识,需要被获取并关联到DCILobLocator中,对应DCIStmtFetch( )中对stmtp->hasForUpdate的处理。同大对象数据读取一样,大对象写操作过程也受字符编码的影响,因此针对CLOB与BLOB分别对应DCILobWriteCLOB( )与DCILobWriteBLOB( ),具体的实现思路为:

Step1:判断写入内容bufp是否为空;

Step2:判断svchp->pEnv->pSveCtx != svchp,确保当前服务上下文句柄与环境句柄中服务上下文句柄一致;

Step3:根据定位器的type属性判断是写入BLOB(1)还是CLOB(2)。

对于DCILobWriteBLOB( ),其实现过程如图3所示。

C:\Users\yangli\Desktop\OCI接口函数调用\DCILobWriteBLOB().png

C:\Users\yangli\Desktop\OCI接口函数调用\DCI_FIRST_PIECE1.png

图3 DCILobWriteBLOB( )实现过程

写入过程中包含为两个步骤:writeToBLOB( )与writeLobToDB( )。writeToBLOB( )是将数据写入DCILobLocator( )并设置对应的成员变量,而writeLobToDB( )是将DCILobLocator中的数据真实写入数据库。

C:\Users\yangli\Desktop\OCI接口函数调用\DCI_ONE_PIECE.png

图4 writeToBLOB( )与writeLobToDB( )实现过程

对于DCILobWriteCLOB( ),其实现过程如图5所示。

C:\Users\yangli\Desktop\OCI接口函数调用\DCILobWriteCLOB().png

图5 DCILobWriteCLOB( )实现过程

DCI对大对象操作函数的兼容情况

DCI中支持两种大对象类型,分别为字符大对象类型SQLT_CLOB,二进制大对象类型SQLT_BLOB。Oracle,KINGBASE V7,PG的大对象都是采用的OID中转方式,即用户表中的大对象实际只存一个定位器,指向系统的大对象表中对应的位置。大对象的实际存储位置和用户表的位置是分开的。所以读写大对象都是靠定位器来中转实现,可以通过LOB函数直接把大对象读写到数据库,不需要像普通字段一样,非得等到insert/update语句时才能写入数据库。例如:可以通过获取select查询返回的结果中的大对象定位器LOBLocater。而KINGBASE V8的大对象实际采用的是BYTEA和TEXT的直接存储方式,和普通类型一样,没有OID这种定位器,也就无法通过定位器直接写入数据库。但是需要保持原OCI对外提供的功能和接口调用形式不变,所以需要实现客户端大对象的各种操作,目前已经兼容了23个大对象操作函数。

由兼容对比可以看出,DCI已经实现了OCI操作LOB的基本功能,但就数据量而言,DCI使用KDB_TYPE_CLOB_LENGTH来标识最大支持的CLOB的数据量,为1GB,用KDB_TYPE_BLOB_LENGTH标识操作BLOB的最大数据量,为2GB,暂不支持4GB以上的大对象操作。

表1 OCI与DCI大对象操作函数实现

OCI接口

功能

DCI接口

OCILobAppend()

将一个LOB附加到另一个

DCILobAppend()

OCILobAssign()

将一个LOB定位器分配给另一个,不能用于临时LOB

DCILobAssign()

OCILobCharSetForm()

从LOB定位器获取字符集形式

DCILobCharSetForm()

OCILobCharSetId()

从LOB定位器获取字符集ID

DCILobCharSetId()

OCILobClose()

关闭先前打开的LOB

DCILobClose()

OCILobCopy2()

将一个LOB的全部或部分复制到另一个,必须用于大于4GB的LOB

DCILobCopy()

OCILobCreateTemporary()

创建一个临时LOB

DCILobCreateTemporary()

OCILobFreeTemporary()

释放临时LOB

DCILobFreeTemporary()

OCILobIsTemporary()

确定给定的LOB是不是临时的

DCILobIsTemporary()

OCILobDisableBuffering()

关闭LOB缓冲

DCILobDisableBuffering()

OCILobEnableBuffering()

打开LOB缓冲

DCILobEnableBuffering()

OCILobFlushBuffer()

刷新LOB缓冲区

DCILobFlushBuffer()

OCILobErase2()

擦除LOB的一部分,必须用于大于4GB的LOB

DCILobErase()

OCILobLocatorIsInit()

检查LOB定位器是否已经初始化

DCILobLocatorIsInit()

OCILobOpen()

打开一个LOB

DCILobOpen()

OCILobRead2()

读一部分LOB,必须用于大于4GB的LOB

DCILobRead()&

DCILobRead2()

OCILobWrite2()

写入LOB,必须用于大于4GB的LOB

DCILobWrite()

OCILobIsEqual()

比较两个LOB定位器指向的LOB是否相等

DCILobIsEqual()

OCILobTrim2()

截断LOB,必须用于大于4GB的LOB

DCILobTrim()

OCILobGetLength2()

获取LOB的长度,必须用于大于4GB的LOB

DCILobGetLength()

OCILobIsOpen()

检查LOB是否打开

DCILobIsOpen()

表1 OCI与DCI大对象操作函数实现(续)

OCI接口

功能

DCI接口

OCIDurationEnd()

临时LOB的最终用户持续时间

DCIDurationEnd()

OCIDurationBegin()

启动临时LOB的用户持续时间

DCIDurationBegin()

OCILobArrayRead()

读取多个定位器的LOB数据

OCILobArrayWrite()

为多个定位器写入LOB数据

OCILobFileClose()

关闭以前打开的BFILE

OCILobFileCloseAll()

关闭所有以前打开的文件

OCILobFileExists()

检查服务器上是否存在文件

OCILobFileGetName()

从LOB定位器上获取目录对象和文件名

OCILobFileIsOpen()

用定位器检查服务器上的文件是否已经打开

OCILobFileOpen()

打开一个BFILE

OCILobFileSetName()

在LOB定位器中设置目录对象和文件名

OCILobGetChunkSize()

获取LOB的块大小

OCILobGetContentType()

检索SecureFile中用户指定的内容类型字符串

OCILobGetOptions()

获取SecureFile的选项设置

OCILobGetStorageLimit()

以字节为单位获取内部LOB的最大长度

OCILobLoadFromFile2()

从BFILE加载LOB,必须用于大于4GB的LOB

OCILobLocatorAssign()

将一个LOB定位器分配给另一个,可用于临时LOB

OCILobSetContentType()

存储SecureFile的用户指定的内容类型字符串

OCILobSetOptions()

为现有和新创建的SecureFiles的选项设置

OCILobWriteAppend2()

写入从LOB结尾开始的数据,必须用于大于4GB的LOB

参考资料

KES客户端编程接口及框架简介

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值