diff options
author | Bruce Momjian | 2001-05-13 02:10:00 +0000 |
---|---|---|
committer | Bruce Momjian | 2001-05-13 02:10:00 +0000 |
commit | 904ba3ff69e1402bc2042807fd1d37f8570bfea1 (patch) | |
tree | 0abff2f3a24df1af18e97b74ddcbba5747540086 /contrib/oracle/Ora2Pg.pm | |
parent | 911a4981763cba58548139dc5c097d8961310d86 (diff) |
Update to ora2pg 1.2.
Diffstat (limited to 'contrib/oracle/Ora2Pg.pm')
-rw-r--r-- | contrib/oracle/Ora2Pg.pm | 420 |
1 files changed, 364 insertions, 56 deletions
diff --git a/contrib/oracle/Ora2Pg.pm b/contrib/oracle/Ora2Pg.pm index c7c99d8f268..95439b88f73 100644 --- a/contrib/oracle/Ora2Pg.pm +++ b/contrib/oracle/Ora2Pg.pm @@ -18,7 +18,7 @@ use vars qw($VERSION); use Carp qw(confess); use DBI; -$VERSION = "1.1"; +$VERSION = "1.2"; =head1 NAME @@ -53,6 +53,50 @@ Ora2Pg - Oracle to PostgreSQL database schema converter exit(0); +or if you only want to extract some tables: + + # Create an instance of the Ora2Pg perl module + my @tables = ('tab1', 'tab2', 'tab3'); + my $schema = new Ora2Pg ( + datasource => $dbsrc, # Database DBD datasource + user => $dbuser, # Database user + password => $dbpwd, # Database password + tables => \@tables, # Tables to extract + debug => 1 # To show somethings when running + ); + +or if you only want to extract the 10 first tables: + + # Create an instance of the Ora2Pg perl module + my $schema = new Ora2Pg ( + datasource => $dbsrc, # Database DBD datasource + user => $dbuser, # Database user + password => $dbpwd, # Database password + max => 10 # 10 first tables to extract + ); + +or if you only want to extract tables 10 to 20: + + # Create an instance of the Ora2Pg perl module + my $schema = new Ora2Pg ( + datasource => $dbsrc, # Database DBD datasource + user => $dbuser, # Database user + password => $dbpwd, # Database password + min => 10 # Begin extraction at indice 10 + max => 20 # End extraction at indice 20 + ); + +To know at which indices table can be found during extraction use the option: + + showtableid => 1 + +To extract all views set the option type as follow: + + type => 'VIEW' + +Default is table schema extraction + + =head1 DESCRIPTION @@ -79,11 +123,13 @@ the connection parameters to the Oracle database. Features must include: - - database schema export (done) - - grant export (done) - - predefined function/trigger export (todo) - - data export (todo) - - sql query converter (todo) + - Database schema export, with unique, primary and foreign key. + - Grants/privileges export by user and group. + - Indexes and unique indexes export. + - Table or view selection (by name and max table) export. + - Predefined function/trigger export (todo) + - Data export (todo) + - Sql query converter (todo) My knowledge regarding database is really poor especially for Oracle so contribution is welcome. @@ -94,6 +140,7 @@ so contribution is welcome. You just need the DBI and DBD::Oracle perl module to be installed + =head1 PUBLIC METHODS =head2 new HASH_OPTIONS @@ -105,6 +152,12 @@ Supported options are: - datasource : DBD datasource (required) - user : DBD user (optional with public access) - password : DBD password (optional with public access) + - type : Type of data to extract, can be TABLE (default) or VIEW + - debug : Print the current state of the parsing + - tables : Extract only the given tables (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 Attempt that this list should grow a little more because all initialization is done by this way. @@ -174,8 +227,31 @@ sub _init die "Error : $DBI::err ... $DBI::errstr\n"; } + $self->{debug} = 0; + $self->{debug} = 1 if ($options{debug}); + + $self->{limited} = (); + $self->{limited} = $options{tables} if ($options{tables}); + + $self->{min} = 0; + $self->{min} = $options{min} if ($options{min}); + + $self->{max} = 0; + $self->{max} = $options{max} if ($options{max}); + + $self->{showtableid} = 0; + $self->{showtableid} = $options{showtableid} if ($options{showtableid}); + + $self->{dbh}->{LongReadLen} = 0; + #$self->{dbh}->{LongTrunkOk} = 1; + # Retreive all table informations - $self->_tables(); + if (!exists $options{type} || ($options{type} eq 'TABLE')) { + $self->_tables(); + } else { + $self->{dbh}->{LongReadLen} = 100000; + $self->_views(); + } # Disconnect from the database $self->{dbh}->disconnect() if ($self->{dbh}); @@ -199,8 +275,9 @@ to the table_info key as array reference. In other way: $self->{tables}{$class_name}{table_info} = [(OWNER,TYPE)]; -TYPE Can be TABLE, VIEW, SYSTEM TABLE, GLOBAL TEMPORARY, LOCAL TEMPORARY, -ALIAS, SYNONYM or a data source specific type identifier. +DBI TYPE can be TABLE, VIEW, SYSTEM TABLE, GLOBAL TEMPORARY, LOCAL TEMPORARY, +ALIAS, SYNONYM or a data source specific type identifier. This only extract +TABLE type. It also get the following informations in the DBI object to affect the main hash of the database structure : @@ -223,23 +300,46 @@ sub _tables my ($self) = @_; # Get all tables information given by the DBI method table_info +print STDERR "Retrieving table information...\n" if ($self->{debug}); my $sth = $self->{dbh}->table_info or die $self->{dbh}->errstr; my @tables_infos = $sth->fetchall_arrayref(); + if ($self->{showtableid}) { + foreach my $table (@tables_infos) { + for (my $i=0; $i<=$#{$table};$i++) { + print STDERR "[", $i+1, "] ${$table}[$i]->[2]\n"; + } + } + return; + } + foreach my $table (@tables_infos) { # Set the table information for each class found + my $i = 1; +print STDERR "Min table dump set to $self->{min}.\n" if ($self->{debug} && $self->{min}); +print STDERR "Max table dump set to $self->{max}.\n" if ($self->{debug} && $self->{max}); foreach my $t (@$table) { - # usually OWNER,TYPE. QUALIFIER is omitted until - # I know what to do with that + # Jump to desired extraction + next if (${@$t}[2] =~ /\$/); + $i++, next if ($self->{min} && ($i < $self->{min})); + last if ($self->{max} && ($i > $self->{max})); + next if (($#{$self->{limited}} >= 0) && !grep(/^${@$t}[2]$/, @{$self->{limited}})); +print STDERR "[$i] " if ($self->{max} || $self->{min}); +print STDERR "Scanning ${@$t}[2] (@$t)...\n" if ($self->{debug}); + + # Check of uniqueness of the table + if (exists $self->{tables}{${@$t}[2]}{field_name}) { + print STDERR "Warning duplicate table ${@$t}[2], SYNONYME ? Skipped.\n"; + next; + } + + # usually OWNER,TYPE. QUALIFIER is omitted until I know what to do with that $self->{tables}{${@$t}[2]}{table_info} = [(${@$t}[1],${@$t}[3])]; # Set the fields information my $sth = $self->{dbh}->prepare("SELECT * FROM ${@$t}[1].${@$t}[2] WHERE 1=0"); if (!defined($sth)) { - $sth = $self->{dbh}->prepare("SELECT * FROM ${@$t}[1].${@$t}[2] WHERE 1=0"); - if (!defined($sth)) { - warn "Can't prepare statement: $DBI::errstr"; - next; - } + warn "Can't prepare statement: $DBI::errstr"; + next; } $sth->execute; if ($sth->err) { @@ -253,10 +353,67 @@ sub _tables @{$self->{tables}{${@$t}[2]}{primary_key}} = &_primary_key($self, ${@$t}[2]); @{$self->{tables}{${@$t}[2]}{unique_key}} = &_unique_key($self, ${@$t}[2]); @{$self->{tables}{${@$t}[2]}{foreign_key}} = &_foreign_key($self, ${@$t}[2]); + ($self->{tables}{${@$t}[2]}{uniqueness}, $self->{tables}{${@$t}[2]}{indexes}) = &_get_indexes($self, ${@$t}[2]); + $self->{tables}{${@$t}[2]}{grants} = &_get_table_privilege($self, ${@$t}[2]); + $i++; } } - ($self->{groups}, $self->{grants}) = &_get_privilege($self); +print STDERR "Retrieving groups/users information...\n" if ($self->{debug}); + $self->{groups} = &_get_roles($self); + +} + + +=head2 _views + +This function is used to retrieve all views information. + +Set the main hash of the views definition $self->{views}. +Keys are the names of all views retrieved from the current +database values are the text definition of the views. + +It then set the main hash as follow: + + # Definition of the view + $self->{views}{$table}{text} = $view_infos{$table}; + # Grants defined on the views + $self->{views}{$table}{grants} = when I find how... + +=cut + +sub _views +{ + my ($self) = @_; + + # Get all views information +print STDERR "Retrieving views information...\n" if ($self->{debug}); + my %view_infos = &_get_views($self); + + if ($self->{showtableid}) { + my $i = 1; + foreach my $table (sort keys %view_infos) { + print STDERR "[$i] $table\n"; + $i++; + } + return; + } + +print STDERR "Min view dump set to $self->{min}.\n" if ($self->{debug} && $self->{min}); +print STDERR "Max view dump set to $self->{max}.\n" if ($self->{debug} && $self->{max}); + my $i = 1; + foreach my $table (sort keys %view_infos) { + # Set the table information for each class found + # Jump to desired extraction + next if ($table =~ /\$/); + $i++, next if ($self->{min} && ($i < $self->{min})); + last if ($self->{max} && ($i > $self->{max})); + next if (($#{$self->{limited}} >= 0) && !grep(/^$table$/, @{$self->{limited}})); +print STDERR "[$i] " if ($self->{max} || $self->{min}); +print STDERR "Scanning $table...\n" if ($self->{debug}); + $self->{views}{$table}{text} = $view_infos{$table}; + $i++; + } } @@ -271,12 +428,29 @@ sub _get_sql_data { my ($self) = @_; - my $sql_output = "-- Generated by Ora2Pg, the Oracle database Schema converter, version $VERSION\n"; - $sql_output .= "-- Copyright 2000 Gilles DAROLD. All rights reserved.\n"; - $sql_output .= "-- Author : <gilles\@darold.net>\n\n"; + my $sql_header = "-- Generated by Ora2Pg, the Oracle database Schema converter, version $VERSION\n"; + $sql_header .= "-- Copyright 2000 Gilles DAROLD. All rights reserved.\n"; + $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"; + + my $sql_output = ""; + + # Process view only + if (exists $self->{views}) { + foreach my $view (sort keys %{$self->{views}}) { + $sql_output .= "CREATE VIEW $view AS $self->{views}{$view}{text};\n"; + } + $sql_output .= "\n"; + + return $sql_header . $sql_output; + } + my @groups = (); + my @users = (); # Dump the database structure as an XML Schema defintion foreach my $table (keys %{$self->{tables}}) { +print STDERR "Dumping table $table...\n" if ($self->{debug}); # Can be: TABLE, VIEW, SYSTEM TABLE, GLOBAL TEMPORARY, $sql_output .= "CREATE ${$self->{tables}{$table}{table_info}}[1] \"\L$table\E\" (\n"; my $sql_ukey = ""; @@ -295,7 +469,7 @@ sub _get_sql_data } if (${$f}[4] ne "") { $sql_output .= " DEFAULT ${$f}[4]"; - } elsif (!${$f}[3]) { + } elsif (!${$f}[3] || (${$f}[3] eq 'N')) { $sql_output .= " NOT NULL"; } # Set the unique key definition @@ -310,8 +484,8 @@ sub _get_sql_data } $sql_ukey =~ s/,$//; $sql_pkey =~ s/,$//; - $sql_output .= "\tCONSTRAINT uk\L$table\E UNIQUE ($sql_ukey),\n" if ($sql_ukey); - $sql_output .= "\tCONSTRAINT pk\L$table\E PRIMARY KEY ($sql_pkey),\n" if ($sql_pkey); + $sql_output .= "\tUNIQUE ($sql_ukey),\n" if ($sql_ukey); + $sql_output .= "\tPRIMARY KEY ($sql_pkey),\n" if ($sql_pkey); # Add constraint definition foreach my $h (@{$self->{tables}{$table}{foreign_key}}) { @@ -323,25 +497,49 @@ sub _get_sql_data my $destname = "$desttable"; my $remote = "${${$h}{$link}{remote}}[$i]"; my $local = "${${$h}{$link}{local}}[$i]"; - $sql_output .= "\tCONSTRAINT fk${i}_\L$table\E FOREIGN KEY ($local) REFERENCES $desttable ($remote),\n"; + $sql_output .= "\tCONSTRAINT ${i}_\L$table\E_fk FOREIGN KEY ($local) REFERENCES $desttable ($remote),\n"; } } } $sql_output =~ s/,$//; $sql_output .= ");\n"; + foreach my $idx (keys %{$self->{tables}{$table}{indexes}}) { + my $columns = join(',', @{$self->{tables}{$table}{indexes}{$idx}}); + my $unique = ''; + $unique = ' UNIQUE' if ($self->{tables}{$table}{uniqueness}{$idx} eq 'UNIQUE'); + $sql_output .= "CREATE$unique INDEX \L$idx\E ON \L$table\E (\L$columns\E);\n"; + } + # Add grant on this table + $sql_output .= "REVOKE ALL ON $table FROM PUBLIC;\n"; + foreach my $grp (keys %{$self->{tables}{$table}{grants}}) { + if (exists $self->{groups}{$grp}) { + $sql_output .= "GRANT " . join(',', @{$self->{tables}{$table}{grants}{$grp}}) . " ON $table TO GROUP $grp;\n"; + push(@groups, $grp) if (!grep(/^$grp$/, @groups)); + } else { + $sql_output .= "GRANT " . join(',', @{$self->{tables}{$table}{grants}{$grp}}) . " ON $table TO $grp;\n"; + push(@users, $grp) if (!grep(/^$grp$/, @users)); + } + } $sql_output .= "\n"; } # Add privilege definition - foreach my $role (keys %{$self->{groups}}) { - $sql_output .= "CREATE GROUP $role;\n"; - $sql_output .= "ALTER GROUP $role ADD USERS " . join(',', @{$self->{groups}{$role}}) . ";\n"; - foreach my $grant (keys %{$self->{grants}{$role}}) { - $sql_output .= "GRANT $grant ON " . join(',', @{$self->{grants}{$role}{$grant}}) . " TO GROUP $role;\n"; +print STDERR "Add groups/users privileges...\n" if ($self->{debug} && exists $self->{groups}); + my $grants = ''; + foreach my $role (@groups) { + next if (!exists $self->{groups}{$role}); + $grants .= "CREATE GROUP $role;\n"; + $grants .= "ALTER GROUP $role ADD USERS " . join(',', @{$self->{groups}{$role}}) . ";\n"; + foreach my $u (@{$self->{groups}{$role}}) { + push(@users, $u) if (!grep(/^$u$/, @users)); } } + foreach my $u (@users) { + $sql_header .= "CREATE USER $u WITH PASSWORD 'secret';\n"; + } + $sql_header .= "\n" . $grants . "\n"; - return $sql_output; + return $sql_header . $sql_output; } @@ -416,6 +614,11 @@ WHERE TABLE_NAME='$table' END $sth->execute or die $sth->errstr; my $data = $sth->fetchall_arrayref(); +if ($self->{debug}) { + foreach my $d (@$data) { +print STDERR "\t$d->[0] => type:$d->[1] , length:$d->[2] , nullable:$d->[3] , default:$d->[4]\n"; + } +} return @$data; @@ -544,27 +747,52 @@ END } -=head2 _get_privilege +=head2 _get_table_privilege TABLE -This function implements a Oracle-native tables grants +This function implements a Oracle-native table grants information. -Return a hash of all groups (roles) with associated users -and a hash of arrays of all grants on related tables. +Return a hash of array of all users and their grants on the +given table. =cut -sub _get_privilege +sub _get_table_privilege +{ + my($self, $table) = @_; + + my @pg_grants = ('DELETE','INSERT','SELECT','UPDATE'); + + # Retrieve all ROLES defined in this database + my $str = "SELECT GRANTEE, PRIVILEGE FROM DBA_TAB_PRIVS WHERE TABLE_NAME='$table' ORDER BY GRANTEE, PRIVILEGE"; + my $sth = $self->{dbh}->prepare($str) or die $self->{dbh}->errstr; + $sth->execute or die $sth->errstr; + my %data = (); + while (my $row = $sth->fetch) { + push(@{$data{$row->[0]}}, $row->[1]) if (grep(/$row->[1]/, @pg_grants)); + } + + return \%data; +} + + +=head2 _get_roles + +This function implements a Oracle-native roles/users +information. + +Return a hash of all groups (roles) as an array of associated users. + +=cut + +sub _get_roles { my($self) = @_; # Retrieve all ROLES defined in this database - my $sth = $self->{dbh}->prepare(<<END) or die $self->{dbh}->errstr; -SELECT - ROLE -FROM DBA_ROLES - ORDER BY ROLE -END + my $str = "SELECT ROLE FROM DBA_ROLES ORDER BY ROLE"; + my $sth = $self->{dbh}->prepare($str) or die $self->{dbh}->errstr; + $sth->execute or die $sth->errstr; my @roles = (); while (my $row = $sth->fetch) { @@ -572,35 +800,115 @@ END } # Get all users associated to these roles - my %data = (); my %groups = (); foreach my $r (@roles) { - my $str = "SELECT GRANTEE FROM DBA_ROLE_PRIVS WHERE GRANTED_ROLE='$r' AND GRANTEE IN (SELECT USERNAME FROM DBA_USERS)"; + my $str = "SELECT GRANTEE FROM DBA_ROLE_PRIVS WHERE GRANTEE <> 'SYS' AND GRANTEE <> 'SYSTEM' AND GRANTED_ROLE='$r' AND GRANTEE IN (SELECT USERNAME FROM DBA_USERS)"; $sth = $self->{dbh}->prepare($str) or die $self->{dbh}->errstr; $sth->execute or die $sth->errstr; my @users = (); while (my $row = $sth->fetch) { - next if ($row->[0] eq 'SYSTEM'); push(@users, $row->[0]); } - # Don't process roles relatives to DBA - next if (grep(/^DBSNMP$/, @users)); - next if (grep(/^SYS$/, @users)); + $groups{$r} = \@users if ($#users >= 0); + } - $groups{$r} = \@users; + return \%groups; +} - $str = "SELECT PRIVILEGE,TABLE_NAME FROM DBA_TAB_PRIVS WHERE GRANTEE='$r'"; - $sth = $self->{dbh}->prepare($str) or die $self->{dbh}->errstr; - $sth->execute or die $sth->errstr; - my @grants = (); - while (my $row = $sth->fetch) { - push(@{$data{$r}{"${@$row}[0]"}}, ${@$row}[1]); - } + +=head2 _get_indexes TABLE + +This function implements a Oracle-native indexes +information. + +Return an array of all indexes name which are not primary keys +for the given table. + +Note: Indexes name must be created like this tablename_fieldname +else they will not be retrieved or if tablename false in the output +fieldname. + +=cut + +sub _get_indexes +{ + my($self, $table) = @_; + + # Retrieve all indexes + my $str = "SELECT DISTINCT DBA_IND_COLUMNS.INDEX_NAME, DBA_IND_COLUMNS.COLUMN_NAME, DBA_INDEXES.UNIQUENESS FROM DBA_IND_COLUMNS, DBA_INDEXES WHERE DBA_IND_COLUMNS.TABLE_NAME='$table' AND DBA_INDEXES.INDEX_NAME=DBA_IND_COLUMNS.INDEX_NAME AND DBA_IND_COLUMNS.INDEX_NAME NOT IN (SELECT CONSTRAINT_NAME FROM ALL_CONSTRAINTS WHERE TABLE_NAME='$table')"; + my $sth = $self->{dbh}->prepare($str) or die $self->{dbh}->errstr; + $sth->execute or die $sth->errstr; + + my %data = (); + my %unique = (); + while (my $row = $sth->fetch) { + $unique{$row->[0]} = $row->[2]; + push(@{$data{$row->[0]}}, $row->[1]); + } + + return \%unique, \%data; +} + + +=head2 _get_sequences TABLE + +This function implements a Oracle-native sequence +information. + +Return a hash of array of sequence name with MIN_VALUE, MAX_VALUE, +INCREMENT and LAST_NUMBER for the given table. + +Not working yet. + +=cut + +sub _get_sequences +{ + my($self, $table) = @_; + + # Retrieve all indexes + my $str = "SELECT SEQUENCE_NAME, MIN_VALUE, MAX_VALUE, INCREMENT_BY, LAST_NUMBER FROM DBA_SEQUENCES WHERE SEQUENCE_OWNER <> 'SYS' AND SEQUENCE_OWNER <> 'SYSTEM'"; + my $sth = $self->{dbh}->prepare($str) or die $self->{dbh}->errstr; + $sth->execute or die $sth->errstr; + + my %data = (); + while (my $row = $sth->fetch) { + # next if ($row->[0] !~ /${table}_/); + # push(@data, $row->[0]); + } + + return %data; +} + + +=head2 _get_views + +This function implements a Oracle-native views information. + +Return a hash of array of sequence name with MIN_VALUE, MAX_VALUE, +INCREMENT and LAST_NUMBER for the given table. + +=cut + +sub _get_views +{ + my($self) = @_; + + # Retrieve all views + my $str = "SELECT VIEW_NAME,TEXT FROM DBA_VIEWS WHERE OWNER <> 'SYS' AND OWNER <> 'SYSTEM'"; + my $sth = $self->{dbh}->prepare($str) or die $self->{dbh}->errstr; + $sth->execute or die $sth->errstr; + + my %data = (); + while (my $row = $sth->fetch) { + $data{$row->[0]} = $row->[1]; } - return \%groups, \%data; + return %data; } + + 1; __END__ |