]> git.saurik.com Git - cyql.git/blame - __init__.py
This interface is actually getting closer to what I want.
[cyql.git] / __init__.py
CommitLineData
426cccf1
JF
1from __future__ import unicode_literals
2from __future__ import print_function
736932f0
JF
3
4import inspect
5import os
6
7from contextlib import contextmanager
8
9import psycopg2
426cccf1 10import psycopg2.extras
736932f0
JF
11import psycopg2.pool
12
736932f0
JF
13psycopg2.extensions.register_type(psycopg2.extensions.UNICODE)
14
38cd52b6
JF
15def 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
22class 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 97def 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
114def 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
123def 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
129def 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"""