External Routine

본 장에서는 다른 프로그래밍 언어로 작성된 external routine을 호출하는 데이터베이스 응용 프로그램을 개발하는 방법을 설명한다.

External Routine

External routine은 shared library에 저장되어 있는 함수이다.
External routine은 PSM routine에서 external routine process (gextproc)를 통해 shared library를 로딩한 후에 실행한다.
gextproc는 external routine의 실행 결과를 PSM routine의 결과로 반환한다.
이 기능을 통해 사용자는 필요한 순간에 shared library에 구현된 기능을 DBMS에서 사용할 수 있다.

External routine

External routine

Call Specification

Call specification은 external C function을 로드하여 실행할 수 있도록 정보를 명시한다.
external C function은 C 언어로 프로그래밍 된 external routine 이다.

Call Specification의 역할은 다음과 같다.

자세한 내용은 Call Specification를 참고한다.

Loading External C Function

External C function을 실행하기 위해서는 external routine process (gextproc)를 시작한다.
PSM routine 런타임 중 GOLDILOCKS에서 설정한 네트워크 연결을 사용하여 아래의 정보를 gextproc에 전달한다.

gextproc는 shared library를 로드하고, external c function을 실행하고 반환한 결과를 전달한다.

External C function을 호출하기 위해서는 아래와 같은 단계를 따른다.

  1. external C function을 정의한다.

  2. shared library에 대응하는 Schema 객체인 library 객체를 생성한다.

  3. external C function을 호출할 수 있도록 call specification을 정의한다.

External C function

external C function을 정의한다.

다음은 간단한 더하기 함수를 생성하는 예이다.

#include <stdio.h>

int add( int a , int b )
{
  return a + b;
}
다음은 linux 환경에서 shared library를 만드는 예이다.
gcc 옵션은 사용자의 편의에 따라 추가할 수 있다.
gcc -shared -fPIC -o add.so add.c

Library 객체 생성

shared library 파일은 external C function이 저장된 동적으로 로드 가능한 운영체제 파일이다.
shared library에 대한 보안을 위해 DBA가 shared library에 대한 액세스를 제어한다.
DBA는 CREATE LIBRARY 구문을 사용하여 shared library를 지칭하는 스키마 객체를 생성한다.
그 다음 권한이 있는 사용자라면 DBA가 library 객체에 대한 EXECUTE 권한을 부여한다.
또는 DBA가 CREATE ANY LIBRARY 권한을 부여할 수 있고 이 권한이 있는 사용자는 직접 library 객체를 생성할 수 있다.
CREATE LIBRARY [ <schema name> ].<library name>
   { IS | AS } '<file path name>';

library 객체를 생성할 때 shared library 파일에 대해 이름만 명시하거나 전체 경로를 명시할 수 있다.

다음 예와 같이 파일 이름만 지정한 경우, shared library 파일을 로딩하려면 해당 파일이 반드시 EXTLIB_DIR 프로퍼티에 설정된 폴더에 위치해야 한다.

CREATE LIBRARY lib_add AS 'add.so';
사용자가 아래와 shared library에 대한 전체 경로를 명시했다면, gextproc는 해당 경로에 있는 shared library 파일을 로딩한다.
CREATE LIBRARY lib_add AS '/home/user/extlib/add.so';

Publish External C function

Proceudre, Function 또는 Package를 DDL 할 때 Call Specification 구문을 작성하여 실행하려고 하는 external C function을 명시한다.

3.1에서 정의한 External C Function을 호출하는 PSM Routine 예는 다음과 같다.

CREATE FUNCTION func1( p1 IN NATIVE_INTEGER, 
                       p2 IN NATIVE_INTEGER )
      RETURN NATIVE_INTEGER AS
LANGUAGE C
LIBRARY lib_add NAME "add"
PARAMETERS ( p1 INT,
             p2 INT,
             RETURN INT );

아래와 같이 stored function 의 PARAMETERS 정의가 external C function 의 prototype 과 일치하는지 확인한다.

gSQL> \set vertical on

gSQL> SELECT routine_name,
       external_name,
       external_c_function
  FROM information_schema.routines
 WHERE routine_name = 'FUNC1';

            ROUTINE_NAME # FUNC1
           EXTERNAL_NAME # add
     EXTERNAL_C_FUNCTION # int add( int P1 , int P2 )

1 row selected.

External C Function 명시

external C function name, parameter datatype 및 return datatype을 PSM routine에 매핑하는 External Routine을 통해 게시된 external C function을 실행할 수 있다.

Call specification은 아래와 같이 구성된다.

Call Specification는 아래 구문에서 사용할 수 있다.
Call Specification 구문은 다음과 같다.
LANGUAGE C
{ LIBRARY <library name> NAME <double_quote_string> |
  NAME <double_quote_string> LIBRARY <library name> }
[ WITH CONTEXT ] 
PARAMETERS ( <external parameter> [ , ... ] )
<library name>은 library 스키마 객체의 이름이며, <double_quote_string> 은 external C function의 이름이다.
 <external parameter> 는 다음과 같다.
{
   CONTEXT
 | <parameter name> [ <property> ] [ BY REFERENCE ] <external datatype> 
 | RETURN [ <property> ] [ BY REFERENCE ] <external datatype> 
}
<property>는 아래와 같다.
{
   INDICATOR | LENGTH | MAXLEN 
}

LIBRARY

shared library에 대응하는 스키마 객체로 library name은 식별자이다.
library는 EXECUTE 권한이 있어야 실행할 수 있다.

NAME

호출하고자 하는 external C function 이름이다.
external C function 이름은 큰따움표(" ")로 묶어서 표기한다.

LANGUAGE

external routine이 작성된 프로그래밍 언어를 나타낸다.
현재는 C언어만 지원되고 있다.

WITH CONTEXT

CONTEXT가 external C function으로 전달되도록 지정한다.

PARAMETERS

external C function에 전달되는 parameter의 순서, datatype을 지정한다.
현재 길이 및 최대 길이와 같은 parameter 속성과 parameter의 값 전달 방법을 지정할 수 있다.

Call Specification 사용 예

gSQL>
CREATE OR REPLACE PROCEDURE proc1( p1 IN NATIVE_INTEGER,
                                   p2 IN NATIVE_INTEGER,
                                   p3 OUT NATIVE_INTEGER )
AS
LANGUAGE C
LIBRARY lib_sample NAME "sample_add_proc"
PARAMETERS( p1 INT,
            p2 INT,
            p3 INT );
/

Procedure created.
gSQL>
CREATE OR REPLACE FUNCTION func1( p1 IN NATIVE_INTEGER,
                                  p2 IN NATIVE_INTEGER )
    RETURN NATIVE_INTEGER
AS
LANGUAGE C
NAME "sample_add_func" LIBRARY lib_sample
PARAMETERS( p1 INT,
            p2 INT,
            RETURN INT );
/

Function created.
gSQL>
CREATE OR REPLACE PACKAGE pkg1 AS  
  FUNCTION func1( p1 IN NATIVE_INTEGER,
                  p2 IN NATIVE_INTEGER )
      RETURN NATIVE_INTEGER
  AS
  LANGUAGE C
  NAME "sample_add_func" LIBRARY lib_sample
  PARAMETERS( p1 INT,
              p2 INT,
              RETURN INT );
END;
/

Package created.
gSQL>
CREATE OR REPLACE PACKAGE pkg1 AS   
  FUNCTION func1( p1 IN NATIVE_INTEGER,
                  p2 IN NATIVE_INTEGER )
      RETURN NATIVE_INTEGER;
END;
/

Package created.

gSQL>
CREATE OR REPLACE PACKAGE BODY pkg1 AS   
  FUNCTION func1( p1 IN NATIVE_INTEGER,
                  p2 IN NATIVE_INTEGER )
      RETURN NATIVE_INTEGER
  AS
  LANGUAGE C
  NAME "sample_add_func" LIBRARY lib_sample
  PARAMETERS( p1 INT,
              p2 INT,
              RETURN INT );
END;
/

Package created.

Passing Parameters to External C Functions with Call Specifications

Call specification은 PSM과 C의 datatype를 매핑한다.
몇 가지 이유가 있어 external C function의 parameter는 전달하기 복잡하다.

Parameter Data Type Mapping

PSM Routine의 parameter와 external C function은 아래의 표과 같이 매핑된다.
Parameter Data Type Mapping

PSM datatype

external datatype

recommend external datatype

BOOLEAN

UNSIGNED CHAR

CHAR

UNSIGNED SHORT

SHORT

UNSIGNED INT

INT

UNSIGNED LONG

LONG

CHAR

NATIVE_SMALLINT

UNSIGNED CHAR

CHAR

UNSIGNED SHORT

SHORT

UNSIGNED INT

INT

UNSIGNED LONG

LONG

SHORT

NATIVE_INTEGER

UNSIGNED CHAR

CHAR

UNSIGNED SHORT

SHORT

UNSIGNED INT

INT

UNSIGNED LONG

LONG

INT

NATIVE_BIGINT

UNSIGNED CHAR

CHAR

UNSIGNED SHORT

SHORT

UNSIGNED INT

INT

UNSIGNED LONG

LONG

LONG

NATIVE_REAL

FLOAT

DOUBLE

FLOAT

NATIVE_DOUBLE

FLOAT

DOUBLE

DOUBLE

FLOAT

DOUBLE

DOUBLE

NUMBER w/o precision

DOUBLE

DOUBLE

NUMBER w/ precision

SQL_NUMERIC

SQL_NUMERIC

CHAR

STRING

STRING

VARCHAR

STRING

STRING

LONG VARCHAR

SQL_LONG_VARIABLE_LENGTH

SQL_LONG_VARIABLE_LENGTH

BINARY

RAW

RAW

VARBINARY

RAW

RAW

LONG VARBINARY

SQL_LONG_VARIABLE_LENGTH

SQL_LONG_VARIABLE_LENGTH

DATE

SQL_DATA

SQL_TIMESTAMP

SQL_TIMESTAMP

TIME

SQL_TIME

SQL_TIME

TIME WITH TIME ZONE

SQL_TIME_TZ

SQL_TIME_TZ

TIMESTAMP

SQL_TIMESTAMP

SQL_TIMESTAMP

TIMESTAMP WITH TIME ZONE

SQL_TIMESTAMP_TZ

SQL_TIMESTAMP_TZ

INTERVAL YEAR TO MONTH

SQL_INTERVAL

SQL_INTERVAL

INTERVAL DAY TO SECOND

SQL_INTERVAL

SQL_INTERVAL

ROWID

STRING

STRING

External Data Type Mapping

external datatype은 C datatype 에 매핑된다. C prototype을 작성 할 때 오류가 발생하지 않도록 아래의 표를 참고한다.
External Data Type Mappings

external datatype

parameter mode가

IN 이거나

RETURN

parameter mode가

IN by reference 이거나

RETURNING by reference

parameter mode가

OUT type,

IN OUT type 일 때

UNSIGNED CHAR

unsigned char

unsigned char *

unsigned char *

CHAR

char

char *

char *

UNSIGNED SHORT

unsigned short

unsigned short *

unsigned short *

SHORT

short

short *

short *

UNSIGNED INT

unsigned int

unsigned int *

unsigned int *

INT

int

int *

int *

UNSIGNED LONG

unsigned long

unsigned long *

unsigned long *

LONG

long

long *

long *

FLOAT

float

float *

float *

DOUBLE

double

double *

double *

STRING

char *

char *

char *

RAW

unsigned char *

unsigned char *

unsigned char *

SQL_LONG_VARIABLE_LENGTH

SQL_LONG_VARIABLE_LENGTH_STRUCT *

SQL_LONG_VARIABLE_LENGTH_STRUCT *

SQL_LONG_VARIABLE_LENGTH_STRUCT *

SQL_NUMERIC

SQL_NUMERIC_STRUCT *

SQL_NUMERIC_STRUCT *

SQL_NUMERIC_STRUCT *

SQL_DATE

SQL_DATE_STRUCT *

SQL_DATE_STRUCT *

SQL_DATE_STRUCT *

SQL_TIME

SQL_TIME_STRUCT *

SQL_TIME_STRUCT *

SQL_TIME_STRUCT *

SQL_TIME_TZ

SQL_TIME_WITH_TIMEZONE_STRUCT *

SQL_TIME_WITH_TIMEZONE_STRUCT *

SQL_TIME_WITH_TIMEZONE_STRUCT *

SQL_TIMESTAMP

SQL_TIMESTAMP_STRUCT *

SQL_TIMESTAMP_STRUCT *

SQL_TIMESTAMP_STRUCT *

SQL_TIMESTAMP_TZ

SQL_TIMESTAMP_WITH_TIMEZONE_STRUCT *

SQL_TIMESTAMP_WITH_TIMEZONE_STRUCT *

SQL_TIMESTAMP_WITH_TIMEZONE_STRUCT *

SQL_INTERNAL

SQL_INTERVAL_STRUCT *

SQL_INTERVAL_STRUCT *

SQL_INTERVAL_STRUCT *

Passing Parameters by VALUE or BY REFERENCE

parameter mode가 IN type이거나 RETURN이면 기본적으로 pass by value로 전달된다. BY REFERENCE 옵션을 주면, parameter mode가 IN type이거나 RETURN이어도 pass by reference로 전달된다.
parameter mode가 OUT type, IN OUT type이면 기본적으로 pass by reference로 전달된다. BY REFERENCE 옵션을 주어도 아무런 영향이 없다.
단, 아래의 external datatype은 기본적으로 pass by reference로 전달된다.

Declaring Formal Parameters

일반적으로 external C function을 실행하는 routine은 다음과 같이 formal parameter들을 선언한다.
CREATE FUNCTION func1( p1 IN NATIVE_INTEGER, 
                       p2 IN NATIVE_INTEGER )
      RETURN NATIVE_INTEGER AS
LANGUAGE C
LIBRARY lib_add NAME "add"
PARAMETERS ( p1 INT,
             p2 INT,
             RETURN INT );
각 formal parameter를 선언할 때, parameter mode와 datatype을 지정한다. 이런 정보들은 external C function을 위해 필요한 정보로써 PARAMETERS 절을 사용하여 다음과 같은 정보를 제공한다.
PARAMETERS 절을 작성할 때는 다음과 같은 사항에 유의한다.

Properties

PARAMETERS 절을 사용하여 PSM Formal Parameter 및 함수 결과를 external C function으로 전달할 수 있다. Property를 지정하여 이를 수행한다.

아래 표는 지정된 Property에 허용되는 external datatype, PSM datatype 및 PSM Parameter mode를 나타낸다.

Properties and data types

Property

external datatype

default external datatype

PSM datatype

PSM Parameter Mode

Passing Method

INDICATOR

short

short

all datatype

IN

IN OUT

OUT

RETURN

BY VALUE

BY REFERENCE

BY REFERENCE

BY REFERENCE

LENGTH

unsigned short

short

unsigned int

int

unsigned long

long

int

CHAR

VARCHAR

LONG VARCHAR

BINARY

VARBINARY

LONG VARBINARY

IN

IN OUT

OUT

RETURN

BY VALUE

BY REFERENCE

BY REFERENCE

BY REFERENCE

MAXLEN

unsigned short

short

unsigned int

int

unsigned long

long

int

CHAR

VARCHAR

LONG VARCHAR

BINARY

VARBINARY

LONG VARBINARY

IN OUT

OUT

RETURN

BY VALUE

다음은 PARAMETERS 절을 사용하여 PSM formal parameter와 RETURN의 property를 지정하는 예이다.
CREATE OR REPLACE FUNCTION strncpy_func( p1 IN CHAR(10),
                                         p2 IN OUT CHAR(10) )
RETURN VARCHAR AS
LANGUAGE C
LIBRARY strncpy_lib NAME "strncpy_extern_func"
PARAMETERS( p1 STRING,              -- formal parameter p1의 값
            p1 INDICATOR SHORT,     -- formal parameter p1의 NULL 여부
            p1 LENGTH INT,          -- formal parameter p1의 현재 길이
            p2 STRING,              -- formal parameter p2의 값
            p2 INDICATOR SHORT,     -- formal parameter p2의 NULL 여부
            p2 LENGTH INT,          -- formal parameter p2의 현재 길이     
            p2 MAXLEN INT,          -- formal parameter p2의 최대 길이                   
            RETURN LENGTH INT,      -- RETURN 값의 현재 길이
            RETURN STRING );        -- RETURN 값
위의 PARAMETERS 절을 통해 예측할 수 있는 C prototype은 다음과 같다.
char * strncpy_extern_func( char  * p1,
                            short   p1_indicator,
                            int     p1_length,
                            char  * p2,
                            short * p2_indicator,
                            int   * p2_length,
                            int     p2_maxlen,
                            int   * return_length );
위의 같은 C prototype은 information_schema.routines를 통해 확인할 수 있다.
gSQL> \set vertical on

gSQL>
SELECT routine_name,
       external_name,
       external_c_function
  FROM information_schema.routines
 WHERE routine_name = 'STRNCPY_FUNC';

            ROUTINE_NAME # STRNCPY_FUNC
           EXTERNAL_NAME # strncpy_extern_func
     EXTERNAL_C_FUNCTION # char * strncpy_extern_func( char * P1 , short P1_INDICATOR , int P1_LENGTH , char * P2 , short * P2_INDICATOR , int * P2_LENGTH , int P2_MAXLEN , int * RETURN_LENGTH )

1 row selected.

INDICATOR

INDICATOR는 parameter의 NULL 여부를 나타내는 property이다.
DBMS에서는 NULL 개념이 있지만, C언어에서는 NULL 개념이 없다.
PSM Routine 과 external C function 간의 NULL에 대한 정보를 전달할 수 있어야 한다.
Parameter가 NULL일 경우 external C function에서 parameter 값이 NULL임을 확인할 수 있어야 한다.
반대로 external C function의 반환 결과가 NULL이면 이를 서버에서 확인할 수 있어야 한다.
이런 경우에 INDICATOR property를 통해 정보를 전달할 수 있다.
INDICATOR 값은 SQL_NULL_DATA 상수를 통해 확인할 수 있다.
Parameter mode와 RETURN 인 경우 INDICATOR 값은 아래와 같이 전달된다.

LENGTH and MAXLEN

LENGTH와 MAXLEN은 CHARACTER STRING 타입, BINARY STRING 타입의 parameter의 현재 길이와 최대 길이를 나타내는 Property이다.

LENGTH 값은 Parameter mode와 RETURN의 현재 길이를 나타내며 다음과 같이 전달된다.

MAXLEN 값은 Parameter mode와 RETURN의 최대 길이를 나타내며 다음과 같이 전달된다.

BY REFERENCE

BY REFERENCE는 참조에 의한 전달로 값을 전달하도록 지정한다.
C 언어에서는 상수를 값에 의해 전달하거나 참조에 의한 전달할 수 있다.
C 함수가 포인터 변수일 경우, BY REFERENCE 구문을 지정하여 parameter를 참조로 전달한다.

다음은 사용 예이다.

CREATE OR REPLACE PROCEDURE circleArea( radius IN NATIVE_INTEGER,
                                        area OUT FLOAT ) AS
LANGUAGE C
NAME "circle_area" LIBRARY circle_lib
PARAMETERS( radius BY REFERENCE INT,
            area DOUBLE );
/
이 경우, c 함수의 prototype은 다음과 같다.
void circle_area( int * radius , double * area );

WITH CONTEXT

WITH CONTEXT 절을 포함하면 external C function에서 parameter, 메모리 할당 및 예외에 대한 정보의 접근 권한을 부여할 수 있다.
WITH CONTEXT 절은 CONTEXT 변수가 external C function에 전달되도록 지정한다.

다음은 사용 예이다.

CREATE FUNCTION str_concat( p1 VARCHAR,
                            p2 VARCHAR )
RETURN VARCHAR
AS LANGUAGE C
LIBRARY lib_context
NAME "c_concat"
WITH CONTEXT
PARAMETERS ( CONTEXT,
             p1 STRING,
             p2 STRING,
             RETURN STRING );
/
이 경우의 c 함수의 prototype은 아래와 같다.
char * c_concat( SQLExtProcContext * context , char * p1 , char * p2 );

Using Service Routines with External C Functions

SQLExtProcAllocCallMemory()

SQLExtProcAllocCallMemory 함수는 external C function에서 메모리를 할당한다.
함수에서 할당된 메모리들은 PSM으로 반환되는 즉시 자동으로 해제된다.
다음은 SQLExtProcAllocCallMemory()를 위한 PSM과 C 함수의 예이다.
char * c_concat( SQLExtProcContext * context,    
                 char              * p1,    
                 short               p1_indicator,     
                 int                 p1_length,         
                 char              * p2,    
                 short               p2_indicator,    
                 int                 p2_length,    
                 short             * result_indicator,    
                 int               * result_length );
CREATE OR REPLACE  FUNCTION str_concat( p1 VARCHAR,
                                        p2 VARCHAR )
  RETURN VARCHAR AS
LANGUAGE C
LIBRARY lib_concat
NAME "c_concat"
WITH CONTEXT
PARAMETERS ( CONTEXT,
             p1 STRING,
             p1 INDICATOR SHORT,
             p1 LENGTH INT,
             p2 STRING,
             p2 INDICATOR SHORT,
             p2 LENGTH INT,
             RETURN INDICATOR SHORT,
             RETURN LENGTH INT,
             RETURN STRING );
/
gSQL>
SELECT str_concat( 'Hello, ' , 'GOLDILOCKS' ) FROM dual;

STR_CONCAT( 'Hello, ' , 'GOLDILOCKS' )
--------------------------------------
Hello, GOLDILOCKS                     

1 row selected.
#include <stdio.h>
#include <string.h>
#include <goldilocks.h>

char * c_concat( SQLExtProcContext * context,
                 char              * p1,
                 short               p1_indicator,
                 int                 p1_length,
                 char              * p2,
                 short               p2_indicator,
                 int                 p2_length,
                 short             * result_indicator,
                 int               * result_length )
{ 
    char * resultStr       = NULL;
    int    resultLen       = 0;
    short  resultIndicator = SQL_NULL_DATA;

    /*
     * NULL 여부 확인
     */      
    
    if( (p1_indicator == SQL_NULL_DATA) || (p2_indicator == SQL_NULL_DATA) )
    {
        resultIndicator = SQL_NULL_DATA;
        resultLen       = 0;
             
        /*   
         * PSM에는 NULL Pointer가 없다. 따라서 결과로 zero-byte string을 반환한다.
         */

        resultStr = SQLExtProcAllocCallMemory( context, 1 );
        resultStr[resultLen] = '\0';
    }
    else
    {
        resultIndicator = !SQL_NULL_DATA;
        resultLen       = p1_length + p2_length;

        /*
         * 결과를 위한 메모리를 마지막 NULL terminator를 고려하여 할당한다.
         */

        resultStr = SQLExtProcAllocCallMemory( context, resultLen + 1 );

        /*
         * string concat
         */

        strcpy( resultStr, p1 );
        strcat( resultStr, p2 );
        resultStr[resultLen] = '\0';
    }

    /*
     * 결과 반환
     * 할당된 메모리는 PSM에서 나중에 메모리 해제 한다.
     */

    *result_indicator = resultIndicator;
    *result_length    = resultLen;

    return resultStr;
}

SQLExtProcRaiseServerError()

SQLExtProcRaiseServerError 함수는 사전에 정의된 예외를 발생시킨다.
이 예외를 사용하기 위해서는 GOLDILOCKS의 에러 번호를 표기해야 한다.
다음은 SQLExtProcRaiseServerError()를 위한 PSM과 C 함수의 예제이다.
void c_division( SQLExtProcContext * context,
                 int                 dividend,
                 int                 divisor,
                 double            * result );
CREATE OR REPLACE PROCEDURE proc_division( dividend     NATIVE_INTEGER,
                                           divisor  IN  NATIVE_INTEGER,
                                           result   OUT FLOAT )
AS
LANGUAGE C
LIBRARY lib_division NAME "c_division"
WITH CONTEXT
PARAMETERS( CONTEXT,
            dividend INT,
            divisor  INT,
            result   DOUBLE );
/
gSQL> \var ret double

gSQL> CALL proc_division( 10 , 0 , :ret );

ERR-38000(12122): divisor is equal to zero : ERROR at PROCEDURE("PROC_DIVISION")
#include <stdio.h>
#include <assert.h>
#include <goldilocks.h>

void c_division( SQLExtProcContext * context,
                 int                 dividend,
                 int                 divisor,
                 double            * result )
{
    /*
     * divisor가 0인지 확인
     */
    
    if( divisor == 0 )
    {
        /*
         * ZERO_DIVIDE 에러를 반환한다.
         * GOLDILOCKS의 에러 코드는 12122 이다.
         */ 
    
        if( SQLExtProcRaiseServerError( context, 12122 ) == SQL_SUCCESS )
        {
            return;
        }
        else
        {
            assert( 0 );
        }
    }

    *result = (double) dividend / (double) divisor;
}

SQLExtProcRaiseUserError()

SQLExtProcRaiseUserError 함수는 사용자가 정의한 예외를 발생시킨다.
사용자는 에러 코드와 에러 메시지를 지정하여 에러를 정의할 수 있다.
에러 코드는 20000 ~ 20999 사이에서 사용할 수 있다.
다음은 SQLExtProcRaiseUserError()를 위한 PSM과 C 함수의 예제이다.
void c_division( SQLExtProcContext * context,
                 int                 dividend,
                 int                 divisor,
                 double            * result );
CREATE OR REPLACE PROCEDURE proc_division( dividend     NATIVE_INTEGER,
                                           divisor  IN  NATIVE_INTEGER,
                                           result   OUT FLOAT )
AS
LANGUAGE C
LIBRARY lib_division NAME "c_division"
WITH CONTEXT
PARAMETERS( CONTEXT,
            dividend INT,
            divisor  INT,
            result   DOUBLE );
/
gSQL> \var ret double

gSQL> CALL proc_division( 10 , 0 , :ret );

ERR-38000(20001): 0 is not allowed in the divisor. : ERROR at PROCEDURE("PROC_DIVISION")
#include <stdio.h>
#include <assert.h>
#include <goldilocks.h>

void c_division( SQLExtProcContext * context,
                 int                 dividend,
                 int                 divisor,
                 double            * result )
{
    /*
     * divisor가 0인지 확인
     */
            
    if( divisor == 0 )
    {
        /*  
         * User Defined exception을 반환한다.
         * 이 때 에러코드와 메시지를 포함해야 한다.
         * 사용자가 사용할 수 있는 에러코드는 20000 ~ 20999 범위이다.
         */
            
        if( SQLExtProcRaiseUserError( context, 20001, (SQLCHAR *) "0 is not allowed in the divisor." ) == SQL_SUCCESS )
        {
            return;
        } 
        else
        {
            assert( 0 );
        }
    }

    *result = (double) dividend / (double) divisor;
}

ERR-39000(26009): Lost Connection to 'gextproc' 에러 해결 가이드

ERR-39000(26009): lost connection to 'gextproc' 에러는 주로 두 가지 사용자 실수로 인해 발생한다.
gextproc 프로세스가 비정상 종료되어 ERR-39000(26009) 에러가 발생했을 경우, $GOLDILOCKS_DATA/extlib 디렉토리에 프로세스 ID로 구분되어 trace log 파일이 생성된다.

사용자가 작성한 External C Function이 잘못된 경우

사용자 라이브러리가 잘못된 경우, trace log 파일의 callstack에서 사용자 라이브러리에서 호출한 C 함수명을 확인할 수 있다.
다음은 잘못된 C 함수이다.
int add_f(int a, int b)
{
    int *c = NULL;
    *c = a + b;
    return a + b;
}
다음은 external C function을 호출하는 PSM routine을 작성하는 예제이다.
gSQL>
CREATE LIBRARY lib1 as 'add.so';
/
Library created.

gSQL>
CREATE OR REPLACE FUNCTION func1( p1 NATIVE_INTEGER, p2 NATIVE_INTEGER ) 
    RETURN NATIVE_INTEGER AS
LANGUAGE C
LIBRARY lib1 NAME "add_f"
PARAMETERS ( p1 INT , p2 INT , RETURN INT );
/
Function created.

gSQL> SELECT func1(1, 1) FROM dual;
ERR-39000(26009): lost connection to 'gextproc': ERROR at FUNCTION("FUNC1")
다음은 위와 같이 생성된 트레이스 파일 (gextproc.448892.trc)의 내용이다.
% cat gextproc.448892.trc
SIGNAL(11) received
=================================================
CALL STACK
=================================================
gextproc(stbBacktraceToFile+0x18)[0x4f2ed8]
gextproc(ztemFatalHandler+0x178)[0x431e58]
gextproc(steFatalHandler+0x6a)[0x4f2e6a]
/lib/x86_64-linux-gnu/libpthread.so.0(+0x14420)[0x7faaa025c420]
/home/test/work/product/Gliese/home/extlib/managed/add.so(add_f+0x22)[0x7faaa03d111b]
gextproc[0x4f2a12]
Trace log 파일을 보면 callstack에 add.so(add_f+0x22) 라인이 포함되어 있는 것을 확인할 수 있다.

사용자가 작성한 External C Function과 PSM Routine의 PARAMETERS 정보가 다를 경우

사용자가 작성한 external C function과 PSM routine의 PARAMETERS 정보가 다르면, trace log 파일의 callstack에 gextproc 만 존재한다.
다음은 사용자가 작성한 C 함수이다.
int add(int p1, int p2)
{
    return p1 + p2;
}
다음은 add 라는 external C function을 호출하는 PSM routine 이다.
gSQL> 
CREATE LIBRARY lib2 AS 'add.so';
/
Library created.

gSQL>
CREATE OR REPLACE FUNCTION func2(p1 NATIVE_INTEGER, 
                                 p2 NATIVE_INTEGER,
                                 p3 NATIVE_INTEGER)
    RETURN VARCHAR AS
LANGUAGE C
LIBRARY lib2 NAME "add"
PARAMETERS ( p1 INT , p2 INT , p3 INT , RETURN STRING );
/
Function created.

gSQL> SELECT func2(1, 1, 1) FROM dual;
ERR-39000(26009): lost connection to 'gextproc': ERROR at FUNCTION("FUNC2")
다음은 위와 같이 생성된 트레이스 파일 (gextproc.449987.trc) 의 내용이다.
% cat gextproc.449987.trc
SIGNAL(11) received
=================================================
CALL STACK
=================================================
gextproc(stbBacktraceToFile+0x18)[0x4f2ed8]
gextproc(ztemFatalHandler+0x178)[0x431e58]
gextproc(steFatalHandler+0x6a)[0x4f2e6a]
/lib/x86_64-linux-gnu/libpthread.so.0(+0x14420)[0x7f5b0a52a420]
/lib/x86_64-linux-gnu/libc.so.6(+0x188915)[0x7f5b0a49c915]
gextproc(ffvExternalStringToParameterVarChar+0x108)[0x438a48]
gextproc(fffSetOutParameterValues+0x315)[0x438475]
gextproc(fflExecuteFunction+0x138)[0x434eb8]
gextproc(ztepCmdCallExternalFunction+0x177)[0x4341d7]
gextproc(ztecRun+0x362)[0x432b52]
gextproc(main+0x1ce)[0x431bce]
/lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0xf3)[0x7f5b0a338083]
gextproc(_start+0x2e)[0x431c1e]
callstack에 사용자 라이브러리는 보이지 않고 gextproc에만 문제가 발생한 것을 확인할 수 있다.
PSM routine의 PARAMETERS 정보를 통해 구성한 external C function prototype은 다음과 같이 조회할 수 있다.
gSQL>
SELECT specific_name ,
       external_c_function
  FROM information_schema.routines
 WHERE specific_name = 'FUNC2';

SPECIFIC_NAME EXTERNAL_C_FUNCTION                   
------------- --------------------------------------
FUNC2         char * add( int P1 , int P2 , int P3 )

1 row selected.
위의 정보를 통해 사용자가 작성한 external C function과 PSM routine을 생성할 때의 PARAMETERS 정보가 다르다는 것을 확인할 수 있다.
int add(int p1, int p2)
char * add( int P1 , int P2 , int P3 )
External routine을 정상적으로 실행하려면 사용자가 작성한 external C function과 PSM routine을 생성할 때 기술하는 PARAMETERS 정보가 일치해야 한다.
그렇지 않을 경우, external routine process가 비정상적으로 종료될 뿐만 아니라 그 외 예기치 못한 결과가 발생할 수 있다.