pg8000 not working with enums.

Issue #1631 resolved
Former user created an issue

Hey guys,

I tried to get enums to work in pg8000 today, but it hates inserting them. Not sure what the deal is here, but here is a code example to reproduce it and the traceback. I tried pulling in the Enum stuff from the psycopg2 dialect, but it fails in the same manner.

cheers. -chris (percious)

Source:

from sqlalchemy import *
from sqlalchemy.orm import *
url = 'postgresql+pg8000://postgres@localhost/test8000'

engine = create_engine(url, encoding='utf-8')

metadata = MetaData(bind=engine)
metadata.drop_all()

metadata = MetaData(bind=engine)
some_table = Table(u'somes', metadata,
    Column(u'some_id', Integer, primary_key=True),
    Column(u'e', Enum('one', 'two', name=u'e'))
)

metadata.create_all()

class Some(object):pass

mapper(Some, some_table)

session = sessionmaker(bind=engine)()

s = Some()
setattr(s, 'e', 'one')

session.add(s)
session.flush()
session.commit()

Traceback:

$ python pg8000test.py 
Traceback (most recent call last):
  File "pg8000test.py", line 33, in <module>
    print session.query(Some).all()
  File "/Users/percious/clients/mvpss/mvp2.1/src/sqlalchemy_trunk/lib/sqlalchemy/orm/query.py", line 1217, in all
    return list(self)
  File "/Users/percious/clients/mvpss/mvp2.1/src/sqlalchemy_trunk/lib/sqlalchemy/orm/query.py", line 1311, in __iter__
    return self._execute_and_instances(context)
  File "/Users/percious/clients/mvpss/mvp2.1/src/sqlalchemy_trunk/lib/sqlalchemy/orm/query.py", line 1316, in _execute_and_instances
    mapper=self._mapper_zero_or_none())
  File "/Users/percious/clients/mvpss/mvp2.1/src/sqlalchemy_trunk/lib/sqlalchemy/orm/session.py", line 739, in execute
    clause, params or {})
  File "/Users/percious/clients/mvpss/mvp2.1/src/sqlalchemy_trunk/lib/sqlalchemy/engine/base.py", line 991, in execute
    return Connection.executors[c](c)(self, object, multiparams, params)
  File "/Users/percious/clients/mvpss/mvp2.1/src/sqlalchemy_trunk/lib/sqlalchemy/engine/base.py", line 1053, in _execute_clauseelement
    return self.__execute_context(context)
  File "/Users/percious/clients/mvpss/mvp2.1/src/sqlalchemy_trunk/lib/sqlalchemy/engine/base.py", line 1076, in __execute_context
    self._cursor_execute(context.cursor, context.statement, context.parameters[0](0), context=context)
  File "/Users/percious/clients/mvpss/mvp2.1/src/sqlalchemy_trunk/lib/sqlalchemy/engine/base.py", line 1138, in _cursor_execute
    self._handle_dbapi_exception(e, statement, parameters, cursor, context)
  File "/Users/percious/clients/mvpss/mvp2.1/src/sqlalchemy_trunk/lib/sqlalchemy/engine/base.py", line 1136, in _cursor_execute
    self.dialect.do_execute(cursor, statement, parameters, context=context)
  File "/Users/percious/clients/mvpss/mvp2.1/src/sqlalchemy_trunk/lib/sqlalchemy/engine/default.py", line 207, in do_execute
    cursor.execute(statement, parameters)
  File "/Users/percious/clients/mvpss/mvp2.1/src/pg8000/pg8000/dbapi.py", line 243, in _fn
    return fn(self, *args, **kwargs)
  File "/Users/percious/clients/mvpss/mvp2.1/src/pg8000/pg8000/dbapi.py", line 312, in execute
    self._execute(operation, args)
  File "/Users/percious/clients/mvpss/mvp2.1/src/pg8000/pg8000/dbapi.py", line 317, in _execute
    self.cursor.execute(new_query, *new_args)
  File "/Users/percious/clients/mvpss/mvp2.1/src/pg8000/pg8000/interface.py", line 304, in execute
    self._stmt.execute(*args, **kwargs)
  File "/Users/percious/clients/mvpss/mvp2.1/src/pg8000/pg8000/interface.py", line 139, in execute
    self._row_desc, cmd = self.c.bind(self._portal_name, self._statement_name, args, self._parse_row_desc, kwargs.get("stream"))
  File "/Users/percious/clients/mvpss/mvp2.1/src/pg8000/pg8000/protocol.py", line 920, in _fn
    return fn(self, *args, **kwargs)
  File "/Users/percious/clients/mvpss/mvp2.1/src/pg8000/pg8000/protocol.py", line 1107, in bind
    output_fc = [self._record_field_names) for f in row_desc.fields](types.py_type_info(f,)
  File "/Users/percious/clients/mvpss/mvp2.1/src/pg8000/pg8000/types.py", line 166, in py_type_info
    raise NotSupportedError("type oid %r not mapped to py type" % type_oid)
sqlalchemy.exc.NotSupportedError: (NotSupportedError) type oid 2015924 not mapped to py type u'SELECT somes.some_id AS somes_some_id, somes.e AS somes_e \nFROM somes' [percious$ python pg8000test.py 
Traceback (most recent call last):
  File "pg8000test.py", line 28, in <module>
    session.flush()
  File "/Users/percious/clients/mvpss/mvp2.1/src/sqlalchemy_trunk/lib/sqlalchemy/orm/session.py", line 1307, in flush
    self._flush(objects)
  File "/Users/percious/clients/mvpss/mvp2.1/src/sqlalchemy_trunk/lib/sqlalchemy/orm/session.py", line 1385, in _flush
    flush_context.execute()
  File "/Users/percious/clients/mvpss/mvp2.1/src/sqlalchemy_trunk/lib/sqlalchemy/orm/unitofwork.py", line 261, in execute
    UOWExecutor().execute(self, tasks)
  File "/Users/percious/clients/mvpss/mvp2.1/src/sqlalchemy_trunk/lib/sqlalchemy/orm/unitofwork.py", line 753, in execute
    self.execute_save_steps(trans, task)
  File "/Users/percious/clients/mvpss/mvp2.1/src/sqlalchemy_trunk/lib/sqlalchemy/orm/unitofwork.py", line 768, in execute_save_steps
    self.save_objects(trans, task)
  File "/Users/percious/clients/mvpss/mvp2.1/src/sqlalchemy_trunk/lib/sqlalchemy/orm/unitofwork.py", line 759, in save_objects
    task.mapper._save_obj(task.polymorphic_tosave_objects, trans)
  File "/Users/percious/clients/mvpss/mvp2.1/src/sqlalchemy_trunk/lib/sqlalchemy/orm/mapper.py", line 1420, in _save_obj
    c = connection.execute(statement.values(value_params), params)
  File "/Users/percious/clients/mvpss/mvp2.1/src/sqlalchemy_trunk/lib/sqlalchemy/engine/base.py", line 991, in execute
    return Connection.executors[c](]
(mvp2.1)percious-new-macbook:src)(self, object, multiparams, params)
  File "/Users/percious/clients/mvpss/mvp2.1/src/sqlalchemy_trunk/lib/sqlalchemy/engine/base.py", line 1053, in _execute_clauseelement
    return self.__execute_context(context)
  File "/Users/percious/clients/mvpss/mvp2.1/src/sqlalchemy_trunk/lib/sqlalchemy/engine/base.py", line 1076, in __execute_context
    self._cursor_execute(context.cursor, context.statement, context.parameters[0](0), context=context)
  File "/Users/percious/clients/mvpss/mvp2.1/src/sqlalchemy_trunk/lib/sqlalchemy/engine/base.py", line 1138, in _cursor_execute
    self._handle_dbapi_exception(e, statement, parameters, cursor, context)
  File "/Users/percious/clients/mvpss/mvp2.1/src/sqlalchemy_trunk/lib/sqlalchemy/engine/base.py", line 1136, in _cursor_execute
    self.dialect.do_execute(cursor, statement, parameters, context=context)
  File "/Users/percious/clients/mvpss/mvp2.1/src/sqlalchemy_trunk/lib/sqlalchemy/engine/default.py", line 207, in do_execute
    cursor.execute(statement, parameters)
  File "/Users/percious/clients/mvpss/mvp2.1/src/pg8000/pg8000/dbapi.py", line 243, in _fn
    return fn(self, *args, **kwargs)
  File "/Users/percious/clients/mvpss/mvp2.1/src/pg8000/pg8000/dbapi.py", line 312, in execute
    self._execute(operation, args)
  File "/Users/percious/clients/mvpss/mvp2.1/src/pg8000/pg8000/dbapi.py", line 317, in _execute
    self.cursor.execute(new_query, *new_args)
  File "/Users/percious/clients/mvpss/mvp2.1/src/pg8000/pg8000/interface.py", line 303, in execute
    self._stmt = PreparedStatement(self.connection, query, statement_name="", *[type(x), "value": x} for x in args]({"type":))
  File "/Users/percious/clients/mvpss/mvp2.1/src/pg8000/pg8000/interface.py", line 108, in __init__
    self._parse_row_desc = self.c.parse(self._statement_name, statement, types)
  File "/Users/percious/clients/mvpss/mvp2.1/src/pg8000/pg8000/protocol.py", line 920, in _fn
    return fn(self, *args, **kwargs)
  File "/Users/percious/clients/mvpss/mvp2.1/src/pg8000/pg8000/protocol.py", line 1094, in parse
    return reader.handle_messages()
  File "/Users/percious/clients/mvpss/mvp2.1/src/pg8000/pg8000/protocol.py", line 906, in handle_messages
    raise exc
sqlalchemy.exc.ProgrammingError: (ProgrammingError) ('ERROR', '42804', 'column "e" is of type e but expression is of type text') u'INSERT INTO somes (e) VALUES (%s) RETURNING somes.some_id' ['one']('one')

Comments (11)

  1. Mike Bayer repo owner

    are you saying that ENUM doesn't work with pyscopg2 ? ENUM is known not to work with pg8000 as of yet.

    Also here is the actual ticket for pg8000, they are on launchpad:

    https://bugs.launchpad.net/pg8000/+bug/412000

    I would also suggest reporting pg8000 bugs using pure pg8000 code, which eliminates the possibility that SQLA's implementation is wrong.

  2. Former user Account Deleted

    oh no, enums work awesome with psycopg2. I'm just excited about pg8000 and starting to try it out on some of my clients' apps. Already have it going on one of them. I am pretty amazed that you have known about this for a month tho mike.

    cheers. -chris

  3. Mike Bayer repo owner

    great. not sure if you're aware, I created a fork of pg8000 that made extensive changes to the internals in the efforts of lots more speed and also extension capability. It was specific to a database I was contracted to work with called Akiban, but the thrust of the internal changes was to do away with most of the internal object creation pg8000 uses in order to represent postgres structures. I had emailed Mathieu about it but he did not respond. the fork is at https://github.com/zzzeek/pg8000/ , examples of major reductions in object creation can be seen at https://github.com/zzzeek/pg8000/commit/fffc22a5b92d849a23df8f46d1c54d3e98e9fba4, https://github.com/zzzeek/pg8000/commit/bc9127429b1a9df192d782198aee16488da0962f and https://github.com/zzzeek/pg8000/commit/b59841124f474fe14c69f7ff671e98f814447717 where I tackle that huge parameter formatting function, it looks like you might have addressed this as well. I have it supporting just the two formats that psycopg2 does instead of all of them.

  4. Tony Locke

    This is interesting. Looking at https://github.com/zzzeek/pg8000/compare/mfenniak:trunk...trunk it seems that @mfenniak did accept all your changes. I checked, and all your work is in the HEAD of 'trunk' on the mfenniak repo.

    I too have been working on speeding up PG8000, but all of my stuff has been on the Python 3 version. The two branches were very different when I started, and now they've diverged even more. It's a shame that the two versions have divided our efforts. I've not sure what the accepted strategy is for maintaining Python 2 and 3 libraries, do you have any views?

    Having worked on the speed for py3, I'm now focussing on fixing bugs. With bugs I've taken the approach of doing the fix to both trunk and py3 branches.

    Btw, I had a similar thought to you about simplifying the paramstyles. I've put my suggestion for DBAPI 3 at:

    https://github.com/tlocke/pg8000/tree/three

    It removes the 'paramstyle' attribute entirely and just has parameter labels as :1, :2, :3, ... or the :name style. If it can cast the label to an int, it assumes the parameters argument is a sequence, otherwise it assumes it's a mapping. What do you think?

  5. Mike Bayer repo owner

    Hm I guess he merged it after I had done the extra work? I might have still been getting used to pull requests at that point.

    for Py2/3, the only rational choice is an in-place codebase. all of my major libs/tools are now py2k/3k in place, no 2to3 needed - alembic, dogpile, mako, sqlalchemy as of 0.9. mako in fact supports from py2.4 all the way to py3.4, and is very heavy on unicode as well as python interpreter internals even. It's not very hard to make a pure-python codebase support between 2 and 3.

    the two paramstyles I think are important, and the ones we've decided on the DB-SIG list would be the standard in a DBAPI 3, are qmark and named. These two formats are commonplace outside of the Python world.

    my next choice would be format and pyformat. I'm not too keen on numbered since it is just another named style, just more awkward.

  6. Log in to comment