Package osh :: Package external :: Package pg8000 :: Module pg8000
[frames] | no frames]

Source Code for Module osh.external.pg8000.pg8000

   1  # vim: sw=4:expandtab:foldmethod=marker 
   2  # 
   3  # Copyright (c) 2007, Mathieu Fenniak 
   4  # All rights reserved. 
   5  # 
   6  # Redistribution and use in source and binary forms, with or without 
   7  # modification, are permitted provided that the following conditions are 
   8  # met: 
   9  # 
  10  # * Redistributions of source code must retain the above copyright notice, 
  11  # this list of conditions and the following disclaimer. 
  12  # * Redistributions in binary form must reproduce the above copyright notice, 
  13  # this list of conditions and the following disclaimer in the documentation 
  14  # and/or other materials provided with the distribution. 
  15  # * The name of the author may not be used to endorse or promote products 
  16  # derived from this software without specific prior written permission. 
  17  # 
  18  # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 
  19  # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 
  20  # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 
  21  # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 
  22  # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 
  23  # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 
  24  # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 
  25  # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 
  26  # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 
  27  # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
  28  # POSSIBILITY OF SUCH DAMAGE. 
  29   
  30  __author__ = "Mathieu Fenniak" 
  31   
  32  import socket 
  33  import struct 
  34  import datetime 
  35  import md5 
  36  import decimal 
  37  import threading 
  38  import time 
  39   
40 -class Warning(StandardError):
41 pass
42
43 -class Error(StandardError):
44 pass
45
46 -class InterfaceError(Error):
47 pass
48
49 -class DatabaseError(Error):
50 pass
51
52 -class DataError(DatabaseError):
53 pass
54
55 -class OperationalError(DatabaseError):
56 pass
57
58 -class IntegrityError(DatabaseError):
59 pass
60
61 -class InternalError(DatabaseError):
62 pass
63
64 -class ProgrammingError(DatabaseError):
65 pass
66
67 -class NotSupportedError(DatabaseError):
68 pass
69
70 -class DataIterator(object):
71 - def __init__(self, obj, func):
72 self.obj = obj 73 self.func = func
74
75 - def __iter__(self):
76 return self
77
78 - def next(self):
79 retval = self.func(self.obj) 80 if retval == None: 81 raise StopIteration() 82 return retval
83
84 -class DBAPI(object):
85 Warning = Warning 86 Error = Error 87 InterfaceError = InterfaceError 88 InternalError = InternalError 89 DatabaseError = DatabaseError 90 DataError = DataError 91 OperationalError = OperationalError 92 IntegrityError = IntegrityError 93 ProgrammingError = ProgrammingError 94 NotSupportedError = NotSupportedError 95 96 apilevel = "2.0" 97 threadsafety = 3 98 paramstyle = 'format' # paramstyle can be changed to any DB-API paramstyle 99
100 - def convert_paramstyle(src_style, query, args):
101 # I don't see any way to avoid scanning the query string char by char, 102 # so we might as well take that careful approach and create a 103 # state-based scanner. We'll use int variables for the state. 104 # 0 -- outside quoted string 105 # 1 -- inside single-quote string '...' 106 # 2 -- inside quoted identifier "..." 107 # 3 -- inside escaped single-quote string, E'...' 108 state = 0 109 output_query = "" 110 output_args = [] 111 if src_style == "numeric": 112 output_args = args 113 elif src_style in ("pyformat", "named"): 114 mapping_to_idx = {} 115 i = 0 116 while 1: 117 if i == len(query): 118 break 119 c = query[i] 120 # print "begin loop", repr(i), repr(c), repr(state) 121 if state == 0: 122 if c == "'": 123 i += 1 124 output_query += c 125 state = 1 126 elif c == '"': 127 i += 1 128 output_query += c 129 state = 2 130 elif c == 'E': 131 # check for escaped single-quote string 132 i += 1 133 if i < len(query) and i > 1 and query[i] == "'": 134 i += 1 135 output_query += "E'" 136 state = 3 137 else: 138 output_query += c 139 elif src_style == "qmark" and c == "?": 140 i += 1 141 param_idx = len(output_args) 142 if param_idx == len(args): 143 raise ProgrammingError("too many parameter fields, not enough parameters") 144 output_args.append(args[param_idx]) 145 output_query += "$" + str(param_idx + 1) 146 elif src_style == "numeric" and c == ":": 147 i += 1 148 if i < len(query) and i > 1 and query[i].isdigit(): 149 output_query += "$" + query[i] 150 i += 1 151 else: 152 raise ProgrammingError("numeric parameter : does not have numeric arg") 153 elif src_style == "named" and c == ":": 154 name = "" 155 while 1: 156 i += 1 157 if i == len(query): 158 break 159 c = query[i] 160 if c.isalnum(): 161 name += c 162 else: 163 break 164 if name == "": 165 raise ProgrammingError("empty name of named parameter") 166 idx = mapping_to_idx.get(name) 167 if idx == None: 168 idx = len(output_args) 169 output_args.append(args[name]) 170 idx += 1 171 mapping_to_idx[name] = idx 172 output_query += "$" + str(idx) 173 elif src_style == "format" and c == "%": 174 i += 1 175 if i < len(query) and i > 1: 176 if query[i] == "s": 177 param_idx = len(output_args) 178 if param_idx == len(args): 179 raise ProgrammingError("too many parameter fields, not enough parameters") 180 output_args.append(args[param_idx]) 181 output_query += "$" + str(param_idx + 1) 182 elif query[i] == "%": 183 output_query += "%" 184 else: 185 raise ProgrammingError("Only %s and %% are supported") 186 i += 1 187 else: 188 raise ProgrammingError("numeric parameter : does not have numeric arg") 189 elif src_style == "pyformat" and c == "%": 190 i += 1 191 if i < len(query) and i > 1: 192 if query[i] == "(": 193 i += 1 194 # begin mapping name 195 end_idx = query.find(')', i) 196 if end_idx == -1: 197 raise ProgrammingError("began pyformat dict read, but couldn't find end of name") 198 else: 199 name = query[i:end_idx] 200 i = end_idx + 1 201 if i < len(query) and query[i] == "s": 202 i += 1 203 idx = mapping_to_idx.get(name) 204 if idx == None: 205 idx = len(output_args) 206 output_args.append(args[name]) 207 idx += 1 208 mapping_to_idx[name] = idx 209 output_query += "$" + str(idx) 210 else: 211 raise ProgrammingError("format not specified or not supported (only %(...)s supported)") 212 elif query[i] == "%": 213 output_query += "%" 214 else: 215 i += 1 216 output_query += c 217 elif state == 1: 218 output_query += c 219 i += 1 220 if c == "'": 221 # Could be a double '' 222 if i < len(query) and query[i] == "'": 223 # is a double quote. 224 output_query += query[i] 225 i += 1 226 else: 227 state = 0 228 elif src_style in ("pyformat","format") and c == "%": 229 # hm... we're only going to support an escaped percent sign 230 if i < len(query): 231 if query[i] == "%": 232 # good. We already output the first percent sign. 233 i += 1 234 else: 235 raise ProgrammingError("'%" + query[i] + "' not supported in quoted string") 236 elif state == 2: 237 output_query += c 238 i += 1 239 if c == '"': 240 state = 0 241 elif src_style in ("pyformat","format") and c == "%": 242 # hm... we're only going to support an escaped percent sign 243 if i < len(query): 244 if query[i] == "%": 245 # good. We already output the first percent sign. 246 i += 1 247 else: 248 raise ProgrammingError("'%" + query[i] + "' not supported in quoted string") 249 elif state == 3: 250 output_query += c 251 i += 1 252 if c == "\\": 253 # check for escaped single-quote 254 if i < len(query) and query[i] == "'": 255 output_query += "'" 256 i += 1 257 elif c == "'": 258 state = 0 259 elif src_style in ("pyformat","format") and c == "%": 260 # hm... we're only going to support an escaped percent sign 261 if i < len(query): 262 if query[i] == "%": 263 # good. We already output the first percent sign. 264 i += 1 265 else: 266 raise ProgrammingError("'%" + query[i] + "' not supported in quoted string") 267 268 return output_query, tuple(output_args)
269 convert_paramstyle = staticmethod(convert_paramstyle) 270 271
272 - class CursorWrapper(object):
273 - def __init__(self, conn):
274 self.cursor = Cursor(conn) 275 self.arraysize = 1
276 277 rowcount = property(lambda self: self._getRowCount())
278 - def _getRowCount(self):
279 return -1
280 281 description = property(lambda self: self._getDescription())
282 - def _getDescription(self):
283 if self.cursor.row_description == None: 284 return None 285 columns = [] 286 for col in self.cursor.row_description: 287 columns.append((col["name"], col["type_oid"])) 288 return columns
289
290 - def execute(self, operation, args=()):
291 if self.cursor == None: 292 raise InterfaceError("cursor is closed") 293 new_query, new_args = DBAPI.convert_paramstyle(DBAPI.paramstyle, operation, args) 294 try: 295 self.cursor.execute(new_query, *new_args) 296 except: 297 # any error will rollback the transaction to-date 298 self.cursor.connection.rollback() 299 raise
300
301 - def executemany(self, operation, parameter_sets):
302 for parameters in parameter_sets: 303 self.execute(operation, parameters)
304
305 - def fetchone(self):
306 if self.cursor == None: 307 raise InterfaceError("cursor is closed") 308 return self.cursor.read_tuple()
309
310 - def fetchmany(self, size=None):
311 if size == None: 312 size = self.arraysize 313 rows = [] 314 for i in range(size): 315 rows.append(self.fetchone()) 316 return rows
317
318 - def fetchall(self):
319 if self.cursor == None: 320 raise InterfaceError("cursor is closed") 321 return tuple(self.cursor.iterate_tuple())
322
323 - def close(self):
324 self.cursor = None
325
326 - def setinputsizes(self, sizes):
327 pass
328
329 - def setoutputsize(self, size, column=None):
330 pass
331
332 - class ConnectionWrapper(object):
333 - def __init__(self, **kwargs):
334 self.conn = Connection(**kwargs) 335 self.conn.begin()
336
337 - def cursor(self):
338 return DBAPI.CursorWrapper(self.conn)
339
340 - def commit(self):
341 # There's a threading bug here. If a query is sent after the 342 # commit, but before the begin, it will be executed immediately 343 # without a surrounding transaction. Like all threading bugs -- it 344 # sounds unlikely, until it happens every time in one 345 # application... however, to fix this, we need to lock the 346 # database connection entirely, so that no cursors can execute 347 # statements on other threads. Support for that type of lock will 348 # be done later. 349 if self.conn == None: 350 raise InterfaceError("connection is closed") 351 self.conn.commit() 352 self.conn.begin()
353
354 - def rollback(self):
355 # see bug description in commit. 356 if self.conn == None: 357 raise InterfaceError("connection is closed") 358 self.conn.rollback() 359 self.conn.begin()
360
361 - def close(self):
362 self.conn = None
363
364 - def connect(user, host=None, unix_sock=None, port=5432, database=None, password=None, socket_timeout=60, ssl=False):
365 return DBAPI.ConnectionWrapper(user=user, host=host, 366 unix_sock=unix_sock, port=port, database=database, 367 password=password, socket_timeout=socket_timeout, ssl=ssl)
368 connect = staticmethod(connect) 369
370 - def Date(year, month, day):
371 return datetime.date(year, month, day)
372 Date = staticmethod(Date) 373
374 - def Time(hour, minute, second):
375 return datetime.time(hour, minute, second)
376 Time = staticmethod(Time) 377
378 - def Timestamp(year, month, day, hour, minute, second):
379 return datetime.datetime(year, month, day, hour, minute, second)
380 Timestamp = staticmethod(Timestamp) 381
382 - def DateFromTicks(ticks):
383 return DBAPI.Date(*time.localtime(ticks)[:3])
384 DateFromTicks = staticmethod(DateFromTicks) 385
386 - def TimeFromTicks(ticks):
387 return DBAPI.Time(*time.localtime(ticks)[3:6])
388 TimeFromTicks = staticmethod(TimeFromTicks) 389
390 - def TimestampFromTicks(ticks):
391 return DBAPI.Timestamp(*time.localtime(ticks)[:6])
392 TimestampFromTicks = staticmethod(TimestampFromTicks) 393
394 - def Binary(value):
395 return Bytea(value)
396 Binary = staticmethod(Binary) 397 398 # I have no idea what this would be used for by a client app. Should it be 399 # TEXT, VARCHAR, CHAR? It will only compare against row_description's 400 # type_code if it is this one type. It is the TEXT type_oid for now. 401 STRING = 25 402 403 # bytea type_oid 404 BINARY = 17 405 406 # numeric type_oid 407 NUMBER = 1700 408 409 # timestamp type_oid 410 DATETIME = 1114 411 412 # oid type_oid 413 ROWID = 26
414 415 416 ## 417 # This class represents a prepared statement. A prepared statement is 418 # pre-parsed on the server, which reduces the need to parse the query every 419 # time it is run. The statement can have parameters in the form of $1, $2, $3, 420 # etc. When parameters are used, the types of the parameters need to be 421 # specified when creating the prepared statement. 422 # <p> 423 # As of v1.01, instances of this class are thread-safe. This means that a 424 # single PreparedStatement can be accessed by multiple threads without the 425 # internal consistency of the statement being altered. However, the 426 # responsibility is on the client application to ensure that one thread reading 427 # from a statement isn't affected by another thread starting a new query with 428 # the same statement. 429 # <p> 430 # Stability: Added in v1.00, stability guaranteed for v1.xx. 431 # 432 # @param connection An instance of {@link Connection Connection}. 433 # 434 # @param statement The SQL statement to be represented, often containing 435 # parameters in the form of $1, $2, $3, etc. 436 # 437 # @param types Python type objects for each parameter in the SQL 438 # statement. For example, int, float, str.
439 -class PreparedStatement(object):
440 441 ## 442 # Determines the number of rows to read from the database server at once. 443 # Reading more rows increases performance at the cost of memory. The 444 # default value is 100 rows. The affect of this parameter is transparent. 445 # That is, the library reads more rows when the cache is empty 446 # automatically. 447 # <p> 448 # Stability: Added in v1.00, stability guaranteed for v1.xx. It is 449 # possible that implementation changes in the future could cause this 450 # parameter to be ignored.O 451 row_cache_size = 100 452
453 - def __init__(self, connection, statement, *types):
454 self.c = connection.c 455 self._portal_name = "pg8000_portal_%s_%s" % (id(self.c), id(self)) 456 self._statement_name = "pg8000_statement_%s_%s" % (id(self.c), id(self)) 457 self._row_desc = None 458 self._cached_rows = [] 459 self._command_complete = True 460 self._parse_row_desc = self.c.parse(self._statement_name, statement, types) 461 self._lock = threading.RLock()
462
463 - def __del__(self):
464 # This __del__ should work with garbage collection / non-instant 465 # cleanup. It only really needs to be called right away if the same 466 # object id (and therefore the same statement name) might be reused 467 # soon, and clearly that wouldn't happen in a GC situation. 468 self.c.close_statement(self._statement_name)
469 470 row_description = property(lambda self: self._getRowDescription())
471 - def _getRowDescription(self):
472 return self._row_desc.fields
473 474 ## 475 # Run the SQL prepared statement with the given parameters. 476 # <p> 477 # Stability: Added in v1.00, stability guaranteed for v1.xx.
478 - def execute(self, *args):
479 self._lock.acquire() 480 try: 481 if not self._command_complete: 482 # cleanup last execute 483 self._cached_rows = [] 484 self.c.close_portal(self._portal_name) 485 self._command_complete = False 486 self._row_desc = self.c.bind(self._portal_name, self._statement_name, args, self._parse_row_desc) 487 if self._row_desc: 488 # We execute our cursor right away to fill up our cache. This 489 # prevents the cursor from being destroyed, apparently, by a rogue 490 # Sync between Bind and Execute. Since it is quite likely that 491 # data will be read from us right away anyways, this seems a safe 492 # move for now. 493 self._fill_cache() 494 finally: 495 self._lock.release()
496
497 - def _fill_cache(self):
498 self._lock.acquire() 499 try: 500 if self._cached_rows: 501 raise InternalError("attempt to fill cache that isn't empty") 502 end_of_data, rows = self.c.fetch_rows(self._portal_name, self.row_cache_size, self._row_desc) 503 self._cached_rows = rows 504 if end_of_data: 505 self._command_complete = True 506 finally: 507 self._lock.release()
508
509 - def _fetch(self):
510 self._lock.acquire() 511 try: 512 if not self._cached_rows: 513 if self._command_complete: 514 return None 515 self._fill_cache() 516 if self._command_complete and not self._cached_rows: 517 # fill cache tells us the command is complete, but yet we have 518 # no rows after filling our cache. This is a special case when 519 # a query returns no rows. 520 return None 521 row = self._cached_rows[0] 522 del self._cached_rows[0] 523 return tuple(row) 524 finally: 525 self._lock.release()
526 527 ## 528 # Read a row from the database server, and return it in a dictionary 529 # indexed by column name/alias. This method will raise an error if two 530 # columns have the same name. Returns None after the last row. 531 # <p> 532 # Stability: Added in v1.00, stability guaranteed for v1.xx.
533 - def read_dict(self):
534 row = self._fetch() 535 if row == None: 536 return row 537 retval = {} 538 for i in range(len(self._row_desc.fields)): 539 col_name = self._row_desc.fields[i]['name'] 540 if retval.has_key(col_name): 541 raise InterfaceError("cannot return dict of row when two columns have the same name (%r)" % (col_name,)) 542 retval[col_name] = row[i] 543 return retval
544 545 ## 546 # Read a row from the database server, and return it as a tuple of values. 547 # Returns None after the last row. 548 # <p> 549 # Stability: Added in v1.00, stability guaranteed for v1.xx.
550 - def read_tuple(self):
551 row = self._fetch() 552 if row == None: 553 return row 554 return row
555 556 ## 557 # Return an iterator for the output of this statement. The iterator will 558 # return a tuple for each row, in the same manner as {@link 559 # #PreparedStatement.read_tuple read_tuple}. 560 # <p> 561 # Stability: Added in v1.00, stability guaranteed for v1.xx.
562 - def iterate_tuple(self):
564 565 ## 566 # Return an iterator for the output of this statement. The iterator will 567 # return a dict for each row, in the same manner as {@link 568 # #PreparedStatement.read_dict read_dict}. 569 # <p> 570 # Stability: Added in v1.00, stability guaranteed for v1.xx.
571 - def iterate_dict(self):
573 574 ## 575 # The Cursor class allows multiple queries to be performed concurrently with a 576 # single PostgreSQL connection. The Cursor object is implemented internally by 577 # using a {@link PreparedStatement PreparedStatement} object, so if you plan to 578 # use a statement multiple times, you might as well create a PreparedStatement 579 # and save a small amount of reparsing time. 580 # <p> 581 # As of v1.01, instances of this class are thread-safe. See {@link 582 # PreparedStatement PreparedStatement} for more information. 583 # <p> 584 # Stability: Added in v1.00, stability guaranteed for v1.xx. 585 # 586 # @param connection An instance of {@link Connection Connection}.
587 -class Cursor(object):
588 - def __init__(self, connection):
589 self.connection = connection 590 self._stmt = None
591 592 row_description = property(lambda self: self._getRowDescription())
593 - def _getRowDescription(self):
594 if self._stmt == None: 595 return None 596 return self._stmt.row_description
597 598 ## 599 # Run an SQL statement using this cursor. The SQL statement can have 600 # parameters in the form of $1, $2, $3, etc., which will be filled in by 601 # the additional arguments passed to this function. 602 # <p> 603 # Stability: Added in v1.00, stability guaranteed for v1.xx. 604 # @param query The SQL statement to execute.
605 - def execute(self, query, *args):
606 self._stmt = PreparedStatement(self.connection, query, *[type(x) for x in args]) 607 self._stmt.execute(*args)
608 609 ## 610 # Read a row from the database server, and return it in a dictionary 611 # indexed by column name/alias. This method will raise an error if two 612 # columns have the same name. Returns None after the last row. 613 # <p> 614 # Stability: Added in v1.00, stability guaranteed for v1.xx.
615 - def read_dict(self):
616 if self._stmt == None: 617 raise ProgrammingError("attempting to read from unexecuted cursor") 618 return self._stmt.read_dict()
619 620 ## 621 # Read a row from the database server, and return it as a tuple of values. 622 # Returns None after the last row. 623 # <p> 624 # Stability: Added in v1.00, stability guaranteed for v1.xx.
625 - def read_tuple(self):
626 if self._stmt == None: 627 raise ProgrammingError("attempting to read from unexecuted cursor") 628 return self._stmt.read_tuple()
629 630 ## 631 # Return an iterator for the output of this statement. The iterator will 632 # return a tuple for each row, in the same manner as {@link 633 # #PreparedStatement.read_tuple read_tuple}. 634 # <p> 635 # Stability: Added in v1.00, stability guaranteed for v1.xx.
636 - def iterate_tuple(self):
637 if self._stmt == None: 638 raise ProgrammingError("attempting to read from unexecuted cursor") 639 return self._stmt.iterate_tuple()
640 641 ## 642 # Return an iterator for the output of this statement. The iterator will 643 # return a dict for each row, in the same manner as {@link 644 # #PreparedStatement.read_dict read_dict}. 645 # <p> 646 # Stability: Added in v1.00, stability guaranteed for v1.xx.
647 - def iterate_dict(self):
648 if self._stmt == None: 649 raise ProgrammingError("attempting to read from unexecuted cursor") 650 return self._stmt.iterate_dict()
651 652 ## 653 # This class represents a connection to a PostgreSQL database. 654 # <p> 655 # The database connection is derived from the {@link #Cursor Cursor} class, 656 # which provides a default cursor for running queries. It also provides 657 # transaction control via the 'begin', 'commit', and 'rollback' methods. 658 # Without beginning a transaction explicitly, all statements will autocommit to 659 # the database. 660 # <p> 661 # As of v1.01, instances of this class are thread-safe. See {@link 662 # PreparedStatement PreparedStatement} for more information. 663 # <p> 664 # Stability: Added in v1.00, stability guaranteed for v1.xx. 665 # 666 # @param user The username to connect to the PostgreSQL server with. This 667 # parameter is required. 668 # 669 # @keyparam host The hostname of the PostgreSQL server to connect with. 670 # Providing this parameter is necessary for TCP/IP connections. One of either 671 # host, or unix_sock, must be provided. 672 # 673 # @keyparam unix_sock The path to the UNIX socket to access the database 674 # through, for example, '/tmp/.s.PGSQL.5432'. One of either unix_sock or host 675 # must be provided. The port parameter will have no affect if unix_sock is 676 # provided. 677 # 678 # @keyparam port The TCP/IP port of the PostgreSQL server instance. This 679 # parameter defaults to 5432, the registered and common port of PostgreSQL 680 # TCP/IP servers. 681 # 682 # @keyparam database The name of the database instance to connect with. This 683 # parameter is optional, if omitted the PostgreSQL server will assume the 684 # database name is the same as the username. 685 # 686 # @keyparam password The user password to connect to the server with. This 687 # parameter is optional. If omitted, and the database server requests password 688 # based authentication, the connection will fail. On the other hand, if this 689 # parameter is provided and the database does not request password 690 # authentication, then the password will not be used. 691 # 692 # @keyparam socket_timeout Socket connect timeout measured in seconds. 693 # Defaults to 60 seconds. 694 # 695 # @keyparam ssl Use SSL encryption for TCP/IP socket. Defaults to False.
696 -class Connection(Cursor):
697 - def __init__(self, user, host=None, unix_sock=None, port=5432, database=None, password=None, socket_timeout=60, ssl=False):
698 self._row_desc = None 699 try: 700 self.c = Protocol.Connection(unix_sock=unix_sock, host=host, port=port, socket_timeout=socket_timeout, ssl=ssl) 701 #self.c.connect() 702 self.c.authenticate(user, password=password, database=database) 703 except socket.error, e: 704 print repr(e) 705 print dir(e) 706 print str(e) 707 708 raise InterfaceError("communication error", e) 709 Cursor.__init__(self, self) 710 self._begin = PreparedStatement(self, "BEGIN TRANSACTION") 711 self._commit = PreparedStatement(self, "COMMIT TRANSACTION") 712 self._rollback = PreparedStatement(self, "ROLLBACK TRANSACTION")
713 714 ## 715 # Begins a new transaction. 716 # <p> 717 # Stability: Added in v1.00, stability guaranteed for v1.xx.
718 - def begin(self):
719 self._begin.execute()
720 721 ## 722 # Commits the running transaction. 723 # <p> 724 # Stability: Added in v1.00, stability guaranteed for v1.xx.
725 - def commit(self):
726 self._commit.execute()
727 728 ## 729 # Rolls back the running transaction. 730 # <p> 731 # Stability: Added in v1.00, stability guaranteed for v1.xx.
732 - def rollback(self):
733 self._rollback.execute()
734 735
736 -class Protocol(object):
737
738 - class SSLRequest(object):
739 - def __init__(self):
740 pass
741
742 - def serialize(selF):
743 return struct.pack("!ii", 8, 80877103)
744
745 - class StartupMessage(object):
746 - def __init__(self, user, database=None):
747 self.user = user 748 self.database = database
749
750 - def serialize(self):
751 protocol = 196608 752 val = struct.pack("!i", protocol) 753 val += "user\x00" + self.user + "\x00" 754 if self.database: 755 val += "database\x00" + self.database + "\x00" 756 val += "\x00" 757 val = struct.pack("!i", len(val) + 4) + val 758 return val
759
760 - class Query(object):
761 - def __init__(self, qs):
762 self.qs = qs
763
764 - def serialize(self):
765 val = self.qs + "\x00" 766 val = struct.pack("!i", len(val) + 4) + val 767 val = "Q" + val 768 return val
769
770 - class Parse(object):
771 - def __init__(self, ps, qs, type_oids):
772 self.ps = ps 773 self.qs = qs 774 self.type_oids = type_oids
775
776 - def serialize(self):
777 val = self.ps + "\x00" + self.qs + "\x00" 778 val = val + struct.pack("!h", len(self.type_oids)) 779 for oid in self.type_oids: 780 # Parse message doesn't seem to handle the -1 type_oid for NULL 781 # values that other messages handle. So we'll provide type_oid 705, 782 # the PG "unknown" type. 783 if oid == -1: oid = 705 784 val = val + struct.pack("!i", oid) 785 val = struct.pack("!i", len(val) + 4) + val 786 val = "P" + val 787 return val
788
789 - class Bind(object):
790 - def __init__(self, portal, ps, in_fc, params, out_fc, client_encoding):
791 self.portal = portal 792 self.ps = ps 793 self.in_fc = in_fc 794 self.params = [] 795 for i in range(len(params)): 796 if len(self.in_fc) == 0: 797 fc = 0 798 elif len(self.in_fc) == 1: 799 fc = self.in_fc[0] 800 else: 801 fc = self.in_fc[i] 802 self.params.append(Types.pg_value(params[i], fc, client_encoding = client_encoding)) 803 self.out_fc = out_fc
804
805 - def serialize(self):
806 val = self.portal + "\x00" + self.ps + "\x00" 807 val = val + struct.pack("!h", len(self.in_fc)) 808 for fc in self.in_fc: 809 val = val + struct.pack("!h", fc) 810 val = val + struct.pack("!h", len(self.params)) 811 for param in self.params: 812 if param == None: 813 # special case, NULL value 814 val = val + struct.pack("!i", -1) 815 else: 816 val = val + struct.pack("!i", len(param)) + param 817 val = val + struct.pack("!h", len(self.out_fc)) 818 for fc in self.out_fc: 819 val = val + struct.pack("!h", fc) 820 val = struct.pack("!i", len(val) + 4) + val 821 val = "B" + val 822 return val
823
824 - class Close(object):
825 - def __init__(self, typ, name):
826 if len(typ) != 1: 827 raise InternalError("Close typ must be 1 char") 828 self.typ = typ 829 self.name = name
830
831 - def serialize(self):
832 val = self.typ + self.name + "\x00" 833 val = struct.pack("!i", len(val) + 4) + val 834 val = "C" + val 835 return val
836
837 - class ClosePortal(Close):
838 - def __init__(self, name):
839 Protocol.Close.__init__(self, "P", name)
840
841 - class ClosePreparedStatement(Close):
842 - def __init__(self, name):
843 Protocol.Close.__init__(self, "S", name)
844
845 - class Describe(object):
846 - def __init__(self, typ, name):
847 if len(typ) != 1: 848 raise InternalError("Describe typ must be 1 char") 849 self.typ = typ 850 self.name = name
851
852 - def serialize(self):
853 val = self.typ + self.name + "\x00" 854 val = struct.pack("!i", len(val) + 4) + val 855 val = "D" + val 856 return val
857
858 - class DescribePortal(Describe):
859 - def __init__(self, name):
860 Protocol.Describe.__init__(self, "P", name)
861
862 - class DescribePreparedStatement(Describe):
863 - def __init__(self, name):
864 Protocol.Describe.__init__(self, "S", name)
865
866 - class Flush(object):
867 - def serialize(self):
868 return 'H\x00\x00\x00\x04'
869
870 - class Sync(object):
871 - def serialize(self):
872 return 'S\x00\x00\x00\x04'
873
874 - class PasswordMessage(object):
875 - def __init__(self, pwd):
876 self.pwd = pwd
877
878 - def serialize(self):
879 val = self.pwd + "\x00" 880 val = struct.pack("!i", len(val) + 4) + val 881 val = "p" + val 882 return val
883
884 - class Execute(object):
885 - def __init__(self, portal, row_count):
886 self.portal = portal 887 self.row_count = row_count
888
889 - def serialize(self):
890 val = self.portal + "\x00" + struct.pack("!i", self.row_count) 891 val = struct.pack("!i", len(val) + 4) + val 892 val = "E" + val 893 return val
894
895 - class AuthenticationRequest(object):
896 - def __init__(self, data):
897 pass
898
899 - def createFromData(data):
900 ident = struct.unpack("!i", data[:4])[0] 901 klass = Protocol.authentication_codes.get(ident, None) 902 if klass != None: 903 return klass(data[4:]) 904 else: 905 raise NotSupportedError("authentication method %r not supported" % (ident,))
906 createFromData = staticmethod(createFromData) 907
908 - def ok(self, conn, user, **kwargs):
909 raise InternalError("ok method should be overridden on AuthenticationRequest instance")
910
911 - class AuthenticationOk(AuthenticationRequest):
912 - def ok(self, conn, user, **kwargs):
913 return True
914
915 - class AuthenticationMD5Password(AuthenticationRequest):
916 - def __init__(self, data):
917 self.salt = "".join(struct.unpack("4c", data))
918
919 - def ok(self, conn, user, password=None, **kwargs):
920 if password == None: 921 raise InterfaceError("server requesting MD5 password authentication, but no password was provided") 922 pwd = "md5" + md5.new(md5.new(password + user).hexdigest() + self.salt).hexdigest() 923 conn._send(Protocol.PasswordMessage(pwd)) 924 msg = conn._read_message() 925 if isinstance(msg, Protocol.AuthenticationRequest): 926 return msg.ok(conn, user) 927 elif isinstance(msg, Protocol.ErrorResponse): 928 if msg.code == "28000": 929 raise InterfaceError("md5 password authentication failed") 930 else: 931 raise InternalError("server returned unexpected error %r" % msg) 932 else: 933 raise InternalError("server returned unexpected response %r" % msg)
934 935 authentication_codes = { 936 0: AuthenticationOk, 937 5: AuthenticationMD5Password, 938 } 939
940 - class ParameterStatus(object):
941 - def __init__(self, key, value):
942 self.key = key 943 self.value = value
944
945 - def createFromData(data):
946 key = data[:data.find("\x00")] 947 value = data[data.find("\x00")+1:-1] 948 return Protocol.ParameterStatus(key, value)
949 createFromData = staticmethod(createFromData)
950
951 - class BackendKeyData(object):
952 - def __init__(self, process_id, secret_key):
953 self.process_id = process_id 954 self.secret_key = secret_key
955
956 - def createFromData(data):
957 process_id, secret_key = struct.unpack("!2i", data) 958 return Protocol.BackendKeyData(process_id, secret_key)
959 createFromData = staticmethod(createFromData)
960
961 - class NoData(object):
962 - def createFromData(data):
963 return Protocol.NoData()
964 createFromData = staticmethod(createFromData)
965
966 - class ParseComplete(object):
967 - def createFromData(data):
968 return Protocol.ParseComplete()
969 createFromData = staticmethod(createFromData)
970
971 - class BindComplete(object):
972 - def createFromData(data):
973 return Protocol.BindComplete()
974 createFromData = staticmethod(createFromData)
975
976 - class CloseComplete(object):
977 - def createFromData(data):
978 return Protocol.CloseComplete()
979 createFromData = staticmethod(createFromData)
980
981 - class PortalSuspended(object):
982 - def createFromData(data):
983 return Protocol.PortalSuspended()
984 createFromData = staticmethod(createFromData)
985
986 - class ReadyForQuery(object):
987 - def __init__(self, status):
988 self.status = status
989
990 - def __repr__(self):
991 return "<ReadyForQuery %s>" % \ 992 {"I": "Idle", "T": "Idle in Transaction", "E": "Idle in Failed Transaction"}[self.status]
993
994 - def createFromData(data):
995 return Protocol.ReadyForQuery(data)
996 createFromData = staticmethod(createFromData)
997
998 - class NoticeResponse(object):
999 - def __init__(self):
1000 pass
1001 - def createFromData(data):
1002 # we could read the notice here, but we don't care yet. 1003 return Protocol.NoticeResponse()
1004 createFromData = staticmethod(createFromData)
1005
1006 - class ErrorResponse(object):
1007 - def __init__(self, severity, code, msg):
1008 self.severity = severity 1009 self.code = code 1010 self.msg = msg
1011
1012 - def __repr__(self):
1013 return "<ErrorResponse %s %s %r>" % (self.severity, self.code, self.msg)
1014
1015 - def createException(self):
1016 return ProgrammingError(self.severity, self.code, self.msg)
1017
1018 - def createFromData(data):
1019 args = {} 1020 for s in data.split("\x00"): 1021 if not s: 1022 continue 1023 elif s[0] == "S": 1024 args["severity"] = s[1:] 1025 elif s[0] == "C": 1026 args["code"] = s[1:] 1027 elif s[0] == "M": 1028 args["msg"] = s[1:] 1029 return Protocol.ErrorResponse(**args)
1030 createFromData = staticmethod(createFromData)
1031
1032 - class ParameterDescription(object):
1033 - def __init__(self, type_oids):
1034 self.type_oids = type_oids
1035 - def createFromData(data):
1036 count = struct.unpack("!h", data[:2])[0] 1037 type_oids = struct.unpack("!" + "i"*count, data[2:]) 1038 return Protocol.ParameterDescription(type_oids)
1039 createFromData = staticmethod(createFromData)
1040
1041 - class RowDescription(object):
1042 - def __init__(self, fields):
1043 self.fields = fields
1044
1045 - def createFromData(data):
1046 count = struct.unpack("!h", data[:2])[0] 1047 data = data[2:] 1048 fields = [] 1049 for i in range(count): 1050 null = data.find("\x00") 1051 field = {"name": data[:null]} 1052 data = data[null+1:] 1053 field["table_oid"], field["column_attrnum"], field["type_oid"], field["type_size"], field["type_modifier"], field["format"] = struct.unpack("!ihihih", data[:18]) 1054 data = data[18:] 1055 fields.append(field) 1056 return Protocol.RowDescription(fields)
1057 createFromData = staticmethod(createFromData)
1058
1059 - class CommandComplete(object):
1060 - def __init__(self, tag):
1061 self.tag = tag
1062
1063 - def createFromData(data):
1064 return Protocol.CommandComplete(data[:-1])
1065 createFromData = staticmethod(createFromData)
1066
1067 - class DataRow(object):
1068 - def __init__(self, fields):
1069 self.fields = fields
1070
1071 - def createFromData(data):
1072 count = struct.unpack("!h", data[:2])[0] 1073 data = data[2:] 1074 fields = [] 1075 for i in range(count): 1076 val_len = struct.unpack("!i", data[:4])[0] 1077 data = data[4:] 1078 if val_len == -1: 1079 fields.append(None) 1080 else: 1081 fields.append(data[:val_len]) 1082 data = data[val_len:] 1083 return Protocol.DataRow(fields)
1084 createFromData = staticmethod(createFromData)
1085
1086 - class SSLWrapper(object):
1087 - def __init__(self, sslobj):
1088 self.sslobj = sslobj
1089 - def send(self, data):
1090 self.sslobj.write(data)
1091 - def recv(self, num):
1092 return self.sslobj.read(num)
1093
1094 - class Connection(object):
1095 - def __init__(self, unix_sock=None, host=None, port=5432, socket_timeout=60, ssl=False):
1096 self._client_encoding = "ascii" 1097 if unix_sock == None and host != None: 1098 self._sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 1099 elif unix_sock != None: 1100 self._sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) 1101 else: 1102 raise ProgrammingError("one of host or unix_sock must be provided") 1103 if unix_sock == None and host != None: 1104 self._sock.connect((host, port)) 1105 elif unix_sock != None: 1106 self._sock.connect(unix_sock) 1107 if ssl: 1108 self._send(Protocol.SSLRequest()) 1109 resp = self._sock.recv(1) 1110 if resp == 'S': 1111 self._sock = Protocol.SSLWrapper(socket.ssl(self._sock)) 1112 else: 1113 raise InterfaceError("server refuses SSL") 1114 else: 1115 # settimeout causes ssl failure, on windows. Python bug 1462352. 1116 self._sock.settimeout(socket_timeout) 1117 self._state = "noauth" 1118 self._backend_key_data = None 1119 self._sock_lock = threading.Lock()
1120
1121 - def verifyState(self, state):
1122 if self._state != state: 1123 raise InternalError("connection state must be %s, is %s" % (state, self._state))
1124
1125 - def _send(self, msg):
1126 data = msg.serialize() 1127 self._sock.send(data)
1128
1129 - def _read_message(self):
1130 bytes = "" 1131 while len(bytes) < 5: 1132 tmp = self._sock.recv(5 - len(bytes)) 1133 bytes += tmp 1134 if len(bytes) != 5: 1135 raise InternalError("unable to read 5 bytes from socket %r" % bytes) 1136 message_code = bytes[0] 1137 data_len = struct.unpack("!i", bytes[1:])[0] - 4 1138 if data_len == 0: 1139 bytes = "" 1140 else: 1141 bytes = "" 1142 while len(bytes) < data_len: 1143 tmp = self._sock.recv(data_len - len(bytes)) 1144 bytes += tmp 1145 assert len(bytes) == data_len 1146 msg = Protocol.message_types[message_code].createFromData(bytes) 1147 if isinstance(msg, Protocol.NoticeResponse): 1148 # ignore NoticeResponse 1149 return self._read_message() 1150 else: 1151 return msg
1152
1153 - def authenticate(self, user, **kwargs):
1154 self.verifyState("noauth") 1155 self._send(Protocol.StartupMessage(user, database=kwargs.get("database",None))) 1156 msg = self._read_message() 1157 if isinstance(msg, Protocol.AuthenticationRequest): 1158 if msg.ok(self, user, **kwargs): 1159 self._state = "auth" 1160 while 1: 1161 msg = self._read_message() 1162 if isinstance(msg, Protocol.ReadyForQuery): 1163 # done reading messages 1164 self._state = "ready" 1165 break 1166 elif isinstance(msg, Protocol.ParameterStatus): 1167 if msg.key == "client_encoding": 1168 self._client_encoding = msg.value 1169 elif isinstance(msg, Protocol.BackendKeyData): 1170 self._backend_key_data = msg 1171 elif isinstance(msg, Protocol.ErrorResponse): 1172 raise msg.createException() 1173 else: 1174 raise InternalError("unexpected msg %r" % msg) 1175 else: 1176 raise InterfaceError("authentication method %s failed" % msg.__class__.__name__) 1177 else: 1178 raise InternalError("StartupMessage was responded to with non-AuthenticationRequest msg %r" % msg)
1179
1180 - def parse(self, statement, qs, types):
1181 self.verifyState("ready") 1182 self._sock_lock.acquire() 1183 try: 1184 type_info = [Types.pg_type_info(x) for x in types] 1185 param_types, param_fc = [x[0] for x in type_info], [x[1] for x in type_info] # zip(*type_info) -- fails on empty arr 1186 self._send(Protocol.Parse(statement, qs, param_types)) 1187 self._send(Protocol.DescribePreparedStatement(statement)) 1188 self._send(Protocol.Flush()) 1189 while 1: 1190 msg = self._read_message() 1191 if isinstance(msg, Protocol.ParseComplete): 1192 # ok, good. 1193 pass 1194 elif isinstance(msg, Protocol.ParameterDescription): 1195 # well, we don't really care -- we're going to send whatever 1196 # we want and let the database deal with it. But thanks 1197 # anyways! 1198 pass 1199 elif isinstance(msg, Protocol.NoData): 1200 # We're not waiting for a row description. Return 1201 # something destinctive to let bind know that there is no 1202 # output. 1203 return (None, param_fc) 1204 elif isinstance(msg, Protocol.RowDescription): 1205 return (msg, param_fc) 1206 elif isinstance(msg, Protocol.ErrorResponse): 1207 raise msg.createException() 1208 else: 1209 raise InternalError("Unexpected response msg %r" % (msg)) 1210 finally: 1211 self._sock_lock.release()
1212
1213 - def bind(self, portal, statement, params, parse_data):
1214 self.verifyState("ready") 1215 self._sock_lock.acquire() 1216 try: 1217 row_desc, param_fc = parse_data 1218 if row_desc == None: 1219 # no data coming out 1220 output_fc = () 1221 else: 1222 # We've got row_desc that allows us to identify what we're going to 1223 # get back from this statement. 1224 output_fc = [Types.py_type_info(f) for f in row_desc.fields] 1225 self._send(Protocol.Bind(portal, statement, param_fc, params, output_fc, self._client_encoding)) 1226 # We need to describe the portal after bind, since the return 1227 # format codes will be different (hopefully, always what we 1228 # requested). 1229 self._send(Protocol.DescribePortal(portal)) 1230 self._send(Protocol.Flush()) 1231 while 1: 1232 msg = self._read_message() 1233 if isinstance(msg, Protocol.BindComplete): 1234 # good news everybody! 1235 pass 1236 elif isinstance(msg, Protocol.NoData): 1237 # No data means we should execute this command right away. 1238 self._send(Protocol.Execute(portal, 0)) 1239 self._send(Protocol.Sync()) 1240 exc = None 1241 while 1: 1242 msg = self._read_message() 1243 if isinstance(msg, Protocol.CommandComplete): 1244 # more good news! 1245 pass 1246 elif isinstance(msg, Protocol.ReadyForQuery): 1247 if exc != None: 1248 raise exc 1249 break 1250 elif isinstance(msg, Protocol.ErrorResponse): 1251 exc = msg.createException() 1252 else: 1253 raise InternalError("unexpected response") 1254 return None 1255 elif isinstance(msg, Protocol.RowDescription): 1256 # Return the new row desc, since it will have the format 1257 # types we asked for 1258 return msg 1259 elif isinstance(msg, Protocol.ErrorResponse): 1260 raise msg.createException() 1261 else: 1262 raise InternalError("Unexpected response msg %r" % (msg)) 1263 finally: 1264 self._sock_lock.release()
1265
1266 - def fetch_rows(self, portal, row_count, row_desc):
1267 self.verifyState("ready") 1268 self._sock_lock.acquire() 1269 try: 1270 self._send(Protocol.Execute(portal, row_count)) 1271 self._send(Protocol.Flush()) 1272 rows = [] 1273 end_of_data = False 1274 while 1: 1275 msg = self._read_message() 1276 if isinstance(msg, Protocol.DataRow): 1277 rows.append( 1278 [Types.py_value(msg.fields[i], row_desc.fields[i], client_encoding=self._client_encoding) 1279 for i in range(len(msg.fields))] 1280 ) 1281 elif isinstance(msg, Protocol.PortalSuspended): 1282 # got all the rows we asked for, but not all that exist 1283 break 1284 elif isinstance(msg, Protocol.CommandComplete): 1285 self._send(Protocol.ClosePortal(portal)) 1286 self._send(Protocol.Sync()) 1287 while 1: 1288 msg = self._read_message() 1289 if isinstance(msg, Protocol.ReadyForQuery): 1290 # ready to move on with life... 1291 self._state = "ready" 1292 break 1293 elif isinstance(msg, Protocol.CloseComplete): 1294 # ok, great! 1295 pass 1296 elif isinstance(msg, Protocol.ErrorResponse): 1297 raise msg.createException() 1298 else: 1299 raise InternalError("unexpected response msg %r" % msg) 1300 end_of_data = True 1301 break 1302 elif isinstance(msg, Protocol.ErrorResponse): 1303 raise msg.createException() 1304 else: 1305 raise InternalError("Unexpected response msg %r" % msg) 1306 return end_of_data, rows 1307 finally: 1308 self._sock_lock.release()
1309
1310 - def close_statement(self, statement):
1311 self.verifyState("ready") 1312 self._sock_lock.acquire() 1313 try: 1314 self._send(Protocol.ClosePreparedStatement(statement)) 1315 self._send(Protocol.Sync()) 1316 while 1: 1317 msg = self._read_message() 1318 if isinstance(msg, Protocol.CloseComplete): 1319 # thanks! 1320 pass 1321 elif isinstance(msg, Protocol.ReadyForQuery): 1322 return 1323 elif isinstance(msg, Protocol.ErrorResponse): 1324 raise msg.createException() 1325 else: 1326 raise InternalError("Unexpected response msg %r" % msg) 1327 finally: 1328 self._sock_lock.release()
1329
1330 - def close_portal(self, portal):
1331 self.verifyState("ready") 1332 self._sock_lock.acquire() 1333 try: 1334 self._send(Protocol.ClosePortal(portal)) 1335 self._send(Protocol.Sync()) 1336 while 1: 1337 msg = self._read_message() 1338 if isinstance(msg, Protocol.CloseComplete): 1339 # thanks! 1340 pass 1341 elif isinstance(msg, Protocol.ReadyForQuery): 1342 return 1343 elif isinstance(msg, Protocol.ErrorResponse): 1344 raise msg.createException() 1345 else: 1346 raise InternalError("Unexpected response msg %r" % msg) 1347 finally: 1348 self._sock_lock.release()
1349 1350 message_types = { 1351 "N": NoticeResponse, 1352 "R": AuthenticationRequest, 1353 "S": ParameterStatus, 1354 "K": BackendKeyData, 1355 "Z": ReadyForQuery, 1356 "T": RowDescription, 1357 "E": ErrorResponse, 1358 "D": DataRow, 1359 "C": CommandComplete, 1360 "1": ParseComplete, 1361 "2": BindComplete, 1362 "3": CloseComplete, 1363 "s": PortalSuspended, 1364 "n": NoData, 1365 "t": ParameterDescription, 1366 }
1367
1368 -class Bytea(str):
1369 pass
1370
1371 -class Types(object):
1372
1373 - def pg_type_info(typ):
1374 data = Types.py_types.get(typ) 1375 if data == None: 1376 raise NotSupportedError("type %r not mapped to pg type" % typ) 1377 type_oid = data.get("tid") 1378 if type_oid == None: 1379 raise InternalError("type %r has no type_oid" % typ) 1380 elif type_oid == -1: 1381 # special case: NULL values 1382 return type_oid, 0 1383 prefer = data.get("prefer") 1384 if prefer != None: 1385 if prefer == "bin": 1386 if data.get("bin_out") == None: 1387 raise InternalError("bin format prefered but not avail for type %r" % typ) 1388 format = 1 1389 elif prefer == "txt": 1390 if data.get("txt_out") == None: 1391 raise InternalError("txt format prefered but not avail for type %r" % typ) 1392 format = 0 1393 else: 1394 raise InternalError("prefer flag not recognized for type %r" % typ) 1395 else: 1396 # by default, prefer bin, but go with whatever exists 1397 if data.get("bin_out"): 1398 format = 1 1399 elif data.get("txt_out"): 1400 format = 0 1401 else: 1402 raise InternalError("no conversion fuction for type %r" % typ) 1403 return type_oid, format
1404 pg_type_info = staticmethod(pg_type_info) 1405
1406 - def pg_value(v, fc, **kwargs):
1407 typ = type(v) 1408 data = Types.py_types.get(typ) 1409 if data == None: 1410 raise NotSupportedError("type %r not mapped to pg type" % typ) 1411 elif data.get("tid") == -1: 1412 # special case: NULL values 1413 return None 1414 if fc == 0: 1415 func = data.get("txt_out") 1416 elif fc == 1: 1417 func = data.get("bin_out") 1418 else: 1419 raise InternalError("unrecognized format code %r" % fc) 1420 if func == None: 1421 raise NotSupportedError("type %r, format code %r not supported" % (typ, fc)) 1422 return func(v, **kwargs)
1423 pg_value = staticmethod(pg_value) 1424
1425 - def py_type_info(description):
1426 type_oid = description['type_oid'] 1427 data = Types.pg_types.get(type_oid) 1428 if data == None: 1429 raise NotSupportedError("type oid %r not mapped to py type" % type_oid) 1430 prefer = data.get("prefer") 1431 if prefer != None: 1432 if prefer == "bin": 1433 if data.get("bin_in") == None: 1434 raise InternalError("bin format prefered but not avail for type oid %r" % type_oid) 1435 format = 1 1436 elif prefer == "txt": 1437 if data.get("txt_in") == None: 1438 raise InternalError("txt format prefered but not avail for type oid %r" % type_oid) 1439 format = 0 1440 else: 1441 raise InternalError("prefer flag not recognized for type oid %r" % type_oid) 1442 else: 1443 # by default, prefer bin, but go with whatever exists 1444 if data.get("bin_in"): 1445 format = 1 1446 elif data.get("txt_in"): 1447 format = 0 1448 else: 1449 raise InternalError("no conversion fuction for type oid %r" % type_oid) 1450 return format
1451 py_type_info = staticmethod(py_type_info) 1452
1453 - def py_value(v, description, **kwargs):
1454 if v == None: 1455 # special case - NULL value 1456 return None 1457 type_oid = description['type_oid'] 1458 format = description['format'] 1459 data = Types.pg_types.get(type_oid) 1460 if data == None: 1461 raise NotSupportedError("type oid %r not supported" % type_oid) 1462 if format == 0: 1463 func = data.get("txt_in") 1464 elif format == 1: 1465 func = data.get("bin_in") 1466 else: 1467 raise NotSupportedError("format code %r not supported" % format) 1468 if func == None: 1469 raise NotSupportedError("data response format %r, type %r not supported" % (format, type_oid)) 1470 return func(v, **kwargs)
1471 py_value = staticmethod(py_value) 1472
1473 - def boolin(data, **kwargs):
1474 return data == 't'
1475
1476 - def boolrecv(data, **kwargs):
1477 return data == "\x01"
1478
1479 - def int2recv(data, **kwargs):
1480 return struct.unpack("!h", data)[0]
1481
1482 - def int2in(data, **kwargs):
1483 return int(data)
1484
1485 - def int4recv(data, **kwargs):
1486 return struct.unpack("!i", data)[0]
1487
1488 - def int4in(data, **kwargs):
1489 return int(data)
1490
1491 - def int8recv(data, **kwargs):
1492 return struct.unpack("!q", data)[0]
1493
1494 - def int8in(data, **kwargs):
1495 return int(data)
1496
1497 - def float4in(data, **kwargs):
1498 return float(data)
1499
1500 - def float4recv(data, **kwargs):
1501 return struct.unpack("!f", data)[0]
1502
1503 - def float8recv(data, **kwargs):
1504 return struct.unpack("!d", data)[0]
1505
1506 - def float8in(data, **kwargs):
1507 return float(data)
1508
1509 - def float8send(v, **kwargs):
1510 return struct.pack("!d", v)
1511 1512 # The timestamp_recv function is sadly not in use because some PostgreSQL 1513 # servers are compiled with HAVE_INT64_TIMESTAMP, and some are not. This 1514 # alters the binary format of the timestamp, cannot be perfectly detected, 1515 # and there is no message from the server indicating which format is in 1516 # use. Ah, well, obviously binary formats are hit-and-miss... 1517 #def timestamp_recv(data, **kwargs): 1518 # val = struct.unpack("!d", data)[0] 1519 # return datetime.datetime(2000, 1, 1) + datetime.timedelta(seconds = val) 1520
1521 - def timestamp_in(data, **kwargs):
1522 year = int(data[0:4]) 1523 month = int(data[5:7]) 1524 day = int(data[8:10]) 1525 hour = int(data[11:13]) 1526 minute = int(data[14:16]) 1527 sec = decimal.Decimal(data[17:]) 1528 return datetime.datetime(year, month, day, hour, minute, int(sec), int((sec - int(sec)) * 1000000))
1529
1530 - def timestamp_out(v, **kwargs):
1531 return v.isoformat(' ')
1532
1533 - def numeric_in(data, **kwargs):
1534 if data.find(".") == -1: 1535 return int(data) 1536 else: 1537 return decimal.Decimal(data)
1538
1539 - def numeric_out(v, **kwargs):
1540 return str(v)
1541
1542 - def varcharin(data, client_encoding, **kwargs):
1543 return unicode(data, client_encoding)
1544
1545 - def textout(v, client_encoding, **kwargs):
1546 return v.encode(client_encoding)
1547
1548 - def timestamptz_in(data, description):
1549 year = int(data[0:4]) 1550 month = int(data[5:7]) 1551 day = int(data[8:10]) 1552 hour = int(data[11:13]) 1553 minute = int(data[14:16]) 1554 tz_sep = data.rfind("-") 1555 sec = decimal.Decimal(data[17:tz_sep]) 1556 tz = data[tz_sep:] 1557 print repr(data), repr(description) 1558 print repr(tz) 1559 return datetime.datetime(year, month, day, hour, minute, int(sec), int((sec - int(sec)) * 1000000), Types.FixedOffsetTz(tz))
1560
1561 - class FixedOffsetTz(datetime.tzinfo):
1562 - def __init__(self, hrs):
1563 self.hrs = int(hrs) 1564 self.name = hrs
1565
1566 - def utcoffset(self, dt):
1567 return datetime.timedelta(hours=1) * self.hrs
1568
1569 - def tzname(self, dt):
1570 return self.name
1571
1572 - def dst(self, dt):
1573 return datetime.timedelta(0)
1574
1575 - def byteasend(v, **kwargs):
1576 return str(v)
1577
1578 - def bytearecv(data, **kwargs):
1579 return Bytea(data)
1580 1581 # interval support does not provide a Python-usable interval object yet
1582 - def interval_in(data, **kwargs):
1583 return data
1584 1585 py_types = { 1586 int: {"tid": 1700, "txt_out": numeric_out}, 1587 long: {"tid": 1700, "txt_out": numeric_out}, 1588 str: {"tid": 25, "txt_out": textout}, 1589 unicode: {"tid": 25, "txt_out": textout}, 1590 float: {"tid": 701, "bin_out": float8send}, 1591 decimal.Decimal: {"tid": 1700, "txt_out": numeric_out}, 1592 Bytea: {"tid": 17, "bin_out": byteasend}, 1593 datetime.datetime: {"tid": 1114, "txt_out": timestamp_out}, 1594 type(None): {"tid": -1}, 1595 } 1596 1597 pg_types = { 1598 16: {"txt_in": boolin, "bin_in": boolrecv, "prefer": "bin"}, 1599 17: {"bin_in": bytearecv}, 1600 20: {"txt_in": int8in, "bin_in": int8recv, "prefer": "bin"}, 1601 21: {"txt_in": int2in, "bin_in": int2recv, "prefer": "bin"}, 1602 23: {"txt_in": int4in, "bin_in": int4recv, "prefer": "bin"}, 1603 25: {"txt_in": varcharin}, # TEXT type 1604 26: {"txt_in": numeric_in}, # oid type 1605 700: {"txt_in": float4in, "bin_in": float4recv, "prefer": "bin"}, 1606 701: {"txt_in": float8in, "bin_in": float8recv, "prefer": "bin"}, 1607 1042: {"txt_in": varcharin}, # CHAR type 1608 1043: {"txt_in": varcharin}, # VARCHAR type 1609 1114: {"txt_in": timestamp_in}, #, "bin_in": timestamp_recv, "prefer": "bin"}, 1610 1186: {"txt_in": interval_in}, 1611 1700: {"txt_in": numeric_in}, 1612 }
1613 #1184: (timestamptz_in, None), # timestamp w/ tz 1614