David Fetter informed me that he has some changes to a document I wrote back in 2005 about migration tips going from MySQL to PostgreSQL. As such I started to revamping the document, moving from DocBook 4.3 to 4.5 in the process. The result will be located at http://www.in-nomine.org/~asmodai/mysql-to-pgsql.html. I hope to have his comments merged in in the remainder of the week. Feel free to send me updates after it got properly updated with David's notes.
These posts use code from the Trac project. I'm using the question mark notation for in-place variable substitution, this is where you normally use either direct variables or an indirection mechanism.
If you have done SQL before you are familiar with the syntax such as:
SELECT name FROM auth_cookie WHERE cookie = ? AND ipnr = ?;
So, how does one do this with SQLAlchemy?
With SQLAlchemy (SA in short) you first declare a schema within Python:
auth_cookie = Table('auth_cookie', metadata, Column('cookie', String, primary_key=True), Column('name', String, primary_key=True), Column('ipnr', String, primary_key=True), Column('time', Integer))
Next you import this schema (living within Trac as trac/db/schema.py) as follows:
from trac.db.schema import auth_cookie
This allows direct manipulation using direct calls to auth_cookie. So for a SQL select we need to extend our code as follows:
from sqlalchemy import select
This allows us to build an almost equivalent statement as follows:
statement = select([auth_cookie.c.name], auth_cookie.c.cookie==?)
To add the AND clause SA has a very simple function to add into your code:
from sqlalchemy import and_, select
This allows us to extend the previous statement as such:
statement = select([auth_cookie.c.name], and_(auth_cookie.c.cookie==?, auth_cookie.c.ipnr==?)
Similarly there's an or_() function as well, which works exactly the same.
Now the difficulty arose due to the fact this SQL query changed its WHERE-clause depending on an if/else. The regular case was the first statement we created, the other case added the cookie's IP number into the equation. So how to deal with that?
statement = select([auth_cookie.c.name], auth_cookie.c.cookie==?) if self.check_ip: statement.append_whereclause(and_(auth_cookie.c.ipnr==?))
As you can see, depending on whether or not check_ip is set it changes the statement in-place and expands the WHERE-clause with an AND for ipnr.
One thing I never understood is why MySQL insists on creating a table with 'latin1_swedish_ci' as 'collation'. Now, this does more than just collation, it specifies encoding, collation order, and case sensitivity. That's not the issue, but why, oh why, does it insist on making this the default? What is wrong with actually using UTF-8? I mean, MySQL is only used across the world, which means the geographic spread when it comes to character sets would be served by actually having a default that could handle those languages! A missed chance if you ask me.