Merge branch 'master' of https://github.com/jgoodman/Freeside
[freeside.git] / rt / lib / RT / Handle.pm
index 38905de..4ea1576 100644 (file)
@@ -2,7 +2,7 @@
 #
 # COPYRIGHT:
 #
-# This software is Copyright (c) 1996-2011 Best Practical Solutions, LLC
+# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
 #                                          <sales@bestpractical.com>
 #
 # (Except where explicitly superseded by other copyright notices)
@@ -70,7 +70,8 @@ package RT::Handle;
 
 use strict;
 use warnings;
-use vars qw/@ISA/;
+
+use File::Spec;
 
 =head1 METHODS
 
@@ -102,6 +103,7 @@ Takes nothing.
 
 sub Connect {
     my $self = shift;
+    my %args = (@_);
 
     my $db_type = RT->Config->Get('DatabaseType');
     if ( $db_type eq 'Oracle' ) {
@@ -112,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' ) {
@@ -139,7 +143,6 @@ from the config.
 
 =cut
 
-require File::Spec;
 
 sub BuildDSN {
     my $self = shift;
@@ -159,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'};
@@ -211,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;
 }
 
@@ -226,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 {
@@ -271,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 <http://dev.mysql.com/doc/mysql/en/innodb-storage-engine.html>\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' ) {
@@ -289,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
@@ -346,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");
@@ -361,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<SystemDSN> method during connection.
+a special handle to allow you to drop a database, so you may have
+to use L<SystemDSN> 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
 
@@ -380,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 ) {
@@ -478,7 +487,7 @@ sub InsertSchema {
     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 = "";
@@ -557,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;
+}
+
 }
 
 
@@ -593,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',
@@ -615,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',
@@ -632,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',
@@ -650,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',
@@ -670,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.";
@@ -702,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',
@@ -735,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,
@@ -758,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 ) {
@@ -770,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( %$_ );
                     }
@@ -804,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 );
@@ -817,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 );
@@ -830,7 +864,7 @@ 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;
@@ -840,26 +874,30 @@ sub InsertData {
                 @queues = @{ delete $item->{'Queue'} };
             }
 
-            my ( $return, $msg ) = $new_entry->Create(%$item);
-            unless( $return ) {
-                $RT::Logger->error( $msg );
-                next;
-            }
-
             if ( $item->{'BasedOn'} ) {
-                my $basedon = RT::CustomField->new($RT::SystemUser);
-                my ($ok, $msg ) = $basedon->LoadByCols( Name => $item->{'BasedOn'},
-                                                        LookupType => $new_entry->LookupType );
-                if ($ok) {
-                    ($ok, $msg) = $new_entry->SetBasedOn( $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) {
-                        $RT::Logger->debug("Added BasedOn $item->{BasedOn}: $msg");
+                        $item->{'BasedOn'} = $basedon->Id;
                     } else {
-                        $RT::Logger->error("Failed to add basedOn $item->{BasedOn}: $msg");
+                        $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 $item->{BasedOn} as a $item->{LookupType} CF.  Skipping BasedOn");
+                    $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 );
+                next;
             }
 
             foreach my $value ( @{$values} ) {
@@ -869,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,
@@ -899,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;
@@ -914,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' ) {
@@ -929,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
@@ -953,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 );
@@ -970,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 );
@@ -987,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 );
@@ -1002,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
@@ -1020,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
@@ -1047,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.");
 
@@ -1067,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;
     }
@@ -1080,6 +1132,108 @@ 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<You almost certainly do not need this.>
+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;
 
 RT::Base->_ImportOverlays();