Versions Compared

Key

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

...

The basic header file and library for Altibase development are located below.

 

 
Location

Header file

$ALTIBASE_HOME/include

Library

$ALTIBASE_HOME/lib

The part to be added in the library is "-lapre –lodbccli". With this reference, a simple Makefile that compiles connect1.sc can be written as follows.

Code Block
%.cpp : %.sc
        apre -unsafe_null -t cpp $<
connect1: connect1.cpp
        g++ -Wl,-relax -L. -O3 -L${ALTIBASE_HOME}/lib -I${ALTIBASE_HOME}/include -o connect1 connect1.o -lapre -lodbccli -ldl -lpthread -lcrypt -lrt

In addition to the ALTIBASE library, a system library is required for compilation. For a list of system libraries required for each platform and an example of a makefile, use the list defined in the "LIBS" tag in the $ALTIBASE_HOME/install/altibase_env.mk file.

One more point to consider is that if the user uses a C compiler other than a C++ compiler, the ALTIBASE library is provided in C++, so the C++ system library must be linked for compatibility. For details, refer to the "Altibase APRE(SES)C makefile".

Host Variables

When coding using SQL statements on the source, all variables used to control I/O for data to the DBMS are referred to as host variables. Prior to ALTIBASE version 5.3.3, all host variables could be used only those declared in the “EXEC SQL BEGIN DECLARE SECTION” and “EXEC SQL END DECLARE SECTION” clauses.

However, starting from version 5.3.3 or later, it is possible to recognize general variables as host variables through the apre option. (apre -parse full option)

Code Block
Ex) char H_TODAY [20];
 
EXEC SQL SELECT TO_CHAR(SYSDATE, ‘yyyy-mm-dd’) INTO :H_TODAY  FROM DUAL;

In the process of development, there may be a case where it is necessary to write a host variable in the Target clause of the SELECT statement. From ALTIBASE version 5, it is supported in the following form with an operator called CAST.

 

Code Block
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 Structure

ALTIBASE returns an error with the sqlca structure. The user can check the error code and error message with the following.

Structure

Description

sqlca.sqlcode

SQL_ERROR (-1)

When an error occurs

SQL_SUCCESS (0)

When processed normally

 

SQL_SUCCESS_WITH_INFO (1)

When it has been processed but the user confirmation is required

 

SQL_NO_DATA

When there is no target data

sqlca.sqlerrm.sqlerrmc

Contain an error message

 

sqlca.sqlerrd[2]

In the case of modified DML, it has the number of processed records, and in the case of the select statement, when the host variable is an array, it only has the number of arrays (not cumulative) of variables containing the currently fetched records.

SQLCODE

Contain error codes defined in ALTIBASE

 

SQLSTATE

Contain CLI standard error codes (ex) 08S01

Since the above code values are already defined in the source in the precompile stage, the developer does not need to redefine or include it in the header.

Connect/Disconnect 

ALTIBASE does not have the same configuration as a separate listener and is implemented in a form that directly connects to the thread inside the DBMS. Therefore, the connection is made by directly entering the IP and PORT number of the server to be connected. The following is a basic example code for connecting to ALTIBASE.

Code Block
  // Start of host variable declaration
  EXEC SQL BEGIN  DECLARE SECTION;      
  char   usr[20];
  char   pwd[20];
  char   opt[200];
  // End of host variable delcaration
  EXEC SQL END  DECLARE SECTION;        
 
    
  main()
  {
  // Unique ID of DB user account
  sprintf (usr, “sys”);  
 
  // Password of DB user account
  sprintf (pwd, “manager”);                
   
  // Server IP and conenction port
  sprintf (opt, “DSN=127.0.0.1; PORT_NO=20300;CONNTYPE=1”);
 
  // Connection trial
  EXEC SQL CONNECT :usr IDENTIFIED BY  :pwd USING :opt;
  if (sqlca.sqlcode != 0)
  {
          Printf(“ConnectError:: %d %s\n”, SQLCODE,  sqlca.sqlerrm.sqlerrmc);
  }
 
}

 

Connection type


The type of connection method is the setting value of the CONNTYPE property, and one of three types can be selected: TCP/IP, unix domain, and IPC.

 

CONNTYPE

Description

CONNTYPE=1

Communication between the program and DBMS is performed in the TCP/IP method

CONNTYPE=2

Communication between the program and DBMS is performed in the unix domain method

CONNTYPE=3

Communication between the program and DBMS is performed in the IPC method

CONNTYPE=2, 3 methods can be used only if the program is located in the same equipment as the DBMS server. Since both methods do not incur communication costs up to the hardware level, it is recommended if achieving faster performance than TCP/IP is required. When selecting the IPC connection method, the IPC_CHANNEL_COUNT in the ALTIBASE_HOME/conf/altibase.properties file must be adjusted to a value of 1 or larger. This setting can be used only by restarting the DB, and kernel settings related to IPC must be sufficiently adjusted in advance.

Connection Name


It is possible to give an explicit name at the time of connection, but it is performed as follows.

 

Code Block
EXEC SQL AT CONN1 CONNECT ~~
 
EXEC SQL AT :ConnName CONNECT ~~
It can be processed by saving it as an explicit name or host variable.

Connection close

The method of disconnecting the connection is as follows. If abnormal connection termination occurs, be sure to perform "EXEC SQL FREE" before attempting to reconnect.
Code Block
EXEC SQL DISCONNECT;
 
EXEC SQL AT CONN1 DISCONNECT;
 
EXEC SQL AT :ConnName DISCONNECT;
Info

Please note that ALTIBASE DISCONNECT is operated in NonAuto-Commit mode, but it should be noted that the changed transaction normally processed immediately before DISCONNECT by the user is committed. (If the user does not want to reflect it, the user must explicitly perform rollback and then disconnect.)

 

Commit/Rollback 

Commit/Rollback can be executed as follows.

...

In a non-autocommit environment, it is not recommending to fetch the cursor by record unit after opening the cursor and commit/rollback during fetching in the ANSI standard. Altibase complies with ANSI standards, and errors occur in FAC by default. However, starting from Altibase version 6.3.1 or later, the FAC function is supported with the option of opening the cursor.

Example of code which the error occurs

 

Code Block
while (1)
{
    EXEC SQL FETCH CURSOR1 INTO :O_C1,   :O_C2;
    if (sqlca.sqlcode != 0)
    {
        printf(“FetchError::%d, %s\n”, SQLCODE, sqlca.sqlerrm.sqlerrmc);
        break;
    }
 
 
    EXEC SQL UPDATE SET CHANGE_V = :O_C2 WHERE C1 = :O_C1;
    if (sqlca.sqlcode != SQL_SUCCESS)
       EXEC SQL ROLLBACK;
    else
       EXEC SQL COMMIT;
}

In the above code, an error check is performed at the point after the fetch, and the following errors may occur during the execution. (Based on ALTIBASE version 5.3)

Code Block
shell>./SampleTest
FetchError: 100, Not found data

At this point, there are more actual records, but this error occurs because cursor1 that was previously opened at the time of committing has been internally terminated and there are no more result sets to fetch. (This error may be somewhat ambiguous to judge because it is the same as when there is no actual data, but this situation can be considered if such an error occurs below the expected number.)

Therefore, when a separate change transaction needs to be created in the process of fetching using a cursor, the session must be executed in auto-commit, or a separate connection object must be created and processed with the session.

FAC Error Message

Depending on the version of Altibase, the FAC error message is displayed slightly differently.

Version
Error Code
Error Message
Reference
 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 or laterERR-410D2  Request of fetching data to an unprepared SQL statement.http://aid.altibase.com/x/9oKZ

Example of performing FAC by creating a separate connection

 

Code Block
main()
{
    // DB Connection
 
    // Create conneciton for separate change transaction
    EXEC SQL AT CONN1 CONNECT :usr IDENTIFIED BY :pwd USING :opt;
 
    // Omit cursor mode
    while (1)
    {
        EXEC SQL FETCH CURSOR1 INTO :O_C1,  :O_C2;
        // Specify the connection name created above at the time of change.
        EXEC SQL AT CONN1 UPDATE t1 set change_v = :O_C2 where C1 = :O_C1;
   
       // In commit / rollback, specify the connection name created above.
       If (sqlca.sqlcode != SQL_SUCCESS)
           EXEC SQL AT CONN1 ROLLBACK;
        Else
           EXEC SQL AT CONN1 COMMIT;
    }
}


In the case of using a separate connection, the user must consider that the entire execution flow must be tied to one transaction. When using a separate connection object, some are committed due to an error during execution and some cannot be processed. If it is to be processed in the form of All or Nothing in one transaction,  the commit/rollback statement must be used outside the fetch-loop.

Use "WITH HOLD" statement when declaring cursor

Starting from Altibase version 6.3.1 or later, if the cursor declared with "WITH HOLD" is opened, the cursor is not closed even after the transaction is completed after Commit/Rollback is executed. This is valid only when the session is in non-autocommit mode. For more detailed information, please refer to "ALTIBASE CLI User's Manual" and "ALTIBASE Precompiler Manual".

 

Code Block
main()
{
     .....
     EXEC SQL AUTOCOMMIT OFF;
     ......
     EXEC SQL DECLARE CURSOR1 CURSOR WITH HOLD FOR      // When declaring a cursor, use the With hold option.
                                 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;
        // FAC error does not occur even if Commit/Rollback is executed.
       If (sqlca.sqlcode != SQL_SUCCESS)
           EXEC SQL ROLLBACK;
       Else
           EXEC SQL COMMIT;
        ......
    }
   ...
}

Dynamic SQL 

Two examples of change transaction and inquiry transaction are described as follow.

 

Code Block
EXEC SQL BEGIN DECLARE SECTION;
char  sql_text [1024];
int    H_var1;
int    H_var2;
EXEC SQL END DECLARE SECTION;
 
 
main()
{
    // DB Connection
    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);
        }
    }
 
}


Example of query transaction

 

Code Block
EXEC SQL BEGIN DECLARE SECTION;
char  sql_text [1024];
int    H_var1;
int    H_var2;
EXEC SQL END DECLARE SECTION;
 
main()
{
    // DB Connection
 
    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;
 
    // The following code is the same as cursor fetch.

This is a method of creating an SQL statement in a separate char* type variable and dynamically using the SQL statement when the table name is not specified or the column or search condition needs to be changed.

Starting from ALTIBASE 5.1 or later,  the execution plan executed by all sessions is internally shared in the area called Plan-Cache. However, if the query statement is changed each time like Dynamic SQL, the execution plan already created in Plan-Cache is not used, and the query performance is inevitably slower than that of static SQL statements because it operates in Prepare/Execute form again.

Call Function


This describes an example of how to call a user-created or DBMS built-in function.

Example of calling DBMS built-in function

Code Block
EXEC SQL BEGIN DECLARE SECTION;
 char H_day [20];
EXEC SQL END DECLARE SECTION;
 
main()
{
    // Example of using a function to retrieve a date
    EXEC SQL SELECT TO_CHAR(SYSDATE,‘yyyy-mm-dd hh:mi:ss’) INTO :H_day from dual;
}


 

Example of calling user function

Code Block
// Function that adds the two input values and returns
Create or replace function user_func (a integer, b integer)
Return integer
As
Begin
    Return a + b;
End;
/
 
---- calling source ----
main()
{
    // Example of using user-generated function
    EXEC SQL SELECT user_func (1, 2) INTO :H_value from dual;
}

 

It doesn't always have to be from dual, and it is fine if the user designates an arbitrary table as needed. (The user should be cautious about the function when char/varchar is returned in the return clause, the returned length must be specified. If not specified, only 1 byte is returned, so the following exception occurs in most functions: “Invalid length of the data type”)

Call Procedure

Unlike other functions, procedures are executed with the separate calling method. Please note that functions can also be used in the same way.

Code Block
// In the case of a function
EXEC SQL EXECUTE
BEGIN
      :H_var := user_func (:in_param1,  :in_param2);
END;
END-EXEC;
 
// In the case of a procedure, there is no return clause.
EXEC SQL EXECUTE
BEGIN 
       user_proc (:in_param1 in, :in_param2 out);
END;
END-EXEC;

WHENEVER Statement

The WHENEVER statement of ALTIBASE provides such as do/goto/continue/stop. This statement must be declared before executing the embedded SQL statement. The following form is possible in the condition statement of the WHENEVER statement.

Condition

Description

NOT FOUND

When sqlca.sqlcode is SQL_NO_DATA

 

SQLERROR

When sqlca.sqlcode is SQL_ERROR

 

The processing part is possible as follows.

Process

Description

CONTINUE

Ignore the error and continue

 

DO [사용자 함수]

Execute a user-defined function

 

DO BREAK

Exit the loop. It is only valid within a loop.

 

DO CONTINUE

Move to the first position in the loop. This statement is only valid within loops.

 

GOTO [Label]

Execute GOTO to the designated by the user

 

STOP

Disconnect and close the program

Examples are as follows.

Code Block
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 function and Checking current connection

In ALTIBASE, there isn't any way to actively return the status of the connection object, but the developer can detect that the connection has been terminated by checking the status of SQLSTATE after executing a query.

One of the possible methods is to put the following example source into a common function.

Example of Session Failover by User Code

Code Block
if ( memcmp (SQLSTATE, “08001”, 5) == 0 ||  memcmp (SQLSTATE, “08S01”, 5) == 0  || memcmp (SQLSTATE, “08003”, 5) == 0 )
{
    // Connection is disconnected. Execute FREE, not DISCONNECT call.
    EXEC SQL FREE;       
 
    EXEC SQL CONNECT ~~ // Opposite server
}

Starting from ALTIBASE Version 5.3.3 or later, the Session FailOver function of the concept called CTP (Connection Time Failover) and STF (Service Time FailOver) are provided. CTF means FailOver tries to connect when the program first start, and STF means FailOver occurs in the process of executing a query after the connection.

Since this function does not support transaction FailOver, the user must consider reprocessing the business logic after the Session FailOver. The example is as follows.

Example of using Altibase FailOver function

 

Code Block
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)
{
   // If this error appears, it means that FailOver has already been normally performed.!!
   if (SQLCODE == EMBEDED_ALTIBASE_FAILOVER_SUCCESS)
   {
       printf(“FailOver Success\n”);
       goto Retry_business_logic;
   }
}

 

The meaning of each input item of the connection string is as follows.

 

Description

AlternateServers

The target servers to FailOver are listed in the form of IP:PORT in order based on comma (,).

 

ConnectionRetryCount

The number of connection attempts to one target serer

ConnectionRetryDelay

After sleep for this interval (in seconds), retry the connection

 

SessionFailOver

Determine whether to use the SessionFailOver function during the service time. In other words, this specifies whether to automatically connect to the server in the AlternateServers list when an SQL error occurs. (on/off)

LoadBalance

When truend turned on, the server and AlternateServers list specified in the DSB are randomly selected at the time of initial connection. If turned off, the DSN is selected first, and then the connection is attempted in the order described in the AlternateServers list.

If an error occurs at the time of cursor opening and needs to be reprocessed, the cursor must be reprocessed after executing the cursor until close release before reprocessing. 

 

EXEC  SQL CLOSE RELEASE CURSOR1;

 

For more detailed information on CTF/STF, please refer to the "Altibase UL FailOver Guide".