# -- # Kernel/System/DB/oracle.pm - oracle database backend # Copyright (C) 2001-2007 OTRS GmbH, http://otrs.org/ # -- # $Id: oracle.pm,v 1.27 2007/07/23 08:57:50 martin Exp $ # -- # This software comes with ABSOLUTELY NO WARRANTY. For details, see # the enclosed file COPYING for license information (GPL). If you # did not receive this file, see http://www.gnu.org/licenses/gpl.txt. # -- package Kernel::System::DB::oracle; use strict; use warnings; use vars qw($VERSION); $VERSION = '$Revision: 1.27 $'; $VERSION =~ s/^\$.*:\W(.*)\W.+?$/$1/; sub new { my $Type = shift; my %Param = @_; # allocate new hash for object my $Self = {}; bless ($Self, $Type); # check needed objects foreach (keys %Param) { $Self->{$_} = $Param{$_}; } return $Self; } sub LoadPreferences { my $Self = shift; my %Param = @_; # db settings $Self->{'DB::Limit'} = 0; $Self->{'DB::DirectBlob'} = 0; $Self->{'DB::QuoteSingle'} = '\''; $Self->{'DB::QuoteBack'} = 0; $Self->{'DB::QuoteSemicolon'} = ''; $Self->{'DB::Attribute'} = { LongTruncOk => 1, LongReadLen => 4*1024*1024, }; # set current time stamp if different to "current_timestamp" $Self->{'DB::CurrentTimestamp'} = ''; # set encoding of selected data to utf8 $Self->{'DB::Encode'} = 0; # shell setting $Self->{'DB::Comment'} = '-- '; $Self->{'DB::ShellCommit'} = ';'; $Self->{'DB::ShellConnect'} = 'SET DEFINE OFF'; return 1; } sub Quote { my $Self = shift; my $Text = shift; if (defined(${$Text})) { if ($Self->{'DB::QuoteBack'}) { ${$Text} =~ s/\\/$Self->{'DB::QuoteBack'}\\/g; } if ($Self->{'DB::QuoteSingle'}) { ${$Text} =~ s/'/$Self->{'DB::QuoteSingle'}'/g; } if ($Self->{'DB::QuoteSemicolon'}) { ${$Text} =~ s/;/$Self->{'DB::QuoteSemicolon'};/g; } } return $Text; } sub DatabaseCreate { my $Self = shift; my %Param = @_; # check needed stuff if (!$Param{Name}) { $Self->{LogObject}->Log(Priority => 'error', Message => "Need Name!"); return; } # return SQL return ("CREATE DATABASE $Param{Name}"); } sub DatabaseDrop { my $Self = shift; my %Param = @_; # check needed stuff if (!$Param{Name}) { $Self->{LogObject}->Log(Priority => 'error', Message => "Need Name!"); return; } # return SQL return ("DROP DATABASE $Param{Name}"); } sub TableCreate { my $Self = shift; my @Param = @_; my $SQLStart = ''; my $SQLEnd = ''; my $SQL = ''; my @Column = (); my $TableName = ''; my $ForeignKey = (); my %Foreign = (); my $IndexCurrent = (); my %Index = (); my $UniqCurrent = (); my %Uniq = (); my $PrimaryKey = ''; my @Return = (); my @Return2 = (); foreach my $Tag (@Param) { if ($Tag->{Tag} eq 'Table' && $Tag->{TagType} eq 'Start') { if ($Self->{ConfigObject}->Get('Database::ShellOutput')) { $SQLStart .= $Self->{'DB::Comment'}."----------------------------------------------------------\n"; $SQLStart .= $Self->{'DB::Comment'}." create table $Tag->{Name}\n"; $SQLStart .= $Self->{'DB::Comment'}."----------------------------------------------------------\n"; } } if (($Tag->{Tag} eq 'Table' || $Tag->{Tag} eq 'TableCreate') && $Tag->{TagType} eq 'Start') { $SQLStart .= "CREATE TABLE $Tag->{Name} (\n"; $TableName = $Tag->{Name}; } if (($Tag->{Tag} eq 'Table' || $Tag->{Tag} eq 'TableCreate') && $Tag->{TagType} eq 'End') { $SQLEnd .= "\n)"; } elsif ($Tag->{Tag} eq 'Column' && $Tag->{TagType} eq 'Start') { push (@Column, $Tag); } elsif ($Tag->{Tag} eq 'Index' && $Tag->{TagType} eq 'Start') { $IndexCurrent = $Tag->{Name}; } elsif ($Tag->{Tag} eq 'IndexColumn' && $Tag->{TagType} eq 'Start') { push (@{$Index{$IndexCurrent}}, $Tag); } elsif ($Tag->{Tag} eq 'Unique' && $Tag->{TagType} eq 'Start') { $UniqCurrent = $Tag->{Name} || $TableName.'_U_'.int(rand(999)); } elsif ($Tag->{Tag} eq 'UniqueColumn' && $Tag->{TagType} eq 'Start') { push (@{$Uniq{$UniqCurrent}}, $Tag); } elsif ($Tag->{Tag} eq 'ForeignKey' && $Tag->{TagType} eq 'Start') { $ForeignKey = $Tag->{ForeignTable}; } elsif ($Tag->{Tag} eq 'Reference' && $Tag->{TagType} eq 'Start') { push (@{$Foreign{$ForeignKey}}, $Tag); } } foreach my $Tag (@Column) { # type translation $Tag = $Self->_TypeTranslation($Tag); if ($SQL) { $SQL .= ",\n"; } # normal data type $SQL .= " $Tag->{Name} $Tag->{Type}"; if ($Tag->{Required} =~ /^true$/i) { $SQL .= " NOT NULL"; } # add primary key my $Constraint = $TableName; if (length($Constraint) > 26) { $Constraint = substr($Constraint, 0, 24); $Constraint .= int(rand(99)); } if ($Tag->{PrimaryKey} && $Tag->{PrimaryKey} =~ /true/i) { push (@Return2, "ALTER TABLE $TableName ADD CONSTRAINT $Constraint"."_PK PRIMARY KEY ($Tag->{Name})"); } # auto increment if ($Tag->{AutoIncrement} && $Tag->{AutoIncrement} =~ /^true$/i) { my $Shell = ''; if ($Self->{ConfigObject}->Get('Database::ShellOutput')) { $Shell = "/\n--"; } push (@Return2, "DROP SEQUENCE $Constraint"."_seq"); push (@Return2, "CREATE SEQUENCE $Constraint"."_seq"); push (@Return2, "CREATE OR REPLACE TRIGGER $Constraint"."_s_t\n". "before insert on $TableName\n". "for each row\n". "begin\n". " select $Constraint"."_seq.nextval\n". " into :new.$Tag->{Name}\n". " from dual;\nend;\n". "$Shell", ); } } # add uniq my $UniqCunter = 0; foreach my $Name (keys %Uniq) { $UniqCunter++; if ($SQL) { $SQL .= ",\n"; } $SQL .= " CONSTRAINT $TableName"."_U_$UniqCunter UNIQUE ("; my @Array = @{$Uniq{$Name}}; foreach (0..$#Array) { if ($_ > 0) { $SQL .= ", "; } $SQL .= $Array[$_]->{Name}; } $SQL .= ")"; } push(@Return, $SQLStart.$SQL.$SQLEnd, @Return2); # add indexs foreach my $Name (keys %Index) { push (@Return, $Self->IndexCreate( TableName => $TableName, Name => $Name, Data => $Index{$Name}, )); } # add uniq # foreach my $Name (keys %Uniq) { # push (@Return, $Self->UniqueCreate( # TableName => $TableName, # Name => $Name, # Data => $Uniq{$Name}, # )); # } # add foreign keys foreach my $ForeignKey (keys %Foreign) { my @Array = @{$Foreign{$ForeignKey}}; foreach (0..$#Array) { push (@{$Self->{Post}}, $Self->ForeignKeyCreate( LocalTableName => $TableName, Local => $Array[$_]->{Local}, ForeignTableName => $ForeignKey, Foreign => $Array[$_]->{Foreign}, )); } } return @Return; } sub TableDrop { my $Self = shift; my @Param = @_; my $SQL = ''; foreach my $Tag (@Param) { if ($Tag->{Tag} eq 'Table' && $Tag->{TagType} eq 'Start') { if ($Self->{ConfigObject}->Get('Database::ShellOutput')) { $SQL .= $Self->{'DB::Comment'}."----------------------------------------------------------\n"; $SQL .= $Self->{'DB::Comment'}." drop table $Tag->{Name}\n"; $SQL .= $Self->{'DB::Comment'}."----------------------------------------------------------\n"; } } $SQL .= "DROP TABLE $Tag->{Name} CASCADE CONSTRAINTS"; return ($SQL); } return (); } sub TableAlter { my $Self = shift; my @Param = @_; my $SQLStart = ''; my @SQL = (); my $Table = ''; foreach my $Tag (@Param) { if ($Tag->{Tag} eq 'TableAlter' && $Tag->{TagType} eq 'Start') { if ($Self->{ConfigObject}->Get('Database::ShellOutput')) { $SQLStart .= $Self->{'DB::Comment'}."----------------------------------------------------------\n"; $SQLStart .= $Self->{'DB::Comment'}." alter table $Tag->{Name}\n"; $SQLStart .= $Self->{'DB::Comment'}."----------------------------------------------------------\n"; } $SQLStart .= "ALTER TABLE $Tag->{Name}"; $Table = $Tag->{Name}; } elsif ($Tag->{Tag} eq 'ColumnAdd' && $Tag->{TagType} eq 'Start') { # Type translation $Tag = $Self->_TypeTranslation($Tag); # normal data type my $SQLEnd = $SQLStart." ADD $Tag->{Name} $Tag->{Type}"; if (!$Tag->{Default} && $Tag->{Required} && $Tag->{Required} =~ /^true$/i) { $SQLEnd .= " NOT NULL"; } # auto increment if ($Tag->{AutoIncrement} && $Tag->{AutoIncrement} =~ /^true$/i) { $SQLEnd .= " AUTO_INCREMENT"; } # add primary key if ($Tag->{PrimaryKey} && $Tag->{PrimaryKey} =~ /true/i) { $SQLEnd .= " PRIMARY KEY($Tag->{Name})"; } push (@SQL, $SQLEnd); # default values if ($Tag->{Default}) { if ($Tag->{Type} =~ /int/i) { push (@SQL, "UPDATE $Table SET $Tag->{Name} = $Tag->{Default} WHERE $Tag->{Name} IS NULL"); } else { push (@SQL, "UPDATE $Table SET $Tag->{Name} = $Tag->{Default} WHERE '$Tag->{Name}' IS NULL"); } if ($Tag->{Required} && $Tag->{Required} =~ /^true$/i) { push (@SQL, "ALTER TABLE $Table MODIFY $Tag->{Name} $Tag->{Type} NOT NULL"); } } } elsif ($Tag->{Tag} eq 'ColumnChange' && $Tag->{TagType} eq 'Start') { # Type translation $Tag = $Self->_TypeTranslation($Tag); # rename oldname to newname if ($Tag->{NameOld} ne $Tag->{NameNew}) { push (@SQL, $SQLStart." RENAME COLUMN $Tag->{NameOld} TO $Tag->{NameNew}"); } # alter table name modify if (!$Tag->{Name} && $Tag->{NameNew}) { $Tag->{Name} = $Tag->{NameNew}; } if (!$Tag->{Name} && $Tag->{NameOld}) { $Tag->{Name} = $Tag->{NameOld}; } my $SQLEnd = $SQLStart." MODIFY $Tag->{Name} $Tag->{Type}"; if ($Tag->{Required} && $Tag->{Required} =~ /^true$/i) { $SQLEnd .= " NOT NULL"; } else { $SQLEnd .= " NULL"; } # auto increment if ($Tag->{AutoIncrement} && $Tag->{AutoIncrement} =~ /^true$/i) { $SQLEnd .= " AUTO_INCREMENT"; } # add primary key if ($Tag->{PrimaryKey} && $Tag->{PrimaryKey} =~ /true/i) { $SQLEnd .= " PRIMARY KEY($Tag->{Name})"; } push (@SQL, $SQLEnd); } elsif ($Tag->{Tag} eq 'ColumnDrop' && $Tag->{TagType} eq 'Start') { my $SQLEnd = $SQLStart." DROP COLUMN $Tag->{Name}"; push (@SQL, $SQLEnd); } } return @SQL; } sub IndexCreate { my $Self = shift; my %Param = @_; # check needed stuff foreach (qw(TableName Name Data)) { if (!$Param{$_}) { $Self->{LogObject}->Log(Priority => 'error', Message => "Need $_!"); return; } } my $Index = $Param{Name}; if (length($Index) > 30) { $Index = substr($Index, 0, 28); $Index .= int(rand(99)); } my $SQL = "CREATE INDEX $Index ON $Param{TableName} ("; my @Array = @{$Param{'Data'}}; foreach (0..$#Array) { if ($_ > 0) { $SQL .= ", "; } $SQL .= $Array[$_]->{Name}; if ($Array[$_]->{Size}) { # $SQL .= "($Array[$_]->{Size})"; } } $SQL .= ")"; # return SQL return ($SQL); } sub IndexDrop { my $Self = shift; my %Param = @_; # check needed stuff foreach (qw(TableName Name)) { if (!$Param{$_}) { $Self->{LogObject}->Log(Priority => 'error', Message => "Need $_!"); return; } } my $SQL = "DROP INDEX $Param{Name}"; return ($SQL); } sub ForeignKeyCreate { my $Self = shift; my %Param = @_; # check needed stuff foreach (qw(LocalTableName Local ForeignTableName Foreign)) { if (!$Param{$_}) { $Self->{LogObject}->Log(Priority => 'error', Message => "Need $_!"); return; } } my $ForeignKey = "fk_$Param{LocalTableName}_$Param{Local}_$Param{Foreign}"; if (length($ForeignKey) > 30) { $ForeignKey = substr($ForeignKey, 0, 28); $ForeignKey .= int(rand(99)); } my $SQL = "ALTER TABLE $Param{LocalTableName} ADD CONSTRAINT $ForeignKey FOREIGN KEY ("; $SQL .= "$Param{Local}) REFERENCES "; $SQL .= "$Param{ForeignTableName}($Param{Foreign})"; # return SQL return ($SQL); } sub ForeignKeyDrop { my $Self = shift; my %Param = @_; # check needed stuff foreach (qw(TableName Name)) { if (!$Param{$_}) { $Self->{LogObject}->Log(Priority => 'error', Message => "Need $_!"); return; } } my $SQL = "ALTER TABLE $Param{TableName} DISABLE CONSTRAINT $Param{Name}"; return ($SQL); } sub UniqueCreate { my $Self = shift; my %Param = @_; # check needed stuff foreach (qw(TableName Name Data)) { if (!$Param{$_}) { $Self->{LogObject}->Log(Priority => 'error', Message => "Need $_!"); return; } } my $SQL = "ALTER TABLE $Param{TableName} ADD CONSTRAINT $Param{Name} UNIQUE ("; my @Array = @{$Param{'Data'}}; foreach (0..$#Array) { if ($_ > 0) { $SQL .= ", "; } $SQL .= $Array[$_]->{Name}; } $SQL .= ")"; # return SQL return ($SQL); } sub UniqueDrop { my $Self = shift; my %Param = @_; # check needed stuff foreach (qw(TableName Name)) { if (!$Param{$_}) { $Self->{LogObject}->Log(Priority => 'error', Message => "Need $_!"); return; } } my $SQL = "ALTER TABLE $Param{TableName} DROP CONSTRAINT $Param{Name}"; return ($SQL); } sub Insert { my $Self = shift; my @Param = @_; my $SQL = ''; my @Keys = (); my @Values = (); foreach my $Tag (@Param) { if ($Tag->{Tag} eq 'Insert' && $Tag->{TagType} eq 'Start') { if ($Self->{ConfigObject}->Get('Database::ShellOutput')) { $SQL .= $Self->{'DB::Comment'}."----------------------------------------------------------\n"; $SQL .= $Self->{'DB::Comment'}." insert into table $Tag->{Table}\n"; $SQL .= $Self->{'DB::Comment'}."----------------------------------------------------------\n"; } $SQL .= "INSERT INTO $Tag->{Table} "; } if ($Tag->{Tag} eq 'Data' && $Tag->{TagType} eq 'Start') { $Tag->{Key} = ${$Self->Quote(\$Tag->{Key})}; push (@Keys, $Tag->{Key}); my $Value; if (defined($Tag->{Value})) { $Value = $Tag->{Value}; $Self->{LogObject}->Log( Priority => 'error', Message => "The content for inserts is not longer appreciated attribut Value, use Content from now on! Reason: You can't use new lines in attributes.", ); } elsif (defined($Tag->{Content})) { $Value = $Tag->{Content}; } else { $Value = ''; } if ($Tag->{Type} && $Tag->{Type} eq 'Quote') { $Value = "'".${$Self->Quote(\$Value)}."'"; } else { $Value = ${$Self->Quote(\$Value)}; } push (@Values, $Value); } } my $Key = ''; foreach (@Keys) { if ($Key ne '') { $Key .= ", "; } $Key .= $_; } my $Value = ''; foreach my $Tmp (@Values) { if ($Value ne '') { $Value .= ", "; } if ($Tmp eq 'current_timestamp') { if ($Self->{ConfigObject}->Get('Database::ShellOutput')) { $Value .= $Tmp; } else { my $Timestamp = $Self->{TimeObject}->CurrentTimestamp(); $Value .= '\''.$Timestamp.'\''; } } else { if ($Self->{ConfigObject}->Get('Database::ShellOutput')) { $Tmp =~ s/\n/\r/g; } $Value .= $Tmp; } } $SQL .= "($Key)\n VALUES\n ($Value)"; return ($SQL); } sub _TypeTranslation { my $Self = shift; my $Tag = shift; # Type translation if ($Tag->{Type} =~ /^DATE$/i) { $Tag->{Type} = 'DATE'; } elsif ($Tag->{Type} =~ /^integer$/i) { $Tag->{Type} = 'NUMBER'; } elsif ($Tag->{Type} =~ /^smallint$/i) { $Tag->{Type} = 'NUMBER (5, 0)'; } elsif ($Tag->{Type} =~ /^bigint$/i) { $Tag->{Type} = 'NUMBER (20, 0)'; } elsif ($Tag->{Type} =~ /^longblob$/i) { $Tag->{Type} = 'CLOB'; } elsif ($Tag->{Type} =~ /^VARCHAR$/i) { $Tag->{Type} = "VARCHAR2 ($Tag->{Size})"; if ($Tag->{Size} > 4000) { $Tag->{Type} = "CLOB"; } } elsif ($Tag->{Type} =~ /^DECIMAL$/i) { $Tag->{Type} = "DECIMAL ($Tag->{Size})"; } return $Tag; } 1;