1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28 __doc__ = """
29 3kong DB API 2 Interface
30
31 It saves the connection data for following connections. The data is stored as module space.
32
33 """
34
35 import threading
36 import warn
37 warn = warn.Warn("db3k")
38 from Queue import Queue, Empty, Full
39
41
43 - def __init__(self, user, password, host, database, manager, preamble, kwargs = None, rewritesql=None):
44 self.user = user
45 self.password = password
46 self.host = host
47 self.database = database
48 self.manager = manager
49 self.preamble = preamble
50 self.rewritesql = rewritesql
51 if kwargs is None: kwargs = {}
52 if manager and manager.startswith('sqlite'):
53 kwargs['check_same_thread'] = False
54 self.kwargs = kwargs
55 if manager in ('sqlite2', 'sqlite'):
56 try:
57 from pysqlite2 import dbapi2
58 except ImportError:
59 from sqlite2 import dbapi2
60 self.dbm = dbapi2
61 elif manager == 'sqlite3':
62 try:
63 from sqlite3 import dbapi2
64 except ImportError:
65 from pysqlite3 import dbapi2
66 self.dbm = dbapi2
67 elif manager == 'mysql':
68 import MySQLdb
69 if self.host is None:
70 self.host = '127.0.0.1'
71 self.dbm = MySQLdb
72 elif manager == 'postgresql':
73 import pgdb
74 self.dbm = pgdb
75 else:
76 raise UnknownDatabaseManager(manager)
77
78 __DB_pool__ = { }
79
80 -def set_database_data(user=None,password=None,host=None,database=None,name=None,manager='postgresql',preamble=(),kwargs=None,rewritesql=None,dbm=None):
81 """ Sets the data to connect to the database
82 possible managers are: postgresql, mysql, sqlite2, sqlite3
83 """
84 if name is None: name = ''
85 __DB_pool__[name] = DBData(user,password,host,database,manager,preamble,kwargs,rewritesql)
86
88 """ Removes the database connection data """
89 del __DB_pool__[name]
90
92 """ Retrieves the connection data """
93 return __DB_pool__[name].__dict__
94
95 -def create2(user=None,password=None,host=None,database=None,name=None,manager='postgresql',preamble=(),rewritesql=None,**kwargs):
96 """ Creates a new connection to the database.
97 Returns a tuple (connection,module-of-connection)
98
99 module-of-connection is useful to manage exceptions
100 """
101 if name is None: name = ''
102 data = None
103 try:
104 data = __DB_pool__[name]
105 if database and (data.database != database):
106 raise KeyError, "KeyError Fake, database != data.database"
107 if user and (data.user != user):
108 raise KeyError, "KeyError Fake, user != data.user"
109 if host and (data.host != host):
110 raise KeyError, "KeyError Fake, host != data.host"
111 except KeyError, err:
112 set_database_data(user = user, password = password,
113 host = host, database = database,
114 name = name, manager = manager,
115 preamble = preamble,
116 rewritesql = rewritesql,
117 kwargs = kwargs)
118 data = __DB_pool__[name]
119 dbm = data.dbm
120 if data.manager in ('sqlite2', 'sqlite', 'sqlite3'):
121 conn = dbm.connect("%s.%s"%(data.database,data.manager),**data.kwargs)
122 elif data.manager == 'mysql':
123 conn = dbm.connect(user=data.user, passwd=data.password,
124 host=data.host, db=data.database,**data.kwargs)
125 else:
126 conn = dbm.connect(user=data.user, password=data.password,
127 host=data.host, database=data.database,**data.kwargs)
128 cursor = conn.cursor()
129 try:
130 for sql in data.preamble:
131 warn.debug( sql, cursor.execute(sql) )
132 finally:
133 cursor.close()
134 conn.commit()
135 return conn, dbm
136
137
139 """ The same that create2 but only returns the connection """
140 return create2(*args, **kwargs)[0]
141
143 """ Creates a new checked connection to the database manager.
144 It uses the |create| function to get new connections.
145
146 This class must not be used directly from a web application,
147 unless you know what are you doing.
148 Instead a new sub-class or managing methods must be created
149 validating tables in insertions, deletions, and updates.
150
151 """
153 try:
154 name = kwargs['name']
155 except KeyError:
156 name = kwargs['name'] = ''
157 try:
158 get_database_data(name)
159 except KeyError:
160 set_database_data(**kwargs)
161 data = get_database_data(name)
162 self.dbm = data['dbm']
163 self.rewritesql = data['rewritesql']
164 self.connname = kwargs.get('name','')
165 self.queue = Queue()
166
168 try:
169 q = self.queue.get_nowait()
170 except Empty:
171 q,self.dbm = create2(name=self.connname)
172 return q
173
175 try:
176 self.queue.put_nowait(q)
177 return
178 except Full:
179 pass
180 q.close()
181
184
186 """
187 Closes the connection
188 """
189 try:
190 while True:
191 q = self.queue.get_nowait()
192 q.close()
193 except Empty:
194 return
195
197 """ Returns a cursor from the RDBM backend and the connection owner of the cursor
198 (cursor,connection). The cursor must be closed by the requester. The connection
199 must be closed or returned to the db3k.Connection object using the self.putconn
200 method """
201 try:
202 conn = self.getconn()
203 cursor = conn.cursor()
204 except:
205 warn.warn("An unexpected error was found creating the cursor. I will try to create one more time")
206 try:
207 conn = self.getconn()
208 cursor = conn.cursor()
209 except:
210 warn.warn("A second unexpected error was found creating the cursor."
211 "Please check that your RDBMs is running and working")
212 raise
213 return cursor, conn
214
215 - def execute(self, sqlstring, values=()):
216 """ Execute a single cursor operation. Returns a tuple of
217 resultarray, description """
218
219 conn = self.getconn()
220 cursor = conn.cursor()
221 des = res = None
222
223 dbm = self.dbm
224 if self.rewritesql:
225 sqlstring = self.rewritesql(sqlstring)
226 try:
227 cursor.execute(sqlstring,values)
228 if cursor.description:
229 res = cursor.fetchall()
230 des = cursor.description
231 except (dbm.DatabaseError, dbm.IntegrityError, dbm.NotSupportedError, dbm.ProgrammingError, dbm.InternalError):
232 warn.warn("A Database error was detected."
233 "The connection will be closed and replaced..."
234 "But the error and their consecuences will not be fixed")
235 cursor.close()
236 conn.rollback()
237 conn.close()
238
239 raise
240 except:
241 warn.warn("An unknown error was found in db3k..."
242 "The connection will be closed and replaced..."
243 "But the error and their consecuences will not be fixed")
244 cursor.close()
245 conn.rollback()
246 conn.close()
247
248 raise
249 cursor.close()
250 conn.commit()
251 self.putconn(conn)
252
253 return res, des
254
255 - def hquery(self, sqlstring, values={}):
256 """ Performs the |sqlstring| query and returns an array of dictionaries
257 (fieldname => value)
258 """
259 try:
260 res, des = self.execute(sqlstring, values)
261 except ValueError:
262 return ([],[])
263 names = map(lambda x: x[0], des)
264 rows = []
265 for r in res:
266 x = {}
267 rows.append(x)
268 for i in range(0,len(r)):
269 if isinstance(r[i], unicode):
270 x[des[i][0]] = r[i].encode('utf8')
271 else:
272 x[des[i][0]] = r[i]
273 return rows
274
275 - def query(self, sqlstring, values={}):
276 """ Performs the |sqlstring| query and returns an array of arrays of
277 field values """
278 res = self.execute(sqlstring, values)
279 try:
280 return self.execute(sqlstring, values)[0]
281 except IndexError:
282 return []
283
285 """ Deletes from |tabname| table where the dictionary |cond| is true """
286 if isinstance(condition, dict):
287 condition = self.join_keys(condition)
288 return """ DELETE FROM %s WHERE %s"""%(tabname, condition), ()
289
290 - def delete(self, tabname, condition):
293
295 """ Creates the data to execute an
296 Insert |keyvalues| (dictionary of fieldname => value) into
297 the table |tabname| """
298 values = []
299 keys = []
300 if self.dbm.paramstyle == 'qmark':
301 pstyle = '?'
302 else:
303 pstyle = '%s'
304
305 for k,v in keyvalues.items():
306 keys.append(k)
307 values.append(v)
308 return """ INSERT INTO %s (%s) VALUES (%s) """%(tabname, ",".join(keys), ",".join((pstyle,) * (len(values)))), values
309
310 - def insert(self, tabname, keyvalues):
311 """ Insert |keyvalues| (dictionary of fieldname => value) into
312 the table |tabname| """
313 sqlstring, values = self.ne_insert(tabname, keyvalues)
314 self.execute(sqlstring, values)
315
316 - def join_keys(self, keys, join_string = " AND "):
317 return join_string.join([ "%s = '%s'"%(str(k).replace("'","''"),str(v).replace("'","''")) for k,v in keys.items() ])
318
319 - def ne_update(self, tabname, newvalues, condition):
320 """ Creates an sql statement to
321 Update to |newvalues| (dictionary of fieldname => value)
322 the table |tabname| where the |condition| is True. Condition can be a string or a dictionary with constraint key-pair set.
323 """
324 values = []
325 string = []
326 if self.dbm.paramstyle == 'qmark':
327 pstyle = '?'
328 else:
329 pstyle = '%s'
330 for k, v in newvalues.items():
331 string.append("%s = %s"%(k,pstyle))
332 values.append(v)
333 if isinstance(condition, dict):
334 condition = self.join_keys(condition)
335 return """ UPDATE %s SET %s WHERE %s """%(tabname,",".join(string), condition), values
336
337 - def update(self, tabname, newvalues, condition):
338 """ Update to |newvalues| (dictionary of fieldname => value)
339 the table |tabname| where the |condition| is True. Condition can be a string or a dictionary with constraint key-pair set.
340 """
341 sqlstring, values = self.ne_update(tabname, newvalues, condition)
342 self.execute(sqlstring, values)
343
344 if __name__ == '__main__':
345 import getpass
346 set_database_data(user='sadit',password=getpass.getpass(),database='catalogos')
347 conn = Connection()
348
349 print conn.query("""
350 SELECT conkey, confkey,
351 (SELECT relname FROM pg_class WHERE pg_class.oid = conrelid) as "InitialTable",
352 (SELECT relname FROM pg_class WHERE pg_class.oid = confrelid) as "FinalTable"
353 FROM pg_constraint
354 WHERE contype = 'f' AND conrelid = (SELECT oid FROM pg_class WHERE relname = 'comment')""");
355 conn.close()
356