4 ODBC Examples
4.1 Example Introduction
In this and the following chapter two examples of the usage of the
ODBC
interface will be introduced.The examples contain some basic operations, creating tables, writing and reading data, and dropping tables. They do the same things using the two different interface parts: the Utility API and the Basic API. The SQL used in the example is supported by Oracle8 and Intersolv's DataDirect ODBC Oracle8 Driver, but it may not work with other DBMSs/Drivers. The strings used to connect to the database depend on how your ODBC Driver is configured.
4.1.1 Utility API
A database has been created in the RDBMS at hand. We will now create a new table, insert data into it, select the data, and finally delete the table. The ODBC application has been started on a named node with some cookie.
-module(utility). -author('joke@smeagol'). -export([start/0]). % This string depends on how your ODBC Driver is configured. % You need to fill in your own value. %-define(ConnectStr, "DSN=Oracle8;UID=myself;PWD=secret"). %% Note that the SQL syntax is database and ODBC Driver dependent. %% start() -> % Start a new ODBC server. The application must already be started. {ok, _Pid} = odbc:start_link({local, odbc1}, [], []), % Initialise the environment (also loads the Driver Manager). {ok, EnvHandle} = odbc:init_env(odbc1, infinity), % Load the Driver and connect to the database. {ok, ConnectionHandle} = odbc:connect(odbc1, EnvHandle, ?ConnectStr, infinity), % Create a new table. % By default, all transactions are automatically committed. CreateStmt = "CREATE TABLE TAB1 (ID number(3), DATA char(10))", {updated, NAffectedRows1} = odbc:execute_stmt(odbc1, ConnectionHandle, CreateStmt, infinity), ok = io:format("Create: Number of affected rows: ~p~n", [NAffectedRows1]), % Insert a row. InsertStmt = "INSERT INTO TAB1 VALUES (1, 'a1a2a3a4a5')", {updated, NAffectedRows2} = odbc:execute_stmt(odbc1, ConnectionHandle, InsertStmt, infinity), ok = io:format("Insert: Number of affected rows: ~p~n", [NAffectedRows2]), % Select all rows. SelectStmt = "SELECT * FROM TAB1", {selected, ColumnNames, Rows} = odbc:execute_stmt(odbc1, ConnectionHandle, SelectStmt, infinity), ok = io:format("Select: Column names: ~p, Rows: ~p~n", [ColumnNames, Rows]), % Delete the table. DropStmt = "DROP TABLE TAB1", {updated, NAffectedRows3} = odbc:execute_stmt(odbc1, ConnectionHandle, DropStmt, infinity), ok = io:format("Delete: Number of affected rows: ~p~n", [NAffectedRows3]), % Disconnect. ok = odbc:disconnect(odbc1, ConnectionHandle, infinity), % Terminate the environment. ok = odbc:terminate_env(odbc1, EnvHandle, infinity), % Stop the server. ok = odbc:stop(odbc1, infinity).4.1.2 Basic API
A database has been created in the RDBMS at hand. We will now create a new table, insert data into it, select the data, and finally delete the table. The ODBC application has been started on a named node with some cookie. In this example we will not use the autocommit mode, meaning that we have to commit changes explicitly. The example does not include any error handling.
-module(basic). -author('joke@smeagol'). -export([start/0]). % Contains macros defined by the ODBC standard. -include("/clearcase/otp/libraries/odbc/include/odbc.hrl"). % These strings depend on how your ODBC Driver is configured. % You need to fill in your own values. %-define(DSN, "Oracle8"). %-define(UID, "myself"). %-define(PWD, "secret"). % The maximum length for column names + 1. % The 1 is there to allow room for a null-termination character. -define(BufLenColName, 64). %% The maximum length of table data + 1. -define(MaxDataBufLen, 1024). %% Note that the SQL syntax is database and ODBC Driver dependent. %% Error handling is not covered by the example. %% start() -> % Start a new ODBC server. The application must already be started. {ok, _Pid} = odbc:start_link({local, odbc1}, [], []), % Allocate an environment handle (also loads the Driver Manager). {?SQL_SUCCESS, EnvHandle} = odbc:sql_alloc_handle(odbc1, ?SQL_HANDLE_ENV, ?SQL_NULL_HANDLE, infinity), % Set the ODBC version attribute to tell the Driver we're a 3.0 application. ?SQL_SUCCESS = odbc:sql_set_env_attr(odbc1, EnvHandle, ?SQL_ATTR_ODBC_VERSION, ?SQL_OV_ODBC3, ?SQL_C_ULONG, infinity), % Allocate a connection handle. {?SQL_SUCCESS, ConnectionHandle} = odbc:sql_alloc_handle(odbc1, ?SQL_HANDLE_DBC, EnvHandle, infinity), % Connect to the database (also loads the Driver). ?SQL_SUCCESS = odbc:sql_connect(odbc1, ConnectionHandle, ?DSN, ?UID, ?PWD, infinity), % Turn the autocommit mode off (if you don't want it). ?SQL_SUCCESS = odbc:sql_set_connect_attr(odbc1, ConnectionHandle, ?SQL_ATTR_AUTOCOMMIT, ?SQL_AUTOCOMMIT_OFF, ?SQL_C_ULONG, infinity), % Allocate a statement handle. {?SQL_SUCCESS, StmtHandle} = odbc:sql_alloc_handle(odbc1, ?SQL_HANDLE_STMT, ConnectionHandle, infinity), % Create a new table. CreateStmt = "CREATE TABLE TAB1 (ID number(3), DATA char(10))", ?SQL_SUCCESS = odbc:sql_exec_direct(odbc1, StmtHandle, CreateStmt, infinity), % Print how many rows were affected by the statement. {?SQL_SUCCESS, NAffectedRows1} = odbc:sql_row_count(odbc1, StmtHandle, infinity), ok = io:format("Create: Number of affected rows: ~p~n", [NAffectedRows1]), % Commit the transaction. ?SQL_SUCCESS = odbc:sql_end_tran(odbc1, ?SQL_HANDLE_DBC, ConnectionHandle, ?SQL_COMMIT, infinity), % Insert a new row. InsertStmt = "INSERT INTO TAB1 VALUES (1, 'a1a2a3a4a5')", ?SQL_SUCCESS = odbc:sql_exec_direct(odbc1, StmtHandle, InsertStmt, infinity), % Print how many rows were affected by the statement. {?SQL_SUCCESS, NAffectedRows2} = odbc:sql_row_count(odbc1, StmtHandle, infinity), ok = io:format("Insert: Number of affected rows: ~p~n", [NAffectedRows2]), % Commit the transaction. ?SQL_SUCCESS = odbc:sql_end_tran(odbc1, ?SQL_HANDLE_DBC, ConnectionHandle, ?SQL_COMMIT, infinity), % Select the DATA column from all rows. SelectStmt = "SELECT DATA FROM TAB1", ?SQL_SUCCESS = odbc:sql_exec_direct(odbc1, StmtHandle, SelectStmt, infinity), % Print how many columns there are in the table resulting from the % statement. {?SQL_SUCCESS, NSelectedCols} = odbc:sql_num_result_cols(odbc1, StmtHandle, infinity), ok = io:format("Select: Number of columns: ~p~n", [NSelectedCols]), % Describe the column(s) of the resulting table. {?SQL_SUCCESS, {ColName, _LenColName}, SqlType, ColSize, _DecDigits, _Nullable} = odbc:sql_describe_col(odbc1, StmtHandle, 1, ?BufLenColName, infinity), % Calculate the size of the buffer(s) we're going to use to retrieve data. % Make sure you protect yourself from trying to allocate huge amounts of % memory. DispSize = odbc:display_size(SqlType, ColSize), BufSz = if ColSize > ?MaxDataBufLen -> ?MaxDataBufLen; true -> DispSize end, % Allocate data buffer(s). {ok, Buf} = odbc:alloc_buffer(odbc1, ?SQL_C_CHAR, BufSz, infinity), % Bind the buffer(s) to the column. ?SQL_SUCCESS = odbc:sql_bind_col(odbc1, StmtHandle, 1, Buf, infinity), % Fetch the first row into the bound buffer(s) (only one buffer bound here). ?SQL_SUCCESS = odbc:sql_fetch(odbc1, StmtHandle, infinity), % Read the value from the buffer(s). {ok, {ColValue, _LenColValue}} = odbc:read_buffer(odbc1, Buf, infinity), io:format("Select: Column name: ~p, Data: ~p~n", [ColName, ColValue]), % Check that there are no more rows to fetch. ?SQL_NO_DATA = odbc:sql_fetch(odbc1, StmtHandle, infinity), % Close the cursor on the statement. ?SQL_SUCCESS = odbc:sql_close_cursor(odbc1, StmtHandle, infinity), % Deallocate the buffer(s). ok = odbc:dealloc_buffer(odbc1, Buf, infinity), % Delete the table. DropStmt = "DROP TABLE TAB1", ?SQL_SUCCESS = odbc:sql_exec_direct(odbc1, StmtHandle, DropStmt, infinity), % Print how many rows were affected by the statement. {?SQL_SUCCESS, NAffectedRows3} = odbc:sql_row_count(odbc1, StmtHandle, infinity), ok = io:format("Delete: Number of affected rows: ~p~n", [NAffectedRows3]), % Commit the transaction. ?SQL_SUCCESS = odbc:sql_end_tran(odbc1, ?SQL_HANDLE_DBC, ConnectionHandle, ?SQL_COMMIT, infinity), % Free the statement handle. ?SQL_SUCCESS = odbc:sql_free_handle(odbc1, ?SQL_HANDLE_STMT, StmtHandle, infinity), % Disconnect from the database. ?SQL_SUCCESS = odbc:sql_disconnect(odbc1, ConnectionHandle, infinity), % Free the connection handle. ?SQL_SUCCESS = odbc:sql_free_handle(odbc1, ?SQL_HANDLE_DBC, ConnectionHandle, infinity), % Free the environment handle. ?SQL_SUCCESS = odbc:sql_free_handle(odbc1, ?SQL_HANDLE_ENV, EnvHandle, infinity), % Stop the server. ok = odbc:stop(odbc1).