Подтвердить что ты не робот

Создание настраиваемого драйвера ODBC

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

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

Любые комментарии и ответы будут очень признательны. Спасибо!

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

4b9b3361

Ответ 1

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

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

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

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

Ответ 2

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

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

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

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

Ниже приведен пример простого примера Python 2.7 сервера, который понимает части проводного протокола Postgresql. Пример script создает сервер, который слушает порт 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 session:

[~]
$ 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, должен работать (но я еще не пробовал).

Ответ 3

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

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

  • Функции доступа к метаданным
  • Анализ синтаксиса синтаксиса специальных запросов ODBC
  • Сопоставления сообщений SQLSTATE
  • Маршрутизация Multibyte/Character set
  • Поддержка версии ODBC версии 2,3 - сообщения об ошибках/сопоставления функций
  • курсоры
  • Пользовательский интерфейс конфигурации DM для управления источником данных

Ответ 4

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

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

  • unixODBC лицензируется в соответствии с LGPL, а это означает, что если вы измените код, вы должны внести свои изменения в open-source.

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

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

Ответ 5

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

Ответ 6

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

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

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

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