2008-12-02 3 views
32

В моей текущей работе мы стремимся реализовать собственный драйвер odbc, чтобы позволить многим различным приложениям подключаться к нашему собственному приложению в качестве источника данных. Сейчас мы пытаемся взвесить варианты разработки нашего собственного драйвера для спецификации реализации, которая является массивной, или с использованием SDK, который позволяет программистам «заполнять» детали, специфичные для данных, и допускать более высокие уровни абстракции.Создание пользовательского драйвера ODBC

Кто-нибудь еще реализовал пользовательский драйвер odbc? В какие подводные камни вы столкнулись? Какие преимущества вы видели от этого? Сколько человеко-часов вы бы приблизили? Вы использовали SDK, и если да, то какие выгоды/недостатки вы видели из этого подхода?

Любые комментарии и ответы были бы весьма признательны. Благодаря!

EDIT: Мы стараемся поддерживать мобильность с нашим кодом, который написан на C.

+2

Могу ли я спросить: почему вы решили создать пользовательский драйвер odbc (15-летняя технология), а не драйвер oleDb (10-летняя технология) или управляемый код ADO, поставщик данных Net (8-летний и текущий)? – 2008-12-02 18:50:57

+0

Наш код написан на языке C, и мы хотим, чтобы он поддерживал переносимость. – 2008-12-02 18:53:37

+0

Кроме того, довольно много систем (MSQuery и Access приходят на ум) были действительно разработаны с учетом ODBC и лучше всего работают с этим. – ConcernedOfTunbridgeWells 2008-12-02 19:39:14

ответ

7

У меня нет, но я однажды дал интервью в компании, которая сделала именно это. Они сделали продуктом 4GL/DBMS под названием AMPS той же архитектуры, что и MUMPS - иерархическая база данных со встроенным 4GL (целый жанр таких систем вышел в 1970-х годах). У них была довольно значительная базовая база кода и клиенты, желающие подключиться к ней с помощью MS Access.

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

Одной из альтернатив для этого было бы предоставление продукта данных/продукта BI (вдоль линий SAP BW), который представляет ваши данные приложения во внешней базе данных и массирует его в более дружественный формат, такой как звезда или снежинка схемы.

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

5

Я не реализован драйвер ODBC, а просто хотел предложить предположение, что вы можете начать с открытым -source и добавить свои собственные настройки. Это может заставить вас начать намного быстрее.

Есть как минимум два варианта:

  • unixODBC лицензирован под LGPL, что означает, если вы измените код, который вы должны сделать ваши изменения с открытым исходным кодом.

  • iODBC предоставляется по лицензии LGPL или New BSD по вашему выбору. Новый BSD позволяет вносить изменения без внести свои изменения с открытым исходным кодом.

Однако, это не ясно, если эти пакеты работают на Windows, а не работает на UNIX/Linux с клиентского API в соответствии с стандартным ODBC. Вы не указываете, какую платформу вы используете, поэтому я не знаю, относится ли это к вам.

9

Драйверы ODBC очень сложны - решение написать одно не следует воспринимать легкомысленно.Обзор существующих драйверов с открытым исходным кодом является хорошим подходом к примерам, но большинство из них имеют недостатки, которые вы не хотите эмулировать :). API одинаковы независимо от платформы ОС. FreeTDS для MSSQL/Sybase имеет одну из лучших версий ODBC-драйверов с открытым исходным кодом, которые я видел.

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

  • функции доступа к метаданным
  • ODBC синтаксис конкретный запрос разборе
  • SQLSTATE об ошибках отображения сообщений
  • Multibyte/набор символов сортировочного
  • ODBC версия 2,3 поддержки - сообщения об ошибках/Отображению функций
  • курсоров
  • Конфигурационный интерфейс DM для управления источником данных
15

Другой вариант: вместо создания драйвера ODBC реализуйте задний конец, в котором говорится о проводном протоколе, который использует другая база данных (например, Postgresql или MySQL).

Ваши пользователи могут затем загрузить и использовать, например, драйвер Postgresql ODBC.

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

И Postgres, и MySQL имеет приличную документацию для своих клиент-серверных протоколов.

Простой пример Python 2.7 сервера, который понимает части протокола проводки Postgresql, приведен ниже. Пример сценария создает сервер, который слушает порт 9876. Я могу использовать команду psql -h localhost -p 9876 для подключения к серверу. Любой выполненный запрос возвращает результирующий набор с столбцами abc и def и двумя строками, все значения NULL.

Чтение документов Postgresql и использование чего-то типа wireshark для проверки реального трафика протокола сделало бы довольно простым реализовать совместимый с Postgresql задний конец.

import SocketServer 
import struct 

def char_to_hex(char): 
    retval = hex(ord(char)) 
    if len(retval) == 4: 
     return retval[-2:] 
    else: 
     assert len(retval) == 3 
     return "0" + retval[-1] 

def str_to_hex(inputstr): 
    return " ".join(char_to_hex(char) for char in inputstr) 

class Handler(SocketServer.BaseRequestHandler): 
    def handle(self): 
     print "handle()" 
     self.read_SSLRequest() 
     self.send_to_socket("N") 

     self.read_StartupMessage() 
     self.send_AuthenticationClearText() 
     self.read_PasswordMessage() 
     self.send_AuthenticationOK() 
     self.send_ReadyForQuery() 
     self.read_Query() 
     self.send_queryresult() 

    def send_queryresult(self): 
     fieldnames = ['abc', 'def'] 
     HEADERFORMAT = "!cih" 
     fields = ''.join(self.fieldname_msg(name) for name in fieldnames) 
     rdheader = struct.pack(HEADERFORMAT, 'T', struct.calcsize(HEADERFORMAT) - 1 + len(fields), len(fieldnames)) 
     self.send_to_socket(rdheader + fields) 

     rows = [[1, 2], [3, 4]] 
     DRHEADER = "!cih" 
     for row in rows: 
      dr_data = struct.pack("!ii", -1, -1) 
      dr_header = struct.pack(DRHEADER, 'D', struct.calcsize(DRHEADER) - 1 + len(dr_data), 2) 
      self.send_to_socket(dr_header + dr_data) 

     self.send_CommandComplete() 
     self.send_ReadyForQuery() 

    def send_CommandComplete(self): 
     HFMT = "!ci" 
     msg = "SELECT 2\x00" 
     self.send_to_socket(struct.pack(HFMT, "C", struct.calcsize(HFMT) - 1 + len(msg)) + msg) 

    def fieldname_msg(self, name): 
     tableid = 0 
     columnid = 0 
     datatypeid = 23 
     datatypesize = 4 
     typemodifier = -1 
     format_code = 0 # 0=text 1=binary 
     return name + "\x00" + struct.pack("!ihihih", tableid, columnid, datatypeid, datatypesize, typemodifier, format_code) 

    def read_socket(self): 
     print "Trying recv..." 
     data = self.request.recv(1024) 
     print "Received {} bytes: {}".format(len(data), repr(data)) 
     print "Hex: {}".format(str_to_hex(data)) 
     return data 

    def send_to_socket(self, data): 
     print "Sending {} bytes: {}".format(len(data), repr(data)) 
     print "Hex: {}".format(str_to_hex(data)) 
     return self.request.sendall(data) 

    def read_Query(self): 
     data = self.read_socket() 
     msgident, msglen = struct.unpack("!ci", data[0:5]) 
     assert msgident == "Q" 
     print data[5:] 


    def send_ReadyForQuery(self): 
     self.send_to_socket(struct.pack("!cic", 'Z', 5, 'I')) 

    def read_PasswordMessage(self): 
     data = self.read_socket() 
     b, msglen = struct.unpack("!ci", data[0:5]) 
     assert b == "p" 
     print "Password: {}".format(data[5:]) 


    def read_SSLRequest(self): 
     data = self.read_socket() 
     msglen, sslcode = struct.unpack("!ii", data) 
     assert msglen == 8 
     assert sslcode == 80877103 

    def read_StartupMessage(self): 
     data = self.read_socket() 
     msglen, protoversion = struct.unpack("!ii", data[0:8]) 
     print "msglen: {}, protoversion: {}".format(msglen, protoversion) 
     assert msglen == len(data) 
     parameters_string = data[8:] 
     print parameters_string.split('\x00') 

    def send_AuthenticationOK(self): 
     self.send_to_socket(struct.pack("!cii", 'R', 8, 0)) 

    def send_AuthenticationClearText(self): 
     self.send_to_socket(struct.pack("!cii", 'R', 8, 3)) 

if __name__ == "__main__": 
    server = SocketServer.TCPServer(("localhost", 9876), Handler) 
    try: 
     server.serve_forever() 
    except: 
     server.shutdown() 

Пример командной строки PSQL сессия:

[~] 
$ psql -h localhost -p 9876 
Password: 
psql (9.1.6, server 0.0.0) 
WARNING: psql version 9.1, server version 0.0. 
     Some psql features might not work. 
Type "help" for help. 

codeape=> Select; 
abc | def 
-----+----- 
    | 
    | 
(2 rows) 

codeape=> 

водителю ODBC, который говорит протокол Postgresql должен работать как хорошо (но я не пробовал еще).

0

Это сообщение немного устарело, но стоит упомянуть, что если вам нужен драйвер ODBC, вы можете использовать SDK, например: http://www.simba.com/drivers/simba-engine-sdk/ Он позаботится о большей части очков, поднятых в других ответах, и дает вам для упрощения интерфейса.

Я работаю для Simba, поэтому я немного предвзятый, но с помощью SDK довольно легко создать драйвер ODBC для всего, что вы пытаетесь сделать. Вы можете получить что-то за 5 дней, если вы владеете навыками кодирования.

В качестве отправной точки один из других сообщений рекомендует использовать unixODBC или iODBC, однако это не сработает. Важно понимать различие между менеджером драйверов (unixODBC, iODBC и т. Д.) И драйвером. Диспетчер драйверов выступает в роли посредника между приложением и драйвером, устраняя необходимость прямого привязки к драйверу.

Вы можете начать с драйверов Postgres или MySQL в качестве отправной точки и развернуть их для использования своей собственной базы данных, однако это вряд ли будет тривиальной задачей. Создание драйвера с нуля еще сложнее и, вероятно, будет иметь текущие (и более высокие, чем ожидалось) затраты на обслуживание. До тех пор, пока вы будете знать о расходах на этот подход, он также может быть жизнеспособным.

1

Дополнительные сведения об этом: Обратите внимание, что это не так сложно использовать SDK. Как предложил Кайл, создание пользовательского драйвера odbc уже не так сложно. С решением вроде OpenAccess 99% кода уже предоставлено, и вы реализуете только 12 функций. Вы можете выбрать любой из следующих языков для написания кода: C/C++, Java, .NET, C#, ABL или 4GL. Для получения дополнительной информации о начале работы читайте в этом блоге: https://www.progress.com/blogs/quick-guide-build-custom-odbc-driver-in-java-or-c

Смежные вопросы