[Rt-commit] rt branch, 3.9-fts, created. rt-3.9.4-30-gdceeb23
Ruslan Zakirov
ruz at bestpractical.com
Thu Sep 30 22:17:48 EDT 2010
The branch, 3.9-fts has been created
at dceeb2377ab6721e11e09f17cf558e5811b20a31 (commit)
- Log -----------------------------------------------------------------
commit 240d3047398fd4d930f654ce6a54fb384eeef6ee
Author: Ruslan Zakirov <ruz at bestpractical.com>
Date: Thu Sep 30 04:33:48 2010 +0400
JoinTransactions method
based on fd0794e856331bd2f2d5f71534751d6e08e2057c
diff --git a/lib/RT/SearchBuilder.pm b/lib/RT/SearchBuilder.pm
index 21102de..07cbeda 100755
--- a/lib/RT/SearchBuilder.pm
+++ b/lib/RT/SearchBuilder.pm
@@ -85,6 +85,37 @@ sub _Init {
$self->SUPER::_Init( 'Handle' => $RT::Handle);
}
+sub CleanSlate {
+ my $self = shift;
+ $self->{'_sql_aliases'} = {};
+ return $self->SUPER::CleanSlate(@_);
+}
+
+sub JoinTransactions {
+ my $self = shift;
+ my %args = ( New => 0, @_ );
+
+ return $self->{'_sql_aliases'}{'transactions'}
+ if !$args{'New'} && $self->{'_sql_aliases'}{'transactions'};
+
+ my $alias = $self->Join(
+ ALIAS1 => 'main',
+ FIELD1 => 'id',
+ TABLE2 => 'Transactions',
+ FIELD2 => 'ObjectId',
+ );
+ $self->Limit(
+ LEFTJOIN => $alias,
+ ALIAS => $alias,
+ FIELD => 'ObjectType',
+ VALUE => ref $self->NewItem,
+ );
+ $self->{'_sql_aliases'}{'transactions'} = $alias
+ unless $args{'New'};
+
+ return $alias;
+}
+
=head2 LimitToEnabled
Only find items that haven't been disabled
commit e10464d8705f8db3c6412a31152db498532ec793
Author: Ruslan Zakirov <ruz at bestpractical.com>
Date: Thu Sep 30 04:39:33 2010 +0400
use JoinTransactions
diff --git a/lib/RT/Tickets_Overlay.pm b/lib/RT/Tickets_Overlay.pm
index 113fc57..ec098f2 100755
--- a/lib/RT/Tickets_Overlay.pm
+++ b/lib/RT/Tickets_Overlay.pm
@@ -245,7 +245,6 @@ sub CleanSlate {
_sql_group_members_aliases
_sql_object_cfv_alias
_sql_role_group_aliases
- _sql_transalias
_sql_trattachalias
_sql_u_watchers_alias_for_sort
_sql_u_watchers_aliases
@@ -622,20 +621,7 @@ sub _TransDateLimit {
# See the comments for TransLimit, they apply here too
- unless ( $sb->{_sql_transalias} ) {
- $sb->{_sql_transalias} = $sb->Join(
- ALIAS1 => 'main',
- FIELD1 => 'id',
- TABLE2 => 'Transactions',
- FIELD2 => 'ObjectId',
- );
- $sb->SUPER::Limit(
- ALIAS => $sb->{_sql_transalias},
- FIELD => 'ObjectType',
- VALUE => 'RT::Ticket',
- ENTRYAGGREGATOR => 'AND',
- );
- }
+ my $txn_alias = $sb->JoinTransactions;
my $date = RT::Date->new( $sb->CurrentUser );
$date->Set( Format => 'unknown', Value => $value );
@@ -653,7 +639,7 @@ sub _TransDateLimit {
my $dayend = $date->ISO;
$sb->_SQLLimit(
- ALIAS => $sb->{_sql_transalias},
+ ALIAS => $txn_alias,
FIELD => 'Created',
OPERATOR => ">=",
VALUE => $daystart,
@@ -661,7 +647,7 @@ sub _TransDateLimit {
@rest
);
$sb->_SQLLimit(
- ALIAS => $sb->{_sql_transalias},
+ ALIAS => $txn_alias,
FIELD => 'Created',
OPERATOR => "<=",
VALUE => $dayend,
@@ -677,7 +663,7 @@ sub _TransDateLimit {
#Search for the right field
$sb->_SQLLimit(
- ALIAS => $sb->{_sql_transalias},
+ ALIAS => $txn_alias,
FIELD => 'Created',
OPERATOR => $op,
VALUE => $date->ISO,
@@ -734,24 +720,11 @@ sub _TransLimit {
my ( $self, $field, $op, $value, %rest ) = @_;
- unless ( $self->{_sql_transalias} ) {
- $self->{_sql_transalias} = $self->Join(
- ALIAS1 => 'main',
- FIELD1 => 'id',
- TABLE2 => 'Transactions',
- FIELD2 => 'ObjectId',
- );
- $self->SUPER::Limit(
- ALIAS => $self->{_sql_transalias},
- FIELD => 'ObjectType',
- VALUE => 'RT::Ticket',
- ENTRYAGGREGATOR => 'AND',
- );
- }
+ my $txn_alias = $self->JoinTransactions;
unless ( defined $self->{_sql_trattachalias} ) {
$self->{_sql_trattachalias} = $self->_SQLJoin(
TYPE => 'LEFT', # not all txns have an attachment
- ALIAS1 => $self->{_sql_transalias},
+ ALIAS1 => $txn_alias,
FIELD1 => 'id',
TABLE2 => 'Attachments',
FIELD2 => 'TransactionId',
commit 377f37fdd9eb479d105592634741b0f82372fab1
Author: Ruslan Zakirov <ruz at bestpractical.com>
Date: Thu Sep 30 05:00:38 2010 +0400
factor _TransContentLimit method out of _TransLimit
diff --git a/lib/RT/Tickets_Overlay.pm b/lib/RT/Tickets_Overlay.pm
index ec098f2..0eec0ba 100755
--- a/lib/RT/Tickets_Overlay.pm
+++ b/lib/RT/Tickets_Overlay.pm
@@ -123,7 +123,7 @@ our %FIELD_METADATA = (
LastUpdated => [ 'DATE' => 'LastUpdated', ], #loc_left_pair
Created => [ 'DATE' => 'Created', ], #loc_left_pair
Subject => [ 'STRING', ], #loc_left_pair
- Content => [ 'TRANSFIELD', ], #loc_left_pair
+ Content => [ 'TRANSCONTENT', ], #loc_left_pair
ContentType => [ 'TRANSFIELD', ], #loc_left_pair
Filename => [ 'TRANSFIELD', ], #loc_left_pair
TransactionDate => [ 'TRANSDATE', ], #loc_left_pair
@@ -156,6 +156,7 @@ our %dispatch = (
DATE => \&_DateLimit,
STRING => \&_StringLimit,
TRANSFIELD => \&_TransLimit,
+ TRANSCONTENT => \&_TransContentLimit,
TRANSDATE => \&_TransDateLimit,
WATCHERFIELD => \&_WatcherLimit,
MEMBERSHIPFIELD => \&_WatcherMembershipLimit,
@@ -677,16 +678,43 @@ sub _TransDateLimit {
=head2 _TransLimit
-Limit based on the Content of a transaction or the ContentType.
-
-Meta Data:
- none
+Limit based on the ContentType or the Filename of a transaction.
=cut
sub _TransLimit {
+ my ( $self, $field, $op, $value, %rest ) = @_;
+
+ my $txn_alias = $self->JoinTransactions;
+ unless ( defined $self->{_sql_trattachalias} ) {
+ $self->{_sql_trattachalias} = $self->_SQLJoin(
+ TYPE => 'LEFT', # not all txns have an attachment
+ ALIAS1 => $txn_alias,
+ FIELD1 => 'id',
+ TABLE2 => 'Attachments',
+ FIELD2 => 'TransactionId',
+ );
+ }
+
+ $self->_SQLLimit(
+ %rest,
+ ALIAS => $self->{_sql_trattachalias},
+ FIELD => $field,
+ OPERATOR => $op,
+ VALUE => $value,
+ CASESENSITIVE => 0,
+ );
+}
+
+=head2 _TransContentLimit
+
+Limit based on the Content of a transaction.
+
+=cut
+
+sub _TransContentLimit {
- # Content, ContentType, Filename
+ # Content search
# If only this was this simple. We've got to do something
# complicated here:
@@ -732,36 +760,25 @@ sub _TransLimit {
}
#Search for the right field
- if ( $field eq 'Content' and RT->Config->Get('DontSearchFileAttachments') ) {
- $self->_OpenParen;
- $self->_SQLLimit(
- %rest,
- ALIAS => $self->{_sql_trattachalias},
- FIELD => $field,
- OPERATOR => $op,
- VALUE => $value,
- CASESENSITIVE => 0,
- );
- $self->_SQLLimit(
- ENTRYAGGREGATOR => 'AND',
- ALIAS => $self->{_sql_trattachalias},
- FIELD => 'Filename',
- OPERATOR => 'IS',
- VALUE => 'NULL',
- );
- $self->_CloseParen;
- } else {
+ $self->_OpenParen;
+ $self->_SQLLimit(
+ %rest,
+ ALIAS => $self->{_sql_trattachalias},
+ FIELD => $field,
+ OPERATOR => $op,
+ VALUE => $value,
+ CASESENSITIVE => 0,
+ );
+ if ( RT->Config->Get('DontSearchFileAttachments') ) {
$self->_SQLLimit(
- %rest,
- ALIAS => $self->{_sql_trattachalias},
- FIELD => $field,
- OPERATOR => $op,
- VALUE => $value,
- CASESENSITIVE => 0,
+ ENTRYAGGREGATOR => 'AND',
+ ALIAS => $self->{_sql_trattachalias},
+ FIELD => 'Filename',
+ OPERATOR => 'IS',
+ VALUE => 'NULL',
);
}
-
-
+ $self->_CloseParen;
}
=head2 _WatcherLimit
commit 5000a8ee8d0fa18b313822201cd00dea4c7421b6
Author: Ruslan Zakirov <ruz at bestpractical.com>
Date: Fri Oct 1 04:55:10 2010 +0400
FullTextSearch option
diff --git a/etc/RT_Config.pm.in b/etc/RT_Config.pm.in
index 65c120f..b6d8153 100755
--- a/etc/RT_Config.pm.in
+++ b/etc/RT_Config.pm.in
@@ -1533,6 +1533,26 @@ from being displayed in-line when viewing a ticket's history.
Set($SuppressInlineTextFiles, undef);
+=item C<%FullTextSearch>
+
+Full text search (FTS) without indexes is a very slow operation and by
+default is disabled at all. To enable FTS set key 'Enable' to true value.
+
+Setup of indexes and filling them with data requires additional steps that
+vary from DB to DB. Use F<sbin/rt-setup-fulltext-index> helper
+for quick start. This script creates required structures in the DB and
+gives some ideas on next steps.
+
+=cut
+
+Set(%FullTextSearch,
+ Enable => 0,
+ Indexed => 0,
+# Table => 'AttachmentsIndex',
+# Column => 'ftsindex',
+);
+
+
=item C<$DontSearchFileAttachments>
If C<$DontSearchFileAttachments> is set to a true value, then uploaded
commit be330a82da8c1fd6d6f6c571c3cc617ae1eeca1e
Author: Ruslan Zakirov <ruz at bestpractical.com>
Date: Fri Oct 1 05:30:12 2010 +0400
don't show content search if it's disabled
diff --git a/share/html/Elements/SelectAttachmentField b/share/html/Elements/SelectAttachmentField
index b81ebbe..220faed 100755
--- a/share/html/Elements/SelectAttachmentField
+++ b/share/html/Elements/SelectAttachmentField
@@ -47,7 +47,9 @@
%# END BPS TAGGED BLOCK }}}
<select name="<%$Name%>">
<option value="Subject"><&|/l&>Subject</&></option>
+% if ( RT->Config->Get('FullTextSearch')->{'Enable'} ) {
<option value="Content"><&|/l&>Content</&></option>
+% }
<option value="ContentType"><&|/l&>Content-Type</&></option>
<option value="Filename"><&|/l&>Filename</&></option>
</select>
commit 90805dc5af43d9ef369b4524cdecc05e05c2a9f2
Author: Ruslan Zakirov <ruz at bestpractical.com>
Date: Fri Oct 1 05:32:46 2010 +0400
sbin/rt-fts-oracle
diff --git a/.gitignore b/.gitignore
index e5d60a9..484ff94 100644
--- a/.gitignore
+++ b/.gitignore
@@ -24,6 +24,7 @@ sbin/rt-dump-database
sbin/rt-email-dashboards
sbin/rt-email-digest
sbin/rt-email-group-admin
+sbin/rt-fts-oracle
sbin/rt-server
sbin/rt-session-viewer
sbin/rt-setup-database
diff --git a/configure.ac b/configure.ac
index 9d4cad4..c6502f6 100755
--- a/configure.ac
+++ b/configure.ac
@@ -413,6 +413,7 @@ AC_CONFIG_FILES([
sbin/rt-validator
sbin/rt-email-group-admin
sbin/rt-server
+ sbin/rt-fts-oracle
bin/fastcgi_server
bin/mason_handler.fcgi
bin/mason_handler.scgi
diff --git a/sbin/rt-fts-oracle.in b/sbin/rt-fts-oracle.in
new file mode 100755
index 0000000..d2a7bf2
--- /dev/null
+++ b/sbin/rt-fts-oracle.in
@@ -0,0 +1,420 @@
+#!@PERL@
+# BEGIN BPS TAGGED BLOCK {{{
+#
+# COPYRIGHT:
+#
+# This software is Copyright (c) 1996-2010 Best Practical Solutions, LLC
+# <jesse at bestpractical.com>
+#
+# (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
+# you are the copyright holder for those contributions and you grant
+# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable,
+# 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 }}}
+use strict;
+use warnings;
+no warnings 'once';
+
+# fix lib paths, some may be relative
+BEGIN {
+ require File::Spec;
+ my @libs = ("@RT_LIB_PATH@", "@LOCAL_LIB_PATH@");
+ my $bin_path;
+
+ for my $lib (@libs) {
+ unless ( File::Spec->file_name_is_absolute($lib) ) {
+ unless ($bin_path) {
+ if ( File::Spec->file_name_is_absolute(__FILE__) ) {
+ $bin_path = ( File::Spec->splitpath(__FILE__) )[1];
+ }
+ else {
+ require FindBin;
+ no warnings "once";
+ $bin_path = $FindBin::Bin;
+ }
+ }
+ $lib = File::Spec->catfile( $bin_path, File::Spec->updir, $lib );
+ }
+ unshift @INC, $lib;
+ }
+
+}
+
+=head1 NAME
+
+rt-fts-oracle - setup Oracle Text index for full text search
+
+=head1 USAGE
+
+ rt-fts-oracle --help
+ rt-fts-oracle --dba sysdba --dba-password 'secret'
+
+=head1 DESCRIPTION
+
+This utility grants CTXAPP role to RT's user and rights to execute functions
+from CTX_DDL package. Also, it creates several prefernces, functions and triggers
+all starting with 'rt_fts_' prefix in the name. After all an index is created.
+
+=cut
+
+use RT;
+RT::LoadConfig();
+
+my %DB = (
+ user => $RT::DatabaseUser,
+ admin => undef,
+ admin_password => undef,
+);
+
+my %OPT = (
+ help => 0,
+);
+
+use Getopt::Long qw(GetOptions);
+GetOptions(
+ 'h|help!' => \$OPT{'help'},
+ 'dba=s' => \$DB{'admin'},
+ 'dba-password=s' => \$DB{'admin_password'},
+);
+
+if ( $OPT{'help'} || !$DB{'admin'}) {
+ require Pod::Usage;
+ Pod::Usage::pod2usage(
+ -message => "",
+ -exitval => $OPT{'help'}? 0 : 1,
+ -verbose => 99,
+ -sections => $OPT{'help'}? 'NAME|USAGE|DESCRIPTION|OPTIONS' : 'NAME|USAGE',
+ );
+}
+
+RT::Init();
+
+{
+ my $dbah = dba_handle();
+ do_print_error( $dbah => 'GRANT CTXAPP TO '. $DB{'user'} );
+ do_print_error( $dbah => 'GRANT EXECUTE ON CTXSYS.CTX_DDL TO '. $DB{'user'} );
+}
+
+my $dbh = $RT::Handle->dbh;
+$dbh->{'RaiseError'} = 1;
+$dbh->{'PrintError'} = 1;
+
+my $prefix = 'rt_fts_';
+
+our %PREFERENCES = (
+ datastore => {
+ type => 'DIRECT_DATASTORE',
+ },
+ filter => {
+ type => 'AUTO_FILTER',
+# attributes => {
+# timeout => 120, # seconds
+# timeout_type => 'HEURISTIC', # or 'FIXED'
+# },
+ },
+ lexer => {
+ type => 'WORLD_LEXER',
+ },
+ word_list => {
+ type => 'BASIC_WORDLIST',
+ attributes => {
+ stemmer => 'AUTO',
+ fuzzy_match => 'AUTO',
+# fuzzy_score => undef,
+# fuzzy_numresults => undef,
+# substring_index => undef,
+# prefix_index => undef,
+# prefix_length_min => undef,
+# prefix_length_max => undef,
+# wlidcard_maxterms => undef,
+ },
+ },
+ 'section_group' => {
+ type => 'NULL_SECTION_GROUP',
+ },
+
+ storage => {
+ type => 'BASIC_STORAGE',
+ attributes => {
+ R_TABLE_CLAUSE => 'lob (data) store as (cache)',
+ I_INDEX_CLAUSE => 'compress 2',
+ },
+ },
+);
+
+my @params = ();
+push @params, create_datastore();
+push @params, create_filter();
+push @params, create_lexer();
+push @params, create_word_list();
+push @params, create_stop_list();
+push @params, create_section_group();
+push @params, create_storage();
+
+my $index_params = join "\n", @params;
+do_error_is_ok( $dbh => "DROP INDEX ${prefix}index" );
+$dbh->do(
+ "CREATE INDEX ${prefix}index ON Attachments(Content)
+ indextype is ctxsys.context parameters('
+ $index_params
+ ')",
+);
+
+sub create_datastore {
+ return sprintf 'datastore %s', create_preference(
+ %{ $PREFERENCES{'datastore'} },
+ name => 'datastore',
+ );
+}
+
+sub create_filter {
+ my $res = '';
+ $res .= sprintf "format column %s\n", create_format_column();
+ $res .= sprintf 'filter %s', create_preference(
+ %{ $PREFERENCES{'filter'} },
+ name => 'filter',
+ );
+ return $res;
+}
+
+sub create_lexer {
+ return sprintf 'lexer %s', create_preference(
+ %{ $PREFERENCES{'lexer'} },
+ name => 'lexer',
+ );
+}
+
+sub create_word_list {
+ return sprintf 'wordlist %s', create_preference(
+ %{ $PREFERENCES{'word_list'} },
+ name => 'word_list',
+ );
+}
+
+sub create_stop_list {
+ my $file = shift || 'etc/stopwords/en.txt';
+
+ my $name = $prefix .'stop_list';
+ do_error_is_ok( $dbh => 'begin ctx_ddl.drop_stoplist(?); end;', $name );
+
+ $dbh->do(
+ 'begin ctx_ddl.create_stoplist(?, ?); end;',
+ undef, $name, 'BASIC_STOPLIST'
+ );
+
+ open my $fh, '<:utf8', $file
+ or die "couldn't open file '$file': $!";
+ while ( my $word = <$fh> ) {
+ chomp $word;
+ $dbh->do(
+ 'begin ctx_ddl.add_stopword(?, ?); end;',
+ undef, $name, $word
+ );
+ }
+ close $fh;
+ return sprintf 'stoplist %s', $name;
+}
+
+sub create_section_group {
+ my $name = $prefix .'section_group';
+ do_error_is_ok( $dbh => 'begin ctx_ddl.drop_section_group(?); end;', $name );
+ $dbh->do(
+ 'begin ctx_ddl.create_section_group(?, ?); end;',
+ undef, $name, $PREFERENCES{'section_group'}{'type'}
+ );
+ return sprintf 'section group %s', $name;
+}
+
+sub create_storage {
+ return sprintf 'storage %s', create_preference(
+ %{ $PREFERENCES{'storage'} },
+ name => 'storage',
+ );
+}
+
+sub create_format_column {
+ my $column_name = 'ContentOracleFormat';
+ unless (
+ $dbh->column_info(
+ undef, undef, uc('Attachments'), uc( $column_name )
+ )->fetchrow_array
+ ) {
+ $dbh->do(qq{
+ ALTER TABLE Attachments ADD $column_name VARCHAR2(10)
+ });
+ }
+
+ my $detect_format = qq{
+ CREATE OR REPLACE FUNCTION ${prefix}detect_format_simple(
+ parent IN NUMBER,
+ type IN VARCHAR2,
+ encoding IN VARCHAR2,
+ fname IN VARCHAR2
+ )
+ RETURN VARCHAR2
+ AS
+ format VARCHAR2(10);
+ BEGIN
+ format := CASE
+ };
+ if ( $RT::DontSearchFileAttachments ) {
+ $detect_format .= qq{
+ WHEN fname IS NOT NULL THEN 'ignore'
+ };
+ }
+ my $binary = $RT::DontSearchBinaryAttachments? 'ignore' : 'binary';
+ $detect_format .= qq{
+ WHEN type = 'text' THEN 'text'
+ WHEN type = 'text/rtf' THEN '$binary'
+ WHEN type LIKE 'text/%' THEN 'text'
+ WHEN type LIKE 'message/%' THEN 'text'
+ WHEN type LIKE 'multipart/%' THEN 'ignore'
+ WHEN type LIKE 'image/%' THEN 'ignore'
+ WHEN type LIKE 'audio/%' THEN 'ignore'
+ WHEN type LIKE 'video/%' THEN 'ignore'
+ WHEN type LIKE '%signature%' THEN 'ignore'
+ WHEN type LIKE '%pkcs7%' THEN 'ignore'
+ WHEN type LIKE '%compress%' THEN 'ignore'
+ WHEN type LIKE '%zip%' THEN 'ignore'
+ WHEN type LIKE '%tar%' THEN 'ignore'
+ WHEN type LIKE '%/octet-stream' THEN 'ignore'
+ ELSE '$binary'
+ END;
+ RETURN format;
+ END;
+ };
+ create_procedure( $detect_format );
+
+ $dbh->do(qq{
+ UPDATE Attachments
+ SET $column_name = ${prefix}detect_format_simple(
+ Parent,
+ ContentType, ContentEncoding,
+ Filename
+ )
+ WHERE $column_name IS NULL
+ });
+ $dbh->do(qq{
+ CREATE OR REPLACE TRIGGER ${prefix}set_format
+ BEFORE INSERT
+ ON Attachments
+ FOR EACH ROW
+ BEGIN
+ :new.$column_name := ${prefix}detect_format_simple(
+ :new.Parent,
+ :new.ContentType, :new.ContentEncoding,
+ :new.Filename
+ );
+ END;
+ });
+ return $column_name;
+}
+
+sub create_preference {
+ my %info = @_;
+ my $name = $prefix . $info{'name'};
+ do_error_is_ok( $dbh => 'begin ctx_ddl.drop_preference(?); end;', $name );
+ $dbh->do(
+ 'begin ctx_ddl.create_preference(?, ?); end;',
+ undef, $name, $info{'type'}
+ );
+ return $name unless $info{'attributes'};
+
+ while ( my ($attr, $value) = each %{ $info{'attributes'} } ) {
+ $dbh->do(
+ 'begin ctx_ddl.set_attribute(?, ?, ?); end;',
+ undef, $name, $attr, $value
+ );
+ }
+
+ return $name;
+}
+
+sub create_procedure {
+ my $text = shift;
+
+ my $status = $dbh->do($text, { RaiseError => 0 });
+
+ # Statement succeeded
+ return if $status;
+
+ if ( 6550 != $dbh->err ) {
+ # Utter failure
+ die $dbh->errstr;
+ }
+ else {
+ my $msg = $dbh->func( 'plsql_errstr' );
+ die $dbh->errstr if !defined $msg;
+ die $msg if $msg;
+ }
+}
+
+sub dba_handle {
+ $ENV{'NLS_LANG'} = "AMERICAN_AMERICA.AL32UTF8";
+ $ENV{'NLS_NCHAR'} = "AL32UTF8";
+ my $dsn = do { my $h = new RT::Handle; $h->BuildDSN; $h->DSN };
+ my $dbh = DBI->connect(
+ $dsn, $DB{admin}, $DB{admin_password},
+ { RaiseError => 1, PrintError => 1 },
+ );
+ unless ( $dbh ) {
+ die "Failed to connect to $dsn as user '$DB{admin}': ". $DBI::errstr;
+ }
+ return $dbh;
+}
+
+sub do_error_is_ok {
+ my $dbh = shift;
+ local $dbh->{'RaiseError'} = 0;
+ local $dbh->{'PrintError'} = 0;
+ return $dbh->do(shift, undef, @_);
+}
+
+sub do_print_error {
+ my $dbh = shift;
+ local $dbh->{'RaiseError'} = 0;
+ local $dbh->{'PrintError'} = 1;
+ return $dbh->do(shift, undef, @_);
+}
+
+=head1 AUTHOR
+
+Ruslan Zakirov E<lt>ruz at bestpractical.comE<gt>
+
+=cut
+
commit dceeb2377ab6721e11e09f17cf558e5811b20a31
Author: Ruslan Zakirov <ruz at bestpractical.com>
Date: Fri Oct 1 06:19:46 2010 +0400
FTS implementation for Pg and Oracle
diff --git a/lib/RT/Tickets_Overlay.pm b/lib/RT/Tickets_Overlay.pm
index 0eec0ba..25af445 100755
--- a/lib/RT/Tickets_Overlay.pm
+++ b/lib/RT/Tickets_Overlay.pm
@@ -748,6 +748,12 @@ sub _TransContentLimit {
my ( $self, $field, $op, $value, %rest ) = @_;
+ my $config = RT->Config->Get('FullTextSearch') || {};
+ unless ( $config->{'Enable'} ) {
+ $self->_SQLLimit( %rest, FIELD => 'id', VALUE => 0 );
+ return;
+ }
+
my $txn_alias = $self->JoinTransactions;
unless ( defined $self->{_sql_trattachalias} ) {
$self->{_sql_trattachalias} = $self->_SQLJoin(
@@ -759,16 +765,73 @@ sub _TransContentLimit {
);
}
- #Search for the right field
$self->_OpenParen;
- $self->_SQLLimit(
- %rest,
- ALIAS => $self->{_sql_trattachalias},
- FIELD => $field,
- OPERATOR => $op,
- VALUE => $value,
- CASESENSITIVE => 0,
- );
+ if ( $config->{'Indexed'} ) {
+ my $db_type = RT->Config->Get('DatabaseType');
+
+ my $alias;
+ if ( $config->{'Table'} ) {
+ $alias = $self->{'_sql_aliases'}{'full_text'} ||= $self->_SQLJoin(
+ TYPE => 'LEFT',
+ ALIAS1 => $self->{'_sql_trattachalias'},
+ FIELD1 => 'id',
+ TABLE2 => $config->{'Table'},
+ FIELD2 => 'id',
+ );
+ } else {
+ $alias = $self->{'_sql_trattachalias'};
+ }
+
+ my $field = $config->{'Field'} || 'Content';
+ if ( $db_type eq 'Oracle' ) {
+ my $dbh = $RT::Handle->dbh;
+ $self->_SQLLimit(
+ %rest,
+ # XXX: Nasty hack
+ ALIAS => 'CONTAINS( '. $self->{_sql_trattachalias},
+ FIELD => $field . ', '. $dbh->quote($value) .')',
+ OPERATOR => '>',
+ VALUE => 0,
+ QUOTEVALUE => 0,
+ CASESENSITIVE => 1,
+ );
+ # this is required to trick DBIx::SB's LEFT JOINS optimizer
+ # into deciding that join is redundant as it is
+ $self->_SQLLimit(
+ ENTRYAGGREGATOR => 'AND',
+ ALIAS => $self->{_sql_trattachalias},
+ FIELD => 'Content',
+ OPERATOR => 'IS NOT',
+ VALUE => 'NULL',
+ );
+ }
+ elsif ( $db_type eq 'Pg' ) {
+ my $dbh = $RT::Handle->dbh;
+ #XXX: handle negative searches
+ $self->_SQLLimit(
+ %rest,
+ ALIAS => $alias,
+ FIELD => $field,
+ OPERATOR => '@@',
+ VALUE => 'plainto_tsquery('. $dbh->quote($value) .')',
+ QUOTEVALUE => 0,
+ );
+ }
+ else {
+ $RT::Logger->error( "Indexed full text search is not supported for $db_type" );
+ $self->_SQLLimit( %rest, FIELD => 'id', VALUE => 0 );
+ return;
+ }
+ } else {
+ $self->_SQLLimit(
+ %rest,
+ ALIAS => $self->{_sql_trattachalias},
+ FIELD => $field,
+ OPERATOR => $op,
+ VALUE => $value,
+ CASESENSITIVE => 0,
+ );
+ }
if ( RT->Config->Get('DontSearchFileAttachments') ) {
$self->_SQLLimit(
ENTRYAGGREGATOR => 'AND',
-----------------------------------------------------------------------
More information about the Rt-commit
mailing list