...
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 ~~ |
Connection close
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
...
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.9 | ERR-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 later | ERR-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
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; } |
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".