############################################################################### # Codestriker: Copyright (c) 2001, 2002 David Sitsky. All rights reserved. # sits@users.sourceforge.net # # This program is free software; you can redistribute it and modify it under # the terms of the GPL. package Codestriker::DB::Oracle; use strict; use DBI; use Codestriker; use Codestriker::DB::Database; # Module for handling an Oracle database. @Codestriker::DB::Oracle::ISA = ("Codestriker::DB::Database"); # Type mappings. my $_TYPE = { $Codestriker::DB::Column::TYPE->{TEXT} => "clob", $Codestriker::DB::Column::TYPE->{VARCHAR} => "varchar2", $Codestriker::DB::Column::TYPE->{INT32} => "number(10)", $Codestriker::DB::Column::TYPE->{INT16} => "number(4)", $Codestriker::DB::Column::TYPE->{DATETIME} => "date", $Codestriker::DB::Column::TYPE->{FLOAT} => "float" }; # Create a new Oracle database object. sub new { my $type = shift; # Database is parent class. my $self = Codestriker::DB::Database->new(); $self->{sequence_created} = 0; return bless $self, $type; } # Return the DBD module this is dependent on. sub get_module_dependencies { return { name => 'DBD::Oracle', version => '0' }; } # Retrieve a database connection. sub get_connection { my $self = shift; # Oracle support transactions, don't enable auto_commit. my $dbh = $self->_get_connection(0, 1); # Make sure the default date type is set to something used consistently # in Codestriker. $dbh->do("ALTER session SET NLS_DATE_FORMAT='YYYY-MM-DD HH24:MI:SS'"); return $dbh; } # Method for retrieving the list of current tables attached to the database. # For oracle, $dbh->tables doesn't work, need to retrieve data from the # user_tables table. sub get_tables() { my $self = shift; my @tables = (); my $table_select = $self->{dbh}->prepare_cached("SELECT table_name FROM user_tables"); $table_select->execute(); while (my ($table_name) = $table_select->fetchrow_array()) { push @tables, $table_name; } $table_select->finish(); return @tables; } # Return the mapping for a specific type. sub _map_type { my ($self, $type) = @_; return $_TYPE->{$type}; } # Oracle implements autoincrements with triggers. sub _get_autoincrement_type { return ""; } # Create the table in the database for the specified table, and with the # provided type mappings. sub create_table { my ($self, $table) = @_; # Let the base class actually do the work in creating the table. $self->SUPER::create_table($table); # Create the necessary triggers for any autoincrement fields. foreach my $column (@{$table->get_columns()}) { if ($column->is_autoincrement()) { print "Creating autoincrement trigger for table: " . $table->get_name() . " field: " . $column->get_name() . "\n"; $self->_oracle_handle_auto_increment($table->get_name(), $column->get_name()); } } } # Oracle-specific routine for creating a trigger on a new row insert to # automatically assign a value to the specified fieldname from a sequence. # This is used since Oracle doesn't support auto-increment or default values # for fields. sub _oracle_handle_auto_increment { my ($self, $tablename, $fieldname) = @_; my $dbh = $self->{dbh}; # Make sure the sequence is present in the database for the trigger to # work. eval { if ($self->{sequence_created} == 0) { $dbh->do("CREATE SEQUENCE sequence"); print "Created sequence\n"; $self->{sequence_created} = 1; } # Now create the actual trigger on the table. $dbh->do("CREATE TRIGGER ${tablename}_${fieldname}_ins_row " . "BEFORE INSERT ON ${tablename} FOR EACH ROW " . "DECLARE newid integer; " . "BEGIN " . "IF (:NEW.${fieldname} IS NULL) " . "THEN " . "SELECT sequence.NextVal INTO newid FROM DUAL; " . ":NEW.${fieldname} := newid; " . "END IF; " . "END;"); print "Created trigger\n"; $dbh->commit(); }; if ($@) { eval { $self->rollback() }; die "Unable to create sequence/trigger.\n"; } } # Add a field to a specific table. If the field already exists, then catch # the error and continue silently. The SYNTAX for SQL Server is slightly # different to standard SQL, there is no "COLUMN" keyword after "ADD". sub add_field { my ($self, $table, $field, $definition) = @_; my $dbh = $self->{dbh}; my $rc = 0; eval { $dbh->{PrintError} = 0; my $field_type = $self->_map_type($definition); $dbh->do("ALTER TABLE $table ADD $field $field_type"); print "Added new field $field to table $table.\n"; $rc = 1; $self->commit(); }; if ($@) { eval { $self->rollback() }; } $dbh->{PrintError} = 1; return $rc; } # Indicate if the LIKE operator can be applied on a "text" field. # For Oracle, this is false. sub has_like_operator_for_text_field { my $self = shift; return 0; } # Function for generating an SQL subexpression for a case insensitive LIKE # operation. sub case_insensitive_like { my ($self, $field, $expression) = @_; # Convert the field and expression to lower case to get case insensitivity. my $field_lower = "lower($field)"; my $expression_lower = $expression; $expression_lower =~ tr/[A-Z]/[a-z]/; $expression_lower = $self->{dbh}->quote($expression_lower); return "$field_lower LIKE $expression_lower"; } 1;