Skip to content

适用虚谷数据库版本

v12.9



适用虚谷数据库版本

v12.9


快速入门

📄字数 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;
}