Microsoft Corporation
This section contains a list of all of the open database connectivity (ODBC) application programming interface (API) calls used by Microsoft® Access®. Included with each API is a list of parameters that are passed to that API.
Unless otherwise noted, functions may be called in any valid manner, and parameters may be any value, including NULL, 0, -1, and so on.
SQLAllocConnect
SQLAllocEnv
SQLAllocStmt
SQLCancel
SQLColumns
TableQualifier | always NULL |
TableOwner | always specified |
TableName | always specified |
ColumnName | always NULL |
Result set columns needed:
2 | (owner name) |
3 | (table name) |
4 | (column name) |
5 | (data type) |
7 | (precision) |
9 | (scale) |
11 | (nullable) |
SQLDescribeCol
SQLDisconnect
SQLDriverConnect
szConnStrIn | may be complete, partial, or empty |
szConnStrOut | not always specified |
cbConnStrOutMax | not always specified |
pcbConnStrOut | always NULL |
fDriverCompletion
SQL_DRIVER_NOPROMPT
SQL_DRIVER_COMPLETE
SQL_DRIVER_COMPLETE_REQUIRED
SQLError
hstmt | sometimes 0 |
szSqlState | always specified |
pfNativeError | always specified |
szErrorMsg | always specified |
cbErrorMsgMax | always specified |
pcbErrorMsg | always specified |
SQLExecDirect
SQLExecute
SQLFetch
SQLFreeConnect
SQLFreeEnv
SQLFreeStmt
fOption
SQL_CLOSE
SQL_DROP
SQLGetData
fCType | For catalog functions, always SQL_C_DEFAULT. Otherwise, fCType can be any value, including SQL_C_DEFAULT. If "Driver not capable" is returned, SQL_C_CHAR is used instead. |
SQLGetInfo
fInfoType
SQL_ACTIVE_STATEMENTS
SQL_ODBC_API_CONFORMANCE (must be >= 1)
SQL_DBMS_NAME
SQL_DBMS_VER
SQL_STRING_FUNCTIONS
SQL_NUMERIC_FUNCTIONS
SQL_TIMEDATE_FUNCTIONS
SQL_SYSTEM_FUNCTIONS
SQL_OUTER_JOINS
SQL_EXPRESSIONS_IN_ORDERBY
SQL_CONCAT_NULL_BEHAVIOR
SQL_DATA_SOURCE_READ_ONLY
SQL_TXN_CAPABLE
SQL_CURSOR_COMMIT_BEHAVIOR
SQL_CURSOR_ROLLBACK_BEHAVIOR
SQL_IDENTIFIER_QUOTE_CHAR (expecting 1 char)
SQLGetTypeInfo
fSqlType
SQL_CHAR
SQL_VARCHAR
SQL_LONGVARCHAR
SQL_ALL_TYPES
Result set columns needed:
1 | (type name) |
2 | (data type) |
3 | (precision) |
6 | (create params) |
9 | (searchable) |
SQLNumResultCols
SQLParamData
SQLPrepare
SQLPutData
SQLRowCount
SQLSetConnectOption
fOption
SQL_AUTOCOMMIT
SQL_OPT_TRACEFILE
SQL_OPT_TRACE
SQL_LOGIN_TIMEOUT
SQLSetParam
fCType | any value, including SQL_C_DEFAULT |
fSqlType | any value (generally whatever SQLColumns returned) NOTE: fCType-to-fSqlType will be "reasonable". |
cbColDef | max bytes in column (precision), or length of long value (if cbValue == SQL_DATA_AT_EXEC), or SQL_NULL_DATA |
ibScale | Generally whatever SQLColumns returned |
rgbValue | pointer to data buffer, or pointer to client's private data structure (for SQLParamData) |
cbValue | length of data buffer, or SQL_NULL_DATA, or SQL_DATA_AT_EXEC (cbColDef == total length of long value) |
SQLSetStmtOption
fOption
SQL_QUERY_TIMEOUT (need not succeed)
SQL_MAX_LENGTH
SQL_ASYNC_ENABLE (need not succeed)
SQLSpecialColumns
fColType | always SQL_ROWVER |
TableQualifier | always NULL |
TableOwner | always specified |
TableName | always specified |
fScope | always SQL_SCOPE_TRANSACTION |
fNullable | always SQL_NULLABLE |
Result set columns needed:
2 | (column name) |
SQLStatistics
TableQualifier | always NULL |
TableOwner | always specified |
TableName | always specified |
fUnique | SQL_INDEX_ALL |
fAccuracy | SQL_ENSURE |
Result set columns needed:
2 | (owner name) |
3 | (table name) |
4 | (non unique) |
5 | (index qualifier) |
6 | (index name) |
9 | (column name) |
10 | (collation) |
11 | (cardinality) |
12 | (pages) |
SQLTables
TableQualifier | always NULL |
TableOwner | always NULL |
TableName | not always specified |
TableType | always NULL |
Result set columns needed:
2 | (table owner) |
3 | (table name) |
4 | (table type) |
SQLTransact
fType
SQL_COMMIT
SQL_ROLLBACK
This section describes the elements of the ODBC SQL Grammar that are used by Microsoft Access. The ODBC SQL Grammar is described in full in Appendix C of the ODBC Programmer's Reference.
CREATE TABLE base-table-name (name type, ...)
DELETE FROM ... WHERE search-condition
DROP TABLE
INSERT INTO ... (...) VALUES (...)
SELECT [DISTINCT] select-list
FROM ...
WHERE ...
GROUP BY ...
HAVING ...
ORDER BY ...
UPDATE ... SET ... WHERE search-condition
That is, except for GROUP BY and HAVING, all statements are Minimum.
Minimum
all elements used
Core (NOT USED)
sub-query
cursor-name
exists-predicate
index-name
pattern-value ::= USER
quantified-predicate
query-specification
referenced-columns
referencing-columns
table-name.*
viewed-table-name
Extended (NOT USED)
binary-literal
date-literal
date-type
ODBC-std-esc-initiator
ODBC-std-esc-terminator
procedure
time-literal
time-type
Note data-types and ODBC-esc-extensions (outer joins, and so on) are used only to the extent that the driver claims to support them. Exception: ODBC-date-time-extension is assumed to be supported.
This section describes the mapping of datatypes between Microsoft Access and ODBC.
When creating an attachment, Access calls SQLColumns(szRemoteTableName) to enumerate ODBC column information for each column in the remote table.
For each column, it returns:
fSqlType | ODBC data type |
lPrecision | ODBC precision of column |
wScale | ODBC scale of column |
Documentation for ODBC types and ODBC's idea of "precision" and "scale" is in Appendix D of the ODBC Programmer's Reference.
Access maps [fSqlType,lPrecision,wScale] to an Access datatype. This is the datatype stored in the attached table definition, and it is what the user sees. The ODBC type information is saved, per column, and fed back into ODBC whenever Access "uses" the column (SELECTing, UPDATEing, INSERTing the column, and making parameters from queries by it).
The type mapping is done as follows:
ODBC Datatype | Access Datatype |
SQL_BIT | Yes/No |
SQL_TINYINT SQL_SMALLINT |
Number -- Size: Integer |
SQL_INTEGER | Number -- Size: Long Integer |
SQL_REAL | Number -- Size: Single |
SQL_FLOAT SQL_DOUBLE |
Number -- Size: Double |
SQL_TIMESTAMP SQL_DATE SQL_TIME |
DateTime |
SQL_CHAR SQL_VARCHAR |
if lPrecision <= 255, then Text (Field Size = lPrecision) if lPrecision > 255, then Memo |
SQL_BINARY SQL_VARBINARY |
if lPrecision <= 255, then Binary (Field Size = lPrecision) if lPrecision > 255, then OLE Object |
SQL_LONGVARBINARY | OLE Object |
SQL_LONGVARCHAR | Memo |
SQL_DECIMAL SQL_NUMERIC |
if SQL Server, then if lPrecision = 19, and wScale = 4, then Currency if lPrecision = 10, and wScale = 4, then Currency if wScale = 0, then if lPrecision <= 4, then Number -- Size: Integer if lPrecision <= 9, then Number -- Size: Long Integer if lPrecision <= 15, then Number -- Size: Double if wScale > 0, then if lPrecision <= 15, then Number -- Size: Double |
Anything not covered above is mapped to Text(Field Size==255).
When executing a "SELECT INTO" query with an ODBC destination (this includes File Export in Access), Access maps each source column type to a destination column type. A CREATE TABLE statement and multiple INSERT statements are sent to the server using these destination types. Access calls SQLGetTypeInfo to get ODBC type information for all datatypes supported by the back-end. A collection of internal data structures is built, describing the type information in an Access-digestible format. The type mapping is done as follows.
(In the mapping below, replace SQL_SMALLINT with SQL_NUMERIC(5,0) if SQL_SMALLINT is not supported by the server. Replace SQL_INTEGER with SQL_NUMERIC(10,0) if SQL_INTEGER is not supported. Replace SQL_VARCHAR with SQL_CHAR if SQL_VARCHAR is not supported by the server. If SQL_CHAR is also not supported, the query fails.)
Access Datatype | ODBC Datatype |
Yes/No | SQL_BIT, if supported, else SQL_SMALLINT, if supported, else SQL_INTEGER, if supported, else SQL_VARCHAR(5) |
Number --Size: Byte Number --Size: Integer |
SQL_SMALLINT, if supported, else SQL_INTEGER, if supported, else SQL_VARCHAR(10) |
Number --Size: Long Integer | SQL_INTEGER, if supported, else SQL_VARCHAR(20) |
Currency | SQL_DECIMAL(19,4), if SQL Server, else SQL_FLOAT, if supported, else SQL_VARCHAR(30) |
Number --Size: Single | SQL_REAL, if supported, else SQL_FLOAT, if supported, else SQL_VARCHAR(30) |
Number --Size: Double | SQL_FLOAT, if supported, else SQL_VARCHAR(40) |
DateTime | SQL_TIMESTAMP, if supported, else SQL_VARCHAR(40) |
Text(Field Size) | SQL_VARCHAR(MIN(Field Size,ServerMax)) |
Binary(Field Size) | SQL_VARBINARY(MIN(Field Size,ServerMax)), if supported, else query fails |
Memo | SQL_LONGVARCHAR(ServerMax), if supported, else SQL_VARCHAR(ServerMax), if ServerMax >= 2000, else query fails |
OLE Object | SQL_LONGVARBINARY(ServerMax), if supported, else SQL_VARBINARY(ServerMax), if ServerMax >= 2000, else query fails |
This section contains a table of all of the valid entries that can appear in the Microsoft Access Initialization File, MSACCESS.INI.
Section | Entry | Value | Effect |
Debug | RmtTrace | 0 | Use asynchronous query execution if possible; no ODBC API tracing (default) |
8 | Trace ODBC API calls into "odbcapi.txt" in Access directory | ||
16 | Force synchronous query execution | ||
24 | Trace ODBC API calls; force asynchronous query execution | ||
ODBC | TraceSQLMode | 0 | No tracing (default) |
1 | Trace SQL sent to ODBC into "sqlout.txt" in Access directory | ||
ODBC | QueryTimeout | S | S sec, works async (default:60) |
ODBC | LoginTimeout | S | S sec (default:20) |
ODBC | ConnectionTimeout | S | S sec, kill idle connections (default:600) |
ODBC | AsyncRetryInterval | M | M millisecs, between async retries (default:500) |
ODBC | AttachCaseSensitive | 0 | Attach to first name matching specified string |
1 | Attach only to name exactly matching specified string | ||
ODBC | AttachableObjects | string | List of object types to allow attaching (default:'TABLE','VIEW','SYSTEM TABLE', 'ALIAS','SYNONYM') |
ODBC | SnapshotOnly | 0 | Get index information on attach, to allow Dynasets (default) |
1 | Ignore index information on attach, forces snapshots |
This section contains a table of errors that will be returned when Access determines that a driver has failed to comply with the ODBC specification. The table contains an error number that will be returned by Access. Following the error number are two pieces of information:
Error | ODBC Call | Condition causing the error |
-7701 | SQLGetInfo(ODBC_API_CONFORMANCE) | *pcbInfoValue != 2 |
-7702 | SQLGetInfo(ODBC_API_CONFORMANCE) | wValue < 1 |
-7703 | SQLGetData(fCType=SQL_C_CHAR) | Call return "driver could not convert" |
-7704 | SQLGetTypeInfo(SQL_ALL_TYPES) | neither SQL_CHAR nor SQL_VARCHAR was returned; type support is insufficient |
-7705 | SQLGetTypeInfo ==> SQLNumResultCols | *pccol < 6 |
-7706 | SQLGetTypeInfo ==> SQLGetData(TYPE_NAME) | *pcbValue <= 0 |
-7707 | SQLGetTypeInfo ==> SQLGetData(DATA_TYPE) | *pcbValue != 2 |
-7708 | SQLGetTypeInfo ==> SQLGetData(PRECISION) | *pcbValue != 0 or *pcbValue != 4 |
-7709 | odbc.dll missing API function (possibly bad odbc.dll) | |
-7710 | SQLSetParam(fSQLType=SQL_VARCHAR) | driver could not convert |
-7711 | SQLError | An error was returned by an ODBC call, but no error string was returned |
-7712 | primary key > 255 bytes | |
-7713 | SQL_INVALID_HANDLE returned by ODBC API; i.e., driver claims henv/hdbc/hstmt is invalid | |
-7714 | SQLGetTypeInfo ==> SQLNumResultCols | *pccol < 9 |
-7715 | SQLTables ==> SQLGetData(TABLE_OWNER/TABLE_NAME) | length(ownername.tablename) > 255 bytes |
-7716 | SQLTables ==> SQLGetData(TABLE_NAME) | *pcbValue <= 0 |
-7717 | SQLTables ==> SQLGetData(TABLE_TYPE) | *pcbValue <= 0 |
-7718 | SQLTables ==> SQLGetData(TABLE_TYPE) | *pcbValue > 128 |
-7719 | SQLStatistics ==> SQLGetData(COLUMN_NAME) | total length of columns for index > 255 bytes |
-7720 | SQLGetInfo(SQL_CURSOR_COMMIT_BEHAVIOR) | *pcbInfoValue != 2 |
-7721 | SQLGetInfo(SQL_CURSOR_ROLLBACK_ BEHAVIOR) |
*pcbInfoValue != 2 |
-7722 | SQLTables ==> SQLNumResultCols | *pccol < 4 |
-7723 | SQLSpecialColumns ==> SQLNumResultCols | *pccol < 2 |
-7724 | SQLSpecialColumns ==> SQLGetData(COLUMN_NAME) | *pcbValue <= 0 |
-7725 | SQLGetTypeInfo ==> SQLGetData(SEARCHABLE) | *pcbValue != 2 |
-7726 | SQLGetTypeInfo ==> SQLGetData(SEARCHABLE) | value out of range |
-7727 | SQLColumns ==> SQLNumResultCols | *pccol < 11 |
-7728 | SQLColumns ==> SQLGetData(TABLE_OWNER) | *pcbValue < 0 |
-7729 | SQLColumns ==> SQLGetData(TABLE_NAME) | *pcbValue <= 0 |
-7730 | SQLColumns ==> SQLGetData(COLUMN_NAME) | *pcbValue <= 0 |
-7731 | SQLColumns ==> SQLGetData(DATA_TYPE) | *pcbValue != 2 |
-7732 | SQLColumns ==> SQLGetData(PRECISION) | *pcbValue != 0 or 4 |
-7733 | SQLColumns ==> SQLGetData(SCALE) | *pcbValue != 0 or 2 |
-7734 | SQLColumns ==> SQLGetData(NULLABLE) | *pcbValue != 0 or 2 |
-7735 | SQLColumns ==> SQLGetData(NULLABLE) | value out of range |
-7736 | SQLStatistics ==> SQLNumResultCols | *pccol < 12 |
-7737 | SQLStatistics ==> SQLGetData(TABLE_OWNER) | *pcbValue < 0 |
-7738 | SQLStatistics ==> SQLGetData(TABLE_NAME) | *pcbValue <= 0 |
-7739 | SQLStatistics ==> SQLGetData(NON_UNIQUE) | *pcbValue != 2 |
-7740 | SQLStatistics ==> SQLGetData(INDEX_QUALIFIER) | *pcbValue < 0 |
-7741 | SQLStatistics ==> SQLGetData(INDEX_QUALIFIER/INDEX_NAME) | length(qualifier.indexname) > 255 bytes |
-7742 | SQLStatistics ==> SQLGetData(INDEX_NAME) | *pcbValue < 0 |
-7743 | SQLStatistics ==> SQLGetData(TYPE) | *pcbValue != 2 |
-7744 | SQLStatistics ==> SQLGetData(TYPE) | value out of range |
-7745 | SQLStatistics ==> SQLGetData(TYPE/NON_UNIQUE/INDEX_NAME) | TYPE == SQL_TABLE_STAT, but either NON_UNIQUE or INDEX_NAME is non-NULL |
-7746 | SQLStatistics ==> SQLGetData(TYPE/NON_UNIQUE/INDEX_NAME) | TYPE != SQL_TABLE_STAT, but either NON_UNIQUE or INDEX_NAME is NULL |
-7747 | SQLStatistics ==> SQLGetData(COLUMN_NAME) | *pcbValue <= 0 |
-7748 | SQLStatistics ==> SQLGetData(COLLATION) | *pcbValue != 0 or 1 |
-7749 | SQLStatistics ==> SQLGetData(COLLATION) | value not 'A' or 'D' |
-7750 | SQLGetInfo(SQL_TXN_CAPABLE) | *pcbInfoValue != 2 |
-7751 | SQLGetInfo(SQL_TXN_CAPABLE) | value < 0 or > 2 |
-7752 | SQLGetInfo(SQL_DATA_SOURCE_READ_ONLY) | *pcbInfoValue != 1 |
-7753 | SQLGetInfo(SQL_DATA_SOURCE_READ_ONLY) | value not 'Y' or 'N' |
-7754 | SQLGetInfo(SQL_IDENTIFIER_QUOTE_CHAR) | *pcbInfoValue != 1 |
-7755 | SQLGetInfo(SQL_IDENTIFIER_QUOTE_CHAR) | value '.' or alphanum |
-7756 | SQLGetInfo(SQL_STRING_FUNCTIONS) | *pcbInfoValue != 4 |
-7757 | SQLGetInfo(SQL_NUMERIC_FUNCTIONS) | *pcbInfoValue != 4 |
-7758 | SQLGetInfo(SQL_TIMEDATE_FUNCTIONS) | *pcbInfoValue != 4 |
-7759 | SQLGetInfo(SQL_SYSTEM_FUNCTIONS) | *pcbInfoValue != 4 |
-7760 | SQLGetInfo(SQL_OUTER_JOINS) | *pcbInfoValue != 1 |
-7761 | SQLGetInfo(SQL_OUTER_JOINS) | value not 'Y' or 'N' |
-7762 | SQLGetInfo(SQL_EXPRESSIONS_IN_ORDERBY) | *pcbInfoValue != 1 |
-7763 | SQLGetInfo(SQL_EXPRESSIONS_IN_ORDERBY) | value not 'Y' or 'N' |
-7764 | SQLGetInfo(SQL_CONCAT_NULL_BEHAVIOR) | *pcbInfoValue != 2 |
-7765 | SQLGetInfo(SQL_CONCAT_NULL_BEHAVIOR) | value not 0 or 1 |
-7766 | SQLGetData(SQL_C_BIT) | pcbValue != 1 |
-7767 | SQLGetData(SQL_C_SHORT) | pcbValue != 2 |
-7768 | SQLGetData(SQL_C_TIMESTAMP) | pcbValue != sizeof(TIMESTAMP_STRUCT) |
This section describes general caveats to keep in mind when using ODBC drivers with Microsoft Access. It includes assumptions that Access makes regarding driver behavior as well as giving some specific implementation details of Access functionality.
Access calls SQLGetInfo for:
SQL_ACTIVE_STATEMENTS
SQL_TXN_CAPABLE
SQL_CURSOR_COMMIT_BEHAVIOR
SQL_CURSOR_ROLLBACK_BEHAVIOR
If ACTIVE_STATEMENTS = = 1, then Access will spawn a new real connection whenever it needs to execute a query that will have pending results, that is, that will not immediately be SQLFetched to the end. If ACTIVE_STATEMENTS != 1, then Access will share existing connections, even for queries that have pending results. The positions of these cursors is assumed to be stable, even across transactions.
If TXN_CAPABLE returns >= 1, then Access will simulate wrapping a transaction around each INSERT/UPDATE/DELETE statement.
If TXN_CAPABLE returns 0, then Access will stay in AUTO_COMMIT mode permanently.
Let CURSOR_BEHAVIOR be the minimum of CURSOR_COMMIT_BEHAVIOR and CURSOR_ROLLBACK_BEHAVIOR. If CURSOR_BEHAVIOR < 1, then Access will pretend that the data source is READ_ONLY, and that transactions are not supported. If CURSOR_BEHAVIOR = = 1, and ACTIVE_STATEMENTS != 1, then Access will pretend that ACTIVE_STATEMENTS = = 1, in order to force a new connection for each pending-results-query. See comments on ACTIVE_STATMENTS.
Access calls SQLGetInfo for:
SQL_IDENTIFIER_QUOTE_CHAR
If this is a blank, then identifier quoting is assumed unsupported. If it is not a blank, then this char is used to prefix and suffix every owner, table, and column reference in every query that Access sends to the driver.
When attaching an ODBC table, Access calls SQLStatistics to collect index information. The first unique index returned is marked as the "primary" index. Exception: Indexes on floating-point columns are not marked "primary." The determination of a column being a floating-point column is made after the ODBC type has been mapped to an Access type (see typemap.doc). Unless a "primary" index is found, the attached table will not be updatable.
Access calls SQLTypeInfo to determine the "searchability" of SQL_CHAR, SQL_VARCHAR, and SQL_LONGVARCHAR. Unless the SEARCHABLE column of the result set is SQL_LIKE_ONLY or SQL_SEARCHABLE, Access will perform LIKE operations on such columns locally.
Access calls SQLGetInfo for:
SQL_STRING_FUNCTIONS
SQL_NUMERIC_FUNCTIONS
SQL_TIMEDATE_FUNCTIONS
SQL_SYSTEM_FUNCTIONS
SQL_OUTER_JOINS
SQL_EXPRESSIONS_IN_ORDERBY
SQL_CONCAT_NULL_BEHAVIOR
The values returned are used to determine how much of a query Access may safely send to the driver for execution, and how much must be performed locally by the Access engine.
Access calls SQLGetData to retrieve result sets. The fCType parameter value is determined as follows: SQLColumns returns the ODBC type of each column in a table. SQLDescribeCol is also sometimes used. These are mapped to Access data types (see typemap.doc). These Access types are then mapped to fCType values.
Access Type | ODBC fCType | Type in Table Design |
Bit | SQL_C_BIT | Yes/No |
UByte | SQL_C_SHORT | Number, Field Size = Byte |
Short | SQL_C_SHORT | Number, Field Size = Integer |
Long | SQL_C_LONG | Number, Field Size = Long Integer |
Currency | SQL_C_CHAR | Currency |
Single | SQL_C_FLOAT | Number, Field Size = Single |
Double | SQL_C_DOUBLE | Number, Field Size = Double |
DateTime | SQL_C_TIMESTAMP | Date/Time |
Binary | SQL_C_BINARY | <none> |
Text | SQL_C_CHAR | Text |
LongBin | SQL_C_BINARY | OLE Object |
LongText | SQL_C_CHAR | Memo |
Generally, these mappings will not result in "unreasonable" requests for driver conversions. But if SQLGetData returns the "driver cannot convert" error (sqlstate = = "S1C00"), then Access will use fCType = = SQL_C_CHAR for that column, for the remainder of the result set. If the driver cannot convert the data to SQL_C_CHAR, Access returns an error. Conversion of any type to char is not unreasonable.
A self-join query on a table with a column name containing ',' (comma) will fail, with a "syntax error". Also, column names containing '=' (equals) will cause updates/deletes to fail ("syntax error"), if NULL values are involved. If the primary key has a column name containing '=' (equals), then NULL values in this column will cause browsing to fail ("syntax error"). These behaviors are Access bugs, but may not be fixed for Access 1.1 because of their rarity, and the difficulty of handling these cases.
Adding TraceSQLMode=1 in the [ODBC] section of the MSACCESS.INI file will cause a log file, "sqlout.txt", to be written in the Access directory. This file is always appended to, never overwritten. The lines beginning with "SQLExecDirect" and "SQLPrepare" indicate the SQL that Access is passing to the driver. The "SQLExecute" lines trace calls to SQLExecute on a previously prepared hstmt, and print only the general type of statement executed (for example, "UPDATE", "DELETE"). This log file often contains as much useful information as ODBC API tracing, but with much less chaff, especially in ASYNC mode.
Access holds onto one or two connections, even when idle, so that you can quickly open and close datasheets/forms without constantly reconnecting. Two idle connections are held only if SQL_ACTIVE_STATEMENTS = = 1. These idle connections are closed after N seconds of idle time, controlled by the ConnectionTimeout value in the [ODBC] section of the MSACCESS.INI file (600 seconds, by default). All connections are closed when Access exits.
Access implemented connection-sharing based on two things: SQL_ACTIVE_STATEMENTS (see above comment) and matching connect strings. Access will consider two connect strings to match if:
When starting a connection, Access executes the query
SELECT nValue FROM MSysConf WHERE Config = 101
against the data source. Any errors during execution/fetching of this query are ignored. This table is not required to exist in the data source, but allows for Access-specific configuration options.
SQL_SUCCESS_WITH_INFO with sqlstate = = "S1000" is treated as an error. This prevents attaching SQL Server tables on a server whose ODBC Stored Procedures are out-of-date.
Access calls SQLError immediately after any ODBC API that returns an error code other than:
SQL_SUCCESS
SQL_INVALID_HANDLE
SQL_NO_DATA_FOUND
SQL_STILL_EXECUTING
SQL_NEED_DATA
SQLError must return at least one sqlstate and error string. Otherwise, the "error" is assumed to be a harmless warning, and is ignored.
Sending "parameterized" queries to the ODBC driver
Access queries may contain parameters. If declared explicitly, and given a type, Access will try to send expressions containing these parameters to the driver for evaluation. Parameters are explicitly declared by adding them to the Parameter Dialog under the Query menu in Query Design. SQLSetParam is called for each query parameter sent, with values as follows:
Access Type | ODBC fCType | fSqlType | cbColDef | ibScale |
Yes/No | SQL_C_BIT | SQL_BIT | 0 | 0 |
Byte | SQL_C_SHORT | SQL_SMALLINT | 0 | 0 |
Integer | SQL_C_SHORT | SQL_SMALLINT | 0 | 0 |
LongInteger | SQL_C_LONG | SQL_INTEGER | 0 | 0 |
Currency | SQL_C_CHAR | SQL_DECIMAL | 19 | 4 |
Single | SQL_C_FLOAT | SQL_FLOAT | 0 | 0 |
Double | SQL_C_DOUBLE | SQL_FLOAT | 0 | 0 |
Date/Time | SQL_C_TIMESTAMP | SQL_TIMESTAMP | 19 | 0 |
Binary | SQL_C_BINARY | SQL_VARBINARY | 255 | 0 |
Text | SQL_C_CHAR | SQL_VARCHAR | 255 | 0 |
OLE Object | SQL_C_BINARY | SQL_LONGVARBINARY | 0 | 0 |
Memo | SQL_C_CHAR | SQL_LONGVARCHAR | 0 | 0 |
Generally, these mappings will not result in "unreasonable" requests for driver conversions. But if SQLSetParam returns the "driver cannot convert" error (sqlstate = = "S1C00"), then Access will try again with fSqlType = = SQL_VARCHAR, cbColDef = = 255, and ibScale = = 0 (with fCType unchanged). If the driver still cannot convert, Access returns an error.
Access calls SQLSetStmtOption with SQL_QUERY_TIMEOUT and SQL_ASYNC_ENABLE, and ignores any errors that result.
When updating/inserting/exporting ODBC data, Access always specifies SQL_DATA_AT_EXEC for all non-NULL columns of type SQL_LONGVARCHAR and SQL_LONGVARBINARY. In all other cases, SQL_DATA_AT_EXEC is not used.
When inserting in a datasheet, Access uses:
INSERT INTO MyTable (col1, col2, ...) VALUES (?, ?, ...)
When exporting, Access uses:
INSERT INTO MyTable VALUES (?, ?, ...)
unless there are Memo or OLE Object columns, in which case Access again uses:
INSERT INTO MyTable (col1, col2, ...) VALUES (?, ?, ...)
When simulating a ROLLBACK TRANSACTION, errors from SQLTransact are ignored, and are assumed to be of the "already rolled back" nature. This because triggers often do their own rollback, but Access cannot know this.
If SQLSpecialColumns(ROWVER) reports a ROWVER column (say, "RV"), Access uses it to implement optimistic concurrency during datasheet updates. On datasheet update/delete, the UPDATE/DELETE query is appended with "AND RV = ?", and the old value supplied.
If there is no ROWVER column, then all updatable columns are compared to their old values, as in:
UPDATE MyTable SET col1 = ?, col2 = ?, col3 = ?
WHERE PrimKey = ? AND col1 = ? AND col2 = ? AND col3 = ?
LONGVARCHARs and LONGVARBINARYs are excluded from the comparison.
In either case, the query is executed, and SQLRowCount is called. If crow = = 1, the operation succeeds. If crow = = 0, Access assumes that another user changed the row first, and the operation fails. If crow < 0, an error is assumed, and the operation fails. If crow > 1, then it is assumed that the primary index was dropped, and that rows with duplicate keys were added; the operation fails, after rolling back the multiple updates/deletes.
This section describes the Microsoft Access ODBC Test Application. It gives a brief overview of the purpose of the application and then describes the installation process. It then describes the functionality of the application and how you can customize the application for your ODBC driver.
The Microsoft Access ODBC Test Application is an Access database designed to verify that your ODBC driver works correctly when used with Microsoft Access. The database contains forms, reports, queries, macros, and modules that will test a variety of functions using the ODBC driver you have developed. In addition to the database, ODBCTEST.MDB, the application contains the SQL Pass-Through DLL, MSASP110.DLL. This DLL allows Access commands directly to an ODBC data source for processing.
The application presents the driver writer with a list of tasks to perform. You may choose any or all of the tasks, and have those tasks executed against data that is accessed via your driver. The application will keep track of the total number of tasks that have been executed, as well as the results of those tasks. You can even configure the application to execute tasks that you define yourself!
To install the Microsoft Access ODBC Test Application, simply copy the file from the Install diskette to the Access directory on your hard drive. To begin the application, open the database ODBCTEST.MDB from the FileOpenDatabase menu option in Access.
When the application is first started, you will be presented with the Setup Parameters dialog. The Setup Parameters dialog prompts for five pieces of information:
After the Setup Parameters have been specified, choose OK. The application will connect to the data source and bring up the ODBC Test Database Main Menu. The Main Menu contains five options:
The Select Tasks to Run form presents a list of tests to be executed. The list of tests is in a multi-select scrolling subform. Choose the Select All button to select all of the tests or choose the Clear All button to de-select all of the tests. Once you have chosen the set of tests you would like to execute, choose the Go! button. This will begin the test execution. The Close button will return you to the Main Menu.
The Table Status Form is present whenever the ODBC Test Application is active. It shows the current status of executing tests as well as the status of each of the tables used by the tests. Tables can be in one of three states: DOES NOT EXIST, DIRTY, or LOADED. You can force all tables to be marked as either DIRTY or DOES NOT EXIST. DOES NOT EXIST means that the table has not been created yet on the specified data source, and therefore must be exported before a test can be run against that table. DIRTY means that the table exists, but one or more records has been modified. This implies that the data needs to be re-exported before a test can be run against that table. LOADED means the table exists and is in an unmodified state. Tests can be run against this table without further operations being performed on it.
During test execution, the Table Status Form will display the name of the currently executing test. It will also display a running summary of the total number of tests that have passed and failed, as well as indicating the current progress of the tests—for example, Executing test 5 of 10.
Although the ODBCTEST.MDB is designed to be used "as is," it may be necessary to customize the application for data-source-specific features. There is an Access Basic module called "SQL Syntax Sensitive" that contains all of the SQL commands that are sent to the data source via the SQL Pass-through DLL. You should verify that the syntax being executed is the proper syntax for your driver. In addition, there is a function in the "SQL Syntax Sensitive" module called pf_DumpTransaction. If your data source does not support the concept of a transaction log (or you do not need to manually clear out the log), you should comment out the call to pf_DumpTransaction in the pf_BeginTest function of the "Testing Tasks" module.