ODBC. Урок 2. Соединение с базой данных — Архив WASM.RU

Все статьи

ODBC. Урок 2. Соединение с базой данных — Архив WASM.RU

  На этой консультации, мы изучим механику использования ODBC API.

  Ваша программа не общается непосредственно с драйверами ODBC, она пользуется услугами менеджера ODBC. Управление работой менеджера осуществляется с помощью API функций к которым вы можете обращатся непосредственно из своей программы, вы должны только подключить odbc32.inc и odbc32.lib, а так же windows.inc.

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

  1. Получить идентификатор окружения. Вам нужно делать это только один раз за ODBC-сеанс. Как только вы получите идентификатор, вы сможете модифицировать свойства окружения так, чтобы они удовлетворяли вашим требованиям. Вы можете понимать этот шаг как создание некой рабочей области.
  2. Указать какую версию ODBC ваша программа хочет использовать. Вы можете выбрать между версиями ODBC 2.x и 3.x. Они различны во многих аспектах, поэтому этот шаг - необходим. Таким образом менеджер ODBC сможет решить какой синтаксис он должен использовать, чтобы связаться с вашей программой и проинтерпретировать её команды.
  3. Выделить память для идентификатора соединения. Этот шаг можетрассматриватся как создание пустого соединения. Вы не обязаны определять нужный для соединения с базой данных драйвер.
  4. Установить связь. Вы вызываете функцию ODBC, чтобы установить связь.

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

  1. Отключится от источника данных
  2. Уничтожить идентификатор соединения
  3. Уничтожить идентификатор окружения (если вы нежелаете использовать это окружение для других соединений)

ВЫДЕЛЕНИЕ ПАМЯТИ ДЛЯ ИДЕНТИФИКАТОРА

  В версиях 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. Вы можете cвериться с библиотекой импорта: 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

  • EnvironmentHandle. Содержит идентификатор окружения, атрибут которого вы хотите установить.
  • Attribute. Константа, которая представляет атрибут, который выхотите установить. Для нашей цели, это - SQL_ATTR_ODBC_VERSION. Вы можете искать полный списокв MSDN.
  • ValuePtr. Значение этого параметра зависит от атрибута, который вы хотите установить. Если атрибут - 32-разрядное значение, этот параметр обрабатывается как значение, которое вы хотите установить. Если атрибут - текстовая строка или двоичный буфер, то это интерпретируется как указатель на строку или буфер. Если вы определяете SQL_ATTR_ODBC_VERSION, то имеются два возможных значения, которые вы можете использовать: SQL_OV_ODBC3 и SQL_OV_ODBC2, для ODBC версий 3.x и 2.x соответственно.
  • StringLength. Размер значения, указанного ValuePtr. Если значение - строка или двоичный буфер, этот параметр должен быть действителен. Если атрибут, который вы хотите установить - dword, этот параметр игнорируется. С тех пор как SQL_ATTR_ODBC_VERSION атрибут содержит значение dword, вы можете передавать NULL как этот параметр.

  Список возможных возвращаемых значений идентичен 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
  • ConnectionHandle. Идентификатор подключения который вы хотите использовать.
  • pDSN. Указатель на DSN-строку.
  • DSNLength. Длина DSN-строки.
  • pUserName. Указатель на строку содержащую имя пользователя.
  • NameLength. Длинна строки содержащей имя пользователя.
  • pPassword. Указатель на строку содержащую пароль ассоциированный с данным именем пользователя.
  • PasswordLength. Длина пароля

  По минимуму, 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
  • ConnectionHandle. Идентификатор соединения.
  • hWnd. Дескриптор вашего окна. Если вы передадите NULL как параметр, драйвер не будет запрашивать пользователя для получения дополнительной информации (если необходимо).
  • pInConnectString.Указатель на строку подключения. Это - ASCIIZ строка, которая отформатирована, согласно специфике ODBC драйвера, с которым вы хотите соединиться. Она описывает название драйвера и источника данных а так же некоторые дополнительные параметры. Полное описание строки подключения можно найти в MSDN. Здесь я не буду углублятся в подробности.
  • InStringLength. Длина строки соединения.
  • pOutConnectString. Указатель на буфер, который будет заполнен законченной строкой подключения. Размер этого буфера должен быть по крайней мере 1,024 байта. Это может звучать запутывающе. Если строка подключения, которую вы передаёте функции, не закончена, то в этом случае, ODBC драйвер может запрашивать пользователя для получения дополнительной информации. ODBC драйвер в этом случае создает законченную строку подключения из всей располагаемой информации и помещает её в буфер. Даже если строка подключения, которую вы составили, была функциональна, этот буфер будет заполнен большим количеством атрибутов. Цель этого параметра - сохранить законченную строку подключения для будущего подключения.
  • OutBufferSize. Размер буфера, указанного pOutConnectString.
  • pOutConnectStringLength. Указатель на dword переменную, которая получит фактическую длину законченной строки подключения, возвращенной ODBC драйвером.
  • DriverCompletion. Флаг, который определяет запросит ли ODBC менеджер/драйвер пользователя для получения дополнительной информации. Однако, флаг зависит от того, передаёте ли вы дескриптор окна hWnd параметру SQLDriverConnect. Если вы не делали этого, ODBC менеджер/драйвер не будет запрашивать пользователя, даже если этот флаг инструктирует об этом.
    SQL_DRIVER_PROMPTODBC драйвер запрашивает пользователя относительно информации. Эта информация используется для создания строки подключения.
    SQL_DRIVER_COMPLETE
    SQL_DRIVER_COMPLETE_REQUIRED
    ODBC драйвер запросит пользователя только, если строка подключения, составленная в вашей программе не закончена.
    SQL_DRIVER_NOPROMPTODBC драйвер не будет запрашивать пользователя для получения дополнительной информации.

  Пример:

.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
  • HandleType. Константа, которая идентифицирует тип идентификатора, который вы передаёте этой функции как второй параметр. Возможные значения - те же самые, как и в SQLAllocHandle
  • Handle. Идентификатор который вы хотите удалить.

  Например:

invoke SQLFreeHandle, SQL_HANDLE_DBC, hConn
invoke SQLFreeHandle, SQL_HANDLE_ENV, hEnv

2002-2013 (c) wasm.ru