首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >创建自定义ODBC驱动程序

创建自定义ODBC驱动程序
EN

Stack Overflow用户
提问于 2008-12-02 18:45:01
回答 5查看 31.2K关注 0票数 46

在我目前的工作中,我们希望实现我们自己的odbc驱动程序,以允许许多不同的应用程序作为数据源连接到我们自己的应用程序。现在,我们正在考虑为实现规范开发自己的驱动程序的选择,或者使用SDK,允许程序员“填充”特定于数据的部分,并允许更高级别的抽象。

还有其他人实现了自定义odbc驱动程序吗?你遇到了什么陷阱?你认为自己做这件事有什么好处?你大概花了多少个小时?您使用过SDK吗?如果是的话,您从这种方法中看到了哪些好处/缺点?

如有任何意见和答复,将不胜感激。谢谢!

编辑:我们试图用我们的代码来维护可移植性,这是用C语言编写的。

EN

回答 5

Stack Overflow用户

回答已采纳

发布于 2008-12-02 18:59:04

我没有,但我曾经在一家公司面试过,这家公司正是这样做的。他们制作了一个名为AMPS的4GL/DBMS产品,其架构与腮腺炎相同--这是一个具有集成4GL的层次数据库(1970年代出现了一种完整的此类系统类型)。他们有相当多的遗留代码库,并且客户希望使用MS连接到它。

采访我的首席开发人员分享了一些有关这方面的战争故事。显然,这样做非常痛苦,不应该掉以轻心。然而,他们确实成功地实现了这一目标。

这样做的一个替代方法是提供一个数据集市/BI产品(类似于SAP BW),该产品在外部数据库中显示应用程序数据,并将其转换成更友好的格式,如星型或雪花模式。

这将受到不支持实时访问的影响,但可能比ODBC驱动程序更容易实现(更重要的是维护)。如果您的实时访问需求是合理的预测和限制,您可能会公开一个web服务API来支持这些访问。

票数 9
EN

Stack Overflow用户

发布于 2012-12-03 14:32:28

另一种选择:不是创建ODBC驱动程序,而是实现一个后端,该后端讨论另一个数据库(例如Postgresql或MySQL )使用的有线协议。

然后,用户可以下载并使用Postgresql驱动程序。

确切地说,您选择要模拟的后端数据库很可能最依赖于电线协议格式的文档记录得有多好。

波斯特格斯MySQL都有很好的客户端服务器协议文档。

下面是一个简单的Python2.7服务器后端示例,它可以理解PostgreSQL导线协议的部分内容。示例脚本创建一个监听端口9876的服务器。我可以使用命令psql -h localhost -p 9876连接到服务器。执行的任何查询都将返回包含abc和def列以及两行的结果集,所有值均为NULL。

读取Postgresql文档并使用类似于wireshark的工具来检查真正的协议通信量将使实现与Postgresql兼容的后端变得非常简单。

代码语言:javascript
运行
复制
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会话:

代码语言:javascript
运行
复制
[~]
$ 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=>

使用Postgresql协议的ODBC驱动程序也应该可以工作(但我还没有试过)。

票数 29
EN

Stack Overflow用户

发布于 2008-12-02 19:38:21

ODBC驱动程序非常复杂--编写一个驱动程序的决定不应掉以轻心。回顾现有的开源驱动程序是一个很好的方法,但是大多数都有您可能不想模仿的短消息:) API是相同的,而不管操作系统平台如何。FreeTDS for MSSQL/Sybase有我见过的更好的开源ODBC驱动程序实现之一。

如果您控制应用程序,您可以在合理的时间内实现可能只是规范的一个非常小的子集。在通用环境中使用可能需要付出更多的努力才能得到正确的结果。除了简单地实现几十个包装器调用之外,您还必须实现:

  • 元数据访问功能
  • ODBC特定查询语法解析
  • SQLSTATE错误消息映射
  • 多字节/字符集编组
  • ODBC版本2,3支持错误消息/函数映射
  • 游标
  • 用于管理数据源的DM配置UI
票数 10
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/335008

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档