[PEAK] DB type conversion (for inputs to execute() and callproc())

Ian Bicking ianb at colorstudy.com
Tue Dec 16 18:48:08 EST 2003


A difficult case to consider: supporting boolean columns, for Postgres, 
MySQL, and Python, where each has very different notions of boolean.  
MySQL is probably just TINYINT, using 0 and 1 (there's no formal type). 
  Postgres has a BOOLEAN column, that accepts 't'/'f', or '1'/'0', but 
not 1/0 (just to mess with your head).  Python of course has a looser 
notion of booleans, with many possible true and false values.

So declaring a column in your model to be boolean can effect several 
layers, which should be accounted for.  And all for such a seemingly 
simple type.  Important, though -- having worked with code that used 
't' and 'f' for booleans (because of database interaction), I can say 
that such situations are best to be avoided.

On Dec 16, 2003, at 5:33 PM, Phillip J. Eby wrote:
> Currently, peak.storage.SQL supports automatic type conversion on all 
> data *received from* the database.  It can do this because, for a 
> given database type there is arguably one "best" Python type to render 
> that value as, for the given application.
>
> However, it does not currently support type conversion for values that 
> are being *sent to* the database.  One must manually render the values 
> as the correct datatype (using e.g. 'db.API.DateFromTicks()'), and 
> then either convert them to strings and embed them in SQL, or supply 
> them as parameters to the cursor.execute() method.
>
> But for stored procedures, this will be inconvenient at best.  One of 
> the goals for stored procedures is that they should be able to accept 
> application types (such as the Python 2.3 'datetime' types included 
> with PEAK) and convert them to the necessary database type(s).
>
> Therefore, it would seem a conversion mechanism is necessary.  Here 
> are the scenarios I think it might need to cover:
>
> * convert this Python object to "something that works with the DB" in 
> a general sense (i.e., the specific DB target type is unknown)
>
> * convert to a DBAPI-level type, such as NUMBER, STRING, BINARY, etc.
>
> * convert to a specific DB native type such as NUMBER(10,2) or some 
> such.
>
> The first item is critical.  The second may be needed to eliminate 
> conversion ambiguities for stored procedure arguments.  The third item 
> seems like a YAGNI, especially since correct implementation could be 
> complex.  (And I didn't even list supporting per-connection 
> configuration for conversions, since the assumption here is that a 
> given Python type should have a single unambiguous representation for 
> a given DB backend.)
>
> So, what I propose is to create a 'types' attribute on DB connections, 
> such that e.g. 'db.types.NUMBER' would represent the protocol to adapt 
> to to get a "number" value from some python type.  In addition to the 
> DBAPI types supported by that driver, there would also be an '_ANY_' 
> protocol.
>
> These protocols would be implemented as 'protocols.protocolForURI()' 
> objects within a driver-specific namespace.  For databases with 
> multiple drivers (like Oracle and PostgreSQL), I'm not sure if we'll 
> have any overlap.  If so, we may create a URI namespace for the 
> "generic" DB, and then have implied protocols for the specific 
> drivers.
>
> Anyway, by using protocolForURI(), it means that application code can 
> declare adapters for specific backends without those backends needing 
> to be installed.  That is, if I want to declare an adapter from my 
> application's date class to "DCOracle2 datetime", I can do so by using 
> the URI for "DCOracle2 datetime", whether DCOracle2 is installed or 
> not.  (Of course, you could use the 'whenImported()' function to set 
> up a hook to do this, too, but I think that's more awkward.)
>
> In order to simplify the declaration process, I'll probably have 
> peak.storage.SQL export a utility function like 
> 'storage.dbType(driver,type="_ANY_")' that returns the right 
> protocolForURI() object.  Thus, one would declare one's adapter as 
> providing e.g. 'storage.dbType("DCOracle2","DATETIME")' to declare an 
> adapter to DCOracle2's DATETIME DBAPI type.  And, the 'types' 
> attribute of a db connection would simply set its attributes to the 
> dbType() objects for the connection's driver.
>
> Finally, functions that wanted to convert a Python object to a 
> suitable object for the database type would use 'adapt(anObject, 
> db.types.DBAPITYPENAME)' to get an object suitable for passing as an 
> execute() or callproc() parameter value.
>
> Comments, anyone?
>
> _______________________________________________
> PEAK mailing list
> PEAK at eby-sarna.com
> http://www.eby-sarna.com/mailman/listinfo/peak
>
--
Ian Bicking | ianb at colorstudy.com | http://blog.ianbicking.org




More information about the PEAK mailing list