Bug 237862: New database layer for cross-database compatibility
Patch By Tomas Kopal <Tomas.Kopal@altap.cz> r=mkanat, a=myk git-svn-id: svn://10.0.0.236/trunk@169270 18797224-902f-48f8-a5cc-f745e15eee43
This commit is contained in:
@@ -71,7 +71,7 @@ our $webdotdir = "$datadir/webdot";
|
||||
%Bugzilla::Config::EXPORT_TAGS =
|
||||
(
|
||||
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)],
|
||||
);
|
||||
Exporter::export_ok_tags('admin', 'db', 'locations');
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
# Jacob Steenhagen <jake@bugzilla.org>
|
||||
# Bradley Baetz <bbaetz@student.usyd.edu.au>
|
||||
# Christopher Aillon <christopher@aillon.com>
|
||||
# Tomas Kopal <Tomas.Kopal@altap.cz>
|
||||
|
||||
package Bugzilla::DB;
|
||||
|
||||
@@ -29,7 +30,11 @@ use strict;
|
||||
|
||||
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 =
|
||||
(
|
||||
@@ -42,6 +47,7 @@ Exporter::export_ok_tags('deprecated');
|
||||
|
||||
use Bugzilla::Config qw(:DEFAULT :db);
|
||||
use Bugzilla::Util;
|
||||
use Bugzilla::Error;
|
||||
|
||||
# All this code is backwards compat fu. As such, its a bit ugly. Note the
|
||||
# circular dependencies on Bugzilla.pm
|
||||
@@ -122,43 +128,29 @@ sub PopGlobalSQLState() {
|
||||
sub connect_shadow {
|
||||
die "Tried to connect to non-existent shadowdb" unless Param('shadowdb');
|
||||
|
||||
my $dsn = "DBI:mysql:host=" . Param("shadowdbhost") .
|
||||
";database=" . Param('shadowdb') . ";port=" . Param("shadowdbport");
|
||||
|
||||
$dsn .= ";mysql_socket=" . Param("shadowdbsock") if Param('shadowdbsock');
|
||||
|
||||
return _connect($dsn);
|
||||
return _connect($db_driver, Param("shadowdbhost"),
|
||||
Param('shadowdb'), Param("shadowdbport"),
|
||||
Param("shadowdbsock"), $db_user, $db_pass);
|
||||
}
|
||||
|
||||
sub connect_main {
|
||||
my $dsn = "DBI:mysql:host=$db_host;database=$db_name;port=$db_port";
|
||||
|
||||
$dsn .= ";mysql_socket=$db_sock" if $db_sock;
|
||||
|
||||
return _connect($dsn);
|
||||
return _connect($db_driver, $db_host, $db_name, $db_port,
|
||||
$db_sock, $db_user, $db_pass);
|
||||
}
|
||||
|
||||
sub _connect {
|
||||
my ($dsn) = @_;
|
||||
my ($driver, $host, $dbname, $port, $sock, $user, $pass) = @_;
|
||||
|
||||
# connect using our known info to the specified db
|
||||
# Apache::DBI will cache this when using mod_perl
|
||||
my $dbh = DBI->connect($dsn,
|
||||
'',
|
||||
'',
|
||||
{ RaiseError => 1,
|
||||
PrintError => 0,
|
||||
Username => $db_user,
|
||||
Password => $db_pass,
|
||||
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?)
|
||||
});
|
||||
# DB specific module have the same name as DB driver, here we
|
||||
# just make sure we are not case sensitive
|
||||
(my $db_module = $driver) =~ s/(\w+)/\u\L$1/g;
|
||||
my $pkg_module = "Bugzilla::DB::" . $db_module;
|
||||
|
||||
# do the actual import
|
||||
eval ("require $pkg_module") || die ($@);
|
||||
|
||||
# instantiate the correct DB specific module
|
||||
my $dbh = $pkg_module->new($user, $pass, $host, $dbname, $port, $sock);
|
||||
|
||||
return $dbh;
|
||||
}
|
||||
@@ -173,6 +165,39 @@ sub _handle_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;
|
||||
sub server_version {
|
||||
return $cached_server_version if defined($cached_server_version);
|
||||
@@ -183,8 +208,8 @@ sub server_version {
|
||||
return $cached_server_version;
|
||||
}
|
||||
|
||||
sub GetFieldDefs {
|
||||
my $dbh = Bugzilla->dbh;
|
||||
sub bz_get_field_defs {
|
||||
my ($self) = @_;
|
||||
|
||||
my $extra = "";
|
||||
if (!&::UserInGroup(Param('timetrackinggroup'))) {
|
||||
@@ -193,9 +218,9 @@ sub GetFieldDefs {
|
||||
}
|
||||
|
||||
my @fields;
|
||||
my $sth = $dbh->prepare("SELECT name, description
|
||||
FROM fielddefs $extra
|
||||
ORDER BY sortkey");
|
||||
my $sth = $self->prepare("SELECT name, description
|
||||
FROM fielddefs $extra
|
||||
ORDER BY sortkey");
|
||||
$sth->execute();
|
||||
while (my $field_ref = $sth->fetchrow_hashref()) {
|
||||
push(@fields, $field_ref);
|
||||
@@ -203,6 +228,82 @@ sub GetFieldDefs {
|
||||
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;
|
||||
|
||||
__END__
|
||||
@@ -213,60 +314,249 @@ Bugzilla::DB - Database access routines, using L<DBI>
|
||||
|
||||
=head1 SYNOPSIS
|
||||
|
||||
# Connection
|
||||
my $dbh = Bugzilla::DB->connect_main;
|
||||
my $shadow = Bugzilla::DB->connect_shadow;
|
||||
# Obtain db handle
|
||||
use Bugzilla::DB;
|
||||
my $dbh = Bugzilla->dbh;
|
||||
|
||||
# prepare a query using DB methods
|
||||
my $sth = $dbh->prepare("SELECT " .
|
||||
$dbh->sql_date_format("creation_ts", "%Y%m%d") .
|
||||
" FROM bugs WHERE bug_status != 'RESOLVED' " .
|
||||
$dbh->sql_limit(1));
|
||||
|
||||
# Execute the query
|
||||
$sth->execute;
|
||||
|
||||
# Get the results
|
||||
my @result = $sth->fetchrow_array;
|
||||
|
||||
# Schema Information
|
||||
my @fields = Bugzilla::DB::GetFieldDefs();
|
||||
|
||||
# Deprecated
|
||||
SendSQL("SELECT COUNT(*) FROM bugs");
|
||||
my $cnt = FetchOneColumn();
|
||||
my @fields = $dbh->bz_get_field_defs();
|
||||
|
||||
=head1 DESCRIPTION
|
||||
|
||||
This allows creation of a database handle to connect to the Bugzilla database.
|
||||
This should never be done directly; all users should use the L<Bugzilla> module
|
||||
to access the current C<dbh> instead.
|
||||
Functions in this module allows creation of a database handle to connect
|
||||
to the Bugzilla database. This should never be done directly; all users
|
||||
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
|
||||
importing the C<:deprecated> tag. These routines should not be used in new
|
||||
code.
|
||||
|
||||
The only functions that should be used by modern, regular Bugzilla code
|
||||
are the "Schema Information" functions.
|
||||
|
||||
=head1 CONNECTION
|
||||
|
||||
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
|
||||
should not be called from anywhere else.
|
||||
|
||||
=head2 Functions
|
||||
|
||||
=over 4
|
||||
|
||||
=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>
|
||||
|
||||
Connects to the shadow database, returning a new dbh. This routine C<die>s if
|
||||
no shadow database is configured.
|
||||
Description: Function to connect to the shadow database, returning a new
|
||||
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
|
||||
|
||||
=head1 SCHEMA INFORMATION
|
||||
=head2 Methods
|
||||
|
||||
Bugzilla::DB also contains routines to get schema information about the
|
||||
database.
|
||||
Note: Methods which can be implemented generically for all DBs are implemented in
|
||||
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
|
||||
|
||||
=item C<GetFieldDefs>
|
||||
=item C<new>
|
||||
|
||||
Returns a list of all the "bug" fields in Bugzilla. The list contains
|
||||
hashes, with a 'name' key and a 'description' key.
|
||||
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.
|
||||
|
||||
=back
|
||||
|
||||
=head1 DEPRECATED ROUTINES
|
||||
|
||||
|
||||
162
mozilla/webtools/bugzilla/Bugzilla/DB/Mysql.pm
Normal file
162
mozilla/webtools/bugzilla/Bugzilla/DB/Mysql.pm
Normal file
@@ -0,0 +1,162 @@
|
||||
# -*- 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;
|
||||
180
mozilla/webtools/bugzilla/Bugzilla/DB/Pg.pm
Normal file
180
mozilla/webtools/bugzilla/Bugzilla/DB/Pg.pm
Normal file
@@ -0,0 +1,180 @@
|
||||
# -*- 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 @@
|
||||
# The format of that file is....
|
||||
#
|
||||
# $answer{'db_host'} = '$db_host = "localhost";
|
||||
# $db_driver = "mydbdriver";
|
||||
# $db_port = 3306;
|
||||
# $db_name = "mydbname";
|
||||
# $db_user = "mydbuser";';
|
||||
@@ -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', '
|
||||
#
|
||||
# How to access the SQL database:
|
||||
@@ -793,6 +802,7 @@ if ($newstuff ne "") {
|
||||
# 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.
|
||||
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_port = ${*{$main::{'db_port'}}{SCALAR}};
|
||||
my $my_db_name = ${*{$main::{'db_name'}}{SCALAR}};
|
||||
@@ -1505,11 +1515,6 @@ $::ENV{'PATH'} = $origPath;
|
||||
# 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
|
||||
# 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
|
||||
@@ -1522,15 +1527,15 @@ if ($my_db_check) {
|
||||
my $sql_want = "3.23.41"; # minimum version of MySQL
|
||||
|
||||
# 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
|
||||
# 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 "") {
|
||||
$dsn .= ";mysql_socket=$my_db_sock";
|
||||
}
|
||||
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 " .
|
||||
"and password selected in\nlocalconfig?\n\n";
|
||||
printf("Checking for %15s %-9s ", "MySQL Server", "(v$sql_want)") unless $silent;
|
||||
@@ -1581,14 +1586,14 @@ EOF
|
||||
}
|
||||
|
||||
# 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 "") {
|
||||
$connectstring .= ";mysql_socket=$my_db_sock";
|
||||
}
|
||||
|
||||
my $dbh = DBI->connect($connectstring, $my_db_user, $my_db_pass)
|
||||
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 }
|
||||
|
||||
@@ -2191,7 +2196,7 @@ while (my ($tabname, $fielddef) = each %table) {
|
||||
$fielddef =~ s/\$my_platforms/$my_platforms/;
|
||||
|
||||
$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;
|
||||
$vars->{'closed_status'} = \@closed_status;
|
||||
|
||||
# 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;
|
||||
# default is JavaScript.
|
||||
|
||||
@@ -351,7 +351,7 @@ $vars->{'bug_severity'} = \@::legal_severity;
|
||||
# Boolean charts
|
||||
my @fields;
|
||||
push(@fields, { name => "noop", description => "---" });
|
||||
push(@fields, Bugzilla::DB::GetFieldDefs());
|
||||
push(@fields, Bugzilla->dbh->bz_get_field_defs());
|
||||
$vars->{'fields'} = \@fields;
|
||||
|
||||
# Creating new charts - if the cmd-add value is there, we define the field
|
||||
|
||||
Reference in New Issue
Block a user