Skip to content

Commit

Permalink
Fixed empty array bug
Browse files Browse the repository at this point in the history
This fixes bug #10. Also added a test.

Tidied up code using pyflakes and pep8.
  • Loading branch information
tlocke committed Jul 1, 2013
1 parent 2af943f commit bd5e768
Show file tree
Hide file tree
Showing 17 changed files with 837 additions and 749 deletions.
5 changes: 3 additions & 2 deletions pg8000/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,8 @@
from . import dbapi as DBAPI
pg8000_dbapi = DBAPI

from .errors import *
from .interface import *
from .errors import DatabaseError
#from .interface import *
from .types import Bytea

__all__ = [Bytea, DatabaseError]
71 changes: 45 additions & 26 deletions pg8000/dbapi.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,12 +32,18 @@
__author__ = "Mathieu Fenniak"

from . import util, errors, interface, types
from .errors import *
from .errors import ConnectionClosedError, ProgrammingError, \
CursorClosedError, CopyQueryOrTableRequiredError, DatabaseError, Error, \
Warning, InterfaceError, OperationalError, IntegrityError, InternalError, \
NotSupportedError
import datetime
import itertools
import time
import threading

__all__ = [
DatabaseError, Error, Warning, InterfaceError, OperationalError,
IntegrityError, InternalError, NotSupportedError]

##
# The DBAPI level supported. Currently 2.0. This property is part of the
# DBAPI 2.0 specification.
Expand All @@ -62,11 +68,12 @@

def require_open_cursor(fn):
def _fn(self, *args, **kwargs):
if self.cursor == None:
if self.cursor is None:
raise CursorClosedError()
return fn(self, *args, **kwargs)
return _fn


##
# The class of object returned by the
# {@link #ConnectionWrapper.cursor cursor method}.
Expand Down Expand Up @@ -102,7 +109,7 @@ def _getConnection(self):
@property
@require_open_cursor
def rowcount(self):
if self._override_rowcount != None:
if self._override_rowcount is not None:
return self._override_rowcount
return self.cursor.row_count

Expand All @@ -117,12 +124,12 @@ def rowcount(self):
@property
@require_open_cursor
def description(self):
if self.cursor.row_description == None:
if self.cursor.row_description is None:
return None
columns = []
for col in self.cursor.row_description:
columns.append((col["name"], col["type_oid"],
None, None, None, None, None))
columns.append(
(col["name"], col["type_oid"], None, None, None, None, None))
return columns

##
Expand All @@ -144,16 +151,16 @@ def execute(self, operation, args=()):
@require_open_cursor
def execute_notrans(self, operation, args=()):
if self._connection.in_transaction:
raise ProgrammingError("Connection is in a transaction; "
"please commit() or rollback() first.")
raise ProgrammingError(
"Connection is in a transaction; "
"please commit() or rollback() first.")
self._override_rowcount = None
if hasattr(args, 'keys'):
query, param_fn = util.coerce_named(operation, args)
else:
query, param_fn = util.coerce_positional(operation, args)
self._execute(query, param_fn(args))


##
# Prepare a database operation and then execute it against all parameter
# sequences or mappings provided.
Expand All @@ -171,7 +178,8 @@ def executemany(self, operation, parameter_sets):
if hasattr(parameters, 'keys'):
query, param_fn = util.coerce_named(operation, parameters)
else:
query, param_fn = util.coerce_positional(operation, parameters)
query, param_fn = util.coerce_positional(
operation, parameters)

self._execute(query, param_fn(parameters))
if self.cursor.row_count == -1 or self._override_rowcount == -1:
Expand All @@ -181,7 +189,8 @@ def executemany(self, operation, parameter_sets):

def _execute(self, operation, args):
try:
self.cursor.execute(operation, row_adapter=self._row_adapter, *args)
self.cursor.execute(
operation, row_adapter=self._row_adapter, *args)
except ConnectionClosedError:
# can't rollback in this case
raise
Expand All @@ -192,17 +201,17 @@ def _execute(self, operation, args):
raise

def copy_from(self, fileobj, table=None, sep='\t', null=None, query=None):
if query == None:
if table == None:
if query is None:
if table is 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:
if query is None:
if table is None:
raise CopyQueryOrTableRequiredError()
query = "COPY %s TO stdout DELIMITER '%s'" % (table, sep)
if null is not None:
Expand All @@ -223,7 +232,6 @@ def copy_execute(self, fileobj, query):
self._connection.rollback()
raise


##
# Fetch the next row of a query result set, returning a single sequence, or
# None when no more data is available.
Expand All @@ -244,6 +252,7 @@ def fetchone(self):
def fetchmany(self, size=None):
if size is None:
size = self.arraysize

def iter():
for i, row in enumerate(self.cursor.iterate_tuple()):
yield row
Expand Down Expand Up @@ -272,7 +281,7 @@ def close(self):

def next(self):
retval = self.fetchone()
if retval == None:
if retval is None:
raise StopIteration()
return retval

Expand All @@ -293,13 +302,15 @@ def fileno(self):
def isready(self):
return self.cursor.isready()


def require_open_connection(fn):
def _fn(self, *args, **kwargs):
if self.conn == None:
if self.conn is None:
raise ConnectionClosedError()
return fn(self, *args, **kwargs)
return _fn


##
# The class of object returned by the {@link #connect connect method}.
class ConnectionWrapper(object):
Expand Down Expand Up @@ -402,6 +413,7 @@ def close(self):
def server_version(self):
return self.conn.server_version()


##
# Creates a DBAPI 2.0 compatible interface to a PostgreSQL database.
# <p>
Expand Down Expand Up @@ -439,30 +451,39 @@ def server_version(self):
# @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):
return ConnectionWrapper(user=user, host=host,
unix_sock=unix_sock, port=port, database=database,
password=password, socket_timeout=socket_timeout, ssl=ssl)
def connect(
user, host=None, unix_sock=None, port=5432, database=None,
password=None, socket_timeout=60, ssl=False):
return ConnectionWrapper(
user=user, host=host, unix_sock=unix_sock, port=port,
database=database, password=password, socket_timeout=socket_timeout,
ssl=ssl)


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):
Expand All @@ -485,5 +506,3 @@ def Binary(value):

# oid type_oid
ROWID = 26


22 changes: 21 additions & 1 deletion pg8000/errors.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,87 +29,107 @@

__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

Loading

0 comments on commit bd5e768

Please sign in to comment.