Commit 62b2ec34 authored by mkanat%kerio.com's avatar mkanat%kerio.com

Bug 237862: New database layer for cross-database compatibility

Patch By Tomas Kopal <Tomas.Kopal@altap.cz> r=mkanat, a=myk
parent fc3aa3e7
...@@ -71,7 +71,7 @@ our $webdotdir = "$datadir/webdot"; ...@@ -71,7 +71,7 @@ our $webdotdir = "$datadir/webdot";
%Bugzilla::Config::EXPORT_TAGS = %Bugzilla::Config::EXPORT_TAGS =
( (
admin => [qw(GetParamList UpdateParams SetParam WriteParams)], admin => [qw(GetParamList UpdateParams SetParam WriteParams)],
db => [qw($db_host $db_port $db_name $db_user $db_pass $db_sock)], db => [qw($db_driver $db_host $db_port $db_name $db_user $db_pass $db_sock)],
locations => [qw($libpath $localconfig $datadir $templatedir $webdotdir)], locations => [qw($libpath $localconfig $datadir $templatedir $webdotdir)],
); );
Exporter::export_ok_tags('admin', 'db', 'locations'); Exporter::export_ok_tags('admin', 'db', 'locations');
......
...@@ -22,6 +22,7 @@ ...@@ -22,6 +22,7 @@
# Jacob Steenhagen <jake@bugzilla.org> # Jacob Steenhagen <jake@bugzilla.org>
# Bradley Baetz <bbaetz@student.usyd.edu.au> # Bradley Baetz <bbaetz@student.usyd.edu.au>
# Christopher Aillon <christopher@aillon.com> # Christopher Aillon <christopher@aillon.com>
# Tomas Kopal <Tomas.Kopal@altap.cz>
package Bugzilla::DB; package Bugzilla::DB;
...@@ -29,7 +30,11 @@ use strict; ...@@ -29,7 +30,11 @@ use strict;
use DBI; use DBI;
use base qw(Exporter); # Inherit the DB class from DBI::db and Exporter
# Note that we inherit from Exporter just to allow the old, deprecated
# interface to work. If it gets removed, the Exporter class can be removed
# from this list.
use base qw(Exporter DBI::db);
%Bugzilla::DB::EXPORT_TAGS = %Bugzilla::DB::EXPORT_TAGS =
( (
...@@ -42,6 +47,7 @@ Exporter::export_ok_tags('deprecated'); ...@@ -42,6 +47,7 @@ Exporter::export_ok_tags('deprecated');
use Bugzilla::Config qw(:DEFAULT :db); use Bugzilla::Config qw(:DEFAULT :db);
use Bugzilla::Util; use Bugzilla::Util;
use Bugzilla::Error;
# All this code is backwards compat fu. As such, its a bit ugly. Note the # All this code is backwards compat fu. As such, its a bit ugly. Note the
# circular dependencies on Bugzilla.pm # circular dependencies on Bugzilla.pm
...@@ -122,43 +128,29 @@ sub PopGlobalSQLState() { ...@@ -122,43 +128,29 @@ sub PopGlobalSQLState() {
sub connect_shadow { sub connect_shadow {
die "Tried to connect to non-existent shadowdb" unless Param('shadowdb'); die "Tried to connect to non-existent shadowdb" unless Param('shadowdb');
my $dsn = "DBI:mysql:host=" . Param("shadowdbhost") . return _connect($db_driver, Param("shadowdbhost"),
";database=" . Param('shadowdb') . ";port=" . Param("shadowdbport"); Param('shadowdb'), Param("shadowdbport"),
Param("shadowdbsock"), $db_user, $db_pass);
$dsn .= ";mysql_socket=" . Param("shadowdbsock") if Param('shadowdbsock');
return _connect($dsn);
} }
sub connect_main { sub connect_main {
my $dsn = "DBI:mysql:host=$db_host;database=$db_name;port=$db_port"; return _connect($db_driver, $db_host, $db_name, $db_port,
$db_sock, $db_user, $db_pass);
$dsn .= ";mysql_socket=$db_sock" if $db_sock;
return _connect($dsn);
} }
sub _connect { sub _connect {
my ($dsn) = @_; my ($driver, $host, $dbname, $port, $sock, $user, $pass) = @_;
# connect using our known info to the specified db # DB specific module have the same name as DB driver, here we
# Apache::DBI will cache this when using mod_perl # just make sure we are not case sensitive
my $dbh = DBI->connect($dsn, (my $db_module = $driver) =~ s/(\w+)/\u\L$1/g;
'', my $pkg_module = "Bugzilla::DB::" . $db_module;
'',
{ RaiseError => 1, # do the actual import
PrintError => 0, eval ("require $pkg_module") || die ($@);
Username => $db_user,
Password => $db_pass, # instantiate the correct DB specific module
ShowErrorStatement => 1, my $dbh = $pkg_module->new($user, $pass, $host, $dbname, $port, $sock);
HandleError => \&_handle_error,
TaintIn => 1,
FetchHashKeyName => 'NAME',
# Note: NAME_lc causes crash on ActiveState Perl
# 5.8.4 (see Bug 253696)
# XXX - This will likely cause problems in DB
# back ends that twiddle column case (Oracle?)
});
return $dbh; return $dbh;
} }
...@@ -173,6 +165,39 @@ sub _handle_error { ...@@ -173,6 +165,39 @@ sub _handle_error {
return 0; # Now let DBI handle raising the error return 0; # Now let DBI handle raising the error
} }
# List of abstract methods we are checking the derived class implements
our @_abstract_methods = qw(new sql_regexp sql_not_regexp sql_limit
sql_to_days sql_date_format sql_interval
bz_lock_tables bz_unlock_tables);
# This overriden import method will check implementation of inherited classes
# for missing implementation of abstract methods
# See http://perlmonks.thepen.com/44265.html
sub import {
my $pkg = shift;
# do not check this module
if ($pkg ne __PACKAGE__) {
# make sure all abstract methods are implemented
foreach my $meth (@_abstract_methods) {
$pkg->can($meth)
or croak("Class $pkg does not define method $meth");
}
}
# Now we want to call our superclass implementation.
# If our superclass is Exporter, which is using caller() to find
# a namespace to populate, we need to adjust for this extra call.
# All this can go when we stop using deprecated functions.
my $is_exporter = $pkg->isa('Exporter');
$Exporter::ExportLevel++ if $is_exporter;
$pkg->SUPER::import(@_);
$Exporter::ExportLevel-- if $is_exporter;
}
# note that when multiple databases are supported, version number does not
# make sense anymore (as it is DB dependant). This needs to be removed in
# the future and places where it's used fixed.
my $cached_server_version; my $cached_server_version;
sub server_version { sub server_version {
return $cached_server_version if defined($cached_server_version); return $cached_server_version if defined($cached_server_version);
...@@ -183,8 +208,8 @@ sub server_version { ...@@ -183,8 +208,8 @@ sub server_version {
return $cached_server_version; return $cached_server_version;
} }
sub GetFieldDefs { sub bz_get_field_defs {
my $dbh = Bugzilla->dbh; my ($self) = @_;
my $extra = ""; my $extra = "";
if (!&::UserInGroup(Param('timetrackinggroup'))) { if (!&::UserInGroup(Param('timetrackinggroup'))) {
...@@ -193,7 +218,7 @@ sub GetFieldDefs { ...@@ -193,7 +218,7 @@ sub GetFieldDefs {
} }
my @fields; my @fields;
my $sth = $dbh->prepare("SELECT name, description my $sth = $self->prepare("SELECT name, description
FROM fielddefs $extra FROM fielddefs $extra
ORDER BY sortkey"); ORDER BY sortkey");
$sth->execute(); $sth->execute();
...@@ -203,6 +228,82 @@ sub GetFieldDefs { ...@@ -203,6 +228,82 @@ sub GetFieldDefs {
return(@fields); return(@fields);
} }
sub bz_last_key {
my ($self, $table, $column) = @_;
return $self->last_insert_id($db_name, undef, $table, $column);
}
sub bz_start_transaction {
my ($self) = @_;
if ($self->{private_bz_in_transaction}) {
carp("Can't start transaction within another transaction");
ThrowCodeError("nested_transaction");
} else {
# Turn AutoCommit off and start a new transaction
$self->begin_work();
$self->{privateprivate_bz_in_transaction} = 1;
}
}
sub bz_commit_transaction {
my ($self) = @_;
if (!$self->{private_bz_in_transaction}) {
carp("Can't commit without a transaction");
ThrowCodeError("not_in_transaction");
} else {
$self->commit();
$self->{private_bz_in_transaction} = 0;
}
}
sub bz_rollback_transaction {
my ($self) = @_;
if (!$self->{private_bz_in_transaction}) {
carp("Can't rollback without a transaction");
ThrowCodeError("not_in_transaction");
} else {
$self->rollback();
$self->{private_bz_in_transaction} = 0;
}
}
sub db_new {
my ($class, $dsn, $user, $pass, $attributes) = @_;
# set up default attributes used to connect to the database
# (if not defined by DB specific implementation)
$attributes = { RaiseError => 1,
AutoCommit => 1,
PrintError => 0,
ShowErrorStatement => 1,
HandleError => \&_handle_error,
TaintIn => 1,
FetchHashKeyName => 'NAME',
# Note: NAME_lc causes crash on ActiveState Perl
# 5.8.4 (see Bug 253696)
# XXX - This will likely cause problems in DB
# back ends that twiddle column case (Oracle?)
} if (!defined($attributes));
# connect using our known info to the specified db
# Apache::DBI will cache this when using mod_perl
my $self = DBI->connect($dsn, $user, $pass, $attributes);
# class variables
$self->{private_bz_in_transaction} = 0;
bless ($self, $class);
return $self;
}
1; 1;
__END__ __END__
...@@ -213,60 +314,249 @@ Bugzilla::DB - Database access routines, using L<DBI> ...@@ -213,60 +314,249 @@ Bugzilla::DB - Database access routines, using L<DBI>
=head1 SYNOPSIS =head1 SYNOPSIS
# Connection # Obtain db handle
my $dbh = Bugzilla::DB->connect_main; use Bugzilla::DB;
my $shadow = Bugzilla::DB->connect_shadow; my $dbh = Bugzilla->dbh;
# Schema Information # prepare a query using DB methods
my @fields = Bugzilla::DB::GetFieldDefs(); my $sth = $dbh->prepare("SELECT " .
$dbh->sql_date_format("creation_ts", "%Y%m%d") .
" FROM bugs WHERE bug_status != 'RESOLVED' " .
$dbh->sql_limit(1));
# Deprecated # Execute the query
SendSQL("SELECT COUNT(*) FROM bugs"); $sth->execute;
my $cnt = FetchOneColumn();
# Get the results
my @result = $sth->fetchrow_array;
# Schema Information
my @fields = $dbh->bz_get_field_defs();
=head1 DESCRIPTION =head1 DESCRIPTION
This allows creation of a database handle to connect to the Bugzilla database. Functions in this module allows creation of a database handle to connect
This should never be done directly; all users should use the L<Bugzilla> module to the Bugzilla database. This should never be done directly; all users
to access the current C<dbh> instead. should use the L<Bugzilla> module to access the current C<dbh> instead.
This module also contains methods extending the returned handle with
functionality which is different between databases allowing for easy
customization for particular database via inheritance. These methods
should be always preffered over hard-coding SQL commands.
Access to the old SendSQL-based database routines are also provided by Access to the old SendSQL-based database routines are also provided by
importing the C<:deprecated> tag. These routines should not be used in new importing the C<:deprecated> tag. These routines should not be used in new
code. code.
The only functions that should be used by modern, regular Bugzilla code
are the "Schema Information" functions.
=head1 CONNECTION =head1 CONNECTION
A new database handle to the required database can be created using this A new database handle to the required database can be created using this
module. This is normally done by the L<Bugzilla> module, and so these routines module. This is normally done by the L<Bugzilla> module, and so these routines
should not be called from anywhere else. should not be called from anywhere else.
=head2 Functions
=over 4 =over 4
=item C<connect_main> =item C<connect_main>
Connects to the main database, returning a new dbh. Description: Function to connect to the main database, returning a new
database handle.
Params: none
Returns: new instance of the DB class
=item C<connect_shadow> =item C<connect_shadow>
Connects to the shadow database, returning a new dbh. This routine C<die>s if Description: Function to connect to the shadow database, returning a new
no shadow database is configured. database handle.
This routine C<die>s if no shadow database is configured.
Params: none
Returns: new instance of the DB class
=item C<_connect>
Description: Internal function, creates and returns a new, connected
instance of the correct DB class.
This routine C<die>s if no driver is specified.
Params: $driver = name of the database driver to use
$host = host running the database we are connecting to
$dbname = name of the database to connect to
$port = port the database is listening on
$sock = socket the database is listening on
$user = username used to log in to the database
$pass = password used to log in to the database
Returns: new instance of the DB class
=item C<_handle_error>
Description: Function passed to the DBI::connect call for error handling.
It shortens the error for printing.
=item C<import>
Description: Overrides the standard import method to check that derived class
implements all required abstract methods. Also calls original
implementation in its super class.
=back =back
=head1 SCHEMA INFORMATION =head2 Methods
Bugzilla::DB also contains routines to get schema information about the Note: Methods which can be implemented generically for all DBs are implemented in
database. this module. If needed, they can be overriden with DB specific code.
Methods which do not have standard implementation are abstract and must
be implemented for all supported databases separately.
To avoid confusion with standard DBI methods, all methods returning string with
formatted SQL command have prefix C<sql_>. All other methods have prefix C<bz_>.
=over 4 =over 4
=item C<GetFieldDefs> =item C<new>
Description: Constructor
Abstract method, should be overriden by database specific code.
Params: $user = username used to log in to the database
$pass = password used to log in to the database
$host = host running the database we are connecting to
$dbname = name of the database to connect to
$port = port the database is listening on
$sock = socket the database is listening on
Returns: new instance of the DB class
Note: The constructor should create a DSN from the parameters provided and
then call C<db_new()> method of its super class to create a new
class instance. See C<db_new> description in this module. As per
DBI documentation, all class variables must be prefixed with
"private_". See L<DBI>.
=item C<sql_regexp>
Description: Outputs SQL regular expression operator for POSIX regex
searches in format suitable for a given database.
Abstract method, should be overriden by database specific code.
Params: none
Returns: formatted SQL for regular expression search (e.g. REGEXP)
(scalar)
=item C<sql_not_regexp>
Description: Outputs SQL regular expression operator for negative POSIX
regex searches in format suitable for a given database.
Abstract method, should be overriden by database specific code.
Params: none
Returns: formatted SQL for negative regular expression search
(e.g. NOT REGEXP) (scalar)
=item C<sql_limit>
Description: Returns SQL syntax for limiting results to some number of rows
with optional offset if not starting from the begining.
Abstract method, should be overriden by database specific code.
Params: $limit = number of rows to return from query (scalar)
$offset = number of rows to skip prior counting (scalar)
Returns: formatted SQL for limiting number of rows returned from query
with optional offset (e.g. LIMIT 1, 1) (scalar)
=item C<sql_to_days>
Description: Outputs SQL syntax for converting date to Julian days.
Abstract method, should be overriden by database specific code.
Params: $date = date to convert to days
Returns: formatted SQL for returning date fields in Julian days. (scalar)
=item C<sql_date_format>
Description: Outputs SQL syntax for formatting dates.
Abstract method, should be overriden by database specific code.
Params: $date = date or name of date type column (scalar)
$format = format string for date output (scalar)
(%Y = year, four digits, %y = year, two digits, %m = month,
%d = day, %a = weekday name, 3 letters, %H = hour 00-23,
%i = minute, %s = second)
Returns: formatted SQL for date formatting (scalar)
=item C<sql_interval>
Description: Outputs proper SQL syntax for a time interval function.
Abstract method, should be overriden by database specific code.
Params: $interval = the time interval requested (e.g. '30 minutes')
(scalar)
Returns: formatted SQL for interval function (scalar)
=item C<bz_lock_tables>
Description: Performs a table lock operation on specified tables.
If the underlying database supports transactions, it should also
implicitly start a new transaction.
Abstract method, should be overriden by database specific code.
Params: @tables = list of names of tables to lock in MySQL
notation (ex. 'bugs AS bugs2 READ', 'logincookies WRITE')
Returns: none
=item C<bz_unlock_tables>
Description: Performs a table unlock operation
If the underlying database supports transactions, it should also
implicitly commit or rollback the transaction.
Also, this function should allow to be called with the abort flag
set even without locking tables first without raising an error
to simplify error handling.
Abstract method, should be overriden by database specific code.
Params: $abort = true (1) if the operation on locked tables failed
(if transactions are supported, the action will be rolled
back). False (0) or no param if the operation succeeded.
Returns: none
=item C<bz_last_key>
Description: Returns the last serial number, usually from a previous INSERT.
Must be executed directly following the relevant INSERT.
This base implementation uses DBI->last_insert_id. If the
DBD supports it, it is the preffered way to obtain the last
serial index. If it is not supported, the DB specific code
needs to override it with DB specific code.
Params: $table = name of table containing serial column (scalar)
$column = name of column containing serial data type (scalar)
Returns: Last inserted ID (scalar)
=item C<bz_get_field_defs>
Description: Returns a list of all the "bug" fields in Bugzilla. The list
contains hashes, with a 'name' key and a 'description' key.
Params: none
Returns: List of all the "bug" fields
=item C<bz_start_transaction>
Description: Starts a transaction if supported by the database being used
Params: none
Returns: none
=item C<bz_commit_transaction>
Description: Ends a transaction, commiting all changes, if supported by
the database being used
Params: none
Returns: none
=item C<bz_rollback_transaction>
Description: Ends a transaction, rolling back all changes, if supported by
the database being used
Params: none
Returns: none
=item C<db_new>
Description: Constructor
Params: $dsn = database connection string
$user = username used to log in to the database
$pass = password used to log in to the database
$attributes = set of attributes for DB connection (optional)
Returns: new instance of the DB class
Note: the name of this constructor is not new, as that would make
our check for implementation of new() by derived class useles.
Returns a list of all the "bug" fields in Bugzilla. The list contains =back
hashes, with a 'name' key and a 'description' key.
=head1 DEPRECATED ROUTINES =head1 DEPRECATED ROUTINES
......
# -*- 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 Netscape Communications
# Corporation. Portions created by Netscape are
# Copyright (C) 1998 Netscape Communications Corporation. All
# Rights Reserved.
#
# Contributor(s): Dave Miller <davem00@aol.com>
# Gayathri Swaminath <gayathrik00@aol.com>
# Jeroen Ruigrok van der Werven <asmodai@wxs.nl>
# Dave Lawrence <dkl@redhat.com>
# Tomas Kopal <Tomas.Kopal@altap.cz>
=head1 NAME
Bugzilla::DB::Mysql - Bugzilla database compatibility layer for MySQL
=head1 DESCRIPTION
This module overrides methods of the Bugzilla::DB module with MySQL specific
implementation. It is instantiated by the Bugzilla::DB module and should never
be used directly.
For interface details see L<Bugzilla::DB> and L<DBI>.
=cut
package Bugzilla::DB::Mysql;
use strict;
use Bugzilla::Error;
use Carp;
# This module extends the DB interface via inheritance
use base qw(Bugzilla::DB);
sub new {
my ($class, $user, $pass, $host, $dbname, $port, $sock) = @_;
# construct the DSN from the parameters we got
my $dsn = "DBI:mysql:host=$host;database=$dbname;port=$port";
$dsn .= ";mysql_socket=$sock" if $sock;
my $self = $class->db_new($dsn, $user, $pass);
# all class local variables stored in DBI derived class needs to have
# a prefix 'private_'. See DBI documentation.
$self->{private_bz_tables_locked} = 0;
bless ($self, $class);
return $self;
}
# when last_insert_id() is supported on MySQL by lowest DBI/DBD version
# required by Bugzilla, this implementation can be removed.
sub bz_last_key {
my ($self) = @_;
my ($last_insert_id) = $self->selectrow_array('SELECT LAST_INSERT_ID()');
return $last_insert_id;
}
sub sql_regexp {
return "REGEXP";
}
sub sql_not_regexp {
return "NOT REGEXP";
}
sub sql_limit {
my ($self, $limit,$offset) = @_;
if (defined($offset)) {
return "LIMIT $offset, $limit";
} else {
return "LIMIT $limit";
}
}
sub sql_to_days {
my ($self, $date) = @_;
return "TO_DAYS($date)";
}
sub sql_date_format {
my ($self, $date, $format) = @_;
$format = "%Y.%m.%d %H:%i:%s" if !$format;
return "DATE_FORMAT($date, " . $self->quote($format) . ")";
}
sub sql_interval {
my ($self, $interval) = @_;
return "INTERVAL $interval";
}
sub bz_lock_tables {
my ($self, @tables) = @_;
# Check first if there was no lock before
if ($self->{private_bz_tables_locked}) {
carp("Tables already locked");
ThrowCodeError("already_locked");
} else {
$self->do('LOCK TABLE ' . join(', ', @tables));
$self->{private_bz_tables_locked} = 1;
}
}
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;
carp("No matching lock");
ThrowCodeError("no_matching_lock");
} else {
$self->do("UNLOCK TABLES");
$self->{private_bz_tables_locked} = 0;
}
}
# As Bugzilla currently runs on MyISAM storage, which does not supprt
# transactions, these functions die when called.
# Maybe we should just ignore these calls for now, but as we are not
# using transactions in MySQL yet, this just hints the developers.
sub bz_start_transaction {
die("Attempt to start transaction on DB without transaction support");
}
sub bz_commit_transaction {
die("Attempt to commit transaction on DB without transaction support");
}
sub bz_rollback_transaction {
die("Attempt to rollback transaction on DB without transaction support");
}
1;
# -*- 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 Netscape Communications
# Corporation. Portions created by Netscape are
# Copyright (C) 1998 Netscape Communications Corporation. All
# Rights Reserved.
#
# Contributor(s): Dave Miller <davem00@aol.com>
# Gayathri Swaminath <gayathrik00@aol.com>
# Jeroen Ruigrok van der Werven <asmodai@wxs.nl>
# Dave Lawrence <dkl@redhat.com>
# Tomas Kopal <Tomas.Kopal@altap.cz>
=head1 NAME
Bugzilla::DB::Pg - Bugzilla database compatibility layer for PostgreSQL
=head1 DESCRIPTION
This module overrides methods of the Bugzilla::DB module with PostgreSQL
specific implementation. It is instantiated by the Bugzilla::DB module
and should never be used directly.
For interface details see L<Bugzilla::DB> and L<DBI>.
=cut
package Bugzilla::DB::Pg;
use strict;
use Bugzilla::Error;
use Carp;
# This module extends the DB interface via inheritance
use base qw(Bugzilla::DB);
sub new {
my ($class, $user, $pass, $host, $dbname, $port) = @_;
# construct the DSN from the parameters we got
my $dsn = "DBI:Pg:host=$host;dbname=$dbname;port=$port";
my $self = $class->db_new($dsn, $user, $pass);
# all class local variables stored in DBI derived class needs to have
# a prefix 'private_'. See DBI documentation.
$self->{private_bz_tables_locked} = 0;
bless ($self, $class);
return $self;
}
# if last_insert_id is supported on PostgreSQL by lowest DBI/DBD version
# supported by Bugzilla, this implementation can be removed.
sub bz_last_key {
my ($self, $table, $column) = @_;
my $seq = $table . "_" . $column . "_seq";
my ($last_insert_id) = $self->selectrow_array("SELECT CURRVAL('$seq')");
return $last_insert_id;
}
sub sql_regexp {
return "~";
}
sub sql_not_regexp {
return "!~"
}
sub sql_limit {
my ($self, $limit,$offset) = @_;
if (defined($offset)) {
return "LIMIT $limit OFFSET $offset";
} else {
return "LIMIT $limit";
}
}
sub sql_to_days {
my ($self, $date) = @_;
return "TO_CHAR($date, 'J')::int";
}
sub sql_date_format {
my ($self, $date, $format) = @_;
$format = "%Y.%m.%d %H:%i:%s" if !$format;
$format =~ s/\%Y/YYYY/g;
$format =~ s/\%y/YY/g;
$format =~ s/\%m/MM/g;
$format =~ s/\%d/DD/g;
$format =~ s/\%a/Dy/g;
$format =~ s/\%H/HH24/g;
$format =~ s/\%i/MI/g;
$format =~ s/\%s/SS/g;
return "TO_CHAR($date, " . $self->quote($format) . ")";
}
sub sql_interval {
my ($self, $interval) = @_;
return "INTERVAL '$interval'";
}
sub bz_lock_tables {
my ($self, @tables) = @_;
# Check first if there was no lock before
if ($self->{private_bz_tables_locked}) {
carp("Tables already locked");
ThrowCodeError("already_locked");
} 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;
}
}
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;
carp("No matching lock");
ThrowCodeError("no_matching_lock");
} else {
# End transaction, tables will be unlocked automatically
if ($abort) {
$self->bz_rollback_transaction();
} else {
$self->bz_commit_transaction();
}
}
}
1;
...@@ -101,6 +101,7 @@ ...@@ -101,6 +101,7 @@
# The format of that file is.... # The format of that file is....
# #
# $answer{'db_host'} = '$db_host = "localhost"; # $answer{'db_host'} = '$db_host = "localhost";
# $db_driver = "mydbdriver";
# $db_port = 3306; # $db_port = 3306;
# $db_name = "mydbname"; # $db_name = "mydbname";
# $db_user = "mydbuser";'; # $db_user = "mydbuser";';
...@@ -644,6 +645,14 @@ END ...@@ -644,6 +645,14 @@ END
LocalVar('db_driver', '
#
# What SQL database to use. Default is mysql. List of supported databases
# can be obtained by listing Bugzilla/DB directory - every module corresponds
# to one supported database and the name corresponds to a driver name.
#
$db_driver = "mysql";
');
LocalVar('db_host', ' LocalVar('db_host', '
# #
# How to access the SQL database: # How to access the SQL database:
...@@ -793,6 +802,7 @@ if ($newstuff ne "") { ...@@ -793,6 +802,7 @@ if ($newstuff ne "") {
# Note that we won't need to do this in globals.pl because globals.pl couldn't # Note that we won't need to do this in globals.pl because globals.pl couldn't
# care less whether they were defined ahead of time or not. # care less whether they were defined ahead of time or not.
my $my_db_check = ${*{$main::{'db_check'}}{SCALAR}}; my $my_db_check = ${*{$main::{'db_check'}}{SCALAR}};
my $my_db_driver = ${*{$main::{'db_driver'}}{SCALAR}};
my $my_db_host = ${*{$main::{'db_host'}}{SCALAR}}; my $my_db_host = ${*{$main::{'db_host'}}{SCALAR}};
my $my_db_port = ${*{$main::{'db_port'}}{SCALAR}}; my $my_db_port = ${*{$main::{'db_port'}}{SCALAR}};
my $my_db_name = ${*{$main::{'db_name'}}{SCALAR}}; my $my_db_name = ${*{$main::{'db_name'}}{SCALAR}};
...@@ -1505,11 +1515,6 @@ $::ENV{'PATH'} = $origPath; ...@@ -1505,11 +1515,6 @@ $::ENV{'PATH'} = $origPath;
# Check if we have access to --MYSQL-- # Check if we have access to --MYSQL--
# #
# This settings are not yet changeable, because other code depends on
# the fact that we use MySQL and not, say, PostgreSQL.
my $db_base = 'mysql';
# No need to "use" this here. It should already be loaded from the # No need to "use" this here. It should already be loaded from the
# version-checking routines above, and this file won't even compile if # version-checking routines above, and this file won't even compile if
# DBI isn't installed so the user gets nasty errors instead of our # DBI isn't installed so the user gets nasty errors instead of our
...@@ -1522,15 +1527,15 @@ if ($my_db_check) { ...@@ -1522,15 +1527,15 @@ if ($my_db_check) {
my $sql_want = "3.23.41"; # minimum version of MySQL my $sql_want = "3.23.41"; # minimum version of MySQL
# original DSN line was: # original DSN line was:
# my $dsn = "DBI:$db_base:$my_db_name;$my_db_host;$my_db_port"; # my $dsn = "DBI:$my_db_driver:$my_db_name;$my_db_host;$my_db_port";
# removed the $db_name because we don't know it exists yet, and this will fail # removed the $db_name because we don't know it exists yet, and this will fail
# if we request it here and it doesn't. - justdave@syndicomm.com 2000/09/16 # if we request it here and it doesn't. - justdave@syndicomm.com 2000/09/16
my $dsn = "DBI:$db_base:;$my_db_host;$my_db_port"; my $dsn = "DBI:$my_db_driver:;$my_db_host;$my_db_port";
if ($my_db_sock ne "") { if ($my_db_sock ne "") {
$dsn .= ";mysql_socket=$my_db_sock"; $dsn .= ";mysql_socket=$my_db_sock";
} }
my $dbh = DBI->connect($dsn, $my_db_user, $my_db_pass) my $dbh = DBI->connect($dsn, $my_db_user, $my_db_pass)
or die "Can't connect to the $db_base database. Is the database " . or die "Can't connect to the $my_db_driver database. Is the database " .
"installed and\nup and running? Do you have the correct username " . "installed and\nup and running? Do you have the correct username " .
"and password selected in\nlocalconfig?\n\n"; "and password selected in\nlocalconfig?\n\n";
printf("Checking for %15s %-9s ", "MySQL Server", "(v$sql_want)") unless $silent; printf("Checking for %15s %-9s ", "MySQL Server", "(v$sql_want)") unless $silent;
...@@ -1581,14 +1586,14 @@ EOF ...@@ -1581,14 +1586,14 @@ EOF
} }
# now get a handle to the database: # now get a handle to the database:
my $connectstring = "dbi:$db_base:$my_db_name:host=$my_db_host:port=$my_db_port"; my $connectstring = "dbi:$my_db_driver:$my_db_name:host=$my_db_host:port=$my_db_port";
if ($my_db_sock ne "") { if ($my_db_sock ne "") {
$connectstring .= ";mysql_socket=$my_db_sock"; $connectstring .= ";mysql_socket=$my_db_sock";
} }
my $dbh = DBI->connect($connectstring, $my_db_user, $my_db_pass) my $dbh = DBI->connect($connectstring, $my_db_user, $my_db_pass)
or die "Can't connect to the table '$connectstring'.\n", or die "Can't connect to the table '$connectstring'.\n",
"Have you read the Bugzilla Guide in the doc directory? Have you read the doc of '$db_base'?\n"; "Have you read the Bugzilla Guide in the doc directory? Have you read the doc of '$my_db_driver'?\n";
END { $dbh->disconnect if $dbh } END { $dbh->disconnect if $dbh }
...@@ -2191,7 +2196,7 @@ while (my ($tabname, $fielddef) = each %table) { ...@@ -2191,7 +2196,7 @@ while (my ($tabname, $fielddef) = each %table) {
$fielddef =~ s/\$my_platforms/$my_platforms/; $fielddef =~ s/\$my_platforms/$my_platforms/;
$dbh->do("CREATE TABLE $tabname (\n$fielddef\n) TYPE = MYISAM") $dbh->do("CREATE TABLE $tabname (\n$fielddef\n) TYPE = MYISAM")
or die "Could not create table '$tabname'. Please check your '$db_base' access.\n"; or die "Could not create table '$tabname'. Please check your '$my_db_driver' access.\n";
} }
########################################################################### ###########################################################################
......
...@@ -83,7 +83,7 @@ $vars->{'open_status'} = \@open_status; ...@@ -83,7 +83,7 @@ $vars->{'open_status'} = \@open_status;
$vars->{'closed_status'} = \@closed_status; $vars->{'closed_status'} = \@closed_status;
# Generate a list of fields that can be queried. # Generate a list of fields that can be queried.
$vars->{'field'} = [Bugzilla::DB::GetFieldDefs()]; $vars->{'field'} = [Bugzilla->dbh->bz_get_field_defs()];
# Determine how the user would like to receive the output; # Determine how the user would like to receive the output;
# default is JavaScript. # default is JavaScript.
......
...@@ -351,7 +351,7 @@ $vars->{'bug_severity'} = \@::legal_severity; ...@@ -351,7 +351,7 @@ $vars->{'bug_severity'} = \@::legal_severity;
# Boolean charts # Boolean charts
my @fields; my @fields;
push(@fields, { name => "noop", description => "---" }); push(@fields, { name => "noop", description => "---" });
push(@fields, Bugzilla::DB::GetFieldDefs()); push(@fields, Bugzilla->dbh->bz_get_field_defs());
$vars->{'fields'} = \@fields; $vars->{'fields'} = \@fields;
# Creating new charts - if the cmd-add value is there, we define the field # Creating new charts - if the cmd-add value is there, we define the field
......
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