lib/CrashTest/Plugin/Storage/Sql/Utils.pm
author Vincent Tondellier <tonton+hg@team1664.org>
Mon, 17 Jul 2023 22:52:42 +0200
changeset 134 26ba2717da6e
parent 127 0bbbadd5d9ea
permissions -rw-r--r--
Allow matching filelink on partial url paths

# 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 3 of the License, 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, see <http://www.gnu.org/licenses/>.

package CrashTest::Plugin::Storage::Sql::Utils;

use Mojo::Base -base;
use Storable 'dclone';
use Search::QueryParser::SQL;

has [ qw/instance app config/ ];

sub new {
    my $self = shift->SUPER::new(@_);

    return $self;
}

sub build_query_from_search_string {
    my ($self, $search, $extra_columns, $extra_search_fields) = @_;

    my @values;

    # define a callback to collect values safely (without magic markers)
    my $cb = sub {
        my ($column, $this_op, $value) = @_;
        my $sql;

        if($column->type eq "fuzzy") {
            if ( $this_op =~ /!|NOT/ ) {
                $this_op = " NOT ILIKE ";
            } else {
                $this_op = " ILIKE ";
            }
            $value = "%$value%";
        } elsif($column->type eq "sql") {
            $sql = $column->name;
            $sql =~ s/\s+\?op\?\s+/ $this_op /;
        }

        if(!defined($sql)) {
            $sql = join('', $column->stringify, $this_op, '?');
        }

        push @values, $value;
        return $sql;
    };

    my $search_fields = dclone($extra_search_fields);

    foreach my $k(keys %$search_fields) {
        $search_fields->{$k}->{callback} = $cb;
    }

    foreach my $extra_col(@$extra_columns) {
        $search_fields->{$extra_col->{id}} = { callback => $cb, name => $extra_col->{db_column} };
    }

    my $parser = Search::QueryParser::SQL->new(
        columns         => $search_fields,
        default_column  => "function",
        strict          => 1,
    );

    my $query;
    eval {
        $query = $parser->parse($search, 1);
    };
    if($@) {
        # strip file and line info from message
        $@ =~ /(.+)(?:\s+at\s+[^\s]+\s+line\s+\d+\.)$/;
        my $msg = $1 || $@;
        die($msg . "\n");
    } elsif(!defined($query)) {
        die($parser->err . "\n");
    }

    # reset before calling dbi
    @values = ();
    my $dbi = $query->dbi();

    return [ $dbi->[0], \@values ];
}

1;