On Oct 2, 2009, at 6:05 PM, volx wrote:

>
> My module is called sandbox.py
> After importing it to ipython and letting it run, here's what I get
> for the test you suggested:
>
> In [47]: (sandbox.price_sources.c.desciption=='EJV').right.type
> Out[47]: OracleChar(length=100, convert_unicode=False,
> assert_unicode=None)
>
> The trouble that for some reason the code goes into
> sqlalchemy.types.String.get_dbapi_type instead of
> sqlalchemy.databases.oracle.OracleChar

I added an _OracleChar type to the oracle dialect in trunk (0.6) which  
provides dbapi.FIXED_CHAR.   I was able to reproduce your original  
behavior, added a unit test which exercises the select lookup on the  
CHAR column both with a straight Table as well as a reflected one, and  
it all passes.  There was no issue with the get_dbapi_type()  
mechanism.   That is in r6386.

test:

         m = MetaData(testing.db)
         t = Table('t1', m,
             Column('id', Integer, primary_key=True),
             Column('data', CHAR(30), nullable=False)
         )

         t.create()
         try:
             t.insert().execute(
                 dict(id=1, data="value 1"),
                 dict(id=2, data="value 2"),
                 dict(id=3, data="value 3")
             )

             eq_(t.select().where(t.c.data=='value 2').execute 
().fetchall(),
                 [(2, 'value 2                       ')]
                 )

             m2 = MetaData(testing.db)
             t2 = Table('t1', m2, autoload=True)
             assert type(t2.c.data.type) is CHAR
             eq_(t2.select().where(t2.c.data=='value 2').execute 
().fetchall(),
                 [(2, 'value 2                       ')]
                 )

         finally:
             t.drop()




>
> In [14]: pdb.run('sandbox.test()')
>> <string>(1)<module>()
> (Pdb) b /usr/local/lib/python2.6/site-packages/SQLAlchemy-0.5.5-
> py2.6.egg/sqlalchemy/engine/default.py:322
> Breakpoint 7 at /usr/local/lib/python2.6/site-packages/
> SQLAlchemy-0.5.5-py2.6.egg/sqlalchemy/engine/default.py:322
> (Pdb) c
> 2009-10-02 17:58:18,020 INFO sqlalchemy.engine.base.Engine.0x...97ec
> SELECT USER FROM DUAL
> 2009-10-02 17:58:18,020 INFO sqlalchemy.engine.base.Engine.0x...97ec
> {}
> 2009-10-02 17:58:18,025 INFO sqlalchemy.engine.base.Engine.0x...97ec
> select table_name from all_tables where table_name=:name and
> owner=:schema_name
> 2009-10-02 17:58:18,026 INFO sqlalchemy.engine.base.Engine.0x...97ec
> {'name': 'VICTOR_PRICE_SOURCES', 'schema_name': 'CDOCOLLAT'}
>> /usr/local/lib/python2.6/site-packages/SQLAlchemy-0.5.5-py2.6.egg/ 
>> sqlalchemy/engine/default.py(322)set_input_sizes()
> -> dbtype = typeengine.dialect_impl(self.dialect).get_dbapi_type
> (self.dialect.dbapi)
> (Pdb) dbtype
> *** NameError: name 'dbtype' is not defined
> (Pdb) typeengine.dialect_impl(self.dialect)
>
> OracleChar(length=100, convert_unicode=False, assert_unicode=None)
>
> (Pdb) # we should be going into OracleChar.get_dbapi_type
>
> *** SyntaxError: unexpected EOF while parsing (<stdin>, line 1)
> (Pdb) s
> --Call--
>> /usr/local/lib/python2.6/site-packages/SQLAlchemy-0.5.5-py2.6.egg/ 
>> sqlalchemy/types.py(124)dialect_impl()
> -> def dialect_impl(self, dialect, **kwargs):
> (Pdb) l
> 119               Column('data', MyType(16))
> 120               )
> 121
> 122         """
> 123
> 124  ->     def dialect_impl(self, dialect, **kwargs):
> 125             try:
> 126                 return self._impl_dict[dialect]
> 127             except AttributeError:
> 128                 self._impl_dict = weakref.WeakKeyDictionary()   #
> will be optimized in 0.6
> 129                 return self._impl_dict.setdefault(dialect,
> dialect.type_descriptor(self))
> (Pdb) l
> 130             except KeyError:
> 131                 return self._impl_dict.setdefault(dialect,
> dialect.type_descriptor(self))
> 132
> 133         def __getstate__(self):
> 134             d = self.__dict__.copy()
> 135             d.pop('_impl_dict', None)
> 136             return d
> 137
> 138         def get_col_spec(self):
> 139             """Return the DDL representation for this type."""
> 140             raise NotImplementedError()
> (Pdb) dialect
> <sqlalchemy.databases.oracle.OracleDialect object at 0xa1ae54c>
> (Pdb) n
>> /usr/local/lib/python2.6/site-packages/SQLAlchemy-0.5.5-py2.6.egg/ 
>> sqlalchemy/types.py(125)dialect_impl()
> -> try:
> (Pdb) n
>> /usr/local/lib/python2.6/site-packages/SQLAlchemy-0.5.5-py2.6.egg/ 
>> sqlalchemy/types.py(126)dialect_impl()
> -> return self._impl_dict[dialect]
> (Pdb) n
> --Return--
>> /usr/local/lib/python2.6/site-packages/SQLAlchemy-0.5.5-py2.6.egg/ 
>> sqlalchemy/types.py(126)dialect_impl()->OracleCh...ode=None)
> -> return self._impl_dict[dialect]
> (Pdb) l
> 121
> 122         """
> 123
> 124         def dialect_impl(self, dialect, **kwargs):
> 125             try:
> 126  ->             return self._impl_dict[dialect]
> 127             except AttributeError:
> 128                 self._impl_dict = weakref.WeakKeyDictionary()   #
> will be optimized in 0.6
> 129                 return self._impl_dict.setdefault(dialect,
> dialect.type_descriptor(self))
> 130             except KeyError:
> 131                 return self._impl_dict.setdefault(dialect,
> dialect.type_descriptor(self))
> (Pdb) dialect
> <sqlalchemy.databases.oracle.OracleDialect object at 0xa1ae54c>
> (Pdb) n
> --Call--
>> /usr/local/lib/python2.6/site-packages/SQLAlchemy-0.5.5-py2.6.egg/ 
>> sqlalchemy/types.py(477)get_dbapi_type()
> -> def get_dbapi_type(self, dbapi):
> (Pdb) l
> 472                         return value
> 473                 return process
> 474             else:
> 475                 return None
> 476
> 477  ->     def get_dbapi_type(self, dbapi):
> 478             return dbapi.STRING
> 479
> 480     class Text(String):
> 481         """A variably sized string type.
> 482
> (Pdb) l 1
>  1     # types.py
>  2     # Copyright (C) 2005, 2006, 2007, 2008, 2009 Michael Bayer
> mike...@zzzcomputing.com
>  3     #
>  4     # This module is part of SQLAlchemy and is released under
>  5     # the MIT License: http://www.opensource.org/licenses/mit-license.php
>  6
>  7     """defines genericized SQL types, each represented by a
> subclass of
>  8     :class:`~sqlalchemy.types.AbstractType`.  Dialects define
> further subclasses of these
>  9     types.
> 10
> 11     For more information see the SQLAlchemy documentation on
> types.
>
>
>
>
> On Oct 1, 2:48 pm, "Michael Bayer" <mike...@zzzcomputing.com> wrote:
>> volx wrote:
>>
>>> Here's my program, modified as you suggest. It also creates the  
>>> table
>>> so you can try it on any instance of Oracle. No joy on the result.
>>> Seems like the set_inputsizes isn't called? How can I tell for
>>> certain?
>>
>>> import sqlalchemy.types as sqltypes
>>> from exceptions import NotImplementedError
>>
>>> class OracleChar(sqltypes.CHAR):
>>>     """Patched OracleChar type to fix padding issue
>>>    http://groups.google.com/group/sqlalchemy/browse_thread/thread/965287 
>>> ...
>>>     """
>>>     def get_col_spec(self):
>>>         return "CHAR(%(length)s)" % {'length' : self.length}
>>
>>>     def get_dbapi_type(self, dbapi):
>>>         raise NotImplementedError('gotcha')
>>>         return dbapi.FIXED_CHAR
>>
>> is this using 0.5 or current trunk ?  There's some additional  
>> restrictions
>> in trunk on setinputsizes due to some cx_oracle bugs I reported.
>> Unfortanately my macbook is in the shop so I won't be able to test  
>> this
>> myself until possibly next week.   Basically the the  
>> "set_input_sizes()"
>> in sqlalchemy/base/default.py should be called for all oracle  
>> executions
>> and the datatype on the bind parameter generated by "mycol == <some
>> value>" should be of your type, i.e. isinstance 
>> ((mycol==5).right.type,
>> OracleChar).
>>
>>
>>
>>> from sqlalchemy.databases import oracle
>>> oracle.OracleChar = OracleChar
>>
>>> from sqlalchemy import Table, Column, Integer, MetaData
>>> from sqlalchemy.databases.oracle import OracleChar
>>> from sqlalchemy.sql import select, insert
>>> from sqlalchemy import create_engine
>>
>>> metadata = MetaData()
>>
>>> price_sources = Table('victor_price_sources', metadata,
>>>     Column('pricesource', Integer, primary_key=True),
>>>     Column('desciption', OracleChar(100))
>>> )
>>
>>> dburl = "oracle://user:pas...@servername:1521/dbname"
>>
>>> e = create_engine(dburl, echo=True)
>>> metadata.bind = e
>>> metadata.create_all()
>>> con = e.connect()
>>
>>> ins = price_sources.insert().values(pricesource=1, desciption='EJV')
>>> con.execute(ins)
>>
>>> s = select([price_sources], price_sources.c.desciption=='EJV')
>>> all = con.execute(s).fetchall()
>>> print all
>>
>>> s = select([price_sources])
>>> all = con.execute(s).fetchall()
>>> print all
>>
>>> And the results:
>>
>>> In [1]: import sandbox
>>> 2009-10-01 13:24:25,015 INFO sqlalchemy.engine.base.Engine.0x...9ccc
>>> SELECT USER FROM DUAL
>>> 2009-10-01 13:24:25,018 INFO sqlalchemy.engine.base.Engine.0x...9ccc
>>> {}
>>> 2009-10-01 13:24:25,022 INFO sqlalchemy.engine.base.Engine.0x...9ccc
>>> select table_name from all_tables where table_name=:name and
>>> owner=:schema_name
>>> 2009-10-01 13:24:25,022 INFO sqlalchemy.engine.base.Engine.0x...9ccc
>>> {'name': 'VICTOR_PRICE_SOURCES', 'schema_name': 'CDOCOLLAT'}
>>> 2009-10-01 13:24:25,027 INFO sqlalchemy.engine.base.Engine.0x...9ccc
>>> CREATE TABLE victor_price_sources (
>>>         pricesource INTEGER NOT NULL,
>>>         desciption CHAR(100),
>>>         PRIMARY KEY (pricesource)
>>> )
>>
>>> 2009-10-01 13:24:25,027 INFO sqlalchemy.engine.base.Engine.0x...9ccc
>>> {}
>>> 2009-10-01 13:24:25,082 INFO sqlalchemy.engine.base.Engine.0x...9ccc
>>> COMMIT
>>> 2009-10-01 13:24:25,084 INFO sqlalchemy.engine.base.Engine.0x...9ccc
>>> INSERT INTO victor_price_sources (pricesource, desciption) VALUES
>>> (:pricesource, :desciption)
>>> 2009-10-01 13:24:25,084 INFO sqlalchemy.engine.base.Engine.0x...9ccc
>>> {'pricesource': 1, 'desciption': 'EJV'}
>>> 2009-10-01 13:24:25,089 INFO sqlalchemy.engine.base.Engine.0x...9ccc
>>> COMMIT
>>> 2009-10-01 13:24:25,092 INFO sqlalchemy.engine.base.Engine.0x...9ccc
>>> SELECT victor_price_sources.pricesource,
>>> victor_price_sources.desciption
>>> FROM victor_price_sources
>>> WHERE victor_price_sources.desciption = :desciption_1
>>> 2009-10-01 13:24:25,092 INFO sqlalchemy.engine.base.Engine.0x...9ccc
>>> {'desciption_1': 'EJV'}
>>> []
>>> 2009-10-01 13:24:25,105 INFO sqlalchemy.engine.base.Engine.0x...9ccc
>>> SELECT victor_price_sources.pricesource,
>>> victor_price_sources.desciption
>>> FROM victor_price_sources
>>> 2009-10-01 13:24:25,105 INFO sqlalchemy.engine.base.Engine.0x...9ccc
>>> {}
>>> [(1,
>>> 'EJV
>>> ')]
>>
>>> On Oct 1, 10:10 am, "Michael Bayer" <mike...@zzzcomputing.com>  
>>> wrote:
>>>> volx wrote:
>>
>>>>> Micheal:
>>
>>>>> Thanks for your help thus far. Unfortunately I don't think the
>>>>> get_dbapi_type method gets called. From reading your code I see  
>>>>> how it
>>>>> should be called from pre_exec -> setinputsizes ->  
>>>>> get_dbapi_type but
>>>>> empirical evidence shows otherwise. Here's my little harness:
>>
>>>>> import sqlalchemy.types as sqltypes
>>>>> from exceptions import NotImplementedError
>>
>>>>> class OracleChar(sqltypes.CHAR):
>>>>>     """Patched OracleChar type to fix padding issue
>>>>>
>>>>  http://groups.google.com/group/sqlalchemy/browse_thread/thread/965287 
>>>> ...
>>>>>     """
>>>>>     def get_col_spec(self):
>>>>>         return "CHAR(%(length)s)" % {'length' : self.length}
>>
>>>>>     def get_dbapi_type(self, dbapi):
>>>>>         raise NotImplementedError('gotcha')
>>>>>         return dbapi.FIXED_CHAR
>>
>>>>> from sqlalchemy.databases import oracle
>>>>> oracle.OracleChar = OracleChar
>>
>>>>> from sqlalchemy.ext.sqlsoup import SqlSoup
>>
>>>>> dburl = "oracle://....."
>>>>> soup = SqlSoup(dburl)
>>>>> all = soup.price_sources.filter_by(desciption='EJV').all()
>>>>> print all  # shows empty list, we should not even get here due to
>>>>> exception I raise
>>
>>>>> all = soup.price_sources.filter_by
>>>>> (desciption='EJV
>>>>> ').all()
>>>>> print all  # shows record in list
>>
>>>>> Returns empty list.
>>
>>>> SqlSoup uses table reflection and in this case you'd have to  
>>>> ensure that
>>>> the column in question is assigned the CHAR datatype, which its  
>>>> very
>>>> likely that it's not.    I would start by using a hand- 
>>>> constructed Table
>>>> object and testing the behavior against that using simple select()
>>>> constructs.
>>
>>>>> On Sep 30, 3:44 pm, "Michael Bayer" <mike...@zzzcomputing.com>  
>>>>> wrote:
>>>>>> volx wrote:
>>
>>>>>>> cx_Oracle actually has thread on that a topic at
>>>>>>> http://sourceforge.net/mailarchive/message.php?msg_id=47BED8B8.3983.0 
>>>>>>> ...
>>
>>>>>>> It boils down to having to specify a type for input parameter.  
>>>>>>> Is
>>>> that
>>>>>>> something I could "plug-in" as a decorator or would that need  
>>>>>>> to be
>>>>>>> hacked on SqlAlchemy level?
>>
>>>>>> here is the thing.  We call setinputsizes() with oracle.  So  
>>>>>> perhaps
>>>>>> OracleChar() needs to have dbapi.FIXED_CHAR established as its
>>>>>> get_dbapi_type().   would you care to try monkeypatching this (in
>>>> 0.5,
>>>>>> the
>>>>>> fix for 0.6 would be different) ?
>>
>>>>>> class OracleChar(sqltypes.CHAR):
>>>>>>     def get_col_spec(self):
>>>>>>         return "CHAR(%(length)s)" % {'length' : self.length}
>>
>>>>>>     def get_dbapi_type(self, dbapi):
>>>>>>         return dbapi.FIXED_CHAR
>>
>>>>>> from sqlalchemy.databases import oracle
>>>>>> oracle.OracleChar = OracleChar
>>
>>>>>>> Here's how:
>>
>>>>>>> In [70]: query = "select * from price_sources where description
>>>>>>> = :someparam"
>>>>>>> In [71]: cursor.setinputsizes(someparam=cx_Oracle.FIXED_CHAR)
>>>>>>> Out[71]: {'someparam': <cx_Oracle.FIXED_CHAR with value None>}
>>>>>>> In [72]: cursor.execute(query, dict(someparam='EJV')).fetchall()
>>>>>>> Out[72]:
>>>>>>> [(149,
>>
>>>>>>> 'EJV
>>>>>>> ')]
>>
>>>>>>> Any help would be appreciated.
>>
>>>>>>> On Sep 30, 2:36 pm, volx <victor.o...@gmail.com> wrote:
>>>>>>>> I have just now and it looks that this post probably belongs on
>>>>>>>> cx_Oracle mailing list.
>>
>>>>>>>> In [47]: cursor.execute("select * from price_sources where
>>>> desciption
>>>>>>>> = :someparam", dict(someparam='EJV')).fetchall()
>>>>>>>> Out[47]: []
>>>>>>>> In [49]: cursor.execute("select * from price_sources where
>>>> desciption
>>>>>>>> = :someparam", dict
>>>>>>>> (someparam='EJV
>>>>>>>> ')).fetchall()
>>>>>>>> Out[49]:
>>>>>>>> [(149,
>>
>>>>>>>> 'EJV
>>>>>>>> ')]
>>
>>>>>>>> On Sep 30, 2:18 pm, Michael Bayer <mike...@zzzcomputing.com>
>>>> wrote:
>>
>>>>>>>>> On Sep 30, 2:07 pm, volx <victor.o...@gmail.com> wrote:
>>
>>>>>>>>>> Hello all:
>>
>>>>>>>>>> Consider table:
>>
>>>>>>>>>> CREATE TABLE PRICE_SOURCES (
>>>>>>>>>>    ID decimal(22) PRIMARY KEY NOT NULL,
>>>>>>>>>>    DESCRIPTION char(100) NOT NULL
>>>>>>>>>> )
>>
>>>>>>>>>> and this screen dump from ipython session:
>>
>>>>>>>>>> In [28]: import cx_Oracle
>>>>>>>>>> In [29]: from sqlalchemy.ext.sqlsoup import SqlSoup
>>>>>>>>>> In [30]: con = cx_Oracle.connect('myuser/myp...@mydb')
>>>>>>>>>> In [31]: cursor = con.cursor()
>>>>>>>>>> In [32]: cursor.execute("select * from price_sources where
>>>>>>>> description
>>>>>>>>>> = 'EJV'").fetchall()
>>>>>>>>>> Out[32]:
>>>>>>>>>> [(149,
>>
>>>>>>>>>> 'EJV
>>>>>>>>>> ')]
>>
>>>>>>>>>> Note that the result has padding in the column "description"
>>>> as
>>>>>> it
>>>>>>>> is
>>>>>>>>>> of type CHAR(100), with is fixed length.
>>
>>>>>>>>>> Now, let's do the same in SqlAlchemy.
>>
>>>>>>>>>> In [33]: soup = SqlSoup('oracle://myuser:myp...@mydb')
>>>>>>>>>> In [34]: soup.price_sources.filter_by(description='EJV').all 
>>>>>>>>>> ()
>>>>>>>>>> Out[34]: []
>>
>>>>>>>>>> As you can see it returns zero rows. Only after I pad the
>>>>>> parameter
>>>>>>>>>> for where clause will I get my row back:
>>
>>>>>>>>>> In [35]: soup.price_sources.filter_by
>>>>>>>>>> (desciption='EJV
>>>>>>>>>> ').all()
>>>>>>>>>> Out[35]: [MappedPrice_sources(pricesource=Decimal
>>>>>>>>>> ('149'),desciption='EJV
>>>>>>>>>> ')]
>>
>>>>>>>>>> The same behavior happens even if I use ORM and regardless
>>>>>> whether
>>>>>>>> the
>>>>>>>>>> column metadata is defined with String, OracleString or CHAR.
>>
>>>>>>>>>> I can understand the query result being padded as it is
>>>>>> consistent
>>>>>>>>>> with
>>
>> ...
>>
>> read more ยป
> >


--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google Groups 
"sqlalchemy" group.
To post to this group, send email to sqlalchemy@googlegroups.com
To unsubscribe from this group, send email to 
sqlalchemy+unsubscr...@googlegroups.com
For more options, visit this group at 
http://groups.google.com/group/sqlalchemy?hl=en
-~----------~----~----~----~------~----~------~--~---

Reply via email to