快速入门
📄字数 5.8K
👁️阅读量 加载中...
此章节将使用最简单的代码展示如何使用 XuguDB-OCI 向虚谷数据库插入和查询数据,以及展示如何通过 XuguDB-OCI 操作数值类型、字符类型和结构体类型的字段以实现数值的插入和查询。用户阅读此章节可快速了解 XuguDB-OCI 的使用方法,用户可编译运行本章节后附带的示例程序并查看执行效果,可基于附带的示例程序改写符合用户特定需求的程序代码。
句柄类型
在 XuguDB-OCI 中,与数据库进行交互所需要使用的句柄有:
- OCIEnv:环境句柄
- OCIError:错误句柄
- OCIServer:服务器句柄
- OCISvcCtx:上下文句柄
- OCISession:会话句柄
- OCIStmt:语句句柄
创建句柄
环境句柄使用 OCIEnvCreate 创建,其他句柄以环境句柄作为父句柄使用 OCIHandleAlloc 创建。句柄的销毁则统一由 OCIHandleFree 完成。
OCIEnvCreate 的原型为:
C
sword OCIEnvCreate (OCIEnv **envp, ub4 mode, void *ctxp,
void *(*malocfp)(void *ctxp, size_t size),
void *(*ralocfp)(void *ctxp, void *memptr, size_t newsize),
void (*mfreefp)(void *ctxp, void *memptr),
size_t xtramem_sz, void **usrmempp);
一般情况下,不需要指定内存分配相关的函数,因此只需要关注前两个参数,第一个为环境句柄指针的地址,第二个固定传 OCI_DEFAULT,剩下的指针类型参数全部传入 NULL,数值类型传入 0 即可。
OCIHandleAlloc 的原型为:
C
sword OCIHandleAlloc(const void *parenth, void **hndlpp, const ub4 type,
const size_t xtramem_sz, void **usrmempp);
第一个参数为环境句柄,第二个参数为句柄指针的地址,第三个参数为待创建句柄的类型,第四个参数传入 0,第五个参数传入 NULL。
句柄与句柄类型的关系如下:
- OCIEnv:OCI_HTYPE_ENV
- OCIError:OCI_HTYPE_ERROR
- OCIServer:OCI_HTYPE_SERVER
- OCISvcCtx:OCI_HTYPE_SVCCTX
- OCISession:OCI_HTYPE_SESSION
- OCIStmt:OCI_HTYPE_STMT
C
OCIEnv* env;
OCIError* err;
OCIServer* server;
OCISvcCtx* ctx;
OCISession* session;
OCIStmt* stmt;
OCIEnvCreate(&env, OCI_DEFAULT, NULL, NULL, NULL, NULL, 0, NULL);
OCIHandleAlloc(env, &err, OCI_HTYPE_ERROR, 0, NULL);
OCIHandleAlloc(env, &server, OCI_HTYPE_SERVER, 0, NULL);
OCIHandleAlloc(env, &ctx, OCI_HTYPE_SVCCTX, 0, NULL);
OCIHandleAlloc(env, &session, OCI_HTYPE_SESSION, 0, NULL);
OCIHandleAlloc(env, &stmt, OCI_HTYPE_STMT, 0, NULL);
为句柄设置属性
服务器句柄需使用 OCIServerAttach 通过连接串设置连接信息,连接串格式为:host:port/database。接口的原型为:
C
sword OCIServerAttach(OCIServer *srvhp, OCIError *errhp, const OraText *dblink, sb4 dblink_len, ub4 mode);
会话句柄需使用 OCIAttrSet 设置用户名和密码。服务器句柄和会话句柄需使用 OCIAttrSet 设置进上下文句柄。接口的原型为:
C
sword OCIAttrSet(void *trgthndlp, ub4 trghndltyp, void *attributep, ub4 size, ub4 attrtype, OCIError *errhp);
第一二个参数为句柄和句柄类型,第三四个参数为属性值及其大小,第五个参数为属性值类型,此章节用到的属性值类型有:
- OCI_ATTR_USERNAME
- OCI_ATTR_PASSWORD
- OCI_ATTR_SERVER
- OCI_ATTR_SESSION
C
const char* dblink = "127.0.0.1:5138/SYSTEM";
OCIServerAttach(server, err, dblink, strlen(dblink), OCI_DEFAULT);
OCIAttrSet(session, OCI_HTYPE_SESSION, user, strlen(user), OCI_ATTR_USERNAME, err);
OCIAttrSet(session, OCI_HTYPE_SESSION, pwd, strlen(pwd), OCI_ATTR_PASSWORD, err);
OCIAttrSet(ctx, OCI_HTYPE_SVCCTX, server, 0, OCI_ATTR_SERVER, err);
OCIAttrSet(ctx, OCI_HTYPE_SVCCTX, session, 0, OCI_ATTR_SESSION, err);
登录数据库
将连接信息和登录信息正确设置进句柄后,即可登录数据库。登录数据库的接口原型为:
C
sword OCISessionBegin(OCISvcCtx *svchp, OCIError *errhp, OCISession *usrhp, ub4 credt, ub4 mode);
前三个参数均为句柄,后两个参数在 XuguDB-OCI 中无实际用途。
C
ret = OCISessionBegin(ctx, err, session, OCI_CRED_RDBMS, OCI_DEFAULT);
if (ret != OCI_SUCCESS) {
// get error info
}
使用 OCISessionBegin 接口登录数据库前置过程繁琐,OCI 提供了较为方便的 OCILogon。OCILogon 对登录数据库的前置操作进行了封装,大幅简化了登录的流程。其原型为:
C
sword OCILogon(OCIEnv *envhp, OCIError *errhp, OCISvcCtx **svchp,
const OraText *username, ub4 uname_len,
const OraText *password, ub4 passwd_len,
const OraText *dbname, ub4 dbname_len);
使用此接口不再需要创建服务器句柄和会话句柄,参数 dbname 为连接串。
执行 SQL 语句
在 OCI 中,执行所有 SQL 语句均需进行 prepare 操作。若执行查询语句,需在调用 OCIStmtPrepare 之后且在调用 OCIStmtExecute 之前进行 OCIDefineByPos 操作。若语句中带有参数,参数绑定也需在 OCIStmtPrepare 和 OCIStmtExecute 之间进行。
OCIStmtPrepare 原型为:
C
sword OCIStmtPrepare(OCIStmt *stmtp, OCIError *errhp, const OraText *stmt,
ub4 stmt_len, ub4 language, ub4 mode);
其中第一个参数为语句句柄,第二个参数为错误句柄,第三个参数为 SQL 语句,第四个参数为 SQL 语句的长度,最后两个参数未使用。
OCIStmtExecute 原型为:
C
sword OCIStmtExecute(OCISvcCtx *svchp, OCIStmt *stmtp, OCIError *errhp,
ub4 iters, ub4 rowoff, const OCISnapshot *snap_in,
OCISnapshot *snap_out, ub4 mode);
其中前三个参数为句柄,第四个参数的值取决于所执行的语句,若为 DDL 或 DML,其值传入 1 即可,若为 DQL,其值传入 0 即可。剩余参数均未使用。
C
const char* sql = "SELECT id FROM tab_test";
OCIStmtPrepare(stmt, err, sql, strlen(sql), OCI_NTV_SYNTAX, OCI_DEFAULT);
OCIDefine* def;
int id;
OCIDefineByPos(stmt, &def, err, 1, &id, 4, SQLT_INT, NULL, NULL, NULL, OCI_DEFAULT);
OCIStmtExecute(ctx, stmt, err, 0, 0, NULL, NULL, OCI_DEFAULT);
while ((ret = OCIStmtFetch(stmt, err, 1, OCI_FETCH_NEXT, OCI_DEFAULT)) == OCI_SUCCESS) {
printf("%d\n", id);
}
销毁句柄
每个句柄均存储相关的信息,因此句柄使用完毕后需将其销毁以释放内存,其接口原型为:
C
sword OCIHandleFree(void *hndlp, const ub4 type);
其中第一个参数为句柄,第二个参数为句柄类型。
C
OCIHandleFree(stmt, OCI_HTYPE_STMT);
OCIHandleFree(session, OCI_HTYPE_SESSION);
OCIHandleFree(ctx, OCI_HTYPE_SVCCTX);
OCIHandleFree(server, OCI_HTYPE_SERVER);
OCIHandleFree(err, OCI_HTYPE_ERROR);
OCIHandleFree(env, OCI_HTYPE_ENV);
完整代码
此示例依赖的表结构为:
SQL
CREATE TABLE tab_test (
id INT,
name VARCHAR(20),
created DATE
);
C
//main.c
#include <stdio.h>
#include <string.h>
#include "oci.h"
void printError(OCIError* err)
{
sb4 errCode;
char errMsg[128];
OCIErrorGet(err, 1, NULL, &errCode, errMsg, 128, OCI_HTYPE_ERROR);
printf("[%d]%s\n", errCode, errMsg);
}
void createTable(OCISvcCtx* ctx, OCIStmt* stmt, OCIError* err)
{
sword ret;
const char* sql = "CREATE TABLE tab_test (id INT, name VARCHAR(20), created DATE)";
ret = OCIStmtPrepare(stmt, err, sql, strlen(sql), OCI_NTV_SYNTAX, OCI_DEFAULT);
if (ret != OCI_SUCCESS) printError(err);
ret = OCIStmtExecute(ctx, stmt, err, 1, 0, NULL, NULL, OCI_DEFAULT);
if (ret != OCI_SUCCESS) printError(err);
}
void insert(OCISvcCtx* ctx, OCIStmt* stmt, OCIError* err)
{
sword ret;
const char* sql = "INSERT INTO tab_test (id, name, created) VALUES(:id, :name, :created)";
OCIStmtPrepare(stmt, err, sql, strlen(sql), OCI_NTV_SYNTAX, OCI_DEFAULT);
OCIBind* bind[3];
int id = 1;
const char* name = "Tom";
OCIDate created = { 2000,1,1 };
OCIBindByName(stmt, &bind[0], err, "id", 2, &id, 4, SQLT_INT, NULL, NULL, NULL, 0, NULL, OCI_DEFAULT);
OCIBindByName(stmt, &bind[1], err, "name", 4, name, sizeof(name), SQLT_STR, NULL, NULL, NULL, 0, NULL, OCI_DEFAULT);
OCIBindByName(stmt, &bind[2], err, "created", 7, &created, sizeof(created), SQLT_ODT, NULL, NULL, NULL, 0, NULL, OCI_DEFAULT);
ret = OCIStmtExecute(ctx, stmt, err, 1, 0, NULL, NULL, OCI_DEFAULT);
if (ret != OCI_SUCCESS) printError(err);
}
void query(OCISvcCtx* ctx, OCIStmt* stmt, OCIError* err)
{
sword ret;
const char* sql = "SELECT id, name, created FROM tab_test WHERE id = :id";
ret = OCIStmtPrepare(stmt, err, sql, strlen(sql), OCI_NTV_SYNTAX, OCI_DEFAULT);
int id = 1;
char name[20];
OCIDate created;
OCIBind* bind;
OCIBindByName(stmt, &bind, err, "id", 2, &id, 4, SQLT_INT, NULL, NULL, NULL, 0, NULL, OCI_DEFAULT);
OCIDefine* def[3];
OCIDefineByPos(stmt, &def[0], err, 1, &id, 4, SQLT_INT, NULL, NULL, NULL, OCI_DEFAULT);
OCIDefineByPos(stmt, &def[1], err, 2, name, sizeof(name), SQLT_CHR, NULL, NULL, NULL, OCI_DEFAULT);
OCIDefineByPos(stmt, &def[2], err, 3, &created, sizeof(created), SQLT_ODT, NULL, NULL, NULL, OCI_DEFAULT);
ret = OCIStmtExecute(ctx, stmt, err, 0, 0, NULL, NULL, OCI_DEFAULT);
if (ret != OCI_SUCCESS) printError(err);
while ((ret = OCIStmtFetch(stmt, err, 1, OCI_FETCH_NEXT, OCI_DEFAULT)) == OCI_SUCCESS)
{
printf("%d, %s, %d-%02d-%02d\n", id, name, created.OCIDateYYYY, created.OCIDateMM, created.OCIDateDD);
}
}
int main()
{
OCIEnv* env;
OCIError* err;
OCISvcCtx* ctx;
OCIStmt* stmt;
sword ret;
OCIEnvCreate(&env, OCI_DEFAULT, NULL, NULL, NULL, NULL, 0, NULL);
OCIHandleAlloc(env, &err, OCI_HTYPE_ERROR, 0, NULL);
const char* user = "SYSDBA";
const char* pwd = "SYSDBA";
const char* db = "127.0.0.1:5138/SYSTEM";
ret = OCILogon(env, err, &ctx, user, strlen(user), pwd, strlen(pwd), db, strlen(db));
if (ret != OCI_SUCCESS)
{
printError(err);
goto _end;
}
OCIHandleAlloc(env, &stmt, OCI_HTYPE_STMT, 0, NULL);
createTable(ctx, stmt, err);
insert(ctx, stmt, err);
query(ctx, stmt, err);
OCIHandleFree(stmt, OCI_HTYPE_STMT);
OCILogoff(ctx, err);
_end:
OCIHandleFree(ctx, OCI_HTYPE_SVCCTX);
OCIHandleFree(err, OCI_HTYPE_ERROR);
OCIHandleFree(env, OCI_HTYPE_ENV);
return 0;
}