web-dev-qa-db-ja.com

psycopg2の接続のすべてのクエリのスキーマの設定:search_pathの設定時に競合状態を取得する

私たちのシステムは、Ubuntu、python 3.4、postgres 9.4.x、psycopg2で実行されています。

私たちは(将来的には)スキーマを使用して、devtestprod環境に分割します。データベースへの接続を作成するための便利なメソッドを作成しました。接続文字列を作成するために、json接続構成ファイルを使用します。返された接続を使用して、後続のすべてのクエリで特定のスキーマを使用するように接続を構成したいと思います。クエリにハードコードされたスキーマを含めたくありません。開発、テスト、本番のフェーズ/環境のいずれであるかに応じて、スキーマを簡単に切り替えることができるはずです。

現在、コンビニエンスメソッドは次のようになっています。

_def connect(conn_config_file = 'Commons/config/conn_commons.json'):
    with open(conn_config_file) as config_file:    
        conn_config = json.load(config_file)

    conn = psycopg2.connect(
        "dbname='" + conn_config['dbname'] + "' " +
        "user='" + conn_config['user'] + "' " +
        "Host='" + conn_config['Host'] + "' " +
        "password='" + conn_config['password'] + "' " +
        "port=" + conn_config['port'] + " "
    )
    cur = conn.cursor()
    cur.execute("SET search_path TO " + conn_config['schema'])

    return conn
_

Set _search_path_クエリを実行する時間を与える限り、問題なく動作します。残念ながら、次のクエリの実行が速すぎると、_search_path_が設定されていない場合に競合状態が発生します。 _return conn_の前にconn.commit()を実行して強制的に実行しようとしましたが、これにより_search_path_がデフォルトのスキーマpostgresにリセットされ、たとえば、prodは使用しないでください。データベースまたはアプリケーション層での提案が望ましいですが、おそらくOSレベルでもこれを解決できると思います。その方向での提案も歓迎します。

Json構成ファイルの例は次のようになります。

_{
    "dbname": "thedatabase",
    "user": "theuser",
    "Host": "localhost",
    "password": "theusers_secret_password",
    "port": "6432",
    "schema": "prod"
}
_

どんな提案でも大歓迎です。

15

よりエレガントな解決策は、connect()optionsパラメータにsearch_pathを設定することです。

def connect(conn_config_file = 'Commons/config/conn_commons.json'):
    with open(conn_config_file) as config_file:    
        conn_config = json.load(config_file)

    schema = conn_config['schema']
    conn = psycopg2.connect(
        dbname=conn_config['dbname'],
        user=conn_config['user'],
        Host=conn_config['Host'],
        password=conn_config['password'],
        port=conn_config['port'],
        options=f'-c search_path={schema}',
    )
    return conn

もちろん、接続文字列の一部として「オプション」を使用できます。しかし、キーワード引数を使用することで、文字列の連結に関するすべての面倒を防ぐことができます。

私はこのソリューションをこの psycopg2機能リクエスト で見つけました。 「オプション」パラメータ自体については、 here と記載されています。

19
butla

接続の代わりに「SET search_path ...」を使用してクエリを実行するために使用するDatabaseCursor戻りカーソルのようなものを用意することをお勧めします。まあ私はこのようなものを意味します:

class DatabaseCursor(object):

    def __init__(self, conn_config_file):
        with open(conn_config_file) as config_file:     
            self.conn_config = json.load(config_file)

    def __enter__(self):
        self.conn = psycopg2.connect(
            "dbname='" + self.conn_config['dbname'] + "' " + 
            "user='" + self.conn_config['user'] + "' " + 
            "Host='" + self.conn_config['Host'] + "' " + 
            "password='" + self.conn_config['password'] + "' " + 
            "port=" + self.conn_config['port'] + " " 
        )   
        self.cur = self.conn.cursor()
        self.cur.execute("SET search_path TO " + self.conn_config['schema'])

        return self.cur

    def __exit__(self, exc_type, exc_val, exc_tb):
        # some logic to commit/rollback
        self.conn.close()

そして

with DatabaseCursor('Commons/config/conn_commons.json') as cur:
    cur.execute("...")
7
bersen