]>
Commit | Line | Data |
---|---|---|
426cccf1 JF |
1 | from __future__ import unicode_literals |
2 | from __future__ import print_function | |
736932f0 JF |
3 | |
4 | import inspect | |
5 | import os | |
6 | ||
7 | from contextlib import contextmanager | |
8 | ||
9 | import psycopg2 | |
426cccf1 | 10 | import psycopg2.extras |
736932f0 JF |
11 | import psycopg2.pool |
12 | ||
736932f0 JF |
13 | psycopg2.extensions.register_type(psycopg2.extensions.UNICODE) |
14 | ||
38cd52b6 JF |
15 | def one(values): |
16 | if values == None or len(values) == 0: | |
17 | return None | |
18 | else: | |
19 | assert len(values) == 1 | |
20 | return values[0] | |
21 | ||
426cccf1 JF |
22 | class connection(object): |
23 | def __init__(self, driver): | |
24 | self.driver = driver | |
38cd52b6 | 25 | self.driver.set_isolation_level(psycopg2.extensions.ISOLATION_LEVEL_AUTOCOMMIT) |
426cccf1 JF |
26 | |
27 | @contextmanager | |
28 | def cursor(self): | |
38cd52b6 | 29 | cursor = self.driver.cursor(cursor_factory=psycopg2.extras.DictCursor) |
426cccf1 | 30 | try: |
426cccf1 JF |
31 | yield cursor |
32 | finally: | |
33 | cursor.close() | |
34 | ||
35 | @contextmanager | |
fce82ca2 | 36 | def execute(self, statement, depth=0, context=None): |
426cccf1 | 37 | with self.cursor() as cursor: |
ba4690e8 JF |
38 | # two frames, accounting for execute() and @contextmanager |
39 | locals = inspect.currentframe(depth + 2).f_locals | |
229bbf8c | 40 | try: |
fce82ca2 JF |
41 | if context == None: |
42 | context = locals | |
43 | cursor.execute(statement.format(**locals), context) | |
229bbf8c JF |
44 | finally: |
45 | del locals | |
426cccf1 | 46 | yield cursor |
736932f0 | 47 | |
426cccf1 JF |
48 | @contextmanager |
49 | def transact(self, synchronous_commit=True): | |
38cd52b6 | 50 | self.driver.set_isolation_level(psycopg2.extensions.ISOLATION_LEVEL_READ_COMMITTED) |
426cccf1 | 51 | try: |
38cd52b6 JF |
52 | with self.cursor() as cursor: |
53 | if not synchronous_commit: | |
54 | cursor.execute('set local synchronous_commit = off') | |
55 | ||
56 | yield | |
426cccf1 JF |
57 | self.driver.commit() |
58 | except: | |
59 | self.driver.rollback() | |
60 | raise | |
38cd52b6 JF |
61 | finally: |
62 | self.driver.set_isolation_level(psycopg2.extensions.ISOLATION_LEVEL_AUTOCOMMIT) | |
426cccf1 | 63 | |
38cd52b6 JF |
64 | def one_(self, statement): |
65 | with self.execute(statement, 2) as cursor: | |
66 | one = cursor.fetchone() | |
67 | if one == None: | |
68 | return None | |
736932f0 | 69 | |
38cd52b6 JF |
70 | assert cursor.fetchone() == None |
71 | return one | |
72 | ||
73 | def __call__(self, procedure, *parameters): | |
74 | with self.execute(statement, 1) as cursor: | |
75 | return cursor.callproc(procedure, *parameters) | |
76 | ||
77 | def run(self, statement, locals=None): | |
78 | with self.execute(statement, 1, locals) as cursor: | |
79 | return cursor.rowcount | |
38f04d60 JF |
80 | |
81 | @contextmanager | |
82 | def set(self, statement): | |
38cd52b6 | 83 | with self.execute(statement, 1) as cursor: |
38f04d60 JF |
84 | yield cursor |
85 | ||
86 | def all(self, statement): | |
38cd52b6 | 87 | with self.execute(statement, 1) as cursor: |
38f04d60 JF |
88 | return cursor.fetchall() |
89 | ||
90 | def one(self, statement): | |
38cd52b6 | 91 | return self.one_(statement) |
38f04d60 | 92 | |
38cd52b6 JF |
93 | def has(self, statement): |
94 | return one(self.one_('select exists(%s)' % (statement,))) | |
f1df255a | 95 | |
736932f0 | 96 | @contextmanager |
426cccf1 | 97 | def connect(dsn): |
736932f0 JF |
98 | attempt = 0 |
99 | while True: | |
100 | try: | |
426cccf1 | 101 | driver = psycopg2.connect(**dsn) |
736932f0 JF |
102 | break |
103 | except psycopg2.OperationalError, e: | |
104 | if attempt == 2: | |
105 | raise e | |
106 | attempt = attempt + 1 | |
107 | ||
108 | try: | |
426cccf1 JF |
109 | driver.set_client_encoding('UNICODE') |
110 | yield connection(driver) | |
736932f0 | 111 | finally: |
426cccf1 | 112 | driver.close() |
736932f0 | 113 | |
91d72c6c JF |
114 | def connected(dsn): |
115 | def wrapped(method): | |
116 | def replaced(*args, **kw): | |
117 | with connect(dsn) as connection: | |
118 | return method(connection, *args, **kw) | |
119 | return replaced | |
120 | return wrapped | |
121 | ||
7d11917e JF |
122 | @contextmanager |
123 | def transact(dsn, **args): | |
124 | with connect(dsn) as connection: | |
38cd52b6 JF |
125 | with connection.transact(**args): |
126 | yield connection | |
7d11917e | 127 | |
426cccf1 | 128 | """ |
736932f0 JF |
129 | def slap_(sql, table, keys, values, path): |
130 | csr = sql.cursor() | |
131 | try: | |
132 | csr.execute('savepoint iou') | |
133 | try: | |
134 | both = dict(keys, **values) | |
135 | fields = both.keys() | |
136 | ||
137 | csr.execute(''' | |
138 | insert into %s (%s) values (%s) | |
139 | ''' % ( | |
140 | table, | |
141 | ', '.join(fields), | |
142 | ', '.join(['%s' for key in fields]) | |
143 | ), both.values()) | |
144 | except psycopg2.IntegrityError, e: | |
145 | csr.execute('rollback to savepoint iou') | |
146 | ||
147 | csr.execute(''' | |
148 | update %s set %s where %s | |
149 | ''' % ( | |
150 | table, | |
151 | ', '.join([ | |
152 | key + ' = %s' | |
153 | for key in values.keys()]), | |
154 | ' and '.join([ | |
155 | key + ' = %s' | |
156 | for key in keys.keys()]) | |
157 | ), values.values() + keys.values()) | |
158 | ||
159 | return path_(csr, path) | |
160 | finally: | |
161 | csr.close() | |
426cccf1 | 162 | """ |