Соединение с базой данных
На этой консультации, мы изучим механику использования ODBC API.
Ваша программа не общается непосредственно с драйверами ODBC, она пользуется услугами менеджера Управление работой менеджера осуществляется с помощью API функций, к которым вы можете обращаться непосредственно из своей программы, вы должны только подключить odbc32.inc и odbc32.lib, а так же windows.inc.
Шаги которые необходимо предпринять для соединения с базой данных следующие:
- Получить идентификатор окружения. Вам нужно делать это только один раз за ODBC-сеанс. Как только вы получите идентификатор, вы сможете модифицировать свойства окружения так, чтобы они удовлетворяли вашим требованиям. Вы можете понимать этот шаг как создание некой рабочей области.
- Указать какую версию ODBC ваша программа хочет использовать. Вы можете выбрать между версиями ODBC 2.x и 3.x. Они различны во многих аспектах, поэтому этот шаг - необходим. Таким образом менеджер ODBC сможет решить какой синтаксис он должен использовать, чтобы связаться с вашей программой и проинтерпретировать её команды.
- Выделить память для идентификатора соединения. Этот шаг может рассматриваться как создание пустого соединения. Вы не обязаны определять нужный для соединения с базой данных драйвер.
- Установить связь. Вы вызываете функцию ODBC, чтобы установить связь.
Когда вы закончите работу с базой данных, вы должны закрыть связь с ней следующим образом:
- Отключится от источника данных
- Уничтожить идентификатор соединения
- Уничтожить идентификатор окружения (если вы не желаете использовать это окружение для других соединений)
ВЫДЕЛЕНИЕ ПАМЯТИ ДЛЯ ИДЕНТИФИКАТОРА
В версиях ODBC до 3.x, вам нужно вызывать отдельные функции, чтобы выделить память для идентификатора окружения, соединения иинструкции (SQLAllocEnv, SQLAllocConnect, SQLAllocStmt). Теперь под ODBC 3.x все функции заменяются SQLAllocHandle, которая имеет следующий синтаксис: SQLRETURN SQLAllocHandle(SQLSMALLINT HandleType, SQLHANDLE InputHandle, SQLHANDLE * OutputHandlePtr );
Вышеуказанный синтаксис может утомить ваш взор, поэтому мы его немного упростим. SQLAllocHandle proto HandleType:DWORD, InputHandle:DWORD, OutputHandlePtr:DWORD
SQLRETURN определен как тип SQLSMALLINT, SQLSMALLINT определен как короткое целое, т.e. слово (16 бит). Таким образом функция возвращает выходную величину в ax, а не в eax, что является очень важным. Однако параметр передаётся функции под Win32 в 32-битном стеке. Поэтому, даже если параметр определён как словный (16-бит) вы должны его расширить до 32-бит. Вот почему HandleType - dword вместо word. Вы можете свериться с библиотекой импорта: odbc32.lib. Вход для SQLAllocHandleэто _SQLAllocHandle@12. Эта запись означает, что комбинированный размер параметров составляет 12 байт (3 слова). Тем не менее, это не означает, что функциональный прототип C - неверный. SQLAllocHandle будет только использовать младшее слово HandleTypeи игнорировать старшее слово. Таким образом, функциональный прототип C - корректен пока наш asm-функциональный прототип соответствует сказанному выше.
С типом SQL разберёмся более обстоятельно. Рассмотрим входные функциональные параметры и обратную величину.
- HandleType - константа, которая определяет тип идентификатора, для которого вы хотите распределить память. Возможные величины:
SQL_HANDLE_ENV
идентификатор окружения SQL_HANDLE_DBC идентификатор соединения SQL_HANDLE_STMT идентификатор запроса SQL_HANDLE_DESC идентификатор дескриптора
Дескриптор - это коллекция метаданных, которые описывают параметры инструкций SQL или столбцов с набором результатов, которые обрабатываются приложением или драйвером. - InputHandle - идентификатор родительского "контекста". Если вы хотите выделить память для идентификатора подключения, вы должны получить идентификатор окружения, потому что подключение будет сделано в контексте этого окружения. Если вы хотите выделить память для идентификатора окружения, этот параметр должен быть SQL_HANDLE_NULL (остерегайтесь значения SQL_HANDLE_NULL в windows.inc версии 1.18 и ниже, т.к. там допущена ошибка и определено ненадлежащим образом как 0L. Вы должны стереть "L",иначе ваша программа не будет транслироваться. Что касается операторных и дескрипторных идентификаторов, то вы должны передать идентификатор подключения как этот параметр.
- OutputHandlePtr указывает на dword переменную, которая получит распределенный идентификатор, если запрос успешен.
Возможные возвращаемые значения SQLAllocHandle могут быть:
SQL_SUCCESS | Функция завершена успешно |
SQL_SUCCESS_WITH_INFO | Функция завершена успешно, но с предупреждением |
SQL_ERROR | Функция потерпела неудачу. |
SQL_INVALID_HANDLE | Идентификатор переданный функции, недействителен |
Выполнилась ли функция успешно или потерпела неудачу, вы можете получить подробную информацию относительно этого, вызывая SQLGetDiagRecили SQLGetDiagField. Они играют ту же самую роль, что и GetLastError в Win32 API.
Пример: .data? hEnv dd ?
.code invoke SQLAllocHandle, SQL_HANDLE_ENV, SQL_HANDLE_NULL, addr hEnv .if ax==SQL_SUCCESS || ax==SQL_SUCCESS_WITH_INFO
ВЫБОР ВЕРСИИ ODBC
После выделения памяти для идентификатора окружения вы должны установить атрибут окружения, SQL_ATTR_ODBC_VERSION, в соответствующее значение. Установка значения атрибута окружения делается, вызовом SQLSetEnvAttr. К настоящему времени вы должны знать, что имеются также функции SQLSetConnectAttr и SQLSetStmtAttr. SQLSetEnvAttr определена как: SQLSetEnvAttr proto EnvironmentHandle:DWORD, Attribute:DWORD, ValuePtr:DWORD, StringLength:DWORD
Список возможных возвращаемых значений идентичен SQLAllocHandle.
Пример: .data? hEnv dd ?
.code invoke SQLAllocHandle, SQL_HANDLE_ENV, SQL_HANDLE_NULL, addr hEnv .if ax==SQL_SUCCESS || ax==SQL_SUCCESS_WITH_INFO invoke SQLSetEnvAttr, hEnv, SQL_ATTR_ODBC_VERSION, SQL_OV_ODBC3, NULL .if ax==SQL_SUCCESS || ax==SQL_SUCCESS_WITH_INFO
ВЫДЕЛЕНИЕ ПАМЯТИ ДЛЯ ИДЕНТИФИКАТОРА ПОДКЛЮЧЕНИЯ
Этот шаг подобен выделению памяти для ид. окружения, вы также вызываете SQLAllocHandle, но передаёте другое значение параметра.
Пример: .data? hEnv dd ? hConn dd ?
.code invoke SQLAllocHandle, SQL_HANDLE_ENV, SQL_HANDLE_NULL, addr hEnv .if ax==SQL_SUCCESS || ax==SQL_SUCCESS_WITH_INFO invoke SQLSetEnvAttr, hEnv, SQL_ATTR_ODBC_VERSION, SQL_OV_ODBC3, NULL .if ax==SQL_SUCCESS || ax==SQL_SUCCESS_WITH_INFO invoke SQLAllocHandle, SQL_HANDLE_DBC, hEnv, addr hConn .if ax==SQL_SUCCESS || ax==SQL_SUCCESS_WITH_INFO
УСТАНОВКА СВЯЗИ
Теперь мы готовы сделать попытку фактического подключения к источнику данных через выбранный ODBC драйвер. Имеются фактически три функции ODBC, которые мы можем использовать, чтобы достичь этой цели. Они предлагают различную степень "выбора", который вы можете сделать.
SQLConnect | Ядро | Это - самая простая функция. Требуется только DSN (название источника Данных) и необязательное название пользователя и пароль. Она не предлагает интерфейса GUI типа подсказки пользователю в виде диалогового окна для получения дополнительной информации. Вы должны использовать эту функцию, если вы уже имеете DSN для заданной базы данных. |
SQLDriverConnect | Ядро | Эта функция предлагает более широкий спектр услуг чем SQLConnect. Вы можете соединяться с источником данных, который не определен в системной информации, то есть без DNS. Кроме того, вы можете определить, отобразит ли эта функция диалоговое окно, запрашивающее пользователя для получения дополнительной информации. Например, если вы опустили имя файла базы данных, она будет инструктировать ODBC драйвер об отображении диалогового окна, запрашивающего пользователя выбрать базу данных, для соединения с ней. |
SQLBrowseConnect | Уровень 1 | Эта функция предлагает перечисление источников данных во время выполнения. Она обеспечивает более гибкий интерфейс в сравнении с SQLDriverConnect, потому что вы можете вызывать SQLBrowseConnect неско раз последовательно, каждый раз запрашивая пользователя для получения более конкретной информации, пока наконец вы не получите рабочую строку подключения. |
Я буду исследовать сначала SQLConnect. Чтобы использовать SQLConnect, вы должны знать кое-что относительно DSN. DSN расшифровывается как Название Источника Данных, т.е. это строка, которая уникально идентифицирует источник данных. DSN идентифицирует строение данных, которое содержит информацию о том, как соединиться с удельным источником данных. Информация включает и то, какой ODBC-драйвер использовать и с какой базой данных соединиться. Вы создаете, изменяете и удаляете DSN, используя 32-разрядного ODBC Администратора в панели управления.
SQLConnect имеет следующий синтаксис: SQLConnect proto ConnectionHandle:DWORD pDSN:DWORD, DSNLength:DWORD, pUserName:DWORD, NameLength:DWORD, pPassword:DWORD, PasswordLength:DWORD
По минимуму, SQLConnect требует идентификатор соединения, DSN и их длину: имя пользователя и пароль необязательны, если источник данных не требует их. Список возможных возвращаемых значений идентичен таковому SQLAllocHandle. Предположим мы имеем DSN, называемый "Продажи" в нашей системе, и мы хотим соединиться с ним. Мы можем сделать это следующим образом: .data DSN db "Sales",0
.code ...... invoke SQLConnect, hConn, addr DSN, sizeof DSN,0,0,0,0
Один из недостатков SQLConnect - то, что, вы должны создать DSN прежде, чем сможете соединяться с источником данных. SQLDriverConnect предлагает более гибкий вариант. Она имеет следующий синтаксис: SQLDriverConnect proto ConnectionHandle:DWORD, hWnd:DWORD, pInConnectString:DWORD, InStringLength:DWORD, pOutConnectString:DWORD, OutBufferSize:DWORD, pOutConnectStringLength:DWORD, DriverCompletion:DWORD
SQL_DRIVER_PROMPT | ODBC драйвер запрашивает пользователя относительно информации. Эта информация используется для создания строки подключения. |
SQL_DRIVER_COMPLETE SQL_DRIVER_COMPLETE_REQUIRED |
ODBC драйвер запросит пользователя только, если строка подключения, составленная в вашей программе не закончена. |
SQL_DRIVER_NOPROMPT | ODBC драйвер не будет запрашивать пользователя для получения дополнительной информации. |
/li>
Пример: .data strConnect db "DBQ=c:\data\test.mdb;DRIVER={Microsoft Access Driver (*.mdb)};",0
.data? buffer db 1024 dup(?) OutStringLength dd ?
.code ..... invoke SQLDriverConnect, hConn, hWnd, addr strConnect, sizeof strConnect, addr buffer, sizeof buffer, addr OutBufferLength, SQL_DRIVER_COMPLETE
РАЗЪЕДИНЕНИЕ С ИСТОЧНИКОМ ДАННЫХ
После того, как подключение сделано успешно, вы можете создать одну или большее количество инструкций и сделать запрос источнику данных. Я буду исследовать эту часть на следующей консультации. Пока, давайте предположим, что вы уже отработали с источником данных, и должны разъединится с ним, вызывая SQLDisconnect. Эта функция проста (Это отражение грубой и грустной действительности о том, что разрушение - намного проще чем конструкция или созидание). Требуется только один параметр, маркер подключения. invoke SQLDisconnect, hConn
УДАЛЕНИЕ ИДЕНТИФИКАТОРОВ ПОДКЛЮЧЕНИЯ И СРЕДЫ
После успешного разъединения вы можете уничтожить идентификаторы подключения и среды, вызывая SQLFreeHandle. Это - новая функция, вводимая в ODBC 3.x., она заменяет SQLFreeConnect, SQLFreeEnvи SQLFreeStmt. SQLFreeHandle имеет следующий синтаксис:
SQLFreeHandle proto HandleType:DWORD, Handle:DWORD
Например:
invoke SQLFreeHandle, SQL_HANDLE_DBC, hConn invoke SQLFreeHandle, SQL_HANDLE_ENV, hEnv
[C] Iczelion, пер. SheSan