ODBC Driver Writer's Kit for Microsoft Access

Microsoft Corporation

ODBC API Calls Made by Microsoft Access

Description

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.

Access Use of ODBC

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

ODBC SQL Grammar Used by Microsoft Access

Description

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.

ODBC SQL Grammar

Statements

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.

Elements

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.

Microsoft Access/ODBC Datatype Mappings

Description

This section describes the mapping of datatypes between Microsoft Access and ODBC.

How ODBC Datatypes Are Mapped to Access Types

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).

How Access Datatypes Are Mapped to ODBC Datatypes

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

Microsoft Access Initialization File (MSACCESS.INI) Settings for ODBC

Description

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

Microsoft Access ODBC Spec-Compliance Error Codes

Description

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:

ODBC Spec-Compliance Errors

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)

General Caveats when Using ODBC Drivers with Microsoft Access

Description

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.

ODBC Driver Writer Caveats

Cursor commit behavior and transaction handling

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.

Identifier quoting

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.

Identifying a unique index

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.

Sending LIKE to the ODBC driver

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.

Sending expressions to the ODBC driver

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.

Datatype conversions

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.

Special characters contained in column names

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.

Tracing the SQL that is sent to the ODBC driver

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.

Connection timeout

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.

Connection management

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:

Reading the remote connectivity configuration table

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.

Checking for out-of-date stored procedures on SQL Server

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.

Distinguishing errors from warnings

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.

Asynchronous query execution

Access calls SQLSetStmtOption with SQL_QUERY_TIMEOUT and SQL_ASYNC_ENABLE, and ignores any errors that result.

Using SQL_DATA_AT_EXEC for non-NULL data

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.

INSERT INTO syntax

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 (?, ?, ...)

Rolling back transactions

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.

Special version columns

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.

Using the Microsoft Access ODBC Test Application

Description

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.

Overview

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!

Installation

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.

Setup Parameters Dialog

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:

Main Menu

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:

Select Tasks to Run

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.

Table Status Form

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.

Customizing the Application for Your Driver

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.