Создание собственного драйвера ODBC - PullRequest
33 голосов
/ 02 декабря 2008

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

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

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

РЕДАКТИРОВАТЬ: Мы пытаемся поддерживать переносимость с нашим кодом, который написан на C.

Ответы [ 6 ]

18 голосов
/ 03 декабря 2012

Другой вариант: вместо создания драйвера 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 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, также должен работать (но я еще не пробовал).

9 голосов
/ 02 декабря 2008

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

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

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

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

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

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

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

5 голосов
/ 02 декабря 2008

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

Существует как минимум два варианта:

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

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

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

0 голосов
/ 12 апреля 2017

Дополнительная информация по этому вопросу: обратите внимание, что использовать 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

0 голосов
/ 05 декабря 2016

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

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

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...