/usr/local/CPAN/Catalog/Catalog/implementation.pm
#
# Copyright (C) 1998, 1999 Loic Dachary
#
# This program is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by the
# Free Software Foundation; either version 2, or (at your option) any
# later version.
#
# This program 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, 675 Mass Ave, Cambridge, MA 02139, USA.
#
#
# $Header: /cvsroot/Catalog/Catalog/lib/Catalog/implementation.pm,v 1.7 1999/07/02 13:36:53 loic Exp $
#
package Catalog::implementation;
use strict;
use vars qw(@tablelist_theme @tablelist_alpha @tablelist_date);
@tablelist_theme = qw(catalog_entry2category catalog_category catalog_category2category catalog_path);
@tablelist_alpha = qw(catalog_alpha);
@tablelist_date = qw(catalog_date);
use MD5;
use Text::Query;
use File::Path;
use Catalog::tools::tools;
use Catalog::path qw(path_simplify_component);
sub new {
my($type) = @_;
my($self) = {};
bless($self, $type);
$self->initialize();
return $self;
}
sub initialize {
my($self) = @_;
$self->{'db'} = Catalog::db->new() if(!defined($self->{'db'}));
my($db) = $self->{'db'};
$db->resources_load('catalog_schema', 'Catalog::schema');
}
#
# Load info for all catalogs. Refreshed when catalog edited/removed/created
#
sub cinfo {
my($self) = @_;
if(!exists($self->{'ccatalog'})) {
my($tables) = $self->db()->tables();
my($catalog) = grep(/^catalog$/, @$tables);
if(defined($catalog)) {
$self->{'csetup'} = 'yes';
my($rows) = $self->db()->exec_select("select rowid,name,tablename,navigation,info,fieldname,cwhere,corder,unix_timestamp(updated) as updated,root,dump,dumplocation from catalog");
if(@$rows) {
$self->{'ccatalog'} = { map { $_->{'name'} => $_ } @$rows };
} else {
$self->{'ccatalog'} = undef;
}
$self->{'ctables'} = [ grep(!/^catalog/, @$tables) ];
}
}
return $self->{'ccatalog'};
}
#
# Create needed structures for a catalog to work
#
sub csetup_api {
my($self) = @_;
$self->db()->exec($self->db()->schema('catalog_schema', 'catalog'));
$self->db()->exec($self->db()->schema('catalog_schema', 'catalog_auth'));
$self->db()->exec($self->db()->schema('catalog_schema', 'catalog_auth_properties'));
$self->cinfo_clear();
}
#
# import XML representation
#
sub cimport_api {
my($self, $name, $file) = @_;
my($external) = Catalog::external->new();
$external->load($self, $name, $file);
}
#
# Export XML representation
#
sub cexport_api {
my($self, $name, $file) = @_;
my($external) = Catalog::external->new();
$external->unload($self, $name, $file);
}
#
# Create demo data table
#
sub cdemo_api {
my($self) = @_;
$self->cerror("The urldemo table already exists") if($self->db()->info_table("urldemo"));
$self->db()->exec($self->db()->schema('catalog_schema', 'urldemo'));
}
#
# Create a symbolic link
#
sub categorysymlink_api {
my($self, $name, $up, $down) = @_;
#
# Link the created category to its parent
#
$self->db()->insert("catalog_category2category_$name",
'info' => 'hidden,symlink',
'up' => $up,
'down' => $down);
}
#
# Destroy a catalog with sanity check
#
sub cdestroy_api {
my($self, $name) = @_;
my($ccatalog) = $self->cinfo();
if(exists($ccatalog->{$name})) {
$self->cdestroy_real($name);
}
}
#
# Destroy a catalog
#
sub cdestroy_real {
my($self, $name) = @_;
my($tables) = $self->db()->tables();
my($table);
foreach $table (@tablelist_theme, @tablelist_alpha, @tablelist_date) {
my($real) = "${table}_$name";
if(grep(/^$real$/, @$tables)) {
$self->db()->exec("drop table $real");
}
}
$self->db()->exec("delete from catalog where name = '$name'");
$self->cinfo_clear();
}
#
# Force recalculation of the cached data for alpha catalog
#
sub calpha_count_api {
my($self, $name) = @_;
$self->db()->update("catalog", "name = '$name'",
'updated' => 0);
}
#
# Fill the alpha catalog cache
#
sub calpha_count_1_api {
my($self, $name) = @_;
my($catalog) = $self->cinfo()->{$name};
my($table) = $catalog->{'tablename'};
my($field) = $catalog->{'fieldname'};
my($where) = $catalog->{'cwhere'};
if(defined($where) && $where !~ /^\s*$/) {
$where = "and ($where)";
} else {
$where = '';
}
my($letter);
foreach $letter ('0'..'9', 'a'..'z') {
my($count) = $self->db()->exec_select_one("select count(*) as count from $table where $field like '$letter%' $where")->{'count'};
$self->db()->update("catalog_alpha_$name", "letter = '$letter'",
'count' => $count);
}
$self->db()->update("catalog", "name = '$name'",
'updated' => $self->db()->datetime(time()));
}
#
# Fill the alpha catalog cache
#
sub cdate_count_1_api {
my($self, $name) = @_;
my($catalog) = $self->cinfo()->{$name};
my($table) = $catalog->{'tablename'};
my($field) = $catalog->{'fieldname'};
my($where) = $catalog->{'cwhere'};
if(defined($where) && $where !~ /^\s*$/) {
$where = "where ($where)";
} else {
$where = '';
}
$self->db()->exec("delete from catalog_date_$name");
$self->db()->exec("insert into catalog_date_$name (tag, count) select date_format($field, '%Y') as yyyy, count(rowid) from $table $where group by yyyy order by yyyy");
$self->db()->exec("insert into catalog_date_$name (tag, count) select date_format($field, '%Y%m') as yyyymm, count(rowid) from $table $where group by yyyymm order by yyyymm");
$self->db()->exec("insert into catalog_date_$name (tag, count) select date_format($field, '%Y%m%d') as yyyymmdd, count(rowid) from $table $where group by yyyymmdd order by yyyymmdd");
$self->db()->update("catalog", "name = '$name'",
'updated' => $self->db()->datetime(time()));
}
#
# Upgrade from Catalog-0.3 and below : create and populate catalog_path
# if does not exist
#
sub pathcheck {
my($self, $name) = @_;
my($table) = "catalog_path_$name";
if(!$self->db()->info_table($table)) {
my($schema) = $self->db()->schema('catalog_schema', 'catalog_path');
$schema =~ s/NAME/$name/g;
$self->db()->exec($schema);
my($catalog) = $self->cinfo()->{$name};
$self->db()->insert($table,
'pathname' => '/',
'md5' => MD5->hexhash('/'),
'path' => ' ',
'id' => $catalog->{'root'});
my($func) = sub {
my($id, $name, $pathname, $path) = @_;
$pathname = path_simplify_component("/$pathname/");
eval {
$self->db()->insert($table,
'pathname' => $pathname,
'md5' => MD5->hexhash($pathname),
'path' => ",$path,",
'id' => $id
);
};
warn("$@") if($@);
$self->gauge();
return 1;
};
$self->walk_categories($name, $func);
$self->cinfo_clear();
}
}
#
# Normalize interval structure
#
sub cdate_normalize {
my($self, $spec) = @_;
return if(exists($spec->{'normalized'}));
my($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime();
$mon++;
if($year < 60) {
$year += 2000;
} else {
$year += 1900;
}
my($now) = sprintf("$year%02d%02d", $mon, $mday);
$spec->{'from_op'} = '>=';
$spec->{'to_op'} = '<=';
#
# Fill from and to
#
if($spec->{'date'}) {
#
# A specific day
#
$spec->{'from'} = $spec->{'date'};
$spec->{'to'} = $spec->{'date'};
} else {
if(!$spec->{'from'} && !$spec->{'to'}) {
#
# No date specified, default to all
#
$spec->{'from'} = "19700101";
$spec->{'to'} = $now;
} elsif($spec->{'from'} && !$spec->{'to'}) {
#
# From a specified date in the paste up to now
#
$spec->{'to'} = $now;
} elsif($spec->{'from'} && !$spec->{'to'}) {
#
# From the beginning of type up to the specified date
#
$spec->{'from'} = "19700101";
} else {
#
# A specified interval time
#
;
}
}
#
# Normalize date spec from
#
if($spec->{'from'} =~ /^\d\d\d\d$/) {
$spec->{'from'} .= "0101";
} elsif($spec->{'from'} =~ /^\d\d\d\d\d\d$/) {
$spec->{'from'} .= "01";
}
#
# Normalize date spec to
#
if($spec->{'to'} =~ /^\d\d\d\d$/) {
$spec->{'to'} .= "1231";
} elsif($spec->{'to'} =~ /^(\d\d\d\d)(\d\d)$/) {
my($rows) = $self->db()->exec_select("select date_format(date_sub(date_add('$1-$2-01', interval 1 month), interval 1 day), '%Y%m%d') as d");
$spec->{'to'} = $rows->[0]->{'d'};
}
$spec->{'normalized'}++;
# warn("cdate_normalize " . ostring($spec));
return $spec;
}
#
# Return an interval structure that is the intersection of two intervals
#
sub cdate_intersection {
my($self, $i1, $i2) = @_;
return {
'from' => ($i1->{'from'} > $i2->{'from'} ? $i1->{'from'} : $i2->{'from'}),
'to' => ($i1->{'to'} < $i2->{'to'} ? $i1->{'to'} : $i2->{'to'}),
};
}
#
# Recalculate counts for each category
#
sub category_count_api {
my($self, $name) = @_;
my($catalog) = $self->cinfo()->{$name};
my($where) = $catalog->{'cwhere'};
if(defined($where) && $where !~ /^\s*$/) {
$where = "and ($where)";
} else {
$where = '';
}
$self->db()->update("catalog_category_$name", "",
'count' => 0);
$self->category_count_1($name, $where, $catalog->{'tablename'}, $catalog->{'root'});
}
#
# Recalculate counts for each category subroutine
#
sub category_count_1 {
my($self, $name, $where, $table, $id) = @_;
my($count) = $self->db()->exec_select_one(qq{
select count(*)
from $table, catalog_entry2category_$name
where ($table.rowid = catalog_entry2category_$name.row
and catalog_entry2category_$name.category = $id)
$where
})->{'count(*)'};
dbg("found $count entries at id $id", "catalog");
my($rows) = $self->db()->exec_select(qq{
select b.down
from catalog_category2category_$name as b
where b.up = $id and (b.info is null or not find_in_set('symlink', b.info))
});
my($row);
foreach $row (@$rows) {
$count += $self->category_count_1($name, $where, $table, $row->{down});
$self->gauge();
}
$self->db()->update("catalog_category_$name", "rowid = $id",
'count' => $count);
return $count;
}
#
# Dump theme catalog in file tree
#
sub cdump_api {
my($self, $name, $path, $layout) = @_;
$path =~ s:/$::o;
if(-d $path) {
system("/bin/rm","-fr",$path);
}
mkdir($path, 0777) or $self->cerror("cannot mkdir $path : $!");
my($rows) = $self->db()->exec_select("select pathname from catalog_path_$name");
my($row);
foreach $row (@$rows) {
my($content) = &$layout($row->{'pathname'});
my($dir) = "$path$row->{'pathname'}";
mkpath($dir);
my($file) = "${dir}index.html";
open(FILE, ">$file") or error("cannot open $file for writing : $!");
print FILE $content;
close(FILE);
}
}
#
# Select rowid's of catalogued table that match a category id
# Can be subclassed to implement filtering
#
sub select_entry_rows {
my ($self, $name, $id) = @_;
# was: my($rows) = $self->db()->exec_select(qq{
# select $table.rowid
# from $table, catalog_entry2category_$name
# where $table.rowid = catalog_entry2category_$name.row
# and catalog_entry2category_$name.category = $id
#});
my($rows) = $self->db()->exec_select(qq{
select row
from catalog_entry2category_$name
where category = $id
});
return $rows ? [ map { $_->{'row'} } @$rows ] : undef;
}
#
# HTML walk records of a theme catalog, call $func on each record
#
sub walk_api {
my($self, $name, $func, @ids) = @_;
if(!@ids) {
my($catalog) = $self->cinfo()->{$name};
push(@ids, $catalog->{'root'});
}
my($id);
foreach $id (@ids) {
$self->walk_1($func, $name, $id);
}
}
#
# Walk records of a theme catalog starting at $id
#
sub walk_1 {
my($self, $func, $name, $id) = @_;
my ($row_ids) = $self->select_entry_rows($name, $id);
my ($row_id);
foreach $row_id (@$row_ids) {
return if !&$func($row_id);
}
my ($cat_ids) = $self->select_linked_categories($name, $id);
my $cat_id;
foreach $cat_id (@$cat_ids) {
$self->walk_1($func, $name, $cat_id);
}
}
sub select_linked_categories {
my ($self, $name, $id) = @_;
# was: ($rows) = $self->db()->exec_select(qq{
# select a.rowid
# from catalog_category_$name as a, catalog_category2category_$name as b
# where a.rowid = b.down and b.up = $id
#});
my ($rows) = $self->db()->exec_select(qq{
select down
from catalog_category2category_$name
where up = $id and (info is null or not find_in_set('symlink', info))
});
return $rows ? [ map { $_->{down} } @$rows ] : undef;
}
#
# HTML walk categories of a theme catalog, call $func on each category
#
sub walk_categories {
my($self, $name, $func) = @_;
my($catalog) = $self->cinfo()->{$name};
my($id) = $catalog->{'root'};
$self->walk_categories_1($func, $name, $id);
}
#
# Walk categories of a theme catalog, starting at $id
#
sub walk_categories_1 {
my($self, $func, $name, $id, $path, $pathid) = @_;
my($rows) = $self->db()->exec_select("select a.rowid,a.name from catalog_category_$name as a, catalog_category2category_$name as b where a.rowid = b.down and b.up = $id and (b.info is null or not find_in_set('symlink', b.info))");
my($row);
foreach $row (@$rows) {
my($path_tmp) = $path ? "$path/$row->{'name'}" : $row->{'name'};
my($pathid_tmp) = $pathid ? "$pathid,$row->{'rowid'}" : $row->{'rowid'};
return if(!&$func($row->{'rowid'}, $row->{'name'}, $path_tmp, $pathid_tmp));
$self->walk_categories_1($func, $name, $row->{'rowid'}, $path_tmp, $pathid_tmp);
}
}
#
# HTML update the category count of the $rowid category and upper
# categories for a theme catalog.
#
sub ccount_api {
my($self, $name, $rowid, $increment) = @_;
# warn("update catalog_category_$name set count = count $increment where rowid = $rowid");
$self->db()->update("catalog_category_$name", "rowid = $rowid",
'+= count' => $increment);
my($rows) = $self->db()->exec_select("select a.rowid from catalog_category_$name as a, catalog_category2category_$name as b where a.rowid = b.up and b.down = $rowid and (b.info is null or not find_in_set('symlink', b.info))");
my($row);
foreach $row (@$rows) {
$self->ccount_api($name, $row->{'rowid'}, $increment);
}
}
#
# Remove an empty category
#
sub categoryremove_api {
my($self, $name, $parent, $id, $symlink) = @_;
my($category) = "catalog_category_$name";
my($category2category) = "catalog_category2category_$name";
my($entry2category) = "catalog_entry2category_$name";
my($row) = $self->db()->exec_select_one("select * from $category where rowid = $id");
#
# Sanity checks
#
$self->cerror("no category found for id = $id") if(!defined($row));
if(!defined($symlink)) {
$self->cerror("category has sub categories") if($self->db()->exec_select_one("select down from $category2category where up = $id"));
$self->cerror("category is not empty") if($row->{'count'} > 0);
$self->cerror("entries are still linked to this category") if($self->db()->exec_select_one("select row from $entry2category where category = $id"));
}
#
# Effective deletion
#
if(!defined($symlink)) {
$self->db()->mdelete($category, "rowid = $id");
$self->db()->mdelete($category2category, "down = $id");
$self->db()->mdelete("catalog_path_$name", "id = $id");
} else {
$self->db()->mdelete($category2category, "down = $id and up = $parent");
}
}
#
# The category record has been edited, update catalog structure
# accordingly.
#
sub categoryedit_api {
my($self, $name, $child) = @_;
my($child_length) = length($child);
#
# Replace the name of the category in path table
#
my($category) = $self->db()->exec_select_one("select name from catalog_category_$name where rowid = $child");
my($category_name) = path_simplify_component($category->{'name'});
my($rows) = $self->db()->exec_select("select pathname,path,id from catalog_path_$name where path like '%,$child,%'");
my($row);
foreach $row (@$rows) {
#
# Find position of component to replace by searching the $child in path. Position
# is stored in $count.
#
my($i) = 0;
my($count) = 1;
do { $count++; $i++; } while(($i = index($row->{'path'}, ',', $i)) &&
substr($row->{'path'}, $i + 1, $child_length) ne $child);
$i++;
# warn("child = $child, found = " . substr($row->{'path'}, $i, $child_length) . "\n");
#
# Find exact position and length of component to replace by counting the / in pathname
# (skip $count of them).
#
$i = 0;
while($count) { $i = index($row->{'pathname'}, '/', $i); $i++; $count--; }
my($name_length) = index($row->{'pathname'}, '/', $i) - $i;
# warn("child = $child, found = " . substr($row->{'pathname'}, $i, $name_length) . "\n");
#
# Substitute old category name with new one
#
substr($row->{'pathname'}, $i, $name_length, $category_name);
# warn("changed to $row->{'pathname'}");
#
# Change in table and update the md5 key
#
$self->db()->update("catalog_path_$name", "id = $row->{'id'}",
'pathname' => $row->{'pathname'},
'md5' => MD5->hexhash($row->{'pathname'}));
}
}
#
# Create a new sub category
#
sub categoryinsert_api {
my($self, $name, $up_id, $down_id) = @_;
$self->db()->insert("catalog_category2category_$name",
'info' => 'hidden',
'up' => $up_id,
'down' => $down_id);
#
# Create the path entry
#
my($down_category) = $self->db()->exec_select_one("select rowid,name from catalog_category_$name where rowid = $down_id");
my($up_path) = $self->db()->exec_select_one("select * from catalog_path_$name where id = $up_id");
my($pathname) = "$up_path->{'pathname'}$down_category->{'name'}/";
$pathname = path_simplify_component($pathname);
my($path) = $up_path->{'path'} ? "$up_path->{'path'}$down_category->{'rowid'}," : ",$down_category->{'rowid'},";
$self->db()->insert("catalog_path_$name",
'pathname' => $pathname,
'md5' => MD5->hexhash($pathname),
'path' => $path,
'id' => $down_category->{'rowid'}
);
}
#
# Remove catalog entry and all links to categories step 2
#
sub centryremove_all_api {
my($self, $name, $primary_value) = @_;
#
# Remove all the links between the entry and the categories
#
my($rows) = $self->db()->exec_select("select category from catalog_entry2category_$name where row = $primary_value");
if(defined($rows)) {
my($row);
foreach $row (@$rows) {
my($id) = $row->{'category'};
$self->db()->mdelete("catalog_entry2category_$name",
"row = $primary_value and category = $id");
$self->ccount_api($name, $id, '-1');
}
}
#
# Remove the entry itself
#
my($ccatalog) = $self->cinfo();
my($table) = $ccatalog->{$name}->{'tablename'};
my($primary_key) = $self->db()->info_table($table)->{'_primary_'};
$self->db()->mdelete($table, "$primary_key = $primary_value");
}
#
# Remove link between current category and record
#
sub centryremove_api {
my($self, $name, $id, $row) = @_;
$self->db()->mdelete("catalog_entry2category_$name",
"row = $row and category = $id");
$self->ccount_api($name, $id, '-1');
}
#
# HTML Create a record and link to current category
#
sub centryinsert_api {
my($self, $name, $id, $row) = @_;
$self->db()->insert("catalog_entry2category_$name",
'info' => 'hidden',
'row' => $row,
'category' => $id);
$self->ccount_api($name, $id, '+1');
}
#
# Create a date/alpha/theme catalog with sanity checks
#
sub cbuild_api {
my($self, %record) = @_;
my($error) = $self->cbuild_check($record{'name'},
$record{'tablename'},
$record{'navigation'},
'step2',
$record{'fieldname'});
error($error) if(defined($error));
my($rowid) = $self->db()->insert("catalog",
%record);
$self->cbuild_real($rowid,
$record{'name'},
$record{'tablename'},
$record{'navigation'},
$record{'fieldname'});
$self->cinfo_clear();
}
#
# Create a date/alpha/theme catalog
#
sub cbuild_real {
my($self, $rowid, $name, $table, $navigation, $field) = @_;
eval {
if(!$navigation || $navigation =~ /theme/) {
$self->cbuild_theme($name, $rowid);
} elsif($navigation eq 'date') {
$self->cbuild_date($name, $rowid, $field);
} else {
$self->cbuild_alpha($name, $rowid, $field);
}
};
#
# Construction of the catalog failed, rewind
#
if($@) {
my($error) = $@;
$self->db()->exec("delete from catalog where rowid = $rowid");
error($error);
}
$self->cinfo_clear();
}
#
# Create an alpha catalog
#
sub cbuild_alpha {
my($self, $name, $rowid, $field) = @_;
#
# Create catalog tables
#
my($table);
foreach $table (@tablelist_alpha) {
my($schema) = $self->db()->schema('catalog_schema', $table);
$schema =~ s/NAME/$name/g;
$self->db()->exec($schema);
}
my($letter);
foreach $letter ('0'..'9', 'a'..'z') {
$self->db()->insert("catalog_alpha_$name",
'letter' => $letter);
}
}
#
# Create a date catalog
#
sub cbuild_date {
my($self, $name, $rowid, $field) = @_;
#
# Create catalog tables
#
my($table);
foreach $table (@tablelist_date) {
my($schema) = $self->db()->schema('catalog_schema', $table);
$schema =~ s/NAME/$name/g;
$self->db()->exec($schema);
}
}
#
# Sanity checks on catalog creation parameters
#
sub cbuild_check {
my($self, $name, $table, $navigation, $step, $field) = @_;
return undef if($::opt_fake);
if($step eq 'step2') {
my($name_quoted) = $self->db()->quote($name);
return "you must specify the name of the catalog (name)" if(!$name);
}
return "you must specify a table name (tablename)" if(!$table);
return "the table $table does not exist (tablename)" if(!grep($table eq $_, @{$self->db()->tables()}));
if($navigation eq 'theme') {
my($info) = $self->db()->info_table($table);
if(!exists($info->{'_primary_'}) ||
$info->{'_primary_'} ne 'rowid' ||
$info->{$info->{'_primary_'}}->{'type'} ne 'int') {
return "the table $table does not have a unique primary numerical key named rowid";
}
} elsif($step eq 'step2' &&
($navigation eq 'date' ||
$navigation eq 'alpha')) {
my($info) = $self->db()->info_table($table);
return "a field name must be specified for date catalogs (fieldname)" if(!$field);
return "$field is not a field of $table (fieldname)" if(!exists($info->{$field}));
if($navigation eq 'date') {
return "$field of table $table is not a field of type date or time" if($info->{$field}->{'type'} ne 'date' && $info->{$field}->{'type'} ne 'time');
} elsif($navigation eq 'alpha') {
return "$field of table $table is not a field of type char" if($info->{$field}->{'type'} ne 'char');
}
}
return undef;
}
#
# Create a theme catalog
#
sub cbuild_theme {
my($self, $name, $rowid) = @_;
#
# Create catalog tables
#
my($table);
foreach $table (@tablelist_theme) {
my($schema) = $self->db()->schema('catalog_schema', $table);
$schema =~ s/NAME/$name/g;
$self->db()->exec($schema);
}
#
# Create root of catalog
#
my($root_rowid) = $self->db()->insert("catalog_category_$name",
'info' => 'root',
'name' => '');
$self->db()->insert("catalog_path_$name",
'pathname' => '/',
'md5' => MD5->hexhash('/'),
'path' => ' ',
'id' => $root_rowid);
#
# Register root in catalog table
#
$self->db()->update("catalog", "rowid = '$rowid'",
'root' => $root_rowid);
}
sub csearch_parse {
my($self, $words, $querymode, $fields_searched, $select) = @_;
my($parse_package) = (!defined($querymode) || $querymode eq 'simple') ? 'Text::Query::ParseSimple' : 'Text::Query::ParseAdvanced';
my($query) = Text::Query->new($words,
-parse => $parse_package,
-build => 'Text::Query::BuildSQLMySQL',
-fields_searched => $fields_searched,
-select => $select);
return $query->matchexp();
}
#
# Basic error handling mechanism
#
sub cerror {
my($self) = shift;
error(@_);
}
1;
# Local Variables: ***
# mode: perl ***
# End: ***