Chapter 43 – SQL/CLI: stmt Functions¶
In this chapter, we’ll describe the third essential CLI resource: the stmt.
For CLI programs, the stmt is at the level below dbc and (usually)
above desc. The dbc may contain multiple stmts, and the stmt
may contain four descs. Here’s a closeup view of a stmt:
... from dbc
| |
| |
v v
-------------------------------------
- [Attributes] -
- -
- [Diagnostics] -
- -
- [Cursor] [Result Set] [Statement] -
-------------------------------------
| | | |
| | | |
v v v v
... to IPD ... to IRD ... to APD ... to ARD
There are six CLI functions for creating stmts, dropping stmts,
getting stmt attributes and setting stmt attributes. Their
descriptions follow.
Table of Contents
SQLAllocHandle(SQL_HANDLE_STMT,…)¶
Function Prototype:
SQLRETURN SQLAllocHandle (
SQLSMALLINT HandleType, /* 16-bit input = SQL_HANDLE_STMT */
SQLINTEGER InputHandle, /* 32-bit input, must be a hdbc */
SQLINTEGER *OutputHandle /* pointer to 32-bit output, a hstmt */
);
Job: Allocate a stmt.
Algorithm:
If (HandleType == SQL_HANDLE_STMT) {
If InputHandle is not a valid hdbc:
return error: CLI-specific condition-invalid handle
The dbc's diagnostics area is emptied.
If hdbc is not "connected":
Set *OutputHandle = 0
return error: 08003 connection exception-connection does not exist
If maximum number of stmts has already been made:
Set *OutputHandle = 0
return error: CLI-specific condition-limit on number of handles exceeded
If (dbc's connection is not the current connection)
Change current connection = dbc's connection
If (memory-allocation failure while trying to allocate the new stmt)
Set *OutputHandle = 0
return error: CLI-specific condition-memory allocation error
Make four descs: an IPD, an IRD, an APD, an ARD.
(The four descs have some pre-set values, described in the next chapter.)
Set *OutputHandle = handle of new stmt. }
Notes:
- You cannot allocate a
stmtuntil you have allocated adbcand connected (withSQLConnect). - The handle of the new
stmtis “unique”, which means that there is no otherstmtin theenvwith the same value. This uniqueness would not be guaranteed if there were twoenvs, but that’s usually impossible anyway. - The Standard calls it the “handle of the allocated SQL-statement”,
but in all our examples we name it a
hstmt, which is the general fashion. - Keep the
hstmt. You’ll need it later forSQLFreeHandle(SQL_HANDLE_STMT...)and for quite a few other things. In fact, the majority of CLI functions (42 of the 62) take ahstmtas input.
Example:
#include "sqlcli.h"
...
SQLHDBC hdbc;
SQLHSTMT hstmt;
...
sqlreturn = SQLAllocHandle(SQL_HANDLE_STMT,hdbc,&hstmt);
ODBC: The SQLAllocHandle function is new in ODBC 3.0.
SQLAllocStmt¶
Function Prototype:
SQLRETURN SQLAllocStmt(
SQLHDBC hdbc, /* 32-bit input, must be a hdbc */
SQLHSTMT *hstmt /* pointer to 32-bit output, a hstmt */
);
Job: Make a new stmt.
Algorithm:
SQLRETURN = SQLAllocStmt(hdbc,&hstmt);
is the same as
SQLRETURN = SQLAllocHandle(SQL_HANDLE_STMT,hdbc,&hstmt);
Notes:
- The Standard does not deprecate
SQLAllocStmt. All DBMSs should continue to support it. It is, however, redundant.
Example:
#include "sqlcli.h"
...
SQLHDBC hdbc;
SQLHSTMT hstmt;
...
SQLAllocStmt(hdbc,&hstmt);
...
ODBC: The SQLAllocStmt function 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_STMT...).
SQLGetStmtAttr¶
Function Prototype:
SQLRETURN SQLGetStmtAttr(
SQLHSTMT hstmt, /* 32-bit input -- statement handle */
SQLINTEGER Attribute, /* 32-bit input */
SQLPOINTER Value, /* pointer to ANY* output */
SQLINTEGER BufferLength, /* 32-bit input */
SQLINTEGER *StringLength); /* pointer to 32-bit output */
Job: Retrieve one attribute of a stmt.
Algorithm:
If (Attribute == SQL_ATTR_APP_ROW_DESC)
Set *Value = (SQLHDESC) stmt's ARD handle.
If (Attribute == SQL_ATTR_APP_PARAM_DESC)
Set *Value = (SQLHDESC) stmt's APD handle.
If (Attribute == SQL_ATTR_IMP_ROW_DESC)
Set *Value = (SQLHDESC) stmt's IRD handle.
If (Attribute == SQL_ATTR_IMP_PARAM_DESC)
Set *Value = (SQLHDESC) stmt's IPD handle.
If (Attribute == SQL_ATTR_CURSOR_SCROLLABLE)
If (DBMS supports scrollable Cursors)
Set *Value = "Cursor scrollable" attribute (0 or 1) (false or true)
Else
return error: HYC00 CLI-specific condition-optional feature not implemented
If (Attribute == SQL_ATTR_CURSOR_SENSITIVITY)
If (implementation supports sensitive Cursors)
Set *Value = "Cursor sensitivity" attribute (0 or 1 or 2)
Else
return error: HYC00 CLI-specific condition-optional feature not implemented
If (Attribute == SQL_ATTR_METADATA_ID)
Set *Value = "metadata ID" attribute (0 or 1)
If (Attribute == SQL_ATTR_CURSOR_HOLDABLE)
If (implementation supports holdable Cursors)
Set *Value = "Cursor holdable" attribute (0 or 1)
Else
return error: HYC00 CLI-specific condition-optional feature not implemented
If (Attribute == none of the above)
return error: HY092 CLI-specific condition-invalid attribute identifier
Notes:
- Here is a list of the standard
stmtattributes:
| VALUE | #DEFINE IN SQLCLI.H |
SETTABLE? | DATA TYPE | REMARKS |
|---|---|---|---|---|
| -3 | SQL_ATTR_CURSOR_HOLDABLE |
yes | SQLINTEGER |
ANSI SQL3 only
0=nonholdable
1=holdable
|
| -2 | SQL_ATTR_CURSOR_SENSITIVITY |
yes | SQLINTEGER |
0=asensitive
1=insensitive
2=sensitive
|
| -1 | SQL_ATTR_CURSOR_SCROLLABLE |
yes | SQLINTEGER |
ANSI SQL3 only
0=nonscrollable
1=scrollable
|
| 10010 | SQL_ATTR_APP_ROW_DESC |
yes | SQLHDESC |
ARD |
| 10011 | SQL_ATTR_APP_PARAM_DESC |
yes | SQLHDESC |
APD |
| 10012 | SQL_ATTR_IMP_ROW_DESC |
no | SQLHDESC |
IRD |
| 10013 | SQL_ATTR_IMP_PARAM_DESC |
no | SQLHDESC |
IPD |
| 10014 | SQL_ATTR_METADATA_ID |
yes | SQLINTEGER |
SQL3 only
0=false
1=true
|
Other stmt attributes may be defined in particular implementations.
(Reminder: SQLHDESC stands for “handle of a desc”, a 32-bit value.)
When you allocate a
stmt(withSQLAllocHandleorSQLAllocStmt), the DBMS automatically sets up fourdescs: the ARD, the APD, the IRD and the IPD. You can pick up the handles of thesedescs by callingSQLGetStmtAttrwithSQL_ATTR_APP_ROW_DESC,SQL_ATTR_APP_PARAM_DESC,SQL_ATTR_IMP_ROW_DESCandSQL_ATTR_IMP_PARAM_DESC, respectively – you’ll need these because there are several CLI functions which use adeschandle as input.When you get a result set (by calling
SQLExecDirectorSQLExecutefor a query expression such as"SELECT ..."), the DBMS implicitly declares a Cursor. What kind of Cursor? That depends on the “cursor” attributes:SQL_ATTR_CURSOR_SCROLLABLEif
0= “false” =SQL_NONSCROLLABLE: you can only fetchNEXT.
if1= “true” =SQL_SCROLLABLE: you can fetchFIRST,NEXT,PRIOR,LAST,ABSOLUTEand RELATIVE``.
The default is0, but you can callSQLSetStmtAttrto change that.SQL_ATTR_CURSOR_SENSITIVITYif
0 = SQL_ASENSITIVE = SQL_UNSPECIFIED: sensitivity isn’t known.
if1 = SQL_INSENSITIVE: changes made elsewhere won’t be seen here.
if2 = SQL_SENSITIVE: changes made elsewhere will be seen here.
The default is0, but you can callSQLSetStmtAttrto change that.SQL_ATTR_CURSOR_HOLDABLEif
0= “false” =SQL_NONHOLDABLE: Cursor disappears at transaction end.
if1= “true” =SQL_HOLDABLE: Cursor remains at transaction end.
The default is probably 0 (you’d have to callSQLGetInfoto be certain) but you can callSQLSetStmtAttrto change that.
When you use a Catalog function (such as
SQLColumnsorSQLTables), the DBMS has to search the “metadata”, that is, theINFORMATION_SCHEMAViews. TheSQL_ATTR_METADATA_IDattribute indicates how searches are conducted with Catalog functions. We discuss this in more detail in our chapter on SQL/CLI Catalog functions.The
BufferLengthandStringLengthparameters aren’t currently in use. They’re there in case we ever have a statement attribute that contains a string value.
Example:
#include "sqlcli.h"
HSTMT hstmt;
HDESC hipd;
...
SQLGetStmtAttr(
hstmt, /* handle of stmt */
SQL_ATTR_IMP_PARAM_DESC, /* we want the IPD handle */
&hipd, /* put the handle here */
NULL, /* "don't care" */
NULL); /* "don't care" */
ODBC: The SQLGetStmtAttr function is new in ODBC 3.0 (in ODBC 2.0
there was a nearly-equivalent function, SQLGetStmtOption).
SQLSetStmtAttr¶
Function Prototype:
SQLRETURN SQLSetStmtAttr(
SQLHSTMT hstmt, /* 32-bit input - statement handle */
SQLINTEGER Attribute, /* 32-bit input */
SQLPOINTER Value, /* pointer to ANY* input */
SQLINTEGER StringLength /* 32-bit input */
);
Job: Set an attribute of the stmt – to change the DBMS’s behaviour
for Cursor control, to change the handle used for an application desc,
or to change the search method for Catalog functions.
There are eight standard stmt attributes, listed in the previous section.
Algorithm:
If (Attribute is not one of the settable attributes)
return error: HY092 CLI-specific condition-invalid attribute identifier
If (Attribute == SQL_ATTR_APD_HANDLE)
If (*Value is not the handle of a desc)
return error: HY024 CLI-specific condition-invalid attribute value
If (desc's SQL_ALLOC_TYPE field == 'AUTOMATIC')
If (desc is not stmt's default/automatic APD)
return error: HY017 CLI-specific condition-invalid use of automatically-allocated descriptor handle.
Desc becomes associated with this stmt, as its APD, non-exclusively.
If (Attribute == SQL_ATTR_ARD_HANDLE)
If (*Value is not the handle of a desc)
return error: HY024 CLI-specific condition-invalid attribute value
If (desc's ALLOC_TYPE field == 'AUTOMATIC')
If (desc is not stmt's default/automatic ARD)
return error: HY017 CLI-specific condition-invalid use of automatically-allocated descriptor handle.
Desc becomes associated with this stmt, as its ARD, non-exclusively.
If (Attribute == SQL_ATTR_CURSOR_SCROLLABLE)
If (the DBMS does not support scrollable Cursors)
return error: HYC00 CLI-specific condition-optional feature not implemented
If (stmt already has an open Cursor)
/* You can't change how a Cursor works while the Cursor is open */
return error: HY011 CLI-specific condition-attribute cannot be set now
If (*Value is not SQL_SCROLLABLE (1) or SQL_NONSCROLLABLE (0))
return error: HY011 CLI-specific condition-attribute cannot be set now
Stmt's Cursor's "scrollable" attribute = *Value.
If (Attribute == SQL_ATTR_CURSOR_SENSITIVITY)
If (the DBMS does not support sensitive Cursors)
return error: HYC00 CLI-specific condition-optional feature not implemented
If (there is already an open Cursor for stmt)
return error: HY011 CLI-specific condition-attribute cannot be set now
If (*Value is not SQL_SENSITIVE (2) or SQL_INSENSITIVE (1) or UNSPECIFIED (0))
return error: HY024 CLI-specific condition-invalid attribute value
Stmt's Cursor's "sensitivity" attribute = *Value.
If (Attribute == SQL_ATTR_METADATA_ID)
If (*Value not TRUE or FALSE)
return error: HY024 CLI-specific condition-invalid attribute value
Set stmt's "metadata id" attribute = *Value.
If (Attribute == SQL_ATTR_CURSOR_HOLDABLE)
If (the DBMS does not support holdable Cursors)
return error: HYC00 CLI-specific condition-optional feature not implemented
If (stmt already has an open Cursor)
return error: HY011 CLI-specific condition-attribute cannot be set now
If (*Value is not SQL_HOLDABLE (1) or SQL_NONHOLDABLE (0))
return error: HY024 CLI-specific condition-invalid attribute value
Set stmt's Cursor's "holdable" attribute = Value.
Notes:
- The specification allows for implementation-defined attributes and for the
possibility of future change. That’s why the
StringLengthparameter exists, although it’s not currently used. SQL_ATTR_APP_ROW_DESCandSQL_ATTR_APP_PARAM_DESCare listed as settable attributes, so you can change the stmt’s ARD and APD. (You can’t change thestmt’s IRD and IPD.) TheValueparameter must point to the handle of a userdesc. This option makes it possible to save or share applicationdescs.SQL_ATTR_CURSOR_SCROLLABLEandSQL_ATTR_CURSOR_HOLDABLEare used to specify whether astmt’s Cursor is “scrollable” and/or “holdable”.SQL_ATTR_METADATA_IDis an abbreviation for “the attribute for metadata identifiers”, for example the names of Tables inINFORMATION_SCHEMA.- It’s easy to forget that the Value parameter is described as
SQLPOINTER. Don’t make the mistake of passing"hdesc"(handle of adesc); pass"&hdesc"(address of a handle of adesc) instead.
Example: Change the settings of all settable stmt attributes. This
example only shows what the syntax looks like for the various options. More
realistic examples will appear in the chapters on “Cursors”, descs, and
“Catalog functions”.
#include "sqlcli.h"
SQLHSTMT hstmt;
SQLHDESC hdesc1,hdesc2;
SQLINTEGER scrollable = SQL_SCROLLABLE;
SQLINTEGER sensitivity = SQL_INSENSITIVE;
SQLINTEGER metadata_id = SQL_FALSE;
SQLINTEGER holdable = SQL_NONHOLDABLE;
...
SQLSetStmtAttr(hstmt,SQL_ATTR_APP_ROW_DESC,&hdesc1,NULL);
SQLSetStmtAttr(hstmt,SQL_ATTR_APP_PARAM_DESC,&hdesc2,NULL);
SQLSetStmtAttr(hstmt,SQL_ATTR_CURSOR_SCROLLABLE,&scrollable,NULL);
SQLSetStmtAttr(hstmt,SQL_ATTR_CURSOR_SENSITIVITY,&sensitivity,NULL);
SQLSetStmtAttr(hstmt,SQL_ATTR_METADATA_ID,&metadata_id,NULL);
SQLSetStmtAttr(hstmt,SQL_ATTR_CURSOR_HOLDABLE,&holdable,NULL);
ODBC: The SQLSetStmtAttr function is new in ODBC 3.0 but there was
a similar function in ODBC 2.0 (SQLSetStmtOption).
SQLFreeHandle(SQL_HANDLE_STMT,…)¶
Function Prototype:
SQLRETURN SQLFreeHandle( /* function returns SMALLINT */
SQLSMALLINT HandleType, /* 16-bit input, = SQL_HANDLE_STMT */
SQLINTEGER Handle /* 32-bit input, must be a hstmt */
);
Job: Destroy a stmt.
Algorithm:
If (HandleType == SQL_HANDLE_STMT)
If (Handle is not a hstmt)
return error: CLI-specific condition-invalid handle
Empty the stmt's diagnostics area.
The stmt's dbc becomes the "current dbc".
If (there is a deferred parameter associated with stmt)
return error: HY010 CLI-specific condition-function sequence error
If (there is an open Cursor associated with the stmt)
Throw away all information about the Cursor's result set
Close the Cursor
Deallocate the stmt's four automatic descs: ARD, APD, IRD, IPD.
Deallocate stmt.
Notes:
- Correctly speaking, this functions “frees the
stmt”; it doesn’t merely free thestmt’s handle. - The DBMS calls
SQLFreeHandle(SQL_HANDLE_STMT...)implicitly, for allstmts in adbc, as part of anSQLDisconnectoperation. - There is a mention here of a “current”
dbc. If there are multipledbcs, the DBMS will implicitly switch, when necessary, to thedbcwhose handle is passed in the function call (or, as in this case, to thedbcassociated with thestmtwhose handle is passed in the function call). This switching is transparent. There is no need, when using the CLI, for any analogue of the basic SQL"SET CONNECTION ..."statement. - Freeing a statement causes an implicit “close Cursor” operation, but not an implicit “end transaction” operation.
- Quite often, programmers allocate a
stmtonly once, and re-use the samestmtthroughout the SQL-session. SoSQLFreeHandle(SQL_HANDLE_STMT...)may be seen only infrequently.
Example:
#include "sqlcli.h"
SQLHDBC hdbc;
SQLHSTMT hstmt;
...
SQLAllocHandle(SQL_HANDLE_STMT,hdbc,&hstmt);
...
SQLFreeHandle(SQL_HANDLE_STMT,hstmt);
ODBC: The SQLFreeHandle function is new in ODBC 3.0.
SQLFreeStmt¶
Function Prototype:
SQLRETURN SQLFreeStmt(
SQLHSTMT hstmt, /* 32-bit input */
SQLSMALLINT Option; /* 16-bit input */
);
Job: SQLFreeStmt has five different jobs, depending on the value of
Option:
- If
Optionis 0,SQLFreeStmt’s job is to close a Cursor. - If
Optionis 1,SQLFreeStmt’s job is to destroy astmt. - If
Optionis 2,SQLFreeStmt’s job is to unbind Columns. - If
Optionis 3,SQLFreeStmt’s job is to unbind parameters. - If
Optionis 4,SQLFreeStmt’s job is to reallocate.
Algorithm:
If (Option == SQL_CLOSE (0))
If (there is a Cursor)
Cancel all information about the result set.
Close the Cursor.
If (Option == SQL_DROP (1))
/* SQLFreeStmt(...,SQL_DROP) is the reverse of SQLAllocStmt(...).
sqlreturn = SQLFreeStmt(hstmt,SQL_DROP);
is the same as:
sqlreturn = SQLFreeHandle(SQL_HANDLE_STMT,hstmt);
If (Option == SQL_UNBIND (2))
/* This affects the application row descriptor (ARD). In
effect, it cancels out any SQLBindCol calls made on stmt. */
For (i=1; i<=ARD.SQL_DESC_COUNT;++i)
Set ARD.IDA[i].SQL_DESC_DATA_POINTER = 0
If (Option == SQL_RESET_PARAMS (3))
/* This affects the application parameter descriptor (APD). In
effect, it cancels out any SQLBindParameter calls made on stmt. */
For (i=1; i<=APD.SQL_DESC_COUNT;++i)
Set APD.IDA[i].SQL_DESC_DATA_POINTER = 0
If (Option == SQL_REALLOCATE (4))
Destroy the stmt's statement and Cursor parts
If (Option == none of the above)
return error: HY092 CLI-specific condition-invalid attribute identifier
Notes:
Regrettably, we are forced to mention a few details before their time for full discussion is come. For the immediate purpose, we hope it is enough to say that:
RESULT SETSare temporary Tables which result from the execution of an SQL query.Cursorsare named interface objects through which result sets can be retrieved one row at a time.BINDINGSare associations between a host program’s host variables (i.e.: pointers to data buffers and indicator variables) and their corresponding SQL Objects (Columns and/or parameter markers). Often Columns are bound with theSQLBindColfunction, while parameters are bound with theSQLBindParameterfunction.
Because
SQLFreeHandleis now the main function for freeing resources, the nameSQLFreeStmtis now a misnomer – we don’t useSqlFreeStmtfor freeingstmts nowadays. However, it maintains a residue of usefulness. It’s good for “partialstmtfreeing”, freeing things associated withstmts that might be taking up space, or whose continued existence might cause conflict with other operations.SQLFreeStmt(...,SQL_CLOSE)does exactly the same thing as theSQLCloseCursorfunction, except for one detail: if there is no Cursor currently open, thenSQLCloseCursorreturns an error, whileSQLFreeStmt(...,SQL_CLOSE)does not return an error.SQLFreeStmt(hstmt,SQL_DROP)is now just another way of saying:SQLFreeHandle(SQL_HANDLE_STMT,hstmt).SQLFreeStmt(...,SQL_UNBIND)andSQLFreeStmt(...,SQL_RESET_PARAMS)are handy ways of “disassociating” the application’s RAM from the DBMS, so that there won’t be inadvertent access to memory that’s no longer valid. Here’s an example:x=malloc(1024); /* malloc an area */ SQLBindCol(...,x,...); /* Now there is a binding to the malloc'd area */ free(x); /* uh oh. x is no longer a valid pointer */ SQLFreeStmt(...,UNBIND ColumnS); /* okay, chances of GPF are reduced */
However, only the “data pointer” field is cleared – not the “indicator pointer” field (this is probably an inadvertent omission from the Standard).
SQLFreeStmt(...,SQL_REALLOCATE)can be used to clear parts ofstmts:- The “statement” (a copy of the SQL statement string which was last
prepared or executed for this
stmt, including “select” source statements). - The “Cursor”, including the Cursor’s result set. These things take up
space, so presumably
SQLFreeStmt(...,SQL_REALLOCATE)is useful under low-memory conditions. But normally it’s unnecessary: “statement” and “Cursor” are superseded in any case when an SQL statement is re-prepared/re-executed.
- The “statement” (a copy of the SQL statement string which was last
prepared or executed for this
Example: Since SQLFreeStmt(...,SQL_DROP) is the reverse of
SQLAllocStmt(...), we show both these obsolescent calls together.
#include "sqlcli.h"
SQLHDBC hdbc;
SQLHSTMT hstmt;
...
SQLAllocStmt(hdbc,&hstmt); /* allocate a stmt */
SQLBindCol(hstmt,...); /* bind a stmt Column */
SQLFreeStmt(hstmt,SQL_UNBIND); /* unbind all stmt Columns */ ...
SQLFreeStmt(hstmt,SQL_DROP); /* free a stmt */
ODBC: The SQLFreeStmt function has been in ODBC since version 1.0. The
ODBC 3.0 manual deprecates the use of SQLFreeStmt(...,SQL_DROP), suggesting
that the user should switch to using SQLFreeHandle(SQL_HANDLE_STMT,...).
The ODBC action for SQL_UNBIND is different: ARD.SQL_DESC_COUNT is set
to 0. The ODBC action for SQL_RESET_PARAMS is different:
APD.SQL_DESC_COUNT is set to 0. The SQL_REALLOCATE option is not
supported.
And that’s it for the stmt functions. In the next chapter, we’ll take a
look at the statement functions.