콘텐츠로 이동

9.응용프로그램 작성#

이 장에서는 Altibase를 활용하는 응용 프로그램 작성법에 관하여 간략하게 설명하도록 한다.

응용 프로그램 작성 방법#

Altibase CLI, JDBC, ODBC, APRE C/C++ Precompiler 등을 사용하여 Altibase 응용 프로그램을 개발하는 것은 범용 데이터베이스 운용 환경에서의 응용 프로그램 작성법과 크게 다르지 않다.

본 장에서는 Altibase 응용 프로그램 작성법에 관하여 간략하게 설명하도록 한다.

응용 프로그램 작성의 자세한 내용은 CLI User's Manual, ODBC User's Manual, Precompiler User’s Manual, 및 API User’s Manual 을 참조한다.

Altibase CLI를 활용한 프로그램#

이 절은 Altibase CLI를 이용하여 클라이언트 응용 프로그램를 작성하는 방법을 설명한다. Altibase CLI는 Altibase를 클라이언트-서버 구조로 운영하는 상황에서 사용할 수 있는 API이다. 자세한 내용은 CLI User's Manual을 참조한다.

헤더 파일과 라이브러리#

Altibase CLI를 이용하여 프로그램을 작성하기 위해서는 Altibase의 설치 홈 디렉토리의 서브 디렉토리인 include와 lib에 있는 헤더 파일과 라이브러리 파일이 필요하다.

$ALTIBASE_HOME/include/sqlcli.h
$ALTIBASE_HOME/lib/libodbccli.a

Makefile#

작성된 Altibase CLI 소스를 컴파일하기 위한 Makefile에는 다음과 같은 내용이 포함되어야 한다.

include $(ALTIBASE_HOME)/install/altibase_env.mk

이 파일 안에는 컴파일시 필요로 하는 라이브러리 경로와 라이브러리들을 링크시키기 위한 옵션들이 정의되어 있으며 오브젝트 파일을 만드는 규칙을 제공한다. \$ALTIBASE_HOME/sample/SQLCLI 내의 Makefile을 참조한다.

예문#
include $(ALTIBASE_HOME)/install/altibase_env.mk

SRCS=
OBJS=$(SRCS:.cpp=.$(OBJEXT))

BINS=demo_ex1

all: $(BINS)

demo_ex1: demo_ex1.$(OBJEXT)
    $(LD) $(LFLAGS) $(LDOUT)demo_ex1$(BINEXT) demo_ex1.$(OBJEXT) $(LIBOPT)odbccli$(LIBAFT) $(LIBS)

멀티 쓰레드 프로그램#

멀티 쓰레드 프로그램을 작성할 때 다음과 같은 사항을 주의해야 한다.

  • 각 쓰레드 별로 환경 핸들, 연결 핸들을 각각 할당해야 한다.

프로그램 작성#

Altibase CLI를 이용하여 작성하는 프로그램에서 Altibase 서버에 접속하고 종료하는 방법을 간략하게 설명하도록 한다.

Altibase CLI 프로그램의 예#
/* test.cpp */

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <time.h>
#include <sys/time.h>
#include <unistd.h>
#include <sqlcli.h>

void sbigint_bigint(int cnt);  
void slong_integer(int cnt);
void char_char(int cnt);
void char_number(int cnt);
void double_double(int cnt);
void prepare();
void execute();
void usage();
long logMsec(const char *astr);
void conn(char *port, char *conntype);

#define MSG_LEN 1024  

SQLHENV   env;  // Environment를 할당 받을 handle
SQLHDBC   con;  // Connection을 할당 받을 handle
SQLHSTMT  hstmt;  // Statement를 할당 받을 handle
SQLHSTMT  bstmt;  // Statement를 할당 받을 handle
int          errNo;
short        msgLength;
char         errMsg[MSG_LEN];
SQLRETURN rc;

/* Main프로그램 - 인자 개수가 5보다 적을 경우 사용법(usage) 출력 */
int main(int ac, char **av)  
{
    if (ac < 5) 
    {
        usage();
    }

    conn(av[2], av[3]);

    switch(atoi(av[1]))
    {
    case 1:
        logMsec(" BIGINT - START TIME : ");
        sbigint_bigint(atoi(av[4]));
        logMsec(" BIGINT - END TIME : ");
        break;
    case 2:
        logMsec(" INTEGER - START TIME : ");
        slong_integer(atoi(av[4]));
        logMsec(" INTEGER - END TIME : ");
        break;
    case 3:
        logMsec(" CHAR - START TIME : ");
        char_char(atoi(av[4]));
        logMsec(" CHAR - END TIME : ");
        break;
    case 4:
        logMsec(" NUMBER - START TIME : ");
        char_number(atoi(av[4]));
        logMsec(" NUMBER - END TIME : ");
        break;
    case 5:
        logMsec(" DOUBLE - START TIME : ");
        double_double(atoi(av[4]));
        logMsec(" DOUBLE - END TIME : ");
        break;
    }
}

/* 프로그램의 사용법을 출력 */
void usage()
{
    printf("Usage: ./test <program_no> <port_no> <conntype> <cnt>\n");
    printf("\tprogram_no : 1  => \t SBIGINT-BIGINT\n");
    printf("\tprogram_no : 2  => \t SLONG-INTEGER\n");
    printf("\tprogram_no : 3  => \t CHAR-CHAR\n");
    printf("\tprogram_no : 4  => \t CHAR-NUMERIC\n");
    printf("\tprogram_no : 5  => \t DOUBLE-DOUBLE\n");
    exit(1);
}

/* 프로그램 시작시간 및 종료시간 체크 */
long logMsec(const char *astr)
{
    struct timeval  tv;
    struct tm       *ctm;

    gettimeofday(&tv,NULL);
    ctm = localtime(&(tv.tv_sec));

    fprintf(stderr, "%s [%.02d:%.02d:%.02d]\n", astr, ctm->tm_hour, 
ctm->tm_min, ctm->tm_sec);

    return tv.tv_usec;
}

/* Altibase 접속 구문 */
void conn(char *port, char *conntype)
{
    char connStr[200];
    char query[200];

    if (SQL_ERROR == SQLAllocEnv(&env))
    {
        fprintf(stderr, "SQLAllocEnv error!!\n"); 
//Environment를 위한 메모리를 할당.
        return;
    }

    if (SQL_ERROR == SQLAllocConnect(env, &con)) 
// connection을 위한 메모리를 할당
    {
        fprintf(stderr, "SQLAllocConnect error!!\n");
        SQLFreeEnv(env);
        return;
    }

    sprintf((char*)connStr, "DSN=127.0.0.1;PORT_NO=%s;UID=SYS;PWD=MANAGER;CONNTYPE=%s", port, conntype);

/* Connection 생성 */
    if (SQL_ERROR == SQLDriverConnect(con, NULL, (char*)connStr,
SQL_NTS, NULL, 0, NULL, SQL_DRIVER_NOPROMPT))
    {
        if (SQL_SUCCESS == SQLError(env, con, NULL, NULL, &errNo, (char*)errMsg, MSG_LEN, &msgLength)) 
        {
            fprintf(stderr, " rCM_-%d : %s\n", errNo, errMsg);
        }
        SQLFreeConnect(con);  
        SQLFreeEnv(env);
        return;
    }
/* 각 SQL문을 실행 시 자동으로 반영하지 않는다 */
    SQLSetConnectAttr(con, SQL_ATTR_AUTOCOMMIT, 
(void*)SQL_AUTOCOMMIT_OFF, 0);
    if (rc == SQL_ERROR)              
    {                                 
        if (SQL_SUCCESS == SQLError(env, con, NULL, NULL, &errNo, (char*)errMsg, MSG_LEN, &msgLength))
        {
            fprintf(stderr, "[%d : %s]\n", errNo, errMsg);
        }
    }

    hstmt = bstmt = SQL_NULL_HSTMT;  

    SQLAllocStmt(con, &hstmt);
    SQLAllocStmt(con, &bstmt);

/* DDL문을 직접 수행하며 오류발생시 메시지를 지정서식에 따라 파일로 출력 */
    strcpy(query, "drop table t1");   
    rc = SQLExecDirect(hstmt, (char*)query, SQL_NTS);  
    if (rc == SQL_ERROR)
    {
        if (SQL_SUCCESS == SQLError(env, con, hstmt, NULL, &errNo, (char*)errMsg, MSG_LEN, &msgLength))
        {
            fprintf(stderr, "[%d : %s]\n", errNo, errMsg); 
}
    }

/* DDL문을 직접 수행하며 오류발생시 메시지를 지정서식에 따라 파일로 출력 */
    strcpy(query, "create table t1(i1 number(6))");
    rc = SQLExecDirect(hstmt, (char*)query, SQL_NTS);
    if (rc == SQL_ERROR)
    {
        if (SQL_SUCCESS == SQLError(env, con, hstmt, NULL, &errNo, (char*)errMsg, MSG_LEN, &msgLength))
        {
            fprintf(stderr, "[%d : %s]\n", errNo, errMsg); 
}
    }
}

/* 실행할 SQL문을 준비 */
void prepare()   
{
    char query[100];

    strcpy(query, "insert into t1 values(?)");

    rc = SQLPrepare(bstmt, (char*)query, SQL_NTS);
    if (rc == SQL_ERROR)              
    {                                 
        if (SQL_SUCCESS == SQLError(env, con, bstmt, NULL, &errNo, (char*)errMsg, MSG_LEN, &msgLength))
        {
            fprintf(stderr, "[%d : %s]\n", errNo, errMsg);
        }
    }
}

/* 준비된 SQL문장을 실행 */
void execute()  
{
    rc = SQLExecute(bstmt);
    if (rc == SQL_ERROR)              
    {                                 
        if (SQL_SUCCESS == SQLError(env, con, bstmt, NULL, &errNo, (char*)errMsg, MSG_LEN, &msgLength))
        {
            fprintf(stderr, "[%d : %s]\n", errNo, errMsg);
        }
    }
}

void sbigint_bigint(int cnt)
{
    int i;
    long long i1;
    char tmp[100];
    int len = SQL_NTS;

    prepare();

/* SQL문장에 매개변수(parameter)를 연결시킴 */
    SQLBindParameter(bstmt, 1, SQL_PARAM_INPUT, SQL_C_SBIGINT, 
SQL_BIGINT, 0, 0, (void*)&i1, 0, &len);

    for(i=0; i<cnt; i++)
    {
        sprintf(tmp, "%d", i);
        i1 = atol(tmp);
        execute();
    }

/* 트랜잭션을 COMMIT 처리 */
    rc = SQLTransact(NULL, con, SQL_COMMIT); 
}

void slong_integer(int cnt)
{
    int i;
    int i1;
    char tmp[100];
    int len = SQL_NTS;

    prepare();

/* SQL문장에 매개변수(parameter)를 연결시킴 */
    SQLBindParameter(bstmt, 1, SQL_PARAM_INPUT, SQL_C_SLONG, 
SQL_INTEGER, 0, 0, (void*)&i1, 0, &len);

    for(i=0; i<cnt; i++)
    {
        sprintf(tmp, "%d", i);
        i1 = atoi(tmp);
        execute();
    }

/* 트랜잭션을 COMMIT 처리 */
    SQLTransact(NULL, con, SQL_COMMIT);
}

void char_char(int cnt)
{
    int  i;
    char i1[100];
    char tmp[100];
    int len = SQL_NTS;

    prepare();

/* SQL문장에 매개변수(parameter)를 연결시킴 */
    SQLBindParameter(bstmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, 
SQL_CHAR, sizeof(i1)-1, 0, (void*)i1, sizeof(i1), &len);

    for(i=0; i<cnt; i++)
    {
        sprintf(tmp, "%d", i);
        strcpy(i1, tmp);
        execute();
    }

/* 트랜잭션을 COMMIT 처리 */
    SQLTransact(NULL, con, SQL_COMMIT);
}

void char_number(int cnt)
{
    int  i;
    char i1[100];
    char tmp[100];
    int len = SQL_NTS;

    prepare();

    SQLBindParameter(bstmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, 
SQL_NUMERIC, sizeof(i1)-1, 0, (void*)i1, sizeof(i1), 
&len);

    for(i=0; i<cnt; i++)
    {
        sprintf(tmp, "%d", i);
        strcpy(i1, tmp);
        execute();
    }

/* 트랜잭션을 COMMIT 처리 */
    SQLTransact(NULL, con, SQL_COMMIT);
}

void double_double(int cnt)
{
    int  i;
    double i1;
    char tmp[100];
    int len = SQL_NTS;

    prepare();

    SQLBindParameter(bstmt, 1, SQL_PARAM_INPUT, SQL_C_DOUBLE, 
SQL_DOUBLE, 0, 0, (void*)&i1, 0, &len);

    for(i=0; i<cnt; i++)
    {
        sprintf(tmp, "%d", i);
        i1 = atof(tmp);
        execute();
    }

/* 트랜잭션을 COMMIT 처리 */
    SQLTransact(NULL, con, SQL_COMMIT);
}
실행결과#
$ make test
$ ./test 3 20300 1 100

BIGINT - START TIME :  [16:43:48]
BIGINT - END TIME :  [16:43:49]

JDBC를 활용한 프로그램#

Altibase의 JDBC 드라이버를 이용하여 클라이언트를 작성하는 방법이다. 자세한 내용은 JDBC User's Manual을 참조한다.

JDBC 드라이버#

Altibase는 기본적으로 \$ALTIBASE_HOME/lib 디렉토리 안에 Altibase.jar 파일을 JDBC 드라이버로 제공한다.

Altibase 서버와 연결을 설정하기 위해, 먼저 드라이버를 로드하고, URL과의 연결을 시도한다. Altibase JDBC 드라이버에서 지원하는 URL 형식은 다음과 같다.

jdbc:Altibase://hostname:portnum/databasename
  1. JDBC 드라이버를 로드할 때에는 프로그램 내에서 다음과 같은 코드로 JDBC 드라이버를 등록하여 사용한다.

    Class.forName(“Altibase.jdbc.driver.AltibaseDriver”)
    
  2. Altibase의 URL을 제공하고, URL과 연결을 시도하기 위한 일반적인 방법으로 다음과 같다. (데이터베이스에 로그인 하기 위한 id가 "SYS"이고, 패스워드가 "MANAGER"인 경우)

     String url = "jdbc:Altibase://127.0.0.1:20300/mydb";
     Connection con = DriverManager.getConnection(url, “SYS”, “MANAGER”);
    

CLASSPATH#

JDBC 응용 프로그램의 수행을 위해 Altibase.jar파일이 CLASSPATH에 포함되어야 한다.

$ export CLASSPATH=$ALTIBASE_HOME/lib/Altibase.jar:$CLASSPATH

프로그램 작성#

JDBC를 이용하여 작성하는 프로그램에서 Altibase 서버에 접속하고 종료하는 방법은 대략 다음과 같다.

JDBC 프로그램의 예#
/* JdbcTest.java */
import java.util.Properties;
import java.sql.*;

class JdbcTest
{
    public static void main(String args[]) {

        Properties props = new Properties();
        Connection con = null;
        Statement stmt = null;
        PreparedStatement pstmt = null;
        ResultSet res;

        if ( args.length == 0 )
        {
            System.err.println("Usage : java JdbcTest port_no\n");
            System.exit(1);
        }

        String port = args[0];
        String url = "jdbc:Altibase://127.0.0.1:" + port + "/mydb";
        String user = "SYS";
        String passwd = "MANAGER";
        String enc = "US7ASCII";

        props.put("user", user);
        props.put("password", passwd);
        props.put("encoding", enc);

        /* Altibase JDBC 드라이버 등록 */
        try {
            Class.forName("Altibase.jdbc.driver.AltibaseDriver" ); 
        } catch ( Exception e ) {
            System.err.println("Can't register Altibase Driver");
            return;
        }

        /* 접속하고 Statment를 할당 */
        try {
            con = DriverManager.getConnection(url,props);
            stmt = con.createStatement();
        } catch ( Exception e ) {
            e.printStackTrace();
        }

        /* 쿼리수행 */
        try {
            stmt.execute("DROP TABLE TEST001");
        } catch ( SQLException se ) { }

        try {        
            stmt.execute("CREATE TABLE TEST001 ( name varchar(20), age number(3) )");
            pstmt = con.prepareStatement("INSERT INTO TEST001 VALUES(?,?)");

            pstmt.setString(1,"김민석");
            pstmt.setInt(2,28);
            pstmt.execute();

            pstmt.setString(1,"홍길동");
            pstmt.setInt(2,25);
            pstmt.execute();

            pstmt.setString(1,"원훈");
            pstmt.setInt(2,34);
            pstmt.execute();

            res = stmt.executeQuery("SELECT * FROM TEST001");

            /* 결과를 받아 화면에 출력 */
            while(res.next()) {
                System.out.println(" Name : "+res.getString(1)+", Age : "+res.getInt(2));
            }

            /* 연결 해제 */
            stmt.close();
            pstmt.close();
            con.close();
        } catch ( Exception e ) {
            e.printStackTrace();
        }
    }
}
실행결과#
$ javac JdbcTest      
$ java JdbcTest 20300 <- port
Name : 김민석, Age : 28
Name : 홍길동, Age : 25
Name : 원훈, Age : 34

C/C++ Precompiler를 활용한 프로그램#

C/C++ Precompiler는 내장 SQL문이 포함된 소스 프로그램을 입력으로 받아, 내장 SQL문들을 실행 시간 라이브러리 호출로 변환하여, 호스트 언어로 컴파일될 수 있는 수정된 소스 프로그램을 생성한다. 자세한 내용은 Precompiler User's Manual을 참조한다.

환경 설정#

C/C++ Precompiler로 전처리된 결과 파일을 컴파일 및 링크하기 위해서 필요한 환경 설정은 다음과 같다.

헤더파일#

전처리된 소스파일을 컴파일하는데 필요한 헤더파일은 ulpLibInterface.h 이며, \$ALTIBASE_HOME/include 디렉토리에 있다.

전처리된 응용 프로그램을 컴파일하기 위해서 아래의 옵션을 사용하라:

-I$ALTIBASE_HOME/include
라이브러리#

필요한 라이브리는 libapre.a와 libodbccli.a이며, \$ALTIBASE_HOME/lib 디렉토리에 있다.

전처리된 응용 프로그램을 링크하기 위해서 아래의 옵션을 사용하라:

-L$ALTIBASE_HOME/lib -lapre -lodbccli -lpthread

전처리 실행#

C/C++ Precompiler는 내장 SQL문을 포함하는 C 또는 C++로 작성된 프로그램을 전처리하여 변환된 C 또는 C++ 프로그램을 생성한다. 입력 파일은 .sc 확장자를 가지는 C 또는 C++로 작성된 프로그램이며, 출력 파일은 .c 또는 .cpp를 확장자를 가진다. 출력 파일의 확장자는 사용자가 지정할 수 있으며 기본적으로 .c이다.

실행 옵션#

다음은 apre의 도움말 출력 결과이다. apre에 사용할 수 있는 다양한 전처리 옵션을 보여준다.

$ apre -h
===========================================================
APRE (Altibase Precompiler) C/C++ Precompiler HELP Screen
===========================================================
Usage  :  apre [<options>] <filename>

-h               : Display this help information.
-t <c|cpp>       : Specify the file extension for the output file.
                   c   - File extension is '.c' (default)
                   cpp - File extension is '.cpp'
-o <output_path> : Specify the directory path for the output file.
                   (default : current directory)
-mt              : When precompiling a multithreaded application,
                   this option must be specified.
-I<include_path> : Specify the directory paths for files included using APRE C/C++.
                   (default : current directory)
-parse <none|partial|full>
                 : Control which non-SQL code is parsed.
-D<define_name>  : Use to define a preprocessor symbol.
-v               : Output the version of APRE.
-n               : Specify when CHAR variables are not null-padded.
-unsafe_null     : Specify to suppress errors when NULL values are fetched
                   and indicator variables are not used.
-align           : Specify when using alignment in AIX.
-spill <values>  : Specify the register allocation spill area size.
-keyword         : Display all reserved keywords.
-debug <macro|symbol>
                 : Use for debugging.
                   macro   - Display macro table.
                   symbol  - Display symbol table.
-nchar_var <variable_name_list>
                 : Process the specified variables using
                   the Altibase national character set.
-nchar_utf16     : Set client nchar encoding to UTF-16.

===========================================================

멀티 쓰레드 프로그램#

C/C++ Precompiler는 멀티 쓰레드 프로그램을 지원한다. 멀티 쓰레드 프로그래밍시 내장 SQL문 사용 방법 및 주의 사항에 대해 알아보기로 한다.

  • 전처리 시 멀티 쓰레드 프로그램의 판단 근거를 전처리기에게 옵션(-mt)으로 제공하여야 한다.

  • 각 쓰레드마다 각각의 연결(connection)이 있어야 한다. 즉, 여러 쓰레드가 하나의 연결을 공유할 수 없다.

  • 연결 이름을 가지지 않는 connection(즉, default connection)은 한 프로그램 내에 하나만 허용된다.

  • 내장 SQL문에서도 사용할 연결 이름을 명시하여야 한다.

프로그램 작성#

Precompiler 응용 프로그램의 예#
/******************************************************************
 * SAMPLE : DELETE
 * .CODE   : delete.sc
 *          1. Using scalar host variables
 *          2. Reference  : array host variables - arrays1.sc
 ******************************************************************/


int main()
{
    /* 호스트 변수 선언 */
    EXEC SQL BEGIN DECLARE SECTION;
    char usr[10];
    char pwd[10];
    char conn_opt[1024];

    /* 스칼라형 */
    int      s_eno;
    short    s_dno;
    EXEC SQL END DECLARE SECTION;

    printf("<DELETE>\n");

    /* 이름, 비밀번호, 옵션 설정 */
    strcpy(usr, "SYS");
    strcpy(pwd, "MANAGER");
    strcpy(conn_opt, "DSN=127.0.0.1;CONNTYPE=1;PORT_NO=20300");

    /* Altibase 서버에 접속 */
    EXEC SQL CONNECT :usr IDENTIFIED BY :pwd USING :conn_opt;  
    /* check sqlca.sqlcode */
    if (sqlca.sqlcode != SQL_SUCCESS)
    {
        printf("Error : [%d] %s\n\n", SQLCODE, sqlca.sqlerrm.sqlerrmc);
        exit(1);
    }

    /* 스칼라 호스트 변수 사용 */
    s_eno = 5;
    s_dno = 1000;

    EXEC SQL DELETE FROM EMPLOYEES 
                WHERE ENO > :s_eno 
AND DNO > :s_dno 
AND EMP_JOB LIKE 'P%';

    printf("-----------------------------------------------\n");
    printf("[Scalar Host Variables]                                           \n");
    printf("-----------------------------------------------\n");

    /* sqlca.sqlcode 검사 */
    if (sqlca.sqlcode == SQL_SUCCESS)
    {
        /* sqlca.sqlerrd[2] holds the rows-processed(deleted) count */
        printf("%d rows deleted\n\n", sqlca.sqlerrd[2]);
    }
    else 
    {
        printf("Error : [%d] %s\n\n", SQLCODE, sqlca.sqlerrm.sqlerrmc);
    }

    /* 접속 해제 */
    EXEC SQL DISCONNECT;
    /* check sqlca.sqlcode */
    if(sqlca.sqlcode != SQL_SUCCESS)
    {
        printf("Error : [%d] %s\n\n", SQLCODE, sqlca.sqlerrm.sqlerrmc);
    }
}
실행결과#
$ make delete
$ <DELETE>
----------------------------------------------------
[Scalar Host Variables]                                           
----------------------------------------------------
7 rows deleted