hawq-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From r...@apache.org
Subject [27/28] incubator-hawq git commit: HAWQ-837. Add python modules into HAWQ code
Date Tue, 21 Jun 2016 02:41:43 GMT
http://git-wip-us.apache.org/repos/asf/incubator-hawq/blob/80e25b46/tools/bin/ext/figleaf/internals.py
----------------------------------------------------------------------
diff --git a/tools/bin/ext/figleaf/internals.py b/tools/bin/ext/figleaf/internals.py
new file mode 100644
index 0000000..6a25c0e
--- /dev/null
+++ b/tools/bin/ext/figleaf/internals.py
@@ -0,0 +1,257 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+# 
+#   http://www.apache.org/licenses/LICENSE-2.0
+# 
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+"""
+Coverage tracking internals.
+"""
+
+import sys
+import threading
+
+err = sys.stderr
+
+import types, symbol
+
+# use builtin sets if in >= 2.4, otherwise use 'sets' module.
+try:
+    set()
+except NameError:
+    from sets import Set as set
+
+def get_interesting_lines(code):
+    """
+    Count 'interesting' lines of Python in a code object, where
+    'interesting' is defined as 'lines that could possibly be
+    executed'.
+
+    This is done by dissassembling the code objecte and returning
+    line numbers.
+    """
+
+    # clean up weird end-of-file issues
+
+    lines = set([ l for (o, l) in findlinestarts(code) ])
+    for const in code.co_consts:
+        if type(const) == types.CodeType:
+            lines.update(get_interesting_lines(const))
+
+    return lines
+
+def findlinestarts(code):
+    """Find the offsets in a byte code which are start of lines in the source.
+
+    Generate pairs (offset, lineno) as described in Python/compile.c.
+
+    CTB -- swiped from Python 2.5, module 'dis', so that earlier versions
+    of Python could use the function, too.
+    """
+    byte_increments = [ord(c) for c in code.co_lnotab[0::2]]
+    line_increments = [ord(c) for c in code.co_lnotab[1::2]]
+
+    lastlineno = None
+    lineno = code.co_firstlineno
+    addr = 0
+    for byte_incr, line_incr in zip(byte_increments, line_increments):
+        if byte_incr:
+            if lineno != lastlineno:
+                yield (addr, lineno)
+                lastlineno = lineno
+            addr += byte_incr
+        lineno += line_incr
+    if lineno != lastlineno:
+        yield (addr, lineno)
+
+class CodeTracer:
+    """
+    Basic mechanisms for code coverage tracking, using sys.settrace.  
+    """
+    def __init__(self, exclude_prefix, include_only_prefix):
+        self.common = self.c = set()
+        self.section_name = None
+        self.sections = {}
+        
+        self.started = False
+
+        assert not (exclude_prefix and include_only_prefix), \
+               "mutually exclusive"
+        
+        self.excl = exclude_prefix
+        self.incl = include_only_prefix
+
+    def start(self):
+        """
+        Start recording.
+        """
+        if not self.started:
+            self.started = True
+
+            if self.excl and not self.incl:
+                global_trace_fn = self.g1
+            elif self.incl and not self.excl:
+                global_trace_fn = self.g2
+            else:
+                global_trace_fn = self.g0
+
+            sys.settrace(global_trace_fn)
+
+            if hasattr(threading, 'settrace'):
+                threading.settrace(global_trace_fn)
+
+    def stop(self):
+        if self.started:
+            sys.settrace(None)
+            
+            if hasattr(threading, 'settrace'):
+                threading.settrace(None)
+
+            self.started = False
+            self.stop_section()
+
+    def g0(self, f, e, a):
+        """
+        global trace function, no exclude/include info.
+
+        f == frame, e == event, a == arg        .
+        """
+        if e == 'call':
+            return self.t
+
+    def g1(self, f, e, a):
+        """
+        global trace function like g0, but ignores files starting with
+        'self.excl'.
+        """
+        if e == 'call':
+            excl = self.excl
+            path = f.f_globals.get('__file__')
+            if path is None:
+                path = f.f_code.co_filename
+
+            if excl and path.startswith(excl):
+                return
+
+            return self.t
+
+    def g2(self, f, e, a):
+        """
+        global trace function like g0, but only records files starting with
+        'self.incl'.
+        """
+        if e == 'call':
+            incl = self.incl
+            if incl and f.f_code.co_filename.startswith(incl):
+                return self.t
+
+    def t(self, f, e, a):
+        """
+        local trace function.
+        """
+        if e is 'line':
+            self.c.add((f.f_code.co_filename, f.f_lineno))
+        return self.t
+
+    def clear(self):
+        """
+        wipe out coverage info
+        """
+
+        self.c = {}
+
+    def start_section(self, name):
+        self.stop_section()
+
+        self.section_name = name
+        self.c = self.sections.get(name, set())
+        
+    def stop_section(self):
+        if self.section_name:
+            self.sections[self.section_name] = self.c
+            self.section_name = None
+            self.c = self.common
+
+class CoverageData:
+    """
+    A class to manipulate and combine data from the CodeTracer object.
+
+    In general, do not pickle this object; it's simpler and more
+    straightforward to just pass the basic Python objects around
+    (e.g. CoverageData.common, a set, and CoverageData.sections, a
+    dictionary of sets).
+    """
+    def __init__(self, trace_obj=None):
+        self.common = set()
+        self.sections = {}
+        
+        if trace_obj:
+            self.update(trace_obj)
+            
+    def update(self, trace_obj):
+        # transfer common-block code coverage -- if no sections are set,
+        # this will be all of the code coverage info.
+        self.common.update(trace_obj.common)
+
+        # update our internal section dictionary with the (filename, line_no)
+        # pairs from the section coverage as well.
+        
+        for section_name, section_d in trace_obj.sections.items():
+            section_set = self.sections.get(section_name, set())
+            section_set.update(section_d)
+            self.sections[section_name] = section_set
+
+    def gather_files(self, name=None):
+        """
+        Return the dictionary of lines of executed code; the dict
+        keys are filenames and values are sets containing individual
+        (integer) line numbers.
+        
+        'name', if set, is the desired section name from which to gather
+        coverage info.
+        """
+        cov = set()
+        cov.update(self.common)
+
+        if name is None:
+            for section_name, coverage_set in self.sections.items():
+                cov.update(coverage_set)
+        else:
+            coverage_set = self.sections.get(name, set())
+            cov.update(coverage_set)
+            
+#        cov = list(cov)
+#        cov.sort()
+
+        files = {}
+        for (filename, line) in cov:    # @CTB could optimize
+            d = files.get(filename, set())
+            d.add(line)
+            files[filename] = d
+
+        return files
+
+    def gather_sections(self, file):
+        """
+        Return a dictionary of sets containing section coverage information for
+        a specific file.  Dict keys are sections, and the dict values are
+        sets containing (integer) line numbers.
+        """
+        sections = {}
+        for k, c in self.sections.items():
+            s = set()
+            for (filename, line) in c.keys():
+                if filename == file:
+                    s.add(line)
+            sections[k] = s
+        return sections

http://git-wip-us.apache.org/repos/asf/incubator-hawq/blob/80e25b46/tools/bin/ext/figleaf/nose_sections.py
----------------------------------------------------------------------
diff --git a/tools/bin/ext/figleaf/nose_sections.py b/tools/bin/ext/figleaf/nose_sections.py
new file mode 100644
index 0000000..09654a5
--- /dev/null
+++ b/tools/bin/ext/figleaf/nose_sections.py
@@ -0,0 +1,133 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+# 
+#   http://www.apache.org/licenses/LICENSE-2.0
+# 
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+"""
+figleafsections plugin for nose.
+
+Automatically records coverage info for Python tests and connects it with
+with test was being run at the time.  Can be used to produce a "barcode"
+of code execution.
+"""
+
+DEFAULT_COVERAGE_FILE='.figleaf_sections'
+import pkg_resources
+
+try:
+    pkg_resources.require('figleaf>=0.6.1')
+    import figleaf
+except ImportError:
+    figleaf = None
+
+import sys
+err = sys.stderr
+
+import nose.case
+from nose.plugins.base import Plugin
+
+import logging
+import os
+
+log = logging.getLogger(__name__)
+
+def calc_testname(test):
+    """
+    Build a reasonably human-readable testname from each test.
+    """
+    name = str(test)
+    if ' ' in name:
+        name = name.split(' ')[1]
+
+    return name
+
+class FigleafSections(Plugin):
+    def __init__(self):
+        self.name = 'figleafsections'
+        Plugin.__init__(self)
+        self.testname = None
+
+    def add_options(self, parser, env=os.environ):
+        env_opt = 'NOSE_WITH_%s' % self.name.upper()
+        env_opt.replace('-', '_')
+        parser.add_option("--with-%s" % self.name,
+                          action="store_true",
+                          dest=self.enableOpt,
+                          default=env.get(env_opt),
+                          help="Enable plugin %s: %s [%s]" %
+                          (self.__class__.__name__, self.help(), env_opt))
+
+        parser.add_option("--figleaf-file",
+                          action="store",
+                          dest="figleaf_file",
+                          default=None,
+                          help="Store figleaf section coverage in this file")
+        
+    def configure(self, options, config):
+        """
+        Configure: enable plugin?  And if so, where should the coverage
+        info be placed?
+        """
+        self.conf = config
+
+        # enable?
+        if hasattr(options, self.enableOpt):
+            self.enabled = getattr(options, self.enableOpt)
+
+        ### save coverage file name, if given.
+        if options.figleaf_file:
+            self.figleaf_file = options.figleaf_file
+        else:
+            self.figleaf_file = DEFAULT_COVERAGE_FILE
+
+        if self.enabled and figleaf is None:
+                raise Exception("You must install figleaf 0.6.1 before you can use the figleafsections plugin! See http://darcs.idyll.org/~t/projects/figleaf/doc/")
+
+    def begin(self):
+        """
+        Initialize: start recording coverage info.
+        """
+        figleaf.start()
+
+    def finalize(self, result):
+        """
+        Finalize: stop recording coverage info, save & exit.
+        """
+        figleaf.stop()
+        
+        fp = open(self.figleaf_file, 'w')
+        figleaf.dump_pickled_coverage(fp)
+        fp.close()
+
+    def startTest(self, test):
+        """
+        Run at the beginning of each test, before per-test fixtures.
+
+        One weakness is that this is only run for specific kinds of
+        nose testcases.
+        """
+        if isinstance(test, nose.case.Test):
+           
+            self.testname = calc_testname(test)
+            assert self.testname
+
+            figleaf.start_section(self.testname)
+
+    def stopTest(self, test):
+        """
+        Run at the end of each test, after per-test fixtures.
+        """
+        if self.testname:
+            figleaf.stop_section()
+            self.testname = None

http://git-wip-us.apache.org/repos/asf/incubator-hawq/blob/80e25b46/tools/bin/ext/pg8000/__init__.py
----------------------------------------------------------------------
diff --git a/tools/bin/ext/pg8000/__init__.py b/tools/bin/ext/pg8000/__init__.py
new file mode 100644
index 0000000..57de8e8
--- /dev/null
+++ b/tools/bin/ext/pg8000/__init__.py
@@ -0,0 +1,37 @@
+# vim: sw=4:expandtab:foldmethod=marker
+#
+# Copyright (c) 2007-2009, Mathieu Fenniak
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+# * Redistributions of source code must retain the above copyright notice,
+# this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions and the following disclaimer in the documentation
+# and/or other materials provided with the distribution.
+# * The name of the author may not be used to endorse or promote products
+# derived from this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+
+__author__ = "Mathieu Fenniak"
+
+import dbapi as DBAPI
+pg8000_dbapi = DBAPI
+
+from interface import *
+from types import Bytea
+

http://git-wip-us.apache.org/repos/asf/incubator-hawq/blob/80e25b46/tools/bin/ext/pg8000/dbapi.py
----------------------------------------------------------------------
diff --git a/tools/bin/ext/pg8000/dbapi.py b/tools/bin/ext/pg8000/dbapi.py
new file mode 100644
index 0000000..3f90188
--- /dev/null
+++ b/tools/bin/ext/pg8000/dbapi.py
@@ -0,0 +1,621 @@
+# vim: sw=4:expandtab:foldmethod=marker
+#
+# Copyright (c) 2007-2009, Mathieu Fenniak
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+# * Redistributions of source code must retain the above copyright notice,
+# this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions and the following disclaimer in the documentation
+# and/or other materials provided with the distribution.
+# * The name of the author may not be used to endorse or promote products
+# derived from this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+
+__author__ = "Mathieu Fenniak"
+
+import datetime
+import time
+import interface
+import types
+import threading
+from errors import *
+
+from warnings import warn
+
+##
+# The DBAPI level supported.  Currently 2.0.  This property is part of the
+# DBAPI 2.0 specification.
+apilevel = "2.0"
+
+##
+# Integer constant stating the level of thread safety the DBAPI interface
+# supports.  This DBAPI interface supports sharing of the module, connections,
+# and cursors.  This property is part of the DBAPI 2.0 specification.
+threadsafety = 3
+
+##
+# String property stating the type of parameter marker formatting expected by
+# the interface.  This value defaults to "format".  This property is part of
+# the DBAPI 2.0 specification.
+# <p>
+# Unlike the DBAPI specification, this value is not constant.  It can be
+# changed to any standard paramstyle value (ie. qmark, numeric, named, format,
+# and pyformat).
+paramstyle = 'format' # paramstyle can be changed to any DB-API paramstyle
+
+def convert_paramstyle(src_style, query, args):
+    # I don't see any way to avoid scanning the query string char by char,
+    # so we might as well take that careful approach and create a
+    # state-based scanner.  We'll use int variables for the state.
+    #  0 -- outside quoted string
+    #  1 -- inside single-quote string '...'
+    #  2 -- inside quoted identifier   "..."
+    #  3 -- inside escaped single-quote string, E'...'
+    
+    if args is None:
+        return  query, args
+    
+    state = 0
+    output_query = ""
+    output_args = []
+    if src_style == "numeric":
+        output_args = args
+    elif src_style in ("pyformat", "named"):
+        mapping_to_idx = {}
+    i = 0
+    while 1:
+        if i == len(query):
+            break
+        c = query[i]
+        # print "begin loop", repr(i), repr(c), repr(state)
+        if state == 0:
+            if c == "'":
+                i += 1
+                output_query += c
+                state = 1
+            elif c == '"':
+                i += 1
+                output_query += c
+                state = 2
+            elif c == 'E':
+                # check for escaped single-quote string
+                i += 1
+                if i < len(query) and i > 1 and query[i] == "'":
+                    i += 1
+                    output_query += "E'"
+                    state = 3
+                else:
+                    output_query += c
+            elif src_style == "qmark" and c == "?":
+                i += 1
+                param_idx = len(output_args)
+                if param_idx == len(args):
+                    raise QueryParameterIndexError("too many parameter fields, not enough parameters")
+                output_args.append(args[param_idx])
+                output_query += "$" + str(param_idx + 1)
+            elif src_style == "numeric" and c == ":":
+                i += 1
+                if i < len(query) and i > 1 and query[i].isdigit():
+                    output_query += "$" + query[i]
+                    i += 1
+                else:
+                    raise QueryParameterParseError("numeric parameter : does not have numeric arg")
+            elif src_style == "named" and c == ":":
+                name = ""
+                while 1:
+                    i += 1
+                    if i == len(query):
+                        break
+                    c = query[i]
+                    if c.isalnum() or c == '_':
+                        name += c
+                    else:
+                        break
+                if name == "":
+                    raise QueryParameterParseError("empty name of named parameter")
+                idx = mapping_to_idx.get(name)
+                if idx == None:
+                    idx = len(output_args)
+                    output_args.append(args[name])
+                    idx += 1
+                    mapping_to_idx[name] = idx
+                output_query += "$" + str(idx)
+            elif src_style == "format" and c == "%":
+                i += 1
+                if i < len(query) and i > 1:
+                    if query[i] == "s":
+                        param_idx = len(output_args)
+                        if param_idx == len(args):
+                            raise QueryParameterIndexError("too many parameter fields, not enough parameters")
+                        output_args.append(args[param_idx])
+                        output_query += "$" + str(param_idx + 1)
+                    elif query[i] == "%":
+                        output_query += "%"
+                    else:
+                        raise QueryParameterParseError("Only %s and %% are supported")
+                    i += 1
+                else:
+                    raise QueryParameterParseError("format parameter % does not have format code")
+            elif src_style == "pyformat" and c == "%":
+                i += 1
+                if i < len(query) and i > 1:
+                    if query[i] == "(":
+                        i += 1
+                        # begin mapping name
+                        end_idx = query.find(')', i)
+                        if end_idx == -1:
+                            raise QueryParameterParseError("began pyformat dict read, but couldn't find end of name")
+                        else:
+                            name = query[i:end_idx]
+                            i = end_idx + 1
+                            if i < len(query) and query[i] == "s":
+                                i += 1
+                                idx = mapping_to_idx.get(name)
+                                if idx == None:
+                                    idx = len(output_args)
+                                    output_args.append(args[name])
+                                    idx += 1
+                                    mapping_to_idx[name] = idx
+                                output_query += "$" + str(idx)
+                            else:
+                                raise QueryParameterParseError("format not specified or not supported (only %(...)s supported)")
+                    elif query[i] == "%":
+                        output_query += "%"
+                    elif query[i] == "s":
+                        # we have a %s in a pyformat query string.  Assume
+                        # support for format instead.
+                        i -= 1
+                        src_style = "format"
+                    else:
+                        raise QueryParameterParseError("Only %(name)s, %s and %% are supported")
+            else:
+                i += 1
+                output_query += c
+        elif state == 1:
+            output_query += c
+            i += 1
+            if c == "'":
+                # Could be a double ''
+                if i < len(query) and query[i] == "'":
+                    # is a double quote.
+                    output_query += query[i]
+                    i += 1
+                else:
+                    state = 0
+            elif src_style in ("pyformat","format") and c == "%":
+                # hm... we're only going to support an escaped percent sign
+                if i < len(query):
+                    if query[i] == "%":
+                        # good.  We already output the first percent sign.
+                        i += 1
+                    else:
+                        raise QueryParameterParseError("'%" + query[i] + "' not supported in quoted string")
+        elif state == 2:
+            output_query += c
+            i += 1
+            if c == '"':
+                state = 0
+            elif src_style in ("pyformat","format") and c == "%":
+                # hm... we're only going to support an escaped percent sign
+                if i < len(query):
+                    if query[i] == "%":
+                        # good.  We already output the first percent sign.
+                        i += 1
+                    else:
+                        raise QueryParameterParseError("'%" + query[i] + "' not supported in quoted string")
+        elif state == 3:
+            output_query += c
+            i += 1
+            if c == "\\":
+                # check for escaped single-quote
+                if i < len(query) and query[i] == "'":
+                    output_query += "'"
+                    i += 1
+            elif c == "'":
+                state = 0
+            elif src_style in ("pyformat","format") and c == "%":
+                # hm... we're only going to support an escaped percent sign
+                if i < len(query):
+                    if query[i] == "%":
+                        # good.  We already output the first percent sign.
+                        i += 1
+                    else:
+                        raise QueryParameterParseError("'%" + query[i] + "' not supported in quoted string")
+
+    return output_query, tuple(output_args)
+
+def require_open_cursor(fn):
+    def _fn(self, *args, **kwargs):
+        if self.cursor == None:
+            raise CursorClosedError()
+        return fn(self, *args, **kwargs)
+    return _fn
+
+##
+# The class of object returned by the {@link #ConnectionWrapper.cursor cursor method}.
+class CursorWrapper(object):
+    def __init__(self, conn, connection):
+        self.cursor = interface.Cursor(conn)
+        self.arraysize = 1
+        self._connection = connection
+        self._override_rowcount = None
+
+    ##
+    # This read-only attribute returns a reference to the connection object on
+    # which the cursor was created.
+    # <p>
+    # Stability: Part of a DBAPI 2.0 extension.  A warning "DB-API extension
+    # cursor.connection used" will be fired.
+    connection = property(lambda self: self._getConnection())
+
+    def _getConnection(self):
+        warn("DB-API extension cursor.connection used", stacklevel=3)
+        return self._connection
+
+    ##
+    # This read-only attribute specifies the number of rows that the last
+    # .execute*() produced (for DQL statements like 'select') or affected (for
+    # DML statements like 'update' or 'insert').
+    # <p>
+    # The attribute is -1 in case no .execute*() has been performed on the
+    # cursor or the rowcount of the last operation is cannot be determined by
+    # the interface.
+    # <p>
+    # Stability: Part of the DBAPI 2.0 specification.
+    rowcount = property(lambda self: self._getRowCount())
+
+    @require_open_cursor
+    def _getRowCount(self):
+        if self._override_rowcount != None:
+            return self._override_rowcount
+        return self.cursor.row_count
+
+    ##
+    # This read-only attribute is a sequence of 7-item sequences.  Each value
+    # contains information describing one result column.  The 7 items returned
+    # for each column are (name, type_code, display_size, internal_size,
+    # precision, scale, null_ok).  Only the first two values are provided by
+    # this interface implementation.
+    # <p>
+    # Stability: Part of the DBAPI 2.0 specification.
+    description = property(lambda self: self._getDescription())
+
+    @require_open_cursor
+    def _getDescription(self):
+        if self.cursor.row_description == None:
+            return None
+        columns = []
+        for col in self.cursor.row_description:
+            columns.append((col["name"], col["type_oid"], None, None, None, None, None))
+        return columns
+
+    ##
+    # Executes a database operation.  Parameters may be provided as a sequence
+    # or mapping and will be bound to variables in the operation.
+    # <p>
+    # Stability: Part of the DBAPI 2.0 specification.
+    @require_open_cursor
+    def execute(self, operation, args=()):
+        self._override_rowcount = None
+        self._execute(operation, args)
+
+    def _execute(self, operation, args=()):
+        new_query, new_args = convert_paramstyle(paramstyle, operation, args)
+        try:
+            self.cursor.execute(new_query, *new_args)
+        except ConnectionClosedError:
+            # can't rollback in this case
+            raise
+        except:
+            # any error will rollback the transaction to-date
+            self._connection.rollback()
+            raise
+
+    def copy_from(self, fileobj, table=None, sep='\t', null=None, query=None):
+        if query == None:
+            if table == None:
+                raise CopyQueryOrTableRequiredError()
+            query = "COPY %s FROM stdout DELIMITER '%s'" % (table, sep)
+            if null is not None:
+                query += " NULL '%s'" % (null,)
+        self.copy_execute(fileobj, query)
+
+    def copy_to(self, fileobj, table=None, sep='\t', null=None, query=None):
+        if query == None:
+            if table == None:
+                raise CopyQueryOrTableRequiredError()
+            query = "COPY %s TO stdout DELIMITER '%s'" % (table, sep)
+            if null is not None:
+                query += " NULL '%s'" % (null,)
+        self.copy_execute(fileobj, query)
+    
+    @require_open_cursor
+    def copy_execute(self, fileobj, query):
+        try:
+            self.cursor.execute(query, stream=fileobj)
+        except ConnectionClosedError:
+            # can't rollback in this case
+            raise
+        except:
+            # any error will rollback the transaction to-date
+            import traceback; traceback.print_exc()
+            self._connection.rollback()
+            raise
+
+    ##
+    # Prepare a database operation and then execute it against all parameter
+    # sequences or mappings provided.
+    # <p>
+    # Stability: Part of the DBAPI 2.0 specification.
+    @require_open_cursor
+    def executemany(self, operation, parameter_sets):
+        self._override_rowcount = 0
+        for parameters in parameter_sets:
+            self._execute(operation, parameters)
+            if self.cursor.row_count == -1 or self._override_rowcount == -1:
+                self._override_rowcount = -1
+            else:
+                self._override_rowcount += self.cursor.row_count
+
+    ##
+    # Fetch the next row of a query result set, returning a single sequence, or
+    # None when no more data is available.
+    # <p>
+    # Stability: Part of the DBAPI 2.0 specification.
+    @require_open_cursor
+    def fetchone(self):
+        return self.cursor.read_tuple()
+
+    ##
+    # Fetch the next set of rows of a query result, returning a sequence of
+    # sequences.  An empty sequence is returned when no more rows are
+    # available.
+    # <p>
+    # Stability: Part of the DBAPI 2.0 specification.
+    # @param size   The number of rows to fetch when called.  If not provided,
+    #               the arraysize property value is used instead.
+    def fetchmany(self, size=None):
+        if size == None:
+            size = self.arraysize
+        rows = []
+        for i in range(size):
+            value = self.fetchone()
+            if value == None:
+                break
+            rows.append(value)
+        return rows
+
+    ##
+    # Fetch all remaining rows of a query result, returning them as a sequence
+    # of sequences.
+    # <p>
+    # Stability: Part of the DBAPI 2.0 specification.
+    @require_open_cursor
+    def fetchall(self):
+        return tuple(self.cursor.iterate_tuple())
+
+    ##
+    # Close the cursor.
+    # <p>
+    # Stability: Part of the DBAPI 2.0 specification.
+    @require_open_cursor
+    def close(self):
+        self.cursor.close()
+        self.cursor = None
+        self._override_rowcount = None
+
+    def next(self):
+        warn("DB-API extension cursor.next() used", stacklevel=2)
+        retval = self.fetchone()
+        if retval == None:
+            raise StopIteration()
+        return retval
+
+    def __iter__(self):
+        warn("DB-API extension cursor.__iter__() used", stacklevel=2)
+        return self
+
+    def setinputsizes(self, sizes):
+        pass
+
+    def setoutputsize(self, size, column=None):
+        pass
+
+    @require_open_cursor
+    def fileno(self):
+        return self.cursor.fileno()
+    
+    @require_open_cursor
+    def isready(self):
+        return self.cursor.isready()
+
+def require_open_connection(fn):
+    def _fn(self, *args, **kwargs):
+        if self.conn == None:
+            raise ConnectionClosedError()
+        return fn(self, *args, **kwargs)
+    return _fn
+
+##
+# The class of object returned by the {@link #connect connect method}.
+class ConnectionWrapper(object):
+    # DBAPI Extension: supply exceptions as attributes on the connection
+    Warning = property(lambda self: self._getError(Warning))
+    Error = property(lambda self: self._getError(Error))
+    InterfaceError = property(lambda self: self._getError(InterfaceError))
+    DatabaseError = property(lambda self: self._getError(DatabaseError))
+    OperationalError = property(lambda self: self._getError(OperationalError))
+    IntegrityError = property(lambda self: self._getError(IntegrityError))
+    InternalError = property(lambda self: self._getError(InternalError))
+    ProgrammingError = property(lambda self: self._getError(ProgrammingError))
+    NotSupportedError = property(lambda self: self._getError(NotSupportedError))
+
+    def _getError(self, error):
+        warn("DB-API extension connection.%s used" % error.__name__, stacklevel=3)
+        return error
+
+    def __init__(self, **kwargs):
+        self.conn = interface.Connection(**kwargs)
+        self.notifies = []
+        self.notifies_lock = threading.Lock()
+        self.conn.NotificationReceived += self._notificationReceived
+        self.conn.begin()
+
+    def _notificationReceived(self, notice):
+        try:
+        # psycopg2 compatible notification interface
+            self.notifies_lock.acquire()
+            self.notifies.append((notice.backend_pid, notice.condition))
+        finally:
+            self.notifies_lock.release()
+
+    ##
+    # Creates a {@link #CursorWrapper CursorWrapper} object bound to this
+    # connection.
+    # <p>
+    # Stability: Part of the DBAPI 2.0 specification.
+    @require_open_connection
+    def cursor(self):
+        return CursorWrapper(self.conn, self)
+
+    ##
+    # Commits the current database transaction.
+    # <p>
+    # Stability: Part of the DBAPI 2.0 specification.
+    @require_open_connection
+    def commit(self):
+        # There's a threading bug here.  If a query is sent after the
+        # commit, but before the begin, it will be executed immediately
+        # without a surrounding transaction.  Like all threading bugs -- it
+        # sounds unlikely, until it happens every time in one
+        # application...  however, to fix this, we need to lock the
+        # database connection entirely, so that no cursors can execute
+        # statements on other threads.  Support for that type of lock will
+        # be done later.
+        self.conn.commit()
+        self.conn.begin()
+
+    ##
+    # Rolls back the current database transaction.
+    # <p>
+    # Stability: Part of the DBAPI 2.0 specification.
+    @require_open_connection
+    def rollback(self):
+        # see bug description in commit.
+        self.conn.rollback()
+        self.conn.begin()
+
+    ##
+    # Closes the database connection.
+    # <p>
+    # Stability: Part of the DBAPI 2.0 specification.
+    @require_open_connection
+    def close(self):
+        self.conn.close()
+        self.conn = None
+
+    @require_open_connection
+    def recache_record_types(self):
+        self.conn.recache_record_types()
+
+
+##
+# Creates a DBAPI 2.0 compatible interface to a PostgreSQL database.
+# <p>
+# Stability: Part of the DBAPI 2.0 specification.
+#
+# @param user   The username to connect to the PostgreSQL server with.  This
+# parameter is required.
+#
+# @keyparam host   The hostname of the PostgreSQL server to connect with.
+# Providing this parameter is necessary for TCP/IP connections.  One of either
+# host, or unix_sock, must be provided.
+#
+# @keyparam unix_sock   The path to the UNIX socket to access the database
+# through, for example, '/tmp/.s.PGSQL.5432'.  One of either unix_sock or host
+# must be provided.  The port parameter will have no affect if unix_sock is
+# provided.
+#
+# @keyparam port   The TCP/IP port of the PostgreSQL server instance.  This
+# parameter defaults to 5432, the registered and common port of PostgreSQL
+# TCP/IP servers.
+#
+# @keyparam database   The name of the database instance to connect with.  This
+# parameter is optional, if omitted the PostgreSQL server will assume the
+# database name is the same as the username.
+#
+# @keyparam password   The user password to connect to the server with.  This
+# parameter is optional.  If omitted, and the database server requests password
+# based authentication, the connection will fail.  On the other hand, if this
+# parameter is provided and the database does not request password
+# authentication, then the password will not be used.
+#
+# @keyparam socket_timeout  Socket connect timeout measured in seconds.
+# Defaults to 60 seconds.
+#
+# @keyparam ssl     Use SSL encryption for TCP/IP socket.  Defaults to False.
+#
+# @return An instance of {@link #ConnectionWrapper ConnectionWrapper}.
+def connect(user, host=None, unix_sock=None, port=5432, database=None, password=None, socket_timeout=60, ssl=False, options=None):
+    return ConnectionWrapper(user=user, host=host,
+            unix_sock=unix_sock, port=port, database=database,
+            password=password, socket_timeout=socket_timeout, ssl=ssl, options=options)
+
+def Date(year, month, day):
+    return datetime.date(year, month, day)
+
+def Time(hour, minute, second):
+    return datetime.time(hour, minute, second)
+
+def Timestamp(year, month, day, hour, minute, second):
+    return datetime.datetime(year, month, day, hour, minute, second)
+
+def DateFromTicks(ticks):
+    return Date(*time.localtime(ticks)[:3])
+
+def TimeFromTicks(ticks):
+    return Time(*time.localtime(ticks)[3:6])
+
+def TimestampFromTicks(ticks):
+    return Timestamp(*time.localtime(ticks)[:6])
+
+##
+# Construct an object holding binary data.
+def Binary(value):
+    return types.Bytea(value)
+
+# I have no idea what this would be used for by a client app.  Should it be
+# TEXT, VARCHAR, CHAR?  It will only compare against row_description's
+# type_code if it is this one type.  It is the varchar type oid for now, this
+# appears to match expectations in the DB API 2.0 compliance test suite.
+STRING = 1043
+
+# bytea type_oid
+BINARY = 17
+
+# numeric type_oid
+NUMBER = 1700
+
+# timestamp type_oid
+DATETIME = 1114
+
+# oid type_oid
+ROWID = 26
+
+

http://git-wip-us.apache.org/repos/asf/incubator-hawq/blob/80e25b46/tools/bin/ext/pg8000/errors.py
----------------------------------------------------------------------
diff --git a/tools/bin/ext/pg8000/errors.py b/tools/bin/ext/pg8000/errors.py
new file mode 100644
index 0000000..b8b5acf
--- /dev/null
+++ b/tools/bin/ext/pg8000/errors.py
@@ -0,0 +1,115 @@
+# vim: sw=4:expandtab:foldmethod=marker
+#
+# Copyright (c) 2007-2009, Mathieu Fenniak
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+# * Redistributions of source code must retain the above copyright notice,
+# this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions and the following disclaimer in the documentation
+# and/or other materials provided with the distribution.
+# * The name of the author may not be used to endorse or promote products
+# derived from this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+
+__author__ = "Mathieu Fenniak"
+
+class Warning(StandardError):
+    pass
+
+class Error(StandardError):
+    pass
+
+class InterfaceError(Error):
+    pass
+
+class ConnectionClosedError(InterfaceError):
+    def __init__(self):
+        InterfaceError.__init__(self, "connection is closed")
+
+class CursorClosedError(InterfaceError):
+    def __init__(self):
+        InterfaceError.__init__(self, "cursor is closed")
+
+class DatabaseError(Error):
+    pass
+
+class DataError(DatabaseError):
+    pass
+
+class OperationalError(DatabaseError):
+    pass
+
+class IntegrityError(DatabaseError):
+    pass
+
+class InternalError(DatabaseError):
+    pass
+
+class ProgrammingError(DatabaseError):
+    pass
+
+class NotSupportedError(DatabaseError):
+    pass
+
+##
+# An exception that is thrown when an internal error occurs trying to
+# decode binary array data from the server.
+class ArrayDataParseError(InternalError):
+    pass
+
+##
+# Thrown when attempting to transmit an array of unsupported data types.
+class ArrayContentNotSupportedError(NotSupportedError):
+    pass
+
+##
+# Thrown when attempting to send an array that doesn't contain all the same
+# type of objects (eg. some floats, some ints).
+class ArrayContentNotHomogenousError(ProgrammingError):
+    pass
+
+##
+# Attempted to pass an empty array in, but it's not possible to determine the
+# data type for an empty array.
+class ArrayContentEmptyError(ProgrammingError):
+    pass
+
+##
+# Attempted to use a multidimensional array with inconsistent array sizes.
+class ArrayDimensionsNotConsistentError(ProgrammingError):
+    pass
+
+# A cursor's copy_to or copy_from argument was not provided a table or query
+# to operate on.
+class CopyQueryOrTableRequiredError(ProgrammingError):
+    pass
+
+# Raised if a COPY query is executed without using copy_to or copy_from
+# functions to provide a data stream.
+class CopyQueryWithoutStreamError(ProgrammingError):
+    pass
+
+# When query parameters don't match up with query args.
+class QueryParameterIndexError(ProgrammingError):
+    pass
+
+# Some sort of parse error occured during query parameterization.
+class QueryParameterParseError(ProgrammingError):
+    pass
+

http://git-wip-us.apache.org/repos/asf/incubator-hawq/blob/80e25b46/tools/bin/ext/pg8000/interface.py
----------------------------------------------------------------------
diff --git a/tools/bin/ext/pg8000/interface.py b/tools/bin/ext/pg8000/interface.py
new file mode 100644
index 0000000..d2f70fa
--- /dev/null
+++ b/tools/bin/ext/pg8000/interface.py
@@ -0,0 +1,542 @@
+# vim: sw=4:expandtab:foldmethod=marker
+#
+# Copyright (c) 2007-2009, Mathieu Fenniak
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+# * Redistributions of source code must retain the above copyright notice,
+# this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions and the following disclaimer in the documentation
+# and/or other materials provided with the distribution.
+# * The name of the author may not be used to endorse or promote products
+# derived from this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+
+__author__ = "Mathieu Fenniak"
+
+import socket
+import protocol
+import threading
+from errors import *
+
+class DataIterator(object):
+    def __init__(self, obj, func):
+        self.obj = obj
+        self.func = func
+
+    def __iter__(self):
+        return self
+
+    def next(self):
+        retval = self.func(self.obj)
+        if retval == None:
+            raise StopIteration()
+        return retval
+
+statement_number_lock = threading.Lock()
+statement_number = 0
+
+##
+# This class represents a prepared statement.  A prepared statement is
+# pre-parsed on the server, which reduces the need to parse the query every
+# time it is run.  The statement can have parameters in the form of $1, $2, $3,
+# etc.  When parameters are used, the types of the parameters need to be
+# specified when creating the prepared statement.
+# <p>
+# As of v1.01, instances of this class are thread-safe.  This means that a
+# single PreparedStatement can be accessed by multiple threads without the
+# internal consistency of the statement being altered.  However, the
+# responsibility is on the client application to ensure that one thread reading
+# from a statement isn't affected by another thread starting a new query with
+# the same statement.
+# <p>
+# Stability: Added in v1.00, stability guaranteed for v1.xx.
+#
+# @param connection     An instance of {@link Connection Connection}.
+#
+# @param statement      The SQL statement to be represented, often containing
+# parameters in the form of $1, $2, $3, etc.
+#
+# @param types          Python type objects for each parameter in the SQL
+# statement.  For example, int, float, str.
+class PreparedStatement(object):
+
+    ##
+    # Determines the number of rows to read from the database server at once.
+    # Reading more rows increases performance at the cost of memory.  The
+    # default value is 100 rows.  The affect of this parameter is transparent.
+    # That is, the library reads more rows when the cache is empty
+    # automatically.
+    # <p>
+    # Stability: Added in v1.00, stability guaranteed for v1.xx.  It is
+    # possible that implementation changes in the future could cause this
+    # parameter to be ignored.
+    row_cache_size = 100
+
+    def __init__(self, connection, statement, *types, **kwargs):
+        global statement_number
+        if connection == None or connection.c == None:
+            raise InterfaceError("connection not provided")
+        try:
+            statement_number_lock.acquire()
+            self._statement_number = statement_number
+            statement_number += 1
+        finally:
+            statement_number_lock.release()
+        self.c = connection.c
+        self._portal_name = None
+        self._statement_name = kwargs.get("statement_name", "pg8000_statement_%s" % self._statement_number)
+        self._row_desc = None
+        self._cached_rows = []
+        self._ongoing_row_count = 0
+        self._command_complete = True
+        self._parse_row_desc = self.c.parse(self._statement_name, statement, types)
+        self._lock = threading.RLock()
+
+    def close(self):
+        if self._statement_name != "": # don't close unnamed statement
+            self.c.close_statement(self._statement_name)
+        if self._portal_name != None:
+            self.c.close_portal(self._portal_name)
+            self._portal_name = None
+
+    row_description = property(lambda self: self._getRowDescription())
+    def _getRowDescription(self):
+        if self._row_desc == None:
+            return None
+        return self._row_desc.fields
+
+    ##
+    # Run the SQL prepared statement with the given parameters.
+    # <p>
+    # Stability: Added in v1.00, stability guaranteed for v1.xx.
+    def execute(self, *args, **kwargs):
+        self._lock.acquire()
+        try:
+            if not self._command_complete:
+                # cleanup last execute
+                self._cached_rows = []
+                self._ongoing_row_count = 0
+            if self._portal_name != None:
+                self.c.close_portal(self._portal_name)
+            self._command_complete = False
+            self._portal_name = "pg8000_portal_%s" % self._statement_number
+            self._row_desc, cmd = self.c.bind(self._portal_name, self._statement_name, args, self._parse_row_desc, kwargs.get("stream"))
+            if self._row_desc:
+                # We execute our cursor right away to fill up our cache.  This
+                # prevents the cursor from being destroyed, apparently, by a rogue
+                # Sync between Bind and Execute.  Since it is quite likely that
+                # data will be read from us right away anyways, this seems a safe
+                # move for now.
+                self._fill_cache()
+            else:
+                self._command_complete = True
+                self._ongoing_row_count = -1
+                if cmd != None and cmd.rows != None:
+                    self._ongoing_row_count = cmd.rows
+        finally:
+            self._lock.release()
+
+    def _fill_cache(self):
+        self._lock.acquire()
+        try:
+            if self._cached_rows:
+                raise InternalError("attempt to fill cache that isn't empty")
+            end_of_data, rows = self.c.fetch_rows(self._portal_name, self.row_cache_size, self._row_desc)
+            self._cached_rows = rows
+            if end_of_data:
+                self._command_complete = True
+        finally:
+            self._lock.release()
+
+    def _fetch(self):
+        if not self._row_desc:
+            raise ProgrammingError("no result set")
+        self._lock.acquire()
+        try:
+            if not self._cached_rows:
+                if self._command_complete:
+                    return None
+                self._fill_cache()
+                if self._command_complete and not self._cached_rows:
+                    # fill cache tells us the command is complete, but yet we have
+                    # no rows after filling our cache.  This is a special case when
+                    # a query returns no rows.
+                    return None
+            row = self._cached_rows.pop(0)
+            self._ongoing_row_count += 1
+            return tuple(row)
+        finally:
+            self._lock.release()
+
+    ##
+    # Return a count of the number of rows relevant to the executed statement.
+    # For a SELECT, this is the number of rows returned.  For UPDATE or DELETE,
+    # this the number of rows affected.  For INSERT, the number of rows
+    # inserted.  This property may have a value of -1 to indicate that there
+    # was no row count.
+    # <p>
+    # During a result-set query (eg. SELECT, or INSERT ... RETURNING ...),
+    # accessing this property requires reading the entire result-set into
+    # memory, as reading the data to completion is the only way to determine
+    # the total number of rows.  Avoid using this property in with
+    # result-set queries, as it may cause unexpected memory usage.
+    # <p>
+    # Stability: Added in v1.03, stability guaranteed for v1.xx.
+    row_count = property(lambda self: self._get_row_count())
+    def _get_row_count(self):
+        self._lock.acquire()
+        try:
+            if not self._command_complete:
+                end_of_data, rows = self.c.fetch_rows(self._portal_name, 0, self._row_desc)
+                self._cached_rows += rows
+                if end_of_data:
+                    self._command_complete = True
+                else:
+                    raise InternalError("fetch_rows(0) did not hit end of data")
+            return self._ongoing_row_count + len(self._cached_rows)
+        finally:
+            self._lock.release()
+
+    ##
+    # Read a row from the database server, and return it in a dictionary
+    # indexed by column name/alias.  This method will raise an error if two
+    # columns have the same name.  Returns None after the last row.
+    # <p>
+    # Stability: Added in v1.00, stability guaranteed for v1.xx.
+    def read_dict(self):
+        row = self._fetch()
+        if row == None:
+            return row
+        retval = {}
+        for i in range(len(self._row_desc.fields)):
+            col_name = self._row_desc.fields[i]['name']
+            if retval.has_key(col_name):
+                raise InterfaceError("cannot return dict of row when two columns have the same name (%r)" % (col_name,))
+            retval[col_name] = row[i]
+        return retval
+
+    ##
+    # Read a row from the database server, and return it as a tuple of values.
+    # Returns None after the last row.
+    # <p>
+    # Stability: Added in v1.00, stability guaranteed for v1.xx.
+    def read_tuple(self):
+        return self._fetch()
+
+    ##
+    # Return an iterator for the output of this statement.  The iterator will
+    # return a tuple for each row, in the same manner as {@link
+    # #PreparedStatement.read_tuple read_tuple}.
+    # <p>
+    # Stability: Added in v1.00, stability guaranteed for v1.xx.
+    def iterate_tuple(self):
+        return DataIterator(self, PreparedStatement.read_tuple)
+
+    ##
+    # Return an iterator for the output of this statement.  The iterator will
+    # return a dict for each row, in the same manner as {@link
+    # #PreparedStatement.read_dict read_dict}.
+    # <p>
+    # Stability: Added in v1.00, stability guaranteed for v1.xx.
+    def iterate_dict(self):
+        return DataIterator(self, PreparedStatement.read_dict)
+
+##
+# The Cursor class allows multiple queries to be performed concurrently with a
+# single PostgreSQL connection.  The Cursor object is implemented internally by
+# using a {@link PreparedStatement PreparedStatement} object, so if you plan to
+# use a statement multiple times, you might as well create a PreparedStatement
+# and save a small amount of reparsing time.
+# <p>
+# As of v1.01, instances of this class are thread-safe.  See {@link
+# PreparedStatement PreparedStatement} for more information.
+# <p>
+# Stability: Added in v1.00, stability guaranteed for v1.xx.
+#
+# @param connection     An instance of {@link Connection Connection}.
+class Cursor(object):
+    def __init__(self, connection):
+        self.connection = connection
+        self._stmt = None
+
+    def require_stmt(func):
+        def retval(self, *args, **kwargs):
+            if self._stmt == None:
+                raise ProgrammingError("attempting to use unexecuted cursor")
+            return func(self, *args, **kwargs)
+        return retval
+
+    row_description = property(lambda self: self._getRowDescription())
+    def _getRowDescription(self):
+        if self._stmt == None:
+            return None
+        return self._stmt.row_description
+
+    ##
+    # Run an SQL statement using this cursor.  The SQL statement can have
+    # parameters in the form of $1, $2, $3, etc., which will be filled in by
+    # the additional arguments passed to this function.
+    # <p>
+    # Stability: Added in v1.00, stability guaranteed for v1.xx.
+    # @param query      The SQL statement to execute.
+    def execute(self, query, *args, **kwargs):
+        if self.connection.is_closed:
+            raise ConnectionClosedError()
+        self.connection._unnamed_prepared_statement_lock.acquire()
+        try:
+            self._stmt = PreparedStatement(self.connection, query, statement_name="", *[{"type": type(x), "value": x} for x in args])
+            self._stmt.execute(*args, **kwargs)
+        finally:
+            self.connection._unnamed_prepared_statement_lock.release()
+
+    ##
+    # Return a count of the number of rows currently being read.  If possible,
+    # please avoid using this function.  It requires reading the entire result
+    # set from the database to determine the number of rows being returned.
+    # <p>
+    # Stability: Added in v1.03, stability guaranteed for v1.xx.
+    # Implementation currently requires caching entire result set into memory,
+    # avoid using this property.
+    row_count = property(lambda self: self._get_row_count())
+
+    @require_stmt
+    def _get_row_count(self):
+        return self._stmt.row_count
+
+    ##
+    # Read a row from the database server, and return it in a dictionary
+    # indexed by column name/alias.  This method will raise an error if two
+    # columns have the same name.  Returns None after the last row.
+    # <p>
+    # Stability: Added in v1.00, stability guaranteed for v1.xx.
+    @require_stmt
+    def read_dict(self):
+        return self._stmt.read_dict()
+
+    ##
+    # Read a row from the database server, and return it as a tuple of values.
+    # Returns None after the last row.
+    # <p>
+    # Stability: Added in v1.00, stability guaranteed for v1.xx.
+    @require_stmt
+    def read_tuple(self):
+        return self._stmt.read_tuple()
+
+    ##
+    # Return an iterator for the output of this statement.  The iterator will
+    # return a tuple for each row, in the same manner as {@link
+    # #PreparedStatement.read_tuple read_tuple}.
+    # <p>
+    # Stability: Added in v1.00, stability guaranteed for v1.xx.
+    @require_stmt
+    def iterate_tuple(self):
+        return self._stmt.iterate_tuple()
+
+    ##
+    # Return an iterator for the output of this statement.  The iterator will
+    # return a dict for each row, in the same manner as {@link
+    # #PreparedStatement.read_dict read_dict}.
+    # <p>
+    # Stability: Added in v1.00, stability guaranteed for v1.xx.
+    @require_stmt
+    def iterate_dict(self):
+        return self._stmt.iterate_dict()
+
+    def close(self):
+        if self._stmt != None:
+            self._stmt.close()
+            self._stmt = None
+
+
+    ##
+    # Return the fileno of the underlying socket for this cursor's connection.
+    # <p>
+    # Stability: Added in v1.07, stability guaranteed for v1.xx.
+    def fileno(self):
+        return self.connection.fileno()
+
+    ##
+    # Poll the underlying socket for this cursor and sync if there is data waiting
+    # to be read. This has the effect of flushing asynchronous messages from the
+    # backend. Returns True if messages were read, False otherwise.
+    # <p>
+    # Stability: Added in v1.07, stability guaranteed for v1.xx.
+    def isready(self):
+        return self.connection.isready()
+    
+
+##
+# This class represents a connection to a PostgreSQL database.
+# <p>
+# The database connection is derived from the {@link #Cursor Cursor} class,
+# which provides a default cursor for running queries.  It also provides
+# transaction control via the 'begin', 'commit', and 'rollback' methods.
+# Without beginning a transaction explicitly, all statements will autocommit to
+# the database.
+# <p>
+# As of v1.01, instances of this class are thread-safe.  See {@link
+# PreparedStatement PreparedStatement} for more information.
+# <p>
+# Stability: Added in v1.00, stability guaranteed for v1.xx.
+#
+# @param user   The username to connect to the PostgreSQL server with.  This
+# parameter is required.
+#
+# @keyparam host   The hostname of the PostgreSQL server to connect with.
+# Providing this parameter is necessary for TCP/IP connections.  One of either
+# host, or unix_sock, must be provided.
+#
+# @keyparam unix_sock   The path to the UNIX socket to access the database
+# through, for example, '/tmp/.s.PGSQL.5432'.  One of either unix_sock or host
+# must be provided.  The port parameter will have no affect if unix_sock is
+# provided.
+#
+# @keyparam port   The TCP/IP port of the PostgreSQL server instance.  This
+# parameter defaults to 5432, the registered and common port of PostgreSQL
+# TCP/IP servers.
+#
+# @keyparam database   The name of the database instance to connect with.  This
+# parameter is optional, if omitted the PostgreSQL server will assume the
+# database name is the same as the username.
+#
+# @keyparam password   The user password to connect to the server with.  This
+# parameter is optional.  If omitted, and the database server requests password
+# based authentication, the connection will fail.  On the other hand, if this
+# parameter is provided and the database does not request password
+# authentication, then the password will not be used.
+#
+# @keyparam socket_timeout  Socket connect timeout measured in seconds.
+# Defaults to 60 seconds.
+#
+# @keyparam ssl     Use SSL encryption for TCP/IP socket.  Defaults to False.
+class Connection(Cursor):
+    def __init__(self, user, host=None, unix_sock=None, port=5432, database=None, password=None, socket_timeout=60, ssl=False, options=None):
+        self._row_desc = None
+        try:
+            self.c = protocol.Connection(unix_sock=unix_sock, host=host, port=port, socket_timeout=socket_timeout, ssl=ssl)
+            self.c.authenticate(user, password=password, database=database, options=options)
+        except socket.error, e:
+            raise InterfaceError("communication error", e)
+        Cursor.__init__(self, self)
+        self._begin = PreparedStatement(self, "BEGIN TRANSACTION")
+        self._commit = PreparedStatement(self, "COMMIT TRANSACTION")
+        self._rollback = PreparedStatement(self, "ROLLBACK TRANSACTION")
+        self._unnamed_prepared_statement_lock = threading.RLock()
+
+    ##
+    # An event handler that is fired when NOTIFY occurs for a notification that
+    # has been LISTEN'd for.  The value of this property is a
+    # util.MulticastDelegate.  A callback can be added by using
+    # connection.NotificationReceived += SomeMethod.  The method will be called
+    # with a single argument, an object that has properties: backend_pid,
+    # condition, and additional_info.  Callbacks can be removed with the -=
+    # operator.
+    # <p>
+    # Stability: Added in v1.03, stability guaranteed for v1.xx.
+    NotificationReceived = property(
+            lambda self: getattr(self.c, "NotificationReceived"),
+            lambda self, value: setattr(self.c, "NotificationReceived", value)
+    )
+
+    ##
+    # An event handler that is fired when the database server issues a notice.
+    # The value of this property is a util.MulticastDelegate.  A callback can
+    # be added by using connection.NotificationReceived += SomeMethod.  The
+    # method will be called with a single argument, an object that has
+    # properties: severity, code, msg, and possibly others (detail, hint,
+    # position, where, file, line, and routine).  Callbacks can be removed with
+    # the -= operator.
+    # <p>
+    # Stability: Added in v1.03, stability guaranteed for v1.xx.
+    NoticeReceived = property(
+            lambda self: getattr(self.c, "NoticeReceived"),
+            lambda self, value: setattr(self.c, "NoticeReceived", value)
+    )
+
+    ##
+    # An event handler that is fired when a runtime configuration option is
+    # changed on the server.  The value of this property is a
+    # util.MulticastDelegate.  A callback can be added by using
+    # connection.NotificationReceived += SomeMethod.  Callbacks can be removed
+    # with the -= operator.  The method will be called with a single argument,
+    # an object that has properties "key" and "value".
+    # <p>
+    # Stability: Added in v1.03, stability guaranteed for v1.xx.
+    ParameterStatusReceived = property(
+            lambda self: getattr(self.c, "ParameterStatusReceived"),
+            lambda self, value: setattr(self.c, "ParameterStatusReceived", value)
+    )
+
+    ##
+    # Begins a new transaction.
+    # <p>
+    # Stability: Added in v1.00, stability guaranteed for v1.xx.
+    def begin(self):
+        if self.is_closed:
+            raise ConnectionClosedError()
+        self._begin.execute()
+
+    ##
+    # Commits the running transaction.
+    # <p>
+    # Stability: Added in v1.00, stability guaranteed for v1.xx.
+    def commit(self):
+        if self.is_closed:
+            raise ConnectionClosedError()
+        self._commit.execute()
+
+    ##
+    # Rolls back the running transaction.
+    # <p>
+    # Stability: Added in v1.00, stability guaranteed for v1.xx.
+    def rollback(self):
+        if self.is_closed:
+            raise ConnectionClosedError()
+        self._rollback.execute()
+
+    ##
+    # Closes an open connection.
+    def close(self):
+        if self.is_closed:
+            raise ConnectionClosedError()
+        self.c.close()
+        self.c = None
+
+    is_closed = property(lambda self: self.c == None)
+
+    def recache_record_types(self):
+        self.c._cache_record_attnames()
+
+    ##
+    # Return the fileno of the underlying socket for this connection.
+    # <p>
+    # Stability: Added in v1.07, stability guaranteed for v1.xx.
+    def fileno(self):
+        return self.c.fileno()
+
+    ##
+    # Poll the underlying socket for this connection and sync if there is data
+    # waiting to be read. This has the effect of flushing asynchronous
+    # messages from the backend. Returns True if messages were read, False
+    # otherwise.
+    # <p>
+    # Stability: Added in v1.07, stability guaranteed for v1.xx.
+    def isready(self):
+        return self.c.isready()
+


Mime
View raw message