Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

Table of Contents
maxLevel2



개요 

...

본 문서는 ALTIBASE Precompiler인 APRE*C/C++를 이용하여 DBMS 응용 프로그램을 개발하는 개발자를 위한 문서이다. 여기서는 ALTIBASE를 이용한 가장 적합한 개발방법 등을 소개하며 본 문서에 소개된 여러 섹션들을 참고하여 타 DBMS로부터의 소스 변환 작업에도 참고하도록 한다. 자세한 개발자 문서는 매뉴얼을 참고하도록 한다.

...

Note

이 문서는 정보 제공을 목적으로 제공되며, 사전에 예고 없이 변경될 수 있습니다. 이 문서는 오류가 있을 수 있으며, 상업적 또는 특정 목적에 부합하는 명시적, 묵시적인 책임이 일절 없습니다.

이 문서에 포함된 Altibase 제품의 특징이나 기능의 개발, 발표 등의 시기는 Altibase 재량입니다.

Altibase는 이 문서에 대하여 관련된 특허권, 상표권, 저작권 또는 기타 지적 재산권을 보유할 수 있습니다.

 

 

Altibase 개발을 위한 기본 고려사항

...

Altibase Precompiler를 통해 개발할 경우 사전에 고려할 사항을 설명한다. 이 부분에서는 성능, 쓰레드 및 지원되지 않는 사항을 먼저 언급함으로써 프로그램을 개발하는데 참고하도록 한다.

 

성능을 고려한 사항

...

개발자는 최소한 다음 2가지를 성능과 관련해 고려하도록 해야 한다. 

  1. 매번 Prepare/Execute 또는 Direct-Execute방식의 최소화

    수행형태

    단순 Insert 성능 (2.4GHz / 8EA)

    매번 Prepare/Execute

    0.0008.43sec (1건당 처리 시간)

    1회 Prepare/매번 Execute

    0.0005.55sec (1건당 처리 시간)

    위의 성능 비교는 매우 단순한 Insert를 대상으로 한 성능임으로 실제로 적용 시점에서는 쿼리의 복잡도나 부하 및 장비마다 성능 개선 효과는 다를 수 있음을 전제해야 한다. 하지만, 매번 DBMS와 Prepare로 인한 통신 비용을 줄일 경우 어플리케이션 서버가 DBMS서버와 별도로 분리된 구조에서는 매우 큰 효과를 볼 수 있다. 즉, prepare 1번과 execute 1번의 통신 비용에서 1번의 prepare통신 비용을 줄이는 효과를 기대하는 것이다.

  2. SQL문의 실행계획(PLAN) 확인

    ALTIBASE는 다음 명령으로 실행계획을 확인할 수 있다.
    Code Block
    themeDJango
    languagesql
    iSQL> ALTER SESSION SET EXPLAIN PLAN = on;

    "iSQL" 란 프롬프트는 Altibase가 제공하는 SQL문 실행 유틸리티에서 수행되는 것을 의미하며 이 유틸리티는 $ALTIBASE_HOME/bin/isql 로 제공된다. (이 외에도 윈도우 환경에서는 Altibase가 제공하는 웨어밸리사의 "Orange for Altibase" 및 Opensource "DBeaver"를 사용할 수 있다.)
    위와 같은 명령을 수행한 후 질의 처리기에서 질의를 수행하게 되면 해당 질의의 실행계획이 표시된다.

    Code Block
    themeDJango
    languagesql
    iSQL> select * from t1 where a = 1;
    ------------------------------------------------------------
    PROJECT (COLUMN_COUNT: 2, TUPLE_SIZE: 8 )
    SCAN ( TABLE: T1,INDEX: __SYS_IDX_ID_102, ACCESS: 1, SELF_ID: 2 )

    개발자는 최소한 테이블이 어떤 인덱스(INDEX)를 접근하게 되고 해당 인덱스에서 몇 번의 접근(ACCESS)을 통해 데이터를 가져오는지 확인하여 접근 횟수의 비용을 줄이는 형태로 SQL문을 튜닝 할 필요가 있다. Altibase의 메모리DB는 디스크I/O를 거의 하지 않기 때문에 풀스캔을 하더라도 일정 부분 빠른 성능을 낼 수 있으나 반대로 불필요하게 빈번한 메모리의 접근은 잦은 시스템 콜을 유발하여 상대적으로 CPU 부하를 일으키게 된다. 즉, CPU가 연산할 수 있는 공간인 캐쉬에 메모리의 공간을 적재하는 과정이 빈번하게 발생하는 만큼의 CPU비용을 감당해야 한다는 것이다. 따라서, 적절한 인덱스 스캔을 통해 이러한 비용을 줄이면 성능의 최대화 및 CPU 부하의 최소화라는 효과를 거둘 수 있음을 고려해야 한다.

 

쓰레드를 고려한 사항

...

Altibase는 연결 객체에 대해 쓰레드간 보호를 보장하지 않는다. 즉, 하나의 커넥션을 가지고 동시에 접근하여 질의를 수행하는 경우 알 수 없는 오류가 발생하게 된다. 따라서, 개별 쓰레드 별로 연결 객체를 가져가는 형태로 개발을 하거나 또는 하나의 연결을 다수의 쓰레드가 접근해야 한다면 반드시 사용자가 연결 객체에 대한 동시성 제어를 수행해야 한다. 이와 같은 동시성 제어를 하지 않을 경우 다음과 같은 오류들이 발생할 수 있다.

Code Block
themeDJango
languagesql
- Invalid request to process the SQL statement 
- Communication link failure (EXEC->PREP)  
- Communication link failure (PREP->EXEC) 
- Communication link failure (EXEC->INVL) 
- Connection does not exist

 

호스트 변수의 고려사항

...

Altibase의 호스트 변수 선언 시 char형 호스트 변수 크기를 지정할 때에는 반드시 null-padding을 고려한 "+1byte"를 추가해야 한다. Precompiler의 옵션에서는 "-n" 옵션을 통해 +1byte를 생략할 수 있으나 이 경우 사용자가 char형 호스트 변수에 대한 명확한 길이를 제어해야 한다

...

또한, 소스 내에서의 호스트 변수 앞에는 반드시 콜론(:)을 사용해야 한다. 

 

에러 체크 확인

...

Altibase의 SQLCA객체는 별도의 선언 없이 사용이 가능하다. 개발자는 EXEC SQL 구문을 사용하는 모든 단계에서는 반드시 에러 체크를 수행하는 코드를 삽입할 것을 권고한다. Altibase의 경우 Cursor를 사용할 경우에 Cursor Prepare/Declare 단계에서 에러를 체크하지 않을 경우 Cursor Open 시점에 단순히 Cursor가 define 되지 않았다는 오류만 확인되기 때문에 실제 어떤 구문상의 오류 등을 알 수 없을 경우가 있음으로 일반적으로 누락하는 Cursor Prepare/Declare 단계에서도 에러 체크를 반드시 하도록 한다. Open 또는 Fetch단계에서 다음과 같은 오류가 발생할 수 있다.

...

에러정의

코드

설명

SQL_ERROR

-1

처리에 오류가 발생한 경우

SQL_SUCCESS

0

처리가 성공한 경우

SQL_SUCCESS_WITH_INFO

1

처리는 성공이나 결과에 오류가 있는 경우

SES_DUPKEY_ERR

-69720

인덱스 중복이 발생한 경우

SQL_NO_DATA

100

데이터가 없는 경우

SQL_INVALID_HANDLE

-2

SQL처리에 필요한 내부 객체가 오류인 경우

 

Commit Mode

...

Altibase는 기본적으로 설정을 변경하지 않는 한 Auto-Commit mode로 동작한다. 즉, 변경 트랜잭션이 발생하면 해당 수행 결과가 정상이면 자동으로 DB에 반영되는 형태이다. 사용자가 이 설정을 NonAuto-Commit mode로 변경하고자 할 경우 $ALTIBASE_HOME/conf/altibase.properties 에 정의된 "AUTO_COMMIT = 1"을  "0"으로 변경하거나 프로그램 상에서 다음과 같은 명령을 수행해야 한다.

...

  • 변경트랜잭션에서의 오류(INSERT, UPDATE, DELETE)

    Code Block
    themeDJango
    languagesql
    Connection is in autocommit mode. One can not operate on LOB datas with autocommit mode on
  •  조회 트랜잭션에서의 오류(SELECT)

    Code Block
    themeDJango
    languagesql
    LobLocator can not span the transaction 34734145 // 이 숫자는 임의의 숫자로 표기됨

     또한, LOB에 대한 접근 이후 명시적인 Commit/Rollback을 하지 않으면 해당 트랜잭션에 의해 Lock이 유지되게 되어 리소스 측면에 장애를 유발할 수 있음으로 주의하도록 해야 한다.

 

System Signal 핸들링

...

Altibase 클라이언트 라이브러는 system signal 발생에 대해서 안전하지 않다.  예를 들어서 외부 원인에 의해 네트워크 접속이 종료된 경우 , SIGPIPE 신호를 받아 진행중인 응용 프로그램이 강제로 종료될 수도 있다. 이러한 강제 종료를 막기 위해서는 SIGPIPE 신호를 사용자 애플리케이션에서 처리해야 한다. 그러나 SIGPIPE 신호 처리를 하던 중에 Altibase 클라이언트 라이브러리의 함수를 호출하면 프로그램이 멈출 수 있기 때문에 호출하면 안된다.

...

  • signal 핸들링 예제

    Code Block
    themeDJango
    languagesql
    int  main()
    {
    .......
    signal(SIGPIPE, SIG_IGN );
    ....
    EXEC SQL CONNECT :user identified by :pwd;
    .....
    EXEC SQL DISCONNECT;
    ....
    }

기타 지원되지 않는 사항들

...

타 DBMS와 비교하여 지원되지 않는 사항을 설명한다.

Panel
  1. Dynamic Method4 방식은 지원하지 않는다
  2. Context 구문은 지원하지 않는다 ( 쓰레드를 고려한 사항 참조)
  3. Precompiler를 통해서는 Procedure의 typeset, Ref-Cursor, 배열 형태의 결과 셋을 받아 올 수 없다.
  4. system signal 발생에 대해서 안전하지 않다. system signal 처리는 사용자 코드에 의해서 처리되어야 한다.

 

 

APRE 기본 사용방법

...

여기서는 개발 예제 소스를 통해 상세한 설명을 언급한다. 타 DBMS을 변환할 경우 본 소스를 기반으로 참고하여 변환하도록 한다. 

 

APRE*C/C++의 기본 사용법

...

Altibase Precompiler는 APRE*C/C++라고 호칭하며 실행파일은 "apre"이다. 사용자는 "*.sc" 를 확장자로 하는 소스로 개발해야 한다. 사용법은 다음과 같다.

...

만일, C++이 C컴파일러로 “*.c” 파일이 필요한 경우에는 “-t cpp” 가 아닌 “-t c” 로 Precompile을 수행하면 된다.

 

기본적인 makefile

...

Altibase 개발을 위한 기본 헤더파일 및 라이브러리는 다음에 위치한다.

...

한가지 더 고려할 부분은 사용자가 C++컴파일러 아닌 C컴파일러를 사용할 경우에는 Altibase의 라이브러리가 C++로 제공되기 때문에 이를 호환하기 위한 C++ 시스템 라이브러리를 반드시 링크해야 한다. 자세한 사항은 『Altibase APRE(SES)C makefile』 문서를 참고하도록 한다.

 

호스트 변수

...

소스상에서 SQL문을 사용하여 코딩 할 경우 DBMS에 데이터에 대한 입출력 제어를 위해 사용하는 변수들을 모두 호스트 변수라고 지칭한다. Altibase v5.3.3 이전에서는 모든 호스트 변수는 반드시 “EXEC SQL BEGIN DECLARE SECTION” 과 “EXEC SQL END DECLARE SECTION” 절 내에 선언된 것들만 사용할 수 있었다.

...

Code Block
themeDJango
languagesql
EXEC SQL BEGIN DECLARE SECTION;
  char  H_var1 [10];
  char  H_var2 [10];
  int   H_INT;
  int   H_Out;
EXEC SQL END DECLARE SECTION;

EXEC SQL SELECT CAST (:H_var1 AS CHAR(10)) INTO :H_Var2 FROM DUAL;

……

EXEC SQL SELECT CAST (:H_INT AS INTEGER) + Column1 INTO :H_Out FROM T1;

 

SQLCA 구조체

...

Altibase는 sqlca구조체를 통해 에러를 리턴 한다. 사용자는 다음의 사항을 통해 에러 코드 및 에러 메시지를 확인할 수 있다.

...

위 코드 값들은 이미 Precompile단계에서 소스에 정의되기 때문에 개발자가 별도로 재정의하거나 헤더에 포함할 필요는 없다.

 

Connect / Disconnect

...

Altibase는 별도의 Listener와 같은 설정이 없으며 DBMS내부 쓰레드에 직접 접속하는 형태로 구현되어 있다. 따라서, 접속할 대상 서버의 IP 및 PORT번호를 직접 입력하는 형태로 접속한다. 다음은 ALTIBASE에 접속하기 위한 기본 예제 코드이다.

Code Block
themeDJango
languagesql
  // 호스트 변수의 선언 개시
  EXEC SQL BEGIN  DECLARE SECTION;       
  char   usr[20];
  char   pwd[20];
  char   opt[200];
  // 호스트 변수의 선언 종료
  EXEC SQL END  DECLARE SECTION;         

   
  main()
  {
  // DB 사용자 계정의 고유ID
  sprintf (usr, “sys”);   

  // DB 사용자 계정의 패스워드 
  sprintf (pwd, “manager”);                 
  
  // 해당 서버IP 및 접속 Port
  sprintf (opt, “DSN=127.0.0.1; PORT_NO=20300;CONNTYPE=1”); 

  // 연결 시도
  EXEC SQL CONNECT :usr IDENTIFIED BY  :pwd USING :opt; 
  if (sqlca.sqlcode != 0)
  {
          Printf(“ConnectError:: %d %s\n”, SQLCODE,  sqlca.sqlerrm.sqlerrmc);
  }

}

 

Connection Type

...

연결 방식의 종류는 CONNTYPE 속성의  설정값으로  TCP/IP,  unix Domain,  IPC 방식  3가지 중에 한가지를 선택할  수 있다.

...

CONNTYPE=2, 3 방식은 모두 프로그램이 DBMS서버와 같은 장비 내에 위치해야지만 사용할 수 있다. 2가지 방식 모두 하드웨어 레벨까지 통신비용이 발생하지 않기 때문에 TCP/IP에 비해 빠른 성능을 내고자 할 경우 권장한다. IPC접속 방식을 선택할 경우에는  ALTIBASE_HOME/conf/altibase.properties 파일 내의 IPC_CHANNEL_COUNT를 1 이상의 값으로 조정해야 한다. 이 설정은 DB를 재 구동해야 사용 가능하며 사전에 IPC와 관련된 커널 설정값을 충분히 조정해야 한다. 

 

Connection Name

...

연결 시점에 명시적인 이름을 주는 것이 가능한데 다음과 같이 수행한다.

...

명시적인 이름 또는 호스트 변수로 저장하여 처리가 가능하다.

 

Connection Close

...

연결의 해제 방법은 아래와 같으며 비정상적인 연결 종료가 발생하면 재 연결을 시도하기 전에는 반드시  "EXEC SQL FREE"를 수행하도록 한다.

...

Note

Altibase의 DISCONNECT에 주의점은 NonAuto-Commit mode로 동작하였다 하더라도 사용자가 DISCONNECT 직전에 정상 처리된 변경 트랜잭션은 Commit되는 점을 주의해야 한다. (반영하지 않고자 할 경우 명시적으로 Rollback을 수행한 후 Disconnect해야 한다.)

 

Commit / Rollback

...

Commit/Rollback의 수행은 다음과 같이 할 수 있다.

Code Block
themeDJango
languagesql
EXEC SQL COMMIT;

EXEC SQL ROLLBACK;

EXEC SQL AT CONN1 COMMIT;

EXEC SQL AT :ConnName COMMIT;

 

DML 수행의 예

...

Code Block
themeDJango
languagesql
EXEC SQL BEGIN DECLARE SECTION;
 char    H_today [20 + 1];
EXEC SQL END DECLARE SECTION;

main()
{
// DB 접속 (위의 예제를 참고한다.)

   …생략...


  memset (H_today,  0x00, sizeof(H_today));

// 오늘 날짜를 DB로부터 읽어 온다.
EXEC SQL SELECT TO_CHAR  (sysdate, ‘yyyy-mm-dd’) INTO :H_today FROM Dual;


// 테이블을 생성한다.
EXEC SQL CREATE TABLE TEST ( C1 CHAR(20));

// 읽어 온 날짜를 테스트 테이블에 입력한다.
EXEC SQL INSERT INTO TEST (C1) VALUES (:H_today);

EXEC SQL INSERT INTO TEST VALUES (:H_today);
// 테이블에서 날짜를 삭제한다.

EXEC SQL DELETE FROM T1 WHERE C1 like ‘2010%’;

EXEC SQL DELETE T1 WHERE C1 = ‘2010’;


}

Altibase의 경우는 SELECT절의 INTO 절은 생략할 수 없다. 이외의 트랜잭션 상의 DML은 일반적인 SQL 사용법을 그대로 이용하면 된다.

 

Cursor

...

일반적인 cursor는 다음과 같이 사용한다.

Code Block
themeDJango
languagesql
EXEC SQL BEGIN DECLARE SECTION;
int  H_Condition;
int O_C1;
int O_C2;
EXEC SQL END DECLARE SECTION;

main()
{
    // DB접속

    // Cursor 선언
   EXEC SQL DECLARE CURSOR1 CURSOR FOR   SELECT C1, C2 FROM TEST1  WHERE C1 >= :H_Condition;

   // Cursor 열기
   EXEC SQL OPEN CURSOR1;

   While (1)
   {
       // Cursor 결과 읽기
       EXEC SQL FETCH CURSOR1 INTO :O_C1,  :O_C2;
       if (sqlca.sqlcode == SQL_NO_DATA) 
          break;
       else if (sqlca.sqlcode != SQL_SUCCESS) 
       {
           printf(“FetchError:: %d, %s\n”, SQLCODE, sqlca.sqlerrm.sqlerrmc);
           break;
       }
   }
   // cursor 닫기
   EXEC SQL CLOSE CURSOR1;
}

 

 

FAC(Fetch Across Commit) 오류

...

Non-autocommit 환경에서  커서를 Open후 레코드 단위로 Fetch 하면서 Fetch 도중에 Commit/Rollback 하는 것은 ANSI 표준에서 권장하지 않는다. Altibase 는 ANSI 표준을 준수하여 기본적으로 FAC 시 오류가 발생한다.  그러나 Altibase v6.3.1 이상 버전부터는  Cursor Open시의 옵션을 통해서  FAC 를 수행할 수 있는 기능을 지원한다.


오류가 발생하는 코드의 예

...

 FAC 오류 발생하는 코드의 예를 보여준다.

...

따라서, cursor를 사용하여 fetch하는 과정에서 별도의 변경 트랜잭션을 발생시켜야 하는 경우는 해당 세션을 auto-commit mode로 수행하거나 또는 별도의 연결 객체를 만들어 해당 세션을 통해 처리하도록 해야 한다.

 

FAC 오류 메세지

...

Altibase 버전에 따라서 FAC 에러 메세지는  약간씩 다르게 출력된다.

버전에러코드에러메세지참고 페이지
 Altibase 4.3.9ERR-4103C Request of fetching data to an unprepared SQL statement.http://aid.altibase.com/x/6YKZ
Altibase 5.3.3 ~ 6.1.1 100   
Altibase 6.3.1 이상 ERR-410D2  Request of fetching data to an unprepared SQL statement.http://aid.altibase.com/x/9oKZ

 

별도의 연결을 만들어 FAC를 수행하는 예 

...

FAC  연결  수행 예입니다.

Code Block
themeDJango
languagesql
main()
{
    // DB연결

    // 별도의 변경 트랜잭션을 위한 연결 생성
    EXEC SQL AT CONN1 CONNECT :usr IDENTIFIED BY :pwd USING :opt;

    // cursor 코드 생략.
    while (1)
    {
        EXEC SQL FETCH CURSOR1 INTO :O_C1,  :O_C2;
        // 변경 시점에 위에서 생성한 연결 명을 명시한다.
        EXEC SQL AT CONN1 UPDATE t1 set change_v = :O_C2 where C1 = :O_C1;
  
       // commit / rollback에서도 위에서 생성한 연결 명을 명시한다.
       If (sqlca.sqlcode != SQL_SUCCESS) 
           EXEC SQL AT CONN1 ROLLBACK;
        Else
           EXEC SQL AT CONN1 COMMIT;
    }
}

별도의 연결을 사용할 경우에는 사용자가 고려할 사항은 전체 수행 흐름이 하나의 트랜잭션으로 묶여야 하는 경우 별도의 연결 객체를 사용할 경우 수행 도중 오류로 인해 일부는 commit되고 일부는 처리되지 못하는 상황이 발생할 수 있다. 만일, 하나의 트랜잭션 안에 All or Nothing 형태로 처리되어야 한다면 반드시 fetch-loop 밖에서 commit/rollback 구문을 사용하도록 해야 한다.

 

Cursor 선언시 "WITH HOLD" 구문 사용

...

Altibase v6.3.1 이후 부터는 "WITH HOLD" 구문으로  선언한 Cursor를 Open 한 경우에는  Commit/Rollback 수행후  트랜잭션 완료 후에도 커서가 닫히지 않는다. 세션이 non-autocommit 모드일 경우에만 유효하다.  자세한 설명은  『ALTIBASE CLI User's Manaul』 과   『ALTIBASE Precompiler Manaul』  을 참고하기 바란다.

Code Block
themeDJango
languagesql
main()
{
     .....
     EXEC SQL AUTOCOMMIT OFF;
     ......
     EXEC SQL DECLARE CURSOR1 CURSOR WITH HOLD FOR      // Cursor 선언시 시 With hold 옵션을 사용한다.
                                 SELECT * FROM  DEPARTMENT;
     ......
    while(1)
    {
        EXEC SQL FETCH CURSOR1 INTO :O_C1,  :O_C2;
        ......
        EXEC SQL UPDATE t1 set change_v = :O_C2 where C1 = :O_C1;
        // Commit/Rollback 을 수행하여도 FAC  오류가 발생하지 않는다.
       If (sqlca.sqlcode != SQL_SUCCESS) 
           EXEC SQL ROLLBACK;
       Else
           EXEC SQL COMMIT;
        ......
    }
   ...
}

 

Dynamic SQL

...

변경 트랜잭션의 경우와 조회 트랜잭션 2가지의 예로 설명한다.

 

변경 트랜잭션 

...

Code Block
themeDJango
languagesql
EXEC SQL BEGIN DECLARE SECTION;
char  sql_text [1024];
int    H_var1;
int    H_var2;
EXEC SQL END DECLARE SECTION;


main()
{
    // DB연결 
    sprintf (sql_text,  “insert into test_table values (?, ?)”);

    EXEC SQL PREPARE STMT1 FROM :sql_text;
    if (sqlca.sqlcode != SQL_SUCCESS)
    {
        printf(“PrepareError:: %d, %s\n”, SQLCODE, sqlca.sqlerrm.sqlerrmc);
        exit(-1);
    }   

    for ( i=0; i<1000; i++)
    {
        EXEC SQL EXECUTE STMT1 USING :H_var1, :H_var2; 
       if (sqlca.sqlcode != SQL_SUCCESS)
       {
            printf(“PrepareError:: %d, %s\n”, SQLCODE, sqlca.sqlerrm.sqlerrmc);
            exit(-1);
        }
    }

}

 

조회 트랜잭션 예

...

Code Block
themeDJango
languagesql
EXEC SQL BEGIN DECLARE SECTION;
char  sql_text [1024];
int    H_var1;
int    H_var2;
EXEC SQL END DECLARE SECTION;

main()
{
    // DB연결 

    sprintf (sql_text,  “select * from test_table where c1 >= ?”);
    EXEC SQL PREPARE STMT1 FROM :sql_text;
    if (sqlca.sqlcode != SQL_SUCCESS)
    {
        printf(“PrepareError:: %d, %s\n”,SQLCODE, sqlca.sqlerrm.sqlerrmc);
        exit(-1);
    }   

    EXEC SQL DECLARE CURSOR1 CURSOR FOR STMT1;

    EXEC SQL OPEN CURSOR1 USING :H_var1;

    // 이하 코드는 cursor fetch와 동일하다.

...

Altibase v5.1부터는 내부적으로 Plan-Cache라는 영역에 모든 세션들이 수행하는 실행계획을 공유하고 있다. 하지만, Dynamic SQL과 같이 매번 질의문이 달라지면 Plan-Cache에 이미 생성된 실행계획을 사용하지 않고 다시 Prepare/Execute형태로 동작하기 때문에 질의 성능이 Static SQL문 비해서는 느릴 수 밖에 없다.

 

Call Function

...

사용자가 생성한 또는 DBMS 내장 함수를 호출하는 방법의 예를 설명한다.

 

DBMS 내장함수 호출 예

...

Code Block
themeDJango
languagesql
EXEC SQL BEGIN DECLARE SECTION;
 char H_day [20];
EXEC SQL END DECLARE SECTION;

main()
{
    // 날짜를 가져오는 함수의 사용 예
    EXEC SQL SELECT TO_CHAR(SYSDATE,‘yyyy-mm-dd hh:mi:ss’) INTO :H_day from dual;
}

 

사용자 함수 호출 예

...

Code Block
themeDJango
languagesql
// 입력된 2개의 값을 더하여 리턴 하는 함수
Create or replace function user_func (a integer, b integer)
Return integer
As
Begin
    Return a + b;
End;
/

---- 호출하는 소스 ----
main()
{
    // 사용자가 생성한 함수의 사용 예
    EXEC SQL SELECT user_func (1, 2) INTO :H_value from dual;
}

항상 from dual이어야 할 필요는 없으며 사용자가 필요에 의해 임의의 테이블을 지정하여도 상관없다. (함수에서 사용자가 주의할 것은 리턴 절에 char/varchar가 오는 경우 반드시 리턴 되는 길이를 명시해야 한다. 명시하지 않을 경우 1byte만 리턴 되기 때문에 대부분 함수에서 다음과 같은 Exception이 발생하게 된다. “Invalid length of the data type”)

 

Call Procedure

...

프로시져는 함수와 달리 별도의 호출 방식을 통해 수행한다. 함수도 이와 동일한 방법으로도 사용할 수 있음으로 참고하도록 한다.

Code Block
themeDJango
languagesql
// 함수의 경우
EXEC SQL EXECUTE
BEGIN
      :H_var := user_func (:in_param1,  :in_param2);
END;
END-EXEC;

// 프로시져의 경우는 리턴 절이 없다.
EXEC SQL EXECUTE
BEGIN  
       user_proc (:in_param1 in, :in_param2 out);
END;
END-EXEC;

 

WHENEVER  구문

...

Altibase의 WHENEVER구문에서는 do/goto/continue/stop 등의 구문을 제공한다. 이 구문은 내장SQL문을 수행하기 전에 선언해야 한다.  Whenever 구문의 조건문에는 다음과 같은 형태가 가능하다.

...

Code Block
themeDJango
languagesql
EXEC SQL WHENEVER SQLERROR DO user_function();
EXEC SQL WHENEVER SQLERROR STOP;
EXEC SQL WHENEVER SQLERROR GOTO ERROR_ROUTINE;
EXEC SQL WHENVER SQLERROR CONTINUE;

 

현재의 연결 상태 확인 및 Session Failover 기능

...

Altibase에서는 능동적으로 연결 객체에 대해 상태를 리턴 해주는 방법은 없으며 다만, 개발자가 질의를 수행한 이후 SQLSTATE의 상태를 체크하여 다음의 방법으로 연결이 종료되었음을 감지해 낼 수 있다.

다음과 같은 예제 소스를 공통 함수에 넣는 방법도 가능한 방법 중에 하나이다.

 

사용자 코드에 의한 Session Failover 기능

...

Code Block
themeDJango
languagesql
if ( memcmp (SQLSTATE, “08001”, 5) == 0 ||  memcmp (SQLSTATE, “08S01”, 5) == 0  || memcmp (SQLSTATE, “08003”, 5) == 0 )
{
    // 연결이 해제된 상태임 DISCONNECT 호출이 아닌 FREE를 실행
    EXEC SQL FREE;        

    EXEC SQL CONNECT ~~ // 상대편 서버
}

...

이 기능은 트랜잭션의 FailOver는 지원하지 않기 때문에 사용자는 Session FailOver이후의 비즈니스 로직을 재처리 하는 것을 반드시 고려해야 한다. 사용 예는 다음과 같다.

 

Altibase Failover 기능을 사용한 

...

Code Block
themeDJango
languagesql
Sprintf (opt, "DSN=127.0.0.1;"
              "AlternateServers=(128.0.0.1:20300,128.0.0.2:20301);"
              "ConnectionRetryCount=3;"
              "ConnectionRetryDelay=10;"
              "SessionFailOver=on;LoadBalance=off") ;

EXEC SQL CONNECT :usr IDENTIFIED BY :pwd USING :opt;
.................

EXEC SQL INSERT INTO   T1 VALUES (:H_var1);
if (sqlca.sqlcode != SQL_SUCCESS)
{
   // 이 에러가 나오면 이미 FailOver가 정상적으로 수행 되었다는 의미이다.!!
   if (SQLCODE == EMBEDED_ALTIBASE_FAILOVER_SUCCESS)
   {
       printf(“FailOver Success\n”);
       goto Retry_business_logic;
   }
}

...

CTF/STF와 관련된 부분은 『ALTIBASE UL FailOver 가이드』문서를 참고하도록 한다.

 

 

APRE 예제 프로그램

...

Altibase 설치 파일에 포함되어 있는 예제(Sample)  프로그램에 대해서 간략히 설명한다.

 

예제 프로그램

...

$ALTIBASE_HOME/sample/APRE 에 각종 예제들을 참고할 수 있다. 해당 디렉토리의 소스는 다음과 같이 참고할 수 있다.

...

Code Block
themeDJango
languagesql
Testsample.sc
--------------------------------------------------------------------------
EXEC SQL BEGIN DECLARE SECTION;
 char   usr [20];
 char   pwd [20];
 char   opt [200];
 char   conn2 [20];
 int    H_C1;
 int    H_C2;
 int     i;
EXEC SQL END DECLARE SECTION;

void error_do(const char*step)
{

    if (sqlca.sqlcode != 0)
    {
        printf ("Error: <%s> %d, %s\n", step,  SQLCODE, sqlca.sqlerrm.sqlerrmc);
        exit(-1);
    } else {
        printf ("%s success\n", step);
    }
}

main()
{
    // 연결을 위한 접속정보
    sprintf (usr, "sys");
    sprintf (pwd, "manager");
    sprintf (opt, "DSN=192.168.1.35;PORT_NO=27584;CONNTYPE=1");

    // DB연결을 수행
    EXEC SQL CONNECT :usr IDENTIFIED BY :pwd USING :opt;
    error_do("conn");

    // cursor내에서 update수행을 위한 별도의 연결을 수행
    sprintf (conn2, "update_con");
    EXC SQL AT :conn2 CONNECT :usr IDENTIFIED BY :pwd USING :opt;
    error_do("conn2");

    // NonAutoCommit모드로 변경
    EXEC SQL AT :conn2 ALTER SESSION SET AUTOCOMMIT = FALSE:
    error_do ("alter session");

    // 테스트를 위한 2개의 테이블을 만들고 데이터를 입력한다.
    EXEC SQL CREATE TABLE TABLE_A (C1 INTEGER, C2 INTEGER);
    EXEC SQL CREATE TABLE TABLE_B (C1 INTEGE, C2 INTEGER);

    for (i=0; i<10; i++)
   {
       EXEC SQL INSERT INTO TABLE_A VALUES (:i, :i);
       Error_do ("insert_table_a");
       
       EXEC SQL INSERT INTO TABLE_B VALUES (:i, :i);
       Error_do("insert_table_b");
    }

    EXEC SQL AT :conn2 COMMIT;

    // cursor 선언
    EXEC SQL DECLARE CURSOR1 CURSOR FOR   SELECT C1, C2 FROM TABLE_A;
    error_do("declare");

    // cursor 열기
    EXEC SQL OPEN CURSOR1;
    error_do("open");

    while (1)
    {
         // fetch하면서 update를 수행.
        EXEC SQL FETCH CURSOR1 INTO :H_C1, :H_C2;
        error_do("fetch");

        // update 수행
        EXEC SQL AT :conn2  UPDATE TABLE_B SET C2 = C2 + :H_C2  WHERE C1 = :H_C1;
        error_do("update");
    }

    // 사용한 cursor를 닫는다.
    EXEC SQL CLOSE CURSOR1;

    // 연결 해제
    EXEC SQL DISCONNECT;

    // update를 수행한 세션을 commit하고 연결을 해제
    EXEC SQL AT :conn2 COMMIT;
    EXEC SQL AT :conn2 DISCONNECT;
}

 

 

자주 발생하는 오류 메세지

...

SQLCODE 및 sqlca.sqlerrm.sqlerrmc를 통해 확인되는 자주 발생하는 에러들을 정리해서 설명한다.  SQLCODE는 동일한 sqlca.sqlerrm.sqlerrmc에 대해 2개가 존재할 수 있는데 이것은 Altibase 내부 모듈 중 오류가 발생한 모듈의 위치에 따라 다를 수 있기 때문이며 에러의 내용은 동일하기 때문에 조치 방법도 동일하다.

 

Connection does not exist. (SQLCODE=–2)

질의 처리 수행 과정에서 사용해야 할 연결 객체가 없거나 사라진 경우이다. 제대로 연결이 처리되었는지 여부를 확인하고 쓰레드 프로그램의 경우 EXEC SQL AT 절에 연결 명이 제대로 표기되었는지 확인해야 한다. 경우에 따라서는 DBMS서버에서 연결이 단절된 경우가 있을 수 있다. (오류 메시지 중 Timeout관련 메시지를 확인)  

String data right truncated. (SQLCODE=1)

DB로부터 변수에 값을 담는 과정에서 선언된 변수의 길이가 작은 경우 발생한다. DB변수의 길이를 (데이터 길이+1byte)만큼 크게 변경하도록 한다.

Invalid size of data to bind to a host variable [ Column ID = <A%d>, Data Size = <B%d> , Declared Size of Host Variable = <C%d> ] (SQLCODE=-201144 or -266423)

칼럼의 (A+1)번째의 실제 데이터는 B의 크기를 갖는데 호스트 변수의 길이는 C의 크기를 갖기 때문에 데이터 값이 변수의 길이를 넘은 경우 발생한다. 변수의 길이를 데이터의 길이보다 크게 잡아야 한다. 간혹, 호스트 변수의 메모리가 프로그램의 이상으로 깨진 경우에도 이와 같은 오류가 발생하기 때문에 호스트 변수의 memset 또는 메모리가 깨진 경우가 없는지 확인해야 한다. 또는, 쓰레드 프로그램의 경우에서 다수의 SQL문을 하나의 커넥션을 통해 동시성 제어가 제대로 되지 못할 경우에도 발생할 수 있다

Calculation stack overflow (SQLCODE=-135187 or -659475)

내부에서 질의 처리 과정에서 사용되는 stack의 크기를 넘은 경우 발생한다. 이 경우 해당 질의를 수행하기 전에 다음과 같이 SQL문을 수행하도록 한다. 기본값은 1024이다.

Code Block
themeDJango
languagesql
EXEC SQL ALTER SESSION SET STACK SIZE = 4096;

 

Value overflow or Numeric value out of range (SQLCODE=-135184 or -659472 or -331890)

호스트 변수에 사용된 데이터의 값이 실제 DB에 정의된 칼럼의 표현 범위보다 큰 값이 입력된 경우이다. 데이터 값을 확인하고 필요 시 칼럼을 변경하도록 해야 한다.

Conversion not applicable (SQLCODE=-135180 or -659468)

ALTIBASE에 사용되는 칼럼의 데이터 타입 또는 호스트 변수의 타입간의 변환이 지원되지 않는 유형으로 사용한 경우에 발생한다. 

...

이 외에도 CLOB/BLOB의 핸들링에 있어 변환되지 않는 데이터 타입을 갖는 호스트 변수가 사용될 경우도 에러가 발생할 수 있다.

Invalid length of the data type (SQLCODE=-135181 or -659469)

DB의 칼럼에 저장 가능한 길이보다 큰 데이터가 입력되는 경우 발생한다.

Code Block
themeDJango
languagesql
Table T1 (c1 char(10));

(X) Insert into t1 values ('AAAAAAAAAAAAAAAAAAAAAAAAA');

Invalid literal (SQLCODE=-135185 or -659473)

칼럼 타입이 숫자형으로 선언된 상태에서 사용한 호스트 변수에 숫자로 변환할 수 없는 문자열 있는 경우 발생한다.  또는,  to_number()와 같은 내장 함수에서 입력 인자로 숫자 형태가 와야 하는데 문자열이 온 경우에서 발생한다. 숫자형에 맞는 데이터를 사용하도록 해야 한다.

Code Block
themeDJango
languagesql
(X) select to_number ('A') from dual;

 

Invalid character value for cast specification (SQLCODE=-331893)

숫자 형의 호스트 변수에 문자열을 담아 처리되는 경우 발생한다. 칼럼 타입에 맞는 호스트 변수를 선언하도록 한다.

Invalid cursor state. (SQLCODE=-331822) or Function sequence error (SQLCODE= -331796)

이 에러는 Cursor를 사용하기 위해 지켜야 할 순서가 올바르게 수행되지 않을 경우 발생한다. 혹은, 이미 데이터를 모두 읽은 cursor이거나 close된 cursor인데 다시 fetch를 부르는 경우도 이 에러가 발생한다.

The cursor must be opened for fetch (SQLCODE=-1) or The cursor does not exist (SQLCODE=-589857)

일반적인 Cursor의 사용은 (PREPARE->DECLARE->OPEN->FETCH->CLOSE)의 순서를 거쳐야 한다.  이 에러는 정상적으로 DECLARE/OEPN되지 않은 cursor를 FETCH하려고 시도하는 경우 에러가 발생한다.

Not enough insert values (SQLCODE=-200787)

INSERT구문에서 명시된 칼럼 개수와 호스트 변수의 개수가 일치하지 않을 경우 발생한다.

The tablespace does not have enough free space (SQLCODE=-69685 or -69923)

트랜잭션이 진행되는 과정에서 테이블스페이스의 용량이 모두 사용되어 공간이 없는 경우이다. 이 경우에는 해당 디스크 테이블스페이스의 경우는 데이터파일을 추가함으로 공간을 확보하고 메모리 테이블스페이스의 경우에는 불필요한 데이터의 삭제 또는 Compaction과정 등을 통해 가용 공간을 확보해야 한다.

Input literal is not long enough for date format. (SQLCODE=-135218)

날짜 함수에 입력된 문자열이 너무 짧은 경우 발생한다. 입력된 데이터에 오류가 없는지 확인해야 한다.

Code Block
themeDJango
languagesql
select TO_DATE('10123', 'yyyymmdd') from DUAL

Literals in the input do not match format string. (SQLCODE=-135224)

날짜 함수에서 입력된 날짜의 문자열과 날짜 형식이 일치하지 않는 경우 발생한다.

Code Block
themeDJango
languagesql
select TO_DATE ('2010123', ' yyyy-mmdd') from DUAL // 형식 중에 yyyy-mm에 오류

 

The transaction is already active. (SQLCODE=-266311 or -266312)

세션 단위로 Commit-mode를 조정할 수 있는데 이 과정에서 아직, Commit/Rollback 되지 않은 트랜잭션이 존재하는 상태에서 Auto-Commit으로 변경하는 경우 발생한다. 따라서, 세션의 속성을 변경하는 과정에서 에러가 발생할 경우 이전에 발생한 트랜잭션의 존재 여부를 먼저 확인해 보도록 한다. 혹은, Commit/Rollback을 실행한 이후에 세션의 속성을 변경하도록 한다. 간혹, 쓰레드 프로그램 및 Connection Pool을 구현하여 사용하는 경우에서 연결 객체에 대한 사용자 관리 (즉, autocommit/NonAutoCommit을 혼용하여 사용하는 경우들)가 부주의한 경우 발생할 수 있다.

The row already exists in a unique index. (SQLCODE=-69720)

INSERT 혹은 UPDATE 구문에서 접근하거나 변경하는 값이 이미 primary-key 또는 unique index상에 데이터가 존재하는 경우이다. 이 경우는 조회를 통해 이미 해당하는 데이터 값이 존재하는지 확인하도록 한다.

Unable to insert (or update) NULL into NOT NULL column. (SQLCODE=- 200790)

INSERT 혹은 UPDATE 구문에서 입력하는 데이터 값 중에 칼럼이 NOT NULL인데 NULL값을 넣으려고 하는 경우 발생한다. 데이터 값들을 추적하여 NULL인 데이터가 발생하지 않도록 하거나 칼럼의 NOT NULL Constraints를 적절하게 변경해야 한다.

Indicator variable required but not supplied. (SQLCODE=-331841 or -594101 or 594103)

DB로부터 호스트 변수에 담긴 값이 NULL인 경우 이 에러가 발생한다. 이 에러는 SQL_SUCCESS_WITH_INFO로 리턴 된다. 즉, 넘겨준 부분이 NULL인 상태임을 인지 시켜 주는 오류이다. (실제 변수에는 어떤 값이 들어 있을지 보장할 수 없다.)
이 오류를 없애기 위해서는 칼럼에 NVL처리를 하거나 또는, 별도의 int 타입의 indicator변수를 사용하여 호스트 변수에 사용하는 방법으로 해결할 수 있다. 또는 apre옵션 중  -unsafe_null 옵션을 사용할 수도 있다.

Code Block
themeDJango
languagesql
1) apre –unsafe_null  로 컴파일 한다.
 
2) SELECT NVL (Column, 'N/A') from T1 와 같이 NVL로 대체 값을 넣는 방법
 
3) indicator 변수를 사용하는 방법
    EXEC SQL BEGIN  DECLARE SECTION;
      char  H_var [20 + 1];
      int    H_indi;
    EXEC SQL END DECLARE SECTION;

    EXEC SQL SELECT Column  INTO :H_var indicator :H_indi;

 

Client's query exceeded in the execution time limitation. (SQLCODE=-4164)

질의를 수행하는 과정의 시간이 지정된 시간을 초과하는 경우 해당 질의는 이 에러가 리턴 된다. 문서에서 설명한 질의의 실행계획을 확인하고 필요한 튜닝 조치를 취하거나 만일, 이미 적절한 조치가 됐음에도 전체 처리해야 할 데이터의 양에 의한 경우라면 다음과 같이 수행한 후 질의를 처리하도록 한다.
EXEC SQL ALTER SESSION SET QUERY_TIMEOUT = 0;
"0"으로 설정하면 질의 처리에 대한 Timeout 설정에 상관 없이 수행이 지속된다. 단, 이렇게 할 경우 해당 쿼리가 완료될 때까지는 리소스를 대량으로 점유할 수 있음으로 변경에는 신중하게 결정할 것을 권고한다.

Code Block
themeDJango
languagesql
EXEC SQL ALTER SESSION SET QUERY_TIMEOUT = 0;

 

Communication link failure. (SQLCODE=-331843 or -331855)

질의를 처리하는 과정에서 서버 또는 네트웍의 오류로 인해 연결이 단절되는 경우 발생한다. 이 경우 먼저 $ALTIBASE_HOME/trc/altibase_boot.log 에 timeout과 관련된 로그가 기록되었는지 확인한다. 일반적으로 2개의 경우가 발생한다.

...


"0" 값으로 설정하면 해당 제한이 적용되지 않도록 동작한다. 다만, 개발자가 위와 같이 설정한 경우 QUERY_TIMEOUT의 경우와 마찬가지로 해당 트랜잭션이 지속되는 동안은 DBMS내부의 리소스를 계속 점유하고 경우에 따라 장애를 유발할 가능성이 있기 때문에 위의 문제를 원천적으로 해결할 방안을 모색할 것을 권고한다.
일부 네트웍의 문제 또는 L4, 방화벽의 Timeout등의 설정으로 altibase_boot.log에 오류 없이 단절되는 경우도 있기 때문에 이것은 별도의 PBT(Problem Tracking)과정을 통해 원인을 찾아야 한다.

 

 

타 DBMS에서 변환 시 고려사항

...

앞에서 설명한 각 부분별 내용 중 타DBMS에서 변환할 경우 고려할 부분을 표로 정리한다.

...