Package db3k
[hide private]
[frames] | no frames]

Source Code for Package db3k

  1  #coding: utf8; 
  2  ############################################################################ 
  3  #                                                                          # 
  4  #    This file 'db3k/__init__.py'                                          # 
  5  #    is part of 'SPyRO: Simple Python Distributed Indexing'                # 
  6  #    Copyright (C) 2004-2005 by Eric Sadit Téllez Avila                    # 
  7  #    Copyright (C) 2006      by Eric Sadit Téllez Avila                    # 
  8  #    Copyright (C) 2007      by Eric Sadit Téllez Avila                    # 
  9  #    Copyright (C) 2008      by Eric Sadit Téllez Avila                    # 
 10  #    sadit@lsc.fie.umich.mx or donsadit@gmail.com                          # 
 11  #                                                                          # 
 12  #    This program is free software; you can redistribute it and#or modify  # 
 13  #    it under the terms of the GNU General Public License as published by  # 
 14  #    the Free Software Foundation; either version 2 of the License, or     # 
 15  #    (at your option) any later version.                                   # 
 16  #                                                                          # 
 17  #    This program is distributed in the hope that it will be useful,       # 
 18  #    but WITHOUT ANY WARRANTY; without even the implied warranty of        # 
 19  #    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         # 
 20  #    GNU General Public License for more details.                          # 
 21  #                                                                          # 
 22  #    You should have received a copy of the GNU General Public License     # 
 23  #    along with this program; if not, write to the                         # 
 24  #    Free Software Foundation, Inc.,                                       # 
 25  #    59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             # 
 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   
40 -class UnknownDatabaseManager(Exception): pass
41
42 -class DBData:
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
87 -def del_database_data(name):
88 """ Removes the database connection data """ 89 del __DB_pool__[name]
90
91 -def get_database_data(name):
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
138 -def create(*args, **kwargs):
139 """ The same that create2 but only returns the connection """ 140 return create2(*args, **kwargs)[0]
141
142 -class Connection:
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 """
152 - def __init__(self, **kwargs):
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
167 - def getconn(self):
168 try: 169 q = self.queue.get_nowait() 170 except Empty: 171 q,self.dbm = create2(name=self.connname) 172 return q
173
174 - def putconn(self, q):
175 try: 176 self.queue.put_nowait(q) 177 return 178 except Full: 179 pass 180 q.close()
181
182 - def __del__(self):
183 self.close()
184
185 - def close(self):
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
196 - def cursor(self):
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 # warn.debug(sqlstring, values) 219 conn = self.getconn() 220 cursor = conn.cursor() 221 des = res = None 222 # self.lock.acquire() 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 # self.lock.release() 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 # self.lock.release() 248 raise 249 cursor.close() 250 conn.commit() # not thread safe 251 self.putconn(conn) 252 # self.lock.release() 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
284 - def ne_delete(self, tabname, condition):
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):
291 sqlstring, values = self.ne_delete(tabname, condition) 292 self.execute(sqlstring, values)
293
294 - def ne_insert(self, tabname, keyvalues):
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