it-swarm.com.de

Benutzerdefiniert erstellen ODBC Treiber

In meinem aktuellen Job möchten wir unseren eigenen odbc-Treiber implementieren, damit viele verschiedene Anwendungen eine Verbindung zu unserer eigenen App als Datenquelle herstellen können. Im Moment versuchen wir, die Optionen für die Entwicklung unseres eigenen Treibers auf die Implementierungsspezifikation abzuwägen, die massiv ist, oder mit einem SDK, das es Programmierern ermöglicht, die datenspezifischen Teile auszufüllen und höhere Ebenen zuzulassen der Abstraktion.

Hat jemand einen benutzerdefinierten odbc-Treiber implementiert? Auf welche Fallen sind Sie gestoßen? Welche Vorteile haben Sie gesehen, wenn Sie es selbst gemacht haben? Wie viele Arbeitsstunden würden Sie ungefähr in Anspruch nehmen? Haben Sie ein SDK verwendet und wenn ja, welche Vor-/Nachteile haben Sie aus diesem Ansatz gezogen?

Alle Kommentare und Antworten wäre sehr dankbar. Vielen Dank!

BEARBEITEN: Wir versuchen, die Portabilität mit unserem Code aufrechtzuerhalten, der in C geschrieben ist.

33

Ich habe nicht, aber ich habe einmal bei einer Firma interviewt, die genau das getan hatte. Sie stellten ein 4GL/DBMS-Produkt mit dem Namen AMPS her, das die gleiche Architektur wie MUMPS hat - eine hierarchische Datenbank mit integriertem 4GL (ein ganzes Genre solcher Systeme wurde in den 1970er Jahren herausgebracht). Sie verfügten über eine recht umfangreiche ältere Codebasis und Kunden, die über MS Access eine Verbindung herstellen möchten.

Der leitende Entwickler, der mich interviewt hat, hat einige Kriegsgeschichten darüber geteilt. Anscheinend ist es äußerst schmerzhaft zu tun und sollte nicht leicht genommen werden. Es ist ihnen jedoch tatsächlich gelungen, dies umzusetzen.

Eine Alternative dazu wäre die Bereitstellung eines Data Mart/BI-Produkts (im Sinne von SAP BW), das Ihre Anwendungsdaten in einer externen Datenbank darstellt und in einem benutzerfreundlicheren Format wie einem Stern- oder Schneeflockenschema zusammenfasst.

Dies hat den Nachteil, dass der Echtzeitzugriff nicht unterstützt wird, ist jedoch möglicherweise erheblich einfacher zu implementieren (und vor allem zu warten) als ein ODBC -Treiber. Wenn Ihre Echtzeitzugriffsanforderungen vernünftigerweise vorhersehbar und begrenzt sind, können Sie möglicherweise eine Webdienst-API bereitstellen, um diese zu unterstützen.

Eine weitere Option: Anstatt einen ODBC -Treiber zu erstellen, implementieren Sie ein Back-End, das das Wire-Protokoll einer anderen Datenbank (z. B. Postgresql oder MySQL) verwendet.

Ihre Benutzer können dann beispielsweise den Postgresql ODBC -Treiber herunterladen und verwenden.

Welche Back-End-Datenbank Sie emulieren, hängt wahrscheinlich am meisten davon ab, wie gut das Wire-Protokoll-Format dokumentiert ist.

Sowohl Postgres als auch MySQL haben eine anständige Dokumentation für ihre Client-Server-Protokolle.

Ein einfaches Python 2.7-Beispiel für ein Server-Backend, das Teile des Postgresql-Wire-Protokolls versteht, finden Sie unten. Das Beispielskript erstellt einen Server, der Port 9876 überwacht. Ich kann den Befehl psql -h localhost -p 9876 verwenden, um eine Verbindung zum Server herzustellen. Jede ausgeführte Abfrage gibt eine Ergebnismenge mit den Spalten abc und def und zwei Zeilen zurück, wobei alle Werte NULL sind.

Das Lesen der Postgresql-Dokumente und die Verwendung von Wireshark zur Überprüfung des tatsächlichen Protokollverkehrs machen die Implementierung eines Postgresql-kompatiblen Backends ziemlich einfach.

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 __== "__main__":
    server = SocketServer.TCPServer(("localhost", 9876), Handler)
    try:
        server.serve_forever()
    except:
        server.shutdown()

Beispiel für eine psql-Sitzung in der Befehlszeile:

[~]
$ 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=>

Ein ODBC -Treiber, der das Postgresql-Protokoll spricht, sollte ebenfalls funktionieren (aber ich habe es noch nicht ausprobiert).

17
codeape

ODBC-Treiber sind sehr komplex - die Entscheidung, einen zu schreiben, sollte nicht leichtfertig getroffen werden. Die Überprüfung vorhandener Open Source-Treiber ist ein guter Ansatz, um Beispiele zu nennen. Die meisten haben jedoch Mängel, die Sie möglicherweise nicht emulieren möchten. :) APIs sind unabhängig von der Betriebssystemplattform gleich. FreeTDS für MSSQL/Sybase bietet eine der besseren Open-Source-ODBC-Treiberimplementierungen, die ich gesehen habe.

Wenn Sie die Anwendung steuern, können Sie mit der Implementierung einer möglicherweise nur sehr kleinen Teilmenge der Spezifikation in einem angemessenen Zeitraum fertig werden. Die Verwendung in einer Mehrzweckumgebung kann einen größeren Aufwand erfordern, um die richtigen Ergebnisse zu erzielen. Neben der einfachen Implementierung von Dutzenden von Wrapper-Aufrufen müssen Sie auch Folgendes implementieren:

  • Metadaten-Zugriffsfunktionen
  • Syntaxanalyse für ODBC-spezifische Abfragen
  • SQLSTATE-Fehlernachrichtenzuordnungen
  • Multibyte/Zeichensatz-Marshalling
  • Unterstützung für ODBC Version 2.3 - Fehlermeldungen/Funktionszuordnungen
  • Cursor
  • DM-Konfigurationsoberfläche zur Verwaltung der Datenquelle
9
Einstein

Ich habe keinen ODBC -Treiber implementiert, wollte aber nur einen Vorschlag machen, dass Sie mit einer Open-Source-Implementierung beginnen und Ihre eigenen Anpassungen hinzufügen können. Dadurch können Sie viel schneller loslegen.

Es gibt mindestens zwei Möglichkeiten:

  • unixODBC ist unter LGPL lizenziert, dh wenn Sie den Code ändern, müssen Sie Ihre Änderungen Open Source vornehmen.

  • iODBC ist nach Ihrer Wahl unter LGPL oder New BSD lizenziert. Mit dem neuen BSD können Sie Änderungen vornehmen ohne nehmen Sie Ihre Änderungen Open Source vor.

Es ist jedoch nicht klar, ob diese Pakete unter Windows ausgeführt werden und nicht unter UNIX/Linux mit einer Client-API, die mit Standard-ODBC konsistent ist. Sie geben nicht an, welche Plattform Sie verwenden, daher weiß ich nicht, ob dies für Sie relevant ist.

5
Bill Karwin

Zusätzlicher Input dazu: Bitte beachten Sie, dass die Verwendung eines SDK nicht mehr so ​​aufwändig ist. Wie kyle oben angedeutet hat, ist das Erstellen eines benutzerdefinierten ODBC-Treibers nicht mehr so ​​komplex. Bei einer Lösung wie OpenAccess ist der Code bereits zu 99% vorhanden und Sie implementieren nur 12 Funktionen. Sie können eine der folgenden Sprachen für die Codierung auswählen: C/C++, Java, .NET, C #, ABL oder 4GL. Weitere Informationen zu den ersten Schritten finden Sie in diesem Blog: https://www.progress.com/blogs/quick-guide-build-custom-odbc-driver-in-Java-or-c

0
user6641561

Dieser Beitrag ist jetzt etwas alt, aber erwähnenswert: Wenn Sie einen ODBC -Treiber benötigen, können Sie ein SDK wie das folgende verwenden: http://www.simba.com/drivers/simba -engine-sdk/ Es kümmert sich um die meisten Punkte, die in den anderen Antworten angesprochen wurden, und bietet Ihnen eine stark vereinfachte Schnittstelle zur Implementierung.

Ich arbeite zufällig für Simba, daher bin ich ein bisschen voreingenommen, aber die Verwendung eines SDK macht es ziemlich einfach, einen ODBC - Treiber für alles zu erstellen, was Sie tun möchten. Sie können in 5 Tagen etwas in Gang bringen, wenn Sie sich mit Codierung etwas auskennen.

Einer der anderen Posts empfiehlt unixODBC oder iODBC als Ausgangspunkt, dies wird jedoch nicht funktionieren. Es ist wichtig, den Unterschied zwischen einem Treibermanager (unixODBC, iODBC usw.) und einem Treiber zu erkennen. Der Treiber-Manager fungiert als Vermittler zwischen der Anwendung und dem Treiber, sodass keine direkte Verbindung zu einem Treiber hergestellt werden muss.

Sie können mit den Postgres- oder MySQL-Treibern als Ausgangspunkt beginnen und sie dazu veranlassen, Ihre eigene Datenbank zu verwenden. Dies ist jedoch wahrscheinlich keine triviale Aufgabe. Das Erstellen eines Treibers von Grund auf ist noch schwieriger und verursacht wahrscheinlich laufende (und höhere als erwartete) Wartungskosten. Solange Sie sich der Kosten dieses Ansatzes bewusst sind, kann es auch rentabel sein.

0
KylePorter