Chapter 42 – SQL/CLI: dbc Functions¶
Note
You are reading a digital copy of SQL-99 Complete, Really, a book that documents the SQL-99 standard.
The book and the standard does not reflect the features of CrateDB, we are just publishing it as a service for the community and for reference purposes.
On the one hand, CrateDB does not implement the SQL-99 standard thoroughly, on the other hand, it extends the standard for implementing cluster features and others.
For more information specific to CrateDB, check out the CrateDB Reference documentation.
In this chapter, we’ll describe the second essential CLI resource: the dbc
.
For CLI programs, the dbc
is at the level below env
and above stmt
.
The env
may contain multiple dbc
s, and the dbc
may contain multiple
stmt
s (the dbc
may also contain multiple desc
s, but only of the
“user” kind). Here’s a closeup view of a dbc
:
... from env
| |
| |
v v
-------------------------------------
- [Attributes] | -
- | [Connection] -
- [Diagnostics] | -
- ... | -
-------------------------------------
| | | |
| | | |
v v v v
... to stmts ... to user descs
There are eight CLI functions for creating dbc
s, dropping dbc
s,
getting dbc
attributes, setting dbc
attributes, connecting and
disconnecting. Their descriptions follow.
Table of Contents
SQLAllocHandle(SQL_HANDLE_DBC,…)¶
Function Prototype:
SQLRETURN SQLAllocHandle (
SQLSMALLINT HandleType, /* 16-bit input = SQL_HANDLE_DBC */
SQLINTEGER InputHandle, /* 32-bit input, must be a henv */
SQLINTEGER *OutputHandle /* pointer to 32-bit output, a hdbc */
);
Job: Allocate a dbc
.
Algorithm:
if (HandleType) == SQL_HANDLE_DBC) {
The DBMS allocates a new dbc, and associates it with the env (the env's
handle is passed in InputHandle) and returns the handle of the new dbc into
the memory location addressed by OutputHandle. }
That is, If (HandleType == SQL_HANDLE_DBC)
, then the job is to allocate
a new dbc
.
Notes:
The second parameter,
InputHandle
, must be a validhenv
so this function call happens afterSQLAllocHandle(SQL_ALLOC_ENV,...)
.Keep the
hdbc
, you’ll need it later forSQLAllocHandle(SQL_HANDLE_STMT, ...)
, forSQLFreeHandle(SQL_HANDLE_DBC, ...)
and for other functions.
Example:
#include "sqlcli.h"
SQLHENV henv; /* handle of an env */
SQLHDBC hdbc; /* handle of a dbc */
SQLRETURN sqlreturn; /* code returned by SQL function */
...
sqlreturn = SQLAllocHandle(SQL_HANDLE_DBC,henv,&hdbc);
...
ODBC: The SQLAllocHandle
function is new in ODBC 3.0. Error
information will not be available until you call the “connect” function.
SQLAllocConnect¶
Function Prototype:
SQLRETURN SQLAllocConnect (
SQLHENV henv, /* 32-bit input, must be a henv */
SQLHDBC *hdbc /* pointer to 32-bit output, a hdbc */
);
Job: Make a new dbc
. SQLAllocConnect
is obsolete.
Algorithm:
sqlreturn = SQLAllocConnect(henv,&hdbc);
is the same as:
sqlreturn = SQLAllocHandle(SQL_HANDLE_DBC,henv,&hdbc);
Example:
#include "sqlcli.h"
SQLHENV henv;
SQLHDBC hdbc;
SQLRETURN sqlreturn;
...
if (SQLAllocEnv(&henv)>0) {
if (SQLAllocConnect(henv,&hdbc)>0) {
... a hdbc exists } }
Notes:
Although
SQLAllocConnect
is in the “obsolescent” category, it is still a standard function supported by all DBMSs.
ODBC: The SQLAllocConnect
routine has been in ODBC since version
1.0. The ODBC 3.0 manual deprecates it, suggesting that users should switch
to using SQLAllocHandle(SQL_HANDLE_DBC, ...)
.
SQLConnect¶
Function Prototype:
SQLRETURN SQLConnect(
SQLHDBC hdbc, /* 32-bit input */
SQLCHAR *ServerName, /* CHAR* input */
SQLSMALLINT NameLength1, /* 16-bit input (ServerName length)*/
SQLCHAR *UserName, /* CHAR* input */
SQLSMALLINT NameLength2, /* 16-bit input (UserName length) */
SQLCHAR *Authentication, /* CHAR* input */
SQLSMALLINT NameLength3 /* 16-bit input (Authentication length)*/
);
Job: Establish an SQL-Connection for a dbc
. The details of “how to
connect” depend largely on the implementation. We describe two broad cases: a
Single-Tier Scenario (everything on one computer) and a Two-Tier Scenario
(Client on one computer, Server on another computer). One or the other will be
fairly close to what your specific implementation does.
Single-Tier scenario
Step 1. The client (which is effectively the same thing as “the DBMS”) verifies that the parameters have valid data. Specifically, it must be true that: (a) The
dbc
exists. If it doesn’t, the return is CLI-specific condition: invalid handle. Read up on theSQLAllocHandle
function to see how to set up the handle. (b) There is no SQL transaction running on this Connection. (Actually some sophisticated systems allow this, but we assume the normal case.) If there is one, the return is0A001
feature not supported: multiple transactions. It’s okay if you have a transaction going on a different SQL-Connection – this just means you can’t connect twice using the samedbc
handle. Read up onSQLDisconnect
if you’re already connected. (c) TheServerName
parameter is valid. This should be a string, with a maximum length of 128 octets (as usual the length is passed along with the string, inNameLength1
, and may beSQL_NTS
). If it’s not valid, the return isHY090
invalid string length or buffer length. (d) TheUserName
is valid. The contents of this string will become the <AuthorizationID>, so the string should contain a valid identifier, such as:'USER_1'
or' USER_1 '
(lead and trail spaces don’t matter). (e) The authentication is valid. Usually a blank is acceptable: ‘’.Step 2. The DBMS “opens” the database named
ServerName
. This may seem like a misuse of the parameter, but the fact is, we don’t need to contact a server – but we do need to open a database. And it’s fairly common that there will be more than one database on a computer, so names are necessary.
Two-Tier scenario
Step 1. The client (the local task which your application is calling) verifies that the parameters have valid data. This step is local, the only likely difference is that the client will not bother to verify the “authentication” parameter, since that’s usually the server’s problem.
Step 2. If
ServerName = "DEFAULT"
:
If (User Name Length <> 0) invalid string or buffer length If (Authentication Length <> 0) invalid string or buffer length If (Somebody else already in as default) connection name in use Otherwise: (Compare the effect of a "CONNECT TO DEFAULT;" statement.)
Step 3. Using RDA, the client finds the server identified by the parameter
ServerName
, and sends a message to the server containing the parameter values (UserName
andAuthentication
). If the network’s down, or the server’s not out there, then the return is:08001 connection exception-SQL-client unable to establish SQL-session.
Step 4. The server does its own validation of
UserName
andAuthentication
. One possibility is that theAuthentication
is designed to be a password, and it doesn’t match what thatUserName
's password is supposed to be. In this case, the return is:08004 connection exception-SQL-server rejected establishment of SQL-session.
Notice the difference between this error and the one described in Step 3 –SQLSTATE is '08001'
if the client can’t talk to the server;SQLSTATE is '08004'
is if they can talk, but the server says no.Step 5. All having gone well, we now have a new SQL-session. If there was already an SQL-session in progress, it becomes dormant. The new SQL-session becomes the current SQL-session. The new SQL-session’s session <AuthorizationID> becomes
UserName
– that is, if theUserName
parameter is'X'
, and you use the niladic functionSESSION_USER
in an SQL statement, you’ll get'X'
.
Example:
/* EXAMPLE2.C */
/* This is a program example. Connection is to the default database for the
DBMS that came with this book. For variety, we test sqlreturn each time. Tear-
down calls are omitted. */
#include "sqlcli.h"
SQLHENV henv;
SQLHDBC hdbc;
SQLRETURN sqlreturn;
void main ()
{
sqlreturn = SQLAllocHandle(SQL_HANDLE_ENV,SQL_NULL_HANDLE,&henv);
if (sqlreturn == SQL_SUCCESS || sqlreturn == SQL_SUCCESS_WITH_INFO) {
sqlreturn = SQLAllocHandle(SQL_HANDLE_DBC,henv,&hdbc);
if (sqlreturn == SQL_SUCCESS || sqlreturn == SQL_SUCCESS_WITH_INFO) {
sqlreturn = SQLConnect(hdbc,
(SQLCHAR*)"OCELOT",SQL_NTS,
(SQLCHAR*)"OCELOT",SQL_NTS,
(SQLCHAR*)"",SQL_NTS);
if (sqlreturn == SQL_SUCCESS || sqlreturn == SQL_SUCCESS_WITH_INFO) {
printf("connected successfully.\n"); } } } }
ODBC: SQLConnect
has been a supported function since ODBC 1.0. But
there are other, non-standard, ODBC functions which can be used to connect. The
alternatives take advantage of the Windows environment (by putting up dialog
boxes etc.), and assume that Microsoft’s Driver Manager software will take care
of some details.
CONNECT versus SQLConnect:¶
There is an SQL statement which we’ve already discussed:
CONNECT TO <SQL-server-name> [AS <Connection name>]
USER <AuthorizationID>;
You are not supposed to execute this SQL statement using the CLI! The business
of connecting is to be handled exclusively through the SQLConnect
function.
So if you write a program which accepts user commands in the form of SQL
statements, you must intercept any that begin with "CONNECT ..."
and call
the SQLAllocConnect
and SQLConnect
functions for them. Unfortunately,
this is difficult because there is an imperfect mapping between the arguments
of the CONNECT
statement and the parameters of SQLConnect
.
Similar interceptions will be necessary for the three other SQL statements
which must not be executed directly using the CLI: DISCONNECT
, COMMIT
and ROLLBACK
. For each of these statements there is an approximate
CLI-function analogue: SQLDisconnect
, SQLEndTran(...SQL_COMMIT)
and
SQLEndTran(...SQL_ROLLBACK)
.
SQLDisconnect¶
Function Prototype:
SQLRETURN SQLDisconnect(
SQLHDBC hdbc /* 32-bit input */
);
Job: End an SQL session which was started by calling the SQLConnect
function. Analogous to the SQL DISCONNECT
statement.
Algorithm:
If (hdbc parameter is not a handle of a dbc)
return error: CLI-specific condition-invalid handle
Empty the diagnostics area associated with dbc.
If (there is no connection associated with dbc)
/* i.e. we didn't call SQLConnect or we already called SQLDisconnect */
return error: 08003 connection exception-connection does not exist
For (each stmt associated with the dbc)
If (there is a deferred parameter number)
return error: HY010 CLI-specific condition-function sequence error
If (a transaction is active)
/* Before disconnecting you must end the transaction,
try calling SQLEndTran */
return error: 25001 invalid transaction state-active SQL-transaction
For (each stmt associated with the dbc)
Free the stmt's descs (ARD, APD, IRD, IPD)
Free the stmt
Free any descs which are directly associated with the dbc
If (Client/Server)
Tell the server that this connection is over.
If (server won't reply / server won't let go)
/* This is only a warning, by now the disconnect is unstoppable */
there will be a warning: 01002 warning-disconnect error
If (the connection we just disconnected was the current connection)
There is now no current connection
Notes:
A connected
dbc
takes up space, and in a multi-user scenario there might be conflicts with other SQL-sessions using the same server. You should always callSQLDisconnect
to end an SQL-session, although some single-tier DBMSs don’t require it. After you callSQLDisconnect
, you can either re-connect (seeSQLConnect
) or finish the tear-down process by freeing thedbc
(seeSQLFreeHandle(SQL_HANDLE_DBC...)
).There is a side effect: a previously-dormant SQL-Connection might become current. That can only happen if the DBMS allows double-connections on the same
dbc
.
Example:
/* All function calls except SQLDisconnect are in skeletal form. */
#include "sqlcli.h"
SQLHENV henv;
SQLHDBC hdbc;
...
SQLAllocHandle(...); /* SQLAllocHandle call for env */
SQLAllocHandle(...); /* SQLAllocHandle call for dbc */
SQLConnect(hdbc,...); /* connect: see previous example */
/* we could now call SQLAllocHandle(SQL_HANDLE_STMT,...);
and then perform various functions related to the stmt */
SQLDisconnect(hdbc);
SQLFreeHandle(hdbc,...); /* SQLFreeHandle call for dbc */
SQLFreeHandle(...); } /* SQLFreeHandle call for env */
ODBC: The SQLDisconnect
function has been around since ODBC 1.0.
SQLDisconnect
causes automatic dropping of all statements and
descriptors open on the connection.
SQLGetConnectAttr¶
Function Prototype:
SQLRETURN SQLGetConnectAttr(
SQLHDBC hdbc, /* 32-bit input */
SQLINTEGER Attribute, /* 32-bit input */
SQLPOINTER Value, /* pointer to 32-bit output */
SQLINTEGER BufferLength, /* 32-bit input */
SQLINTEGER *StringLength /* pointer to 32-bit output */
);
Job: Get the value of a dbc
attribute. The standard implementation of
the SQLGetConnectAttr
function doesn’t do anything important, but there
might be non-standard, implementation-defined attributes that you can retrieve
using SQLGetConnectAttr
. The standard connection attribute has this
#define
in sqlcli.h
:
#define SQL_ATTR_AUTO_IPD 10001
It is, of course, an integer and may not be set by SQLSetConnectAttr
.
SQL_ATTR_AUTO_IPD
stands for SQL Attribute: Automatically Populate IPD.
This is a flag integer with a value of either TRUE (1)
or FALSE (0)
.
SQL_ATTR_AUTO_IPD
is the only standard attribute for a connection. If
SQL_ATTR_AUTO_IPD
is TRUE
, the DBMS “populates” the IPD (implementation
parameter descriptor) whenever you prepare an SQL statement. That means that
there will be, automatically, one parameter descriptor for every parameter
marker (symbolized by “?”) inside your SQL statement. For example, if you
execute this SQL statement:
INSERT INTO Table_1 VALUES (?);
there will be an automatic IPD. IPD contents are the subject of a later chapter.
Algorithm:
If (hdbc is not a hdbc)
return error: CLI-specific condition-invalid handle
Empty the dbc's diagnostics area.
If (Attribute <> SQL_ATTR_AUTO_IPD)
return error: HY092 CLI-specific condition-invalid attribute identifier
If (Attribute == SQL_ATTR_AUTO_IPD)
If (SQLConnect not done)
return error: 08003 connection exception-connection does not exist
Set *Value = value of dbc's SQL_ATTR_AUTO_IPD attribute field (0 or 1).
Notes:
There might be several implementation-defined attributes for connections. The Standard allows for that. That’s why
BufferLength
and*Stringlength
– which aren’t needed forSQL_ATTR_AUTO_IPD
– are defined parameters. They’re there in case someday it’s necessary to return a character string value.Some things which we think of as “connection attributes” are not retrieved with
SQLGetConnectAttr
. They are:The default time zone offset – get it by extracting the <time zone interval> from SQL’s
CURRENT_TIME
function.The default Catalog – get it by selecting from the
INFORMATION_SCHEMA_CATALOG_NAME
View, or by usingSQLGetInfo
withSQL_CATALOG_NAME
.The default Schema – get it by using
SQLGetDiagField
after any erroneous statement.The default Character set – get it by using
SQLGetDiagField
after any erroneous statement.The default Collation – get it by using
SQLGetInfo
withSQL_COLLATING_SEQUENCE
.The <Connection name> – get it by using
SQLGetDiagField
after any erroneous statement.The <SQL-server name> – get it by using
SQLGetInfo
withSQL_DATA_SOURCE_NAME
orSQLGetInfo
withSQL_SERVER_NAME
.The SQL-session user – get it from SQL’s
SESSION_USER
function, or by usingSQLGetInfo
withSQL_USER_NAME
.
In the final version of the SQL/CLI there will be two more attributes —
SQL_ATTR_SAVEPOINT_NAME
andSQL_ATTR_SAVEPOINT_NUMBER
.
Example:
#include "sqlcli.h"
SQLHDBC hdbc;
SQLINTEGER popid;
...
if (SQLGetConnectAttr(hdbc,SQL_ATTR_AUTO_IPD,&popid,NULL,NULL) < 0) {
printf("Error.\n");
else {
if (popid==1) printf("It's true.\n");
if (popid==0) printf("It's false (which means DBMS isn't full SQL\n"); }
/* Going on from here: if popid is true, we can make SQL statements with
parameters (?s). Then we can assign buffers/variables based on the IPD. Or we
can make sure our currently-assigned parameters are okay. If pop is false: we
can still use parameters, but we have to fill in IPD values "manually". */
ODBC: The SQLGetConnectAttr
function is new to ODBC 3.0, but a very
similar function (SQLGetConnectOption
) existed in ODBC 2.0. ODBC allows for
16 possible Attributes. One is SQL_ATTR_AUTO_IPD
. Most of the others are
related to ODBC’s optional features (timeout, trace file, network packet size,
etc.).
SQLSetConnectAttr¶
Function Prototype:
SQLRETURN SQLSetConnectAttr(
SQLHDBC hdbc, /* 32-bit input -- SQL-connection handle */
SQLINTEGER Attribute, /* 32-bit input */
SQLPOINTER Value, /* pointer to *ANY input */
SQLINTEGER StringLength /* 32-bit input */
);
Job: Set the value of a dbc
attribute.
Algorithm:
If (hdbc is not really a handle of a dbc)
return error: CLI-specific condition-invalid handle
Empty dbc's diagnostics area.
If (Attribute <> SQL_ATTR_AUTO_IPD)
return error: HY092 CLI-specific condition-invalid attribute identifier
If (Attribute == SQL_ATTR_AUTO_IPD)
/* the SQL_ATTR_AUTO_IPD attribute may not be set */
return error: HY092 CLI-specific condition-invalid attribute identifier
Notes:
This function is useless unless there are implementation-defined
dbc
attributes.In the final version of the SQL/CLI there will be two more attributes —
SQL_ATTR_SAVEPOINT_NAME
andSQL_ATTR_SAVEPOINT_NUMBER
.Value might be a pointer; that’s why we’ve used
SQLPOINTER
in the prototype. But it’s usually an integer. C programmers, when passing an integer value here, will use casts such as(PTR)
or(SQLPOINTER)
or(void*)
.
Example:
#include "sqlcli.h"
SQLHDBC hdbc;
...
sqlreturn = SQLSetConnectAttr(hdbc,SQL_ATTR_AUTO_IPD,(void*)5,NULL);
if (sqlreturn == SQL_SUCCESS || sqlreturn == SQL_SUCCESS_WITH_INFO) {
/* function call succeeded -- which it shouldn't */
else
/* function call failed, as expected */
ODBC: The SQLSetConnectAttr
function is new in ODBC 3.0, but ODBC
2.0 had a broadly similar function (SQLSetConnectOption
). ODBC allows
for 16 dbc
attributes, of various types.
SQLFreeHandle(SQL_HANDLE_DBC,…)¶
Function Prototype:
SQLRETURN SQLFreeHandle( /* function returns SMALLINT */
SQLSMALLINT HandleType, /* 16-bit input, = SQL_HANDLE_DBC */
SQLINTEGER Handle /* 32-bit input, must be a hdbc */
);
Job: Destroy a dbc
.
Algorithm:
If (HandleType == SQL_HANDLE_DBC)
If (Handle is not really a handle of a dbc)
return error: CLI-specific condition-invalid handle
Empty the dbc's diagnostics area.
If (dbc is still connected)
/* you must call SQLDisconnect before you can destroy a dbc */
return error: HY010 CLI-specific condition-function sequence error
Deallocate the connection and anything associated with it.
The handle becomes invalid.
Notes:
If
SQLFreeHandle
returnsSQL_ERROR
, then the handle is still live and you can get diagnostics.The name
SQLFreeHandle
is unfortunate. We are not “freeing a handle”. We are destroying the resource that the handle refers to. In embedded SQL contexts, the preferred word for this process is “deallocate”.Before you call
SQLFreeHandle(SQL_HANDLE_DBC...)
, you must callSQLDisconnect
. Therefore, by this time, there are nostmt
s ordesc
s associated with thedbc
.
Example: Typically, an SQL application ends with a flurry of freeings:
SQLDisconnect(hdbc); /* ends the SQL-session */
SQLFreeHandle(SQL_HANDLE_DBC,hdbc);
SQLFreeHandle(SQL_HANDLE_ENV,henv); /* ends the application */
ODBC: The SQLFreeHandle
function is new in ODBC 3.0. There will be some
differences in behaviour if you use ODBC-specific features, such as tracing
or environment sharing.
SQLFreeConnect¶
Function Prototype:
SQLRETURN SQLFreeConnect(
SQLHDBC hdbc /* 32-bit input */
);
Job: Destroy a dbc
. This is the reverse of the SQLAllocConnect
function. SQLFreeConnect
is redundant.
Algorithm:
sqlreturn = SQLFreeConnect(hdbc);
is the same thing as
sqlreturn = SQLFreeHandle(SQL_HANDLE_DBC,hdbc);
Notes:
The Standard does not say that the
SQLFreeConnect
function is deprecated. Nevertheless,SQLFreeHandle(SQL_HANDLE_DBC,...)
is more modern.
Example:
#include "sqlcli.h"
SQLHENV henv;
SQLHDBC hdbc;
...
SQLAllocConnect(henv,&hdbc);
...
SQLFreeConnect(hdbc);
/* hdbc is now an invalid handle */
...
ODBC: The SQLFreeConnect
function has been in ODBC since version
1.0. The ODBC 3.0 manual deprecates it, suggesting that users should switch
to using SQLFreeHandle(SQL_HANDLE_DBC...)
.
And that’s it for the dbc
functions. In the next chapter, we’ll take a
look at the stmt
functions.