CCI 개요

CCI(C Client Interface)는 CUBRID 브로커와 응용 클라이언트 사이에 위치하여, C 기반의 응용 클라이언트가 브로커를 통해 CUBRID 데이터베이스 서버로 접근할 수 있는 인터페이스로서, 브로커 응용 서버(CAS)를 이용하는 도구(예: PHP, ODBC)를 만들기 위한 하부 구조로도 사용된다. 여기서, CUBRID 브로커는 응용 클라이언트로부터 받은 질의를 데이터베이스에 전달하고, 실행 결과를 응용 클라이언트로 전송하는 역할을 수행한다.

CCI를 사용하기 위해서는 헤더 파일과 라이브러리 파일이 필요하다.

  Windows Unix/Linux
C 헤더 파일 include/cas_cci.h include/cas_cci.h
정적 라이브러리 lib/cascci.lib lib/libcascci.a
동적 라이브러리 bin/cascci.dll lib/libcascci.so

CCI 드라이버는 CUBRID에서 제공되는 C 언어 인터페이스로, CUBRID 설치 패키지에 포함되어 있다. CCI는 브로커를 통해서 접속하므로 다른 인터페이스인 JDBC, PHP, ODBC, Python, Ruby 등과 동일하게 관리될 수 있다. 실제로 PHP, ODBC, Python, Ruby 인터페이스는 CCI를 기반으로 개발되었다. 단, JDBC는 CCI를 기반으로 개발되지 않았다.

../_images/image54.jpg

CCI 프로그래밍

CCI 응용 프로그램 작성

CCI를 이용하는 응용 프로그램은 기본적으로 CAS와 연결하기, 질의 준비, 질의 수행, 응답 처리, 연결 끊기의 과정을 통해 CUBRID를 이용한다. 각 과정에서 CCI는 연결 핸들(connection handle), 질의 핸들(query handle), 응답 핸들(response handle)을 통해 응용 프로그램과 소통한다.

브로커 파라미터인 CCI_DEFAULT_AUTOCOMMIT으로 응용 프로그램 시작 시 자동 커밋 모드의 기본값을 설정할 수 있으며, 브로커 파라미터 설정을 생략하면 기본값은 ON 이다. 응용 프로그램 내에서 자동 커밋 모드를 변경하려면 cci_set_autocommit() 함수를 이용하며, 자동 커밋 모드가 OFF 이면 cci_end_tran() 함수를 이용하여 명시적으로 트랜잭션을 커밋하거나 롤백해야 한다.

기본적인 작성 순서는 다음과 같으며, prepared statement 사용을 위해서는 변수에 데이터를 바인딩하는 작업이 추가된다. 이를 예제 1 및 예제 2에 구현하였다.

Note

  • Windows에서 CCI 응용 프로그램을 컴파일하려면 “WINDOWS”가 define되어야 하므로 “-DWINDOWS” 옵션을 컴파일러에 반드시 포함하도록 한다.
  • 스레드 기반 프로그램에서 데이터베이스 연결은 각 스레드마다 독립적으로 사용해야 한다.
  • 자동 커밋 모드에서 SELECT 문 수행 이후 모든 결과 셋이 fetch되지 않으면 커밋이 되지 않는다. 따라서, 자동 커밋 모드라 하더라도 프로그램 내에서 결과 셋에 대한 fetch 도중 어떠한 오류가 발생한다면 반드시 cci_end_tran() 을 호출하여 트랜잭션을 종료 처리하도록 한다.

예제 1

// Example to execute a simple query
// In Linux: gcc -o simple simple.c -m64 -I${CUBRID}/include -lnsl ${CUBRID}/lib/libcascci.so -lpthread

#include <stdio.h>
#include "cas_cci.h"
#define BUFSIZE  (1024)

int
main (void)
{
    int con = 0, req = 0, col_count = 0, i, ind;
    int error;
    char *data;
    T_CCI_ERROR cci_error;
    T_CCI_COL_INFO *col_info;
    T_CCI_CUBRID_STMT stmt_type;
    char *query = "select * from code";

    //getting a connection handle for a connection with a server
    con = cci_connect ("localhost", 33000, "demodb", "dba", "");
    if (con < 0)
    {
        printf ("cannot connect to database\n");
        return 1;
    }

    //preparing the SQL statement
    req = cci_prepare (con, query, 0, &cci_error);
    if (req < 0)
    {
        printf ("prepare error: %d, %s\n", cci_error.err_code,
                cci_error.err_msg);
        goto handle_error;
    }

    //getting column information when the prepared statement is the SELECT query
    col_info = cci_get_result_info (req, &stmt_type, &col_count);
    if (col_info == NULL)
    {
        printf ("get_result_info error: %d, %s\n", cci_error.err_code,
                cci_error.err_msg);
        goto handle_error;
    }

    //Executing the prepared SQL statement
    error = cci_execute (req, 0, 0, &cci_error);
    if (error < 0)
    {
        printf ("execute error: %d, %s\n", cci_error.err_code,
                cci_error.err_msg);
        goto handle_error;
    }
    while (1)
    {

        //Moving the cursor to access a specific tuple of results
        error = cci_cursor (req, 1, CCI_CURSOR_CURRENT, &cci_error);
        if (error == CCI_ER_NO_MORE_DATA)
        {
            break;
        }
        if (error < 0)
        {
            printf ("cursor error: %d, %s\n", cci_error.err_code,
                    cci_error.err_msg);
            goto handle_error;
        }

        //Fetching the query result into a client buffer
        error = cci_fetch (req, &cci_error);
        if (error < 0)
        {
            printf ("fetch error: %d, %s\n", cci_error.err_code,
                    cci_error.err_msg);
            goto handle_error;
        }
        for (i = 1; i <= col_count; i++)
        {

            //Getting data from the fetched result
            error = cci_get_data (req, i, CCI_A_TYPE_STR, &data, &ind);
            if (error < 0)
            {
                printf ("get_data error: %d, %d\n", error, i);
                goto handle_error;
            }
            printf ("%s\t|", data);
        }
        printf ("\n");
    }

    //Closing the request handle
    error = cci_close_req_handle (req);
    if (error < 0)
    {
        printf ("close_req_handle error: %d, %s\n", cci_error.err_code,
                cci_error.err_msg);
        goto handle_error;
    }

    //Disconnecting with the server
    error = cci_disconnect (con, &cci_error);
    if (error < 0)
    {
        printf ("error: %d, %s\n", cci_error.err_code, cci_error.err_msg);
        goto handle_error;
    }

    return 0;

handle_error:
    if (req > 0)
        cci_close_req_handle (req);
    if (con > 0)
    cci_disconnect (con, &cci_error);

    return 1;
}

예제 2

// Example to execute a query with a bind variable
// In Linux: gcc -o cci_bind cci_bind.c -m64 -I${CUBRID}/include -lnsl ${CUBRID}/lib/libcascci.so -lpthread

#include <stdio.h>
#include <string.h>
#include "cas_cci.h"
#define BUFSIZE  (1024)

int
main (void)
{
    int con = 0, req = 0, col_count = 0, i, ind;
    int error;
    char *data;
    T_CCI_ERROR cci_error;
    T_CCI_COL_INFO *col_info;
    T_CCI_CUBRID_STMT stmt_type;
    char *query = "select * from nation where name = ?";
    char namebuf[128];

    //getting a connection handle for a connection with a server
    con = cci_connect ("localhost", 33000, "demodb", "dba", "");
    if (con < 0)
    {
        printf ("cannot connect to database\n");
        return 1;
    }

    //preparing the SQL statement
    req = cci_prepare (con, query, 0, &cci_error);
    if (req < 0)
    {
        printf ("prepare error: %d, %s\n", cci_error.err_code,
              cci_error.err_msg);
        goto handle_error;
    }

    //Binding date into a value
    strcpy (namebuf, "Korea");
    error =
    cci_bind_param (req, 1, CCI_A_TYPE_STR, namebuf, CCI_U_TYPE_STRING,
                    CCI_BIND_PTR);
    if (error < 0)
    {
        printf ("bind_param error: %d ", error);
        goto handle_error;
    }

    //getting column information when the prepared statement is the SELECT query
    col_info = cci_get_result_info (req, &stmt_type, &col_count);
    if (col_info == NULL)
    {
        printf ("get_result_info error: %d, %s\n", cci_error.err_code,
              cci_error.err_msg);
        goto handle_error;
    }

    //Executing the prepared SQL statement
    error = cci_execute (req, 0, 0, &cci_error);
    if (error < 0)
    {
        printf ("execute error: %d, %s\n", cci_error.err_code,
              cci_error.err_msg);
        goto handle_error;
    }

    //Executing the prepared SQL statement
    error = cci_execute (req, 0, 0, &cci_error);
    if (error < 0)
    {
        printf ("execute error: %d, %s\n", cci_error.err_code,
              cci_error.err_msg);
        goto handle_error;
    }

    while (1)
    {

        //Moving the cursor to access a specific tuple of results
        error = cci_cursor (req, 1, CCI_CURSOR_CURRENT, &cci_error);
        if (error == CCI_ER_NO_MORE_DATA)
        {
            break;
        }
        if (error < 0)
        {
            printf ("cursor error: %d, %s\n", cci_error.err_code,
                  cci_error.err_msg);
            goto handle_error;
        }

        //Fetching the query result into a client buffer
        error = cci_fetch (req, &cci_error);
        if (error < 0)
        {
            printf ("fetch error: %d, %s\n", cci_error.err_code,
                  cci_error.err_msg);
            goto handle_error;
        }
        for (i = 1; i <= col_count; i++)
        {

            //Getting data from the fetched result
            error = cci_get_data (req, i, CCI_A_TYPE_STR, &data, &ind);
            if (error < 0)
            {
                printf ("get_data error: %d, %d\n", error, i);
                goto handle_error;
            }
            if (ind == -1)
            {
                printf ("NULL\t");
            }
            else
            {
                printf ("%s\t|", data);
            }
        }
            printf ("\n");
    }

    //Closing the request handle
    error = cci_close_req_handle (req);
    if (error < 0)
    {
        printf ("close_req_handle error: %d, %s\n", cci_error.err_code,
                cci_error.err_msg);
        goto handle_error;
    }

    //Disconnecting with the server
    error = cci_disconnect (con, &cci_error);
    if (error < 0)
    {
        printf ("error: %d, %s\n", cci_error.err_code, cci_error.err_msg);
        goto handle_error;
    }

    return 0;

handle_error:
    if (req > 0)
        cci_close_req_handle (req);
    if (con > 0)
        cci_disconnect (con, &cci_error);
    return 1;
}

예제 3

// Example to use connection/statement pool in CCI
// In Linux: gcc -o cci_pool cci_pool.c -m64 -I${CUBRID}/include -lnsl ${CUBRID}/lib/libcascci.so -lpthread

#include <stdio.h>
#include "cas_cci.h"

int main ()
{
    T_CCI_PROPERTIES *ps = NULL;
    T_CCI_DATASOURCE *ds = NULL;
    T_CCI_ERROR err;
    T_CCI_CONN cons;
    int rc = 1, i;

    ps = cci_property_create ();
    if (ps == NULL)
    {
        fprintf (stderr, "Could not create T_CCI_PROPERTIES.\n");
        rc = 0;
        goto cci_pool_end;
    }

    cci_property_set (ps, "user", "dba");
    cci_property_set (ps, "url", "cci:cubrid:localhost:33000:demodb:::");
    cci_property_set (ps, "pool_size", "10");
    cci_property_set (ps, "max_wait", "1200");
    cci_property_set (ps, "pool_prepared_statement", "true");
    cci_property_set (ps, "login_timeout", "300000");
    cci_property_set (ps, "query_timeout", "3000");

    ds = cci_datasource_create (ps, &err);
    if (ds == NULL)
    {
        fprintf (stderr, "Could not create T_CCI_DATASOURCE.\n");
        fprintf (stderr, "E[%d,%s]\n", err.err_code, err.err_msg);
        rc = 0;
        goto cci_pool_end;
    }

    for (i = 0; i < 3; i++)
    {
        cons = cci_datasource_borrow (ds, &err);
        if (cons < 0)
        {
            fprintf (stderr,
                    "Could not borrow a connection from the data source.\n");
            fprintf (stderr, "E[%d,%s]\n", err.err_code, err.err_msg);
            continue;
        }
        // put working code here.
        cci_work (cons);
        cci_datasource_release (ds, cons, &err);

    }

cci_pool_end:
  cci_property_destroy (ps);
  cci_datasource_destroy (ds);

  return 0;
}

// working code
int cci_work (T_CCI_CONN con)
{
    T_CCI_ERROR err;
    char sql[4096];
    int req, res, error, ind;
    int data;

    cci_set_autocommit (con, CCI_AUTOCOMMIT_TRUE);
    cci_set_lock_timeout (con, 100, &err);
    cci_set_isolation_level (con, TRAN_REP_CLASS_COMMIT_INSTANCE, &err);

    error = 0;
    snprintf (sql, 4096, "SELECT host_year FROM record WHERE athlete_code=11744");
    req = cci_prepare (con, sql, 0, &err);
    if (req < 0)
    {
        printf ("prepare error: %d, %s\n", err.err_code, err.err_msg);
        return error;
    }

    res = cci_execute (req, 0, 0, &err);
    if (res < 0)
    {
        printf ("execute error: %d, %s\n", err.err_code, err.err_msg);
        goto cci_work_end;
    }

    while (1)
    {
    error = cci_cursor (req, 1, CCI_CURSOR_CURRENT, &err);
    if (error == CCI_ER_NO_MORE_DATA)
    {
        break;
    }
    if (error < 0)
    {
        printf ("cursor error: %d, %s\n", err.err_code, err.err_msg);
        goto cci_work_end;
    }

    error = cci_fetch (req, &err);
    if (error < 0)
    {
        printf ("fetch error: %d, %s\n", err.err_code, err.err_msg);
        goto cci_work_end;
    }

    error = cci_get_data (req, 1, CCI_A_TYPE_INT, &data, &ind);
    if (error < 0)
    {
        printf ("get data error: %d\n", error);
        goto cci_work_end;
    }
    printf ("%d\n", data);
    }

    error = 1;
cci_work_end:
    cci_close_req_handle (req);
    return error;
}

라이브러리 적용

CCI를 이용한 응용 프로그램을 작성했다면 프로그램 특성에 따라 정적 링크 형태로 프로그램을 수행시킬 것인지, 아니면 동적으로 CCI를 호출하여 사용할 것인지를 결정하여 프로그램을 빌드한다. CCI 개요 의 표를 참조하여 사용할 라이브러리를 결정한다.

다음은 유닉스/Linux에서 동적인 라이브러리를 사용하여 링크하는 Makefile의 예제이다.

CC=gcc
CFLAGS = -g -Wall -I. -I$CUBRID/include
LDFLAGS = -L$CUBRID/lib -lcascci -lnsl
TEST_OBJS = test.o
EXES = test
all: $(EXES)
test: $(TEST_OBJS)
    $(CC) -o $@ $(TEST_OBJS) $(LDFLAGS)

다음은 Windows에서 정적 라이브러리를 적용하기 위한 설정이다.

../_images/image55.png

BLOB/CLOB 사용

LOB 데이터 저장

CCI 응용 프로그램에서 다음 함수를 사용하여 LOB 데이터 파일을 생성하고 데이터를 바인딩할 수 있다.

예제

int con = 0; /* connection handle */
int req = 0; /* request handle */
int res;
int n_executed;
int i;
T_CCI_ERROR error;
T_CCI_BLOB blob = NULL;
char data[1024] = "bulabula";

con = cci_connect ("localhost", 33000, "tdb", "PUBLIC", "");
if (con < 0) {
    goto handle_error;
}
req = cci_prepare (con, "insert into doc (doc_id, content) values (?,?)", 0, &error);
if (req< 0)
{
    goto handle_error;
}

res = cci_bind_param (req, 1 /* binding index*/, CCI_A_TYPE_STR, "doc-10", CCI_U_TYPE_STRING, CCI_BIND_PTR);

/* Creating an empty LOB data file */
res = cci_blob_new (con, &blob, &error);
res = cci_blob_write (con, blob, 0 /* start position */, 1024 /* length */, data, &error);

/* Binding BLOB data */
res = cci_bind_param (req, 2 /* binding index*/, CCI_A_TYPE_BLOB, (void *)blob, CCI_U_TYPE_BLOB, CCI_BIND_PTR);

n_executed = cci_execute (req, 0, 0, &error);
if (n_executed < 0)
{
    goto handle_error;
}

/* Commit */
if (cci_end_tran(con, CCI_TRAN_COMMIT, &error) < 0)
{
    goto handle_error;
}

/* Memory free */
cci_blob_free(blob);
return 0;

handle_error:
if (blob != NULL)
{
    cci_blob_free(blob);
}
if (req > 0)
{
    cci_close_req_handle (req);
}
if (con > 0)
{
    cci_disconnect(con, &error);
}
return -1;

LOB 데이터 조회

CCI 응용 프로그램에서 다음 함수를 사용하여 LOB 데이터를 조회할 수 있다. LOB 타입 칼럼에 데이터를 입력하면 실제 LOB 데이터는 외부 저장소 내 파일에 저장되고 LOB 타입 칼럼에는 해당 파일을 참조하는 Locator 값이 저장되므로, 파일에 저장된 LOB 데이터를 조회하기 위해서는 cci_get_data() 가 아닌 cci_blob_read() 함수를 호출해야 한다.

  • LOB 타입 칼럼 메타 데이터(Locator) 인출하기 (관련 함수: cci_get_data())
  • LOB 데이터를 인출하기 (관련 함수: cci_blob_read())
  • LOB 구조체에 대한 메모리 해제하기 (관련 함수: cci_blob_free())

예제

int con = 0; /* connection handle */
int req = 0; /* request handle */
int ind; /* NULL indicator, 0 if not NULL, -1 if NULL*/
int res;
int i;
T_CCI_ERROR error;
T_CCI_BLOB blob;
char buffer[1024];

con = cci_connect ("localhost", 33000, "image_db", "PUBLIC", "");
if (con < 0)
{
    goto handle_error;
}
req = cci_prepare (con, "select content from doc_t", 0 /*flag*/, &error);
if (req< 0)
{
    goto handle_error;
}

res = cci_execute (req, 0/*flag*/, 0/*max_col_size*/, &error);

while (1) {
    res = cci_cursor (req, 1/* offset */, CCI_CURSOR_CURRENT/* cursor position */, &error);
    if (res == CCI_ER_NO_MORE_DATA)
    {
        break;
    }
    res = cci_fetch (req, &error);

    /* Fetching CLOB Locator */
    res = cci_get_data (req, 1 /* colume index */, CCI_A_TYPE_BLOB,
    (void *)&blob /* BLOB handle */, &ind /* NULL indicator */);
    /* Fetching CLOB data */
    res = cci_blob_read (con, blob, 0 /* start position */, 1024 /* length */, buffer, &error);
    printf ("content = %s\n", buffer);
}

/* Memory free */
cci_blob_free(blob);
res=cci_close_req_handle(req);
res = cci_disconnect (con, &error);
return 0;

handle_error:
if (req > 0)
{
    cci_close_req_handle (req);
}
if (con > 0)
{
    cci_disconnect(con, &error);
}
return -1;

CCI 에러 코드와 에러 메시지

CCI API 함수는 에러 발생 시 반환 값이 음수인 CCI 에러 코드 혹은 CAS(브로커 응용 서버) 에러 코드를 반환한다. CCI 에러 코드는 CCI API 함수에서 발생하며, CAS 에러 코드는 CAS에서 발생한다.

  • 모든 에러 코드의 값은 0보다 작은 음수이다.
  • T_CCI_ERROR err_buf를 인자로 가지는 모든 함수의 에러 코드와 에러 메시지는 err_buf.err_code와 err_buf.err_msg에서 확인할 수 있다.
  • T_CCI_ERROR err_buf 인자가 없는 함수의 에러 메시지는 cci_get_err_msg() 함수를 이용하여 에러 코드가 나타내는 에러 메시지를 출력할 수 있다.
  • 에러 번호가 -20002부터 -20999 사이이면, CCI API 함수에서 발생하는 에러이다.
  • 에러 번호가 -10000부터 -10999 사이이면, CAS에서 발생하는 에러를 CCI API 함수가 전달받아 반환하는 에러이다. CAS 에러는 CAS 에러를 참고한다.
  • 함수가 리턴하는 에러 코드의 값이 CCI_ER_DBMS (-20001)인 경우, 데이터베이스 서버에서 발생하는 에러이다. 데이터베이스 서버 에러와 관련한 내용은 데이터베이스 서버 에러를 참고한다.

Warning

서버에서 에러가 발생한 경우 함수가 리턴하는 에러 코드인 CCI_ER_DBMS 와 err_buf.err_code 값이 서로 다름에 주의한다. 서버 에러 외에 err_buf에 저장되는 모든 에러 코드는 함수가 리턴하는 에러 코드와 동일하다.

Note

CUBRID 9.0 미만 버전에서의 CCI, CAS 에러 코드는 CUBRID 9.0 이상 버전의 에러 코드와 다른 값을 가진다. 따라서 에러 코드명을 사용하여 개발한 사용자는 응용 프로그램을 재컴파일하여 사용해야 하며, 에러 코드 번호를 직접 부여하여 개발한 사용자는 번호 값을 바꾼 후 응용 프로그램을 재컴파일해야 한다.

데이터베이스 에러 버퍼(err_buf)는 cas_cci.h 헤더 파일의 T_CCI_ERROR 구조체 변수이다. 사용법은 아래의 예제 프로그램을 참고한다.

CCI_ER 로 시작되는 CCI 에러 코드는 $CUBRID/include/cas_cci.h 파일에 T_CCI_ERROR_CODE 라는 enum 구조체 내에 정의되어 있다. 따라서 프로그램 코드에서 이 에러 코드 명을 사용하려면 코드 상단에 #include “cas_cci.h” 를 입력하여 헤더 파일을 포함해야 한다.

아래의 프로그램은 에러 메시지를 출력한다. 이때 cci_prepare() 가 리턴하는 에러 코드 값 req의 값은 CCI_ER_DMBS 이고, 데이터베이스 에러 버퍼의 cci_error.err_code 에는 서버 에러 코드인 -493이, cci_error.err_msg 에는 ‘Syntax: Unknown class “notable”. select * from notable’이라는 에러 메시지가 저장된다.

// gcc -o err err.c -m64 -I${CUBRID}/include -lnsl ${CUBRID}/lib/libcascci.so -lpthread
#include <stdio.h>
#include "cas_cci.h"

#define BUFSIZE  (1024)

int
main (void)
{
    int con = 0, req = 0, col_count = 0, i, ind;
    int error;
    char *data;
    T_CCI_ERROR err_buf;
    char *query = "select * from notable";

    //getting a connection handle for a connection with a server
    con = cci_connect ("localhost", 33000, "demodb", "dba", "");
    if (con < 0)
    {
        printf ("cannot connect to database\n");
        return 1;
    }

    //preparing the SQL statement
    req = cci_prepare (con, query, 0, &err_buf);
    if (req < 0)
    {
        if (req == CCI_ER_DBMS)
        {
            printf ("error from server: %d, %s\n", err_buf.err_code, err_buf.err_msg);
        }
        else
        {
            printf ("error from cci or cas: %d, %s\n", err_buf.err_code, err_buf.err_msg);
        }
        goto handle_error;
    }
    // ...
}

다음은 CCI 함수의 에러 코드를 나타낸다. CAS 에러는 CAS 에러를 참고한다.

에러 코드명(에러 번호) 에러 메시지 비고
CCI_ER_DBMS(-20001) CUBRID DBMS Error 서버에서 에러가 발생한 경우 함수가 반환하는 에러 코드. 실패 원인은 T_CCI_ERROR 구조체에 저장되는 err_code와 err_msg로 확인 가능.
CCI_ER_CON_HANDLE(-20002) Invalid connection handle  
CCI_ER_NO_MORE_MEMORY(-20003) Memory allocation error 사용 가능한 메모리가 부족함.
CCI_ER_COMMUNICATION(-20004) Cannot communicate with server  
CCI_ER_NO_MORE_DATA(-20005) Invalid cursor position  
CCI_ER_TRAN_TYPE(-20006) Unknown transaction type  
CCI_ER_STRING_PARAM(-20007) Invalid string argument cci_prepare(), cci_prepare_and_execute() 에서 sql_stmt가 NULL이면 발생하는 에러
CCI_ER_TYPE_CONVERSION(-20008) Type conversion error 주어진 타입의 값을 실제 데이터의 타입으로 변경할 수 없음.
CCI_ER_BIND_INDEX(-20009) Parameter index is out of range 바인드할 데이터의 index가 유효하지 않음.
CCI_ER_ATYPE(-20010) Invalid T_CCI_A_TYPE value  
CCI_ER_NOT_BIND(-20011)   사용되지 않음
CCI_ER_PARAM_NAME(-20012) Invalid T_CCI_DB_PARAM value  
CCI_ER_COLUMN_INDEX(-20013) Column index is out of range  
CCI_ER_SCHEMA_TYPE(-20014)   사용되지 않음
CCI_ER_FILE(-20015) Cannot open file 파일을 열거나 읽기/쓰기 실패함.
CCI_ER_CONNECT(-20016) Cannot connect to CUBRID CAS 서버와 연결 시도 시 CAS 접속에 실패함.
CCI_ER_ALLOC_CON_HANDLE(-20017) Cannot allocate connection handle %  
CCI_ER_REQ_HANDLE(-20018) Cannot allocate request handle %  
CCI_ER_INVALID_CURSOR_POS(-20019) Invalid cursor position  
CCI_ER_OBJECT(-20020) Invalid oid string  
CCI_ER_CAS(-20021)   사용되지 않음
CCI_ER_HOSTNAME(-20022) Unknown host name  
CCI_ER_OID_CMD(-20023) Invalid T_CCI_OID_CMD value  
CCI_ER_BIND_ARRAY_SIZE(-20024) Array binding size is not specified  
CCI_ER_ISOLATION_LEVEL(-20025) Unknown transaction isolation level  
CCI_ER_SET_INDEX(-20026) Invalid set index T_CCI_SET 구조체에 포함된 set원소를 가져올 때 잘못된 위치가 지정됨.
CCI_ER_DELETED_TUPLE(-20027) Current row was deleted %  
CCI_ER_SAVEPOINT_CMD(-20028) Invalid T_CCI_SAVEPOINT_CMD value cci_savepoint() 함수의 인자로 유효하지 않은 T_CCI_SAVEPOINT_CMD 값이 사용됨.
CCI_ER_THREAD_RUNNING(-20029) I
CCI_ER_INVALID_URL(-20030) Invalid url string  
CCI_ER_INVALID_LOB_READ_POS(-20031) Invalid lob read position  
CCI_ER_INVALID_LOB_HANDLE(-20032) Invalid lob handle  
CCI_ER_NO_PROPERTY(-20033) Could not find a property  
CCI_ER_PROPERTY_TYPE(-20034) Invalid property type  
CCI_ER_INVALID_DATASOURCE(-20035) Invalid CCI datasource  
CCI_ER_DATASOURCE_TIMEOUT(-20036) All connections are used  
CCI_ER_DATASOURCE_TIMEDWAIT(-20037) pthread_cond_timedwait error  
CCI_ER_LOGIN_TIMEOUT(-20038) Connection timed out  
CCI_ER_QUERY_TIMEOUT(-20039) Request timed out  
CCI_ER_RESULT_SET_CLOSED(-20040)    
CCI_ER_INVALID_HOLDABILITY(-20041) Invalid holdability mode. The only accepted values are 0 or 1  
CCI_ER_NOT_UPDATABLE(-20042) Request handle is not updatable  
CCI_ER_INVALID_ARGS(-20043) Invalid argument  
CCI_ER_USED_CONNECTION(-20044) This connection is used already.  

C Type Definition

다음은 CCI API 함수에서 사용하는 구조체들이다.

이름 타입 멤버 설명
T_CCI_ERROR struct char err_msg[1024] 데이터베이스 에러 정보 표현
int err_code
T_CCI_BIT struct int size bit 타입 표현
char *buf
T_CCI_DATE struct short yr datetime, timestamp, date, time 타입 표현
short mon
short day
short hh
short mm
short ss
short ms
T_CCI_DATE_TZ struct short yr timezone과 date/time 타입 표현
short mon
short day
short hh
short mm
short ss
short ms
char tz[64]
T_CCI_SET void*   set 타입 표현
T_CCI_COL_INFO struct T_CCI_U_EXT_TYPE type SELECT 문에 대한 칼럼 정보 표현
char is_non_null
short scale
int precision
char *col_name
char *real_attr
char *class_name
T_CCI_QUERY_RESULT struct int result_count batch 실행에 대한 결과
int stmt_type
char *err_msg
char oid[32]
T_CCI_PARAM_INFO struct T_CCI_PARAM_MODE mode input 파라미터에 대한 정보 표현
T_CCI_U_TYPE type
short scale
int precision
T_CCI_U_EXT_TYPE unsigned char   데이터베이스 타입 정보
T_CCI_U_TYPE enum CCI_U_TYPE_UNKNOWN 데이터베이스 타입 정보
CCI_U_TYPE_NULL
CCI_U_TYPE_CHAR
CCI_U_TYPE_STRING
CCI_U_TYPE_BIT
CCI_U_TYPE_VARBIT
CCI_U_TYPE_NUMERIC
CCI_U_TYPE_INT
CCI_U_TYPE_SHORT
CCI_U_TYPE_FLOAT
CCI_U_TYPE_DOUBLE
CCI_U_TYPE_DATE
CCI_U_TYPE_TIME
CCI_U_TYPE_TIMESTAMP
CCI_U_TYPE_SET
CCI_U_TYPE_MULTISET
CCI_U_TYPE_SEQUENCE
CCI_U_TYPE_OBJECT
CCI_U_TYPE_BIGINT
CCI_U_TYPE_DATETIME
CCI_U_TYPE_BLOB
CCI_U_TYPE_CLOB
CCI_U_TYPE_ENUM
CCI_U_TYPE_UINT
CCI_U_TYPE_USHORT
CCI_U_TYPE_UBIGINT
CCI_U_TYPE_TIMESTAMPTZ
CCI_U_TYPE_TIMESTAMPLTZ
CCI_U_TYPE_DATETIMETZ
CCI_U_TYPE_DATETIMELTZ
T_CCI_A_TYPE enum CCI_A_TYPE_STR API에서 사용되는 타입 정보 표현
CCI_A_TYPE_INT
CCI_A_TYPE_FLOAT
CCI_A_TYPE_DOUBLE
CCI_A_TYPE_BIT
CCI_A_TYPE_DATE
CCI_A_TYPE_SET
CCI_A_TYPE_BIGINT
CCI_A_TYPE_BLOB
CCI_A_TYPE_CLOB
CCI_A_TYPE_CLOB
CCI_A_TYPE_REQ_HANDLE
CCI_A_TYPE_UINT
CCI_A_TYPE_UBIGINT
CCI_A_TYPE_DATE_TZ
CCI_A_TYPE_UINT
T_CCI_DB_PARAM enum CCI_PARAM_ISOLATION_LEVEL 시스템 파라미터 이름
CCI_PARAM_LOCK_TIMEOUT
CCI_PARAM_MAX_STRING_LENGTH
CCI_PARAM_AUTO_COMMIT
T_CCI_SCH_TYPE enum CCI_SCH_CLASS  
CCI_SCH_VCLASS
CCI_SCH_QUERY_SPEC
CCI_SCH_ATTRIBUTE
CCI_SCH_CLASS_ATTRIBUTE
CCI_SCH_METHOD
CCI_SCH_CLASS_METHOD
CCI_SCH_METHOD_FILE
CCI_SCH_SUPERCLASS
CCI_SCH_SUBCLASS
CCI_SCH_CONSTRAIT
CCI_SCH_TRIGGER
CCI_SCH_CLASS_PRIVILEGE
CCI_SCH_ATTR_PRIVILEGE
CCI_SCH_DIRECT_SUPER_CLASS
CCI_SCH_PRIMARY_KEY
CCI_SCH_IMPORTED_KEYS
CCI_SCH_EXPORTED_KEYS
CCI_SCH_CROSS_REFERENCE
T_CCI_CUBRID_STMT enum CUBRID_STMT_ALTER_CLASS  
    CUBRID_STMT_ALTER_SERIAL  
CUBRID_STMT_COMMIT_WORK
CUBRID_STMT_REGISTER_DATABASE
CUBRID_STMT_CREATE_CLASS
CUBRID_STMT_CREATE_INDEX
CUBRID_STMT_CREATE_TRIGGER
CUBRID_STMT_CREATE_SERIAL
CUBRID_STMT_DROP_DATABASE
CUBRID_STMT_DROP_CLASS
CUBRID_STMT_DROP_INDEX
CUBRID_STMT_DROP_LABEL
CUBRID_STMT_DROP_TRIGGER
CUBRID_STMT_DROP_SERIAL
CUBRID_STMT_EVALUATE
CUBRID_STMT_RENAME_CLASS
CUBRID_STMT_ROLLBACK_WORK
CUBRID_STMT_GRANT
CUBRID_STMT_REVOKE
CUBRID_STMT_STATISTICS
CUBRID_STMT_INSERT
CUBRID_STMT_SELECT
CUBRID_STMT_UPDATE
CUBRID_STMT_DELETE
CUBRID_STMT_CALL
CUBRID_STMT_GET_ISO_LVL
CUBRID_STMT_GET_TIMEOUT
CUBRID_STMT_GET_OPT_LVL
CUBRID_STMT_SET_OPT_LVL
CUBRID_STMT_SCOPE
CUBRID_STMT_GET_TRIGGER
CUBRID_STMT_SET_TRIGGER
CUBRID_STMT_SAVEPOINT
CUBRID_STMT_PREPARE
CUBRID_STMT_ATTACH
CUBRID_STMT_USE
CUBRID_STMT_REMOVE_TRIGGER
CUBRID_STMT_RENAME_TRIGGER
CUBRID_STMT_ON_LDB
CUBRID_STMT_GET_LDB
CUBRID_STMT_SET_LDB
CUBRID_STMT_GET_STATS
CUBRID_STMT_CREATE_USER
CUBRID_STMT_DROP_USER
CUBRID_STMT_ALTER_USER
CUBRID_STMT_SET_SYS_PARAMS
CUBRID_STMT_ALTER_INDEX
CUBRID_STMT_CREATE_STORED_PROCEDURE
CUBRID_STMT_DROP_STORED_PROCEDURE
CUBRID_STMT_PREPARE_STATEMENT
CUBRID_STMT_EXECUTE_PREPARE
CUBRID_STMT_DEALLOCATE_PREPARE
CUBRID_STMT_TRUNCATE
CUBRID_STMT_DO
CUBRID_STMT_SELECT_UPDATE
CUBRID_STMT_SET_SESSION_VARIABLES
CUBRID_STMT_DROP_SESSION_VARIABLES
CUBRID_STMT_MERGE
CUBRID_STMT_SET_NAMES
CUBRID_STMT_ALTER_STORED_PROCEDURE
CUBRID_STMT_KILL
T_CCI_CURSOR_POS enum CCI_CURSOR_FIRST  
CCI_CURSOR_CURRENT
CCI_CURSOR_LAST
T_CCI_TRAN_ISOLATION enum TRAN_READ_COMMITTED  
TRAN_REPEATABLE_READ
TRAN_SERIALIZABLE
T_CCI_PARAM_MODE enum CCI_PARAM_MODE_UNKNOWN  
CCI_PARAM_MODE_IN
CCI_PARAM_MODE_OUT
CCI_PARAM_MODE_INOUT

Note

칼럼에서 정의한 크기보다 큰 문자열을 INSERT / UPDATE 하면 문자열이 잘려서 입력된다.

CCI 예제 프로그램

예제 프로그램은 CUBRID 설치 과정에서 기본적으로 배포되는 데이터베이스인 demodb 를 활용하여 CCI를 사용하는 응용 프로그램을 간단하게 작성한 것이다. 예제를 통하여 CAS와 연결하기, 질의 준비, 질의 수행, 응답 처리, 연결 끊기 등의 과정을 따라한다. 예제는 Linux 기반의 동적 링크를 적용하는 방법으로 작성되었다.

다음은 예제에서 사용하는 demodb 데이터베이스의 olympic 테이블의 스키마 정보이다.

csql> ;sc olympic

=== <Help: Schema of a Class> ===


 <Class Name>

     olympic

 <Attributes>

     host_year            INTEGER NOT NULL
     host_nation          CHARACTER VARYING(40) NOT NULL
     host_city            CHARACTER VARYING(20) NOT NULL
     opening_date         DATE NOT NULL
     closing_date         DATE NOT NULL
     mascot               CHARACTER VARYING(20)
     slogan               CHARACTER VARYING(40)
     introduction         CHARACTER VARYING(1500)

 <Constraints>

     PRIMARY KEY pk_olympic_host_year ON olympic (host_year)

준비

예제 프로그램을 수행하기 전에 반드시 확인해야 할 사항은 demodb 데이터베이스와 브로커의 가동 여부이다. demodb 데이터베이스와 브로커는 cubrid 유틸리티를 이용하여 시작할 수 있다. 다음은 cubrid 유틸리티를 이용하여 데이터베이스 서버와 브로커를 가동하는 예제이다.

[tester@testdb ~]$ cubrid server start demodb
@ cubrid master start
++ cubrid master start: success
@ cubrid server start: demodb

This may take a long time depending on the amount of recovery works to do.

CUBRID 9.2

++ cubrid server start: success
[tester@testdb ~]$ cubrid broker start
@ cubrid broker start
++ cubrid broker start: success

빌드

프로그램 소스와 Makefile이 준비된 상태에서 make 를 수행하면 test 라는 실행 파일이 생성된다. 정적 라이브러리를 사용하면 추가로 파일을 배포할 필요가 없고 속도가 빠르다. 하지만, 프로그램의 크기와 메모리 사용량이 커지는 단점이 있다. 동적 라이브러리를 사용하면 성능상의 오버헤드는 있지만, 메모리와 프로그램 크기에 있어 최적화를 이룰 수 있다.

다음은 Linux에서 make 를 사용하지 않고 동적인 라이브러리를 사용하여 테스트 프로그램을 빌드하는 명령 행의 예제이다.

cc -o test test.c -I$CUBRID/include -L$CUBRID/lib -lnsl -lcascci

예제 코드

#include <stdio.h>
#include <cas_cci.h>
char *cci_client_name = "test";
int main (int argc, char *argv[])
{
    int con = 0, req = 0, col_count = 0, res, ind, i;
    T_CCI_ERROR error;
    T_CCI_COL_INFO *res_col_info;
    T_CCI_CUBRID_STMT stmt_type;
    char *buffer, db_ver[16];
    printf("Program started!\n");
    if ((con=cci_connect("localhost", 30000, "demodb", "PUBLIC", ""))<0) {
        printf( "%s(%d): cci_connect fail\n", __FILE__, __LINE__);
        return -1;
    }

    if ((res=cci_get_db_version(con, db_ver, sizeof(db_ver)))<0) {
        printf( "%s(%d): cci_get_db_version fail\n", __FILE__, __LINE__);
        goto handle_error;
    }
    printf("DB Version is %s\n",db_ver);
    if ((req=cci_prepare(con, "select * from event", 0,&error))<0) {
        if (req < 0) {
            printf( "%s(%d): cci_prepare fail(%d)\n", __FILE__, __LINE__,error.err_code);
        }
        goto handle_error;
    }
    printf("Prepare ok!(%d)\n",req);
    res_col_info = cci_get_result_info(req, &stmt_type, &col_count);
    if (!res_col_info) {
        printf( "%s(%d): cci_get_result_info fail\n", __FILE__, __LINE__);
        goto handle_error;
    }

    printf("Result column information\n"
           "========================================\n");
    for (i=1; i<=col_count; i++) {
        printf("name:%s  type:%d(precision:%d scale:%d)\n",
            CCI_GET_RESULT_INFO_NAME(res_col_info, i),
            CCI_GET_RESULT_INFO_TYPE(res_col_info, i),
            CCI_GET_RESULT_INFO_PRECISION(res_col_info, i),
            CCI_GET_RESULT_INFO_SCALE(res_col_info, i));
    }
    printf("========================================\n");
    if ((res=cci_execute(req, 0, 0, &error))<0) {
        if (req < 0) {
            printf( "%s(%d): cci_execute fail(%d)\n", __FILE__, __LINE__,error.err_code);
        }
        goto handle_error;
    }

    while (1) {
        res = cci_cursor(req, 1, CCI_CURSOR_CURRENT, &error);
        if (res == CCI_ER_NO_MORE_DATA) {
            printf("Query END!\n");
            break;
        }
        if (res<0) {
            if (req < 0) {
                printf( "%s(%d): cci_cursor fail(%d)\n", __FILE__, __LINE__,error.err_code);
            }
            goto handle_error;
        }

        if ((res=cci_fetch(req, &error))<0) {
            if (res < 0) {
                printf( "%s(%d): cci_fetch fail(%d)\n", __FILE__, __LINE__,error.err_code);
            }
            goto handle_error;
        }

        for (i=1; i<=col_count; i++) {
            if ((res=cci_get_data(req, i, CCI_A_TYPE_STR, &buffer, &ind))<0) {
                printf( "%s(%d): cci_get_data fail\n", __FILE__, __LINE__);
                goto handle_error;
            }
            printf("%s \t|", buffer);
        }
        printf("\n");
    }
    if ((res=cci_close_req_handle(req))<0) {
        printf( "%s(%d): cci_close_req_handle fail", __FILE__, __LINE__);
       goto handle_error;
    }
    if ((res=cci_disconnect(con, &error))<0) {
        if (res < 0) {
            printf( "%s(%d): cci_disconnect fail(%d)", __FILE__, __LINE__,error.err_code);
        }
        goto handle_error;
    }
    printf("Program ended!\n");
    return 0;

    handle_error:
    if (req > 0)
        cci_close_req_handle(req);
    if (con > 0)
        cci_disconnect(con, &error);
    printf("Program failed!\n");
    return -1;
}