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

Source Code for Module osh.external.pg8000.protocol

   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 threading 
  34  import struct 
  35  import md5 
  36  from cStringIO import StringIO 
  37   
  38  from errors import * 
  39  from util import MulticastDelegate 
  40  import types 
41 42 ## 43 # An SSLRequest message. To initiate an SSL-encrypted connection, an 44 # SSLRequest message is used rather than a {@link StartupMessage 45 # StartupMessage}. A StartupMessage is still sent, but only after SSL 46 # negotiation (if accepted). 47 # <p> 48 # Stability: This is an internal class. No stability guarantee is made. 49 -class SSLRequest(object):
50 - def __init__(self):
51 pass
52 53 # Int32(8) - Message length, including self.<br> 54 # Int32(80877103) - The SSL request code.<br>
55 - def serialize(self):
56 return struct.pack("!ii", 8, 80877103)
57
58 59 ## 60 # A StartupMessage message. Begins a DB session, identifying the user to be 61 # authenticated as and the database to connect to. 62 # <p> 63 # Stability: This is an internal class. No stability guarantee is made. 64 -class StartupMessage(object):
65 - def __init__(self, user, database=None):
66 self.user = user 67 self.database = database
68 69 # Int32 - Message length, including self. 70 # Int32(196608) - Protocol version number. Version 3.0. 71 # Any number of key/value pairs, terminated by a zero byte: 72 # String - A parameter name (user, database, or options) 73 # String - Parameter value
74 - def serialize(self):
75 protocol = 196608 76 val = struct.pack("!i", protocol) 77 val += "user\x00" + self.user + "\x00" 78 if self.database: 79 val += "database\x00" + self.database + "\x00" 80 val += "\x00" 81 val = struct.pack("!i", len(val) + 4) + val 82 return val
83
84 85 ## 86 # Parse message. Creates a prepared statement in the DB session. 87 # <p> 88 # Stability: This is an internal class. No stability guarantee is made. 89 # 90 # @param ps Name of the prepared statement to create. 91 # @param qs Query string. 92 # @param type_oids An iterable that contains the PostgreSQL type OIDs for 93 # parameters in the query string. 94 -class Parse(object):
95 - def __init__(self, ps, qs, type_oids):
96 self.ps = ps 97 self.qs = qs 98 self.type_oids = type_oids
99
100 - def __repr__(self):
101 return "<Parse ps=%r qs=%r>" % (self.ps, self.qs)
102 103 # Byte1('P') - Identifies the message as a Parse command. 104 # Int32 - Message length, including self. 105 # String - Prepared statement name. An empty string selects the unnamed 106 # prepared statement. 107 # String - The query string. 108 # Int16 - Number of parameter data types specified (can be zero). 109 # For each parameter: 110 # Int32 - The OID of the parameter data type.
111 - def serialize(self):
112 val = self.ps + "\x00" + self.qs + "\x00" 113 val = val + struct.pack("!h", len(self.type_oids)) 114 for oid in self.type_oids: 115 # Parse message doesn't seem to handle the -1 type_oid for NULL 116 # values that other messages handle. So we'll provide type_oid 705, 117 # the PG "unknown" type. 118 if oid == -1: oid = 705 119 val = val + struct.pack("!i", oid) 120 val = struct.pack("!i", len(val) + 4) + val 121 val = "P" + val 122 return val
123
124 125 ## 126 # Bind message. Readies a prepared statement for execution. 127 # <p> 128 # Stability: This is an internal class. No stability guarantee is made. 129 # 130 # @param portal Name of the destination portal. 131 # @param ps Name of the source prepared statement. 132 # @param in_fc An iterable containing the format codes for input 133 # parameters. 0 = Text, 1 = Binary. 134 # @param params The parameters. 135 # @param out_fc An iterable containing the format codes for output 136 # parameters. 0 = Text, 1 = Binary. 137 # @param kwargs Additional arguments to pass to the type conversion 138 # methods. 139 -class Bind(object):
140 - def __init__(self, portal, ps, in_fc, params, out_fc, **kwargs):
141 self.portal = portal 142 self.ps = ps 143 self.in_fc = in_fc 144 self.params = [] 145 for i in range(len(params)): 146 if len(self.in_fc) == 0: 147 fc = 0 148 elif len(self.in_fc) == 1: 149 fc = self.in_fc[0] 150 else: 151 fc = self.in_fc[i] 152 self.params.append(types.pg_value(params[i], fc, **kwargs)) 153 self.out_fc = out_fc
154
155 - def __repr__(self):
156 return "<Bind p=%r s=%r>" % (self.portal, self.ps)
157 158 # Byte1('B') - Identifies the Bind command. 159 # Int32 - Message length, including self. 160 # String - Name of the destination portal. 161 # String - Name of the source prepared statement. 162 # Int16 - Number of parameter format codes. 163 # For each parameter format code: 164 # Int16 - The parameter format code. 165 # Int16 - Number of parameter values. 166 # For each parameter value: 167 # Int32 - The length of the parameter value, in bytes, not including this 168 # this length. -1 indicates a NULL parameter value, in which no 169 # value bytes follow. 170 # Byte[n] - Value of the parameter. 171 # Int16 - The number of result-column format codes. 172 # For each result-column format code: 173 # Int16 - The format code.
174 - def serialize(self):
175 retval = StringIO() 176 retval.write(self.portal + "\x00") 177 retval.write(self.ps + "\x00") 178 retval.write(struct.pack("!h", len(self.in_fc))) 179 for fc in self.in_fc: 180 retval.write(struct.pack("!h", fc)) 181 retval.write(struct.pack("!h", len(self.params))) 182 for param in self.params: 183 if param == None: 184 # special case, NULL value 185 retval.write(struct.pack("!i", -1)) 186 else: 187 retval.write(struct.pack("!i", len(param))) 188 retval.write(param) 189 retval.write(struct.pack("!h", len(self.out_fc))) 190 for fc in self.out_fc: 191 retval.write(struct.pack("!h", fc)) 192 val = retval.getvalue() 193 val = struct.pack("!i", len(val) + 4) + val 194 val = "B" + val 195 return val
196
197 198 ## 199 # A Close message, used for closing prepared statements and portals. 200 # <p> 201 # Stability: This is an internal class. No stability guarantee is made. 202 # 203 # @param typ 'S' for prepared statement, 'P' for portal. 204 # @param name The name of the item to close. 205 -class Close(object):
206 - def __init__(self, typ, name):
207 if len(typ) != 1: 208 raise InternalError("Close typ must be 1 char") 209 self.typ = typ 210 self.name = name
211 212 # Byte1('C') - Identifies the message as a close command. 213 # Int32 - Message length, including self. 214 # Byte1 - 'S' for prepared statement, 'P' for portal. 215 # String - The name of the item to close.
216 - def serialize(self):
217 val = self.typ + self.name + "\x00" 218 val = struct.pack("!i", len(val) + 4) + val 219 val = "C" + val 220 return val
221
222 223 ## 224 # A specialized Close message for a portal. 225 # <p> 226 # Stability: This is an internal class. No stability guarantee is made. 227 -class ClosePortal(Close):
228 - def __init__(self, name):
229 Close.__init__(self, "P", name)
230
231 232 ## 233 # A specialized Close message for a prepared statement. 234 # <p> 235 # Stability: This is an internal class. No stability guarantee is made. 236 -class ClosePreparedStatement(Close):
237 - def __init__(self, name):
238 Close.__init__(self, "S", name)
239
240 241 ## 242 # A Describe message, used for obtaining information on prepared statements 243 # and portals. 244 # <p> 245 # Stability: This is an internal class. No stability guarantee is made. 246 # 247 # @param typ 'S' for prepared statement, 'P' for portal. 248 # @param name The name of the item to close. 249 -class Describe(object):
250 - def __init__(self, typ, name):
251 if len(typ) != 1: 252 raise InternalError("Describe typ must be 1 char") 253 self.typ = typ 254 self.name = name
255 256 # Byte1('D') - Identifies the message as a describe command. 257 # Int32 - Message length, including self. 258 # Byte1 - 'S' for prepared statement, 'P' for portal. 259 # String - The name of the item to close.
260 - def serialize(self):
261 val = self.typ + self.name + "\x00" 262 val = struct.pack("!i", len(val) + 4) + val 263 val = "D" + val 264 return val
265
266 267 ## 268 # A specialized Describe message for a portal. 269 # <p> 270 # Stability: This is an internal class. No stability guarantee is made. 271 -class DescribePortal(Describe):
272 - def __init__(self, name):
273 Describe.__init__(self, "P", name)
274
275 - def __repr__(self):
276 return "<DescribePortal %r>" % (self.name)
277
278 279 ## 280 # A specialized Describe message for a prepared statement. 281 # <p> 282 # Stability: This is an internal class. No stability guarantee is made. 283 -class DescribePreparedStatement(Describe):
284 - def __init__(self, name):
285 Describe.__init__(self, "S", name)
286
287 - def __repr__(self):
288 return "<DescribePreparedStatement %r>" % (self.name)
289
290 291 ## 292 # A Flush message forces the backend to deliver any data pending in its 293 # output buffers. 294 # <p> 295 # Stability: This is an internal class. No stability guarantee is made. 296 -class Flush(object):
297 # Byte1('H') - Identifies the message as a flush command. 298 # Int32(4) - Length of message, including self.
299 - def serialize(self):
300 return 'H\x00\x00\x00\x04'
301
302 - def __repr__(self):
303 return "<Flush>"
304
305 ## 306 # Causes the backend to close the current transaction (if not in a BEGIN/COMMIT 307 # block), and issue ReadyForQuery. 308 # <p> 309 # Stability: This is an internal class. No stability guarantee is made. 310 -class Sync(object):
311 # Byte1('S') - Identifies the message as a sync command. 312 # Int32(4) - Length of message, including self.
313 - def serialize(self):
314 return 'S\x00\x00\x00\x04'
315
316 - def __repr__(self):
317 return "<Sync>"
318
319 320 ## 321 # Transmits a password. 322 # <p> 323 # Stability: This is an internal class. No stability guarantee is made. 324 -class PasswordMessage(object):
325 - def __init__(self, pwd):
326 self.pwd = pwd
327 328 # Byte1('p') - Identifies the message as a password message. 329 # Int32 - Message length including self. 330 # String - The password. Password may be encrypted.
331 - def serialize(self):
332 val = self.pwd + "\x00" 333 val = struct.pack("!i", len(val) + 4) + val 334 val = "p" + val 335 return val
336
337 338 ## 339 # Requests that the backend execute a portal and retrieve any number of rows. 340 # <p> 341 # Stability: This is an internal class. No stability guarantee is made. 342 # @param row_count The number of rows to return. Can be zero to indicate the 343 # backend should return all rows. If the portal represents a 344 # query that does not return rows, no rows will be returned 345 # no matter what the row_count. 346 -class Execute(object):
347 - def __init__(self, portal, row_count):
348 self.portal = portal 349 self.row_count = row_count
350 351 # Byte1('E') - Identifies the message as an execute message. 352 # Int32 - Message length, including self. 353 # String - The name of the portal to execute. 354 # Int32 - Maximum number of rows to return, if portal contains a query that 355 # returns rows. 0 = no limit.
356 - def serialize(self):
357 val = self.portal + "\x00" + struct.pack("!i", self.row_count) 358 val = struct.pack("!i", len(val) + 4) + val 359 val = "E" + val 360 return val
361
362 363 ## 364 # Informs the backend that the connection is being closed. 365 # <p> 366 # Stability: This is an internal class. No stability guarantee is made. 367 -class Terminate(object):
368 - def __init__(self):
369 pass
370 371 # Byte1('X') - Identifies the message as a terminate message. 372 # Int32(4) - Message length, including self.
373 - def serialize(self):
374 return 'X\x00\x00\x00\x04'
375
376 ## 377 # Base class of all Authentication[*] messages. 378 # <p> 379 # Stability: This is an internal class. No stability guarantee is made. 380 -class AuthenticationRequest(object):
381 - def __init__(self, data):
382 pass
383 384 # Byte1('R') - Identifies the message as an authentication request. 385 # Int32(8) - Message length, including self. 386 # Int32 - An authentication code that represents different 387 # authentication messages: 388 # 0 = AuthenticationOk 389 # 5 = MD5 pwd 390 # 2 = Kerberos v5 (not supported by pg8000) 391 # 3 = Cleartext pwd (not supported by pg8000) 392 # 4 = crypt() pwd (not supported by pg8000) 393 # 6 = SCM credential (not supported by pg8000) 394 # 7 = GSSAPI (not supported by pg8000) 395 # 8 = GSSAPI data (not supported by pg8000) 396 # 9 = SSPI (not supported by pg8000) 397 # Some authentication messages have additional data following the 398 # authentication code. That data is documented in the appropriate class.
399 - def createFromData(data):
400 ident = struct.unpack("!i", data[:4])[0] 401 klass = authentication_codes.get(ident, None) 402 if klass != None: 403 return klass(data[4:]) 404 else: 405 raise NotSupportedError("authentication method %r not supported" % (ident,))
406 createFromData = staticmethod(createFromData) 407
408 - def ok(self, conn, user, **kwargs):
409 raise InternalError("ok method should be overridden on AuthenticationRequest instance")
410
411 ## 412 # A message representing that the backend accepting the provided username 413 # without any challenge. 414 # <p> 415 # Stability: This is an internal class. No stability guarantee is made. 416 -class AuthenticationOk(AuthenticationRequest):
417 - def ok(self, conn, user, **kwargs):
418 return True
419
420 421 ## 422 # A message representing the backend requesting an MD5 hashed password 423 # response. The response will be sent as md5(md5(pwd + login) + salt). 424 # <p> 425 # Stability: This is an internal class. No stability guarantee is made. 426 -class AuthenticationMD5Password(AuthenticationRequest):
427 # Additional message data: 428 # Byte4 - Hash salt.
429 - def __init__(self, data):
430 self.salt = "".join(struct.unpack("4c", data))
431
432 - def ok(self, conn, user, password=None, **kwargs):
433 if password == None: 434 raise InterfaceError("server requesting MD5 password authentication, but no password was provided") 435 pwd = "md5" + md5.new(md5.new(password + user).hexdigest() + self.salt).hexdigest() 436 conn._send(PasswordMessage(pwd)) 437 438 reader = MessageReader(conn) 439 reader.add_message(AuthenticationRequest, lambda msg, reader: reader.return_value(msg.ok(conn, user)), reader) 440 reader.add_message(ErrorResponse, self._ok_error) 441 return reader.handle_messages()
442
443 - def _ok_error(self, msg):
444 if msg.code == "28000": 445 raise InterfaceError("md5 password authentication failed") 446 else: 447 raise msg.createException()
448 449 authentication_codes = { 450 0: AuthenticationOk, 451 5: AuthenticationMD5Password, 452 }
453 454 455 ## 456 # ParameterStatus message sent from backend, used to inform the frotnend of 457 # runtime configuration parameter changes. 458 # <p> 459 # Stability: This is an internal class. No stability guarantee is made. 460 -class ParameterStatus(object):
461 - def __init__(self, key, value):
462 self.key = key 463 self.value = value
464 465 # Byte1('S') - Identifies ParameterStatus 466 # Int32 - Message length, including self. 467 # String - Runtime parameter name. 468 # String - Runtime parameter value.
469 - def createFromData(data):
470 key = data[:data.find("\x00")] 471 value = data[data.find("\x00")+1:-1] 472 return ParameterStatus(key, value)
473 createFromData = staticmethod(createFromData)
474
475 476 ## 477 # BackendKeyData message sent from backend. Contains a connection's process 478 # ID and a secret key. Can be used to terminate the connection's current 479 # actions, such as a long running query. Not supported by pg8000 yet. 480 # <p> 481 # Stability: This is an internal class. No stability guarantee is made. 482 -class BackendKeyData(object):
483 - def __init__(self, process_id, secret_key):
484 self.process_id = process_id 485 self.secret_key = secret_key
486 487 # Byte1('K') - Identifier. 488 # Int32(12) - Message length, including self. 489 # Int32 - Process ID. 490 # Int32 - Secret key.
491 - def createFromData(data):
492 process_id, secret_key = struct.unpack("!2i", data) 493 return BackendKeyData(process_id, secret_key)
494 createFromData = staticmethod(createFromData)
495
496 497 ## 498 # Message representing a query with no data. 499 # <p> 500 # Stability: This is an internal class. No stability guarantee is made. 501 -class NoData(object):
502 # Byte1('n') - Identifier. 503 # Int32(4) - Message length, including self.
504 - def createFromData(data):
505 return NoData()
506 createFromData = staticmethod(createFromData)
507
508 509 ## 510 # Message representing a successful Parse. 511 # <p> 512 # Stability: This is an internal class. No stability guarantee is made. 513 -class ParseComplete(object):
514 # Byte1('1') - Identifier. 515 # Int32(4) - Message length, including self.
516 - def createFromData(data):
517 return ParseComplete()
518 createFromData = staticmethod(createFromData)
519
520 521 ## 522 # Message representing a successful Bind. 523 # <p> 524 # Stability: This is an internal class. No stability guarantee is made. 525 -class BindComplete(object):
526 # Byte1('2') - Identifier. 527 # Int32(4) - Message length, including self.
528 - def createFromData(data):
529 return BindComplete()
530 createFromData = staticmethod(createFromData)
531
532 533 ## 534 # Message representing a successful Close. 535 # <p> 536 # Stability: This is an internal class. No stability guarantee is made. 537 -class CloseComplete(object):
538 # Byte1('3') - Identifier. 539 # Int32(4) - Message length, including self.
540 - def createFromData(data):
541 return CloseComplete()
542 createFromData = staticmethod(createFromData)
543
544 545 ## 546 # Message representing data from an Execute has been received, but more data 547 # exists in the portal. 548 # <p> 549 # Stability: This is an internal class. No stability guarantee is made. 550 -class PortalSuspended(object):
551 # Byte1('s') - Identifier. 552 # Int32(4) - Message length, including self.
553 - def createFromData(data):
554 return PortalSuspended()
555 createFromData = staticmethod(createFromData)
556
557 558 ## 559 # Message representing the backend is ready to process a new query. 560 # <p> 561 # Stability: This is an internal class. No stability guarantee is made. 562 -class ReadyForQuery(object):
563 - def __init__(self, status):
564 self._status = status
565 566 ## 567 # I = Idle, T = Idle in Transaction, E = idle in failed transaction. 568 status = property(lambda self: self._status) 569
570 - def __repr__(self):
571 return "<ReadyForQuery %s>" % \ 572 {"I": "Idle", "T": "Idle in Transaction", "E": "Idle in Failed Transaction"}[self.status]
573 574 # Byte1('Z') - Identifier. 575 # Int32(5) - Message length, including self. 576 # Byte1 - Status indicator.
577 - def createFromData(data):
578 return ReadyForQuery(data)
579 createFromData = staticmethod(createFromData)
580
581 582 ## 583 # Represents a notice sent from the server. This is not the same as a 584 # notification. A notice is just additional information about a query, such 585 # as a notice that a primary key has automatically been created for a table. 586 # <p> 587 # A NoticeResponse instance will have properties containing the data sent 588 # from the server: 589 # <ul> 590 # <li>severity -- "ERROR", "FATAL', "PANIC", "WARNING", "NOTICE", "DEBUG", 591 # "INFO", or "LOG". Always present.</li> 592 # <li>code -- the SQLSTATE code for the error. See Appendix A of the 593 # PostgreSQL documentation for specific error codes. Always present.</li> 594 # <li>msg -- human-readable error message. Always present.</li> 595 # <li>detail -- Optional additional information.</li> 596 # <li>hint -- Optional suggestion about what to do about the issue.</li> 597 # <li>position -- Optional index into the query string.</li> 598 # <li>where -- Optional context.</li> 599 # <li>file -- Source-code file.</li> 600 # <li>line -- Source-code line.</li> 601 # <li>routine -- Source-code routine.</li> 602 # </ul> 603 # <p> 604 # Stability: Added in pg8000 v1.03. Required properties severity, code, and 605 # msg are guaranteed for v1.xx. Other properties should be checked with 606 # hasattr before accessing. 607 -class NoticeResponse(object):
608 responseKeys = { 609 "S": "severity", # always present 610 "C": "code", # always present 611 "M": "msg", # always present 612 "D": "detail", 613 "H": "hint", 614 "P": "position", 615 "p": "_position", 616 "q": "_query", 617 "W": "where", 618 "F": "file", 619 "L": "line", 620 "R": "routine", 621 } 622
623 - def __init__(self, **kwargs):
624 for arg, value in kwargs.items(): 625 setattr(self, arg, value)
626
627 - def __repr__(self):
628 return "<NoticeResponse %s %s %r>" % (self.severity, self.code, self.msg)
629
630 - def dataIntoDict(data):
631 retval = {} 632 for s in data.split("\x00"): 633 if not s: continue 634 key, value = s[0], s[1:] 635 key = NoticeResponse.responseKeys.get(key, key) 636 retval[key] = value 637 return retval
638 dataIntoDict = staticmethod(dataIntoDict) 639 640 # Byte1('N') - Identifier 641 # Int32 - Message length 642 # Any number of these, followed by a zero byte: 643 # Byte1 - code identifying the field type (see responseKeys) 644 # String - field value
645 - def createFromData(data):
647 createFromData = staticmethod(createFromData)
648
649 650 ## 651 # A message sent in case of a server-side error. Contains the same properties 652 # that {@link NoticeResponse NoticeResponse} contains. 653 # <p> 654 # Stability: Added in pg8000 v1.03. Required properties severity, code, and 655 # msg are guaranteed for v1.xx. Other properties should be checked with 656 # hasattr before accessing. 657 -class ErrorResponse(object):
658 - def __init__(self, **kwargs):
659 for arg, value in kwargs.items(): 660 setattr(self, arg, value)
661
662 - def __repr__(self):
663 return "<ErrorResponse %s %s %r>" % (self.severity, self.code, self.msg)
664
665 - def createException(self):
666 return ProgrammingError(self.severity, self.code, self.msg)
667
668 - def createFromData(data):
670 createFromData = staticmethod(createFromData)
671
672 673 ## 674 # A message sent if this connection receives a NOTIFY that it was LISTENing for. 675 # <p> 676 # Stability: Added in pg8000 v1.03. When limited to accessing properties from 677 # a notification event dispatch, stability is guaranteed for v1.xx. 678 -class NotificationResponse(object):
679 - def __init__(self, backend_pid, condition, additional_info):
680 self._backend_pid = backend_pid 681 self._condition = condition 682 self._additional_info = additional_info
683 684 ## 685 # An integer representing the process ID of the backend that triggered 686 # the NOTIFY. 687 # <p> 688 # Stability: Added in pg8000 v1.03, stability guaranteed for v1.xx. 689 backend_pid = property(lambda self: self._backend_pid) 690 691 ## 692 # The name of the notification fired. 693 # <p> 694 # Stability: Added in pg8000 v1.03, stability guaranteed for v1.xx. 695 condition = property(lambda self: self._condition) 696 697 ## 698 # Currently unspecified by the PostgreSQL documentation as of v8.3.1. 699 # <p> 700 # Stability: Added in pg8000 v1.03, stability guaranteed for v1.xx. 701 additional_info = property(lambda self: self._additional_info) 702
703 - def __repr__(self):
704 return "<NotificationResponse %s %s %r>" % (self.backend_pid, self.condition, self.additional_info)
705
706 - def createFromData(data):
707 backend_pid = struct.unpack("!i", data[:4])[0] 708 data = data[4:] 709 null = data.find("\x00") 710 condition = data[:null] 711 data = data[null+1:] 712 null = data.find("\x00") 713 additional_info = data[:null] 714 return NotificationResponse(backend_pid, condition, additional_info)
715 createFromData = staticmethod(createFromData)
716
717 718 -class ParameterDescription(object):
719 - def __init__(self, type_oids):
720 self.type_oids = type_oids
721 - def createFromData(data):
722 count = struct.unpack("!h", data[:2])[0] 723 type_oids = struct.unpack("!" + "i"*count, data[2:]) 724 return ParameterDescription(type_oids)
725 createFromData = staticmethod(createFromData)
726
727 728 -class RowDescription(object):
729 - def __init__(self, fields):
730 self.fields = fields
731
732 - def createFromData(data):
733 count = struct.unpack("!h", data[:2])[0] 734 data = data[2:] 735 fields = [] 736 for i in range(count): 737 null = data.find("\x00") 738 field = {"name": data[:null]} 739 data = data[null+1:] 740 field["table_oid"], field["column_attrnum"], field["type_oid"], field["type_size"], field["type_modifier"], field["format"] = struct.unpack("!ihihih", data[:18]) 741 data = data[18:] 742 fields.append(field) 743 return RowDescription(fields)
744 createFromData = staticmethod(createFromData)
745
746 747 -class CommandComplete(object):
748 - def __init__(self, command, rows=None, oid=None):
749 self.command = command 750 self.rows = rows 751 self.oid = oid
752
753 - def createFromData(data):
754 values = data[:-1].split(" ") 755 args = {} 756 args['command'] = values[0] 757 if args['command'] in ("INSERT", "DELETE", "UPDATE", "MOVE", "FETCH", "COPY"): 758 args['rows'] = int(values[-1]) 759 if args['command'] == "INSERT": 760 args['oid'] = int(values[1]) 761 else: 762 args['command'] = data[:-1] 763 return CommandComplete(**args)
764 createFromData = staticmethod(createFromData)
765
766 767 -class DataRow(object):
768 - def __init__(self, fields):
769 self.fields = fields
770
771 - def createFromData(data):
772 count = struct.unpack("!h", data[:2])[0] 773 data = data[2:] 774 fields = [] 775 for i in range(count): 776 val_len = struct.unpack("!i", data[:4])[0] 777 data = data[4:] 778 if val_len == -1: 779 fields.append(None) 780 else: 781 fields.append(data[:val_len]) 782 data = data[val_len:] 783 return DataRow(fields)
784 createFromData = staticmethod(createFromData)
785
786 787 -class SSLWrapper(object):
788 - def __init__(self, sslobj):
789 self.sslobj = sslobj
790 - def send(self, data):
791 self.sslobj.write(data)
792 - def recv(self, num):
793 return self.sslobj.read(num)
794
795 796 -class MessageReader(object):
797 - def __init__(self, connection):
798 self._conn = connection 799 self._msgs = [] 800 801 # If true, raise exception from an ErrorResponse after messages are 802 # processed. This can be used to leave the connection in a usable 803 # state after an error response, rather than having unconsumed 804 # messages that won't be understood in another context. 805 self.delay_raising_exception = False 806 807 self.ignore_unhandled_messages = False
808
809 - def add_message(self, msg_class, handler, *args, **kwargs):
810 self._msgs.append((msg_class, handler, args, kwargs))
811
812 - def clear_messages(self):
813 self._msgs = []
814
815 - def return_value(self, value):
816 self._retval = value
817
818 - def handle_messages(self):
819 exc = None 820 while 1: 821 msg = self._conn._read_message() 822 msg_handled = False 823 for (msg_class, handler, args, kwargs) in self._msgs: 824 if isinstance(msg, msg_class): 825 msg_handled = True 826 retval = handler(msg, *args, **kwargs) 827 if retval: 828 # The handler returned a true value, meaning that the 829 # message loop should be aborted. 830 if exc != None: 831 raise exc 832 return retval 833 elif hasattr(self, "_retval"): 834 # The handler told us to return -- used for non-true 835 # return values 836 if exc != None: 837 raise exc 838 return self._retval 839 if msg_handled: 840 continue 841 elif isinstance(msg, ErrorResponse): 842 exc = msg.createException() 843 if not self.delay_raising_exception: 844 raise exc 845 elif isinstance(msg, NoticeResponse): 846 self._conn.handleNoticeResponse(msg) 847 elif isinstance(msg, ParameterStatus): 848 self._conn.handleParameterStatus(msg) 849 elif isinstance(msg, NotificationResponse): 850 self._conn.handleNotificationResponse(msg) 851 elif not self.ignore_unhandled_messages: 852 raise InternalError("Unexpected response msg %r" % (msg))
853
854 -def sync_on_error(fn):
855 def _fn(self, *args, **kwargs): 856 try: 857 return fn(self, *args, **kwargs) 858 except: 859 self._sync() 860 raise
861 return _fn 862
863 -class Connection(object):
864 - def __init__(self, unix_sock=None, host=None, port=5432, socket_timeout=60, ssl=False):
865 self._client_encoding = "ascii" 866 self._integer_datetimes = False 867 if unix_sock == None and host != None: 868 self._sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 869 elif unix_sock != None: 870 if not hasattr(socket, "AF_UNIX"): 871 raise InterfaceError("attempt to connect to unix socket on unsupported platform") 872 self._sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) 873 else: 874 raise ProgrammingError("one of host or unix_sock must be provided") 875 if unix_sock == None and host != None: 876 self._sock.connect((host, port)) 877 elif unix_sock != None: 878 self._sock.connect(unix_sock) 879 if ssl: 880 self._send(SSLRequest()) 881 resp = self._sock.recv(1) 882 if resp == 'S': 883 self._sock = SSLWrapper(socket.ssl(self._sock)) 884 else: 885 raise InterfaceError("server refuses SSL") 886 else: 887 # settimeout causes ssl failure, on windows. Python bug 1462352. 888 self._sock.settimeout(socket_timeout) 889 self._state = "noauth" 890 self._backend_key_data = None 891 self._sock_lock = threading.Lock() 892 893 self.NoticeReceived = MulticastDelegate() 894 self.ParameterStatusReceived = MulticastDelegate() 895 self.NotificationReceived = MulticastDelegate() 896 897 self.ParameterStatusReceived += self._onParameterStatusReceived
898
899 - def verifyState(self, state):
900 if self._state != state: 901 raise InternalError("connection state must be %s, is %s" % (state, self._state))
902
903 - def _send(self, msg):
904 #print "_send(%r)" % msg 905 data = msg.serialize() 906 self._sock.send(data)
907
908 - def _read_message(self):
909 bytes = "" 910 while len(bytes) < 5: 911 tmp = self._sock.recv(5 - len(bytes)) 912 bytes += tmp 913 if len(bytes) != 5: 914 raise InternalError("unable to read 5 bytes from socket %r" % bytes) 915 message_code = bytes[0] 916 data_len = struct.unpack("!i", bytes[1:])[0] - 4 917 if data_len == 0: 918 bytes = "" 919 else: 920 bytes = "" 921 while len(bytes) < data_len: 922 tmp = self._sock.recv(data_len - len(bytes)) 923 bytes += tmp 924 assert len(bytes) == data_len 925 msg = message_types[message_code].createFromData(bytes) 926 #print "_read_message() -> %r" % msg 927 return msg
928
929 - def authenticate(self, user, **kwargs):
930 self.verifyState("noauth") 931 self._sock_lock.acquire() 932 try: 933 self._send(StartupMessage(user, database=kwargs.get("database",None))) 934 msg = self._read_message() 935 if not isinstance(msg, AuthenticationRequest): 936 raise InternalError("StartupMessage was responded to with non-AuthenticationRequest msg %r" % msg) 937 if not msg.ok(self, user, **kwargs): 938 raise InterfaceError("authentication method %s failed" % msg.__class__.__name__) 939 940 self._state = "auth" 941 942 reader = MessageReader(self) 943 reader.add_message(ReadyForQuery, self._ready_for_query) 944 reader.add_message(BackendKeyData, self._receive_backend_key_data) 945 reader.handle_messages() 946 finally: 947 self._sock_lock.release()
948
949 - def _ready_for_query(self, msg):
950 self._state = "ready" 951 return True
952
953 - def _receive_backend_key_data(self, msg):
954 self._backend_key_data = msg
955 956 @sync_on_error
957 - def parse(self, statement, qs, param_types):
958 self.verifyState("ready") 959 self._sock_lock.acquire() 960 try: 961 type_info = [types.pg_type_info(x) for x in param_types] 962 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 963 self._send(Parse(statement, qs, param_types)) 964 self._send(DescribePreparedStatement(statement)) 965 self._send(Flush()) 966 967 reader = MessageReader(self) 968 969 # ParseComplete is good. 970 reader.add_message(ParseComplete, lambda msg: 0) 971 972 # Well, we don't really care -- we're going to send whatever we 973 # want and let the database deal with it. But thanks anyways! 974 reader.add_message(ParameterDescription, lambda msg: 0) 975 976 # We're not waiting for a row description. Return something 977 # destinctive to let bind know that there is no output. 978 reader.add_message(NoData, lambda msg: (None, param_fc)) 979 980 # Common row description response 981 reader.add_message(RowDescription, lambda msg: (msg, param_fc)) 982 983 return reader.handle_messages() 984 985 finally: 986 self._sock_lock.release()
987 988 @sync_on_error
989 - def bind(self, portal, statement, params, parse_data):
990 self.verifyState("ready") 991 self._sock_lock.acquire() 992 try: 993 row_desc, param_fc = parse_data 994 if row_desc == None: 995 # no data coming out 996 output_fc = () 997 else: 998 # We've got row_desc that allows us to identify what we're going to 999 # get back from this statement. 1000 output_fc = [types.py_type_info(f) for f in row_desc.fields] 1001 self._send(Bind(portal, statement, param_fc, params, output_fc, client_encoding = self._client_encoding, integer_datetimes = self._integer_datetimes)) 1002 # We need to describe the portal after bind, since the return 1003 # format codes will be different (hopefully, always what we 1004 # requested). 1005 self._send(DescribePortal(portal)) 1006 self._send(Flush()) 1007 1008 # Read responses from server... 1009 reader = MessageReader(self) 1010 1011 # BindComplete is good -- just ignore 1012 reader.add_message(BindComplete, lambda msg: 0) 1013 1014 # NoData in this case means we're not executing a query. As a 1015 # result, we won't be fetching rows, so we'll never execute the 1016 # portal we just created... unless we execute it right away, which 1017 # we'll do. 1018 reader.add_message(NoData, self._bind_nodata, portal, reader) 1019 1020 # Return the new row desc, since it will have the format types we 1021 # asked the server for 1022 reader.add_message(RowDescription, lambda msg: (msg, None)) 1023 1024 return reader.handle_messages() 1025 1026 finally: 1027 self._sock_lock.release()
1028
1029 - def _bind_nodata(self, msg, portal, old_reader):
1030 # Bind message returned NoData, causing us to execute the command. 1031 self._send(Execute(portal, 0)) 1032 self._send(Sync()) 1033 1034 output = {} 1035 reader = MessageReader(self) 1036 reader.add_message(CommandComplete, lambda msg, out: out.setdefault('msg', msg) and False, output) 1037 reader.add_message(ReadyForQuery, lambda msg: 1) 1038 reader.delay_raising_exception = True 1039 reader.handle_messages() 1040 1041 old_reader.return_value((None, output['msg']))
1042 1043 @sync_on_error
1044 - def fetch_rows(self, portal, row_count, row_desc):
1045 self.verifyState("ready") 1046 self._sock_lock.acquire() 1047 try: 1048 self._send(Execute(portal, row_count)) 1049 self._send(Flush()) 1050 rows = [] 1051 1052 reader = MessageReader(self) 1053 reader.add_message(DataRow, self._fetch_datarow, rows, row_desc) 1054 reader.add_message(PortalSuspended, lambda msg: 1) 1055 reader.add_message(CommandComplete, self._fetch_commandcomplete, portal) 1056 retval = reader.handle_messages() 1057 1058 # retval = 2 when command complete, indicating that we've hit the 1059 # end of the available data for this command 1060 return (retval == 2), rows 1061 finally: 1062 self._sock_lock.release()
1063
1064 - def _fetch_datarow(self, msg, rows, row_desc):
1065 rows.append( 1066 [ 1067 types.py_value( 1068 msg.fields[i], 1069 row_desc.fields[i], 1070 client_encoding=self._client_encoding, 1071 integer_datetimes=self._integer_datetimes 1072 ) 1073 for i in range(len(msg.fields)) 1074 ] 1075 )
1076
1077 - def _fetch_commandcomplete(self, msg, portal):
1078 self._send(ClosePortal(portal)) 1079 self._send(Sync()) 1080 1081 reader = MessageReader(self) 1082 reader.add_message(ReadyForQuery, self._fetch_commandcomplete_rfq) 1083 reader.add_message(CloseComplete, lambda msg: False) 1084 reader.handle_messages() 1085 1086 return 2 # signal end-of-data
1087
1088 - def _fetch_commandcomplete_rfq(self, msg):
1089 self._state = "ready" 1090 return True
1091 1092 # Send a Sync message, then read and discard all messages until we 1093 # receive a ReadyForQuery message.
1094 - def _sync(self):
1095 self._send(Sync()) 1096 reader = MessageReader(self) 1097 reader.ignore_unhandled_messages = True 1098 reader.add_message(ReadyForQuery, lambda msg: True) 1099 reader.handle_messages()
1100 1101 @sync_on_error
1102 - def close_statement(self, statement):
1103 if self._state == "closed": 1104 return 1105 self.verifyState("ready") 1106 self._sock_lock.acquire() 1107 try: 1108 self._send(ClosePreparedStatement(statement)) 1109 self._send(Sync()) 1110 1111 reader = MessageReader(self) 1112 reader.add_message(CloseComplete, lambda msg: 0) 1113 reader.add_message(ReadyForQuery, lambda msg: 1) 1114 reader.handle_messages() 1115 finally: 1116 self._sock_lock.release()
1117 1118 @sync_on_error
1119 - def close_portal(self, portal):
1120 if self._state == "closed": 1121 return 1122 self.verifyState("ready") 1123 self._sock_lock.acquire() 1124 try: 1125 self._send(ClosePortal(portal)) 1126 self._send(Sync()) 1127 1128 reader = MessageReader(self) 1129 reader.add_message(CloseComplete, lambda msg: 0) 1130 reader.add_message(ReadyForQuery, lambda msg: 1) 1131 reader.handle_messages() 1132 finally: 1133 self._sock_lock.release()
1134
1135 - def close(self):
1136 self._sock_lock.acquire() 1137 try: 1138 self._send(Terminate()) 1139 self._sock.close() 1140 self._state = "closed" 1141 finally: 1142 self._sock_lock.release()
1143
1144 - def _onParameterStatusReceived(self, msg):
1145 if msg.key == "client_encoding": 1146 self._client_encoding = msg.value 1147 elif msg.key == "integer_datetimes": 1148 self._integer_datetimes = (msg.value == "on")
1149
1150 - def handleNoticeResponse(self, msg):
1151 self.NoticeReceived(msg)
1152
1153 - def handleParameterStatus(self, msg):
1154 self.ParameterStatusReceived(msg)
1155
1156 - def handleNotificationResponse(self, msg):
1157 self.NotificationReceived(msg)
1158 1159 message_types = { 1160 "N": NoticeResponse, 1161 "R": AuthenticationRequest, 1162 "S": ParameterStatus, 1163 "K": BackendKeyData, 1164 "Z": ReadyForQuery, 1165 "T": RowDescription, 1166 "E": ErrorResponse, 1167 "D": DataRow, 1168 "C": CommandComplete, 1169 "1": ParseComplete, 1170 "2": BindComplete, 1171 "3": CloseComplete, 1172 "s": PortalSuspended, 1173 "n": NoData, 1174 "t": ParameterDescription, 1175 "A": NotificationResponse, 1176 } 1177