Commit e18d4374 authored by mkanat%bugzilla.org's avatar mkanat%bugzilla.org

Bug 310717: [Oracle] Bugzilla::DB::Oracle module

Bug 310718: [Oracle] Bugzilla::DB::Schema::Oracle module Patch By Xiaoou Wu <xiaoou.wu@oracle.com> r=mkanat, a=mkanat
parent 961cc62c
......@@ -395,6 +395,13 @@ use constant DB_MODULE => {
version => '1.45',
},
name => 'PostgreSQL'},
'oracle'=> {db => 'Bugzilla::DB::Oracle', db_version => '10.01.0',
dbd => {
package => 'DBD-Oracle',
module => 'DBD::Oracle',
version => '1.19',
},
name => 'Oracle'},
};
# The user who should be considered "root" when we're giving
......
......@@ -301,6 +301,62 @@ sub import {
$Exporter::ExportLevel-- if $is_exporter;
}
sub bz_lock_tables {
my ($self, @tables) = @_;
my $list = join(', ', @tables);
# Check first if there was no lock before
if ($self->{private_bz_tables_locked}) {
ThrowCodeError("already_locked", { current => $self->{private_bz_tables_locked},
new => $list });
} else {
my %read_tables;
my %write_tables;
foreach my $table (@tables) {
$table =~ /^([\d\w]+)([\s]+AS[\s]+[\d\w]+)?[\s]+(WRITE|READ)$/i;
my $table_name = $1;
if ($3 =~ /READ/i) {
if (!exists $read_tables{$table_name}) {
$read_tables{$table_name} = undef;
}
}
else {
if (!exists $write_tables{$table_name}) {
$write_tables{$table_name} = undef;
}
}
}
# Begin Transaction
$self->bz_start_transaction();
Bugzilla->dbh->do('LOCK TABLE ' . join(', ', keys %read_tables) .
' IN ROW SHARE MODE') if keys %read_tables;
Bugzilla->dbh->do('LOCK TABLE ' . join(', ', keys %write_tables) .
' IN ROW EXCLUSIVE MODE') if keys %write_tables;
$self->{private_bz_tables_locked} = $list;
}
}
sub bz_unlock_tables {
my ($self, $abort) = @_;
# Check first if there was previous matching lock
if (!$self->{private_bz_tables_locked}) {
# Abort is allowed even without previous lock for error handling
return if $abort;
ThrowCodeError("no_matching_lock");
} else {
$self->{private_bz_tables_locked} = "";
# End transaction, tables will be unlocked automatically
if ($abort) {
$self->bz_rollback_transaction();
} else {
$self->bz_commit_transaction();
}
}
}
sub sql_istrcmp {
my ($self, $left, $right, $op) = @_;
$op ||= "=";
......
......@@ -158,62 +158,6 @@ sub sql_string_concat {
return '(CAST(' . join(' AS text) || CAST(', @params) . ' AS text))';
}
sub bz_lock_tables {
my ($self, @tables) = @_;
my $list = join(', ', @tables);
# Check first if there was no lock before
if ($self->{private_bz_tables_locked}) {
ThrowCodeError("already_locked", { current => $self->{private_bz_tables_locked},
new => $list });
} else {
my %read_tables;
my %write_tables;
foreach my $table (@tables) {
$table =~ /^([\d\w]+)([\s]+AS[\s]+[\d\w]+)?[\s]+(WRITE|READ)$/i;
my $table_name = $1;
if ($3 =~ /READ/i) {
if (!exists $read_tables{$table_name}) {
$read_tables{$table_name} = undef;
}
}
else {
if (!exists $write_tables{$table_name}) {
$write_tables{$table_name} = undef;
}
}
}
# Begin Transaction
$self->bz_start_transaction();
Bugzilla->dbh->do('LOCK TABLE ' . join(', ', keys %read_tables) .
' IN ROW SHARE MODE') if keys %read_tables;
Bugzilla->dbh->do('LOCK TABLE ' . join(', ', keys %write_tables) .
' IN ROW EXCLUSIVE MODE') if keys %write_tables;
$self->{private_bz_tables_locked} = $list;
}
}
sub bz_unlock_tables {
my ($self, $abort) = @_;
# Check first if there was previous matching lock
if (!$self->{private_bz_tables_locked}) {
# Abort is allowed even without previous lock for error handling
return if $abort;
ThrowCodeError("no_matching_lock");
} else {
$self->{private_bz_tables_locked} = "";
# End transaction, tables will be unlocked automatically
if ($abort) {
$self->bz_rollback_transaction();
} else {
$self->bz_commit_transaction();
}
}
}
# Tell us whether or not a particular sequence exists in the DB.
sub bz_sequence_exists {
my ($self, $seq_name) = @_;
......
......@@ -206,6 +206,7 @@ update this column in this table."
=cut
use constant SCHEMA_VERSION => '2.00';
use constant ADD_COLUMN => 'ADD COLUMN';
use constant ABSTRACT_SCHEMA => {
# BUG-RELATED TABLES
......@@ -1750,7 +1751,7 @@ sub get_add_column_ddl {
my ($self, $table, $column, $definition, $init_value) = @_;
my @statements;
push(@statements, "ALTER TABLE $table ADD COLUMN $column " .
push(@statements, "ALTER TABLE $table". ADD_COLUMN ." $column " .
$self->get_type_ddl($definition));
# XXX - Note that although this works for MySQL, most databases will fail
......
# -*- Mode: perl; indent-tabs-mode: nil -*-
#
# The contents of this file are subject to the Mozilla Public
# License Version 1.1 (the "License"); you may not use this file
# except in compliance with the License. You may obtain a copy of
# the License at http://www.mozilla.org/MPL/
#
# Software distributed under the License is distributed on an "AS
# IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
# implied. See the License for the specific language governing
# rights and limitations under the License.
#
# The Original Code is the Bugzilla Bug Tracking System.
#
# The Initial Developer of the Original Code is Oracle Corporation.
# Portions created by Oracle are Copyright (C) 2007 Oracle Corporation.
# All Rights Reserved.
#
# Contributor(s): Lance Larsh <lance.larsh@oracle.com>
# Xiaoou Wu <xiaoou.wu@oracle.com>
# Max Kanat-Alexander <mkanat@bugzilla.org>
package Bugzilla::DB::Schema::Oracle;
###############################################################################
#
# DB::Schema implementation for Oracle
#
###############################################################################
use strict;
use base qw(Bugzilla::DB::Schema);
use Carp qw(confess);
use Digest::MD5 qw(md5_hex);
use Bugzilla::Util;
use constant ADD_COLUMN => 'ADD';
#------------------------------------------------------------------------------
sub _initialize {
my $self = shift;
$self = $self->SUPER::_initialize(@_);
$self->{db_specific} = {
BOOLEAN => 'integer',
FALSE => '0',
TRUE => '1',
INT1 => 'integer',
INT2 => 'integer',
INT3 => 'integer',
INT4 => 'integer',
SMALLSERIAL => 'integer',
MEDIUMSERIAL => 'integer',
INTSERIAL => 'integer',
TINYTEXT => 'varchar(255)',
MEDIUMTEXT => 'varchar(4000)',
LONGTEXT => 'clob',
LONGBLOB => 'blob',
DATETIME => 'date',
};
$self->_adjust_schema;
return $self;
} #eosub--_initialize
#--------------------------------------------------------------------
sub get_table_ddl {
my $self = shift;
my $table = shift;
unshift @_, $table;
my @ddl = $self->SUPER::get_table_ddl(@_);
my @fields = @{ $self->{abstract_schema}{$table}{FIELDS} || [] };
while (@fields) {
my $field_name = shift @fields;
my $field_info = shift @fields;
# Create triggers to deal with empty string.
if ( $field_info->{TYPE} =~ /varchar|TEXT/i
&& $field_info->{NOTNULL} ) {
push (@ddl, _get_notnull_trigger_ddl($table, $field_name));
}
# Create sequences and triggers to emulate SERIAL datatypes.
if ( $field_info->{TYPE} =~ /SERIAL/i ) {
push (@ddl, _get_create_seq_ddl($table, $field_name));
}
}
return @ddl;
} #eosub--get_table_ddl
# Extend superclass method to create Oracle Text indexes if index type
# is FULLTEXT from schema. Returns a "create index" SQL statement.
sub _get_create_index_ddl {
my ($self, $table_name, $index_name, $index_fields, $index_type) = @_;
$index_name = "idx_" . substr(md5_hex($index_name),0,20);
if ($index_type eq 'FULLTEXT') {
my $sql = "CREATE INDEX $index_name ON $table_name ("
. join(',',@$index_fields)
. ") INDEXTYPE IS CTXSYS.CONTEXT "
. " PARAMETERS('LEXER BZ_LEX SYNC(ON COMMIT)')" ;
return $sql;
}
return($self->SUPER::_get_create_index_ddl($table_name, $index_name,
$index_fields, $index_type));
} #eosub--_get_create_index_ddl
# Oracle supports the use of FOREIGN KEY integrity constraints
# to define the referential integrity actions, including:
# - Update and delete No Action (default)
# - Delete CASCADE
# - Delete SET NULL
sub get_fk_ddl {
my ($self, $table, $column, $references) = @_;
return "" if !$references;
my $update = $references->{UPDATE} || 'CASCADE';
my $delete = $references->{DELETE};
my $to_table = $references->{TABLE} || confess "No table in reference";
my $to_column = $references->{COLUMN} || confess "No column in reference";
my $fk_name = $self->_get_fk_name($table, $column, $references);
my $fk_string = "\n CONSTRAINT $fk_name FOREIGN KEY ($column)\n"
. " REFERENCES $to_table($to_column)\n";
$fk_string = $fk_string . " ON DELETE $delete" if $delete;
if ( $update =~ /CASCADE/i ){
my $tr_str = "CREATE OR REPLACE TRIGGER ". $table . "_uc"
. " AFTER UPDATE ON ". $table
. " REFERENCING "
. " NEW AS NEW "
. " OLD AS OLD "
. " FOR EACH ROW "
. " BEGIN "
. " UPDATE ". $to_table
. " SET ". $to_column . " = :NEW.". $column
. " WHERE ". $to_column . " = :OLD.". $column . ";"
. " END ". $table . "_uc;";
my $dbh = Bugzilla->dbh;
$dbh->do($tr_str);
}
return $fk_string;
}
sub _get_fk_name {
my ($self, $table, $column, $references) = @_;
my $to_table = $references->{TABLE};
my $to_column = $references->{COLUMN};
my $fk_name = "${table}_${column}_${to_table}_${to_column}";
$fk_name = "fk_" . substr(md5_hex($fk_name),0,20);
return $fk_name;
}
sub _get_notnull_trigger_ddl {
my ($table, $column) = @_;
my $notnull_sql = "CREATE OR REPLACE TRIGGER "
. " ${table}_${column}"
. " BEFORE INSERT OR UPDATE ON ". $table
. " FOR EACH ROW"
. " BEGIN "
. " IF :NEW.". $column ." IS NULL THEN "
. " SELECT '" . Bugzilla::DB::Oracle::EMPTY_STRING
. "' INTO :NEW.". $column ." FROM DUAL; "
. " END IF; "
. " END ".$table.";";
return $notnull_sql;
}
sub _get_create_seq_ddl {
my ($table, $column) = @_;
my @ddl;
my $seq_name = "${table}_${column}_SEQ";
my $seq_sql = "CREATE SEQUENCE $seq_name "
. " INCREMENT BY 1 "
. " START WITH 1 "
. " NOMAXVALUE "
. " NOCYCLE "
. " NOCACHE";
my $serial_sql = "CREATE OR REPLACE TRIGGER ${table}_${column}_TR "
. " BEFORE INSERT ON ${table} "
. " FOR EACH ROW "
. " BEGIN "
. " SELECT ${seq_name}.NEXTVAL "
. " INTO :NEW.${column} FROM DUAL; "
. " END;";
push (@ddl, $seq_sql);
push (@ddl, $serial_sql);
return @ddl;
}
1;
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment