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