我正在编写一个实用程序,我打算可以使用至少三个不同的数据库服务器后端(SQLite3,PostgreSQL和MySQL)。我没有使用ORM(虽然我在完成原型后尝试使用SQLAlchemy进行分叉,并且对如何维护模式以便其他工具可能访问变得足够舒服,可能来自未来的其他语言)。
我本着StackOverflow政策的精神发布这个问题,关于回答自己的问题并打开问题并回答讨论。
以下答案中发布的代码已针对所有三个受支持的目标进行了测试,这些目标预先配置了我预期架构的一个非常简单的子集(脚本顶部附近的硬代码)。它目前只检查每个表是否具有所有必需的列,并且仅在存在额外列时才发出警告。我没有检查列内容的类型和其他约束。
(我的实用程序应允许最终用户添加其他列,只要它们允许NULL值或代码提供合适的默认值 - 因为我的代码只会插入或更新I&列的列的子集#39; m指定)。
请告诉我是否有更好的方法可以完成此操作,或者如果我在测试中发现了一些未发现的严重错误。
(我想知道如何正确创建可以共享的SQLFiddle条目/链接,以便更轻松地使用这个问题。欢迎提出任何指示,我也欢迎我尝试在my MySQL schema创建这个...让我们看看是否有效。
答案 0 :(得分:1)
如上所述,这是我的方法:
#!/usr/bin/env python
import importlib
import sys
schema = (('tags', set(('id','tag','gen','creation'))),
('nodes', set(('name', 'id', 'gen', 'creation'))),
('node_tag', set(('node_id', 'tag_id', 'creation'))),
('tgen', set(('tid', 'comment', 'creation')))
)
drivers = {
'mysql': 'MySQLdb',
'pg': 'psycopg2',
'sqlite': 'sqlite3',
}
if __name__ == '__main__':
args = sys.argv[1:]
if args[0] in drivers.keys():
dbtype = args[0]
db = importlib.import_module(drivers[dbtype])
else:
print >> sys.stderr, 'Unrecognized dbtype %s, should be one of these:' % args[0], ' '.join(drivers.keys())
sys.exit(127)
if dbtype == 'sqlite':
required_args = 2
dbopts = { 'database': args[1] }
else:
required_args = 6
dbopts = { 'database' : args[1],
'user' : args[2],
'passwd' : args[3],
'host' : args[4],
'port' : int(args[5]),
}
if len(args) < required_args:
print >> sys.stderr, 'Must supply all arguments:',
print >> sys.stderr, '[mysql|pg|sqlite] database user passwd host port'
sys.exit(126)
if dbtype == 'mysql':
dbopts['db'] = dbopts['database']
del dbopts['database']
if dbtype == 'pg':
dbopts['password'] = dbopts['passwd']
del dbopts['passwd']
try:
conn = db.connect(**dbopts)
except db.Error, e:
print 'Database connection failed: %s' % e
sys.exit(125)
cur = conn.cursor()
exit_code = 0
for each_table in schema:
table, columns = each_table
introspected_columns = None
try:
cur.execute("SELECT * FROM %s WHERE 0=1" % table)
introspected_columns = set((x[0] for x in cur.description))
except db.Error, e:
print >> sys.stderr, 'Encountered %s Error on table %s' % (e, table)
if introspected_columns:
missing = columns - introspected_columns
extra = introspected_columns - columns
if missing:
print 'Error: Missing columns in table %s: %s' % (table,' '.join(missing))
exit_code += 1
else:
print 'All columns present for table %s' % table
if extra:
print 'Warning: Extra columns in table %s: %s' % (table, ' '.join(extra))
sys.exit(exit_code)
...在实际操作中,这将被重构为一个类,并在我正在编写的守护进程的服务启动期间调用。
注意这里的实际技术是对所有列SELECT * FROM some_table
进行查询...但保证不返回任何行WHERE 0=1
...这意味着我不需要知道任何表提名。这似乎始终在DBAPI(客户端)游标中设置DBAPI cur.description 字段。
(顺便说一下,这个例子也突出了流行的DBAPI数据库驱动程序中连接关键字参数的一些恼人的差异。它们似乎都没有忽略额外的键,所以我们需要为三者中的每一个都设置明显不同的参数集;只是SQLite3的文件名,并将'database'键的名称更改为MySQLdb的'db',将'password'更改为PostgreSQL的'passwd' - 至少为 psycopg2 驱动程序。