Основы проектирования приложений баз данных

         

Архитектура ODBC


Архитектура ODBC представлена четырьмя компонентами (рис. 1.1):

Приложение-клиент, выполняющее вызов функций ODBC.Менеджер драйверов, загружающий и освобождающий ODBC-драйверы, которые требуются для приложений-клиентов. Менеджер драйверов обрабатывает вызовы ODBC-функций или передает их драйверу.ODBC-драйвер, обрабатывающий вызовы SQL-функций, передавая SQL-серверу выполняемый SQL-оператор, а приложению-клиенту - результат выполнения вызванной функции.Источник данных, определяемый как конкретная локальная или удаленная база данных.


Рис. 1.1.  Архитектура ODBC

Основное назначение менеджера драйверов - загрузка драйвера, соответствующего подключаемому источнику данных, и инкапсуляция взаимодействия с различными типами источников данных посредством применения различных ODBC-драйверов.

ODBC-драйверы, принимая вызовы функций, взаимодействуют с приложением-клиентом, выполняя следующие задачи:

управление коммуникационными протоколами между приложением-клиентом и источником данных;управление запросами к СУБД;выполнение передачи данных от приложения-клиента в СУБД и из базы данных в приложение-клиент;возвращение приложению-клиенту стандартной информации о выполненном вызове ODBC-функции в виде кода возврата;поддерживает работу с курсорами и управляет транзакциями.

Приложение-клиент одновременно может устанавливать соединения с несколькими различными источниками данных, используя разные ODBC-драйверы, а также несколько соединений с одним и тем же источником данных, используя один и тот же ODBC-драйвер.



Функции ODBC API


Все функции ODBC API условно можно разделить на четыре группы:

основные функции ODBC, обеспечивающие взаимодействие с источником данных;функции установки (setup DLL);функции инсталляции (installer DLL) ODBC и источников данных;функции преобразования данных (translation DLL).

Объявления всех функций и используемых ими типов данных содержатся в заголовочных файлах. Группа основных функций ODBC API разбита на три уровня:

функции ядра ODBC;функции 1 уровня;функции 2 уровня.

Каждый ODBC-драйвер специфицируется как драйвер, поддерживающий определенный уровень функций ODBC API.

Прототипы функций ядра ODBC API находятся в файле Sql.h (C/C++, Visual Studio), а прототипы функций 1 и 2 уровней - в файле Sqlext.h.

Применение #define ODBCVER позволяет указать используемую версию (например, #define ODBCVER 0x0351).

Прототипы функций установки и инсталляции находятся в файле odbcinst.h.



Коды возврата


Все функции ODBC API возвращают значения, называемые кодами возврата. Код возврата определяет, была ли функция выполнена успешно, или характеризует тип произошедшей ошибки.

В заголовочном файле sql.h определены следующие коды возврата:

#define SQL_SUCCESS 0Функция выполнена успешно
#define SQL_SUCCESS_WITH_INFO 1Функция выполнена успешно, но с уведомительным сообщением
#if (ODBCVER >= 0x0300) #define SQL_NO_DATA 100 #endifБольше нет строк для извлечения их из результирующего набора. В предыдущей версии ODBC API этот код возврата обозначался как SQL_NO_DATA_FOUND. В версии 3.x код возврата SQL_NO_DATA_FOUND содержатся в заголовочном файле sqlext.h
#define SQL_ERROR (-1)При выполнении функции произошла ошибка
#define SQL_INVALID_HANDLE (-2)Указан неверный дескриптор
#define SQL_STILL_EXECUTING 2Функция, выполняемая асинхронно, пока не завершена
#define SQL_NEED_DATA 99Для успешного выполнения данной функции следует предварительно определить необходимые данные

Первые два кода возврата определяют, что функция была выполнена, а остальные информируют о типе произошедшей ошибки.

Для определения типа кода возврата в заголовочном файле sqltypes.h введено следующее объявление:

typedef signed short RETCODE;



Основа ODBC


Интерфейс ODBC (Open Database Connectivity) был разработан фирмой Microsoft как открытый интерфейс доступа к базам данных. Он предоставляет унифицированные средства взаимодействия прикладной программы, называемой клиентом (или приложением-клиентом), с сервером - базой данных.

В основу интерфейса ODBC были положены спецификация CLI-интерфейса (Call-Level Interface), разработанная X/Open, и ISO/IEC для API баз данных, а также язык SQL (Structured Query Language) как стандарт языка доступа к базам данных.

Интерфейс ODBC проектировался для поддержки максимальной интероперабельности приложений, которая обеспечивает унифицированный доступ любого приложения, использующего ODBC, к различным источникам данных. Так, если приложение, соответствующее стандарту ODBC и SQL, первоначально разрабатывалось для работы с базой данных Microsoft Access, а затем таблицы этой базы были перенесены в базу данных Microsoft SQL Server или базу данных Oracle, то приложение сможет и дальше обрабатывать эти данные без внесения дополнительных изменений.

Для взаимодействия с базой данных приложение-клиент вызывает функции интерфейса ODBC, которые реализованы в специальных модулях, называемых ODBC-драйверами. Как правило, ODBC-драйверы - это DLL-библиотеки, при этом одна DLL-библиотека может поддерживать несколько ODBC-драйверов. При установке на компьютер любого SQL-сервера (базы данных, поддерживающей один из стандартов языка SQL, например, SQL-92) автоматически выполняется регистрация в реестре Windows и соответствующего ODBC-драйвера.



Соотношение стандарта ODBC и стандарта интерфейса уровня вызовов (CLI)




Как уже отмечалось выше, открытый интерфейс доступа к базам данных фирмы Microsoft основан на следующих стандартах:

спецификация X/Open CAE1) (Specification "Data Management: SQL Call-Level Interface (CLI)");спецификация ISO2) /IEC 9075-3:1995 (E) (Call-Level Interface (SQL/CLI)).

В настоящее время фирма Microsoft поддерживает версию 3.x ODBC API. Приложения, написанные на основе спецификации X/Open и ISO CLI, будут правильно работать с ODBC-драйверами версии 3.x или драйверами "согласованного стандарта" в том случае, если они компилируются с заголовочными файлами ODBC версии 3.x и линкуются с ODBC 3.x библиотеками, а доступ к ODBC-драйверу получают через менеджер драйверов ODBC 3.x. Аналогично, что и сами драйверы 3.x, написанные на основе спецификации X/Open и ISO CLI, будут правильно работать с приложениями при соблюдении этих же условий.

Драйвер ODBC 3.x всегда поддерживает все возможности, используемые приложением "согласованного стандарта", а приложение ODBC 3, которое использует только возможности, предоставляемые ISO CLI, и обязательные средства, описываемые X/Open CLI, всегда будет работать с драйвером "согласованного стандарта".

В дополнение к интерфейсу, специфицированному в стандартах ISO/IEC и X/Open CLI, ODBC реализует следующие возможности:

извлечение нескольких строк (блочная выборка) за один вызов функции;связывание с массивом параметров;поддержка закладок, включая выборку посредством закладки, закладки переменной длины, блочное обновление и удаление посредством отмеченных операций над непоследовательными строками;построчное связывание (row-wise binding);связывание со смещением (binding offsets);поддержка пакетов SQL-операторов как в хранимых процедурах, так и в виде последовательности отдельных SQL-операторов, выполняемых при вызове функций SQLExecute и SQLExecDirect;определение точного или приблизительного числа строк курсора;применение операции позиционированного обновления и удаления и пакетных удалений и обновлений с использованием функции SQLSetPos;поддержка функций каталога, позволяющих получать информацию из схемы базы данных (системных таблиц);библиотеки преобразования для кодовых страниц;асинхронное выполнение;поддержка хранимых процедур, включая escape-последовательности, механизм связывания выходных параметров, функции каталога;более продвинутые возможности соединения, включающие поддержку атрибутов соединения и просмотра атрибутов.


Aey niaeaniaaiiinoe ni noaiaa?oii ISO CLI e X/Open CLI a caaieiai?iuo oaeeao niaa??aony inaaaiieiu cia?aiee, eniieucoaiuo a ODBC API. Oae, a inaaaiieiao "MAX" ?anoe?aii ai "MAXIMUM", "LEN" ai "LENGTH", "MULT" ai "MULTIPLE", "OJ" ai "OUTER_JOIN", a "TXN" ai "TRANSACTION". Iai?eia?:

Eiaaioeoeeaoi? ODBCInaaaiiei a caaieiai?iii oaeea
SQL_MAX_CATALOG_NAME_LENSQL_MAXIMUM_CATALOG_NAME_LENGTH
SQL_MAX_COLUMN_NAME_LENSQL_MAXIMUM_COLUMN_NAME_LENGTH
SQL_MAX_COLUMNS_IN_GROUP_BYSQL_MAXIMUM_COLUMNS_IN_GROUP_BY
SQL_MAX_COLUMNS_IN_ORDER_BYSQL_MAXIMUM_COLUMNS_IN_ORDER_BY
SQL_MAX_COLUMNS_IN_SELECTSQL_MAXIMUM_COLUMNS_IN_SELECT
SQL_MAX_COLUMNS_IN_TABLESQL_MAXIMUM_COLUMNS_IN_TABLE
SQL_MAX_CONCURRENT_ACTIVITIESSQL_MAXIMUM_CONCURRENT_ACTIVITIES
SQL_MAX_CURSOR_NAME_LENSQL_MAXIMUM_CURSOR_NAME_LENGTH
SQL_MAX_DRIVER_CONNECTIONSSQL_MAXIMUM_DRIVER_CONNECTIONS
SQL_MAX_IDENTIFIER_LENSQL_MAXIMUM_IDENTIFIER_LENGTH
SQL_MAX_SCHEMA_NAME_LENSQL_MAXIMUM_SCHEMA_NAME_LENGTH
SQL_MAX_STATEMENT_LENSQL_MAXIMUM_STATEMENT_LENGTH
SQL_MAX_TABLE_NAME_LENSQL_MAXIMUM_TABLE_NAME_LENGTH
SQL_MAX_TABLES_IN_SELECTSQL_MAXIMUM_TABLES_IN_SELECT
SQL_MAX_USER_NAME_LENSQL_MAXIMUM_USER_NAME_LENGTH
SQL_MULT_RESULT_SETSSQL_MULTIPLE_RESULT_SETS
SQL_OJ_CAPABILITIESSQL_OUTER_JOIN_CAPABILITIES
SQL_TXN_CAPABLESQL_TRANSACTION_CAPABLE
SQL_TXN_ISOLATION_OPTIONSQL_TRANSACTION_ISOLATION_OPTION


Aey niaeaniaaiiinoe ni noaiaa?oii ISO CLI e X/Open CLI a caaieiai?iuo oaeeao niaa??aony inaaaiieiu cia?aiee, eniieucoaiuo a ODBC API. Oae, a inaaaiieiao "MAX" ?anoe?aii ai "MAXIMUM", "LEN" ai "LENGTH", "MULT" ai "MULTIPLE", "OJ" ai "OUTER_JOIN", a "TXN" ai "TRANSACTION". Iai?eia?:

Eiaaioeoeeaoi? ODBCInaaaiiei a caaieiai?iii oaeea
SQL_MAX_CATALOG_NAME_LENSQL_MAXIMUM_CATALOG_NAME_LENGTH
SQL_MAX_COLUMN_NAME_LENSQL_MAXIMUM_COLUMN_NAME_LENGTH
SQL_MAX_COLUMNS_IN_GROUP_BYSQL_MAXIMUM_COLUMNS_IN_GROUP_BY
SQL_MAX_COLUMNS_IN_ORDER_BYSQL_MAXIMUM_COLUMNS_IN_ORDER_BY
SQL_MAX_COLUMNS_IN_SELECTSQL_MAXIMUM_COLUMNS_IN_SELECT
SQL_MAX_COLUMNS_IN_TABLESQL_MAXIMUM_COLUMNS_IN_TABLE
SQL_MAX_CONCURRENT_ACTIVITIESSQL_MAXIMUM_CONCURRENT_ACTIVITIES
SQL_MAX_CURSOR_NAME_LENSQL_MAXIMUM_CURSOR_NAME_LENGTH
SQL_MAX_DRIVER_CONNECTIONSSQL_MAXIMUM_DRIVER_CONNECTIONS
SQL_MAX_IDENTIFIER_LENSQL_MAXIMUM_IDENTIFIER_LENGTH
SQL_MAX_SCHEMA_NAME_LENSQL_MAXIMUM_SCHEMA_NAME_LENGTH
SQL_MAX_STATEMENT_LENSQL_MAXIMUM_STATEMENT_LENGTH
SQL_MAX_TABLE_NAME_LENSQL_MAXIMUM_TABLE_NAME_LENGTH
SQL_MAX_TABLES_IN_SELECTSQL_MAXIMUM_TABLES_IN_SELECT
SQL_MAX_USER_NAME_LENSQL_MAXIMUM_USER_NAME_LENGTH
SQL_MULT_RESULT_SETSSQL_MULTIPLE_RESULT_SETS
SQL_OJ_CAPABILITIESSQL_OUTER_JOIN_CAPABILITIES
SQL_TXN_CAPABLESQL_TRANSACTION_CAPABLE
SQL_TXN_ISOLATION_OPTIONSQL_TRANSACTION_ISOLATION_OPTION

Создание источника данных


Источник данных DSN, используемый функциями ODBC API, первоначально должен быть создан. Это можно выполнить как программно - вызвав функцию ODBC API, так и интерактивно - используя утилиту ODBC (в зависимости от версии Windows, расположенную на панели управления или администрирования).



Создание источника данных с использованием ODBC API


DLL-библиотека ODBCCP32.DLL предоставляет функции ODBC API ConfigDSN и SQLConfigDataSource, позволяющие выполнять регистрацию новых источников данных или удалять информацию об источниках данных из реестра Windows (и из файла ODBC.ini).

Функция ConfigDSN позволяет добавлять, изменять или удалять источники данных и имеет следующее формальное описание:

BOOL ConfigDSN( HWND hwndParent, WORD fRequest, LPCSTR lpszDriver, LPCSTR lpszAttributes);

Для использования в среде Visual Studio функций ConfigDSN и SQLConfigDataSource следует подключить заголовочный файл odbcinst.h.

Параметр hwndParent определяет дескриптор окна или NULL. Если дескриптор не указан, то при выполнении данной функции окно с предложением уточнить параметры не отображается. Параметр fRequest указывает тип запроса, который задается одной из следующих констант:

ODBC_ADD_DSN - добавление нового источника данных;ODBC_CONFIG_DSN - изменение существующего источника данных;ODBC_REMOVE_DSN - удаление существующего источника данных.

Параметр lpszDriver содержит описание драйвера, а параметр lpszAttributes - список атрибутов в форме "ключевое слово=значение" (например: DSN=MyDB\0UID=U1\0PWD=P1\0DATABASE=DB1\0\0). Список атрибутов завершается двумя null-байтами.

При успешном завершении функция возвращает значение TRUE, в противном случае вызовом функции SQLInstallerError можно получить один из следующих кодов ошибки:

ODBC_ERROR_INVALID_HWND - ошибка в указании дескриптора окна;ODBC_ERROR_INVALID_KEYWORD_VALUE - параметр lpszAttributes содержит ошибки;ODBC_ERROR_INVALID_NAME - параметр lpszDriver не найден в системе;ODBC_ERROR_INVALID_REQUEST_TYPE - параметр fRequest содержит недопустимое значение;ODBC_ERROR_REQUEST_FAILED - нельзя выполнить действие, указанное параметром fRequest;ODBC_ERROR_DRIVER_SPECIFIC - ошибка конкретного драйвера.

Для записи информации об источнике данных в секцию [ODBC Data Sources] секции ODBC.INI реестра Windows функция ConfigDSN вызывает функцию SQLWriteDSNToIni, а для удаления - функцию SQLRemoveDSNFromIni.


Функция SQLConfigDataSource имеет следующее формальное описание:

BOOL SQLConfigDataSource( HWND hwndParent, WORD fRequest, LPCSTR lpszDriver, LPCSTR lpszAttributes);

Параметры функции SQLConfigDataSource аналогичны параметрам функции ConfigDSN, при этом параметр fRequest может принимать следующие значения:

ODBC_ADD_DSN - добавление нового пользовательского DSN;ODBC_CONFIG_DSN - изменение существующего пользовательского DSN;ODBC_REMOVE_DSN - удаление существующего пользовательского DSN;ODBC_ADD_SYS_DSN - добавление нового системного DSN;ODBC_CONFIG_SYS_DSN - изменение существующего системного DSN;ODBC_REMOVE_SYS_DSN - удаление существующего системного DSN.

Функция ConfigDSN относится к группе функций установки DLL (setup DLL), а функция SQLConfigDataSource - к группе функций инсталляции DLL (Installer DLL).

При выполнении функция SQLConfigDataSource использует значение параметра lpszDriver для получения из системной информации полного пути к Setup DLL конкретного драйвера, загружает эту DLL и вызывает функцию ConfigDSN, передавая ей свой список параметров (значение параметра fRequest преобразуется к значению, принимаемому функцией ConfigDSN). Перед вызовом ConfigDSN, в зависимости от типа обрабатываемого DSN, устанавливается режим USERDSN_ONLY (пользовательский DSN) или SYSTEMDSN_ONLY (системный DSN), а перед завершением выполнения функция SQLConfigDataSource возвращает режим BOTHDSN.



Функция SQLConfigDataSource имеет следующее формальное описание:

BOOL SQLConfigDataSource( HWND hwndParent, WORD fRequest, LPCSTR lpszDriver, LPCSTR lpszAttributes);

Параметры функции SQLConfigDataSource аналогичны параметрам функции ConfigDSN, при этом параметр fRequest может принимать следующие значения:

ODBC_ADD_DSN - добавление нового пользовательского DSN;ODBC_CONFIG_DSN - изменение существующего пользовательского DSN;ODBC_REMOVE_DSN - удаление существующего пользовательского DSN;ODBC_ADD_SYS_DSN - добавление нового системного DSN;ODBC_CONFIG_SYS_DSN - изменение существующего системного DSN;ODBC_REMOVE_SYS_DSN - удаление существующего системного DSN.

Функция ConfigDSN относится к группе функций установки DLL (setup DLL), а функция SQLConfigDataSource - к группе функций инсталляции DLL (Installer DLL).

При выполнении функция SQLConfigDataSource использует значение параметра lpszDriver для получения из системной информации полного пути к Setup DLL конкретного драйвера, загружает эту DLL и вызывает функцию ConfigDSN, передавая ей свой список параметров (значение параметра fRequest преобразуется к значению, принимаемому функцией ConfigDSN). Перед вызовом ConfigDSN, в зависимости от типа обрабатываемого DSN, устанавливается режим USERDSN_ONLY (пользовательский DSN) или SYSTEMDSN_ONLY (системный DSN), а перед завершением выполнения функция SQLConfigDataSource возвращает режим BOTHDSN.


Утилита ODBC


При использовании утилиты ODBC (рис. 1.2) на вкладке Пользовательский DSN отображается список всех зарегистрированных источников данных.


Рис. 1.2.  Диалог утилиты ODBC

При добавлении нового источника данных отображается диалог со всеми зарегистрированными в реестре Windows ODBC-драйверами (рис. 1.3).


Рис. 1.3.  Список зарегистрированных драйверов

В зависимости от выбранного ODBC-драйвера последовательно отображаются один или несколько диалогов для ввода параметров создаваемого DSN. Так, для создания источника данных, позволяющего работать с базой данных Microsoft SQL Server, следует определить имя создаваемого DSN, имя зарегистрированного SQL-сервера (рис. 1.4) и имя базы данных (на этом сервере), а также ряд дополнительных параметров.


Рис. 1.4.  Создание DSN для базы данных Microsoft SQL Server



Функции инсталляции


Группа функций инсталляции (installer DLL) объединяет функции, предназначенные для установки ODBC и конфигурирования источников данных. В следующей таблице приведены функции, относящиеся к данной группе.

ФункцияОписаниеУстановка ODBCКонфигурирование источников данных
SQLConfigDriverЗагружает setup DLL для конкретного драйвера
SQLGetInstalledDriversВозвращает список установленных драйверов
SQLInstallDriverExДобавляет в реестр данные о драйвере
SQLInstallDriverManagerВозвращает каталог, назначенный для менеджера драйверов
SQLInstallerErrorВозвращает информацию об ошибке выполнения функции инсталляции
SQLInstallTranslatorExДобавляет в реестр данные о трансляторе
SQLRemoveDriverУдаляет из реестра данные о драйвере
SQLRemoveDriverManagerИзменяет или удаляет данные о базовых компонентах ODBC из реестра
SQLRemoveTranslatorУдаляет из реестра данные о трансляторе
SQLConfigDataSourceВызывает setup DLL для конкретного драйвера
SQLCreateDataSourceОтображает диалог для добавления источника данных
SQLGetConfigModeЗапрашивает режим конфигурации, позволяющий определить, где в реестре Windows искать секцию ODBC.INI. Если функция возвращает значение параметра равным ODBC_USER_DSN, то DSN является пользовательским и вход ODBC.INI находится в разделе HKEY_CURRENT_USER (Software|ODBC), а если функция возвращает значение параметра равным ODBC_SYSTEM_DSN или ODBC_BOTH_DSN, то вход ODBC.INI находится в разделе HKEY_LOCAL_MACHINE
SQLGetPrivateProfileStringЗаписывает значение в реестр
SQLGetTranslatorПоказывает диалог для выбора транслятора
SQLManageDataSourcesОтображает диалог для конфигурирования драйверов и источников данных
SQLReadFileDSNЧитает информацию о DSN из файла
SQLRemoveDefaultDataSourceУдаляет источник данных по умолчанию
SQLRemoveDSNFromIniУдаляет источник данных
SQLSetConfigModeУстанавливает режим конфигурации, указывающий, где в реестре будет использоваться вход ODBC.INI
SQLValidDSNПроверяет правильность имени источника данных
SQLWriteDSNToIniДобавляет источник данных
SQLWriteFileDSNЗаписывает информацию о DSN в файл
SQLWritePrivateProfileStringЗапрашивает значение из реестра Windows



Функции установки


Группа функций установки (setup DLL) объединяет функции, предназначенные для конфигурирования драйверов и источников данных. В следующей таблице приведены функции, относящиеся к данной группе.

Функция Описание
ConfigDriverВыполняет установку или удаление драйвера
ConfigDSNВыполняет добавление, изменение или удаление источника данных
ConfigTranslatorВозвращает используемые по умолчанию опции преобразования



Основные функции ODBC


Как уже отмечалось в предыдущей лекции, все функции ODBC API условно можно разделить на четыре группы:

основные функции ODBC, обеспечивающие взаимодействие с источником данных;функции установки (setup DLL);функции инсталляции (installer DLL) ODBC и источников данных;функции преобразования данных (translation DLL), вызываемые при передаче данных от драйвера к источнику данных или обратно.

В следующей таблице представлен список основных функций ODBC API и их уровень соответствия стандартам (в столбце "Соответствие" показано соответствие стандартам и указана версия ODBC, начиная с которой данная функция доступна):

ФункцияСоответствиеОписаниеСоединение с источником данныхПолучение информации о драйверах и об источниках данныхИзменение атрибутов драйверов и получение информации об атрибутах драйверовИзменение полей дескриптора и получение информации о полях дескриптораПодготовка SQL-запросовВыполнение запросовИзвлечение результатов и информации о нихФункции каталогаОсвобождение оператораОсвобождение соединения
SQLAllocHandle

ISO 92

ODBC 3.0

Получает идентификатор (дескриптор) среды, соединения или оператора, или дескриптор приложения
SQLConnect

ISO 92

ODBC 1.0

Соединение с источником данных по DSN, имени и паролю пользователя
SQLDriverConnect

ODBC

ODBC 1.0

Соединение с источником данных по указанной строке соединения или при помощи отображаемого диалога для интерактивного ввода параметров соединения
SQLBrowseConnect

ODBC

ODBC 1.0

Последовательно запрашивает атрибуты соединения и устанавливает допустимые значения атрибута. После спецификации значения для каждого требуемого атрибута соединения функция выполняет соединение с источником данных
SQLDataSources

ISO 92

ODBC1.0

Возвращает список доступных источников данных
SQLDrivers

ODBC

ODBC2.0

Возвращает список установленных драйверов и их атрибуты
SQLGetInfo

ISO 92

ODBC1.0

Возвращает информацию об указанных драйвере и источнике данных
SQLGetFunctions

ISO 92

ODBC1.0

Возвращает функции, которые поддерживаются используемым драйвером
SQLGetTypeInfo

ISO 92

ODBC1.0

Возвращает информацию о поддерживаемых типах данных
SQLSetConnectAttr

ISO 92

ODBC3.0

Устанавливает атрибуты соединения
SQLGetConnectAttr

ISO 92

ODBC3.0

Возвращает значение атрибута соединения
SQLSetEnvAttr

ISO 92

ODBC3.0

Устанавливает атрибуты среды
SQLGetEnvAttr

ISO 92

ODBC3.0

Возвращает значение атрибута среды
SQLSetStmtAttr

ISO 92

ODBC3.0

Устанавливает атрибуты оператора
SQLGetStmtAttr

ISO 92

ODBC3.0

Возвращает значение атрибута оператора
SQLGetDescField

ISO 92

ODBC3.0

Возвращает значение дескриптора для одного поля
SQLGetDescRec

ISO 92

ODBC3.0

Возвращает значения дескриптора для нескольких полей
SQLSetDescField

ISO 92

ODBC3.0

Устанавливает значение дескриптора для одного поля
SQLSetDescRec

ISO 92

ODBC3.0

Устанавливает значение дескриптора для нескольких полей
SQLPrepare

ISO 92

ODBC1.0

Компилирует SQL-оператор для последующего выполнения
SQLBindParameter

ODBC

ODBC2.0

Связывает буфер с параметрами, используемыми в SQL-операторе
SQLGetCursorName

ISO 92

ODBC1.0

Возвращает имя курсора, которое ассоциировано с дескриптором оператора
SQLSetCursorName

ISO 92

ODBC1.0

Определяет имя курсора
SQLSetScrollOptionsODBC1.0Устанавливает опции, которые управляют поведением курсора. В версиях ODBC 2.x и 3.x эта функция заменена функцией SQLSetStmtAttr
SQLExecute

ISO 92

ODBC1.0

Выполняет откомпилированный SQL-оператор
SQLExecDirect

ISO 92

ODBC1.0

Выполняет SQL-оператор
SQLNativeSql

ODBC

ODBC1.0

Возвращает текст SQL-оператора, преобразованного конкретным драйвером, но не выполняет его
SQLDescribeParam

ODBC

ODBC1.0

Возвращает описание параметров, используемых в откомпилированном SQL-операторе
SQLNumParams

ISO 92

ODBC1.0

Возвращает число параметров в откомпилированном SQL-операторе
SQLParamData

ISO 92

ODBC1.0

Используется совместно с функцией SQLPutData для передачи во время выполнения значений параметров
SQLPutData

ISO 92

ODBC1.0

Передает часть или все значения параметров
SQLRowCount

ISO 92

ODBC1.0

Возвращает число строк, на которые воздействовал SQL-оператор insert, update или delete
SQLNumResultCols

ISO 92

ODBC1.0

Возвращает число столбцов в результирующем наборе
SQLDescribeCol

ISO 92

ODBC1.0

Описывает столбец результирующего набора, возвращая имя поля, его тип, размер и т.п
SQLColAttribute

ISO 92

ODBC3.0

Возвращает информацию о столбце результирующего набора. В отличие от функции SQLColAttribute, позволяет получить более обширную информацию о столбце, включая информацию, определяемую конкретным драйвером
SQLBindCol

ISO 92

ODBC1.0

Выполняет связывание буфера приложения-клиента со столбцами результирующего набора
SQLFetch

ISO 92

ODBC1.0

Извлекает данные одной следующей строки из результирующего набора, возвращая данные для всех связанных столбцов
SQLFetchScroll

ISO 92

ODBC3.0

Извлекает данные одной или нескольких строк из результирующего набора, возвращая данные для всех связанных столбцов. Функция позволяет явно указать, какую строку следует извлечь. Данная функция заменила функцию SQLExtendedFetch из ODBC 2.x
SQLGetData

ISO 92

ODBC1.0

Извлекает из результирующего набора значение одного столбца одной текущей строки. Для использования этой функции не требуется предварительное связывание столбцов
SQLSetPos

ODBC

ODBC1.0

Позиционирует курсор в извлеченном блоке данных и позволяет приложению-клиенту: обновлять данные в строке, модифицировать или удалять данные в результирующем наборе
SQLBulkOperations

ODBC

ODBC3.0

Выполняет несколько вставок или несколько помеченных операций, включая изменение, удаление и выборку по установленному маркеру
SQLMoreResults

ODBC

ODBC1.0

Определяет, есть ли еще следующий результирующий набор, и при его наличии выполняет переход на него
SQLGetDiagField

ISO 92

ODBC3.0

Возвращает значение поля записи из структуры диагностической информации, ассоциированной с конкретным дескриптором (среды, соединения, оператора)
SQLGetDiagRec

ISO 92

ODBC3.0

Возвращает значения нескольких предопределенных полей записи из структуры диагностической информации, ассоциированной с конкретным дескриптором (среды, соединения, оператора)
SQLColumnPrivileges

ODBC

ODBC1.0

Возвращает список полей и имеющиеся привилегии для одной или нескольких таблиц
SQLColumns

X/Open

ODBC1.0

Возвращает список имен полей в указанной таблице
SQLForeignKeys

ODBC

ODBC1.0

Возвращает список полей, которые составляют внешние ключи таблицы, если они созданы
SQLPrimaryKeys

ODBC

ODBC1.0

Возвращает список полей, которые составляют первичный ключ таблицы
SQLProcedureColumns

ODBC

ODBC1.0

Возвращает в виде результирующего набора список входных и выходных параметров указанной процедуры
SQLProceduresODBCODBC1.0Возвращает список хранимых процедур для подключенного источника данных
SQLSpecialColumns

X/Open

ODBC1.0

Получает информацию об оптимальном наборе полей, уникально идентифицирующих строку в указанной таблице, или имя поля, которое автоматически обновляется при изменении какого-либо поля в строке
SQLStatistics

ISO 92

ODBC1.0

Возвращает информацию о таблице и список индексов, ассоциированных с ней
SQLTablePrivileges

ODBC

ODBC1.0

Возвращает в виде результирующего набора список таблиц и привилегии, назначенные для каждой таблицы
SQLTables

X/Open

ODBC1.0

Возвращает в виде результирующего набора список таблиц, хранимых в указанном источнике данных
SQLFreeStmt

ISO 92

ODBC1.0

Завершает обработку оператора, удаляет результирующий набор и освобождает все ресурсы, ассоциированные с данным дескриптором оператора
SQLCloseCursor

ISO 92

ODBC3.0

Закрывает курсор, открытый с данным дескриптором оператора, и удаляет результирующий набор
SQLCancel

ISO 92

ODBC1.0

Завершает выполнение SQL-оператора, прекращая асинхронное выполнение функции, выполнение функции, требующей данные, или функции, выполняемой в другом потоке. В отличие от версии 2.x, данная функция не может выполнить освобождение дескриптора оператора, и требуется явный вызов функции SQLFreeStmt
SQLEndTran

ISO 92

ODBC3.0

Выполняет завершение или откат транзакции
SQLDisconnect

ISO 92

ODBC1.0

Закрывает соединение с источником данных
SQLFreeHandle

ISO 92

ODBC3.0

Освобождает ресурсы, ассоциированные с указанным дескриптором (среды, соединения, оператора, приложения)



Соответствие версий ODBC API


Если приложение использует функции ODBC 2.x с менеджером драйверов ODBC 3.x, то менеджер драйверов подменяет вызываемую функцию и передает драйверу ODBC 3.x вызов функции в соответствии со следующей таблицей.

Функции ODBC 2.x Функции ODBC 3.x
SQLAllocConnectSQLAllocHandle
SQLAllocEnvSQLAllocHandle
SQLAllocStmtSQLAllocHandle
SQLBindParam (для стандарта X/Open и ISO)SQLBindParameter
SQLColAttributesSQLColAttribute
SQLErrorSQLGetDiagRec
SQLFreeConnectSQLFreeHandle
SQLFreeEnvSQLFreeHandle
SQLFreeStmtSQLFreeHandle
SQLGetConnectOptionSQLGetConnectAttr
SQLGetStmtOptionSQLGetStmtAttr
SQLParamOptionsSQLSetStmtAttr
SQLSetConnectOptionSQLSetConnectAttr
SQLSetParam[функция ODBC 1.0]SQLBindParameter
SQLSetScrollOptionSQLSetStmtAttr
SQLSetStmtOptionSQLSetStmtAttr
SQLTransactSQLEndTran



Создание дескрипторов


Перед использованием функций ODBC API приложение-клиент создает дескриптор (идентификатор) окружения, определяющий глобальный контекст для доступа к источникам данных. Дескриптор окружения предоставляет доступ к различной информации, включая текущие установки всех атрибутов окружения, дескрипторы соединений, созданные для данного окружения, диагностику уровня окружения.

Дескриптор окружения определяет некоторую структуру, содержащую данную информацию. Непосредственно дескриптор окружения обычно используется при вызове функций SQLDataSources и SQLDrivers и при создании дескрипторов соединения.

Для приложения-клиента, реализующего с использованием функций ODBC API доступ к источнику данных, достаточно иметь один дескриптор окружения.

Создание дескриптора окружения выполняется функцией SQLAllocHandle, а освобождение - функцией SQLFreeHandle.

Функция SQLAllocHandle введена в версии ODBC 3.x вместо существовавших в версии ODBC 2.0 функций SQLAllocConnect, SQLAllocEnv и SQLAllocStmt. Для того чтобы приложение, использующее функцию SQLAllocHandle, могло работать через драйверы ODBC 2.x, менеджер драйверов версии 3.x заменяет вызовы функций третьей версии на их аналоги второй версии и передает такой "откорректированный" вызов ODBC-драйверу.

Для подключения к базе данных следует создать дескриптор (идентификатор) соединения. Для одного дескриптора окружения может быть создано несколько дескрипторов соединения.

Для выполнения SQL-оператора создается дескриптор (идентификатор) оператора.

Для одного дескриптора соединения может быть создано несколько дескрипторов оператора.

По спецификации ODBC для каждого приложения драйверы могут поддерживать неограниченное число дескрипторов каждого типа. Однако конкретный драйвер может накладывать некоторые ограничения на количество дескрипторов.

Функция, используемая для создания дескриптора окружения, соединения, оператора или приложения, имеет следующее формальное описание:

SQLRETURN SQLAllocHandle( SQLSMALLINT HandleType, SQLHANDLE InputHandle, SQLHANDLE * OutputHandlePtr);


Параметр HandleType ([Input]) указывает одной из следующих констант тип создаваемого дескриптора:

SQL_HANDLE_ENV SQL_HANDLE_DBC SQL_HANDLE_STMT SQL_HANDLE_DESC

Параметр InputHandle ([Input]) определяет контекст, в который добавляется создаваемый дескриптор. Если тип дескриптора SQL_HANDLE_ENV, то параметр InputHandle указывается константой SQL_NULL_HANDLE. При создании дескриптора среды параметр InputHandle задает дескриптор окружения, а для создания дескриптора оператора (SQL_HANDLE_STMT) и дескриптора приложения (SQL_HANDLE_DESC) - дескриптор соединения.

Идентификаторы, определяющие тип дескриптора и сам дескриптор, описаны в заголовочных файлах sql.h и sqltypes.h следующим образом:

/* sql.h */ #if (ODBCVER >= 0x0300) #define SQL_HANDLE_ENV 1 #define SQL_HANDLE_DBC 2 #define SQL_HANDLE_STMT 3 #define SQL_HANDLE_DESC 4 #endif /* sqltypes.h */ #if (ODBCVER >= 0x0300) #if defined(WIN32) || defined(_WIN64) typedef void* SQLHANDLE; #else typedef SQLINTEGER SQLHANDLE; #endif /* defined(WIN32) || defined(_WIN64) */ typedef SQLHANDLE SQLHENV; typedef SQLHANDLE SQLHDBC; typedef SQLHANDLE SQLHSTMT; typedef SQLHANDLE SQLHDESC; #else //ODBCVER < 0x0300 #if defined(WIN32) || defined(_WIN64) typedef void* SQLHENV; typedef void* SQLHDBC; typedef void* SQLHSTMT; #elsetypedef SQLINTEGER SQLHENV; typedef SQLINTEGER SQLHDBC; typedef SQLINTEGER SQLHSTMT; #endif /* defined(WIN32) || defined(_WIN64) */ #endif /* ODBCVER >= 0x0300 */

Параметр OutputHandlePtr ([Output]) - это указатель на буфер, в который помещается создаваемая для дескриптора структура данных.

Функция SQLAllocHandle может возвращать следующие значения:

SQL_SUCCESS - значение, определяемое ODBC API для указания успешного завершения функции;SQL_SUCCESS_WITH_INFO - значение, определяемое ODBC API для указания того, что функция выполнена успешно, но с уведомительным сообщением;SQL_INVALID_HANDLE - значение, определяемое ODBC API для указания, что задан неверный дескриптор;SQL_ERROR - значение, определяемое ODBC API для указания, что при выполнении функции произошла ошибка.



Для получения дополнительной информации об ошибке выполнения функции приложение-клиент может использовать данные из дескриптора, указанного параметром InputHandle.

Если при выполнении функции произошла ошибка (код возврата SQL_ERROR) или функция выполнена, но с уведомительным сообщением (код возврата SQL_SUCCESS_WITH_INFO), то значение SQLSTATE можно получить при вызове функции SQLGetDiagRec.

После создания дескриптора окружения следует установить атрибут SQL_ATTR_ODBC_VERSION. В противном случае при попытке создать дескриптор соединения произойдет ошибка.

Для приложений "согласованного стандарта" во время компиляции функция SQLAllocHandle заменяется на SQLAllocHandleStd. Основное отличие последней состоит в том, что при вызове этой функции с значением параметра HandleType, равным SQL_HANDLE_ENV, происходит установка атрибута окружения SQL_ATTR_ODBC_VERSION, равным SQL_OV_ODBC3 (так как приложения "согласованного стандарта" всегда являются приложениями ODBC 3.x и не требуют регистрации версии приложения).


Пул соединений


Организация пула соединений позволяет приложению выбирать соединения из пула без необходимости переустанавливать их для каждого использования. После того как соединение создано и помещено в пул, приложение может использовать это соединение, не повторяя опять процесс установки соединения.

Использование соединений, помещенных в пул, может значительно увеличить производительность приложений, многократно устанавливающих и разрывающих соединения. Примером таких приложений могут служить серверные Интернет-приложения среднего звена в трехзвенной архитектуре, постоянно повторно устанавливающие и разрывающие соединение.

Соединения из пула могут быть использованы несколькими компонентами в одном процессе. Это означает, что автономные компоненты в одном процессе могут взаимодействовать друг с другом без уведомления друг друга. Соединение из пула может быть использовано повторно несколькими компонентами.

При работе с пулом соединений используемый драйвер ODBC должен быть полностью потокозащищенным, что позволит одновременно выполнять различные вызовы из разных потоков (например, выполнить подсоединение в одном потоке, использовать соединение в другом потоке, а отсоединение выполнить в третьем потоке).

Пул соединений управляется менеджером драйверов. Соединение выбирается из пула при вызове приложением функции SQLConnect или функции SQLDriverConnect, а возвращается в пул при выполнении функции SQLDisconnect. Размер пула изменяется динамически: если соединение не было использовано в течение определенного периода времени, то оно удаляется из пула.

Используя атрибут SQL_ATTR_CONNECTION_DEAD в версии ODBC API 3.х, менеджер драйверов может определить состояние соединения из пула: значение SQL_CD_TRUE определяет, что соединение разорвано, а значение SQL_CD_FALSE означает, что соединение пока еще активно. При этом обычно для получения значения данного атрибута драйвер не обращается к серверу, а возвращает результат, основываясь на состоянии соединения при его последнем использовании.


Информация о том, будут ли предотвращены попытки драйвера менеджера установить соединение при включенном пуле соединений, сохраняется в реестре Windows в разделе:

HKEY_LOCAL_MACHINE\ Software\Odbc\Odbcinst.ini\ ODBC Connection Pooling\Retry Wait

Для использования приложением пула соединений следует:

Для включения режима пула соединений вызвать функцию SQLSetEnvAttr с атрибутом среды SQL_ATTR_CONNECTION_POOLING, равным значению SQL_CP_ONE_PER_DRIVER или значению SQL_CP_ONE_PER_HENV. При вызове функции SQLSetEnvAttr дескриптор среды указывается равным значению NULL, что определяет атрибут SQL_ATTR_CONNECTION_POOLING как атрибут уровня процесса. Значение SQL_CP_ONE_PER_DRIVER определяет, что отдельный пул соединений поддерживается для каждого драйвера. При необходимости иметь один пул для различных драйверов указывается значение SQL_CP_ONE_PER_HENV (отдельный пул соединений поддерживается для каждой среды). Создать дескриптор среды, вызвав функцию SQLAllocHandle со значением параметра типа дескриптора равным SQL_HANDLE_ENV. Созданная среда будет неявно разделяемой средой.Создать дескриптор соединения, вызвав функцию SQLAllocHandle со значением параметра типа дескриптора, равным SQL_HANDLE_DBC. Менеджер драйверов будет искать существующую разделяемую среду с соответствующими атрибутами среды (при нахождении требуемой среды она возвращается приложению и менеджер драйверов увеличивает значение счетчика на 1).Если такой среды нет, то менеджер драйверов создает ее и устанавливает значение счетчика равным 1.Для получения соединения из пула вызвать функцию SQLConnect или SQLDriverConnect. Менеджер драйверов использует значения параметров и значения атрибутов соединения для определения требуемого соединения из пула соединений. При этом учитывается значение атрибута SQL_ATTR_CP_MATCH (соответствие требуемого соединения соединению из пула).Для разрыва соединения вызвать функцию SQLDisconnect. При этом соединение возвращается обратно в пул и делается доступным для дальнейшего использования.

Для отключения режима пула соединений следует установить значение атрибута SQL_ATTR_CONNECTION_POOLING равным SQL_CP_OFF.


Схема доступа к источнику данных с использованием ODBC API


Первым шагом при реализации доступа к источнику данных посредством ODBC API без применения пула соединений является создание дескриптора окружения. После выделения памяти под дескриптор окружения приложение должно вызвать функцию SQLSetEnvAttr для задания значения атрибуту дескриптора окружения SQL_ATTR_ODBC_VERSION. Если не установить номер версии ODBC API, то при создании дескриптора соединения функция SQLAllocHandle вернет код ответа SQLSTATE равным HY010, что соответствует коду произошедшей ошибки.

После создания дескриптора окружения создаются дескрипторы соединений. Каждый дескриптор соединения формируется вызовом функции SQLAllocHandle с типом дескриптора, равным SQL_HANDLE_DBC. Драйвер выделяет память для хранения информации о соединении и возвращает значение дескриптора соединения. Далее для реального соединения с источником данных вызывается функция SQLConnect или функция SQLDriverConnect.

Для выполнения SQL-операторов создаются дескрипторы операторов. Они позволяют получить доступ к информации о выполненном операторе, имени курсора и атрибутах. Дескриптор оператора формируется вызовом функции SQLAllocHandle со значением типа дескриптора, равным SQL_HANDLE_STMT.

При создании дескриптора оператора драйвер автоматически создает еще четыре дескриптора и записывает указатели на них в атрибуты дескриптора оператора SQL_ATTR_APP_ROW_DESC, SQL_ATTR_APP_PARAM_DESC, SQL_ATTR_IMP_ROW_DESC и SQL_ATTR_IMP_PARAM_DESC. Эти четыре дескриптора называются неявно размещаемыми дескрипторами.

Для явного размещения дескриптора приложения следует вызвать функцию SQLAllocHandle со значением типа дескриптора, равным SQL_HANDLE_DESC. При этом формируется дескриптор, называемый явно размещаемым дескриптором.

Приложение может указать драйверу использовать явно размещенный дескриптор приложения вместо автоматически созданного для данного дескриптора оператора. Для этого следует вызвать функцию SQLSetStmtAttr с атрибутом SQL_ATTR_APP_ROW_DESC или SQL_ATTR_APP_PARAM_DESC.

Явно размещаемые дескрипторы также ассоциируются с дескриптором соединения: они остаются доступны только до тех пор, пока приложение имеет соединение с базой данных. Поскольку явно размещаемые дескрипторы ассоциируются с дескриптором соединения, то приложение может ассоциировать такие дескрипторы с несколькими дескрипторами операторов для данного соединения. Неявно размещаемый дескриптор приложения не может быть ассоциирован более чем с одним дескриптором оператора.

Если явно размещаемый дескриптор освобождается, то дескриптору оператора опять назначается неявно размещаемый дескриптор.

На следующей схеме отображена последовательность действий приложения-клиента для реализации доступа к источнику данных.


Последовательность действий приложения-клиента для реализации доступа к источнику данных



Соединение с источником данных


Для непосредственного подключения к базе данных ODBC API предоставляет следующие три функции:

SQLConnect - соединение с источником данных по DSN, имени и паролю пользователяSQLDriverConnect - соединение с источником данных по указанной строке соединения или при помощи отображаемого диалога для интерактивного ввода параметров соединения;SQLBrowseConnect - соединение с источником данных с предварительным последовательным запросом атрибутов соединения.

Функция SQLConnect имеет следующее формальное описание:

SQLRETURN SQLConnect( SQLHDBC ConnectionHandle, SQLCHAR * ServerName, SQLSMALLINT NameLength1, SQLCHAR * UserName, SQLSMALLINT NameLength2, SQLCHAR * Authentication, SQLSMALLINT NameLength3);

Параметр ConnectionHandle ([Input]) указывает используемый дескриптор соединения, параметр ServerName ([Input]) - имя источника данных. Параметры UserName ([Input]) и Authentication ([Input]) описывают имя пользователя и пароль, а параметры NameLength1 ([Input]), NameLength2 ([Input]) и NameLength3 ([Input]) определяют длину параметров *ServerName, *UserName и *Authentication соответственно.

Например:

SQLConnect(hdbc, (SQLCHAR*) "MySQLDB", SQL_NTS, (SQLCHAR*) "", SQL_NTS, (SQLCHAR*) "", SQL_NTS);

Для выполнения соединения с источкиком данных, требующим для подключения дополнительной информации, или отображения перед подключением диалога с уточнением значения параметров используется функция SQLDriverConnect, которая имеет следующее формальное описание:

SQLRETURN SQLDriverConnect( SQLHDBC ConnectionHandle, SQLHWND WindowHandle, SQLCHAR * InConnectionString, SQLSMALLINT StringLength1, SQLCHAR * OutConnectionString, SQLSMALLINT BufferLength, SQLSMALLINT * StringLength2Ptr, SQLUSMALLINT DriverCompletion);

Параметр ConnectionHandle ([Input]) указывает дескриптор соединения; параметр WindowHandle ([Input]) - это указатель родительского окна или NULL.

Параметр InConnectionString ([Input]) задает полностью или частично строку соединения или пустую строку, а параметр StringLength1 ([Input]) - длину в байтах строки *InConnectionString.
Строка соединения позволяет описывать атрибуты соединения в текстовом формате. Пары атрибут=значение разделяются между собой символом "точка с запятой".

Параметр OutConnectionString ([Output]) - это указатель на буфер, в котором после успешного подключения к источнику данных возвращается полная строка соединения. Размер этого буфера задается параметром BufferLength ([Input]) и должен быть не менее 1024 байт. Если вызывается функция SQLDriverConnectW и строка соединения указывается в символах Unicode, то размер буфера должен содержать четное число байтов.

Параметр StringLength2Ptr ([Output]) возвращает указатель на буфер, в котором размещается общее число символов полной строки соединения. Если размер буфера *OutConnectionString, указанный параметром BufferLength, меньше, чем требуется, то возвращаемая строка соединения усекается до размера буфера минус длина null-символа.

Параметр DriverCompletion ([Input]) - это флажок, указывающий, будет ли менеджер драйверов и драйвер предлагать диалоги для формирования завершенной строки соединения. Этот параметр определяется следующими значениями:

SQL_DRIVER_PROMPT - подсказка предлагается и учитывается даже в том случае, если значение атрибута уже задано в строке соединения. Первоначально отображается окно с доступными источниками данных (для атрибута DSN);SQL_DRIVER_COMPLETE - подсказка предлагается только в том случае, если требуемый для подключения атрибут не задан в строке соединения;SQL_DRIVER_COMPLETE_REQUIRED - подсказка предлагается только в том случае, если требуемый для подключения атрибут не задан в строке соединения и при этом запрашиваются только необходимые значения атрибутов;SQL_DRIVER_NOPROMPT - подсказки не предлагаются.

Строка соединения, передаваемая в качестве параметра, имеет следующее формальное описание:

строка_соединения ::= пустая_строка[;] | атрибут[;] | атрибут; строка_соединения атрибут ::= ключевое_слово =значение | DRIVER=[{]значение [}] ключевое слово ::= DSN | UID | PWD | идентификатор_используемый_драйвером



Набор ключевых слов, указываемых в строке соединения, частично зависит от используемого драйвера. К общепринятым ключевым словам относятся следующие:

DSN - имя источника данных (функция SQLDataSources возвращает список доступных источников данных);FILEDSN - имя .dsn файла, из которого будет прочитана строка соединения;DRIVER - описание драйвера (список доступных драйверов возвращается функцией SQLDrivers);UID - идентификатор пользователя;PWD - для указанного идентификатора пользователя или при отсутствии пароля пустая строка (PWD=;);SAVEFILE - имя .dsn файла, в который будет записана строка соединения, используемая для данного успешного подключения к источнику данных.

Ключевые слова DSN и FILEDSN являются в строке соединения взаимоисключающими: будет использовано первое из указанных. С остальными ключевыми словами FILEDSN не является взаимоисключающим: приоритет имеет значение, указанное непосредственно в строке состояния. Очевидно, что значение ключевого слова PWD не сохраняется в .dsn файле.

По умолчанию, каталогом для сохранения и загрузки .dsn файла будет комбинация пути, указанного как CommonFileDir в разделе реестра Windows HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\ Windows\CurrentVersion, и пути ODBC\DataSources.

На рис. 3.1 отображен соответствующий раздел реестра Windows (в данном примере .dsn файлы будут сохраняться в каталоге C:\Program Files\Common Files\ODBC\Data Sources).


Рис. 3.1.  Параметр CommonFileDir из реестра Windows

Функция SQLBrowseConnect реализует итерационный метод запроса значений атрибутов, требуемых для подключения к базе данных, возвращая каждый раз код ответа SQL_NEED_DATA и идентификатор очередного запрашиваемого атрибута. После определения значений всех необходимых атрибутов функция устанавливает соединение с базой данных и при успешном завершении операции возвращает код ответа, равный SQL_SUCCESS или SQL_SUCCESS_WITH_INFO.

Функция SQLBrowseConnect имеет следующее формальное описание:

SQLRETURN SQLBrowseConnect( SQLHDBC ConnectionHandle, SQLCHAR * InConnectionString, SQLSMALLINT StringLength1, SQLCHAR * OutConnectionString, SQLSMALLINT BufferLength, SQLSMALLINT * StringLength2Ptr);



Параметр ConnectionHandle ([Input]) определяет дескриптор соединения, параметр InConnectionString ([Input]) описывает строку подключения или ее часть, указанную при предыдущем вызове функции.

Параметр StringLength1 ([Input]) задает длину буфера*InConnectionString.

Параметр OutConnectionString ([Output]) определяет указатель на буфер, содержащий информацию о недостающем атрибуте строки соединения (например, в буфере может быть возвращено следующее значение: "HOST:Server={MySR1,S2,S3};UID:ID=?;PWD:Password=?").

Параметр BufferLength ([Input]) задает длину буфера*OutConnectionString.

Параметр StringLength2Ptr ([Output]) указывает общее число байтов, которое должно быть возвращено в буфере *OutConnectionString. Если размер этого буфера меньше, чем требуется, то возвращаемое значение будет усечено.

Например:

retcode = SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &henv) if (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO) { retcode = SQLSetEnvAttr(henv, SQL_ATTR_ODBC_VERSION, SQL_OV_ODBC3, 0); if (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO) { /* Создание дескриптора соединения */ retcode = SQLAllocHandle(SQL_HANDLE_DBC, henv, &hdbc); if (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO) { /* Вызов SQLBrowseConnect до тех пор пока*/ /*код ответа будет SQL_NEED_DATA */

lstrcpy(szConnStrIn, "DSN=MeDB"); do { retcode = SQLBrowseConnect(hdbc, szConnStrIn, SQL_NTS, szConnStrOut, BRWS_LEN, &cbConnStrOut); if (retcode == SQL_NEED_DATA) /* Вызов функции пользователя, возвращающей запрашиваемое значение атрибута*/ GetValueAttr(szConnStrOut, szConnStrIn); } while (retcode == SQL_NEED_DATA);

if (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO){

/* Соединение установлено и можно формировать дескриптор оператора. */ retcode = SQLAllocHandle(SQL_HANDLE_STMT, hdbc, &hstmt); } // ... SQLFreeHandle(SQL_HANDLE_STMT, hstmt); } SQLDisconnect(hdbc); } } SQLFreeHandle(SQL_HANDLE_DBC, hdbc); } } SQLFreeHandle(SQL_HANDLE_ENV, henv);


Обработка результирующего набора


Результирующий набор формируется в выделяемой области памяти. Для того чтобы использовать данные, записанные в результирующий набор, их следует извлечь из заданной области памяти в переменные используемого языка программирования.

Извлечение данных из результирующего набора в переменные может быть выполнено:

вызовом функции SQLFetch или SQLFetchScroll;вызовом функции SQLGetData.

Функции SQLFetch или SQLFetchScroll всегда выполняют продвижение курсора на следующую запись. При этом функция SQLFetch, реализует механизм "однонаправленного курсора", а функция SQLFetchScroll позволяет, в зависимости от используемого источника данных, реализовывать механизм "двунаправленного курсора" и механизм "прямой выборки".

Функции SQLFetch или SQLFetchScroll выполняют одновременное извлечение данных только в том случае, если поля результирующего набора предварительно были связаны с переменными вызовом функции SQLBindCol для каждого связываемого поля.

Функция SQLFetch имеет следующее формальное описание:

SQLRETURN SQLFetch( SQLHSTMT StatementHandle);

Параметр StatementHandle ([Input]) указывает дескриптор оператора.

Функция SQLGetData имеет следующее формальное описание:

SQLRETURN SQLGetData( SQLHSTMT StatementHandle, SQLUSMALLINT ColumnNumber, SQLSMALLINT TargetType, SQLPOINTER TargetValuePtr, SQLINTEGER BufferLength, SQLINTEGER * StrLen_or_IndPtr);

Параметр StatementHandle ([Input]) указывает дескриптор оператора.

Параметр ColumnNumber ([Input]) указывает номер связываемого столбца результирующего набора (начиная с 1). По умолчанию столбец номер 0 является столбцом маркера строки, в том случае, если маркеры доступны.

Параметр TargetType ([Input]) определяет C-тип данных для буфера*TargetValuePtr в соответствии со следующей таблицей.

Идентификатор C-типа ODBC C typedefТип C
SQL_C_CHARSQLCHAR *unsigned char *
SQL_C_SSHORTSQLSMALLINTShort int
SQL_C_USHORTSQLUSMALLINTunsigned short int
SQL_C_SLONGSQLINTEGERlong int
SQL_C_ULONGSQLUINTEGERunsigned long int
SQL_C_FLOATSQLREALfloat
SQL_C_DOUBLESQLDOUBLE, SQLFLOATdouble
SQL_C_BITSQLCHARunsigned char
SQL_C_STINYINTSQLSCHARSigned char
SQL_C_UTINYINTSQLCHARunsigned char
SQL_C_SBIGINTSQLBIGINT_int64
SQL_C_UBIGINTSQLUBIGINTunsigned _int64
SQL_C_BINARYSQLCHAR *unsigned char *
SQL_C_BOOKMARKBOOKMARKunsigned long int
SQL_C_VARBOOKMARKSQLCHAR *unsigned char *
SQL_C_TYPE_DATESQL_DATE_STRUCTstruct tagDATE_STRUCT { SQLSMALLINT year; SQLUSMALLINT month; SQLUSMALLINT day; } DATE_STRUCT;
SQL_C_TYPE_TIMESQL_TIME_STRUCTstruct tagTIME_STRUCT { SQLUSMALLINT hour; SQLUSMALLINT minute; SQLUSMALLINT second; } TIME_STRUCT;
SQL_C_TYPE_TIMESTAMPSQL_TIMESTAMP_STRUCTstruct tagTIMESTAMP_STRUCT { SQLSMALLINT year; SQLUSMALLINT month; SQLUSMALLINT day; SQLUSMALLINT hour; SQLUSMALLINT minute; SQLUSMALLINT second; SQLUINTEGER fraction; } TIMESTAMP_STRUCT;
SQL_C_NUMERICSQL_NUMERIC_STRUCTStruct tagSQL_NUMERIC_STRUCT { SQLCHAR precision; SQLSCHAR scale; SQLCHAR sign[g]; SQLCHAR val[SQL_MAX_NUMERIC_LEN]; } SQL_NUMERIC_STRUCT;
SQL_C_GUIDSQLGUIDstruct tagSQLGUID { DWORD Data1; WORD Data2; WORD Data3; BYTE Data4[8]; } SQLGUID;
Все интервальные C типы данныхSSQL_INTERVAL_STRUCTtypedef struct tagSQL_INTERVAL_STRUCT { SQLINTERVAL interval_type; SQLSMALLINT interval_sign; union { SQL_YEAR_MONTH_STRUCT year_month; SQL_DAY_SECOND_STRUCT day_second; } intval; } SQL_INTERVAL_STRUCT; typedef enum {

SQL_IS_YEAR = 1, SQL_IS_MONTH = 2, SQL_IS_DAY = 3, SQL_IS_HOUR = 4, SQL_IS_MINUTE = 5, SQL_IS_SECOND = 6, SQL_IS_YEAR_TO_MONTH = 7, SQL_IS_DAY_TO_HOUR = 8, SQL_IS_DAY_TO_MINUTE = 9, SQL_IS_DAY_TO_SECOND = 10, SQL_IS_HOUR_TO_MINUTE = 11, SQL_IS_HOUR_TO_SECOND = 12, SQL_IS_MINUTE_TO_SECOND = 13 } SQLINTERVAL; typedef struct tagSQL_YEAR_MONTH { SQLUINTEGER year; SQLUINTEGER month; } SQL_YEAR_MONTH_STRUCT; typedef struct tagSQL_DAY_SECOND { SQLUINTEGER day; SQLUINTEGER hour; SQLUINTEGER minute; SQLUINTEGER second; SQLUINTEGER fraction; } SQL_DAY_SECOND_STRUCT;

<
Например:

SQLCHAR ValuePtr[50]; SQLINTEGER ValueLenOrInd; SQLGetData(hstmt, 1, SQL_C_CHAR, ValuePtr, sizeof(ValuePtr), &ValueLenOrInd);

В параметре TargetType также можно указывать идентификатор типа SQL_C_DEFAULT: в этом случае драйвер самостоятельно выбирает тип данных С, основываясь на типе данных поля источника данных.

Параметр TargetValuePtr ([Output]) определяет буфер, в который выполняется извлечение данных, а параметр ([Input]) определяет размер этого буфера в байтах. Для данных, имеющих фиксированную длину, таких как целочисленные значения, драйвер игнорирует значение параметра BufferLength.

Параметр StrLen_or_IndPtr ([Output]) определяет буфер, в котором возвращается размер данных или индикатор и может содержать следующие значения:

размер данных в байтах; SQL_NO_TOTAL - индикатор, указывающий, что размер не может быть определен;SQL_NULL_DATA - индикатор, указывающий, что данные имеют значение NULL.

Следующий пример иллюстрирует применение механизма извлечения данных при помощи функции SQLGetData.

//OBDC_Connect.cpp #include "stdafx.h" #include "Test_ODBC_connect.h" #include <iostream>

#ifdef _DEBUG #define new DEBUG_NEW #endif CWinApp theApp; // Объявление объекта приложения using namespace std; int _tmain(int argc, TCHAR* argv[], TCHAR* envp[]) { int nRetCode = 0; if (!AfxWinInit(::GetModuleHandle(NULL), NULL, ::GetCommandLine(), 0)) { _tprintf(_T("Ошибка инициализации MFC\n")); nRetCode = 1; } else { std::cout<<"Begin"<<std::endl; SQLHENV henv; // Дескриптор окружения SQLHDBC hdbc; // Дескриптор соединения SQLHSTMT hstmt; // Дескриптор оператора SQLRETURN retcode; // Код возврата /*Инициализация дескриптора окружения */ retcode = SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &henv); if (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO) { std::cout<<"SQLAllocHandle создан успешно"<<std::endl; /* Определение версии ODBC */ retcode = SQLSetEnvAttr(henv, SQL_ATTR_ODBC_VERSION, (void*)SQL_OV_ODBC3, 0); if (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO) { /* Инициализация дескриптора соединения */ retcode = SQLAllocHandle(SQL_HANDLE_DBC, henv, &hdbc); if (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO) { std::cout<<"SQLAllocHandle создан успешно "<<std::endl; retcode = SQLConnect(hdbc, (SQLCHAR*) "MySQLDB", SQL_NTS, (SQLCHAR*) "", SQL_NTS, (SQLCHAR*) "", SQL_NTS); if (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO){ /* Инициализация дескриптора оператора */ retcode = SQLAllocHandle(SQL_HANDLE_STMT, hdbc, &hstmt); SQLCHAR sqf3[50]; SQLINTEGER sqf1,sqf2,sbf1,sbf2,sbf3; SQLCHAR selecttxt[] ="SELECT f1, f2, f3 FROM tbl1"; /* Создание результирующего набора */ retcode = SQLExecDirect(hstmt, selecttxt, SQL_NTS); if (retcode == SQL_SUCCESS) { while (TRUE) { /* Выборка данных */ retcode = SQLFetch(hstmt); if (retcode == SQL_ERROR || retcode == SQL_SUCCESS_WITH_INFO) { } if (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO){ /* Извлечение данных трех полей результирующего набора */ SQLGetData(hstmt, 1, SQL_C_ULONG, &sqf1, 0, &sbf1); SQLGetData(hstmt, 2, SQL_C_ULONG, &sqf2, 0, &sbf2); SQLGetData(hstmt, 3, SQL_C_CHAR, sqf3, 50, &sbf3); /* Запись в поток вывода строк результирующего набора */ std::cout<< "1: "<<sqf1<<" 2: "<<sqf2<<" 3: "<< sqf3<<" "<<std::endl; } else { break; } } } /* Освобождение дескрипторов */ if (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO) { SQLFreeHandle(SQL_HANDLE_STMT, hstmt); } SQLDisconnect(hdbc); } SQLFreeHandle(SQL_HANDLE_DBC, hdbc); } } SQLFreeHandle(SQL_HANDLE_ENV, henv); } return nRetCode; } }

//OBDC_Connect.h #pragma once #include "resource.h" #ifndef _AFX_NOFORCE_LIBS //////////////////////////////////////////////////////////////// // Win32 библиотеки #pragma comment(lib, "odbc32.lib") #pragma comment(lib, "odbccp32.lib") #endif //!_AFX_NOFORCE_LIBS #ifndef __SQL #include <sql.h> // ядро #endif #ifndef __SQLEXT #include <sqlext.h> // расширение #endif


Применение ESCAPE-последовательностей


Escape-последовательности позволяют передавать значения даты и времени, скалярных функций и функций даты/времени, строки в предикате LIKE, внешних соединений, процедурных вызовов, одинаково определяемых в стандарте, в различные источники данных. ODBC-драйвер в зависимости от используемого источника данных приводит передаваемый SQL-оператор в соответствующую форму, заменяя значение escape-последовательности. Преобразованный ODBC-драйвером текст SQL-оператора можно посмотреть, вызвав метод SQLNativeSql.

Escape-последовательность указывается в фигурных скобках.

Для определения значений даты и времени используется следующая форма escape-последовательности:

{тип_значения 'значение'}.

Тип значения указывается следующими символами:

d - для типа Date формата yyyy-mm-ddt - для типа Time формата hh:mm:ssts - для типа Timestamp формата yyyy-mm-dd hh:mm:ss[.f...]

Следующий пример иллюстрирует два способа определения даты: в первом случае с применением escape-последовательности, а во втором - с использованием естественного синтаксиса Oracle. При этом код, использующий естественный синтаксис Oracle, не является интероперабельным.

Пример:

UPDATE tbl1 SET OpenDate= {d '2004-01-24'} WHERE FieldID=1010 UPDATE tbl1 SET OpenDate= '24-Jan-2004' WHERE FieldID=1010

Escape-последовательность может передаваться как значение параметра SQL-оператора. Применение параметра позволяет в последующих реализациях быстро переходить от значения в виде escape-последовательности ("{d '2004-01-24'}") к значению, использующему естественное обозначение в соответствии с конкретным драйвером ("24-Jan-2004").

Например:

SQLCHAR Date1[56]; // Размер даты равен 55 SQLINTEGER Date1LenOrInd = SQL_NTS; // Определение параметров SQLBindParameter(hstmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_TYPE_DATE, 0, 0, Date1, sizeof(Date1), &Date1LenOrInd); // Задание значения переменной Date1 как // escape-последовательности. strcpy(Date1, "{d '2004-01-24'}"); // Выполнение SQL-оператора SQLExecDirect(hstmt, "UPDATE tbl1 SET Date1=? WHERE FieldID = 1010", SQL_NTS);


Другим способом определения значения даты посредством параметра является применение структуры SQL_DATE_STRUCT. В большинстве случаев этот способ бывает более эффективен.

Например:

SQL_DATE_STRUCT Date1; SQLINTEGER Date1Ind = 0; // Определение параметра SQLBindParameter(hstmt, 1, SQL_PARAM_INPUT, SQL_C_TYPE_DATE, SQL_TYPE_DATE, 0, 0, &Date1, 0, &Date1Len); // Определение полей структуры Date1 Date1.year = 2004; Date1.month = 1; Date1.day = 24; // Выполнение SQL-оператора SQLExecDirect(hstmt, "UPDATE tbl2 SET Date1=? WHERE FieldID = 1010", SQL_NTS);

Для определения того, поддерживает ли конкретный ODBC-драйвер escape-последовательности для представления значений типа даты и времени, применяется функция SQLGetTypeInfo (если источник данных поддерживает типы данных для даты и времени, то он также должен поддерживать и соответствующие escape-последовательности).

Для определения того, поддерживает ли конкретный ODBC-драйвер представление значений даты и времени в виде, определяемом спецификацией ANSI SQL-92, применяется функция SQLGetInfo с опцией SQL_ANSI_SQL_DATETIME_LITERALS.


Схема извлечения данных с использованием ODBC API


Для извлечения данных с использованием ODBC API сначала следует вызвать функцию, выполняющую SQL-оператор, который определяет формируемый результирующий набор. И только затем можно приступать к выборке данных.

ODBC API предоставляет два способа извлечения данных из результирующего набора:

с предварительным связыванием полей результирующего набора с переменными основного языка программирования;прямая выборка каждого поля результирующего набора в указываемую переменную основного языка программирования.



Связывание данных


Функция, выполняющая предварительное связывание данных, имеет следующее формальное описание:

SQLRETURN SQLBindCol( SQLHSTMT StatementHandle, SQLUSMALLINT ColumnNumber, SQLSMALLINT TargetType, SQLPOINTER TargetValuePtr, SQLINTEGER BufferLength, SQLLEN * StrLen_or_Ind);

Значения параметров функции SQLBindCol аналогичны значениям параметров функции SQLGetData. Но функция SQLBindCol указывается только один раз для каждого поля, а затем выборка данных выполняется автоматически при вызове функции SQLFetch. А при отсутствии связывания функция SQLGetData должна вызываться каждый раз для каждого поля после выполнения функции SQLFetch.

Функция SQLBindCol может выполнять предварительное связывание для последующего извлечения данных функциями SQLFetch, SQLFetchScroll, SQLBulkOperations и SQLSetPos.

Например:

#define NAME_LEN 50 #define PHONE_LEN 10

SQLCHAR szName[20], szPhone[15]; SQLINTEGER sID, cbName, cbID, cbPhone; SQLHSTMT hstmt; SQLRETURN retcode;

retcode = SQLExecDirect(hstmt, "SELECT CID, NAME, PHONE FROM TBL1 ORDER BY 2, 1, 3", SQL_NTS); if (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO) { /* Связывание столбцов 1, 2 и 3 */ SQLBindCol(hstmt, 1, SQL_C_ULONG, &sCID, 0, &cbCID); SQLBindCol(hstmt, 2, SQL_C_CHAR, szName, 20, &cbName); SQLBindCol(hstmt, 3, SQL_C_CHAR, szPhone, 15, &cbPhone); while (TRUE) { retcode = SQLFetch(hstmt); if (retcode == SQL_ERROR || retcode == SQL_SUCCESS_WITH_INFO) { show_error(); } if (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO){ // Значения полей успешно извлечены из // результирующего набора } else { break; } } }



Выполнение SQL-оператора


Результирующий набор создается при выполнении SQL-оператора SELECT. Для выполнения любого SQL-оператора первоначально должен быть создан дескриптор оператора.

Например:

SQLAllocHandle(SQL_HANDLE_STMT, hdbc, &hstmt);

Второй параметр указывает дескриптор соединения, для которого создается дескриптор оператора, а третий параметр определяет область памяти, выделяемую под дескриптор оператора.

Для выполнения запроса ODBC API предоставляет следующие две функции:

SQLExecute - выполняет откомпилированный SQL-оператор;SQLExecDirect - выполняет SQL-оператор, указываемый параметром.

Функция SQLExecDirect реализует одношаговый интерфейс, при котором процесс компиляции SQL-оператора и его выполнение осуществляется единожды при вызове данной функции.

Функции SQLPrepare и SQLExecute реализуют многошаговый интерфейс: сначала выполняется компиляция оператора и строится план выполнения, а затем возможно многократное выполнение подготовленного оператора (например, с различными значениями параметров).

Функция SQLExecDirect имеет следующее формальное описание:

SQLRETURN SQLExecDirect( SQLHSTMT StatementHandle, SQLCHAR * StatementText, SQLINTEGER TextLength);

Параметр StatementHandle ([Input]) указывает дескриптор оператора, параметр StatementText ([Input]) определяет выполняемый SQL-оператор, TextLength (Input]) - длина строки *StatementText.

Функция SQLExecDirect возвращает одно из следующих значений: SQL_SUCCESS, SQL_SUCCESS_WITH_INFO, SQL_NEED_DATA, SQL_STILL_EXECUTING, SQL_ERROR, SQL_NO_DATA, SQL_INVALID_HANDLE.

Функция SQLExecDirect через ODBC-драйвер передает SQL-оператор источнику данных. При этом драйвер может изменить передаваемый SQL-оператор, приводя его к форме, соответствующей используемому источнику данных - например, изменяя передаваемые escape-последовательности (при использовании значений даты и времени, скалярных функций и функций даты/времени, предиката LIKE, внешних соединений, процедурных вызовов).

При выполнении SQL-оператора SELECT с вызовом функции SQLSetCursorName драйвер использует заданное этой функцией имя курсора.
Если имя курсора явно не указывается, то драйвер самостоятельно формирует имя курсора для оператора SELECT.

Если режим работы с источником данных не установлен как автокоммит, а перед выполнением SQL-оператора транзакция не была инициализирована явным образом, то драйвер самостоятельно выполняет открытие транзакции.

Если приложение использует функцию SQLExecDirect для выполнения SQL-операторов COMMIT или ROLLBACK, то оно не будет интероперабельным между различными СУБД. Для достижения полной переносимости разрабатываемого приложения работы с базами данных следует для завершения или отката транзакции вызывать функцию ODBC API SQLEndTran.

При выполнении SQL-оператора, содержащего параметры, их значения предварительно должны быть определены вызовом функций SQLParamData и SQLPutData или SQLBindParameter. В противном случае при выполнении функции SQLExecDirect будет возвращено значение SQL_NO_DATA.

При многошаговом интерфейсе первой должна быть выполнена функция SQLPrepare, инициирующая компиляцию SQL-оператора.

Функция SQLPrepare имеет следующее формальное описание:

SQLRETURN SQLPrepare( SQLHSTMT StatementHandle, SQLCHAR * StatementText, SQLINTEGER TextLength);

Параметр StatementHandle ([Input]) указывает дескриптор оператора, параметр StatementText ([Input]) определяет текст компилируемого SQL-оператора, TextLength ([Input]) - это длина строки *StatementText.

Функция SQLExecute выполняет откомпилированный SQL-оператор. Если выполняемым SQL-оператором был оператор SELECT, то в результате выполнения SQLExecute создается результирующий набор.

Функция SQLExecute имеет следующее формальное описание:

SQLRETURN SQLExecute( SQLHSTMT StatementHandle);

Параметр StatementHandle ([Input]) указывает дескриптор оператора

Например:

SQLHSTMT hstmtS, hstmtU; SQLExecDirect(hstmtS, "SELECT F1, F2 FROM TBL1 ", SQL_NTS); SQLPrepare(hstmtU, "UPDATE TBL1 SET F2=F2*1.3 WHERE F1=1010", SQL_NTS); SQLExecute(hstmtU);


Именованные параметры


Именованные параметры могут использоваться в том случае, когда в SQL-операторе выполняется вызов хранимой процедуры. Именованные параметры идентифицируются в соответствии с их именами, а не по порядку их расположения (как при позиционных параметрах). Именованные параметры, так же как и позиционные параметры, связываются с переменной посредством вызова функции ODBC API SQLBindParameter, но идентифицируются посредством поля SQL_DESC_NAME IPD-дескриптора (Implementation Parameter Descriptor). Именованные параметры также могут быть связаны с переменной при вызове функции SQLSetDescField или функции SQLSetDescRec.

Далее приведен пример применения именованных параметров, используемых при вызове предварительно созданной хранимой процедуры test с двумя параметрами

(CREATE PROCEDURE test @f_id int = 1, @f2 char(30) AS )

Первый параметр процедуры имеет значение, по умолчанию равное 1, а второй параметр @f2 обязательно должен быть указан при вызове процедуры. Параметр @f2 - это динамический параметр, называемый именованным параметром.

// Компилирование вызова хранимой процедуры SQLPrepare(hstmt, "{call test(?)}", SQL_NTS); // Заполнение записи 1 для IPD-дескриптора SQLBindParameter(hstmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, 30, 0, szF2, 0, &cbValue); // Получение IPD-дескриптора и определение полей // SQL_DESC_NAMED и SQL_DESC_UNNAMED для записи #1. SQLGetStmtAttr(hstmt, SQL_ATTR_IMP_PARAM_DESC, &hIpd, 0, 0); SQLSetDescField(hIpd, 1, SQL_DESC_NAME, "@f2", SQL_NTS); SQLSetDescField(hIpd, 1, SQL_DESC_UNNAMED, SQL_NAMED, 0); // Если переменная szF2 была корректно // инициализирована, то можно выполнять // вызов хранимой процедуры SQLExecute(hstmt);



Массивы параметров


Если значение атрибута дескриптора оператора SQL_ATTR_PARAMSET_SIZE больше 1 и SQL-оператор имеет хотя бы один маркер параметра, то функция SQLExecDirect будет выполняться последовательно для каждого значения параметра из массива, указанного параметром ParameterValuePointer функции SQLBindParameter.



Определение параметров


Выполняемый SQL-оператор может содержать несколько параметров.

Для использования параметра его первоначально следует определить (выполнить связывание параметра). Определение параметра заключается в указании типов для значения (С-тип) и для поля таблицы (SQL-тип), а также для указания буфера, в котором будет содержаться значение параметра, или номера параметра - для его последующего запроса во время выполнения. Определение параметра реализуется функцией SQLBindParameter (вместо функции SQLSetParam в ODBC 1.0).

Функция SQLBindParameter имеет следующее формальное описание:

SQLRETURN SQLBindParameter( SQLHSTMT StatementHandle, SQLUSMALLINT ParameterNumber, SQLSMALLINT InputOutputType, SQLSMALLINT ValueType, SQLSMALLINT ParameterType, SQLUINTEGER ColumnSize, SQLSMALLINT DecimalDigits, SQLPOINTER ParameterValuePtr, SQLINTEGER BufferLength, SQLINTEGER * StrLen_or_IndPtr);

Параметр StatementHandle ([Input]) указывает дескриптор оператора, параметр ParameterNumber ([Input]) задает номер параметра (по мере их вхождения в SQL-оператор), начиная с 1.

Параметр InputOutputType ([Input]) определяет тип параметра.

Параметр ValueType ([Input]) определяет тип значения переменной (С-тип), из которой будет извлекаться значение, передаваемое в базу данных, а параметр ParameterType ([Input]) указывает тип параметра (SQL-тип поля таблицы базы данных).

Параметр ColumnSize ([Input]) определяет размер столбца или указывает выражение, соответствующее маркеру параметра, а параметр DecimalDigits ([Input]) определяет количество десятичных знаков в столбце или указывает выражение, соответствующее маркеру параметра. Получить размер столбца и количество десятичных знаков в столбце можно при помощи функции ODBC API SQLDescribeCol.

Параметр ParameterValuePtr ([Deferred Input]) является указателем на буфер для данных, передаваемых в качестве параметра. Длина этого буфера определяется параметром BufferLength ([Input/Output]), а указатель на буфер для длины параметра задается параметром StrLen_or_IndPtr ([Deferred Input]). Функция SQLBindParameter может возвращать следующие значения: SQL_SUCCESS, SQL_SUCCESS_WITH_INFO, SQL_ERROR, or SQL_INVALID_HANDLE.



Параметры SQL-оператора


Параметром называется переменная, используемая в SQL-операторе.

Применение параметров позволяет формировать SQL-операторы непосредственно во время выполнения приложения. Так, при использовании многошагового интерфейса, при котором процесс компиляции и выполнения SQL-оператора происходит как последовательные действия, достаточно откомпилировать SQL-оператор с параметрами вместо явного указания значений полей таблицы базы данных, а затем многократно выполнять данный оператор с различными значениями параметров.

Например:

INSERT INTO TBL1 (F_ID, F2, F3) VALUES (?, ?, ?)

Параметры могут быть именованными и позиционными.

Позиционные параметры указываются символом вопросительный знак (?), называемым маркером параметров. При выполнении оператора вместо параметра в соответствующую позицию SQL-оператора приставляется значение параметра.

Согласно спецификации языка SQL маркеры параметров нельзя размещать в следующих местах SQL-оператора:

в списке полей оператора SELECT;одновременно как оба операнда для бинарного оператора (например, оператора =), так как на этапе компиляции нельзя определить тип операндов, выступающих в роли параметров;одновременно как первый и второй операнд для оператора BETWEEN (например, вместо SQL-оператора SELECT title_id, f_sales FROM tbl1 WHERE f_sales BETWEEN 4095 AND 12000 нельзя записать SELECT title_id, f_sales FROM tbl1 WHERE ? BETWEEN ? AND 12000 );одновременно как первый и третий операнд для оператора BETWEEN;одновременно как первый операнд и второй операнд для оператора IN (например, вместо SQL-оператора SELECT f_ID, f_state FROM tbl1 WHERE f_state IN ('CA', 'CB')

нельзя записать SELECT f_ID, f_state FROM tbl1 WHERE ? IN (?, ?) );как операнд унарного оператора + или -.



Передача параметров


Если перед выполнением функции SQLExecDirect значения параметров, используемых в запросе, не переданы на сервер, то функция возвращает код ответа SQL_NEED_DATA. Для передачи параметров в приложении могут использоваться функции SQLParamData и SQLPutData. Функция SQLParamData используется совместно с функцией SQLPutData для передачи значений параметров во время выполнения. Если функция SQLParamData возвращает значение SQL_NEED_DATA, то она также возвращает и номер параметра, для которого следует ввести значение. Передача значения параметра выполняется функцией SQLPutData.

Функция SQLParamData имеет следующее формальное описание:

SQLRETURN SQLParamData( SQLHSTMT StatementHandle, SQLPOINTER * ValuePtrPtr);

Параметр StatementHandle ([Input]) указывает дескриптор оператора, а параметр ValuePtrPtr ([Output]) указывает буфер, который был предназначен для хранения параметра функцией SQLBindParameter (указывается как значение параметра ParameterValuePtr).

Функция SQLPutData позволяет приложению передавать данные параметра или столбца во время выполнения. При этом данные могут передаваться по частям.

Функция SQLPutData имеет следующее формальное описание:

SQLRETURN SQLPutData( SQLHSTMT StatementHandle, SQLPOINTER DataPtr, SQLINTEGER StrLen_or_Ind);

Параметр StatementHandle ([Input]) указывает дескриптор оператора, параметр DataPtr ([Input]) определяет указатель буфера, в котором размещается значение параметра или столбец (значение соответствующего C-типа, указанного параметром ValueType функции SQLBindParameter или параметром TargetType функции SQLBindCol), а параметр StrLen_or_Ind ([Input]) определяет длину передаваемых данных *DataPtr.

Следующий пример иллюстрирует применение функций OBDC API для передачи параметров, используемых оператором INSERT. Этот оператор содержит два параметра - для полей F1_ID и F2_PIC. Для каждого параметра приложение вызывает метод SQLBindParameter, определяющий С-тип данных и SQL-тип поля.

#define MAX_DATA_LEN 1024 SQLINTEGER cbF1_ID = 0, cbF2_PICParam, cbData; SQLUINTEGER sF1_ID; szPhotoFile; SQLPOINTER pToken, InitValue; SQLCHAR Data[MAX_DATA_LEN]; SQLRETURN retcode; SQLHSTMT hstmt; // Компиляция параметрического запроса retcode = SQLPrepare(hstmt, "INSERT INTO TBL1 (F1_ID, F2_PIC) VALUES (?, ?)", SQL_NTS); if (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO) {


// Выполнение связывания параметров. // Для 2 параметра передаем в ParameterValuePtr // вместо адреса буфера номер параметра SQLBindParameter(hstmt, 1, // Для 1 параметра SQL_PARAM_INPUT, SQL_C_ULONG, SQL_INTEGER, 0, 0, &sF1_ID, 0, &cbF1_ID); SQLBindParameter(hstmt, 2, // Для 2 параметра SQL_PARAM_INPUT, SQL_C_BINARY, SQL_LONGVARBINARY, 0, 0, (SQLPOINTER) 2, // Передаваемый номер 0, &cbF2_PICParam);

// Значения второго параметра будут передаваться // во время выполнения. // Длина параметра в макросе SQL_LEN_DATA_AT_EXEC // равна 0. Это предполагает, что драйвер вернет // значение "N" для типа SQL_NEED_LONG_DATA_LEN, // указанного в функции SQLGetInfo.

cbF2_PICParam = SQL_LEN_DATA_AT_EXEC(0);

sF1_ID = MyGetNextID(); /* Запрос следующего значения поля F1_ID таблицы TBL1 */

retcode = SQLExecute(hstmt);

/* Для параметров времени выполнения (data-at-execution parameters) вызывается функция SQLParamData для получения номера параметра, установленного функцией SQLBindParameter. */ /* Вспомогательные функции MyInitData и MyGetData формируют значение параметра*/ /* Функция SQLParamData завершает процесс обработки параметра */

while (retcode == SQL_NEED_DATA) { retcode = SQLParamData(hstmt, &pToken); // Запрос // значений параметров во время выполнения if (retcode == SQL_NEED_DATA) { MyInitData((SQLSMALLINT)pToken, InitValue); while (MyGetData(InitValue, (SQLSMALLINT)pToken, Data, &cbData)) SQLPutData(hstmt, Data, cbData); } } }

VOID MyInitData (sParam, InitValue) SQLPOINTER InitValue; { SQLCHAR szPhotoFile[MAX_FILE_NAME_LEN];

/* Запрос у пользователя имени используемого BMP-файла, открытие этого файла и возвращения дескриптора файла */

MyPromptPhotoFileName(szPhotoFile); MyOpenBMPFile(szPhotoFile, (FILE *)InitValue); break; }

BOOL MyGetData (InitValue, sParam, Data, cbData) SQLPOINTER InitValue; SQLCHAR * Data; SQLINTEGER * cbData; BOOL Done;

{ /* Функция MyGetNextBMPData возвращает следующую часть данных и количество передаваемых байтов (не более, чем MAX_DATA_LEN). */ Done = MyGetNextBMPData((FILE *)InitValue, Data, MAX_DATA_LEN, &cbData); if (Done) { MyCloseBMPFile((FILE *)InitValue); return (TRUE); } return (FALSE); }


Изменение позиции курсора


При использовании перемещаемого курсора после создания результирующего набора позицию курсора можно перемещать. Это можно выполнять непосредственно функцией выборки данных SQLFetchScroll или функцией перемещения курсора SQLSetPos.

Функция SQLSetPos, кроме изменения позиции курсора в результирующем наборе и обновления данных, также позволяет приложению определять набор строк результирующего набора для изменения или удаления данных.

Для изменения или удаления любой строки из набора строк можно использовать функцию SQLSetPos. Применение функции SQLSetPos является удобной альтернативой выполнения отдельного SQL-оператора. Так, это позволяет ODBC-драйверу поддерживать позиционированное изменение даже в том случае, если сам источник данных не поддерживает позиционированный UPDATE в SQL-операторе.

Функция SQLSetPos оперирует с текущим набором строк (rowset), который создается после вызова функции SQLFetchScroll, выполняющей извлечение этого набора строк из результирующего набора, сформированного, в свою очередь, при выполнении SQL-оператора.

Функция SQLSetPos имеет следующее формальное описание:

SQLSetPos( SQLHSTMT StatementHandle, SQLUSMALLINT RowNumber, SQLUSMALLINT Operation, SQLUSMALLINT LockType);

Параметр StatementHandle ([Input]) указывает дескриптор оператора. Параметр RowNumber ([Input]) определяет позицию строки в наборе строк, над которой выполняется операция, указываемая параметром Operation. Если значение параметра RowNumber равно 0, то операция будет выполняться над каждой строкой набора строк (которая отмечена соответствующим образом в массиве операций над строками).

Параметр Operation ([Input]) определяет тип выполняемой операции и указывается следующими значениями:

SQL_POSITION SQL_REFRESH SQL_UPDATE SQL_DELETE

Отметим, что значение параметра Operation, равное SQL_ADD, начиная с версии ODBC 3.x, отменено. Однако драйверы ODBC 3.x в целях обратной совместимости поддерживают эту возможность, заменяя вызов функции SQLSetPos с данным значением параметра на вызов функции SQLBulkOperations со значением параметра Operation, равным SQL_ADD.
Обратно, если приложение ODBC 3.x использует драйвер ODBC 2.x, то менеджер драйверов заменяет для параметра, равного SQL_ADD, вызов SQLBulkOperations на вызов SQLSetPos.

Параметр LockType ([Input]) определяет уровень блокировки строки при выполнении операции, указываемой параметром Operation, и может принимать следующие значения:

SQL_LOCK_NO_CHANGE - состояние блокировки строки не должно измениться после выполнения функции;SQL_LOCK_EXCLUSIVE - выполняется блокировка строки с эксклюзивным доступом. Это гарантирует, что другое приложение не сможет получить доступ к данной строке;SQL_LOCK_UNLOCK - выполняется освобождение строки.

Строка, заблокированная при выполнении функции SQLSetPos, остается заблокированной до тех пор, пока приложение не вызовет эту же функцию со значением параметра LockType, равным SQL_LOCK_UNLOCK, или функцию SQLFreeHandle для дескриптора оператора, или функцию SQLFreeStmt с опцией SQL_CLOSE. Если драйвер поддерживает транзакции, то строка, заблокированная при выполнении функции SQLSetPos, освобождается при завершении транзакции функцией SQLEndTran (как при коммите, так и при откате транзакции) в том случае, если при завершении транзакции установлено закрытие курсора.

При выполнении операций SQL_DELETE и SQL_UPDATE изменяется состояние строки. Так, в массиве состояния строк, указатель на который определяется атрибутом оператора SQL_ATTR_ROW_STATUS, все удаленные строки будут отмечены как SQL_ROW_DELETED.

Для определения во время выполнения операций, поддерживаемых используемым источником данных, следует вызвать функцию SQLGetInfo с одним из следующих значений параметра в зависимости от типа используемого курсора:

SQL_DYNAMIC_CURSOR_ATTRIBUTES1, SQL_FORWARD_ONLY_CURSOR_ATTRIBUTES1, SQL_KEYSET_CURSOR_ATTRIBUTES1, SQL_STATIC_CURSOR_ATTRIBUTES1.

В следующей таблице приведено описание операций, которые могут быть выполнены функцией SQLSetPos

Значение параметра OperationОписание
SQL_POSITIONПозиция курсора устанавливается на строку с номером, указанным параметром RowNumber (нумерация начинается с 1)
SQL_REFRESHПозиция курсора устанавливается на строку с номером, указанным параметром RowNumber, и драйвер обновляет данные для этой строки в буфере результирующего набора. При этом функция SQLSetPos выполняет обновление содержания и состояния строк в текущем сформированном результирующем наборе: данные в буфере обновляются, а не повторно извлекаются. В отличие от функции SQLSetPos функция SQLFetchScroll с значениями параметров FetchOrientation, равным SQL_FETCH_RELATIVE, и RowNumber, равным 0, выполняет повторное извлечение строк из результирующего набора (при этом, если драйвер допускает, то для обновляемого курсора будут отображаться добавленные строки и не отображаться - удаленные). Успешное выполнение функцией SQLSetPos обновления строки не изменяет состояния строки, равного SQL_ROW_DELETED, а состояние строки, равное SQL_ROW_ADDED, заменяет на SQL_ROW_SUCCESS (в том случае, если массив состояний строк существует). В том случае, если курсор был открыт с атрибутом оператора SQL_ATTR_CONCURRENCY SQL_CONCUR_ROWVER или SQL_CONCUR_VALUES, то в случае операции обновления, выполняемой функцией SQLSetPos, может быть определено, что строки были изменены: при этом буфер результирующего набора обновляется при извлечении строки с сервера
SQL_UPDATEУстанавливает позицию курсора на строку, указанную параметром RowNumber, и изменяет значения строки, беря их из буфера (буферов результирующего набора), определенного параметром TargetValuePtr функции SQLBindCol
SQL_DELETEУстанавливает позицию курсора на строку, определенную параметром RowNumber, и удаляет указанную строку

Курсоры


Применение курсоров позволяет приложению выполнять выборку одной или нескольких строк за одну операцию извлечения данных. Также курсоры поддерживают возможность применения операторов UPDATE, INSERT и DELETE к текущей позиции курсора.

Для использования курсора следует установить соединение с базой данных и установить нужные значения атрибутам оператора, контролирующим поведение курсора.

Для использования курсора требуется выполнить следующие действия:

Установить атрибуты курсора, вызвав функцию SQLSetStmtAttr. Эта функция позволяет устанавливать следующие атрибуты: SQL_ATTR_CURSOR_TYPE и SQL_ATTR_CONCURRENCY, или SQL_CURSOR_SCROLLABLE и SQL_CURSOR_SENSITIVITY.Определите размер результирующего набора, вызвав функцию SQLSetStmtAttr с атрибутом SQL_ATTR_ROW_ARRAY_SIZE.Для того чтобы использовать позиционированный SQL-оператор с фразой WHERE CURRENT OF, следует определить имя курсора, вызвав функцию SQLSetCursorName.Для создания результирующего набора следует выполнить SQL-оператор SELECT, вызвав функцию SQLExecute или функцию SQLExecDirect.Если имя курсора не было предварительно определено, но требуется использовать возможность применения позиционированного SQL-оператора с фразой WHERE CURRENT OF, то следует создать имя курсора, вызвав функцию SQLGetCursorName.Для получения информации о количестве столбцов в сформированном результирующем наборе можно вызвать функцию SQLNumResultCols.Определите связывание столбцов результирующего набора с буфером, предназначенным для извлечения значения столбца (одной строки или сразу набора строк).Выполните функцию ODBC API, извлекающую строки из результирующего набора в связанный буфер (или функцию, выполняющую извлечение поочередно каждого столбца в указываемый параметром буфер, если не было выполнено предварительного связывания). Если выполняемый SQL-оператор состоял из нескольких операторов SELECT, то сформированный результирующий набор будет состоять из нескольких множеств. Для перехода к следующему множеству (результирующему набору) используется функция SQLMoreResults. В том случае, если следующее множество существует, то эта функция выполняет переход к нему и возвращает код ответа SQL_SUCCESS. Если выбраны все множества сформированного результирующего набора, то функция возвращает код ответа SQL_NO_DATA.Для освобождения дескриптора оператора вызовите функцию SQLFreeStmt. При этом для того, чтобы одновременно выполнить освобождение связанных с данным оператором буферов, используемых для извлечения значений столбцов, установите значение параметра fOption равным SQL_UNBIND.



Массив операций над строками


Функции ODBC API позволяют выполнять одновременно (за один вызов функции) некоторую операцию над несколькими строками. Такие операции называются множественными операциями.

Массив операций над строками указывается атрибутом оператора SQL_ATTR_ROW_OPERATION_PTR. Каждый элемент массива может иметь одно из следующих значений:

SQL_ROW_PROCEED - строка должна быть обработана (по умолчанию);SQL_ROW_IGNORE - игнорировать данную строку.

Массив операций над строками определяет, будет ли вызов функции SQLSetPos для множественной операции (bulk operation) выполняться или игнорироваться.



Массив состояния строк


Массив состояния строк указывается атрибутом оператора SQL_ATTR_ROW_STATUS_PTR. Он содержит значения состояния каждой строки результирующего набора. Состояние строки устанавливается драйвером после вызова одной из следующих функций: SQLFetch, SQLFetchScroll, SQLBulkOperations, SQLSetPos.



Применение функции SQLSetPos


При изменении данных перед вызовом функции SQLSetPos следует:

Вызвать функцию SQLBindCol (или SQLSetDescRec) для каждого столбца, определив тип данных, буфер для данных и его размер. Если для столбца не назначается связываемый с ним буфер, то данные могут быть переданы последовательностью вызовов функции SQLPutData. Такие столбцы называются столбцами времени выполнения (data-at-execution columns).Для гарантии того, что изменяемый столбец может быть изменен, следует вызвать функцию SQLColAttribute.Для создания результирующего набора можно выполнить функцию SQLExecDirect или SQLExecute.Для выборки данных использовать функции SQLFetch или SQLFetchScroll.



Управление поведением курсора


Приложение, использующее функции ODBC API, управляет поведением курсора, устанавливая атрибуты оператора. Можно использовать два различных способа определения характеристик курсора:

определить тип курсора;определить поведение курсора.

Для определения типа курсора вызывается функция SQLSetStmtAttr со значением атрибута SQL_ATTR_CURSOR_TYPE, который имеет тип SQLUINTEGER и может задаваться следующими значениями:

SQL_CURSOR_FORWARD_ONLY - однонаправленный курсор;SQL_CURSOR_STATIC - статический курсор, определяющий, что информация, извлеченная в результирующий набор, не будет отражать изменение данных в БД, произошедшее после создания результирующего набора;SQL_CURSOR_KEYSET_DRIVEN - курсор, управляемый ключом. Такой курсор позволяет "видеть" изменение и удаление строк в БД, произошедшие после создания результирующего набора, но не отображает создание новых строк. Количество строк, для которых создаются ключи, указывается атрибутом SQL_ATTR_KEYSET_SIZE;SQL_CURSOR_DYNAMIC - динамический курсор, отражающий изменение данных в БД после создания результирующего набора.

На используемый тип курсора накладывает ограничение применяемый ODBC-драйвер. Так, многие драйверы не поддерживают возможность применения динамического курсора. В том случае, если при вызове функции SQLSetStmtAttr был указан недопустимый тип курсора, то драйвер заменит его на наиболее подходящий из поддерживаемых типов, а функция вернет код ответа SQLSTATE 01S02 (Опция была изменена).

Для определения поведения курсора вызывается функция SQLSetStmtAttr со значениями атрибутов SQL_ATTR_CURSOR_SCROLLABLE и SQL_ATTR_CURSOR_SENSITIVITY. Смысл этих атрибутов соответствует применению ключевых слов SCROLL и SENSITIVE в SQL-операторе DECLARE CURSOR в стандарте SQL-92.

Атрибут SQL_ATTR_CURSOR_SCROLLABLE управляет перемещаемым курсором, имеет тип SQLUINTEGER и может задаваться следующими двумя значениями:

SQL_NONSCROLLABLE - перемещаемый курсор, реализуется как простой однонаправленный курсор. При вызове в приложении функции SQLFetchScroll параметр FetchOrientation может принимать только значение SQL_FETCH_NEXT.SQL_SCROLLABLE - перемещаемый курсор, поддерживающий двунаправленный просмотр результирующего набора, а также прямую выборку строк.


Перемещаемые курсоры для извлечения данных используют функцию SQLFetchScroll (в версии ODBC 2.x использовалась функция SQLExtendedFetched). Если простой однонаправленный курсор, используемый функцией SQLFetch, позволяет перемещаться только в одном прямом направлении и выполнять выборку только одной строки за один вызов функции, то перемещаемый курсор позволяет:

выполнять за один вызов функции извлечение более одной строки;перемещаться в двух направлениях и на любое число строк;выполнять прямую выборку строки из результирующего набора по ее номеру.

Атрибут SQL_ATTR_CURSOR_SENSITIVITY определяет чувствительность курсора к изменениям, выполняемым другими курсорами, имеет тип SQLUINTEGER и может определять режимы, задаваемые следующими значениями:

SQL_UNSPECIFIED - неопределенный курсор, при котором изменения могут быть сделаны как видимыми, так и невидимыми или частично видимыми. Этот режим используется по умолчанию;SQL_INSENSITIVE - режим нечувствительного курсора, при котором все курсоры показывают результирующий набор без отражения изменений, выполненных для других курсоров. Нечуствительный курсор является курсором "только чтение". Он соответствует статическому типу курсора с уровнем изоляции "только чтение";SQL_SENSITIVE - режим чувствительного курсора, при котором курсоры "видят" все изменения, выполненные другими курсорами.

Приложение может определять характеристики курсора двояко: или как тип курсора, или как поведение курсора. При этом ODBC-драйвер неявным образом приводит в соответствие значение остальных атрибутов оператора. Следующая таблица иллюстрирует соответствие атрибутов оператора.

Атрибуты, устанавливаемые вызовом функции SQLSetStmtAttrАтрибуты, устанавливаемые драйвером неявно для соответствия характеристик курсора
SQL_ATTR_CONCURRENCY установлен как SQL_CONCUR_READ_ONLYSQL_ATTR_CURSOR_SENSITIVITY получает значение SQL_INSENSITIVE
SQL_ATTR_CONCURRENCY установлен как SQL_CONCUR_LOCK, SQL_CONCUR_ROWVER или SQL_CONCUR_VALUESSQL_ATTR_CURSOR_SENSITIVITY получает значение SQL_UNSPECIFIED или SQL_SENSITIVE, что определяется драйвером. Значение SQL_INSENSITIVE никогда не будет назначено данному атрибуту, так как оно предусматривает режим "только чтение"
SQL_ATTR_CURSOR_SCROLLABLE установлен как SQL_NONSCROLLABLESQL_ATTR_CURSOR_TYPE получает значение SQL_CURSOR_FORWARD_ONLY
SQL_ATTR_CURSOR_SCROLLABLE установлен как SQL_SCROLLABLESQL_ATTR_CURSOR_TYPE получает значение SQL_CURSOR_STATIC, SQL_CURSOR_KEYSET_DRIVEN или SQL_CURSOR_DYNAMIC, что определяется драйвером. Значение SQL_CURSOR_FORWARD_ONLY никогда не будет назначено данному атрибуту
SQL_ATTR_CURSOR_SENSITIVITY установлен как SQL_INSENSITIVESQL_ATTR_CONCURRENCY получает значение SQL_CONCUR_READ_ONLY. SQL_ATTR_CURSOR_TYPE получает значение SQL_CURSOR_STATIC
SQL_ATTR_CURSOR_SENSITIVITY установлен как SQL_SENSITIVESQL_ATTR_CONCURRENCY получает значение SQL_CONCUR_LOCK, SQL_CONCUR_ROWVER или SQL_CONCUR_VALUES ( как определено драйвером). Атрибут SQL_ATTR_CONCURRENCY никогда не получает значение SQL_CONCUR_READ_ONLY. SQL_ATTR_CURSOR_TYPE получает значение SQL_CURSOR_FORWARD_ONLY, SQL_CURSOR_STATIC, SQL_CURSOR_KEYSET_DRIVEN или SQL_CURSOR_DYNAMIC (как определяется драйвером) SQL_ATTR_CURSOR_SENSITIVITY установлен как SQL_UNSPECIFIED SQL_ATTR_CONCURRENCY получает значение SQL_CONCUR_READ_ONLY, SQL_CONCUR_LOCK, SQL_CONCUR_ROWVER или SQL_CONCUR_VALUES ( как определено драйвером). SQL_ATTR_CURSOR_TYPE получает значение SQL_CURSOR_FORWARD_ONLY, SQL_CURSOR_STATIC, SQL_CURSOR_KEYSET_DRIVEN или SQL_CURSOR_DYNAMIC ( как определено драйвером)
SQL_ATTR_CURSOR_TYPE установлен как SQL_CURSOR_DYNAMICSQL_ATTR_SCROLLABLE получает значение SQL_SCROLLABLE. SQL_ATTR_CURSOR_SENSITIVITY получает значение SQL_SENSITIVE. (Только в том случае, если атрибут SQL_ATTR_CONCURRENCY не равен SQL_CONCUR_READ_ONLY. Обновляемые динамические курсоры всегда "видят" все изменения, сделанные в их собственной транзакции
SQL_ATTR_CURSOR_TYPE установлен как SQL_CURSOR_FORWARD_ONLYSQL_ATTR_CURSOR_SCROLLABLE получает значение SQL_NONSCROLLABLE
SQL_ATTR_CURSOR_TYPE установлен как SQL_CURSOR_KEYSET_DRIVENSQL_ATTR_SCROLLABLE получает значение SQL_SCROLLABLE. SQL_ATTR_SENSITIVITY получает значение SQL_UNSPECIFIED или SQL_SENSITIVE (в соответствии с тем, как определяется драйвером, но если атрибут SQL_ATTR_CONCURRENCY не равен SQL_CONCUR_READ_ONLY)
SQL_ATTR_CURSOR_TYPE установлен как SQL_CURSOR_STATICSQL_ATTR_SCROLLABLE получает значение SQL_SCROLLABLE. SQL_ATTR_SENSITIVITY получает значение SQL_INSENSITIVE (в том случае, если атрибут SQL_ATTR_CONCURRENCY равен SQL_CONCUR_READ_ONLY). SQL_ATTR_SENSITIVITY получает значение SQL_UNSPECIFIED или SQL_SENSITIVE (в том случае, если атрибут SQL_ATTR_CONCURRENCY не равен SQL_CONCUR_READ_ONLY)
Функция SQLSetStmtAttr позволяет установить значения атрибутов оператора и имеет следующее формальное описание:

SQLRETURN SQLSetStmtAttr( SQLHSTMT StatementHandle, SQLINTEGER Attribute, SQLPOINTER ValuePtr, SQLINTEGER StringLength);

Параметр StatementHandle ([Input]) указывает дескриптор оператора.

Параметр Attribute ([Input]) определяет атрибут, значение которого устанавливается, а параметр ValuePtr ([Input]) является указателем на значение, назначаемое атрибуту Attribute.

Параметр StringLength ([Input]) зависит от типа значения, передаваемого параметром ValuePtr, и от вида атрибута (ODBC-определяемый или определяемый драйвером) и может содержать длину строки, игнорироваться или указываться константами, такими как SQL_NTS, SQL_IS_POINTER.


Выполнение множественных операций


Множественная операция выполняется функцией SQLSetPos в том случае, если параметр RowNumber равен 0. Она выполняется для всех строк, у которых в массиве операций над строками установлено значение SQL_ROW_PROCEED.

По умолчанию должны обрабатываться все строки результирующего набора. Для того чтобы драйвер игнорировал одну или несколько строк, им в массиве операций над строками надо назначить значение SQL_ROW_IGNORE.

Это может быть выполнено вызовом функции SQLSetStmtAttr для установки атрибута оператора SQL_ATTR_ROW_OPERATION_PTR, указывающего на массив элементов типа SQLUSMALLINT. Это поле также может быть установлено вызовом функции SQLSetDescField для определения SQL_DESC_ARRAY_STATUS_PTR.

При множественных операциях игнорировать можно не только обработку строк, но и обработку столбцов (например, столбцов "только для чтения"). Для этого в функции SQLBindCol игнорируемый столбец следует пометить как SQL_COLUMN_IGNORE.

Следующий пример иллюстрирует применение функции SQLSetPos.

#define ROWS 20 // Число строк // в результирующем наборе #define STATUS_LEN 6

SQLCHAR szStatus[ROWS][STATUS_LEN], szReply[3]; SQLINTEGER cbStatus[ROWS], cbID; SQLUSMALLINT rgfRowStatus[ROWS]; SQLUINTEGER sID, crow = ROWS, irow; SQLHSTMT hstmtS, hstmtU;

SQLSetStmtAttr(hstmtS, SQL_ATTR_CONCURRENCY, (SQLPOINTER) SQL_CONCUR_ROWVER, 0); // Определение типа курсора SQLSetStmtAttr(hstmtS, SQL_ATTR_CURSOR_TYPE, (SQLPOINTER) SQL_CURSOR_KEYSET_DRIVEN, 0); // Определение размера результирующего набора SQLSetStmtAttr(hstmtS, SQL_ATTR_ROW_ARRAY_SIZE, (SQLPOINTER) ROWS, 0); // Определение массива состояния строк SQLSetStmtAttr(hstmtS, SQL_ATTR_ROW_STATUS_PTR, (SQLPOINTER) rgfRowStatus, 0); SQLSetCursorName(hstmtS, "C1", SQL_NTS); // Выполнение SQL-оператора SQLExecDirect(hstmtS, "SELECT ID1, STATUS FROM TBL1", SQL_NTS); // Выполнение "связывания" данных SQLBindCol(hstmtS, 1, SQL_C_ULONG, &sID, 0, &cbID); SQLBindCol(hstmtS, 2, SQL_C_CHAR, szStatus, STATUS_LEN, &cbStatus);


while ((retcode == SQLFetchScroll(hstmtS, SQL_FETCH_NEXT, 0)) != SQL_ERROR) { if (retcode == SQL_NO_DATA_FOUND) break; // Отображение 20-ти извлеченных строк // результирующего набора for (irow = 0; irow < crow; irow++) { if (rgfRowStatus[irow] != SQL_ROW_DELETED) // Отображение данных printf("%2d %5d %*s\n", irow+1, sID, NAME_LEN-1, szStatus[irow]); } while (TRUE) { printf("\ nУкажите номер изменяемой строки или 0?"); gets(szReply); // Получаем номер строки irow = atoi(szReply); if (irow > 0 && irow <= crow) { printf("\nНовое состояние?"); gets(szStatus[irow-1]); // Получаем новое // значение для поля STATUS // Изменяем текущую позицию курсора SQLSetPos(hstmtS, irow, SQL_POSITION, SQL_LOCK_NO_CHANGE); SQLPrepare(hstmtU, "UPDATE TBL1 SET STATUS=? WHERE CURRENT OF C1", SQL_NTS); // Выполняем "связывание" параметра SQLBindParameter(hstmtU, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, STATUS_LEN, 0, szStatus[irow], 0, NULL); // Выполняем изменение данных SQLExecute(hstmtU); } else if (irow == 0) { break; } } }


Реализация блочной выборки строк


При использовании перемещаемого курсора для изменения текущей позиции курсора и выборки строк используется функция SQLFetchScroll. Эта функция позволяет реализовывать:

относительный скроллинг - перемещение по результирующему набору в двух направлениях и на любое число строк;абсолютный скроллинг - перемещение на первую или на последнюю строку, или на строку с указанным номером.

Функция SQLFetchScroll выполняет выборку набора строк из сформированного результирующего набора и возвращает данные для всех связанных столбцов. Наборы строк (rowset) могут быть указаны как через абсолютное или относительное позиционирование, так и посредством закладок (bookmark). В версии ODBC 2.x для этих целей использовалась функция SQLExtendedFetch.

Функция SQLFetchScroll имеет следующее формальное описание:

SQLRETURN SQLFetchScroll( SQLHSTMT StatementHandle, SQLSMALLINT FetchOrientation, SQLINTEGER FetchOffset);

Параметр StatementHandle ([Input]) указывает дескриптор оператора.

Перемещение курсора определяется типом выборки, указывается параметром FetchOrientation ([Input]) и может принимать следующие значения:

SQL_FETCH_NEXT - переход к следующей строке (относительный скроллинг);SQL_FETCH_PRIOR - переход к предыдущей строке (относительный скроллинг);SQL_FETCH_FIRST - переход к первой строке (абсолютный скроллинг);SQL_FETCH_LAST - переход к последней строке (абсолютный скроллинг);SQL_FETCH_ABSOLUTE - переход к строке с указанным номером (абсолютный скроллинг);SQL_FETCH_RELATIVE - перемещение вперед или назад на указанное количество строк (относительный скроллинг);SQL_FETCH_BOOKMARK - переход к строке по закладке (абсолютный скроллинг).

Количество строк, на которые выполняется перемещение курсора и номер абсолютной позиции, указывается параметром FetchOffset ([Input]).

Параметры FetchOrientation и FetchOffset функции SQLFetchScroll совместно определяют набор строк, который будет извлечен из результирующего набора. На следующей схеме показан механизм выборки строк при позиционировании на следующую, предыдущую, первую или последнюю строку.




Механизм выборки строк при помощи функции SQLFetchScroll также позволяет реализовывать позиционирование по абсолютному указанному номеру, позиционирование со смещением на заданное число строк или позиционирование посредством закладки. Эти механизмы отображены на следующей схеме.



Функция SQLFetchScroll выполняет позиционирование курсора на указанную строку результирующего набора и возвращает набор строк, начиная с установленной позиции курсора. Если требуемый набор строк выходит за нижнюю границу результирующего набора, то возвращается существующая часть требуемого набора строк. А при выходе за верхнюю границу, как правило, возвращается набор строк требуемого размера, начиная с первой строки результирующего набора.

Иногда требуется выполнить позиционирование курсора без извлечения данных - например, для проверки существования строки или для получения закладки строки. В этом случае следует установить режим выполнения SQL_RD_OFF (без чтения). Такой режим устанавливается назначением атрибуту оператора SQL_ATTR_RETRIEVE_DATA значения SQL_RD_OFF. Отметим, что если существует переменная, связанная со столбцом закладок, то она всегда обновляется вне зависимости от того, какой режим установлен.

После того как набор строк будет извлечен, для позиционирования в нем конкретной строки или извлечения строки можно использовать функцию SQLSetPos.


Связывание по столбцу


При использовании связывания по столбцу с каждым столбцом может быть связано от одного до трех массивов: первый массив - для извлекаемых значений, второй - для длины / индикатора буферов, третий - для индикатора буферов (если индикаторы и длина хранятся по отдельности). Каждый массив должен содержать число элементов, равное числу строк в извлекаемом наборе строк.

Хранятся ли по отдельности индикаторы и значения длины, определяется установкой дескрипторов полей SQL_DESC_INDICATOR_PTR и SQL_DESC_OCTET_LENGTH_PTR.

На следующем рисунке приведена схема связывания по столбцам.


Следующий пример иллюстрирует применение связывания по столбцам для набора строк, состоящего из трех столбцов:

#define ROW_ARRAY_SIZE 10 // Кол-во строк // в наборе строк

SQLUINTEGER ID1Array[ROW_ARRAY_SIZE], NumRowsFetched; SQLCHAR SalArray[ROW_ARRAY_SIZE][11], StatusArray[ROW_ARRAY_SIZE][7]; SQLINTEGER ID1IndArray[ROW_ARRAY_SIZE], SalLenOrIndArray[ROW_ARRAY_SIZE], StatusLenOrIndArray[ROW_ARRAY_SIZE]; SQLUSMALLINT RowStatusArray[ROW_ARRAY_SIZE], i; SQLRETURN rc; SQLHSTMT hstmt;

// Устанавливаем атрибут оператора // SQL_ATTR_ROW_BIND_TYPE для использования связывания // по столбцам SQLSetStmtAttr(hstmt, SQL_ATTR_ROW_BIND_TYPE, SQL_BIND_BY_COLUMN, 0); // Размер набора строк задаем атрибутом оператора // SQL_ATTR_ROW_ARRAY_SIZE SQLSetStmtAttr(hstmt, SQL_ATTR_ROW_ARRAY_SIZE, ROW_ARRAY_SIZE, 0); // Устанавливаем атрибут оператора // SQL_ATTR_ROW_STATUS_PTR для определения массива // состояний строк SQLSetStmtAttr(hstmt, SQL_ATTR_ROW_STATUS_PTR, RowStatusArray, 0); // Устанавливаем атрибут оператора // SQL_ATTR_ROWS_FETCHED_PTR для указания на // cRowsFetched SQLSetStmtAttr(hstmt, SQL_ATTR_ROWS_FETCHED_PTR, &NumRowsFetched, 0);

// Связываем массивы со столбцами ID1, Sal и Status SQLBindCol(hstmt, 1, SQL_C_ULONG, ID1Array, 0, ID1IndArray); SQLBindCol(hstmt, 2, SQL_C_CHAR, SalArray, sizeof(SalArray[0]), SalLenOrIndArray); SQLBindCol(hstmt, 3, SQL_C_CHAR, StatusArray, sizeof(StatusArray[0]), StatusLenOrIndArray);


// Выполняем SQL-оператор SELECT для создания // результирующего набора SQLExecDirect(hstmt, " SELECT ID1, Sal, Status FROM TBL1", SQL_NTS);

// Выполняем блочную выборку из результирующего набора // В переменной NumRowsFetched возвращается число // в действительности выбранных строк while ((rc = SQLFetchScroll(hstmt,SQL_FETCH_NEXT,0)) != SQL_NO_DATA) { for (i = 0; i < NumRowsFetched; i++) {

// Отображаем только успешно извлеченные строки (если // код ответа (rc) равен SQL_SUCCESS_WITH_INFO или // SQL_ERROR, то строку не выводим.)

if ((RowStatusArray[i] == SQL_ROW_SUCCESS) || (RowStatusArray[i] == SQL_ROW_SUCCESS_WITH_INFO)) { if (ID1IndArray[i] == SQL_NULL_DATA) printf(" NULL "); else printf("%d\t", ID1Array[i]); if (SalLenOrIndArray[i] == SQL_NULL_DATA) printf(" NULL "); else printf("%s\t", SalArray[i]); if (StatusLenOrIndArray[i] == SQL_NULL_DATA) printf(" NULL\n"); else printf("%s\n", StatusArray[i]); } } } // Закрываем курсор SQLCloseCursor(hstmt);


Связывание по строкам


При использовании связывания по строкам определяется структура, содержащая от одного до трех элементов для каждого столбца извлекаемых данных. Первый элемент предназначается для извлекаемых данных, второй - для длины / индикатора буфера, третий - для индикатора буфера при раздельном сохранении значений длины и индикатора (что определяется дескрипторами полей SQL_DESC_INDICATOR_PTR и SQL_DESC_OCTET_LENGTH_PTR).

Массив таких структур должен содержать количество элементов, равное числу строк в извлекаемом наборе строк.

Размер структуры (число строк) указывается атрибутом оператора SQL_ATTR_ROW_BIND_TYPE. При связывании используются адреса каждого члена первого элемента массива. Так, адрес конкретного значения из набора строк может быть вычислен следующим образом (нумерация строк с 1):

Адрес = Адрес_связывания + ((Номер_строки - 1) * Размер_структуры)

На следующем рисунке приведена схема связывания по строкам.


Следующий пример иллюстрирует применение связывания по строкам для набора строк, состоящего из трех столбцов.

#define ROW_ARRAY_SIZE 10

// Определяем структуру TBL_INFO и создаем массив // структур, содержащий 10 элементов typedef struct { SQLUINTEGER ID1; // Для значения 1 столбца SQLINTEGER ID1Ind; // Для длины/индикатора SQLCHAR Sal[11]; // Для значения 2 столбца SQLINTEGER SalLenOrInd; SQLCHAR Status[7]; // Для значения 3 столбца SQLINTEGER StatusLenOrInd; } ORDERINFO; TBL_INFO TBL_Array[ROW_ARRAY_SIZE]; // Массив структур

SQLUINTEGER NumRowsFetched; SQLUSMALLINT RowStatusArray[ROW_ARRAY_SIZE], i; SQLRETURN rc; SQLHSTMT hstmt;

// Определяем размер структуры, используя атрибут //оператора SQL_ATTR_ROW_BIND_TYPE, и одновременно // устанавливаем тип связывания по строкам SQLSetStmtAttr(hstmt, SQL_ATTR_ROW_BIND_TYPE, sizeof(TBL_INFO), 0);

// Используя атрибут оператора SQL_ATTR_ROW_ARRAY_SIZE // устанавливаем количество строк в извлекаемом наборе // строк SQLSetStmtAttr(hstmt, SQL_ATTR_ROW_ARRAY_SIZE, ROW_ARRAY_SIZE, 0);

// Используя атрибут оператора SQL_ATTR_ROW_STATUS_PTR // определяем массив состояния строк SQLSetStmtAttr(hstmt, SQL_ATTR_ROW_STATUS_PTR, RowStatusArray, 0); // Устанавливаем атрибут оператора // SQL_ATTR_ROWS_FETCHED_PTR для указания на // NumRowsFetched SQLSetStmtAttr(hstmt, SQL_ATTR_ROWS_FETCHED_PTR, &NumRowsFetched, 0);


// Связываем поля структуры первого элемента массива // со столбцами ID1, Sal и Status SQLBindCol(hstmt, 1, SQL_C_ULONG, &TBL_Array[0].ID1, 0, &TBL_Array[0].ID1Ind); SQLBindCol(hstmt, 2, SQL_C_CHAR, TBL_Array[0].Sal, sizeof(TBL_Array[0].Sal), &TBL_Array[0].SalLenOrInd); SQLBindCol(hstmt, 3, SQL_C_CHAR, TBL_Array[0].Status, sizeof(TBL_Array[0].Status), &TBL_Array[0].StatusLenOrInd);

// Выполняем SQL-оператор SELECT для формирования // результирующего набора SQLExecDirect(hstmt, "SELECT ID1, Sal, Status FROM TBL1", SQL_NTS);

// Используя блочный курсор, извлекаем установленное //число строк while ((rc = SQLFetchScroll(hstmt,SQL_FETCH_NEXT,0)) != SQL_NO_DATA) { // Переменная NumRowsFetched содержит число // в действительности извлеченных строк for (i = 0; i < NumRowsFetched; i++) { if (RowStatusArray[i] == SQL_ROW_SUCCESS|| RowStatusArray[i] == SQL_ROW_SUCCESS_WITH_INFO) { if (TBL_Array[i].ID1Ind == SQL_NULL_DATA) std::cout<<" NULL "; else std::cout<< TBL_Array[i].ID1; if (TBL_Array[i].SalLenOrInd == SQL_NULL_DATA) std::cout<< " NULL "; else std::cout<< TBL_Array[i].Sal; if (TBL_Array[i].StatusLenOrInd == SQL_NULL_DATA) std::cout<< " NULL\n"; else std::cout<< TBL_Array[i].Status; } } } // Закрываем курсор SQLCloseCursor(hstmt);


Связывание столбцов, используемых с блочным курсором


При использовании блочного курсора за один вызов функции может возвращаться несколько строк. Поэтому с каждым столбцом должна связываться не просто переменная соответствующего типа, а массив. Такой массив обычно называется буфером набора строк (rowset buffer).

Возможно два типа связывания:

связывание по столбцу (column-wise binding), при котором с каждым столбцом связывается отдельный массив (структура данных, содержащая элементы одного типа);связывание по строке (row-wise binding), при котором с каждой строкой связывается отдельный массив (структура данных, типы элементов которой соответствуют типам столбцов строки набора данных).

Для выполнения любого типа связывания используется функция ODBC API SQLBindCol. При этом тип связывания определяется атрибутом SQL_ATTR_ROW_BIND_TYPE. В случае использования блочного курсора со связыванием по строке или по столбцу, функции SQLBindCol в качестве параметра вместо адреса простой переменной передается адрес массива.



Асинхронное выполнение функций


По умолчанию ODBC-драйверы применяют синхронное выполнение функций, когда приложение ожидает завершения выполнения функции. При асинхронном выполнении функций ODBC-драйвер возвращает управление приложению до окончательного завершения выполнения функции. В этом случае приложение продолжает выполняться дальше и имеет возможность синхронного или асинхронного вызова других функций.

В зависимости от используемого источника данных асинхронное выполнение осуществляется на основе оператора или соединения. Режим асинхронного выполнения можно определить при вызове функции SQLGetInfo с параметром, равным SQL_ASYNC_MODE: для асинхронного выполнения уровня соединения возвращается значение SQL_AM_CONNECTION, а для асинхронного выполнения уровня оператора - значение SQL_AM_STATEMENT.

Для определения того, что функция будет выполняться асинхронно на уровне оператора, следует вызовом функции SQLSetStmtAttr установить значение атрибута SQL_ATTR_ASYNC_ENABLE равным SQL_ASYNC_ENABLE_ON. Для определения асинхронного выполнения на уровне соединения следует вызовом функции SQLSetConnectAttr устанавливать значение атрибута соединения SQL_ATTR_ASYNC_ENABLE равным SQL_ASYNC_ENABLE_ON. При использовании асинхронного режима уровня соединения все операторы, ассоциируемые с данным соединением, могут выполняться в асинхронном режиме.

Количество максимально допустимых параллельно и асинхронно выполняемых операторов для конкретного драйвера определяется вызовом функции SQLGetInfo с параметром, равным SQL_MAX_ASYNC_CONCURRENT_STATEMENTS.

При асинхронном выполнении функции до завершения ее выполнения каждый повторный вызов этой функции с теми же параметрами возвращает код ответа SQL_STILL_EXECUTING. Так, повторяя вызовы через некоторый интервал времени, можно определять момент, когда будет завершено асинхронное выполнение функции.

Например:

SQLHSTMT hstmt1; // Дескриптор оператора SQLRETURN rc;

// Устанавливаем режим асинхронного выполнения SQLSetStmtAttr(hstmt1, SQL_ATTR_ASYNC_ENABLE, SQL_ASYNC_ENABLE_ON, 0); // Выполняем асинхронно оператор SELECT while ((rc=SQLExecDirect(hstmt1, "SELECT * FROM TBL1",SQL_NTS)) == SQL_STILL_EXECUTING) { // Действия, осуществляемые во время асинхронного // выполнения оператора SELECT (нельзя использовать // дескриптор hstmt1) } // Асинхронное выполнение завершено


Во время асинхронного выполнения оператора приложение может осуществлять синхронный или асинхронный вызов функций для других дескрипторов оператора. Для самого дескриптора оператора в период асинхронного выполнения может осуществляться только повторный вызов выполняемой функции или функций SQLCancel (прерывание вызова), SQLGetDiagField и SQLGetDiagRec (но только заголовки полей).

Для соединения, ассоциированного с асинхронно выполняемым оператором, возможен вызов следующих функций: SQLAllocHandle (для размещения дескриптора оператора), SQLGetDiagField, SQLGetDiagRec, SQLGetFunctions.

Пример:

SQLHDBC hdbc1, hdbc2; // Дескрипторы соединения SQLHSTMT hstmt1, hstmt2, hstmt3; SQLCHAR * SQLStatement = "SELECT * FROM TBL1"; SQLUINTEGER InfoValue; SQLRETURN rc; // Создание дескрипторов операторов SQLAllocHandle(SQL_HANDLE_STMT, hdbc1, &hstmt1); SQLAllocHandle(SQL_HANDLE_STMT, hdbc1, &hstmt2); SQLAllocHandle(SQL_HANDLE_STMT, hdbc2, &hstmt3); // Устанавливаем для hstmt1 режим асинхронного // выполнения SQLSetStmtAttr(hstmt1, SQL_ATTR_ASYNC_ENABLE, SQL_ASYNC_ENABLE_ON, 0); // Асинхронное выполнение для дескриптора hstmt1 while ((rc = SQLExecDirect(hstmt1, SQLStatement, SQL_NTS)) == SQL_STILL_EXECUTING) { // Следующий вызов вернет код ответа HY010, // т.к. дескриптор оператора hstmt1 пока " занят", // а второй вызов использует дескриптор hdbc1, в // котором размещен дескриптор оператора hstmt1 SQLExecDirect(hstmt1, SQLStatement, SQL_NTS); SQLGetInfo(hdbc1, SQL_UNION, (SQLPOINTER) &InfoValue, 0, NULL); // Ошибка с кодом HY010 // Следующие операторы будут выполнены без ошибки, // т.к для hdbc1 применяются дескрипторы оператора, //не используемые асинхронно в настоящий момент SQLExecDirect(hstmt2, SQLStatement, SQL_NTS); SQLTables(hstmt3,NULL,0,NULL,0,NULL,0,NULL,0); SQLGetInfo(hdbc2, SQL_UNION, (SQLPOINTER) &InfoValue, 0, NULL); }

Для отключения режима асинхронного выполнения операторов следует вызвать функцию SQLSetStmtAttr со значением атрибута SQL_ATTR_ASYNC_ENABLE, равным SQL_ASYNC_ENABLE_OFF (для уровня оператора) или функцию SQLSetConnectAttr со значением атрибута SQL_ATTR_ASYNC_ENABLE, равным SQL_ASYNC_ENABLE_OFF (для уровня соединения).



При вызове функции SQLGetDiagField, применяемой для получения диагностической информации, с дескриптором оператора, используемого асинхронно, возвращаются следующие значения:

поля SQL_DIAG_CURSOR_ROW_COUNT, SQL_DIAG_DYNAMIC_FUNCTION, SQL_DIAG_DYNAMIC_FUNCTION_CODE, SQL_DIAG_ROW_COUNT возвращают неопределенное значение; поле SQL_DIAG_NUMBER возвращает значение 0; поле SQL_DIAG_RETURNCODE возвращает значение SQL_STILL_EXECUTING;все поля записи возвращают SQL_NO_DATA.

Следующий пример иллюстрирует процесс получения диагностической информации:

SQLCHAR SqlState[6], SQLStmt[100], Msg[SQL_MAX_MESSAGE_LENGTH]; SQLINTEGER NativeError; SQLSMALLINT i, MsgLen; SQLRETURN rc1, rc2; SQLHSTMT hstmt; // В SQLStmt содержится SQL-оператор // Выполнение оператора и получение информации // о возникающих ошибках или предупреждениях rc1 = SQLExecDirect(hstmt, SQLStmt, SQL_NTS); if ((rc1 == SQL_SUCCESS_WITH_INFO) || (rc1 == SQL_ERROR)) { // Получение записей об ошибках i = 1; while ((rc2 = SQLGetDiagRec(SQL_HANDLE_STMT, hstmt, i, SqlState, &NativeError, Msg, sizeof(Msg), &MsgLen)) != SQL_NO_DATA) { cout<< SqlState<<" " <<NativeError<<" " <<Msg<<" " <<MsgLen; i++; } }

if ((rc1 == SQL_SUCCESS) || (rc1 == SQL_SUCCESS_WITH_INFO)) { // Оператор выполнен успешно }


Создание именованного курсора


Курсор, для которого определено имя, называется именованным курсором.

Ассоциировать имя курсора с активным дескриптором оператора можно вызовом функции SQLSetCursorName. В том случае, если эта функция не вызывается явным образом, драйвер при необходимости может генерировать имя курсора при выполнении SQL-оператора.

Функция SQLSetCursorName имеет следующее формальное описание:

SQLRETURN SQLSetCursorName( SQLHSTMT StatementHandle, SQLCHAR * CursorName, SQLSMALLINT NameLength);

Параметр StatementHandle ([Input]) указывает дескриптор оператора, а параметр CursorName ([Input]) задает назначаемое имя курсора. Отметим, что для эффективной обработки имя курсора не должно иметь лидирующих или завершающих пробелов.

Параметр NameLength ([Input]) определяет длину буфера *CursorName.

Следующий пример иллюстрирует применение функции SQLSetCursorName для получения имени курсора и выполнения затем позиционированного SQL-оператора UPDATE:

#define NAME_LEN 40 #define PHONE_LEN 12

SQLHSTMT hstmtSelect; // Дескриптор оператора // (для оператора SELECT) SQLHSTMT hstmtUpdate; // Дескриптор оператора // (для оператора UPDATE) SQLRETURN retcode; SQLHDBC hdbc; SQLCHAR szName[NAME_LEN], szPhone[PHONE_LEN]; SQLINTEGER cbName, cbPhone;

/* Создаем дескриптор оператора и определяем имя курсора */ SQLAllocHandle(SQL_HANDLE_STMT, hdbc, &hstmtSelect); SQLAllocHandle(SQL_HANDLE_STMT, hdbc, &hstmtUpdate); SQLSetCursorName(hstmtSelect, "C1", SQL_NTS);

/* Выполняем SQL-оператор SELECT для формирования результирующего набора и связываем его столбцы с локальными буферами для извлечения данных */ SQLExecDirect(hstmtSelect, "SELECT NAME, PHONE FROM Tbl1", SQL_NTS); SQLBindCol(hstmtSelect, 1, SQL_C_CHAR, szName, NAME_LEN, &cbName); SQLBindCol(hstmtSelect, 2, SQL_C_CHAR, szPhone, PHONE_LEN, &cbPhone);

/* Выбираем строки результирующего набора до тех пор, пока не найдем строку "Иванов А." */ do retcode = SQLFetch(hstmtSelect); while ((retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO) && (strcmp(szName, "Иванов А.") != 0));


/* Выполняем позиционированный UPDATE для текущей выбранной строки именованного курсора */ if (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO) { SQLExecDirect(hstmtUpdate, "UPDATE Tbl2 SET PHONE=\"1373737\" WHERE CURRENT OF C1", SQL_NTS); }

Для получения имени курсора, ассоциированного с конкретным дескриптором оператора, используется функция SQLGetCursorName, которая имеет следующее формальное описание:

SQLRETURN SQLGetCursorName( SQLHSTMT StatementHandle, SQLCHAR * CursorName, SQLSMALLINT BufferLength, SQLSMALLINT * NameLengthPtr);

Параметр StatementHandle ([Input]) указывает дескриптор оператора.

Имя курсора возвращается в буфере, определяемом параметром CursorName ([Output]).

Параметр BufferLength ([Input]) задает длину буфера *CursorName в байтах. Параметр NameLengthPtr ([Output]) определяет число байтов (включая ограничивающий 0-символ), которые занимает имя курсора. Если размер выделенного буфера меньше, чем требуется под имя курсора, то возвращаемое имя усекается.

В том случае, если в приложении функция SQLSetCursorName для задания имени курсора не вызывается, то драйвер автоматически генерирует имя курсора, начинающееся с префикса SQL_CUR.

Имя курсора в основном используется для позиционированного изменения или удаления, которое указывается в SQL-операторе фразой WHERE CURRENT OF имя_курсора.

Отметим, что в ODBC 2.x в том случае, если курсор не был открыт и ему не было назначено имя при вызове функции SQLSetCursorName, то инициировалась ошибка с кодом ответа SQLSTATE HY015 (Нет имени курсора). В версии ODBC 3.x вне зависимости от того, вызывалась ли функция SQLSetCursorName, драйвер всегда возвращает имя курсора.

Пока курсор не открыт, разрешено его переименование. Для этого достаточно вызвать функцию SQLSetCursorName.

Явно или неявно созданное имя курсора остается установленным до тех пор, пока не будет разрушен дескриптор оператора при вызове функции SQLFreeHandle с параметром HandleType, равным SQL_HANDLE_STMT.


Свойства курсора


Свойства курсора устанавливаются посредством атрибутов оператора. Для определения атрибутов оператора используется функция SQLSetStmtAttr, которая имеет следующее формальное описание:

SQLRETURN SQLSetStmtAttr( SQLHSTMT StatementHandle, SQLINTEGER Attribute, SQLPOINTER ValuePtr, SQLINTEGER StringLength);

Параметр StatementHandle ([Input]) указывает дескриптор оператора, для которого устанавливается значение атрибута оператора. Параметр Attribute ([Input]) определяет атрибут, значение которого задается параметром ValuePtr ([Input]). В зависимости от типа устанавливаемого атрибута параметр ValuePtr может быть: 32-битовым беззнаковым целым значением; указателем на null-ограниченную строку символов или двоичный буфер; значением, определяемым драйвером.

Параметр StringLength ([Input]) в зависимости от типа параметра может: игнорироваться (для целочисленных значений); указывать длину строки или быть равным SQL_NTS для null-ограниченных строк; быть равным результату макроса SQL_LEN_BINARY_ATTR(length) для указателя на двоичный буфер; для ValuePtr, который является указателем на значение, отличное от null-ограниченной строки или двоичного буфера, параметр должен быть равен SQL_IS_POINTER; для значений фиксированной длины параметр может быть равен SQL_IS_INTEGER или SQL_IS_UINTEGER.



Объектная модель OLE DB


Спецификация OLE DB описывает набор интерфейсов, реализуемых объектами OLE DB. Каждый объектный тип определен как набор интерфейсов. Спецификация OLE DB определяет набор интерфейсов базового уровня, которые должны реализовываться любыми OLE DB провайдерами.

В базовую модель OLE DB входят следующие объекты:

объект DataSource (источник данных), используемый для соединения с источником данных и создания одного или нескольких сеансов. Этот объект управляет соединением, использует информацию о полномочиях и аутентификации пользователя;объект Session (сеанс) управляет взаимодействием с источником данных - выполняет запросы и создает результирующие наборы. Сеанс также может возвращать метаданные. В сеансе может создаваться одна или несколько команд; объект Rowset (результирующий набор) представляет собой данные, извлекаемые в результате выполнения команды или создаваемые в сеансе.

На следующей схеме приведен пример использования интерфейсов базового уровня для создания результирующего набора.

Создание объекта:

CoCreateInstance

Объект DataSourceIDBInfoIDBInitializeIDBCreateSession
pIDBCreateSession->CreateSession
Объект Session IOpenRowSet
pIOpenRowset->OpenRowset
Объект Rowset IAccessorIRowsetInfoIColumnsInfoIRowset

Спецификация OLE DB определяет объект Command (команда), предназначенный для выполнения текстовой команды. В качестве такой команды может выступать и SQL-оператор. При этом выполнение команды может создавать результирующий набор (в случае SQL-оператора - это оператор SELECT).

Некоторые OLE DB провайдеры поддерживают работу со схемой (Schema), которая предоставляет метаданные по базе данных. Метаданные становятся доступны как обычные результирующие наборы. В заголовочном файле oledb.h содержатся уникальные идентификаторы всех доступных типов результирующих наборов схемы данных (например, для получения информации по таблицам базы данных следует указать уникальный идентификатор DBSCHEMA_TABLES). Столбец результирующего набора с именем TABLE_NAME содержит имя таблицы, столбец TABLE_TYPE указывает один из следующих типов таблицы: ALIAS, TABLE, SYNONYM, SYSTEM TABLE, VIEW, GLOBAL TEMPORARY, LOCAL TEMPORARY, SYSTEM VIEW.


Представление (View) определяет подмножество строк и столбцов из набора данных, но само не содержит их. Представления не могут объединять данные из нескольких наборов данных.

Для обеспечения расширенных возможностей управления транзакциями объектная модель OLE DB включает объект Transaction.

OLE DB провайдеры, как и все COM-компоненты, регистрируются в реестре Windows. Для поиска информации о зарегистрированных источниках данных используются специальные объекты, называемые нумераторами. Нумератор - это обычный СОМ-сервер, позволяющий получить информацию об источниках данных в виде результирующего набора. Для создания такого результирующего набора в объектном типе DataSource специфицирован интерфейс IDBEnumerateSources.

Для каждого объектного типа спецификация OLE DB определяет набор интерфейсов, который должен обязательно быть реализован для данного объекта. Такие интерфейсы отмечаются как [mandatory]. Интерфейсы, которые могут отсутствовать, отмечаются как [optional].

Для объекта "источник данных" специфицирован следующий набор интерфейсов:

CoType TDataSource { [mandatory] interface IDBCreateSession; [mandatory] interface IDBInitialize; [mandatory] interface IDBProperties; [mandatory] interface IPersist; [optional] interface IConnectionPointContainer; [optional] interface IDBAsynchStatus; [optional] interface IDBDataSourceAdmin; [optional] interface IDBInfo; [optional] interface IPersistFile; [optional] interface ISupportErrorInfo; }

Для объекта "сеанс" специфицирован следующий набор интерфейсов:

CoType TSession { [mandatory] interface IGetDataSource; [mandatory] interface IOpenRowset; // Создание набора данных [mandatory] interface ISessionProperties; [optional] interface IAlterIndex; [optional] interface IAlterTable; [optional] interface IBindResource; [optional] interface ICreateRow; [optional] interface IDBCreateCommand; [optional] interface IDBSchemaRowset; [optional] interface IIndexDefinition; [optional] interface ISupportErrorInfo; [optional] interface ITableCreation; [optional] interface ITableDefinition; // Для создания таблицы [optional] interface ITableDefinitionWithConstraints; [optional] interface ITransaction; [optional] interface ITransactionJoin; [optional] interface ITransactionLocal; [optional] interface ITransactionObject; }



Для объекта " результирующий набор" специфицирован следующий набор интерфейсов:

CoType TRowset { [mandatory] interface IAccessor; [mandatory] interface IColumnsInfo; [mandatory] interface IConvertType; [mandatory] interface IRowset;// Последовательное // чтение таблицы [mandatory] interface IRowsetInfo; [optional] interface IChapteredRowset; [optional] interface IColumnsInfo2; [optional] interface IColumnsRowset; [optional] interface IConnectionPointContainer; [optional] interface IDBAsynchStatus; [optional] interface IGetRow; [optional] interface IRowsetChange; // Для удаления, изменения и добавления // строк в набор данных [optional] interface IRowsetChapterMember; [optional] interface IRowsetCurrentIndex; [optional] interface IRowsetFind; [optional] interface IRowsetIdentity; [optional] interface IRowsetIndex; [optional] interface IRowsetLocate; // Прямое // позиционирование на запись набора данных [optional] interface IRowsetRefresh; // Для // обновления данных в созданном наборе данных [optional] interface IRowsetScroll; // Поддержка // скроллинга по набору данных [optional] interface IRowsetUpdate; [optional] interface IRowsetView; [optional] interface ISupportErrorInfo; [optional] interface IRowsetBookmark; }

Все объекты объектного типа Rowset должны реализовывать следующие интерфейсы:

интерфейс IRowset, используемый для извлечения строк;интерфейс IAccessor, используемый для определения связывания; интерфейс IColumnsInfo, предоставляющий информацию о столбцах результирующего набора;интерфейс IRowsetInfo, предоставляющий информацию о самом результирующем наборе;интерфейс IConvertType, предоставляющий информацию о преобразовании типов данных, поддерживаемых в результирующем наборе.


Объекты COMMAND


Перед использованием объекта Command следует определить, поддерживается ли данный объект. Для этого с помощью метода QueryInterface следует запросить интерфейс IDBCreateCommand объекта "сеанс".

Объект Command должен реализовывать следующие интерфейсы:

Icommand;Iaccessor;IcommandText;IcolumnInfo;ICommandProperties.

Для создания команды вызывается метод IDBCreateCommand::CreateCommand объекта "сеанс".

Например:

ICommandText pICommandText; HRESULT hr= pIDBCreateCommand-> CreateCommand( NULL, // если есть агрегирование, то указатель // на управляющий IUnknown IID_ICommandText, // Запрашиваемый интерфейс (IUnknown**)& pICommandText); // Указатель // на запрашиваемый интерфейс

Текст, выполняемый командой, устанавливается при вызове метода ICommandText::SetCommandText. При этом указывается уникальный идентификатор GUID синтаксиса команды (например, DBGUID_SQL (только для версий OLE DB начиная с 1.5)).

Например:

pICommandText->SetCommandText(DBGUID_SQL, "SELECT * FROM TBL1");

Для выполнения команды вызывается метод ICommand::Execute (этот метод наследуется интерфейсом ICommandText).

Например:

ULONG ulRs=0; IRowset** ppRowsets=NULL; HRESULT hr= pICommandText->Execute ( NULL, // если есть агрегирование, то указатель // на управляющий IUnknown IID_IRowset, // Запрашиваемый интерфейс NULL, // Указатель на структуру типа // struct DBPARAMS { // void *pData; // DB_UPARAMS cParamSets; // HACCESSOR hAccessor; //}; ulRs, // Количество строк, на которые // воздействовала команда INSERT, UPDATE //или DELETE (IUnknown**)& ppRowsets); // Указатель на // указатели наборов данных

Алгоритм выполнения команды приведен на следующей схеме:


До выполнения команды можно определить поведение создаваемого результирующего набора вызовом метода ICommandProperties::SetProperties.

Для многократного выполнения запроса и при использовании параметров следует вызвать метод ICommandPrepare::Prepare, а затем определить параметры вызовом метода ICommandWithParameters::SetParameterInfo.

Если в результате выполнения команды возвращается несколько результирующих наборов, то используется метод IMultipleResults::GetResult.



Объекты TRANSACTION


Применение OLE DB позволяет поддерживать простые, вложенные и распределенные транзакции.

Объект Session для работы с транзакциями поддерживает следующие интерфейсы:

интерфейс ITransactionLocal. Для начала транзакции вызывается метод ITransactionLocal::StartTransaction(). Если этот метод вызывается из активной транзакции, то открывается новая вложенная транзакция;интерфейс ITransaction, поддерживающий методы Abort, Commit и GetTransactionInfo;интерфейс ITransactionJoin, реализующий поддержку распределенных транзакций.

Объект Transaction позволяет реализовывать более широкие возможности управления транзакциями, поддерживая следующие интерфейсы:

ITransaction, позволяющий выполнить прерывание транзакции (методы Abort, Commit, GetTransactionInfo);IConnectionPointContainer, поддерживающий управление точками соединения для соединяемых объектов.



Создание результирующего набора


При реализации доступа к БД посредством OLE DB провайдера сначала следует создать объект данных и установить соединение с базой данных. Далее необходимо создать объект "сеанс". И только потом можно создавать результирующий набор.

Механизм создания объекта "сеанс" приведен на следующей схеме.


Результирующий набор может быть создан одним из следующих способов:

Для объекта "сеанс" вызывается метод IOpenRowset::OpenRowset, выполняющий непосредственное создание результирующего набора (интерфейс IOpenRowset должен поддерживаться любым провайдером);Для объекта "сеанс" вызывается метод IDBCreateCommand::CreateCommand, создающий объект Command. Далее для объекта "команда" вызывается метод ICommand::Execute. (при использовании интерфейса IMultipleResults можно работать с несколькими результирующими наборами); Вызывается один из следующих методов IColumnsRowset::GetColumnsRowset, IDBSchemaRowset::GetRowset, IViewRowset::OpenViewRowset или ISourcesRowset::GetSourcesRowset.

Чтобы результирующий набор, хранимый на сервере, можно было использовать, необходимо выполнить связывание и извлечение данных. Для этого следует определить структуры типа DBBINDING, описывающие столбцы, и создать аксессор. Далее для получения строк результирующего набора можно использовать один из следующих методов:

IRowset::GetNextRows;IRowsetLocate::GetRowsByBookMarks;IRowsetLocate::GetRowAt;IRowsetScroll:: GetRowAtRatio.

В заключение для записи данных в структуру, определенную аксессором, вызывается метод IRowset::GetData.

После получения и обработки строк их следует освободить, вызвав метод IRowset::ReleaseRows.

После просмотра всего результирующего набора следует также освободить аксессор, вызвав метод IRowset::ReleaseAccessor, и освободить сам результирующий набор, вызвав метод IRowset::Release.

Интерфейс IAccessor определяет следующие методы:

AddRefAccessor - увеличивает число ссылок на данный аксессор;CreateAccessor - создает аксессор из набора связываний;GetBindings - возвращает связывания, установленные данным аксессором;ReleaseAccessor - освобождает аксессор.


Для создания аксессора следует запросить интерфейс IAccessor и выполнить следующий код:

HRESULT hr=pIAccessor-> CreateAccessor();

Метод CreateAccessor имеет следующее формальное описание:

HRESULT CreateAccessor ( DBACCESSORFLAGS dwAccessorFlags, // Свойства // аксессора и как он используется DBCOUNTITEM cBindings, // Число связей // в аксессоре const DBBINDING rgBindings[], // Описание // столбца или параметра DBLENGTH cbRowSize, // Число байтов, // используемых для одного набора параметров HACCESSOR *phAccessor, // Указатель //на созданный аксессор DBBINDSTATUS rgStatus[]); // Массив значений, // определяющий статус // каждого связывания

Каждый столбец формируемого результирующего набора или параметр описывается структурой DBBINDING, которая имеет следующее формальное описание:

typedef struct tagDBBINDING { DBORDINAL iOrdinal; // Порядковый номер // столбца или параметра (начиная с 1) DBBYTEOFFSET obValue; // Сдвиг в байтах для // значения столбца или параметра в буфере // (указатель на буфер задается при // создании аксессора) DBBYTEOFFSET obLength; DBBYTEOFFSET obStatus; ITypeInfo *pTypeInfo; DBOBJECT *pObject; DBBINDEXT *pBindExt; DBPART dwPart; DBMEMOWNER dwMemOwner; DBPARAMIO eParamIO; DBLENGTH cbMaxLen; DWORD dwFlags; DBTYPE wType; BYTE bPrecision; BYTE bScale; } DBBINDING;

Поле wType определяет тип столбца или параметра, который описывается следующим образом:

typedef WORD DBTYPE; enum DBTYPEENUM { // Следующие значения точно соответствуют VARENUM // при автоматизации и не могут быть использованы // как VARIANT. DBTYPE_EMPTY = 0, // Значение отсутствует, // соответствующего типа С нет DBTYPE_NULL = 1, // Значение равно NULL, // соответствующего типа С нет DBTYPE_I2 = 2, // Двухбайтовое целое со знаком, // соответствует С типу short DBTYPE_I4 = 3, // Четырехбайтовое целое со знаком, // соответствует С типу long DBTYPE_R4 = 4, DBTYPE_R8 = 5, // Вещественное двойной точности, // соответствует С типу Double

DBTYPE_CY = 6, // Тип для значения Cyrrency DBTYPE_DATE = 7, // Тип для значения даты // (дата хранится в виде вещественного числа: // целочисленная часть определяет дату, // а дробная - время) DBTYPE_BSTR = 8, // Указатель на строку BSTR DBTYPE_IDISPATCH = 9, // Указатель на интерфейс // IDispatch DBTYPE_ERROR = 10, // 32-битовый код ошибки DBTYPE_BOOL = 11, // Для логического значения DBTYPE_VARIANT = 12, // Для значения VARIANT DBTYPE_IUNKNOWN = 13, // Указатель на интерфейс // IUnknown DBTYPE_DECIMAL = 14, DBTYPE_UI1 = 17, // Однобайтовое беззнаковое целое, // соответствует С типу byte DBTYPE_ARRAY = 0x2000, DBTYPE_BYREF = 0x4000, DBTYPE_I1 = 16, DBTYPE_UI2 = 18, DBTYPE_UI4 = 19,



// Следующие значения точно соответствуют VARENUM // при автоматизации, но не могут быть использованы // как VARIANT. DBTYPE_I8 = 20, DBTYPE_UI8 = 21, DBTYPE_GUID = 72, // Для уникального идентификатора GUID DBTYPE_VECTOR = 0x1000, DBTYPE_FILETIME = 64, DBTYPE_RESERVED = 0x8000,

// Следующие значения недопустимы в VARENUM для OLE. DBTYPE_BYTES = 128, DBTYPE_STR = 129, DBTYPE_WSTR = 130, DBTYPE_NUMERIC = 131, DBTYPE_UDT = 132, DBTYPE_DBDATE = 133,// Для даты, определяемой // как структура // Typedef struct tagDBDATE { // SHORT year; // USHORT month; // USHORT day; // } DBDATE; DBTYPE_DBTIME = 134, DBTYPE_DBTIMESTAMP = 135 // Для даты и времени, // определяемых как структура // Typedef struct tagDBTIMESTAMP { // SHORT year; // USHORT month; // USHORT day; // USHORT hour; // USHORT minute; // USHORT second; // ULONG fraction; } DBTIMESTAMP;

DBTYPE_HCHAPTER = 136 DBTYPE_PROPVARIANT = 138, DBTYPE_VARNUMERIC = 139 };