dolibarr-mysql2pgsql.pl 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361
  1. #!/usr/bin/perl -w
  2. #------------------------------------------------------------------------------
  3. # Ce script est une version modifiee de mysql2pgsql afin de:
  4. # - gerer les base mysql innodb
  5. # - traiter tous les fichiers mysql/data/*.sql vers pgsql/data
  6. # - gerer les autoincrement en SERIAL plutot qu'en sequenceurs
  7. # - utiliser le CHECK plutot que des sous-tables pour les types enum
  8. # - corriger de nombreux bugs
  9. #
  10. # Regle ecriture de fichier sql portables
  11. # Pour les cles autoincrement: rowid integer AUTO_INCREMENT PRIMARY KEY,
  12. # Mettre les index dans fichier.key.sql
  13. #------------------------------------------------------------------------------
  14. use Data::Dumper;
  15. use Getopt::Long;
  16. use strict;
  17. use vars qw/ $DIR $PROG $Extension $SOURCE $DESTI %filelist $stop /;
  18. # command line options
  19. my( $opt_debug, $opt_help);
  20. # general values
  21. my ($out, $size);
  22. # variables for constructing pre-create-table entities
  23. my $create_sql=''; # if empty we are not making a create statement
  24. my $create_index=''; # if empty we are not making a create statement
  25. my %enum_datafield=(); # holds enumeration choices
  26. my (@column_values,$enum_column, $seq);
  27. my $table="";
  28. #------------------------------------------------------------------------------
  29. # MAIN
  30. #------------------------------------------------------------------------------
  31. ($DIR=$0) =~ s/([^\/\\]+)$//; ($PROG=$1) =~ s/\.([^\.]*)$//; $Extension=$1;
  32. $DIR||='.'; $DIR =~ s/([^\/\\])[\\\/]+$/$1/;
  33. $SOURCE="$DIR/install/mysql/tables";
  34. $DESTI="$DIR/install/pgsql/tables";
  35. # Recherche tous les fichiers .sql
  36. opendir(DIR, $SOURCE);
  37. foreach my $file (readdir(DIR)) {
  38. if ($file =~ /\.sql$/ && -f "$SOURCE/$file") {
  39. print "Found file $file\n";
  40. $filelist{$file}=1;
  41. }
  42. }
  43. closedir(DIR);
  44. # Boucle sur tous les fichiers de SOURCE
  45. #---------------------------------------
  46. foreach my $file (keys %filelist) {
  47. $ARGV[0]="$SOURCE/$file";
  48. $ARGV[1]="$DESTI/$file";
  49. print "Convert file $ARGV[0] into $ARGV[1]\n";
  50. # MySQL to PostgreSQL dump file converter
  51. #
  52. # For usage: perl mysql2pgsql.perl --help
  53. #
  54. # homepage: http://www.rot13.org/~dpavlin/projects.html
  55. # 1999-12-15 DbP -- Dobrica Pavlinusic <dpavlin@rot13.org>
  56. # 1999-12-26 DbP don't make serial from auto_increment, create all manually
  57. # (to set start value right)
  58. # 2000-01-11 DbP now creates sequences with correct value
  59. # 2000-04-25 DbP import into CVS (at cvs.linux.hr)
  60. # 2001-01-29 tpo -- Tomas Pospisek <tpo@sourcepole.ch>:
  61. # 1) make script comply to usage:
  62. # 2) make script output to STDOUT instead of STERR
  63. # 3) change verbosity behaveour
  64. # 4) add debug option
  65. # see rest of changelog at http://cvs.linux.hr/cvsweb.cgi/sql/mysql2pgsql
  66. # 2003-12-16 jsp -- Joe Speigle <joe.speigle@jklh.us>:
  67. # converts: s/\) *Type=MyISAM;/);/i, enum data type -> references,
  68. # auto_increment->sequences
  69. # 2004-01-13 jsp -- moved project to gborg; both the above declined ownership
  70. # 2004-06-29 converts: year(4), year(2)
  71. # homepage: gborg.postgresql.org
  72. GetOptions("debug", "help");
  73. my $DEBUG = $opt_debug || 0;
  74. my $HELP = $opt_help || 0;
  75. if (($HELP) || ! defined($ARGV[0]) || ! defined($ARGV[1])) {
  76. print "Usage: perl $0 {--verbose|--help|--debug} mysql_dump_file.sql pg_dump_file.sql\n";
  77. print "\t* OPTIONS\n";
  78. print "\t--verbose tees to pg_dump_file.sql and STDOUT during conversion\n";
  79. print "\t--debug does ?? \n";
  80. print "\t--help prints this message \n";
  81. print "\t* REQUIRED ARGUMENTS\n";
  82. if (defined ($ARGV[0])) {
  83. print "\tmysql_dump_file.sql ($ARGV[0])\n";
  84. } else {
  85. print "\tmysql_dump_file.sql (undefined)\n";
  86. }
  87. if (defined ($ARGV[1])) {
  88. print "\tpg_dump_file.sql ($ARGV[1])\n";
  89. } else {
  90. print "\tpg_dump_file.sql (undefined)\n";
  91. }
  92. exit 1;
  93. }
  94. open(IN,"<$ARGV[0]") || die "can't open mysql dump file $ARGV[0]";
  95. open(OUT,">$ARGV[1]") || die "can't open pg dump file $ARGV[1]";
  96. print OUT "-- Generated by $PROG\n";
  97. print OUT "-- (c) 2004, PostgreSQL Inc.\n";
  98. print OUT "-- (c) 2005, Laurent Destailleur.\n";
  99. print OUT "\n";
  100. # Output for create table and create index
  101. sub output_create {
  102. # If command ends with "xxx,);", we change to "xxx);"
  103. $create_sql =~ s/,(\s*)\);/$1\);/m;
  104. # If command ends with "xxx, -- yyy );", we change to "xxx -- yyy);"
  105. $create_sql =~ s/,(\s*\-\-[^\)\n]*)(\s*)\);/$1\n\);/m;
  106. print OUT $create_sql;
  107. if ($create_index) {
  108. print OUT "\n";
  109. print OUT $create_index;
  110. }
  111. }
  112. # Reset when moving from each "create table" to "insert" part of dump
  113. sub reset_vars() {
  114. $create_sql="";
  115. $create_index="";
  116. %enum_datafield=();
  117. $enum_column='';
  118. }
  119. # Boucle sur contenu fichier source
  120. #----------------------------------
  121. while(<IN>) {
  122. # comments or empty lines
  123. if (/^-- \$Id/) {
  124. $_ =~ s/\$//g;
  125. print OUT $_;
  126. next;
  127. }
  128. # comments or empty lines
  129. if (/^#/ || /^$/ || /^--/) {
  130. print OUT $_;
  131. next;
  132. }
  133. if (/^USE\s*([^;]*);/) {
  134. print OUT "\\c ". $1;
  135. next;
  136. }
  137. if ($create_sql ne "") { # we are inside create table statement so lets process datatypes
  138. if (/\);/i) { # end of create table squence
  139. $create_sql =~ s/,$//g; # strip last , inside create table
  140. &output_create;
  141. &reset_vars();
  142. next;
  143. # LDR Added "innodb" and "engine"
  144. }
  145. elsif (/(ISAM|innodb)/i) { # end of create table sequence
  146. s/\) *type=(MyISAM|innodb);/);/i;
  147. s/\) *engine=(MyISAM|innodb);/);/i;
  148. $create_sql =~ s/,$//g; # strip last , inside create table
  149. $create_sql .= $_;
  150. &output_create;
  151. &reset_vars();
  152. next;
  153. }
  154. # enum -> check
  155. if (/([\w\"]*)\s+enum\s*\(((?:['"][\?\w]+['"]\s*,)+['"][\?\w]+['"])\)(.*)$/i) {
  156. $enum_column=$1;
  157. $enum_datafield{$enum_column}=$2; # 'abc','def', ...
  158. my $suite=$3;
  159. my $maxlength=0;
  160. foreach my $enum (split(',',$enum_datafield{$enum_column})) {
  161. $enum =~ s/[\"\']//g;
  162. if ($maxlength<length($enum)) { $maxlength=length($enum); }
  163. }
  164. $enum_datafield{$enum_column} =~ s/\"/\'/g;
  165. $_ = qq~ $enum_column CHAR($maxlength) CHECK ($enum_column IN ($enum_datafield{$enum_column})) $suite\n~;
  166. # int, auto_increment -> serial
  167. } elsif (/^[\s\t]*(\w*)\s*.*int.*auto_increment/i) {
  168. $seq = qq~${table}_${1}_seq~;
  169. s/[\s\t]*([a-zA-Z_0-9]*)\s*.*int.*auto_increment[^,]*/ $1 SERIAL PRIMARY KEY/ig;
  170. $create_sql.=$_;
  171. next;
  172. # int type conversion
  173. } elsif (/(\w*)int\(\d+\)/i) {
  174. $size=$1;
  175. $size =~ tr [A-Z] [a-z];
  176. if ($size eq "tiny" || $size eq "small") {
  177. $out = "int2";
  178. } elsif ($size eq "big") {
  179. $out = "int8";
  180. } else {
  181. $out = "int4";
  182. }
  183. s/\w*int\(\d+\)/$out/g;
  184. }
  185. # tinyint -> smallint
  186. elsif (/tinyint/i) {
  187. s/tinyint/smallint/g;
  188. }
  189. # nuke unsigned
  190. s/(int\w+|smallint)\s+unsigned/$1/gi;
  191. # blob -> text
  192. s/\w*blob/text/gi;
  193. # tinytext/mediumtext -> text
  194. s/tinytext/text/gi;
  195. s/mediumtext/text/gi;
  196. # char -> varchar
  197. # PostgreSQL would otherwise pad with spaces as opposed
  198. # to MySQL! Your user interface may depend on this!
  199. s/(\s+)char/${1}varchar/gi;
  200. # nuke date representation (not supported in PostgreSQL)
  201. s/datetime default '[^']+'/datetime/i;
  202. s/date default '[^']+'/datetime/i;
  203. s/time default '[^']+'/datetime/i;
  204. # change not null datetime field to null valid ones
  205. # (to support remapping of "zero time" to null
  206. s/datetime not null/datetime/i;
  207. s/datetime/timestamp/i;
  208. # nuke size of timestamp
  209. s/timestamp\([^)]*\)/timestamp/i;
  210. # double -> numeric
  211. s/^double/numeric/i;
  212. s/(\s*)double/${1}numeric/i;
  213. # float -> numeric
  214. s/^float/numeric/i;
  215. s/(\s*)float/${1}numeric/i;
  216. # unique key(field1,field2)
  217. if (/unique key\s*\((\w+\s*,\s*\w+)\)/i) {
  218. s/unique key\s*\((\w+\s*,\s*\w+)\)/UNIQUE\($1\)/i;
  219. $create_sql.=$_;
  220. next;
  221. }
  222. # unique index(field1,field2)
  223. if (/unique index\s*\((\w+\s*,\s*\w+)\)/i) {
  224. s/unique index\s*\((\w+\s*,\s*\w+)\)/UNIQUE\($1\)/i;
  225. $create_sql.=$_;
  226. next;
  227. }
  228. # unique key [name] (field)
  229. if (/unique key\s*(\w*)\s*\((\w+)\)/i) {
  230. s/unique key\s*(\w*)\s*\((\w+)\)/UNIQUE\($2\)/i;
  231. my $idxname=($1?"$1":"idx_${table}_$2");
  232. $create_sql.=$_;
  233. $create_index .= "CREATE INDEX $idxname ON $table ($2);\n";
  234. next;
  235. }
  236. # unique index [name] (field)
  237. if (/unique index\s*(\w*)\s*\((\w+)\)/i) {
  238. s/unique index\s*(\w*)\s*\((\w+)\)/UNIQUE\($2\)/i;
  239. my $idxname=($1?"$1":"idx_${table}_$2");
  240. $create_sql.=$_;
  241. $create_index .= "CREATE INDEX $idxname ON $table ($2);\n";
  242. next;
  243. }
  244. # unique (field) et unique (field1, field2 ...)
  245. if (/unique\s*\(([\w,\s]+)\)/i) {
  246. s/unique\s*\(([\w,\s]+)\)/UNIQUE\($1\)/i;
  247. my $fieldlist="$1";
  248. my $idxname="idx_${table}_${fieldlist}";
  249. $idxname =~ s/\W/_/g; $idxname =~ tr/_/_/s;
  250. $create_sql.=$_;
  251. $create_index .= "CREATE INDEX $idxname ON $table ($fieldlist);\n";
  252. next;
  253. }
  254. # index(field)
  255. if (/index\s*(\w*)\s*\((\w+)\)/i) {
  256. my $idxname=($1?"$1":"idx_${table}_$2");
  257. $create_index .= "CREATE INDEX $idxname ON $table ($2);\n";
  258. next;
  259. }
  260. # primary key
  261. if (/\bkey\b/i && !/^\s+primary key\s+/i) {
  262. s/KEY(\s+)[^(]*(\s+)/$1 UNIQUE $2/i; # hack off name of the non-primary key
  263. }
  264. # key(xxx)
  265. if (/key\s*\((\w+)\)/i) {
  266. my $idxname="idx_${table}_$1";
  267. $create_index .= "CREATE INDEX $idxname ON $table ($1);\n";
  268. next;
  269. }
  270. # Quote column names
  271. s/(^\s*)([^\s\-\(]+)(\s*)/$1"$2"$3/gi if (!/\bkey\b/i);
  272. # Remap columns with names of existing system attribute
  273. if (/"oid"/i) {
  274. s/"oid"/"_oid"/g;
  275. print STDERR "WARNING: table $table uses column \"oid\" which is renamed to \"_oid\"\nYou should fix application manually! Press return to continue.";
  276. my $wait=<STDIN>;
  277. }
  278. s/oid/_oid/i if (/key/i && /oid/i); # fix oid in key
  279. $create_sql.=$_;
  280. } # END of if ($create_sql ne "") i.e. were inside create table statement so processed datatypes
  281. else { # not inside create table
  282. #---- fix data in inserted data: (from MS world)
  283. # FIX: disabled for now
  284. if (00 && /insert into/i) {
  285. s!\x96!-!g; # --
  286. s!\x93!"!g; # ``
  287. s!\x94!"!g; # ''
  288. s!\x85!... !g; # \ldots
  289. s!\x92!`!g;
  290. }
  291. # fix dates '0000-00-00 00:00:00' (should be null)
  292. s/'0000-00-00 00:00:00'/null/gi;
  293. s/'0000-00-00'/null/gi;
  294. s/'00:00:00'/null/gi;
  295. s/([12]\d\d\d)([01]\d)([0-3]\d)([0-2]\d)([0-6]\d)([0-6]\d)/'$1-$2-$3 $4:$5:$6'/;
  296. if (/create\s+table\s+(\w+)/i) {
  297. $create_sql = $_;
  298. /create\s*table\s*(\w+)/i;
  299. $table=$1 if (defined($1));
  300. } else {
  301. print OUT $_;
  302. }
  303. } # end of if inside create_table
  304. } # END while(<IN>)
  305. close IN;
  306. close OUT;
  307. }
  308. print "\n";
  309. print "Build ".(scalar keys %filelist)." file(s).\n";
  310. print "\n";
  311. print "Press a key to finish...\n";
  312. $stop=<STDIN>;
  313. 0;