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

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

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

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

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

Могу я спросить - почему вы решили создать собственный драйвер odbc (технология 15-летней давности), а не драйвер oleDb (технология 10-летней давности) или управляемый код ADO, поставщик сетевых данных (8-летний и текущий) ??

Charles Bretana 02.12.2008 21:50

Наш код написан на C, и мы хотим, чтобы он сохранял переносимость.

Nicholas Mancuso 02.12.2008 21:53

Кроме того, довольно много систем (на ум приходят MSQuery и Access) действительно были разработаны с учетом ODBC и лучше всего с этим работают.

ConcernedOfTunbridgeWells 02.12.2008 22:39

Мне было просто любопытно ..., исследуя эту проблему, я бы включил потенциальный рынок для компонента в мои факторы принятия решения ... Большинство ваших потенциальных пользователей используют Access и или MsQuery?

Charles Bretana 02.12.2008 23:40

Забавно, что сейчас MS, кажется, уходит от "старого" oledb к "действительно старому" odbc ...

Brady Moritz 08.06.2012 21:36

boomhauer, вероятно, ссылается на то, что Microsoft отказалась от поставщика OLE DB после SQL Server 2010. blogs.msdn.com/b/sqlnativeclient/archive/2011/08/29/…

Fls'Zen 11.07.2012 19:57

unixODBC и iODBC - это менеджеры драйверов ODBC, а не драйверы ODBC.

Tony 04.10.2012 00:16
ReactJs | Supabase | Добавление данных в базу данных
ReactJs | Supabase | Добавление данных в базу данных
Это и есть ваш редактор таблиц в supabase.👇
Понимание Python и переход к SQL
Понимание Python и переход к SQL
Перед нами лабораторная работа по BloodOath:
41
7
28 516
5
Перейти к ответу Данный вопрос помечен как решенный

Ответы 5

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

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

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

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

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

Ответ принят как подходящий

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

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

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

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

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

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

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

Как насчет драйвера MySQL ODBC или Postgresql ODBC? Есть ли у кого-нибудь мнения о качестве кода этих драйверов?

codeape 16.11.2011 01:56

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

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

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

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

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

Чтение документации Postgresql и использование чего-то вроде wirehark для проверки реального трафика протокола упростило бы реализацию 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, также должен работать (но я еще не пробовал).

В настоящее время пытаюсь заставить его работать с драйвером ODBC, и это выглядит многообещающим. Мне просто пришлось удалить часть SSLRequest в начале и вернуть результаты, которые соответствуют тому, что запрашивает драйвер ODBC, я вижу все поступающие запросы :)

Gregory 25.02.2014 20:20

Отличный ответ! Вот что делает база данных h2: h2database.com/html/advanced.html#odbc_driver

Martin Broadhurst 21.04.2014 13:25

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

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

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

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

Какая лицензия у Simba SDK?

Martynas Jusevičius 11.06.2020 15:16

Другие вопросы по теме