Oracle DB driver code style
parent
0a2f0882c4
commit
b7da291986
|
@ -1,5 +1,3 @@
|
|||
# -*- 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
|
||||
|
@ -53,7 +51,8 @@ use constant EMPTY_STRING => '__BZ_EMPTY_STR__';
|
|||
use constant ISOLATION_LEVEL => 'READ COMMITTED';
|
||||
use constant BLOB_TYPE => { ora_type => ORA_BLOB };
|
||||
|
||||
sub new {
|
||||
sub new
|
||||
{
|
||||
my ($class, $user, $pass, $host, $dbname, $port) = @_;
|
||||
|
||||
# You can never connect to Oracle without a DB name,
|
||||
|
@ -61,16 +60,16 @@ sub new {
|
|||
$dbname ||= Bugzilla->localconfig->{db_name};
|
||||
|
||||
# Set the language enviroment
|
||||
$ENV{'NLS_LANG'} = '.AL32UTF8' if Bugzilla->params->{'utf8'};
|
||||
$ENV{NLS_LANG} = '.AL32UTF8' if Bugzilla->params->{utf8};
|
||||
|
||||
# construct the DSN from the parameters we got
|
||||
my $dsn = "dbi:Oracle:host=$host;sid=$dbname";
|
||||
$dsn .= ";port=$port" if $port;
|
||||
my $attrs = { FetchHashKeyName => 'NAME_lc',
|
||||
LongReadLen => ( Bugzilla->params->{'maxattachmentsize'}
|
||||
|| 1000 ) * 1024,
|
||||
LongTruncOk => 1,
|
||||
};
|
||||
my $attrs = {
|
||||
FetchHashKeyName => 'NAME_lc',
|
||||
LongReadLen => (Bugzilla->params->{maxattachmentsize} || 1000) * 1024,
|
||||
LongTruncOk => 1,
|
||||
};
|
||||
my $self = $class->db_new($dsn, $user, $pass, $attrs);
|
||||
# Needed by TheSchwartz
|
||||
$self->{private_bz_dsn} = $dsn;
|
||||
|
@ -80,20 +79,18 @@ sub new {
|
|||
# Set the session's default date format to match MySQL
|
||||
$self->do("ALTER SESSION SET NLS_DATE_FORMAT='YYYY-MM-DD HH24:MI:SS'");
|
||||
$self->do("ALTER SESSION SET NLS_TIMESTAMP_FORMAT='YYYY-MM-DD HH24:MI:SS'");
|
||||
$self->do("ALTER SESSION SET NLS_LENGTH_SEMANTICS='CHAR'")
|
||||
if Bugzilla->params->{'utf8'};
|
||||
$self->do("ALTER SESSION SET NLS_LENGTH_SEMANTICS='CHAR'") if Bugzilla->params->{utf8};
|
||||
# To allow case insensitive query.
|
||||
$self->do("ALTER SESSION SET NLS_COMP='ANSI'");
|
||||
$self->do("ALTER SESSION SET NLS_SORT='BINARY_AI'");
|
||||
return $self;
|
||||
}
|
||||
|
||||
sub bz_last_key {
|
||||
sub bz_last_key
|
||||
{
|
||||
my ($self, $table, $column) = @_;
|
||||
|
||||
my $seq = $table . "_" . $column . "_SEQ";
|
||||
my ($last_insert_id) = $self->selectrow_array("SELECT $seq.CURRVAL "
|
||||
. " FROM DUAL");
|
||||
my ($last_insert_id) = $self->selectrow_array("SELECT $seq.CURRVAL FROM DUAL");
|
||||
return $last_insert_id;
|
||||
}
|
||||
|
||||
|
@ -107,32 +104,40 @@ sub bz_sequence_restart
|
|||
);
|
||||
}
|
||||
|
||||
sub bz_check_regexp {
|
||||
sub bz_check_regexp
|
||||
{
|
||||
my ($self, $pattern) = @_;
|
||||
|
||||
eval { $self->do("SELECT 1 FROM DUAL WHERE "
|
||||
. $self->sql_regexp($self->quote("a"), $pattern, 1)) };
|
||||
eval
|
||||
{
|
||||
$self->do(
|
||||
"SELECT 1 FROM DUAL WHERE ".
|
||||
$self->sql_regexp($self->quote("a"), $pattern, 1)
|
||||
);
|
||||
};
|
||||
|
||||
$@ && ThrowUserError('illegal_regexp',
|
||||
{ value => $pattern, dberror => $self->errstr });
|
||||
}
|
||||
|
||||
sub bz_explain {
|
||||
my ($self, $sql) = @_;
|
||||
my $sth = $self->prepare("EXPLAIN PLAN FOR $sql");
|
||||
$sth->execute();
|
||||
my $explain = $self->selectcol_arrayref(
|
||||
"SELECT PLAN_TABLE_OUTPUT FROM TABLE(DBMS_XPLAN.DISPLAY)");
|
||||
return join("\n", @$explain);
|
||||
sub bz_explain
|
||||
{
|
||||
my ($self, $sql) = @_;
|
||||
my $sth = $self->prepare("EXPLAIN PLAN FOR $sql");
|
||||
$sth->execute();
|
||||
my $explain = $self->selectcol_arrayref("SELECT PLAN_TABLE_OUTPUT FROM TABLE(DBMS_XPLAN.DISPLAY)");
|
||||
return join("\n", @$explain);
|
||||
}
|
||||
|
||||
sub sql_group_concat {
|
||||
sub sql_group_concat
|
||||
{
|
||||
my ($self, $text, $separator) = @_;
|
||||
$separator ||= "','";
|
||||
return "group_concat(T_CLOB_DELIM($text, $separator))";
|
||||
}
|
||||
|
||||
sub sql_regexp {
|
||||
sub sql_regexp
|
||||
{
|
||||
my ($self, $expr, $pattern, $nocheck, $real_pattern) = @_;
|
||||
$real_pattern ||= $pattern;
|
||||
|
||||
|
@ -141,7 +146,8 @@ sub sql_regexp {
|
|||
return "REGEXP_LIKE($expr, $pattern)";
|
||||
}
|
||||
|
||||
sub sql_not_regexp {
|
||||
sub sql_not_regexp
|
||||
{
|
||||
my ($self, $expr, $pattern, $nocheck, $real_pattern) = @_;
|
||||
$real_pattern ||= $pattern;
|
||||
|
||||
|
@ -150,46 +156,51 @@ sub sql_not_regexp {
|
|||
return "NOT REGEXP_LIKE($expr, $pattern)"
|
||||
}
|
||||
|
||||
sub sql_limit {
|
||||
sub sql_limit
|
||||
{
|
||||
my ($self, $limit, $offset) = @_;
|
||||
|
||||
if(defined $offset) {
|
||||
return "LIMIT $offset, $limit";
|
||||
# Will be later translated by adjust_statement()
|
||||
if (defined $offset)
|
||||
{
|
||||
return "LIMIT $offset, $limit";
|
||||
}
|
||||
return "LIMIT $limit";
|
||||
}
|
||||
|
||||
sub sql_string_concat {
|
||||
sub sql_string_concat
|
||||
{
|
||||
my ($self, @params) = @_;
|
||||
|
||||
return 'CONCAT(' . join(', ', @params) . ')';
|
||||
}
|
||||
|
||||
sub sql_string_until {
|
||||
sub sql_string_until
|
||||
{
|
||||
my ($self, $string, $substring) = @_;
|
||||
return "SUBSTR($string, 1, "
|
||||
. $self->sql_position($substring, $string)
|
||||
. " - 1)";
|
||||
return "SUBSTR($string, 1, " . $self->sql_position($substring, $string) . " - 1)";
|
||||
}
|
||||
|
||||
sub sql_to_days {
|
||||
sub sql_to_days
|
||||
{
|
||||
my ($self, $date) = @_;
|
||||
|
||||
return " TO_CHAR(TO_DATE($date),'J') ";
|
||||
}
|
||||
sub sql_from_days{
|
||||
my ($self, $date) = @_;
|
||||
|
||||
sub sql_from_days
|
||||
{
|
||||
my ($self, $date) = @_;
|
||||
return " TO_DATE($date,'J') ";
|
||||
}
|
||||
sub sql_fulltext_search {
|
||||
|
||||
sub sql_fulltext_search
|
||||
{
|
||||
my ($self, $column, $text, $label) = @_;
|
||||
$text = $self->quote($text);
|
||||
trick_taint($text);
|
||||
return "CONTAINS($column,$text,$label)", "SCORE($label)";
|
||||
}
|
||||
|
||||
sub sql_date_format {
|
||||
sub sql_date_format
|
||||
{
|
||||
my ($self, $date, $format) = @_;
|
||||
|
||||
$format = "%Y.%m.%d %H:%i:%s" if !$format;
|
||||
|
@ -206,86 +217,103 @@ sub sql_date_format {
|
|||
return "TO_CHAR($date, " . $self->quote($format) . ")";
|
||||
}
|
||||
|
||||
sub sql_date_math {
|
||||
sub sql_date_math
|
||||
{
|
||||
my ($self, $date, $operator, $interval, $units) = @_;
|
||||
my $time_sql;
|
||||
if ($units =~ /YEAR|MONTH/i) {
|
||||
if ($units =~ /YEAR|MONTH/i)
|
||||
{
|
||||
$time_sql = "NUMTOYMINTERVAL($interval,'$units')";
|
||||
} else{
|
||||
}
|
||||
else
|
||||
{
|
||||
$time_sql = "NUMTODSINTERVAL($interval,'$units')";
|
||||
}
|
||||
return "$date $operator $time_sql";
|
||||
return "$date $operator $time_sql";
|
||||
}
|
||||
|
||||
sub sql_position {
|
||||
sub sql_position
|
||||
{
|
||||
my ($self, $fragment, $text) = @_;
|
||||
return "INSTR($text, $fragment)";
|
||||
}
|
||||
|
||||
sub sql_in {
|
||||
sub sql_in
|
||||
{
|
||||
my ($self, $column_name, $in_list_ref) = @_;
|
||||
my @in_list = @$in_list_ref;
|
||||
return $self->SUPER::sql_in($column_name, $in_list_ref) if $#in_list < 1000;
|
||||
my @in_str;
|
||||
while (@in_list) {
|
||||
while (@in_list)
|
||||
{
|
||||
my $length = $#in_list + 1;
|
||||
my $splice = $length > 1000 ? 1000 : $length;
|
||||
my @sub_in_list = splice(@in_list, 0, $splice);
|
||||
push(@in_str,
|
||||
$self->SUPER::sql_in($column_name, \@sub_in_list));
|
||||
push @in_str, $self->SUPER::sql_in($column_name, \@sub_in_list);
|
||||
}
|
||||
return "( " . join(" OR ", @in_str) . " )";
|
||||
}
|
||||
|
||||
sub _bz_add_field_table {
|
||||
sub _bz_add_field_table
|
||||
{
|
||||
my ($self, $name, $schema_ref, $type) = @_;
|
||||
$self->SUPER::_bz_add_field_table($name, $schema_ref);
|
||||
if (defined($type) && $type == FIELD_TYPE_MULTI_SELECT) {
|
||||
if (defined($type) && $type == FIELD_TYPE_MULTI_SELECT)
|
||||
{
|
||||
my $uk_name = "UK_" . $self->_bz_schema->_hash_identifier($name . '_value');
|
||||
$self->do("ALTER TABLE $name ADD CONSTRAINT $uk_name UNIQUE(value)");
|
||||
}
|
||||
}
|
||||
|
||||
sub bz_drop_table {
|
||||
sub bz_drop_table
|
||||
{
|
||||
my ($self, $name) = @_;
|
||||
my $table_exists = $self->bz_table_info($name);
|
||||
if ($table_exists) {
|
||||
if ($table_exists)
|
||||
{
|
||||
$self->_bz_drop_fks($name);
|
||||
$self->SUPER::bz_drop_table($name);
|
||||
}
|
||||
}
|
||||
|
||||
# Dropping all FKs for a specified table.
|
||||
sub _bz_drop_fks {
|
||||
sub _bz_drop_fks
|
||||
{
|
||||
my ($self, $table) = @_;
|
||||
my @columns = $self->_bz_real_schema->get_table_columns($table);
|
||||
foreach my $column (@columns) {
|
||||
foreach my $column (@columns)
|
||||
{
|
||||
$self->bz_drop_fk($table, $column);
|
||||
}
|
||||
}
|
||||
|
||||
sub _fix_empty {
|
||||
sub _fix_empty
|
||||
{
|
||||
my ($string) = @_;
|
||||
$string = '' if $string eq EMPTY_STRING;
|
||||
return $string;
|
||||
}
|
||||
|
||||
sub _fix_arrayref {
|
||||
sub _fix_arrayref
|
||||
{
|
||||
my ($row) = @_;
|
||||
return undef if !defined $row;
|
||||
foreach my $field (@$row) {
|
||||
foreach my $field (@$row)
|
||||
{
|
||||
$field = _fix_empty($field) if defined $field;
|
||||
}
|
||||
return $row;
|
||||
}
|
||||
|
||||
sub _fix_hashref {
|
||||
my ($row) = @_;
|
||||
return undef if !defined $row;
|
||||
foreach my $value (values %$row) {
|
||||
$value = _fix_empty($value) if defined $value;
|
||||
}
|
||||
return $row;
|
||||
sub _fix_hashref
|
||||
{
|
||||
my ($row) = @_;
|
||||
return undef if !defined $row;
|
||||
foreach my $value (values %$row)
|
||||
{
|
||||
$value = _fix_empty($value) if defined $value;
|
||||
}
|
||||
return $row;
|
||||
}
|
||||
|
||||
sub adjust_statement
|
||||
|
@ -429,23 +457,28 @@ sub do
|
|||
return $self->SUPER::do(@_);
|
||||
}
|
||||
|
||||
sub selectrow_array {
|
||||
sub selectrow_array
|
||||
{
|
||||
my $self = shift;
|
||||
my $stmt = shift;
|
||||
my $new_stmt = (ref $stmt) ? $stmt : adjust_statement($stmt);
|
||||
unshift @_, $new_stmt;
|
||||
if ( wantarray ) {
|
||||
if (wantarray)
|
||||
{
|
||||
my @row = $self->SUPER::selectrow_array(@_);
|
||||
_fix_arrayref(\@row);
|
||||
return @row;
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
my $row = $self->SUPER::selectrow_array(@_);
|
||||
$row = _fix_empty($row) if defined $row;
|
||||
return $row;
|
||||
}
|
||||
}
|
||||
|
||||
sub selectrow_arrayref {
|
||||
sub selectrow_arrayref
|
||||
{
|
||||
my $self = shift;
|
||||
my $stmt = shift;
|
||||
my $new_stmt = (ref $stmt) ? $stmt : adjust_statement($stmt);
|
||||
|
@ -457,7 +490,8 @@ sub selectrow_arrayref {
|
|||
return $ref;
|
||||
}
|
||||
|
||||
sub selectrow_hashref {
|
||||
sub selectrow_hashref
|
||||
{
|
||||
my $self = shift;
|
||||
my $stmt = shift;
|
||||
my $new_stmt = (ref $stmt) ? $stmt : adjust_statement($stmt);
|
||||
|
@ -469,7 +503,8 @@ sub selectrow_hashref {
|
|||
return $ref;
|
||||
}
|
||||
|
||||
sub selectall_arrayref {
|
||||
sub selectall_arrayref
|
||||
{
|
||||
my $self = shift;
|
||||
my $stmt = shift;
|
||||
my $new_stmt = (ref $stmt) ? $stmt : adjust_statement($stmt);
|
||||
|
@ -477,32 +512,38 @@ sub selectall_arrayref {
|
|||
my $ref = $self->SUPER::selectall_arrayref(@_);
|
||||
return undef if !defined $ref;
|
||||
|
||||
foreach my $row (@$ref) {
|
||||
if (ref($row) eq 'ARRAY') {
|
||||
foreach my $row (@$ref)
|
||||
{
|
||||
if (ref($row) eq 'ARRAY')
|
||||
{
|
||||
_fix_arrayref($row);
|
||||
}
|
||||
elsif (ref($row) eq 'HASH') {
|
||||
}
|
||||
elsif (ref($row) eq 'HASH')
|
||||
{
|
||||
_fix_hashref($row);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $ref;
|
||||
}
|
||||
|
||||
sub selectall_hashref {
|
||||
sub selectall_hashref
|
||||
{
|
||||
my $self = shift;
|
||||
my $stmt = shift;
|
||||
my $new_stmt = (ref $stmt) ? $stmt : adjust_statement($stmt);
|
||||
unshift @_, $new_stmt;
|
||||
my $rows = $self->SUPER::selectall_hashref(@_);
|
||||
return undef if !defined $rows;
|
||||
foreach my $row (values %$rows) {
|
||||
_fix_hashref($row);
|
||||
foreach my $row (values %$rows)
|
||||
{
|
||||
_fix_hashref($row);
|
||||
}
|
||||
return $rows;
|
||||
}
|
||||
|
||||
sub selectcol_arrayref {
|
||||
sub selectcol_arrayref
|
||||
{
|
||||
my $self = shift;
|
||||
my $stmt = shift;
|
||||
my $new_stmt = (ref $stmt) ? $stmt : adjust_statement($stmt);
|
||||
|
@ -513,26 +554,27 @@ sub selectcol_arrayref {
|
|||
return $ref;
|
||||
}
|
||||
|
||||
sub prepare {
|
||||
sub prepare
|
||||
{
|
||||
my $self = shift;
|
||||
my $sql = shift;
|
||||
my $new_sql = adjust_statement($sql);
|
||||
unshift @_, $new_sql;
|
||||
return bless $self->SUPER::prepare(@_),
|
||||
'Bugzilla::DB::Oracle::st';
|
||||
return bless $self->SUPER::prepare(@_), 'Bugzilla::DB::Oracle::st';
|
||||
}
|
||||
|
||||
sub prepare_cached {
|
||||
sub prepare_cached
|
||||
{
|
||||
my $self = shift;
|
||||
my $sql = shift;
|
||||
my $new_sql = adjust_statement($sql);
|
||||
unshift @_, $new_sql;
|
||||
return bless $self->SUPER::prepare_cached(@_),
|
||||
'Bugzilla::DB::Oracle::st';
|
||||
return bless $self->SUPER::prepare_cached(@_), 'Bugzilla::DB::Oracle::st';
|
||||
}
|
||||
|
||||
sub quote_identifier {
|
||||
my ($self,$id) = @_;
|
||||
sub quote_identifier
|
||||
{
|
||||
my ($self, $id) = @_;
|
||||
return $id;
|
||||
}
|
||||
|
||||
|
@ -545,20 +587,24 @@ sub release_savepoint
|
|||
# Protected "Real Database" Schema Information Methods
|
||||
#####################################################################
|
||||
|
||||
sub bz_table_columns_real {
|
||||
sub bz_table_columns_real
|
||||
{
|
||||
my ($self, $table) = @_;
|
||||
$table = uc($table);
|
||||
my $cols = $self->selectcol_arrayref(
|
||||
"SELECT LOWER(COLUMN_NAME) FROM USER_TAB_COLUMNS WHERE
|
||||
TABLE_NAME = ? ORDER BY COLUMN_NAME", undef, $table);
|
||||
"SELECT LOWER(COLUMN_NAME) FROM USER_TAB_COLUMNS".
|
||||
" WHERE TABLE_NAME = ? ORDER BY COLUMN_NAME", undef, $table
|
||||
);
|
||||
return @$cols;
|
||||
}
|
||||
|
||||
sub bz_table_list_real {
|
||||
sub bz_table_list_real
|
||||
{
|
||||
my ($self) = @_;
|
||||
my $tables = $self->selectcol_arrayref(
|
||||
"SELECT LOWER(TABLE_NAME) FROM USER_TABLES WHERE
|
||||
TABLE_NAME NOT LIKE ? ORDER BY TABLE_NAME", undef, 'DR$%');
|
||||
"SELECT LOWER(TABLE_NAME) FROM USER_TABLES".
|
||||
" WHERE TABLE_NAME NOT LIKE ? ORDER BY TABLE_NAME", undef, 'DR$%'
|
||||
);
|
||||
return @$tables;
|
||||
}
|
||||
|
||||
|
@ -572,138 +618,148 @@ sub bz_setup_database {
|
|||
# Create a function that returns SYSDATE to emulate MySQL's "NOW()".
|
||||
# Function NOW() is used widely in Bugzilla SQLs, but Oracle does not
|
||||
# have that function, So we have to create one ourself.
|
||||
$self->do("CREATE OR REPLACE FUNCTION NOW "
|
||||
. " RETURN DATE IS BEGIN RETURN SYSDATE; END;");
|
||||
$self->do("CREATE OR REPLACE FUNCTION CHAR_LENGTH(COLUMN_NAME VARCHAR2)"
|
||||
. " RETURN NUMBER IS BEGIN RETURN LENGTH(COLUMN_NAME); END;");
|
||||
$self->do(
|
||||
"CREATE OR REPLACE FUNCTION NOW "
|
||||
. " RETURN DATE IS BEGIN RETURN SYSDATE; END;"
|
||||
);
|
||||
$self->do(
|
||||
"CREATE OR REPLACE FUNCTION CHAR_LENGTH(COLUMN_NAME VARCHAR2)"
|
||||
. " RETURN NUMBER IS BEGIN RETURN LENGTH(COLUMN_NAME); END;"
|
||||
);
|
||||
|
||||
# Create types for group_concat
|
||||
my $t_clob_delim = $self->selectcol_arrayref("
|
||||
SELECT TYPE_NAME FROM USER_TYPES WHERE TYPE_NAME=?",
|
||||
undef, 'T_CLOB_DELIM');
|
||||
undef, 'T_CLOB_DELIM'
|
||||
);
|
||||
|
||||
if ( !@$t_clob_delim ) {
|
||||
$self->do("CREATE OR REPLACE TYPE T_CLOB_DELIM AS OBJECT "
|
||||
. "( p_CONTENT CLOB, p_DELIMITER VARCHAR2(256));");
|
||||
if (!@$t_clob_delim )
|
||||
{
|
||||
$self->do(
|
||||
"CREATE OR REPLACE TYPE T_CLOB_DELIM AS OBJECT "
|
||||
. "(p_CONTENT CLOB, p_DELIMITER VARCHAR2(256));"
|
||||
);
|
||||
}
|
||||
|
||||
$self->do("CREATE OR REPLACE TYPE T_GROUP_CONCAT AS OBJECT
|
||||
( CLOB_CONTENT CLOB,
|
||||
DELIMITER VARCHAR2(256),
|
||||
STATIC FUNCTION ODCIAGGREGATEINITIALIZE(
|
||||
SCTX IN OUT NOCOPY T_GROUP_CONCAT)
|
||||
RETURN NUMBER,
|
||||
MEMBER FUNCTION ODCIAGGREGATEITERATE(
|
||||
SELF IN OUT NOCOPY T_GROUP_CONCAT,
|
||||
VALUE IN T_CLOB_DELIM)
|
||||
RETURN NUMBER,
|
||||
MEMBER FUNCTION ODCIAGGREGATETERMINATE(
|
||||
SELF IN T_GROUP_CONCAT,
|
||||
RETURNVALUE OUT NOCOPY CLOB,
|
||||
FLAGS IN NUMBER)
|
||||
RETURN NUMBER,
|
||||
MEMBER FUNCTION ODCIAGGREGATEMERGE(
|
||||
SELF IN OUT NOCOPY T_GROUP_CONCAT,
|
||||
CTX2 IN T_GROUP_CONCAT)
|
||||
RETURN NUMBER);");
|
||||
$self->do(
|
||||
"CREATE OR REPLACE TYPE T_GROUP_CONCAT AS OBJECT (
|
||||
CLOB_CONTENT CLOB,
|
||||
DELIMITER VARCHAR2(256),
|
||||
STATIC FUNCTION ODCIAGGREGATEINITIALIZE(
|
||||
SCTX IN OUT NOCOPY T_GROUP_CONCAT)
|
||||
RETURN NUMBER,
|
||||
MEMBER FUNCTION ODCIAGGREGATEITERATE(
|
||||
SELF IN OUT NOCOPY T_GROUP_CONCAT,
|
||||
VALUE IN T_CLOB_DELIM)
|
||||
RETURN NUMBER,
|
||||
MEMBER FUNCTION ODCIAGGREGATETERMINATE(
|
||||
SELF IN T_GROUP_CONCAT,
|
||||
RETURNVALUE OUT NOCOPY CLOB,
|
||||
FLAGS IN NUMBER)
|
||||
RETURN NUMBER,
|
||||
MEMBER FUNCTION ODCIAGGREGATEMERGE(
|
||||
SELF IN OUT NOCOPY T_GROUP_CONCAT,
|
||||
CTX2 IN T_GROUP_CONCAT)
|
||||
RETURN NUMBER);"
|
||||
);
|
||||
|
||||
$self->do("CREATE OR REPLACE TYPE BODY T_GROUP_CONCAT IS
|
||||
STATIC FUNCTION ODCIAGGREGATEINITIALIZE(
|
||||
SCTX IN OUT NOCOPY T_GROUP_CONCAT)
|
||||
RETURN NUMBER IS
|
||||
BEGIN
|
||||
SCTX := T_GROUP_CONCAT(EMPTY_CLOB(), NULL);
|
||||
DBMS_LOB.CREATETEMPORARY(SCTX.CLOB_CONTENT, TRUE);
|
||||
RETURN ODCICONST.SUCCESS;
|
||||
END;
|
||||
MEMBER FUNCTION ODCIAGGREGATEITERATE(
|
||||
SELF IN OUT NOCOPY T_GROUP_CONCAT,
|
||||
VALUE IN T_CLOB_DELIM)
|
||||
RETURN NUMBER IS
|
||||
BEGIN
|
||||
SELF.DELIMITER := VALUE.P_DELIMITER;
|
||||
DBMS_LOB.WRITEAPPEND(SELF.CLOB_CONTENT,
|
||||
LENGTH(SELF.DELIMITER),
|
||||
SELF.DELIMITER);
|
||||
DBMS_LOB.APPEND(SELF.CLOB_CONTENT, VALUE.P_CONTENT);
|
||||
|
||||
RETURN ODCICONST.SUCCESS;
|
||||
END;
|
||||
MEMBER FUNCTION ODCIAGGREGATETERMINATE(
|
||||
SELF IN T_GROUP_CONCAT,
|
||||
RETURNVALUE OUT NOCOPY CLOB,
|
||||
FLAGS IN NUMBER)
|
||||
RETURN NUMBER IS
|
||||
BEGIN
|
||||
RETURNVALUE := RTRIM(LTRIM(SELF.CLOB_CONTENT,
|
||||
SELF.DELIMITER),
|
||||
SELF.DELIMITER);
|
||||
RETURN ODCICONST.SUCCESS;
|
||||
END;
|
||||
MEMBER FUNCTION ODCIAGGREGATEMERGE(
|
||||
SELF IN OUT NOCOPY T_GROUP_CONCAT,
|
||||
CTX2 IN T_GROUP_CONCAT)
|
||||
RETURN NUMBER IS
|
||||
BEGIN
|
||||
DBMS_LOB.WRITEAPPEND(SELF.CLOB_CONTENT,
|
||||
LENGTH(SELF.DELIMITER),
|
||||
SELF.DELIMITER);
|
||||
DBMS_LOB.APPEND(SELF.CLOB_CONTENT, CTX2.CLOB_CONTENT);
|
||||
RETURN ODCICONST.SUCCESS;
|
||||
END;
|
||||
END;");
|
||||
$self->do(
|
||||
"CREATE OR REPLACE TYPE BODY T_GROUP_CONCAT IS
|
||||
STATIC FUNCTION ODCIAGGREGATEINITIALIZE(SCTX IN OUT NOCOPY T_GROUP_CONCAT)
|
||||
RETURN NUMBER IS
|
||||
BEGIN
|
||||
SCTX := T_GROUP_CONCAT(EMPTY_CLOB(), NULL);
|
||||
DBMS_LOB.CREATETEMPORARY(SCTX.CLOB_CONTENT, TRUE);
|
||||
RETURN ODCICONST.SUCCESS;
|
||||
END;
|
||||
MEMBER FUNCTION ODCIAGGREGATEITERATE(
|
||||
SELF IN OUT NOCOPY T_GROUP_CONCAT,
|
||||
VALUE IN T_CLOB_DELIM)
|
||||
RETURN NUMBER IS
|
||||
BEGIN
|
||||
SELF.DELIMITER := VALUE.P_DELIMITER;
|
||||
DBMS_LOB.WRITEAPPEND(SELF.CLOB_CONTENT, LENGTH(SELF.DELIMITER), SELF.DELIMITER);
|
||||
DBMS_LOB.APPEND(SELF.CLOB_CONTENT, VALUE.P_CONTENT);
|
||||
RETURN ODCICONST.SUCCESS;
|
||||
END;
|
||||
MEMBER FUNCTION ODCIAGGREGATETERMINATE(
|
||||
SELF IN T_GROUP_CONCAT,
|
||||
RETURNVALUE OUT NOCOPY CLOB,
|
||||
FLAGS IN NUMBER)
|
||||
RETURN NUMBER IS
|
||||
BEGIN
|
||||
RETURNVALUE := RTRIM(LTRIM(SELF.CLOB_CONTENT, SELF.DELIMITER), SELF.DELIMITER);
|
||||
RETURN ODCICONST.SUCCESS;
|
||||
END;
|
||||
MEMBER FUNCTION ODCIAGGREGATEMERGE(
|
||||
SELF IN OUT NOCOPY T_GROUP_CONCAT,
|
||||
CTX2 IN T_GROUP_CONCAT)
|
||||
RETURN NUMBER IS
|
||||
BEGIN
|
||||
DBMS_LOB.WRITEAPPEND(SELF.CLOB_CONTENT, LENGTH(SELF.DELIMITER), SELF.DELIMITER);
|
||||
DBMS_LOB.APPEND(SELF.CLOB_CONTENT, CTX2.CLOB_CONTENT);
|
||||
RETURN ODCICONST.SUCCESS;
|
||||
END;
|
||||
END;"
|
||||
);
|
||||
|
||||
# Create user-defined aggregate function group_concat
|
||||
$self->do("CREATE OR REPLACE FUNCTION GROUP_CONCAT(P_INPUT T_CLOB_DELIM)
|
||||
RETURN CLOB
|
||||
DETERMINISTIC PARALLEL_ENABLE AGGREGATE USING T_GROUP_CONCAT;");
|
||||
$self->do(
|
||||
"CREATE OR REPLACE FUNCTION GROUP_CONCAT(P_INPUT T_CLOB_DELIM)
|
||||
RETURN CLOB
|
||||
DETERMINISTIC PARALLEL_ENABLE AGGREGATE USING T_GROUP_CONCAT;"
|
||||
);
|
||||
|
||||
# Create a WORLD_LEXER named BZ_LEX for multilingual fulltext search
|
||||
my $lexer = $self->selectcol_arrayref(
|
||||
"SELECT pre_name FROM CTXSYS.CTX_PREFERENCES WHERE pre_name = ? AND
|
||||
pre_owner = ?",
|
||||
undef,'BZ_LEX',uc(Bugzilla->localconfig->{db_user}));
|
||||
if(!@$lexer) {
|
||||
$self->do("BEGIN CTX_DDL.CREATE_PREFERENCE
|
||||
('BZ_LEX', 'WORLD_LEXER'); END;");
|
||||
"SELECT pre_name FROM CTXSYS.CTX_PREFERENCES WHERE pre_name = ? AND pre_owner = ?",
|
||||
undef, 'BZ_LEX', uc Bugzilla->localconfig->{db_user}
|
||||
);
|
||||
if (!@$lexer)
|
||||
{
|
||||
$self->do("BEGIN CTX_DDL.CREATE_PREFERENCE ('BZ_LEX', 'WORLD_LEXER'); END;");
|
||||
}
|
||||
|
||||
$self->SUPER::bz_setup_database(@_);
|
||||
|
||||
my @tables = $self->bz_table_list_real();
|
||||
foreach my $table (@tables) {
|
||||
foreach my $table (@tables)
|
||||
{
|
||||
my @columns = $self->bz_table_columns_real($table);
|
||||
foreach my $column (@columns) {
|
||||
foreach my $column (@columns)
|
||||
{
|
||||
my $def = $self->bz_column_info($table, $column);
|
||||
if ($def->{REFERENCES}) {
|
||||
if ($def->{REFERENCES})
|
||||
{
|
||||
my $references = $def->{REFERENCES};
|
||||
my $update = $references->{UPDATE} || 'CASCADE';
|
||||
my $to_table = $references->{TABLE};
|
||||
my $to_column = $references->{COLUMN};
|
||||
my $fk_name = $self->_bz_schema->_get_fk_name($table,
|
||||
$column,
|
||||
$references);
|
||||
if ( $update =~ /CASCADE/i ){
|
||||
my $trigger_name = uc($fk_name . "_UC");
|
||||
my $exist_trigger = $self->selectcol_arrayref(
|
||||
"SELECT OBJECT_NAME FROM USER_OBJECTS
|
||||
WHERE OBJECT_NAME = ?", undef, $trigger_name);
|
||||
if(@$exist_trigger) {
|
||||
my $fk_name = $self->_bz_schema->_get_fk_name($table, $column, $references);
|
||||
if ($update =~ /CASCADE/i)
|
||||
{
|
||||
my $trigger_name = uc($fk_name . "_UC");
|
||||
my $exist_trigger = $self->selectcol_arrayref(
|
||||
"SELECT OBJECT_NAME FROM USER_OBJECTS".
|
||||
" WHERE OBJECT_NAME = ?", undef, $trigger_name
|
||||
);
|
||||
if (@$exist_trigger)
|
||||
{
|
||||
$self->do("DROP TRIGGER $trigger_name");
|
||||
}
|
||||
|
||||
my $tr_str = "CREATE OR REPLACE TRIGGER $trigger_name"
|
||||
. " AFTER UPDATE OF $to_column ON $to_table "
|
||||
. " REFERENCING "
|
||||
. " NEW AS NEW "
|
||||
. " OLD AS OLD "
|
||||
. " FOR EACH ROW "
|
||||
. " BEGIN "
|
||||
. " UPDATE $table"
|
||||
. " SET $column = :NEW.$to_column"
|
||||
. " WHERE $column = :OLD.$to_column;"
|
||||
. " END $trigger_name;";
|
||||
$self->do($tr_str);
|
||||
my $tr_str = "CREATE OR REPLACE TRIGGER $trigger_name"
|
||||
. " AFTER UPDATE OF $to_column ON $to_table "
|
||||
. " REFERENCING "
|
||||
. " NEW AS NEW "
|
||||
. " OLD AS OLD "
|
||||
. " FOR EACH ROW "
|
||||
. " BEGIN "
|
||||
. " UPDATE $table"
|
||||
. " SET $column = :NEW.$to_column"
|
||||
. " WHERE $column = :OLD.$to_column;"
|
||||
. " END $trigger_name;";
|
||||
$self->do($tr_str);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -712,9 +768,11 @@ sub bz_setup_database {
|
|||
# Drop the trigger which causes bug 541553
|
||||
my $trigger_name = "PRODUCTS_MILESTONEURL";
|
||||
my $exist_trigger = $self->selectcol_arrayref(
|
||||
"SELECT OBJECT_NAME FROM USER_OBJECTS
|
||||
WHERE OBJECT_NAME = ?", undef, $trigger_name);
|
||||
if(@$exist_trigger) {
|
||||
"SELECT OBJECT_NAME FROM USER_OBJECTS".
|
||||
" WHERE OBJECT_NAME = ?", undef, $trigger_name
|
||||
);
|
||||
if (@$exist_trigger)
|
||||
{
|
||||
$self->do("DROP TRIGGER $trigger_name");
|
||||
}
|
||||
}
|
||||
|
@ -722,7 +780,8 @@ sub bz_setup_database {
|
|||
package Bugzilla::DB::Oracle::st;
|
||||
use base qw(DBI::st);
|
||||
|
||||
sub fetchrow_arrayref {
|
||||
sub fetchrow_arrayref
|
||||
{
|
||||
my $self = shift;
|
||||
my $ref = $self->SUPER::fetchrow_arrayref(@_);
|
||||
return undef if !defined $ref;
|
||||
|
@ -730,20 +789,25 @@ sub fetchrow_arrayref {
|
|||
return $ref;
|
||||
}
|
||||
|
||||
sub fetchrow_array {
|
||||
sub fetchrow_array
|
||||
{
|
||||
my $self = shift;
|
||||
if ( wantarray ) {
|
||||
if (wantarray)
|
||||
{
|
||||
my @row = $self->SUPER::fetchrow_array(@_);
|
||||
Bugzilla::DB::Oracle::_fix_arrayref(\@row);
|
||||
return @row;
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
my $row = $self->SUPER::fetchrow_array(@_);
|
||||
$row = Bugzilla::DB::Oracle::_fix_empty($row) if defined $row;
|
||||
return $row;
|
||||
}
|
||||
}
|
||||
|
||||
sub fetchrow_hashref {
|
||||
sub fetchrow_hashref
|
||||
{
|
||||
my $self = shift;
|
||||
my $ref = $self->SUPER::fetchrow_hashref(@_);
|
||||
return undef if !defined $ref;
|
||||
|
@ -751,36 +815,44 @@ sub fetchrow_hashref {
|
|||
return $ref;
|
||||
}
|
||||
|
||||
sub fetchall_arrayref {
|
||||
sub fetchall_arrayref
|
||||
{
|
||||
my $self = shift;
|
||||
my $ref = $self->SUPER::fetchall_arrayref(@_);
|
||||
return undef if !defined $ref;
|
||||
foreach my $row (@$ref) {
|
||||
if (ref($row) eq 'ARRAY') {
|
||||
foreach my $row (@$ref)
|
||||
{
|
||||
if (ref($row) eq 'ARRAY')
|
||||
{
|
||||
Bugzilla::DB::Oracle::_fix_arrayref($row);
|
||||
}
|
||||
elsif (ref($row) eq 'HASH') {
|
||||
elsif (ref($row) eq 'HASH')
|
||||
{
|
||||
Bugzilla::DB::Oracle::_fix_hashref($row);
|
||||
}
|
||||
}
|
||||
return $ref;
|
||||
}
|
||||
|
||||
sub fetchall_hashref {
|
||||
sub fetchall_hashref
|
||||
{
|
||||
my $self = shift;
|
||||
my $ref = $self->SUPER::fetchall_hashref(@_);
|
||||
return undef if !defined $ref;
|
||||
foreach my $row (values %$ref) {
|
||||
Bugzilla::DB::Oracle::_fix_hashref($row);
|
||||
foreach my $row (values %$ref)
|
||||
{
|
||||
Bugzilla::DB::Oracle::_fix_hashref($row);
|
||||
}
|
||||
return $ref;
|
||||
return $ref;
|
||||
}
|
||||
|
||||
sub fetch {
|
||||
sub fetch
|
||||
{
|
||||
my $self = shift;
|
||||
my $row = $self->SUPER::fetch(@_);
|
||||
if ($row) {
|
||||
Bugzilla::DB::Oracle::_fix_arrayref($row);
|
||||
if ($row)
|
||||
{
|
||||
Bugzilla::DB::Oracle::_fix_arrayref($row);
|
||||
}
|
||||
return $row;
|
||||
}
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
# -*- 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
|
||||
|
@ -41,8 +39,8 @@ use constant ADD_COLUMN => 'ADD';
|
|||
use constant MAX_IDENTIFIER_LEN => 27;
|
||||
|
||||
#------------------------------------------------------------------------------
|
||||
sub _initialize {
|
||||
|
||||
sub _initialize
|
||||
{
|
||||
my $self = shift;
|
||||
|
||||
$self = $self->SUPER::_initialize(@_);
|
||||
|
@ -81,63 +79,65 @@ sub _initialize {
|
|||
} #eosub--_initialize
|
||||
#--------------------------------------------------------------------
|
||||
|
||||
sub get_table_ddl {
|
||||
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) {
|
||||
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 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, $self->_get_create_seq_ddl($table, $field_name));
|
||||
if ($field_info->{TYPE} =~ /SERIAL/i)
|
||||
{
|
||||
push @ddl, $self->_get_create_seq_ddl($table, $field_name);
|
||||
}
|
||||
}
|
||||
return @ddl;
|
||||
|
||||
} #eosub--get_table_ddl
|
||||
|
||||
# Extend superclass method to create Oracle Text indexes if index type
|
||||
# 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 {
|
||||
|
||||
sub _get_create_index_ddl
|
||||
{
|
||||
my ($self, $table_name, $index_name, $index_fields, $index_type) = @_;
|
||||
$index_name = "idx_" . $self->_hash_identifier($index_name);
|
||||
if (uc($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)')" ;
|
||||
if (uc($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));
|
||||
|
||||
return $self->SUPER::_get_create_index_ddl($table_name, $index_name, $index_fields, $index_type);
|
||||
}
|
||||
|
||||
sub get_drop_index_ddl {
|
||||
sub get_drop_index_ddl
|
||||
{
|
||||
my $self = shift;
|
||||
my ($table, $name) = @_;
|
||||
|
||||
$name = 'idx_' . $self->_hash_identifier($name);
|
||||
return $self->SUPER::get_drop_index_ddl($table, $name);
|
||||
}
|
||||
|
||||
# Oracle supports the use of FOREIGN KEY integrity constraints
|
||||
# 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 {
|
||||
sub get_fk_ddl
|
||||
{
|
||||
my ($self, $table, $column, $references) = @_;
|
||||
return "" if !$references;
|
||||
|
||||
|
@ -147,56 +147,61 @@ sub get_fk_ddl {
|
|||
my $to_column = $references->{COLUMN} || confess "No column in reference";
|
||||
my $fk_name = $self->_get_fk_name($table, $column, $references);
|
||||
|
||||
# 'ON DELETE RESTRICT' is enabled by default
|
||||
# 'ON DELETE RESTRICT' is enabled by default
|
||||
$delete = "" if ( defined $delete && $delete =~ /RESTRICT/i);
|
||||
|
||||
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 $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 ${fk_name}_UC"
|
||||
. " AFTER UPDATE OF $to_column ON $to_table "
|
||||
. " REFERENCING "
|
||||
. " NEW AS NEW "
|
||||
. " OLD AS OLD "
|
||||
. " FOR EACH ROW "
|
||||
. " BEGIN "
|
||||
. " UPDATE $table"
|
||||
. " SET $column = :NEW.$to_column"
|
||||
. " WHERE $column = :OLD.$to_column;"
|
||||
. " END ${fk_name}_UC;";
|
||||
my $dbh = Bugzilla->dbh;
|
||||
$dbh->do($tr_str);
|
||||
. " AFTER UPDATE OF $to_column ON $to_table "
|
||||
. " REFERENCING "
|
||||
. " NEW AS NEW "
|
||||
. " OLD AS OLD "
|
||||
. " FOR EACH ROW "
|
||||
. " BEGIN "
|
||||
. " UPDATE $table"
|
||||
. " SET $column = :NEW.$to_column"
|
||||
. " WHERE $column = :OLD.$to_column;"
|
||||
. " END ${fk_name}_UC;";
|
||||
my $dbh = Bugzilla->dbh;
|
||||
$dbh->do($tr_str);
|
||||
}
|
||||
|
||||
return $fk_string;
|
||||
}
|
||||
|
||||
sub get_drop_fk_sql {
|
||||
sub get_drop_fk_sql
|
||||
{
|
||||
my $self = shift;
|
||||
my ($table, $column, $references) = @_;
|
||||
my $fk_name = $self->_get_fk_name(@_);
|
||||
my @sql;
|
||||
if (!$references->{UPDATE} || $references->{UPDATE} =~ /CASCADE/i) {
|
||||
push(@sql, "DROP TRIGGER ${fk_name}_uc");
|
||||
if (!$references->{UPDATE} || $references->{UPDATE} =~ /CASCADE/i)
|
||||
{
|
||||
push @sql, "DROP TRIGGER ${fk_name}_uc";
|
||||
}
|
||||
push(@sql, $self->SUPER::get_drop_fk_sql(@_));
|
||||
push @sql, $self->SUPER::get_drop_fk_sql(@_);
|
||||
return @sql;
|
||||
}
|
||||
|
||||
sub _get_fk_name {
|
||||
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_" . $self->_hash_identifier($fk_name);
|
||||
|
||||
return $fk_name;
|
||||
}
|
||||
|
||||
sub get_alter_column_ddl {
|
||||
sub get_alter_column_ddl
|
||||
{
|
||||
my ($self, $table, $column, $new_def, $set_nulls_to) = @_;
|
||||
|
||||
my @statements;
|
||||
|
@ -204,227 +209,237 @@ sub get_alter_column_ddl {
|
|||
my $specific = $self->{db_specific};
|
||||
|
||||
# If the types have changed, we have to deal with that.
|
||||
if (uc(trim($old_def->{TYPE})) ne uc(trim($new_def->{TYPE}))) {
|
||||
push(@statements, $self->_get_alter_type_sql($table, $column,
|
||||
$new_def, $old_def));
|
||||
if (uc(trim($old_def->{TYPE})) ne uc(trim($new_def->{TYPE})))
|
||||
{
|
||||
push(@statements, $self->_get_alter_type_sql($table, $column, $new_def, $old_def));
|
||||
}
|
||||
|
||||
my $default = $new_def->{DEFAULT};
|
||||
my $default_old = $old_def->{DEFAULT};
|
||||
# This first condition prevents "uninitialized value" errors.
|
||||
if (!defined $default && !defined $default_old) {
|
||||
if (!defined $default && !defined $default_old)
|
||||
{
|
||||
# Do Nothing
|
||||
}
|
||||
# If we went from having a default to not having one
|
||||
elsif (!defined $default && defined $default_old) {
|
||||
push(@statements, "ALTER TABLE $table MODIFY $column"
|
||||
. " DEFAULT NULL");
|
||||
elsif (!defined $default && defined $default_old)
|
||||
{
|
||||
push @statements, "ALTER TABLE $table MODIFY $column DEFAULT NULL";
|
||||
}
|
||||
# If we went from no default to a default, or we changed the default.
|
||||
elsif ( (defined $default && !defined $default_old) ||
|
||||
($default ne $default_old) )
|
||||
elsif (defined $default && !defined $default_old || $default ne $default_old)
|
||||
{
|
||||
$default = $specific->{$default} if exists $specific->{$default};
|
||||
push(@statements, "ALTER TABLE $table MODIFY $column "
|
||||
. " DEFAULT $default");
|
||||
push(@statements, "ALTER TABLE $table MODIFY $column DEFAULT $default");
|
||||
}
|
||||
|
||||
# If we went from NULL to NOT NULL.
|
||||
if (!$old_def->{NOTNULL} && $new_def->{NOTNULL}) {
|
||||
if (!$old_def->{NOTNULL} && $new_def->{NOTNULL})
|
||||
{
|
||||
my $setdefault;
|
||||
# Handle any fields that were NULL before, if we have a default,
|
||||
$setdefault = $new_def->{DEFAULT} if exists $new_def->{DEFAULT};
|
||||
# But if we have a set_nulls_to, that overrides the DEFAULT
|
||||
# (although nobody would usually specify both a default and
|
||||
# But if we have a set_nulls_to, that overrides the DEFAULT
|
||||
# (although nobody would usually specify both a default and
|
||||
# a set_nulls_to.)
|
||||
$setdefault = $set_nulls_to if defined $set_nulls_to;
|
||||
if (defined $setdefault) {
|
||||
push(@statements, "UPDATE $table SET $column = $setdefault"
|
||||
. " WHERE $column IS NULL");
|
||||
if (defined $setdefault)
|
||||
{
|
||||
push @statements, "UPDATE $table SET $column = $setdefault WHERE $column IS NULL";
|
||||
}
|
||||
push @statements, "ALTER TABLE $table MODIFY $column NOT NULL";
|
||||
if ($old_def->{TYPE} =~ /varchar|text/i && $new_def->{TYPE} =~ /varchar|text/i)
|
||||
{
|
||||
push @statements, _get_notnull_trigger_ddl($table, $column);
|
||||
}
|
||||
push(@statements, "ALTER TABLE $table MODIFY $column"
|
||||
. " NOT NULL");
|
||||
push (@statements, _get_notnull_trigger_ddl($table, $column))
|
||||
if $old_def->{TYPE} =~ /varchar|text/i
|
||||
&& $new_def->{TYPE} =~ /varchar|text/i;
|
||||
}
|
||||
# If we went from NOT NULL to NULL
|
||||
elsif ($old_def->{NOTNULL} && !$new_def->{NOTNULL}) {
|
||||
push(@statements, "ALTER TABLE $table MODIFY $column"
|
||||
. " NULL");
|
||||
push(@statements, "DROP TRIGGER ${table}_${column}")
|
||||
if $new_def->{TYPE} =~ /varchar|text/i
|
||||
&& $old_def->{TYPE} =~ /varchar|text/i;
|
||||
elsif ($old_def->{NOTNULL} && !$new_def->{NOTNULL})
|
||||
{
|
||||
push @statements, "ALTER TABLE $table MODIFY $column NULL";
|
||||
if ($new_def->{TYPE} =~ /varchar|text/i && $old_def->{TYPE} =~ /varchar|text/i)
|
||||
{
|
||||
push @statements, "DROP TRIGGER ${table}_${column}";
|
||||
}
|
||||
}
|
||||
|
||||
# If we went from not being a PRIMARY KEY to being a PRIMARY KEY.
|
||||
if (!$old_def->{PRIMARYKEY} && $new_def->{PRIMARYKEY}) {
|
||||
if (!$old_def->{PRIMARYKEY} && $new_def->{PRIMARYKEY})
|
||||
{
|
||||
push(@statements, "ALTER TABLE $table ADD PRIMARY KEY ($column)");
|
||||
}
|
||||
# If we went from being a PK to not being a PK
|
||||
elsif ( $old_def->{PRIMARYKEY} && !$new_def->{PRIMARYKEY} ) {
|
||||
elsif ($old_def->{PRIMARYKEY} && !$new_def->{PRIMARYKEY})
|
||||
{
|
||||
push(@statements, "ALTER TABLE $table DROP PRIMARY KEY");
|
||||
}
|
||||
|
||||
return @statements;
|
||||
}
|
||||
|
||||
sub _get_alter_type_sql {
|
||||
sub _get_alter_type_sql
|
||||
{
|
||||
my ($self, $table, $column, $new_def, $old_def) = @_;
|
||||
my @statements;
|
||||
|
||||
my $type = $new_def->{TYPE};
|
||||
$type = $self->{db_specific}->{$type}
|
||||
if exists $self->{db_specific}->{$type};
|
||||
$type = $self->{db_specific}->{$type} if exists $self->{db_specific}->{$type};
|
||||
|
||||
if ($type =~ /serial/i && $old_def->{TYPE} !~ /serial/i) {
|
||||
die("You cannot specify a DEFAULT on a SERIAL-type column.")
|
||||
if $new_def->{DEFAULT};
|
||||
if ($type =~ /serial/i && $old_def->{TYPE} !~ /serial/i)
|
||||
{
|
||||
die("You cannot specify a DEFAULT on a SERIAL-type column.") if $new_def->{DEFAULT};
|
||||
}
|
||||
|
||||
if ( ($old_def->{TYPE} =~ /LONGTEXT/i && $new_def->{TYPE} !~ /LONGTEXT/i)
|
||||
|| ($old_def->{TYPE} !~ /LONGTEXT/i && $new_def->{TYPE} =~ /LONGTEXT/i)
|
||||
) {
|
||||
# LONG to VARCHAR or VARCHAR to LONG is not allowed in Oracle,
|
||||
# just a way to work around.
|
||||
if ($old_def->{TYPE} =~ /LONGTEXT/i && $new_def->{TYPE} !~ /LONGTEXT/i ||
|
||||
$old_def->{TYPE} !~ /LONGTEXT/i && $new_def->{TYPE} =~ /LONGTEXT/i)
|
||||
{
|
||||
# LONG to VARCHAR or VARCHAR to LONG is not allowed in Oracle, just a way to work around.
|
||||
# Determine whether column_temp is already exist.
|
||||
my $dbh=Bugzilla->dbh;
|
||||
my $dbh = Bugzilla->dbh;
|
||||
my $column_exist = $dbh->selectcol_arrayref(
|
||||
"SELECT CNAME FROM COL WHERE TNAME = UPPER(?) AND
|
||||
CNAME = UPPER(?)", undef,$table,$column . "_temp");
|
||||
if(!@$column_exist) {
|
||||
push(@statements,
|
||||
"ALTER TABLE $table ADD ${column}_temp $type");
|
||||
"SELECT CNAME FROM COL WHERE TNAME = UPPER(?) AND CNAME = UPPER(?)",
|
||||
undef, $table, $column . "_temp"
|
||||
);
|
||||
if(!@$column_exist)
|
||||
{
|
||||
push @statements, "ALTER TABLE $table ADD ${column}_temp $type";
|
||||
}
|
||||
push(@statements, "UPDATE $table SET ${column}_temp = $column");
|
||||
push(@statements, "COMMIT");
|
||||
push(@statements, "ALTER TABLE $table DROP COLUMN $column");
|
||||
push(@statements,
|
||||
"ALTER TABLE $table RENAME COLUMN ${column}_temp TO $column");
|
||||
} else {
|
||||
push(@statements, "ALTER TABLE $table MODIFY $column $type");
|
||||
push @statements, "UPDATE $table SET ${column}_temp = $column";
|
||||
push @statements, "COMMIT";
|
||||
push @statements, "ALTER TABLE $table DROP COLUMN $column";
|
||||
push @statements, "ALTER TABLE $table RENAME COLUMN ${column}_temp TO $column";
|
||||
}
|
||||
else
|
||||
{
|
||||
push @statements, "ALTER TABLE $table MODIFY $column $type";
|
||||
}
|
||||
|
||||
if ($new_def->{TYPE} =~ /serial/i && $old_def->{TYPE} !~ /serial/i) {
|
||||
push(@statements, _get_create_seq_ddl($table, $column));
|
||||
if ($new_def->{TYPE} =~ /serial/i && $old_def->{TYPE} !~ /serial/i)
|
||||
{
|
||||
push @statements, _get_create_seq_ddl($table, $column);
|
||||
}
|
||||
|
||||
# If this column is no longer SERIAL, we need to drop the sequence
|
||||
# that went along with it.
|
||||
if ($old_def->{TYPE} =~ /serial/i && $new_def->{TYPE} !~ /serial/i) {
|
||||
push(@statements, "DROP SEQUENCE ${table}_${column}_SEQ");
|
||||
push(@statements, "DROP TRIGGER ${table}_${column}_TR");
|
||||
if ($old_def->{TYPE} =~ /serial/i && $new_def->{TYPE} !~ /serial/i)
|
||||
{
|
||||
push @statements, "DROP SEQUENCE ${table}_${column}_SEQ";
|
||||
push @statements, "DROP TRIGGER ${table}_${column}_TR";
|
||||
}
|
||||
|
||||
|
||||
# If this column is changed to type TEXT/VARCHAR, we need to deal with
|
||||
# empty string.
|
||||
if ( $old_def->{TYPE} !~ /varchar|text/i
|
||||
&& $new_def->{TYPE} =~ /varchar|text/i
|
||||
&& $new_def->{NOTNULL} )
|
||||
if ($old_def->{TYPE} !~ /varchar|text/i &&
|
||||
$new_def->{TYPE} =~ /varchar|text/i && $new_def->{NOTNULL})
|
||||
{
|
||||
push (@statements, _get_notnull_trigger_ddl($table, $column));
|
||||
}
|
||||
push (@statements, _get_notnull_trigger_ddl($table, $column));
|
||||
}
|
||||
# If this column is no longer TEXT/VARCHAR, we need to drop the trigger
|
||||
# that went along with it.
|
||||
if ( $old_def->{TYPE} =~ /varchar|text/i
|
||||
&& $old_def->{NOTNULL}
|
||||
&& $new_def->{TYPE} !~ /varchar|text/i )
|
||||
if ($old_def->{TYPE} =~ /varchar|text/i && $old_def->{NOTNULL} &&
|
||||
$new_def->{TYPE} !~ /varchar|text/i)
|
||||
{
|
||||
push(@statements, "DROP TRIGGER ${table}_${column}");
|
||||
}
|
||||
push @statements, "DROP TRIGGER ${table}_${column}";
|
||||
}
|
||||
return @statements;
|
||||
}
|
||||
|
||||
sub get_rename_column_ddl {
|
||||
sub get_rename_column_ddl
|
||||
{
|
||||
my ($self, $table, $old_name, $new_name) = @_;
|
||||
if (lc($old_name) eq lc($new_name)) {
|
||||
if (lc($old_name) eq lc($new_name))
|
||||
{
|
||||
# if the only change is a case change, return an empty list.
|
||||
return ();
|
||||
}
|
||||
my @sql = ("ALTER TABLE $table RENAME COLUMN $old_name TO $new_name");
|
||||
my $def = $self->get_column_abstract($table, $old_name);
|
||||
if ($def->{TYPE} =~ /SERIAL/i) {
|
||||
if ($def->{TYPE} =~ /SERIAL/i)
|
||||
{
|
||||
# We have to rename the series also, and fix the default of the series.
|
||||
push(@sql, "RENAME ${table}_${old_name}_SEQ TO
|
||||
${table}_${new_name}_seq");
|
||||
my $serial_sql =
|
||||
"CREATE OR REPLACE TRIGGER ${table}_${new_name}_TR "
|
||||
. " BEFORE INSERT ON ${table} "
|
||||
. " FOR EACH ROW "
|
||||
. " BEGIN "
|
||||
. " SELECT ${table}_${new_name}_SEQ.NEXTVAL "
|
||||
. " INTO :NEW.${new_name} FROM DUAL; "
|
||||
. " END;";
|
||||
push(@sql, $serial_sql);
|
||||
push(@sql, "DROP TRIGGER ${table}_${old_name}_TR");
|
||||
push @sql, "RENAME ${table}_${old_name}_SEQ TO ${table}_${new_name}_seq";
|
||||
my $serial_sql = "CREATE OR REPLACE TRIGGER ${table}_${new_name}_TR "
|
||||
. " BEFORE INSERT ON ${table} "
|
||||
. " FOR EACH ROW "
|
||||
. " BEGIN "
|
||||
. " SELECT ${table}_${new_name}_SEQ.NEXTVAL "
|
||||
. " INTO :NEW.${new_name} FROM DUAL; "
|
||||
. " END;";
|
||||
push @sql, $serial_sql;
|
||||
push @sql, "DROP TRIGGER ${table}_${old_name}_TR";
|
||||
}
|
||||
if ($def->{TYPE} =~ /varchar|text/i && $def->{NOTNULL} ) {
|
||||
push(@sql, _get_notnull_trigger_ddl($table,$new_name));
|
||||
push(@sql, "DROP TRIGGER ${table}_${old_name}");
|
||||
if ($def->{TYPE} =~ /varchar|text/i && $def->{NOTNULL})
|
||||
{
|
||||
push @sql, _get_notnull_trigger_ddl($table, $new_name);
|
||||
push @sql, "DROP TRIGGER ${table}_${old_name}";
|
||||
}
|
||||
return @sql;
|
||||
}
|
||||
|
||||
sub _get_notnull_trigger_ddl {
|
||||
my ($table, $column) = @_;
|
||||
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;
|
||||
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 ($self, $table, $column, $start_with) = @_;
|
||||
$start_with ||= 1;
|
||||
my @ddl;
|
||||
my $seq_name = "${table}_${column}_SEQ";
|
||||
my $seq_sql = "CREATE SEQUENCE $seq_name "
|
||||
. " INCREMENT BY 1 "
|
||||
. " START WITH $start_with "
|
||||
. " 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);
|
||||
|
||||
sub _get_create_seq_ddl
|
||||
{
|
||||
my ($self, $table, $column, $start_with) = @_;
|
||||
$start_with ||= 1;
|
||||
my @ddl;
|
||||
my $seq_name = "${table}_${column}_SEQ";
|
||||
my $seq_sql = "CREATE SEQUENCE $seq_name "
|
||||
. " INCREMENT BY 1 "
|
||||
. " START WITH $start_with "
|
||||
. " 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;
|
||||
}
|
||||
|
||||
sub get_set_serial_sql {
|
||||
my ($self, $table, $column, $value) = @_;
|
||||
sub get_set_serial_sql
|
||||
{
|
||||
my ($self, $table, $column, $value) = @_;
|
||||
my @sql;
|
||||
my $seq_name = "${table}_${column}_SEQ";
|
||||
push(@sql, "DROP SEQUENCE ${seq_name}");
|
||||
push(@sql, $self->_get_create_seq_ddl($table, $column, $value));
|
||||
push @sql, "DROP SEQUENCE ${seq_name}";
|
||||
push @sql, $self->_get_create_seq_ddl($table, $column, $value);
|
||||
return @sql;
|
||||
}
|
||||
}
|
||||
|
||||
sub get_drop_column_ddl {
|
||||
sub get_drop_column_ddl
|
||||
{
|
||||
my $self = shift;
|
||||
my ($table, $column) = @_;
|
||||
my @sql;
|
||||
push(@sql, $self->SUPER::get_drop_column_ddl(@_));
|
||||
my $dbh=Bugzilla->dbh;
|
||||
push @sql, $self->SUPER::get_drop_column_ddl(@_);
|
||||
my $dbh = Bugzilla->dbh;
|
||||
my $trigger_name = uc($table . "_" . $column);
|
||||
my $exist_trigger = $dbh->selectcol_arrayref(
|
||||
"SELECT OBJECT_NAME FROM USER_OBJECTS
|
||||
WHERE OBJECT_NAME = ?", undef, $trigger_name);
|
||||
if(@$exist_trigger) {
|
||||
"SELECT OBJECT_NAME FROM USER_OBJECTS".
|
||||
" WHERE OBJECT_NAME = ?", undef, $trigger_name
|
||||
);
|
||||
if (@$exist_trigger)
|
||||
{
|
||||
push(@sql, "DROP TRIGGER $trigger_name");
|
||||
}
|
||||
return @sql;
|
||||
|
|
Loading…
Reference in New Issue