X-Git-Url: http://git.freeside.biz/gitweb/?p=freeside.git;a=blobdiff_plain;f=rt%2Flib%2FRT%2FHandle.pm;h=4ea1576dc5e1fbbbb9b8f16476eaa24106351e58;hp=46070cec797afe384454e0a1b50d72daf597f0a2;hb=e9e0cf0989259b94d9758eceff448666a2e5a5cc;hpb=b4b0c7e72d7eaee2fbfc7022022c9698323203dd diff --git a/rt/lib/RT/Handle.pm b/rt/lib/RT/Handle.pm index 46070cec7..4ea1576dc 100644 --- a/rt/lib/RT/Handle.pm +++ b/rt/lib/RT/Handle.pm @@ -1,40 +1,40 @@ # BEGIN BPS TAGGED BLOCK {{{ -# +# # COPYRIGHT: -# -# This software is Copyright (c) 1996-2009 Best Practical Solutions, LLC -# -# +# +# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC +# +# # (Except where explicitly superseded by other copyright notices) -# -# +# +# # LICENSE: -# +# # This work is made available to you under the terms of Version 2 of # the GNU General Public License. A copy of that license should have # been provided with this software, but in any event can be snarfed # from www.gnu.org. -# +# # This work is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. -# +# # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA # 02110-1301 or visit their web page on the internet at # http://www.gnu.org/licenses/old-licenses/gpl-2.0.html. -# -# +# +# # CONTRIBUTION SUBMISSION POLICY: -# +# # (The following paragraph is not intended to limit the rights granted # to you to modify and distribute this software under the terms of # the GNU General Public License and is only of importance to you if # you choose to contribute your changes and enhancements to the # community by submitting them to Best Practical Solutions, LLC.) -# +# # By intentionally submitting any modifications, corrections or # derivatives to this work, or any other work intended for use with # Request Tracker, to Best Practical Solutions, LLC, you confirm that @@ -43,7 +43,7 @@ # royalty-free, perpetual, license to use, copy, create derivative # works based on those contributions, and sublicense and distribute # those contributions and any derivatives thereof. -# +# # END BPS TAGGED BLOCK }}} =head1 NAME @@ -70,7 +70,8 @@ package RT::Handle; use strict; use warnings; -use vars qw/@ISA/; + +use File::Spec; =head1 METHODS @@ -82,8 +83,9 @@ L, using the C configuration. =cut sub FinalizeDatabaseType { - eval "use DBIx::SearchBuilder::Handle::". RT->Config->Get('DatabaseType') ."; - \@ISA= qw(DBIx::SearchBuilder::Handle::". RT->Config->Get('DatabaseType') .");"; + eval { + use base "DBIx::SearchBuilder::Handle::". RT->Config->Get('DatabaseType'); + }; if ($@) { die "Unable to load DBIx::SearchBuilder database handle for '". RT->Config->Get('DatabaseType') ."'.\n". @@ -101,6 +103,7 @@ Takes nothing. sub Connect { my $self = shift; + my %args = (@_); my $db_type = RT->Config->Get('DatabaseType'); if ( $db_type eq 'Oracle' ) { @@ -111,6 +114,8 @@ sub Connect { $self->SUPER::Connect( User => RT->Config->Get('DatabaseUser'), Password => RT->Config->Get('DatabasePassword'), + DisconnectHandleOnDestroy => 1, + %args, ); if ( $db_type eq 'mysql' ) { @@ -119,6 +124,15 @@ sub Connect { $self->dbh->do("SET NAMES 'utf8'") if $version >= 4.1; } + + if ( $db_type eq 'Pg' ) { + my $version = $self->DatabaseVersion; + ($version) = $version =~ /^(\d+\.\d+)/; + $self->dbh->do("SET bytea_output = 'escape'") if $version >= 9.0; + } + + + $self->dbh->{'LongReadLen'} = RT->Config->Get('MaxAttachmentSize'); } @@ -129,7 +143,6 @@ from the config. =cut -require File::Spec; sub BuildDSN { my $self = shift; @@ -149,7 +162,6 @@ sub BuildDSN { Port => $db_port, Driver => $db_type, RequireSSL => RT->Config->Get('DatabaseRequireSSL'), - DisconnectHandleOnDestroy => 1, ); if ( $db_type eq 'Oracle' && $db_host ) { $args{'SID'} = delete $args{'Database'}; @@ -201,10 +213,6 @@ sub SystemDSN { # with postgres, you want to connect to template1 database $dsn =~ s/dbname=\Q$db_name/dbname=template1/; } - elsif ( $db_type eq 'Informix' ) { - # with Informix, you want to connect sans database: - $dsn =~ s/Informix:\Q$db_name/Informix:/; - } return $dsn; } @@ -216,36 +224,29 @@ sub SystemDSN { sub CheckIntegrity { my $self = shift; - - my $dsn = $self->DSN; - my $user = RT->Config->Get('DatabaseUser'); - my $pass = RT->Config->Get('DatabasePassword'); + $self = new $self unless ref $self; - my $dbh = DBI->connect( - $dsn, $user, $pass, - { RaiseError => 0, PrintError => 0 }, - ); - unless ( $dbh ) { - return (0, 'no connection', "Failed to connect to $dsn as user '$user': ". $DBI::errstr); + unless ($RT::Handle and $RT::Handle->dbh) { + local $@; + unless ( eval { RT::ConnectToDatabase(); 1 } ) { + return (0, 'no connection', "$@"); + } } - RT::ConnectToDatabase(); - RT::InitLogging(); - require RT::CurrentUser; - my $test_user = new RT::CurrentUser; + my $test_user = RT::CurrentUser->new; $test_user->Load('RT_System'); unless ( $test_user->id ) { - return (0, 'no system user', "Couldn't find RT_System user in the DB '$dsn'"); + return (0, 'no system user', "Couldn't find RT_System user in the DB '". $self->DSN ."'"); } - $test_user = new RT::CurrentUser; + $test_user = RT::CurrentUser->new; $test_user->Load('Nobody'); unless ( $test_user->id ) { - return (0, 'no nobody user', "Couldn't find Nobody user in the DB '$dsn'"); + return (0, 'no nobody user', "Couldn't find Nobody user in the DB '". $self->DSN ."'"); } - return $dbh; + return $RT::Handle->dbh; } sub CheckCompatibility { @@ -261,17 +262,19 @@ sub CheckCompatibility { unless $version; ($version) = $version =~ /^(\d+\.\d+)/; - return (0, "RT is unsupported on MySQL versions before 4.0.x, it's $version") - if $version < 4; + return (0, "RT is unsupported on MySQL versions before 4.1. Your version is $version.") + if $version < 4.1; # MySQL must have InnoDB support - my $innodb = ($dbh->selectrow_array("show variables like 'have_innodb'"))[1]; - if ( lc $innodb eq "no" ) { + local $dbh->{FetchHashKeyName} = 'NAME_lc'; + my $innodb = lc($dbh->selectall_hashref("SHOW ENGINES", "engine")->{InnoDB}{support} || "no"); + if ( $innodb eq "no" ) { return (0, "RT requires that MySQL be compiled with InnoDB table support.\n". - "See http://dev.mysql.com/doc/mysql/en/InnoDB.html"); - } elsif ( lc $innodb eq "disabled" ) { + "See \n". + "and check that there are no 'skip-innodb' lines in your my.cnf."); + } elsif ( $innodb eq "disabled" ) { return (0, "RT requires that MySQL InnoDB table support be enabled.\n". - "Remove the 'skip-innodb' line from your my.cnf file, restart MySQL, and try again.\n"); + "Remove the 'skip-innodb' or 'innodb = OFF' line from your my.cnf file, restart MySQL, and try again.\n"); } if ( $state eq 'post' ) { @@ -279,18 +282,38 @@ sub CheckCompatibility { unless ( $create_table =~ /(?:ENGINE|TYPE)\s*=\s*InnoDB/i ) { return (0, "RT requires that all its tables be of InnoDB type. Upgrade RT tables."); } - } - if ( $version >= 4.1 && $state eq 'post' ) { - my $create_table = $dbh->selectrow_arrayref("SHOW CREATE TABLE Attachments")->[1]; + + $create_table = $dbh->selectrow_arrayref("SHOW CREATE TABLE Attachments")->[1]; unless ( $create_table =~ /\bContent\b[^,]*BLOB/i ) { return (0, "RT since version 3.8 has new schema for MySQL versions after 4.1.0\n" ."Follow instructions in the UPGRADING.mysql file."); } } + + my $max_packet = ($dbh->selectrow_array("show variables like 'max_allowed_packet'"))[1]; + if ($state =~ /^(create|post)$/ and $max_packet <= (1024 * 1024)) { + my $max_packet = sprintf("%.1fM", $max_packet/1024/1024); + warn "max_allowed_packet is set to $max_packet, which limits the maximum attachment or email size that RT can process. Consider adjusting MySQL's max_allowed_packet setting.\n"; + } } return (1) } +sub CheckSphinxSE { + my $self = shift; + + my $dbh = $RT::Handle->dbh; + local $dbh->{'RaiseError'} = 0; + local $dbh->{'PrintError'} = 0; + my $has = ($dbh->selectrow_array("show variables like 'have_sphinx'"))[1]; + $has ||= ($dbh->selectrow_array( + "select 'yes' from INFORMATION_SCHEMA.PLUGINS where PLUGIN_NAME = 'sphinx' AND PLUGIN_STATUS='active'" + ))[0]; + + return 0 unless lc($has||'') eq "yes"; + return 1; +} + =head2 Database maintanance =head3 CreateDatabase $DBH @@ -336,14 +359,10 @@ sub CreateDatabase { return (1, "Created user $db_user. All RT's objects should be in his schema."); } elsif ( $db_type eq 'Pg' ) { - # XXX: as we get external DBH we don't know if RaiseError or PrintError - # are enabled, so we have to setup it here and restore them back - $status = $dbh->do("CREATE DATABASE $db_name WITH ENCODING='UNICODE' TEMPLATE template0") - || $dbh->do("CREATE DATABASE $db_name TEMPLATE template0"); + $status = $dbh->do("CREATE DATABASE $db_name WITH ENCODING='UNICODE' TEMPLATE template0"); } - elsif ( $db_type eq 'Informix' ) { - local $ENV{'DB_LOCALE'} = 'en_us.utf8'; - $status = $dbh->do("CREATE DATABASE $db_name WITH BUFFERED LOG"); + elsif ( $db_type eq 'mysql' ) { + $status = $dbh->do("CREATE DATABASE $db_name DEFAULT CHARACTER SET utf8"); } else { $status = $dbh->do("CREATE DATABASE $db_name"); @@ -351,15 +370,15 @@ sub CreateDatabase { return ($status, $DBI::errstr); } -=head3 DropDatabase $DBH [Force => 0] +=head3 DropDatabase $DBH Drops RT's database. This method can be used as class method. Takes DBI handle as first argument. Many database systems require -special handle to allow you to create a new database, so you have -to use L method during connection. +a special handle to allow you to drop a database, so you may have +to use L when acquiring the DBI handle. -Fetches type and name of the DB from the config. +Fetches the type and name of the database from the config. =cut @@ -370,7 +389,7 @@ sub DropDatabase { my $db_type = RT->Config->Get('DatabaseType'); my $db_name = RT->Config->Get('DatabaseName'); - if ( $db_type eq 'Oracle' || $db_type eq 'Informix' ) { + if ( $db_type eq 'Oracle' ) { my $db_user = RT->Config->Get('DatabaseUser'); my $status = $dbh->do( "DROP USER $db_user CASCADE" ); unless ( $status ) { @@ -465,10 +484,10 @@ sub InsertSchema { my (@schema); - open my $fh_schema, "<$file"; + open( my $fh_schema, '<', $file ) or die $!; my $has_local = 0; - open my $fh_schema_local, "<" . $self->GetVersionFile( $dbh, $RT::LocalEtcPath . "/schema." . $db_type ) + open( my $fh_schema_local, "<" . $self->GetVersionFile( $dbh, $RT::LocalEtcPath . "/schema." . $db_type )) and $has_local = 1; my $statement = ""; @@ -547,17 +566,34 @@ sub GetVersionFile { return defined $version? $version{ $version } : undef; } +{ my %word = ( + a => -4, + alpha => -4, + b => -3, + beta => -3, + pre => -2, + rc => -1, + head => 9999, +); sub cmp_version($$) { my ($a, $b) = (@_); - $b =~ s/HEAD$/9999/; - my @a = split /[^0-9]+/, $a; - my @b = split /[^0-9]+/, $b; + my @a = grep defined, map { /^[0-9]+$/? $_ : /^[a-zA-Z]+$/? $word{$_}|| -10 : undef } + split /([^0-9]+)/, $a; + my @b = grep defined, map { /^[0-9]+$/? $_ : /^[a-zA-Z]+$/? $word{$_}|| -10 : undef } + split /([^0-9]+)/, $b; + @a > @b + ? push @b, (0) x (@a-@b) + : push @a, (0) x (@b-@a); for ( my $i = 0; $i < @a; $i++ ) { - return 1 unless defined $b[$i]; return $a[$i] <=> $b[$i] if $a[$i] <=> $b[$i]; } - return 0 if @a == @b; - return -1; + return 0; +} + +sub version_words { + return keys %word; +} + } @@ -583,13 +619,13 @@ sub InsertInitialData { { require RT::CurrentUser; - my $test_user = RT::User->new( new RT::CurrentUser ); + my $test_user = RT::User->new( RT::CurrentUser->new() ); $test_user->Load('RT_System'); if ( $test_user->id ) { push @warns, "Found system user in the DB."; } else { - my $user = RT::User->new( new RT::CurrentUser ); + my $user = RT::User->new( RT::CurrentUser->new() ); my ( $val, $msg ) = $user->_BootstrapCreate( Name => 'RT_System', RealName => 'The RT System itself', @@ -605,15 +641,15 @@ sub InsertInitialData { # init RT::SystemUser and RT::System objects RT::InitSystemObjects(); - unless ( $RT::SystemUser->id ) { + unless ( RT->SystemUser->id ) { return (0, "Couldn't load system user"); } # grant SuperUser right to system user { - my $test_ace = RT::ACE->new( $RT::SystemUser ); + my $test_ace = RT::ACE->new( RT->SystemUser ); $test_ace->LoadByCols( - PrincipalId => ACLEquivGroupId( $RT::SystemUser->Id ), + PrincipalId => ACLEquivGroupId( RT->SystemUser->Id ), PrincipalType => 'Group', RightName => 'SuperUser', ObjectType => 'RT::System', @@ -622,9 +658,9 @@ sub InsertInitialData { if ( $test_ace->id ) { push @warns, "System user has global SuperUser right."; } else { - my $ace = RT::ACE->new( $RT::SystemUser ); + my $ace = RT::ACE->new( RT->SystemUser ); my ( $val, $msg ) = $ace->_BootstrapCreate( - PrincipalId => ACLEquivGroupId( $RT::SystemUser->Id ), + PrincipalId => ACLEquivGroupId( RT->SystemUser->Id ), PrincipalType => 'Group', RightName => 'SuperUser', ObjectType => 'RT::System', @@ -640,14 +676,14 @@ sub InsertInitialData { # $self->loc('Privileged'); # For the string extractor to get a string to localize # $self->loc('Unprivileged'); # For the string extractor to get a string to localize foreach my $name (qw(Everyone Privileged Unprivileged)) { - my $group = RT::Group->new( $RT::SystemUser ); + my $group = RT::Group->new( RT->SystemUser ); $group->LoadSystemInternalGroup( $name ); if ( $group->id ) { push @warns, "System group '$name' already exists."; next; } - $group = RT::Group->new( $RT::SystemUser ); + $group = RT::Group->new( RT->SystemUser ); my ( $val, $msg ) = $group->_Create( Type => $name, Domain => 'SystemInternal', @@ -660,7 +696,7 @@ sub InsertInitialData { # nobody { - my $user = RT::User->new( $RT::SystemUser ); + my $user = RT::User->new( RT->SystemUser ); $user->Load('Nobody'); if ( $user->id ) { push @warns, "Found 'Nobody' user in the DB."; @@ -692,14 +728,14 @@ sub InsertInitialData { # system role groups foreach my $name (qw(Owner Requestor Cc AdminCc)) { - my $group = RT::Group->new( $RT::SystemUser ); + my $group = RT::Group->new( RT->SystemUser ); $group->LoadSystemRoleGroup( $name ); if ( $group->id ) { push @warns, "System role '$name' already exists."; next; } - $group = RT::Group->new( $RT::SystemUser ); + $group = RT::Group->new( RT->SystemUser ); my ( $val, $msg ) = $group->_Create( Type => $name, Domain => 'RT::System-Role', @@ -725,6 +761,11 @@ Load some sort of data into the database, takes path to a file. sub InsertData { my $self = shift; my $datafile = shift; + my $root_password = shift; + my %args = ( + disconnect_after => 1, + @_ + ); # Slurp in stuff to insert from the datafile. Possible things to go in here:- our (@Groups, @Users, @ACL, @Queues, @ScripActions, @ScripConditions, @@ -748,7 +789,7 @@ sub InsertData { if ( @Groups ) { $RT::Logger->debug("Creating groups..."); foreach my $item (@Groups) { - my $new_entry = RT::Group->new( $RT::SystemUser ); + my $new_entry = RT::Group->new( RT->SystemUser ); my $member_of = delete $item->{'MemberOf'}; my ( $return, $msg ) = $new_entry->_Create(%$item); unless ( $return ) { @@ -760,7 +801,7 @@ sub InsertData { if ( $member_of ) { $member_of = [ $member_of ] unless ref $member_of eq 'ARRAY'; foreach( @$member_of ) { - my $parent = RT::Group->new($RT::SystemUser); + my $parent = RT::Group->new(RT->SystemUser); if ( ref $_ eq 'HASH' ) { $parent->LoadByCols( %$_ ); } @@ -794,7 +835,10 @@ sub InsertData { if ( @Users ) { $RT::Logger->debug("Creating users..."); foreach my $item (@Users) { - my $new_entry = new RT::User( $RT::SystemUser ); + if ( $item->{'Name'} eq 'root' && $root_password ) { + $item->{'Password'} = $root_password; + } + my $new_entry = RT::User->new( RT->SystemUser ); my ( $return, $msg ) = $new_entry->Create(%$item); unless ( $return ) { $RT::Logger->error( $msg ); @@ -807,7 +851,7 @@ sub InsertData { if ( @Queues ) { $RT::Logger->debug("Creating queues..."); for my $item (@Queues) { - my $new_entry = new RT::Queue($RT::SystemUser); + my $new_entry = RT::Queue->new(RT->SystemUser); my ( $return, $msg ) = $new_entry->Create(%$item); unless ( $return ) { $RT::Logger->error( $msg ); @@ -820,16 +864,36 @@ sub InsertData { if ( @CustomFields ) { $RT::Logger->debug("Creating custom fields..."); for my $item ( @CustomFields ) { - my $new_entry = new RT::CustomField( $RT::SystemUser ); + my $new_entry = RT::CustomField->new( RT->SystemUser ); my $values = delete $item->{'Values'}; my @queues; # if ref then it's list of queues, so we do things ourself if ( exists $item->{'Queue'} && ref $item->{'Queue'} ) { - $item->{'LookupType'} = 'RT::Queue-RT::Ticket'; + $item->{'LookupType'} ||= 'RT::Queue-RT::Ticket'; @queues = @{ delete $item->{'Queue'} }; } + if ( $item->{'BasedOn'} ) { + if ( $item->{'BasedOn'} =~ /^\d+$/) { + # Already have an ID -- should be fine + } elsif ( $item->{'LookupType'} ) { + my $basedon = RT::CustomField->new($RT::SystemUser); + my ($ok, $msg ) = $basedon->LoadByCols( Name => $item->{'BasedOn'}, + LookupType => $item->{'LookupType'} ); + if ($ok) { + $item->{'BasedOn'} = $basedon->Id; + } else { + $RT::Logger->error("Unable to load $item->{BasedOn} as a $item->{LookupType} CF. Skipping BasedOn: $msg"); + delete $item->{'BasedOn'}; + } + } else { + $RT::Logger->error("Unable to load CF $item->{BasedOn} because no LookupType was specified. Skipping BasedOn"); + delete $item->{'BasedOn'}; + } + + } + my ( $return, $msg ) = $new_entry->Create(%$item); unless( $return ) { $RT::Logger->error( $msg ); @@ -843,18 +907,18 @@ sub InsertData { # apply by default if ( !@queues && !exists $item->{'Queue'} && $item->{LookupType} ) { - my $ocf = RT::ObjectCustomField->new($RT::SystemUser); + my $ocf = RT::ObjectCustomField->new(RT->SystemUser); $ocf->Create( CustomField => $new_entry->Id ); } for my $q (@queues) { - my $q_obj = RT::Queue->new($RT::SystemUser); + my $q_obj = RT::Queue->new(RT->SystemUser); $q_obj->Load($q); unless ( $q_obj->Id ) { $RT::Logger->error("Could not find queue ". $q ); next; } - my $OCF = RT::ObjectCustomField->new($RT::SystemUser); + my $OCF = RT::ObjectCustomField->new(RT->SystemUser); ( $return, $msg ) = $OCF->Create( CustomField => $new_entry->Id, ObjectId => $q_obj->Id, @@ -873,12 +937,12 @@ sub InsertData { # Global rights or Queue rights? if ( $item->{'CF'} ) { - $object = RT::CustomField->new( $RT::SystemUser ); + $object = RT::CustomField->new( RT->SystemUser ); my @columns = ( Name => $item->{'CF'} ); push @columns, Queue => $item->{'Queue'} if $item->{'Queue'} and not ref $item->{'Queue'}; $object->LoadByName( @columns ); } elsif ( $item->{'Queue'} ) { - $object = RT::Queue->new($RT::SystemUser); + $object = RT::Queue->new(RT->SystemUser); $object->Load( $item->{'Queue'} ); } else { $object = $RT::System; @@ -888,7 +952,7 @@ sub InsertData { # Group rights or user rights? if ( $item->{'GroupDomain'} ) { - $princ = RT::Group->new($RT::SystemUser); + $princ = RT::Group->new(RT->SystemUser); if ( $item->{'GroupDomain'} eq 'UserDefined' ) { $princ->LoadUserDefinedGroup( $item->{'GroupId'} ); } elsif ( $item->{'GroupDomain'} eq 'SystemInternal' ) { @@ -903,9 +967,17 @@ sub InsertData { } else { $princ->Load( $item->{'GroupId'} ); } + unless ( $princ->Id ) { + RT->Logger->error("Unable to load Group: GroupDomain => $item->{GroupDomain}, GroupId => $item->{GroupId}, Queue => $item->{Queue}"); + next; + } } else { - $princ = RT::User->new($RT::SystemUser); - $princ->Load( $item->{'UserId'} ); + $princ = RT::User->new(RT->SystemUser); + my ($ok, $msg) = $princ->Load( $item->{'UserId'} ); + unless ( $ok ) { + RT->Logger->error("Unable to load user: $item->{UserId} : $msg"); + next; + } } # Grant it @@ -927,7 +999,7 @@ sub InsertData { $RT::Logger->debug("Creating ScripActions..."); for my $item (@ScripActions) { - my $new_entry = RT::ScripAction->new($RT::SystemUser); + my $new_entry = RT::ScripAction->new(RT->SystemUser); my ( $return, $msg ) = $new_entry->Create(%$item); unless ( $return ) { $RT::Logger->error( $msg ); @@ -944,7 +1016,7 @@ sub InsertData { $RT::Logger->debug("Creating ScripConditions..."); for my $item (@ScripConditions) { - my $new_entry = RT::ScripCondition->new($RT::SystemUser); + my $new_entry = RT::ScripCondition->new(RT->SystemUser); my ( $return, $msg ) = $new_entry->Create(%$item); unless ( $return ) { $RT::Logger->error( $msg ); @@ -961,7 +1033,7 @@ sub InsertData { $RT::Logger->debug("Creating templates..."); for my $item (@Templates) { - my $new_entry = new RT::Template($RT::SystemUser); + my $new_entry = RT::Template->new(RT->SystemUser); my ( $return, $msg ) = $new_entry->Create(%$item); unless ( $return ) { $RT::Logger->error( $msg ); @@ -976,7 +1048,7 @@ sub InsertData { $RT::Logger->debug("Creating scrips..."); for my $item (@Scrips) { - my $new_entry = new RT::Scrip($RT::SystemUser); + my $new_entry = RT::Scrip->new(RT->SystemUser); my @queues = ref $item->{'Queue'} eq 'ARRAY'? @{ $item->{'Queue'} }: $item->{'Queue'} || 0; push @queues, 0 unless @queues; # add global queue at least @@ -994,8 +1066,8 @@ sub InsertData { $RT::Logger->debug("done."); } if ( @Attributes ) { - $RT::Logger->debug("Creating predefined searches..."); - my $sys = RT::System->new($RT::SystemUser); + $RT::Logger->debug("Creating attributes..."); + my $sys = RT::System->new(RT->SystemUser); for my $item (@Attributes) { my $obj = delete $item->{Object}; # XXX: make this something loadable @@ -1021,8 +1093,14 @@ sub InsertData { $RT::Logger->debug("done."); } - my $db_type = RT->Config->Get('DatabaseType'); - $RT::Handle->Disconnect() unless $db_type eq 'SQLite'; + # XXX: This disconnect doesn't really belong here; it's a relict from when + # this method was extracted from rt-setup-database. However, too much + # depends on it to change without significant testing. At the very least, + # we can provide a way to skip the side-effect. + if ( $args{disconnect_after} ) { + my $db_type = RT->Config->Get('DatabaseType'); + $RT::Handle->Disconnect() unless $db_type eq 'SQLite'; + } $RT::Logger->debug("Done setting up database content."); @@ -1041,10 +1119,10 @@ Given a userid, return that user's acl equivalence group sub ACLEquivGroupId { my $id = shift; - my $cu = $RT::SystemUser; + my $cu = RT->SystemUser; unless ( $cu ) { require RT::CurrentUser; - $cu = new RT::CurrentUser; + $cu = RT::CurrentUser->new; $cu->LoadByName('RT_System'); warn "Couldn't load RT_System user" unless $cu->id; } @@ -1054,11 +1132,110 @@ sub ACLEquivGroupId { return $equiv_group->Id; } +=head2 QueryHistory + +Returns the SQL query history associated with this handle. The top level array +represents a lists of request. Each request is a hash with metadata about the +request (such as the URL) and a list of queries. You'll probably not be using this. + +=cut + +sub QueryHistory { + my $self = shift; + + return $self->{QueryHistory}; +} + +=head2 AddRequestToHistory + +Adds a web request to the query history. It must be a hash with keys Path (a +string) and Queries (an array reference of arrays, where elements are time, +sql, bind parameters, and duration). + +=cut + +sub AddRequestToHistory { + my $self = shift; + my $request = shift; + + push @{ $self->{QueryHistory} }, $request; +} + +=head2 Quote + +Returns the parameter quoted by DBI. B +Use bind parameters (C) instead. This is used only outside the scope of interacting +with the database. + +=cut + +sub Quote { + my $self = shift; + my $value = shift; + + return $self->dbh->quote($value); +} + +=head2 FillIn + +Takes a SQL query and an array reference of bind parameters and fills in the +query's C parameters. + +=cut + +sub FillIn { + my $self = shift; + my $sql = shift; + my $bind = shift; + + my $b = 0; + + # is this regex sufficient? + $sql =~ s{\?}{$self->Quote($bind->[$b++])}eg; + + return $sql; +} + +# log a mason stack trace instead of a Carp::longmess because it's less painful +# and uses mason component paths properly +sub _LogSQLStatement { + my $self = shift; + my $statement = shift; + my $duration = shift; + my @bind = @_; + + require HTML::Mason::Exceptions; + push @{$self->{'StatementLog'}} , ([Time::HiRes::time(), $statement, [@bind], $duration, HTML::Mason::Exception->new->as_string]); +} + + +sub _TableNames { + my $self = shift; + my $dbh = shift || $self->dbh; + + { + local $@; + if ( + $dbh->{Driver}->{Name} eq 'Pg' + && $dbh->{'pg_server_version'} >= 90200 + && !eval { DBD::Pg->VERSION('2.19.3'); 1 } + ) { + die "You're using PostgreSQL 9.2 or newer. You have to upgrade DBD::Pg module to 2.19.3 or newer: $@"; + } + } + + my @res; + + my $sth = $dbh->table_info( '', undef, undef, "'TABLE'"); + while ( my $table = $sth->fetchrow_hashref ) { + push @res, $table->{TABLE_NAME} || $table->{table_name}; + } + + return @res; +} + __PACKAGE__->FinalizeDatabaseType; -eval "require RT::Handle_Vendor"; -die $@ if ($@ && $@ !~ qr{^Can't locate RT/Handle_Vendor.pm}); -eval "require RT::Handle_Local"; -die $@ if ($@ && $@ !~ qr{^Can't locate RT/Handle_Local.pm}); +RT::Base->_ImportOverlays(); 1;