~vcs-imports/mammoth-replicator/trunk

« back to all changes in this revision

Viewing changes to contrib/mysql/my2pg.pl

  • Committer: alvherre
  • Date: 2005-12-16 21:24:52 UTC
  • Revision ID: svn-v4:db760fc0-0f08-0410-9d63-cc6633f64896:trunk:1
Initial import of the REL8_0_3 sources from the Pgsql CVS repository.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
#!/usr/bin/perl
 
2
 
 
3
#
 
4
# My2Pg: MySQL to PostgreSQL dump conversion utility
 
5
#
 
6
# (c) 2000,2001 Maxim Rudensky  <fonin@ziet.zhitomir.ua>
 
7
# (c) 2000 Valentine Danilchuk  <valdan@ziet.zhitomir.ua>
 
8
# All right reserved.
 
9
#
 
10
# Redistribution and use in source and binary forms, with or without
 
11
# modification, are permitted provided that the following conditions
 
12
# are met:
 
13
# 1. Redistributions of source code must retain the above copyright
 
14
#    notice, this list of conditions and the following disclaimer.
 
15
# 2. Redistributions in binary form must reproduce the above copyright
 
16
#    notice, this list of conditions and the following disclaimer in the
 
17
#    documentation and/or other materials provided with the distribution.
 
18
# 3. All advertising materials mentioning features or use of this software
 
19
#    must display the following acknowledgement:
 
20
# This product includes software developed by the Max Rudensky
 
21
# and its contributors.
 
22
# 4. Neither the name of the author nor the names of its contributors
 
23
#    may be used to endorse or promote products derived from this software
 
24
#    without specific prior written permission.
 
25
 
26
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
 
27
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 
28
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 
29
# ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
 
30
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 
31
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 
32
# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 
33
# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 
34
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 
35
# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 
36
# SUCH DAMAGE.
 
37
#
 
38
# $My2pg: my2pg.pl,v 1.28 2001/12/06 19:32:20 fonin Exp $
 
39
# $Id: my2pg.pl,v 1.13 2004-04-19 23:18:12 momjian Exp $
 
40
 
 
41
# Custom patch
 
42
# Revision 1.9  2002/08/22 00:01:39  tgl
 
43
# Add a bunch of pseudo-types to replace the behavior formerly associated
 
44
# with OPAQUE, as per recent pghackers discussion.  I still want to do some
 
45
# more work on the 'cstring' pseudo-type, but I'm going to commit the bulk
 
46
# of the changes now before the tree starts shifting under me ...
 
47
 
 
48
#
 
49
# $Log: my2pg.pl,v $
 
50
# Revision 1.13  2004-04-19 23:18:12  momjian
 
51
# Update to my2pg version 1.28, add docs, update URL for newest version.
 
52
#
 
53
# Create diff of custom changes Tom made to the utility for CREATE
 
54
# FUNCTION.
 
55
#
 
56
# This will make moving this utility out of CVS easier.
 
57
#
 
58
# Revision 1.12  2004/04/19 23:11:49  momjian
 
59
# Update to my2pg 1.28, from:
 
60
#
 
61
#       http://www.omnistarinc.com/~fonin/downloads.php#my2pg
 
62
#
 
63
# Revision 1.28  2002/11/30 12:03:48  fonin
 
64
# PostgreSQL does not support indexes on the partial length of column,
 
65
# e.g.
 
66
# CREATE INDEX i_index ON table (column(16));
 
67
# will not work. Fixed.
 
68
#
 
69
# Added command-line option -s that prevents my2pg from attempting convert
 
70
# the data (currently only timestamps).
 
71
#
 
72
# Better timestamps conversion.
 
73
#
 
74
# Revision 1.27  2002/07/16 14:54:07  fonin
 
75
# Bugfix - didn't quote the fields inside PRIMARY KEY with -d option.
 
76
# Fix by Milan P. Stanic <mps@rns-nis.co.yu>.
 
77
#
 
78
# Revision 1.26  2002/07/14 10:30:27  fonin
 
79
# Bugfix - MySQL keywords inside data (INSERT INTO sentence) were replaced
 
80
# with Postgres keywords and therefore messed up the data.
 
81
#
 
82
# Revision 1.25  2002/07/05 09:20:25  fonin
 
83
# - fixed data that contains two consecutive timestamps - thanks to
 
84
#   Ben Darnell <bdarnell@google.com>
 
85
# - word 'default' was converted to upper case inside the data - fixed.
 
86
#   Thanks to Madsen Wikholm <madsen@iki.fi>
 
87
#
 
88
# Revision 1.24  2002/04/20 14:15:43  fonin
 
89
# Patch by Felipe Nievinski <fnievinski@terra.com.br>.
 
90
# A table I was re-creating had a composite primary key, and I was using
 
91
# the -d switch to maintain the table and column names
 
92
# adding double quotes around them.
 
93
#
 
94
# The SQL code generated was something like this:
 
95
#
 
96
# CREATE TABLE "rinav" (
 
97
#    "UnidadeAtendimento" INT8 DEFAULT '0' NOT NULL,
 
98
#    "NumeroRinav" INT8 DEFAULT '0' NOT NULL,
 
99
# -- ...
 
100
#    PRIMARY KEY ("UnidadeAtendimento"," NumeroRinav")
 
101
# );
 
102
#
 
103
# Please note the space inside the second column name string in the PK
 
104
# definition. Because of this PostgreSQL was not able to create the table.
 
105
#
 
106
# FIXED.
 
107
#
 
108
# Revision 1.23  2002/02/07 22:13:52  fonin
 
109
# Bugfix by Hans-Juergen Schoenig <hs@cybertec.at>: additional space after
 
110
# FLOAT8 is required.
 
111
#
 
112
# Revision 1.22  2001/12/06 19:32:20  fonin
 
113
# Patch: On line 594 where you check for UNIQUE, I believe the regex should try
 
114
# and match 'UNIQUE KEY'. Otherwise it outputs no unique indexes for the
 
115
# postgres dump.
 
116
# Thanks to Brad Hilton <bhilton@vpop.net>
 
117
#
 
118
# Revision 1.21  2001/08/25 18:55:28  fonin
 
119
# Incorporated changes from Yunliang Yu <yu@math.duke.edu>:
 
120
# - By default table & column names are not quoted; use the new
 
121
#   "-d" option if you want to,
 
122
# - Use conditional substitutions to speed up and preserve
 
123
#   the data integrity.
 
124
# Fixes by Max:
 
125
# - timestamps conversion fix. Shouldn't break now matching binary data and
 
126
# strings.
 
127
#
 
128
# Revision 1.21  2001/07/23 03:04:39  yu
 
129
# Updates & fixes by Yunliang Yu <yu@math.duke.edu>
 
130
# . By default table & column names are not quoted; use the new
 
131
#   "-d" option if you want to,
 
132
# . Use conditional substitutions to speed up and preserve
 
133
#   the data integrity.
 
134
#
 
135
# Revision 1.20  2001/07/05 12:45:05  fonin
 
136
# Timestamp conversion enhancement from Joakim Lemstr�m <jocke@bytewize.com>
 
137
#
 
138
# Revision 1.19  2001/05/07 19:36:38  fonin
 
139
# Fixed a bug in quoting  PRIMARY KEYs, KEYs and UNIQUE indexes with more than 2 columns. Thanks to Jeff Waugh <jaw@ic.net>.
 
140
#
 
141
# Revision 1.18  2001/03/06 22:25:40  fonin
 
142
# Documentation up2dating.
 
143
#
 
144
# Revision 1.17  2001/03/04 13:01:50  fonin
 
145
# Fixes to make work it right with MySQL 3.23 dumps. Tested on mysqldump 8.11.
 
146
# Also, AUTO_INCREMENT->SERIAL fields no more have DEFAULT and NOT NULL 
 
147
# definitions.
 
148
#
 
149
# Revision 1.16  2001/02/02 08:15:34  fonin
 
150
# Sequences should be created BEFORE creating any objects \nthat depends on it.
 
151
#
 
152
# Revision 1.15  2001/01/30 10:13:36  fonin
 
153
# Re-released under BSD-like license.
 
154
#
 
155
# Revision 1.14  2000/12/18 20:55:13  fonin
 
156
# Better -n implementation.
 
157
#
 
158
# Revision 1.13  2000/12/18 15:26:33  fonin
 
159
# Added command-line options. -n forces *CHAR DEFAULT '' NOT NULL to be 
 
160
# converted to *CHAR NULL.
 
161
# AUTO_INCREMENT fields converted not in SERIAL but in 
 
162
# INT* NOT NULL DEFAULT nextval('seqname').
 
163
# Documentation refreshed.
 
164
# Dump enclosed in single transaction from now.
 
165
#
 
166
# Revision 1.12  2000/12/14 20:57:15  fonin
 
167
# Doublequotation bug fixed (in CREATE INDEX ON TABLE (field1,field2))
 
168
#
 
169
# Revision 1.10  2000/11/27 14:18:22  fonin
 
170
# Fixed bug - occasionaly was broken CREATE SEQUENCE generation
 
171
#
 
172
# Revision 1.8  2000/11/24 15:24:16  fonin
 
173
# TIMESTAMP fix: MySQL output YYYYMMDDmmhhss to YYYYMMDD mmhhss
 
174
#
 
175
# Revision 1.7  2000/11/22 23:04:41  fonin
 
176
# TIMESTAMP field fix. Better doublequoting. Splitting output dump
 
177
# into 2 transactions - create/load/indexing first, sequence setvals then. 
 
178
# Added POD documentation.
 
179
#
 
180
#
 
181
 
 
182
use Getopt::Std;
 
183
 
 
184
my %opts;               # command line options
 
185
my $chareg='';          # CHAR conversion regexps
 
186
my $dq=''; # double quote
 
187
 
 
188
# parse command line
 
189
getopts('nhds',\%opts);
 
190
 
 
191
# output syntax
 
192
if($opts{h} ne '') {
 
193
    usage();
 
194
    exit;
 
195
}
 
196
 
 
197
# convert CHAR types from NOT NULL DEFAULT '' to NULL
 
198
if($opts{n} ne '') {
 
199
    $chareg='\s*?(default\s*?\'\')*?\s*?not\s*?null';
 
200
}
 
201
# want double quotes
 
202
if($opts{d} ne '') {
 
203
    $dq='"';
 
204
}
 
205
 
 
206
if($opts{s} ne '') {
 
207
    $safe_data_conv=1;
 
208
}
 
209
else {
 
210
    $safe_data_conv=0;
 
211
}
 
212
 
 
213
$|=1;
 
214
 
 
215
print("------------------------------------------------------------------");
 
216
print("\n-- My2Pg 1.28 translated dump");
 
217
print("\n--");
 
218
print("\n------------------------------------------------------------------");
 
219
 
 
220
print("\n\nBEGIN;\n\n\n");
 
221
 
 
222
my %index;              # contains array of CREATE INDEX for each table
 
223
my %seq;                # contains CREATE SEQUENCE for each table
 
224
my %primary;            # contains primary (eg SERIAL) fields for each table
 
225
my %identifier;         # contains generated by this program identifiers
 
226
my $j=-1;               # current position in $index{table}
 
227
my @check;              # CHECK constraint for current
 
228
 
 
229
# generating full path to libtypes.c
 
230
my $libtypesource='libtypes.c';
 
231
my $libtypename=`pwd`;
 
232
chomp($libtypename);
 
233
$libtypename.='/libtypes.so';
 
234
 
 
235
# push header to libtypes.c
 
236
open(LIBTYPES,">$libtypesource");
 
237
print LIBTYPES "/******************************************************";
 
238
print LIBTYPES "\n * My2Pg 1.27 \translated dump";
 
239
print LIBTYPES "\n * User types definitions";
 
240
print LIBTYPES "\n ******************************************************/";
 
241
print LIBTYPES "\n\n#include <postgres.h>\n";
 
242
print LIBTYPES "\n#define ADD_COMMA if(strcmp(result,\"\")!=0) strcat(result,\",\")\n";
 
243
 
 
244
# reading STDIN...
 
245
my $tabledef=0; # we are outside a table definition
 
246
while (<>) {
 
247
    if(!$tabledef && /^CREATE TABLE \S+/i){
 
248
        $tabledef=1;
 
249
    } elsif($tabledef && /^\) type=\w*;/i){ # /^\w/i
 
250
        $tabledef=0;
 
251
    }
 
252
        
 
253
# Comments start with -- in SQL
 
254
    if(/^#/) {# !/insert into.*\(.*#.*\)/i, in mysqldump output
 
255
        s/#/--/;
 
256
    }
 
257
 
 
258
    if($tabledef) {
 
259
# Convert numeric types
 
260
        s/tinyint\(\d+\)/INT2/i;
 
261
        s/smallint\(\d+\)/INT2/i;
 
262
        s/mediumint\(\d+\)/INT4/i;
 
263
        s/bigint\(\d+\)/INT8/i;
 
264
        s/int\(\d+\)/INT4/i;
 
265
        s/float(\(\d+,\d*\))/DECIMAL$1/i;
 
266
        s/double precision/FLOAT8 /i;
 
267
        s/([\W])double(\(\d+,\d*\))/$1DECIMAL$2/i;
 
268
        s/([\W])double[\W]/$1FLOAT8 /i;
 
269
        s/([\W])real[\W]/$1FLOAT8 /i;
 
270
        s/([\W])real(\(\d+,\d*\))/$1DECIMAL$2/i;
 
271
    
 
272
# Convert string types
 
273
        s/\w*blob$chareg/text/i;
 
274
        s/mediumtext$chareg/text/i;
 
275
        s/tinytext$chareg/text/i;
 
276
        s/\stext\s+not\s+null/ TEXT DEFAULT '' NOT NULL/i;
 
277
        s/(.*?char\(.*?\))$chareg/$1/i;
 
278
 
 
279
# Old and New are reserved words in Postgres    
 
280
        s/^(\s+)Old /${1}MyOld /;
 
281
        s/^(\s+)New /${1}MyNew /;
 
282
 
 
283
# Convert DATE types
 
284
        s/datetime/TIMESTAMP/;
 
285
        s/timestamp\(\d+\)/TIMESTAMP/i;
 
286
        s/ date / DATE /i;
 
287
        if((/date/ig || /time/ig) && /[,(]\d{4}(\d{2})(\d{2})[,)]/ && 
 
288
                            $1>=0 && $1<=12 && $2>=0 && $2<=31) {
 
289
            s/,(\d{4})(\d{2})(\d{2}),/,'$1-$2-$3 00:00:00',/g;
 
290
        }
 
291
 
 
292
# small hack - convert "default" to uppercase, because below we 
 
293
# enclose all lowercase words in double quotes
 
294
        if(!/^INSERT/) {
 
295
            s/default/DEFAULT/;
 
296
        }
 
297
 
 
298
# Change all AUTO_INCREMENT fields to SERIAL ones with a pre-defined sequence
 
299
        if(/([\w\d]+)\sint.*auto_increment/i) {
 
300
            $tmpseq=new_name("$table_name"."_"."$+"."_SEQ",28);
 
301
            $seq{$table_name}=$tmpseq;
 
302
            $primary{$table_name}=$+;
 
303
            s/(int.*?) .*AUTO_INCREMENT/$1 DEFAULT nextval\('$tmpseq'\)/i;
 
304
        }
 
305
 
 
306
# convert UNSIGNED to CHECK constraints
 
307
        if(/^\s+?([\w\d_]+).*?unsigned/i) {
 
308
            $check.=",\n  CHECK ($dq$1$dq>=0)";
 
309
        }
 
310
        s/unsigned//i;
 
311
 
 
312
# Limited ENUM support - little heuristic
 
313
        s/enum\('N','Y'\)/BOOL/i;
 
314
        s/enum\('Y','N'\)/BOOL/i;
 
315
# ENUM support
 
316
        if(/^\s+?([\w\d_]+).*?enum\((.*?)\)/i) {
 
317
            my $enumlist=$2;
 
318
            my @item;
 
319
            $item[0]='';
 
320
            while($enumlist=~s/'([\d\w_]+)'//i) {
 
321
                $item[++$#item]=$1;
 
322
            }
 
323
# forming identifier name
 
324
            $typename=new_name('enum_'.$table_name.'_'.$item[1],28);
 
325
# creating input type function
 
326
            my $func_in="
 
327
int2* $typename"."_in (char *str) {
 
328
    int2* result;
 
329
 
 
330
    if(str==NULL)
 
331
        return NULL;
 
332
    
 
333
    result=(int2*)palloc(sizeof(int2));
 
334
    *result=-1;";
 
335
            for(my $i=0;$i<=$#item;$i++) {
 
336
                $func_in.="
 
337
    if(strcmp(str,\"$item[$i]\")==0) {
 
338
        *result=$i;
 
339
    }";
 
340
            }
 
341
            $func_in.="
 
342
    if(*result == -1) {
 
343
        elog(ERROR,\"$typename"."_in: incorrect input value\");
 
344
        return NULL;
 
345
    }
 
346
    return (result);
 
347
}\n";
 
348
            $types.="\n---";
 
349
            $types.="\n--- Types for table ".uc($table_name);
 
350
            $types.="\n---\n";
 
351
            print LIBTYPES "\n/*";
 
352
            print LIBTYPES "\n * Types for table ".uc($table_name);
 
353
            print LIBTYPES "\n */\n";
 
354
 
 
355
            $types.="\nCREATE FUNCTION $typename"."_in (cstring)
 
356
        RETURNS $typename
 
357
        AS '$libtypename'
 
358
        LANGUAGE 'c'
 
359
        WITH (ISSTRICT, ISCACHABLE);\n";
 
360
 
 
361
# creating output function
 
362
            my $func_out="
 
363
char* $typename"."_out (int2 *outvalue) {
 
364
    char* result;
 
365
 
 
366
    if(outvalue==NULL)
 
367
        return NULL;
 
368
 
 
369
    result=(char*)palloc(10);
 
370
    switch (*outvalue) {";
 
371
            for(my $i=0;$i<=$#item;$i++) {
 
372
                $func_out.="
 
373
        case $i:
 
374
            strcpy(result,\"$item[$i]\");
 
375
            break;";
 
376
            }
 
377
            $func_out.="
 
378
        default  :
 
379
            elog(ERROR,\"$typename"."_out: incorrect stored value\");
 
380
            return NULL;
 
381
            break;
 
382
    }
 
383
    return result;
 
384
}\n";
 
385
            $func_out.="\nbool $typename"."_eq(int2* a, int2* b) {
 
386
    return (*a==*b);
 
387
}
 
388
 
 
389
bool $typename"."_ne(int2* a, int2* b) {
 
390
    return (*a!=*b);
 
391
}
 
392
 
 
393
bool $typename"."_lt(int2* a, int2* b) {
 
394
    return (*a<*b);
 
395
}
 
396
 
 
397
bool $typename"."_le(int2* a, int2* b) {
 
398
    return (*a<=*b);
 
399
}
 
400
 
 
401
bool $typename"."_gt(int2* a, int2* b) {
 
402
    return (*a>*b);
 
403
}
 
404
 
 
405
bool $typename"."_ge(int2* a, int2* b) {
 
406
    return (*a>=*b);
 
407
}\n";
 
408
 
 
409
            $types.="\nCREATE FUNCTION $typename"."_out ($typename)
 
410
        RETURNS cstring
 
411
        AS '$libtypename'
 
412
        LANGUAGE 'c'
 
413
        WITH (ISSTRICT, ISCACHABLE);\n";
 
414
 
 
415
            $types.="\nCREATE TYPE $typename (
 
416
        internallength = 2,
 
417
        input = $typename\_in,
 
418
        output = $typename\_out
 
419
);\n";
 
420
 
 
421
            $types.="\nCREATE FUNCTION $typename"."_eq ($typename,$typename)
 
422
        RETURNS bool
 
423
        AS '$libtypename'
 
424
        LANGUAGE 'c';
 
425
 
 
426
CREATE FUNCTION $typename"."_lt ($typename,$typename)
 
427
        RETURNS bool
 
428
        AS '$libtypename'
 
429
        LANGUAGE 'c';
 
430
 
 
431
CREATE FUNCTION $typename"."_le ($typename,$typename)
 
432
        RETURNS bool
 
433
        AS '$libtypename'
 
434
        LANGUAGE 'c';
 
435
 
 
436
CREATE FUNCTION $typename"."_gt ($typename,$typename)
 
437
        RETURNS bool
 
438
        AS '$libtypename'
 
439
        LANGUAGE 'c';
 
440
 
 
441
CREATE FUNCTION $typename"."_ge ($typename,$typename)
 
442
        RETURNS bool
 
443
        AS '$libtypename'
 
444
        LANGUAGE 'c';
 
445
 
 
446
CREATE FUNCTION $typename"."_ne ($typename,$typename)
 
447
        RETURNS bool
 
448
        AS '$libtypename'
 
449
        LANGUAGE 'c';
 
450
 
 
451
CREATE OPERATOR < (
 
452
        leftarg = $typename,
 
453
        rightarg = $typename,
 
454
--      negator = >=,
 
455
        procedure = $typename"."_lt
 
456
);
 
457
 
 
458
CREATE OPERATOR <= (
 
459
        leftarg = $typename,
 
460
        rightarg = $typename,
 
461
--      negator = >,
 
462
        procedure = $typename"."_le
 
463
);
 
464
 
 
465
CREATE OPERATOR = (
 
466
        leftarg = $typename,
 
467
        rightarg = $typename,
 
468
        commutator = =,
 
469
--      negator = <>,
 
470
        procedure = $typename"."_eq
 
471
);
 
472
 
 
473
CREATE OPERATOR >= (
 
474
        leftarg = $typename,
 
475
        rightarg = $typename,
 
476
        negator = <,
 
477
        procedure = $typename"."_ge
 
478
);
 
479
 
 
480
CREATE OPERATOR > (
 
481
        leftarg = $typename,
 
482
        rightarg = $typename,
 
483
        negator = <=,
 
484
        procedure = $typename"."_gt
 
485
);
 
486
 
 
487
CREATE OPERATOR <> (
 
488
        leftarg = $typename,
 
489
        rightarg = $typename,
 
490
        negator = =,
 
491
        procedure = $typename"."_ne
 
492
);\n";
 
493
 
 
494
            print LIBTYPES $func_in;
 
495
            print LIBTYPES $func_out;
 
496
            s/enum\(.*?\)/$typename/i;
 
497
        }
 
498
 
 
499
# SET support
 
500
        if(/^\s+?([\w\d_]+).*?set\((.*?)\)/i) {
 
501
            my $setlist=$2;
 
502
            my @item;
 
503
            $item[0]='';
 
504
            my $maxlen=0;       # maximal string length
 
505
            while($setlist=~s/'([\d\w_]+)'//i) {
 
506
                $item[++$#item]=$1;
 
507
                $maxlen+=length($item[$#item])+1;
 
508
            }
 
509
            $maxlen+=1;
 
510
            my $typesize=int($#item/8);
 
511
            if($typesize<2) {
 
512
                $typesize=2;
 
513
            }
 
514
            $internalsize=$typesize;
 
515
            $typesize='int'.$typesize;
 
516
            $typename=new_name('set_'.$table_name.'_'.$item[1],28);
 
517
# creating input type function
 
518
            my $func_in="
 
519
$typesize* $typename"."_in (char *str) {
 
520
    $typesize* result;
 
521
    char* token;
 
522
 
 
523
    if(str==NULL)
 
524
        return NULL;
 
525
 
 
526
    result=($typesize*)palloc(sizeof($typesize));
 
527
    *result=0;
 
528
    if(strcmp(str,\"\")==0)
 
529
        return result;
 
530
    for(token=strtok(str,\",\");token!=NULL;token=strtok(NULL,\",\")) {";
 
531
            for(my $i=0,my $j=1;$i<=$#item;$i++,$j*=2) {
 
532
                $func_in.="
 
533
        if(strcmp(token,\"$item[$i]\")==0) {
 
534
            *result|=$j;
 
535
            continue;
 
536
        }";
 
537
            }
 
538
            $func_in.="
 
539
    }
 
540
 
 
541
    if(*result == 0) {
 
542
        elog(ERROR,\"$typename"."_in: incorrect input value\");
 
543
        return NULL;
 
544
    }
 
545
    return (result);
 
546
 
 
547
}\n";
 
548
            $types.="\n---";
 
549
            $types.="\n--- Types for table ".uc($table_name);
 
550
            $types.="\n---\n";
 
551
            print LIBTYPES "\n/*";
 
552
            print LIBTYPES "\n * Types for table ".uc($table_name);
 
553
            print LIBTYPES "\n */\n";
 
554
 
 
555
            $types.="\nCREATE FUNCTION $typename"."_in (cstring)
 
556
        RETURNS $typename
 
557
        AS '$libtypename'
 
558
        LANGUAGE 'c';\n";
 
559
 
 
560
# creating output function
 
561
            my $func_out="
 
562
char* $typename"."_out ($typesize *outvalue) {
 
563
    char* result;
 
564
    int i;
 
565
 
 
566
    if(outvalue==NULL)
 
567
        return NULL;
 
568
 
 
569
    result=(char*)palloc($maxlen);
 
570
    strcpy(result,\"\");
 
571
    for(i=1;i<=2 << (sizeof(int2)*8);i*=2) {
 
572
        switch (*outvalue & i) {";
 
573
            for(my $i=0,$j=1;$i<=$#item;$i++,$j*=2) {
 
574
                $func_out.="
 
575
        case $j:";
 
576
                if($item[$i] ne '') {
 
577
                    $func_out.="ADD_COMMA;";
 
578
                }
 
579
                $func_out.="strcat(result,\"$item[$i]\");
 
580
            break;";
 
581
            }
 
582
            $func_out.="
 
583
        default  :
 
584
            break;
 
585
        }
 
586
    }
 
587
    
 
588
    return result;
 
589
}\n";
 
590
            $func_out.="\nbool $typename"."_eq($typesize* a, $typesize* b) {
 
591
    return (*a==*b);
 
592
}
 
593
 
 
594
$typesize find_in_set($typesize *a, $typesize *b) {
 
595
    int i;
 
596
    
 
597
    for(i=1;i<=sizeof($typesize)*8;i*=2) {
 
598
        if(*a & *b) {
 
599
            return 1;
 
600
        }
 
601
    }
 
602
    return 0;
 
603
}
 
604
 
 
605
\n";
 
606
 
 
607
            $types.="\nCREATE FUNCTION $typename"."_out ($typename)
 
608
        RETURNS cstring
 
609
        AS '$libtypename'
 
610
        LANGUAGE 'c';\n";
 
611
 
 
612
            $types.="\nCREATE TYPE $typename (
 
613
        internallength = $internalsize,
 
614
        input = $typename\_in,
 
615
        output = $typename\_out
 
616
);\n";
 
617
 
 
618
            $types.="\nCREATE FUNCTION $typename"."_eq ($typename,$typename)
 
619
        RETURNS bool
 
620
        AS '$libtypename'
 
621
        LANGUAGE 'c';
 
622
 
 
623
CREATE FUNCTION find_in_set ($typename,$typename)
 
624
        RETURNS bool
 
625
        AS '$libtypename'
 
626
        LANGUAGE 'c';
 
627
 
 
628
CREATE OPERATOR = (
 
629
        leftarg = $typename,
 
630
        rightarg = $typename,
 
631
        commutator = =,
 
632
        procedure = $typename"."_eq
 
633
);
 
634
 
 
635
CREATE OPERATOR <> (
 
636
        leftarg = $typename,
 
637
        rightarg = $typename,
 
638
        commutator = <>,
 
639
        negator = =,
 
640
        procedure = $typename"."_eq
 
641
);
 
642
 
 
643
\n";
 
644
 
 
645
            print LIBTYPES $func_in;
 
646
            print LIBTYPES $func_out;
 
647
            s/set\(.*?\)/$typename/i;
 
648
        }
 
649
 
 
650
# Change multy-field keys to multi-field indices
 
651
# MySQL Dump usually ends the CREATE TABLE statement like this:
 
652
# CREATE TABLE bids (
 
653
#   ...
 
654
#   PRIMARY KEY (bids_id),
 
655
#   KEY offer_id (offer_id,user_id,the_time),
 
656
#   KEY bid_value (bid_value)
 
657
# );
 
658
# We want to replace this with smth like
 
659
# CREATE TABLE bids (
 
660
#   ...
 
661
#   PRIMARY KEY (bids_id),
 
662
# );
 
663
#   CREATE INDEX offer_id ON bids (offer_id,user_id,the_time);
 
664
#   CREATE INDEX bid_value ON bids (bid_value);
 
665
        if (s/CREATE TABLE (.*) /CREATE TABLE $dq$1$dq /i) {
 
666
            if($oldtable ne $table_name) {
 
667
                $oldtable=$table_name;
 
668
                $j=-1;
 
669
                $check='';
 
670
 
 
671
                if($seq{$table_name} ne '') {
 
672
                    print "\n\n--";
 
673
                    print "\n-- Sequences for table ".uc($table_name);
 
674
                    print "\n--\n";
 
675
                    print "\nCREATE SEQUENCE ".$seq{$table_name}.";\n\n";
 
676
                }
 
677
 
 
678
                print $types;
 
679
                $types='';
 
680
                $dump=~s/,\n\).*;/\n\);/gmi;
 
681
# removing table options after closing bracket:
 
682
# ) TYPE=ISAM PACK_KEYS=1;
 
683
                $dump=~s/\n\).*/\n\);/gmi;
 
684
                print $dump;
 
685
                $dump='';
 
686
            }
 
687
            $table_name=$1;
 
688
        }
 
689
 
 
690
# output CHECK constraints instead UNSIGNED modifiers
 
691
        if(/PRIMARY KEY\s+\((.*)\)/i) {
 
692
            my $tmpfld=$1;
 
693
            $tmpfld=~s/,/","/g if $dq;
 
694
            $tmpfld=~s/ //g;
 
695
            s/PRIMARY KEY\s+(\(.*\))/PRIMARY KEY \($dq$tmpfld$dq\)/i;
 
696
            s/(PRIMARY KEY \(.*\)).*/$1$check\n/i;
 
697
        }
 
698
    
 
699
        if(/^\s*KEY ([\w\d_]+)\s*\((.*)\).*/i) {
 
700
            my $tmpfld=$2; my $ky=$1;
 
701
            $tmpfld=~s/\s*,\s*/","/g if $dq;
 
702
            $tmpfld=~s/(\(\d+\))//g;
 
703
            $index{$table_name}[++$j]="CREATE INDEX ${ky}_$table_name\_index ON $dq$table_name$dq ($dq$tmpfld$dq);";
 
704
        }
 
705
        if(/^\s*UNIQUE.*?([\w\d_]+)\s*\((.*)\).*/i) {
 
706
            my $tmpfld=$2; my $ky=$1;
 
707
            $tmpfld=~s/,/","/g if $dq;
 
708
            $tmpfld=~s/(\(\d+\))//g;
 
709
            $index{$table_name}[++$j]="CREATE UNIQUE INDEX ${ky}_$table_name\_index ON $dq$table_name$dq ($dq$tmpfld$dq);";
 
710
        }
 
711
        s/^\s*UNIQUE (.+).*(\(.*\)).*\n//i;
 
712
        s/^\s*KEY (.+).*(\(.*\)).*\n//i;
 
713
    
 
714
        if($dq && !/^\s*(PRIMARY KEY|UNIQUE |KEY |CREATE TABLE|INSERT INTO|\);)/i) {
 
715
            s/\s([A-Za-z_\d]+)\s/ $dq$+$dq /;
 
716
        }
 
717
    } # end of if($tabledef)
 
718
 
 
719
    s/INSERT INTO\s+?(.*?)\s+?/INSERT INTO $dq$1$dq /i;
 
720
 
 
721
# if not defined -s command-line option (safe data conversion),
 
722
# attempting to convert timestamp data
 
723
    if(!$safe_data_conv) {
 
724
# Fix timestamps
 
725
        s/'0000-00-00/'0001-01-01/g;
 
726
# may corrupt data !!!
 
727
        s/([,(])00000000000000(?=[,)])/$1'00010101 000000'/g;
 
728
        if(/[,(]\d{4}(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})[,)]/ && 
 
729
                $1>=0 && $1<=12 && $2>=0 && $2<=31 && $3>=0 && $3<=23 &&
 
730
                    $4>=0 && $4<=59 && $5>=0 && $5<=59) {
 
731
            s/([,(])(\d{8})(\d{6})(?=[,)])/$1'$2 $3'/g;
 
732
        }
 
733
        if(/[,(]\d{4}(\d{2})(\d{2})[,)]/ && 
 
734
                $2>=0 && $2<=12 && $3>=0 && $3<=31) {
 
735
            s/([,(])(\d{4})(\d{2})(\d{2})(?=[,)])/$1'$2-$3-$4 00:00:00'/g;
 
736
        }
 
737
    }
 
738
 
 
739
    $dump.=$_;
 
740
}
 
741
 
 
742
if($seq{$table_name} ne '') {
 
743
    print "\n\n--";
 
744
    print "\n-- Sequences for table ".uc($table_name);
 
745
    print "\n--\n";
 
746
    print "\nCREATE SEQUENCE ".$seq{$table_name}.";\n\n";
 
747
}
 
748
print $types;
 
749
$dump=~s/,\n\).*;/\n\);/gmi;
 
750
$dump=~s/\n\).*/\n\);/gmi;
 
751
print $dump;
 
752
 
 
753
# Output indices for tables
 
754
while(my($table,$ind)=each(%index)) {
 
755
    print "\n\n--";
 
756
    print "\n-- Indexes for table ".uc($table);
 
757
    print "\n--\n";
 
758
    for(my $i=0;$i<=$#{$ind};$i++) {
 
759
        print "\n$ind->[$i]";
 
760
    }
 
761
 
 
762
}
 
763
 
 
764
while(my($table,$s)=each(%seq)) {
 
765
    print "\n\n--";
 
766
    print "\n-- Sequences for table ".uc($table);
 
767
    print "\n--\n";
 
768
 
 
769
    # setting SERIAL sequence values right    
 
770
    if($primary{$table} ne '') {
 
771
        print "\nSELECT SETVAL('".$seq{$table}."',(select case when max($dq".$primary{$table}."$dq)>0 then max($dq".$primary{$table}."$dq)+1 else 1 end from $dq$table$dq));";
 
772
    }
 
773
}
 
774
 
 
775
print("\n\nCOMMIT;\n");
 
776
close(LIBTYPES);
 
777
 
 
778
open(MAKE,">Makefile");
 
779
print MAKE "#
 
780
# My2Pg \$Revision: 1.13 $ \translated dump
 
781
# Makefile
 
782
#
 
783
 
 
784
all: libtypes.so
 
785
 
 
786
libtypes.o: libtypes.c
 
787
        gcc -c -fPIC -g -O libtypes.c
 
788
libtypes.so: libtypes.o
 
789
        ld -Bshareable -o libtypes.so libtypes.o";
 
790
close(MAKE);
 
791
 
 
792
#
 
793
# Function generates unique identifier
 
794
# Args   : template name, max length
 
795
# Globals: %identifier
 
796
#
 
797
sub new_name() {
 
798
    my $name=lc(shift @_);
 
799
    my $len=shift @_;
 
800
 
 
801
# truncate long names
 
802
    if(length($name)>$len) {
 
803
        $name=~s/(.{$len}).*/$1/i;
 
804
    }
 
805
 
 
806
# find reserved identifiers
 
807
    if($identifier{$name}!=1) {
 
808
        $identifier{$name}=1;
 
809
        return $name;
 
810
    }
 
811
    else {
 
812
        for(my $i=1,my $tmpname=$name.$i;$identifier{$tmpname}!=1;) {
 
813
            $tmpname=$name.$i
 
814
        }
 
815
        $identifier{$tmpname}=1;
 
816
        return $tmpname;
 
817
    }
 
818
 
 
819
    die "Error during unique identifier generation :-(";
 
820
}
 
821
 
 
822
sub usage() {
 
823
print <<EOF
 
824
my2pg - MySQL to PostgreSQL database dump converter
 
825
 
 
826
Copyright (c) 2000-2002 Max Rudensky    <fonin\@ziet.zhitomir.ua>
 
827
Copyright (c) 2000 Valentine Danilchuk  <valdan\@ziet.zhitomir.ua>
 
828
 
 
829
This program is distributed in the hope that it will be useful,
 
830
but WITHOUT ANY WARRANTY; without even the implied warranty of
 
831
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
832
code source for license details.
 
833
 
 
834
SYNTAX:
 
835
    my2pg [-hnds]
 
836
 
 
837
OPTIONS:
 
838
    h           - this help
 
839
    n           - convert *CHAR NOT NULL DEFAULT '' types to *CHAR NULL
 
840
    d           - double quotes around table and column names
 
841
    s           - do not attempt to convert data (timestamps at the moment)
 
842
EOF
 
843
;
 
844
}
 
845
 
 
846
 
 
847
=head1 NAME
 
848
 
 
849
my2pg - MySQL -> PostgreSQL dump conversion utility.
 
850
 
 
851
=head1 SYNTAX
 
852
 
 
853
        mysqldump db | ./my2pg.pl [-nds] > pgsqldump.sql
 
854
        vi libtypes.c
 
855
        make
 
856
        psql database < pgsqldump.txt
 
857
where
 
858
 
 
859
=over 4
 
860
 
 
861
=item F<pgsqldump.sql>
 
862
 
 
863
- file suitable for loading into PostgreSQL.
 
864
 
 
865
=item F<libtypes.c>
 
866
 
 
867
- C source for emulated MySQL types (ENUM, SET) generated by B<my2pg>
 
868
 
 
869
=back
 
870
 
 
871
=head1 OVERVIEW
 
872
 
 
873
B<my2pg> utility attempts to convert MySQL database dump to Postgres's one.
 
874
B<my2pg> performs such conversions:
 
875
 
 
876
=over 4
 
877
 
 
878
=item * Type conversion.
 
879
 
 
880
It tries to find proper Postgres 
 
881
type for each column.
 
882
Unknown types are silently pushing to output dump;
 
883
ENUM and SET types implemented via user types 
 
884
(C source for such types can be found in 
 
885
B<libtypes.c> file);
 
886
 
 
887
=item * Encloses identifiers into double quotes.
 
888
 
 
889
All column and table 
 
890
names should be enclosed to double-quotes to prevent 
 
891
conflict with reserved SQL keywords;
 
892
 
 
893
=item * Converting
 
894
 
 
895
AUTO_INCREMENT fields to SERIAL. Actually, creating the sequence and 
 
896
setting default value to nextval('seq'), well, you know :)
 
897
 
 
898
=item * Converting
 
899
 
 
900
KEY(field) to CREATE INDEX i_field on table (field);
 
901
 
 
902
=item * The same
 
903
 
 
904
for UNIQUE keys;
 
905
 
 
906
=item * Indices
 
907
 
 
908
are creating AFTER rows insertion (to speed up the load);
 
909
 
 
910
=item * Translates '#'
 
911
 
 
912
MySQL comments to ANSI SQL '--'
 
913
 
 
914
=back
 
915
 
 
916
It encloses dump in transaction block to prevent single errors 
 
917
during data load.
 
918
 
 
919
=head1 COMMAND-LINE OPTIONS
 
920
 
 
921
My2pg takes the following command-line options:
 
922
 
 
923
=over 2
 
924
 
 
925
=item -n
 
926
 
 
927
Convert *CHAR DEFAULT '' NOT NULL types to *CHAR NULL.
 
928
Postgres can't load empty '' strings in NOT NULL fields.
 
929
 
 
930
=item -d
 
931
 
 
932
Add double quotes around table and column names
 
933
 
 
934
=item -h
 
935
 
 
936
Show usage banner.
 
937
 
 
938
=item -s
 
939
 
 
940
Do not attempt to convert data. Currently my2pg only tries to convert
 
941
date and time data.
 
942
 
 
943
=back
 
944
 
 
945
=head1 SIDE EFFECTS
 
946
 
 
947
=over 4
 
948
 
 
949
=item * creates
 
950
 
 
951
file B<libtypes.c> in current directory 
 
952
overwriting existed file without any checks;
 
953
 
 
954
=item * the same
 
955
 
 
956
for Makefile.
 
957
 
 
958
=back
 
959
 
 
960
=head1 BUGS
 
961
 
 
962
Known bugs are:
 
963
 
 
964
=over 4
 
965
 
 
966
=item * Possible problems with the timestamp data.
 
967
 
 
968
PostgreSQL does not accept incorrect date/time values like B<2002-00-15>,
 
969
while MySQL does not care about that. Currently my2pg cannot handle this
 
970
issue. You should care yourself to convert such a data.
 
971
 
 
972
=item * Use -s option if your numeric data are broken during conversion.
 
973
 
 
974
My2pg attempts to convert MySQL timestamps of the form B<yyyymmdd> to
 
975
B<yyyy-mm-dd> and B<yyyymmddhhmmss> to B<yyyy-mm-dd hh:mm:ss>. It performs
 
976
some heuristic checks to ensure that the month,day,hour,minutes and seconds have
 
977
values from the correct range (0..12, 0..31, 0..23, 0..59, 0..59 respectively).
 
978
It is still possible that your numeric values that satisfy these conditions
 
979
will get broken.
 
980
 
 
981
=item * Possible problems with enclosing identifiers in double quotes.
 
982
 
 
983
All identifiers such as table and column names should be enclosed in double 
 
984
quotes. Program can't handle upper-case identifiers, 
 
985
like DBA. Lower-case identifiers are OK.
 
986
 
 
987
=item * SET type emulation is not full. LIKE operation on 
 
988
 
 
989
SETs, raw integer input values should be implemented
 
990
 
 
991
=item * B<Makefile>
 
992
 
 
993
generated during output is 
 
994
platform-dependent and surely works only on 
 
995
Linux/gcc (FreeBSD/gcc probably works as well - not tested)
 
996
 
 
997
=item * Generated B<libtypes.c> contain line
 
998
 
 
999
        #include <postgres.h>
 
1000
 
 
1001
This file may be located not in standard compiler 
 
1002
include path, you need to check it before compiling.
 
1003
 
 
1004
=back
 
1005
 
 
1006
=head1 AUTHORS
 
1007
 
 
1008
B<(c) 2000-2002 Maxim V. Rudensky        (fonin@ziet.zhitomir.ua)> (developer, maintainer)
 
1009
 
 
1010
B<(c) 2000 Valentine V. Danilchuk (valdan@ziet.zhitomir.ua)> (original script)
 
1011
 
 
1012
=head1 CREDITS
 
1013
 
 
1014
Great thanks to all those people who provided feedback and make development
 
1015
of this tool easier.
 
1016
 
 
1017
Jeff Waugh <jaw@ic.net>
 
1018
 
 
1019
Joakim Lemstr�m <jocke@bytewize.com> || <buddyh19@hotmail.com>
 
1020
 
 
1021
Yunliang Yu <yu@math.duke.edu>
 
1022
 
 
1023
Brad Hilton <bhilton@vpop.net>
 
1024
 
 
1025
If you are not listed here please write to me.
 
1026
 
 
1027
=head1 LICENSE
 
1028
 
 
1029
B<BSD>
 
1030
 
 
1031
=cut