On Fri, 2005-09-02 at 12:29 -0700, Josh Berkus wrote: > > still trying to hold on to my fantasy that I can hack Postgres (and > > contrib/ora2pg) into submission. > > I'm happy to work with you on ora2pg
Cool. It looks like I should have referred to contrib/oracle, not contrib/ora2pg, but you got my point. The latest version I found of ora2pg is at http://www.samse.fr/GPL/ora2pg/ora2pg-3.3.tar.gz This seems to be more recent than the version at contrib/oracle. For example, this newer version has tablespace support. Given this as a starting point, I've made the attached changes. Mostly I've added a few new config options, but I also made a correction to the existing EXCLUDE option, and I corrected a couple spelling/English errors along the way. A big thing that's lacking is conversion for stored procedures and functions. My initial approach to this was to use Perl to post-process the PL/SQL code dumped by the export, making it look more like proper Pl/pgSQL (e.g. VARCHAR2->VARCHAR). I'm no Perl hacker, and when I came across significant PL/SQL <--> PL/pgSQL differences (e.g. PL/pgSQL exception == rollback), I added to my approach the idea of hacking PL/pgSQL to make it look more like PL/SQL. Attacking the problem from both ends like this, I imagined that Nirvana would be reached somewhere in the middle. The beginning of my Perl-based attempt to convert PL/SQL into PL/pgSQL is a pretty simple stand-alone script. I can send it if you like, but I'm a Perl newbie, so you can probably do much better. My attempts to make PL/pgSQL look like PL/SQL have been posted to -hackers and -patches over the last couple months.
diff -c ora2pg_3.3/ora2pg.conf ora2pg/ora2pg.conf *** ora2pg_3.3/ora2pg.conf 2004-12-24 16:05:40.000000000 +0000 --- ora2pg/ora2pg.conf 2005-09-02 20:38:48.900376220 +0000 *************** *** 56,61 **** --- 56,68 ---- # Value must be a list of table name separated by space. #EXCLUDE OTHER_TABLES + # Set whether to include invalid functions, procedures, and packages. + # Under Oracle's on-the-fly invalidation/recompilation model there + # may be any number of objects that have status of 'INVALID' but that + # are actually viable. + INCLUDE_INVALID 1 + + # Display table indice and exit program (do not perform any export) SHOWTABLEID 0 *************** *** 139,148 **** # Constraints will be checked at the end of each transaction. DEFER_FKEY 0 ! # If set to 1 replace portable numeric type into PostgreSQL internal type. # Oracle data type NUMBER(p,s) is approximatively converted to smallint, # integer, bigint, real and float PostgreSQL data type. If you have monetary # fields you should preserve the numeric(p,s) PostgreSQL data type if you need ! # very good precision. NUMBER without precision are set to float. ! PG_NUMERIC_TYPE 1 --- 146,171 ---- # Constraints will be checked at the end of each transaction. DEFER_FKEY 0 ! # If set to 1 replace portable numeric type with PostgreSQL internal type. # Oracle data type NUMBER(p,s) is approximatively converted to smallint, # integer, bigint, real and float PostgreSQL data type. If you have monetary # fields you should preserve the numeric(p,s) PostgreSQL data type if you need ! # very good precision (see PG_INTEGER_TYPE). NUMBER without precision are set to ! # float. ! PG_NUMERIC_TYPE 0 ! ! # If set to 1 replace portable numeric type with PostgreSQL internal type, ! # for integers only. This behaves as PG_NUMERIC_TYPE with respect to ! # Oracle data type NUMBER(p), but preserves exact arithmetic on NUMBER(p,s) ! # columns by converting to PostgreSQL numeric(p,s). NUMBER without precision ! # maps to "numeric" without precision. ! PG_INTEGER_TYPE 1 ! ! # If set to 1 map Oracle's DATE type to PostgreSQL DATE type. Oracle DATE type ! # can contain time information, so PostgreSQL "timestamp" should, in general, be ! # used to hold Oracle DATEs. However, Oracle also supports TIMESTAMP. Setting ! # PG_DATE_TYPE indicates that Oracle TIMESTAMPs are the only incoming date columns ! # with a time portion that needs to be preserved, and that incoming Oracle DATEs ! # effectively contain only a date portion. ! PG_DATE_TYPE 1 diff -c ora2pg_3.3/ora2pg.pl ora2pg/ora2pg.pl *** ora2pg_3.3/ora2pg.pl 2004-12-24 16:05:40.000000000 +0000 --- ora2pg/ora2pg.pl 2005-07-07 18:01:53.000000000 +0000 *************** *** 40,45 **** --- 40,46 ---- #tables => [EMAIL PROTECTED]'TABLES'}}, tables => $Config{'TABLES'}, exclude => $Config{'EXCLUDE'}, + include_invalid => $Config{'INCLUDE_INVALID'} || 0, showtableid => $Config{'SHOWTABLEID'} || 0, min => $Config{'MIN'} || 0, max => $Config{'MAX'} || 0, *************** *** 56,66 **** fkey_deferrable => $Config{'FKEY_DEFERRABLE'} || 0, defer_fkey => $Config{'DEFER_FKEY'} || 0, pg_numeric_type => $Config{'PG_NUMERIC_TYPE'} || 0, ); exit 0 if ($Config{'SHOWTABLEID'}); ! # Mofify export structure if required if ($Config{'TYPE'} =~ /^(DATA|COPY)$/) { for my $t (keys %{$Config{'MODIFY_STRUCT'}}) { $schema->modify_struct($t, @{$Config{'MODIFY_STRUCT'}{$t}}); --- 57,69 ---- fkey_deferrable => $Config{'FKEY_DEFERRABLE'} || 0, defer_fkey => $Config{'DEFER_FKEY'} || 0, pg_numeric_type => $Config{'PG_NUMERIC_TYPE'} || 0, + pg_integer_type => $Config{'PG_INTEGER_TYPE'} || 0, + pg_date_type => $Config{'PG_DATE_TYPE'} || 0, ); exit 0 if ($Config{'SHOWTABLEID'}); ! # Modify export structure if required if ($Config{'TYPE'} =~ /^(DATA|COPY)$/) { for my $t (keys %{$Config{'MODIFY_STRUCT'}}) { $schema->modify_struct($t, @{$Config{'MODIFY_STRUCT'}{$t}}); *************** *** 123,129 **** $Config{"skip_\L$_\E"} = 1; } } ! } elsif (!grep(/^$var$/i, 'TABLES', 'MODIFY_STRUCT', 'REPLACE_TABLES', 'REPLACE_COLS', 'WHERE')) { $Config{"\U$var\E"} = $val; } elsif ( (uc($var) eq 'TABLES') || (uc($var) eq 'EXCLUDE') ) { push(@{$Config{"\U$var\E"}}, split(/\s+/, $val) ); --- 126,132 ---- $Config{"skip_\L$_\E"} = 1; } } ! } elsif (!grep(/^$var$/i, 'TABLES', 'MODIFY_STRUCT', 'REPLACE_TABLES', 'REPLACE_COLS', 'WHERE', 'EXCLUDE')) { $Config{"\U$var\E"} = $val; } elsif ( (uc($var) eq 'TABLES') || (uc($var) eq 'EXCLUDE') ) { push(@{$Config{"\U$var\E"}}, split(/\s+/, $val) ); diff -c ora2pg_3.3/Ora2Pg.pm ora2pg/Ora2Pg.pm *** ora2pg_3.3/Ora2Pg.pm 2005-02-22 17:21:41.000000000 +0000 --- ora2pg/Ora2Pg.pm 2005-07-07 20:46:54.000000000 +0000 *************** *** 271,302 **** Supported options are: ! - datasource : DBD datasource (required) ! - user : DBD user (optional with public access) ! - password : DBD password (optional with public access) ! - schema : Oracle internal schema to extract ! - type : Type of data to extract, can be TABLE,VIEW,GRANT,SEQUENCE, ! TRIGGER,FUNCTION,PROCEDURE,DATA,COPY,PACKAGE,TABLESPACE ! - debug : Print the current state of the parsing ! - export_schema : Export Oracle schema to PostgreSQL 7.3 schema ! - tables : Extract only the given tables (arrayref) and set the extracting order ! - exclude : Exclude the given tables from extract (arrayref) ! - showtableid : Display only the table indice during extraction ! - min : Indice to begin extraction. Default to 0 ! - max : Indice to end extraction. Default to 0 mean no limits ! - data_limit : Number max of tuples to return during data extraction (default 0 no limit) ! - case_sensitive: Allow to preserve Oracle object name as they are written. Default is not. ! - skip_fkeys : Skip foreign key constraints extraction. Default to 0 (extraction) ! - skip_pkeys : Skip primary keys extraction. Default to 0 (extraction) ! - skip_ukeys : Skip unique column constraints extraction. Default to 0 (extraction) ! - skip_indices : Skip all other index types extraction. Default to 0 (extraction) ! - skip_checks : Skip checks constraints extraction. Default to 0 (extraction) ! - bzip2 : Path to the Bzip2 program to compress data export. Default /usr/bin/bzip2 ! - gen_user_pwd : When set to 1 replace default password 'change_my_secret' with a random string. - fkey_deferrable: Force foreign key constraints to be exported as deferrable. Default 0: asis. ! - defer_fkey : Force all foreign key constraints to be deferred during data import. Default 0: asis. ! - pg_numeric_type: Convert Oracle NUMBER data type to internal PostgreSQL data type instead of use of the slow numeric(p,s) data type Attempt that this list should grow a little more because all initialization is --- 271,307 ---- Supported options are: ! - datasource : DBD datasource (required) ! - user : DBD user (optional with public access) ! - password : DBD password (optional with public access) ! - schema : Oracle internal schema to extract ! - type : Type of data to extract, can be TABLE,VIEW,GRANT,SEQUENCE, ! TRIGGER,FUNCTION,PROCEDURE,DATA,COPY,PACKAGE,TABLESPACE ! - debug : Print the current state of the parsing ! - export_schema : Export Oracle schema to PostgreSQL schema ! - tables : Extract only the given tables (arrayref) and set the extracting order ! - exclude : Exclude the given tables from extract (arrayref) ! - include_invalid: Include invalid functions/procedures/packages ! - showtableid : Display only the table indice during extraction ! - min : Indice to begin extraction. Default to 0 ! - max : Indice to end extraction. Default to 0 mean no limits ! - data_limit : Number max of tuples to return during data extraction (default 0 no limit) ! - case_sensitive : Allow to preserve Oracle object name as they are written. Default is not. ! - skip_fkeys : Skip foreign key constraints extraction. Default to 0 (extraction) ! - skip_pkeys : Skip primary keys extraction. Default to 0 (extraction) ! - skip_ukeys : Skip unique column constraints extraction. Default to 0 (extraction) ! - skip_indices : Skip all other index types extraction. Default to 0 (extraction) ! - skip_checks : Skip checks constraints extraction. Default to 0 (extraction) ! - bzip2 : Path to the Bzip2 program to compress data export. Default /usr/bin/bzip2 ! - gen_user_pwd : When set to 1 replace default password 'change_my_secret' with a random string. - fkey_deferrable: Force foreign key constraints to be exported as deferrable. Default 0: asis. ! - defer_fkey : Force all foreign key constraints to be deferred during data import. Default 0: asis. ! - pg_numeric_type: Convert Oracle NUMBER data type to internal PostgreSQL data type instead of using of the slow numeric(p,s) data type + - pg_integer_type: Convert Oracle NUMBER data type to internal PostgreSQL data type instead of using + of the slow numeric(p) data type -- integers only + - pg_date_type : Convert Oracle DATE data type to PostgreSQL DATE data type instead of using + the TIMESTAMP data type for all dates Attempt that this list should grow a little more because all initialization is *************** *** 529,534 **** --- 534,541 ---- $self->{max} = $options{max} || 0; + $self->{include_invalid} = $options{include_invalid} || 0; + $self->{showtableid} = $options{showtableid} || 0; $self->{dbh}->{LongReadLen} = 0; *************** *** 560,565 **** --- 567,574 ---- $self->{fkey_deferrable} = $options{fkey_deferrable} || 0; $self->{defer_fkey} = $options{defer_fkey} || 0; $self->{pg_numeric_type} = $options{pg_numeric_type} || 0; + $self->{pg_integer_type} = $options{pg_integer_type} || 0; + $self->{pg_date_type} = $options{pg_date_type} || 0; $self->{type} = $options{type} || 'TABLE'; *************** *** 909,915 **** $sql_header .= "--\n"; $sql_header .= "-- This program is free software; you can redistribute it and/or modify it under\n"; $sql_header .= "-- the same terms as Perl itself.\n\n"; ! if ($self->{type} ne 'COPY' || $self->{defer_fkey}) { $sql_header .= "BEGIN TRANSACTION;\n\n"; } --- 918,924 ---- $sql_header .= "--\n"; $sql_header .= "-- This program is free software; you can redistribute it and/or modify it under\n"; $sql_header .= "-- the same terms as Perl itself.\n\n"; ! if ($self->{type} ne 'COPY' && $self->{type} ne 'FUNCTION' && $self->{type} ne 'PROCEDURE' || $self->{defer_fkey}) { $sql_header .= "BEGIN TRANSACTION;\n\n"; } *************** *** 1145,1156 **** map { s/^.* out //is } @argu; map { $_ = $self->_sql_type(uc($_)) } @argu; $self->{functions}{$fct} =~ /return ([^\s]*) is/is; ! $self->{functions}{$fct} = "-- Oracle function declaration, please edit to match PostgreSQL syntax.\n$self->{functions}{$fct}"; ! if (!$self->{case_sensitive}) { ! $sql_output .= "-- PostgreSQL possible function declaration, please edit to match your needs.\nCREATE FUNCTION \L$fct\E(" . join(',', @argu) . ") RETURNS " . $self->_sql_type(uc($1)) . " AS '\n$self->{functions}{$fct}\n' LANGUAGE 'sql'\n\n"; ! } else { ! $sql_output .= "-- PostgreSQL possible function declaration, please edit to match your needs.\nCREATE FUNCTION $fct(" . join(',', @argu) . ") RETURNS " . $self->_sql_type(uc($1)) . " AS '\n$self->{functions}{$fct}\n' LANGUAGE 'sql'\n\n"; ! } } if (!$sql_output) { --- 1154,1160 ---- map { s/^.* out //is } @argu; map { $_ = $self->_sql_type(uc($_)) } @argu; $self->{functions}{$fct} =~ /return ([^\s]*) is/is; ! $sql_output .= "\nCREATE\n $self->{functions}{$fct}"; } if (!$sql_output) { *************** *** 1681,1687 **** 'CHAR' => 'char', 'NCHAR' => 'char', # VARCHAR types the limit is 2000 bytes in Oracle 7 and 4000 in Oracle 8. ! # PG varchar type has max length iset to 8104 so it should match all needs 'VARCHAR' => 'varchar', 'NVARCHAR' => 'varchar', 'VARCHAR2' => 'varchar', --- 1685,1691 ---- 'CHAR' => 'char', 'NCHAR' => 'char', # VARCHAR types the limit is 2000 bytes in Oracle 7 and 4000 in Oracle 8. ! # PG varchar type has max length set to 8104 so it should match all needs 'VARCHAR' => 'varchar', 'NVARCHAR' => 'varchar', 'VARCHAR2' => 'varchar', *************** *** 1689,1694 **** --- 1693,1699 ---- # The DATE data type is used to store the date and time information. # Pg type timestamp should match all needs 'DATE' => 'timestamp', + 'TIMESTAMP(6)' => 'timestamp', # Type LONG is like VARCHAR2 but with up to 2Gb. # PG type text should match all needs or if you want you could use blob 'LONG' => 'text', # Character data of variable length *************** *** 1723,1733 **** # Type VARCHAR(2) must have a given length $len = 1 if (!$len && ($type eq "CHAR")); return "$TYPE{$type}($len)"; } elsif ($type eq "NUMBER") { # This is an integer if (!$scale) { if ($precision) { ! if ($self->{pg_numeric_type}) { if ($precision < 5) { return 'smallint'; } elsif ($precision < 10) { --- 1728,1743 ---- # Type VARCHAR(2) must have a given length $len = 1 if (!$len && ($type eq "CHAR")); return "$TYPE{$type}($len)"; + } elsif ($type eq "DATE") { + if ($self->{pg_date_type}) { + return 'date'; + } + return "$TYPE{$type}"; } elsif ($type eq "NUMBER") { # This is an integer if (!$scale) { if ($precision) { ! if ($self->{pg_numeric_type} || $self->{pg_integer_type}) { if ($precision < 5) { return 'smallint'; } elsif ($precision < 10) { *************** *** 1738,1744 **** } return "numeric($precision)"; } elsif ($self->{pg_numeric_type}) { ! # Most of the time interger should be enought? return 'float'; } } else { --- 1748,1754 ---- } return "numeric($precision)"; } elsif ($self->{pg_numeric_type}) { ! # Most of the time integer should be enough? return 'float'; } } else { *************** *** 2268,2274 **** my($self, $type) = @_; # Retrieve all indexes ! my $str = "SELECT DISTINCT OBJECT_NAME,OWNER FROM $self->{prefix}_OBJECTS WHERE OBJECT_TYPE='$type' AND STATUS='VALID'"; if (!$self->{schema}) { $str .= " AND OWNER NOT IN ('SYS','SYSTEM','DBSNMP','OUTLN','PERFSTAT')"; } else { --- 2278,2287 ---- my($self, $type) = @_; # Retrieve all indexes ! my $str = "SELECT DISTINCT OBJECT_NAME,OWNER FROM $self->{prefix}_OBJECTS WHERE OBJECT_TYPE='$type'"; ! if (!$self->{include_invalid}) { ! $str .= " AND STATUS='VALID'"; ! } if (!$self->{schema}) { $str .= " AND OWNER NOT IN ('SYS','SYSTEM','DBSNMP','OUTLN','PERFSTAT')"; } else { *************** *** 2307,2313 **** my ($self) = @_; # Retrieve all indexes ! my $str = "SELECT DISTINCT OBJECT_NAME,OWNER FROM $self->{prefix}_OBJECTS WHERE OBJECT_TYPE='PACKAGE' AND STATUS='VALID'"; if (!$self->{schema}) { $str .= " AND OWNER NOT IN ('SYS','SYSTEM','DBSNMP','OUTLN','PERFSTAT')"; } else { --- 2320,2329 ---- my ($self) = @_; # Retrieve all indexes ! my $str = "SELECT DISTINCT OBJECT_NAME,OWNER FROM $self->{prefix}_OBJECTS WHERE OBJECT_TYPE='PACKAGE'"; ! if (!$self->{include_invalid}) { ! $str .= " AND STATUS='VALID'"; ! } if (!$self->{schema}) { $str .= " AND OWNER NOT IN ('SYS','SYSTEM','DBSNMP','OUTLN','PERFSTAT')"; } else {
---------------------------(end of broadcast)--------------------------- TIP 3: Have you checked our extensive FAQ? http://www.postgresql.org/docs/faq