Converting a Database Schema from MySQL to PostgreSQL

Port your database dump from MySQL to PostgreSQL easily with this PHP script, the article also explains how it works.

The need for portability

You probably have heard of PEAR::DB, you may have even used it, if not let me EXPLAIN - it is a library for database abstraction. PEAR::DB is made to help people port their code to another database backend. That's very good but RDBMS's tend to use different syntaxes and people need to convert the SQL code too.

How it all began

I have used PEAR::DB for a couple of months (7 Jul 2002), and it works perfectly with the MySQL server, so I decided I need to test it against other databases too. As PostgreSQL is a nice Open Source database, a lot more advanced than MySQL, it seemed like a good choice for me.

I installed PostgreSQL FROM sources and it worked, I was able to connect through the bundled client, but this was the moment my problems started. I had a tough time testing different DSN's until I found that I only had to add unix before the database host and separate it with a + FROM it. It seems that the PostgreSQL driver defaults to TCP/IP sockets and the MySQL to Unix as I've always used unix sockets for MySQL.

Converting the schema

The first problem I encountered was that PostgreSQL doesn't like MySQL dumps, so after studying the PostgreSQL docs I came to this translation TABLE that I need to use.

MySQL PostgreSQL
# MySQL comment --ANSI SQL comment
UNIQUE KEY name (cols) UNIQUE (cols)
PRIMARY KEY (cols) the same
KEY name (cols) CREATE INDEX name ON TABLE (cols)
id int NOT NULL auto_increment id SERIAL
binary doesn't exist
enum doesn't exist, convert to varchar
tinyint(n) or smallint(n) smallint
mediumint(n) or int(n) int

MySQL default dumps FROM version 3.23.49 on use the ANSI SQL comment style with a required space after --.

I also found that PostgreSQL doesn't accept dates like 0000-00-00, so I had to strip default values FROM date/datetime columns.

Note: There is no need to convert the defaultINSERT statements as they are compatible with PostgreSQL, only the DATA types are different.

The convertor code

The code is written to be used FROM the commandline not a webserver. I recompiled PHP several times with different configure lines but it always said that $argv is undefined so the filenames are hard-coded.

You'll have to redefine $enum if it doesn't match your needs for a substitution of enum are different.

#!/usr/local/bin/php -q
<?php

$source = "/home/shaggy/shaggy.sql";
$output = "/home/shaggy/pgtest.sql";

$enum = 'varchar(10)'; // convert enum to this

if ( !file_exists($source) ) {
 die("File not found: $sourcen");
}

$fd = fopen($source, "r");
$result = fread($fd, filesize($source));
fclose($fd);

$result = mysql2postgre($result);

$fd = fopen($output, "w");
if (fwrite($fd, $result)) {
 echo "OKn";
} else {
 echo "Failedn";
}
fclose($fd);




function mysql2postgre($source) {
 global $enum;

 $result = $source;
 $result = preg_replace('/Type=MyISAM/i', '', $result);

 // convert line comments
 $result = preg_replace("/#(.*)/", '--$1', $result);
 // and compress newlines
 $result = preg_replace("/n{2,}/", "nn", $result);

 // get rid of proprietary code
 $result = preg_replace("/DROP TABLE IF EXISTSW+.+/i", '', $result);

 // indices
 $result = preg_replace("/(.*)UNIQUE KEY.+((.+))/i",
 "$1UNIQUE ($2)", $result);

 // a little hack to save primary keys
 $result = preg_replace("/(.*)PRIMARY KEY.+((.+))/i",
 "$1PRIMARY ($2)", $result);
 $result = preg_replace("/,n.*KEYW.+((.+))/i",
 "n-- was KEY ($1)", $result);
 $result = preg_replace("/(.*)PRIMARY.+((.+))/i",
 "$1PRIMARY KEY (\2)", $result);

 $result = preg_replace("/(.*?)(w+).+auto_increment/i",
 '$1$2 SERIAL', $result);

 // Postgre doesn't support the binary modifier
 $result = preg_replace('/binary/i', '', $result);

 // type transformations
 $result = preg_replace('/enum(.+)/i', $enum, $result);

 $result = preg_replace('/tinyint(.+)/i', 'smallint', $result);
 $result = preg_replace('/smallint(.+)/i', 'smallint', $result);
 $result = preg_replace('/meduimint(.+)/i', 'int', $result);
 $result = preg_replace('/int(.+)/i', 'int', $result);

 // Most of my default dates are '0000-00-00'
 $result = preg_replace("/datetime(.*) default '.*'/i",
 'datetime$1', $result);
 $result = preg_replace("/date(.*) default '.*'/i",
 'date$1', $result);


 return $result;
}

?>

I am using the PCRE regular expressions instead of POSIX because they are generally faster.

The first regular expression removes the Type=MyISAM identifier that is used in MySQL to set the TABLE type, change it if you use InnoBD or BDB tables.

The code should process any input but it is recommended to use a dump from mysqldump or a similar application.

Comments

enum

I have found an interesting approach to converting ENUM:
(http://archives.postgresql.org/pgsql-sql/2002-03/msg00273.php)
"enum('s','n')
I changed it for:
col1 CHAR CHECK (col1 IN ('s','n'))
Which is SQL. :-)
If what is enumerated are numbres, just use INT instead of CHAR.
Saludos... :-)"

PHP code was a cut & paste for me

Thanks, Martin, migrating from MySQL to PostgreSQL was a breeze thanks to your little swab of code. I just had to change some fields named "user" to "usr".
cheers!

Datetime...

Thanks, this is a great script Martin...
How did you guys handle the "datetime" datatype in postgre?
Jonez

datetime datatype

For others who might come across this page, postgresql's datetime equivalent would be:TIMESTAMP WITHOUT TIME ZONE

TIMESTAMP precision

And to note PostgreSQL's TIMESTAMP precision ranges from 0 to 13, which is out of bounds to MySQL's TIMESTAMP default precision of 14... thats another thing to modify inside the dumps

How do we convert postgresqldatabase to mysql

its very informative
Do u know the conversion from access to postgresql or postgresql to mysql

PostreSQL to MySQL or Access to PostreSQL

Renju it's pretty difficult to convert a PostgreSQL dump into MySQL without losing lots of the definitions. It would also require a lot more processing and may not be exactly a linear process.
As for Access I haven't used it much.

Access to PostgreSQL or MySQL

Go to
http://www.intranet2internet.com/public/default.asp?PAGE=download&ID=DUM...
and get AccessDUMP. It's a freebie that will dump your .mdb to MySQL format. Then just take your resulting .sql file, put on the mysql box, run 'mysql < accessdump.sql', and you are set :)
By the way, I bought the 'Pro' version of AccessDUMP, and give it about 1 star. Don't bother, just stick with the freebie, as there are bugs in the Pro version and the author won't answer any emails (even from a paying customer). The extra features you get for paying aren't worth it IMHO, but hey, the guy's software gets used daily, so I paid up :)

Very useful. Thank you!

Great script. I just had to make a few changes (other than the obvious file names and changing the shebang line to where my bin/php command is.
- The script missed some "mediumint" datatypes. I just changed these to "integer"
- The script did not remove "unsigned" declarations. Wherever these were used was not a problem for me, so I just deleted them and used regular integer types.
- The script inserted a comment part way through a create table command. The comma for the line containing the column definition was placed at the end of the inserted comment (and as a result was ignored), creating a syntax error and the table wasn't created. I just put a comma back where it should have been and everything was fixed.
Thanks a bunch.

Thanks - saved me a load of pain

Needed some extra hacks - e.g. MySQL uses backticks - ` - which can apparently all be deleted, had some problems with BLOBs - but this was still a real timesaver. Thank you!

convert postgresql to mysql

can we convert postgresql to mysql using trigger and view features

it's a good thing if enum names are also given a constraint name and definition.
data-integrity is just so important :P

Hi,

I've modified your script above to make it handle what I get spat out of a MySQL 5 db; it appears to go into PGSQL without any problems (aside from numeric(5,2) perhaps needing to be something better like a decimal??)

Hope it's of some use to someone.

It does the enum conversion to a "VARCHAR CHECK (field IN ('a', 'list', 'here'))" which appears to be ok (at least pgsql allows me to create the table from the code; haven't tried actually using the resultant tables yet.... so it might be rubbish)

http://projects.codepoets.co.uk/ or http://projects.codepoets.co.uk/trac.cgi/browser/scripts/mysql2pgsql.php