上記のように、私のアプローチは次のとおりです。
#!/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 データベース ドライバー間での接続キーワード引数の厄介な違いも強調しています。それらはいずれも追加のキーを無視していないようです。そのため、3 つのそれぞれに大幅に異なる引数セットが必要です。SQLite3 のファイル名だけで、 'database' キーの名前を MySQLdb の場合は 'db' に、PostgreSQL の場合は 'password' を 'passwd' に(少なくともpsycopg2ドライバーの場合)。