Skip to content

Commit

Permalink
Parsing Line Termination - Issue ewaters#20
Browse files Browse the repository at this point in the history
Introducting a new method in the Model to parse a line from the command
prompt and determine whether the command is complete.

For example - altsql> select * from film where title = ";
";

This failed because it stops at the end of the line even though we
are inside quotation marks.
  • Loading branch information
Matt Church committed May 17, 2017
1 parent 889470a commit d6082e5
Show file tree
Hide file tree
Showing 3 changed files with 123 additions and 2 deletions.
82 changes: 82 additions & 0 deletions lib/App/AltSQL/Model.pm
Original file line number Diff line number Diff line change
Expand Up @@ -35,4 +35,86 @@ sub execute_sql {
return $sth;
}

sub parse_sql_line {
my ($self, $line, $in_something) = @_;

# first we parse to strip the strings and quotes
# if we are at the end of a line in a comment or string
# then we return the character that started it (i.e. ` ' " or /*)
# then assuming we're not in one of those we can then run this same
# regex to determine 1 or 0
my @chars = split //, $line;
my @sanitized_string;

$in_something //= '';
my $last_char;
CHAR: while(my $char = shift @chars)
{
if($in_something)
{
if($last_char eq '\\' && $in_something =~ /["'`]/) {
# this character is escaped. lets ignore it.
$last_char = '';
next CHAR;
}
if($char eq $in_something)
{
$in_something = '';
}
if($in_something eq '/*' && $char eq '/' && $last_char eq '*')
{
$in_something = '';
}
}
else
{
for my $start (qw/' " `/)
{
if($char eq $start) {
if($last_char eq '\\') {
last;
# it's escaped
}
$in_something = $start;
}
}
if($char eq '*')
{
if($last_char eq '/')
{
$in_something = '/*';
pop @sanitized_string;
}
}
if($char eq '-')
{
if($last_char eq '-')
{
$in_something = '--';
pop @sanitized_string;
}
}
unless($in_something)
{
push @sanitized_string, $char;
}
}
$last_char = $char;
}
if($in_something eq '--')
{
$in_something = '';
}
return $in_something if $in_something;

$line = join '', @sanitized_string;
print STDERR $line, "\n";
if ($line =~ m{(;|\\G|\\c)\s*$} || $line =~ m{^\s*(quit|exit)\s*$} || $line =~ m{^\s*$}) {
return 1;
}
else {
return 0;
}
}

1;
4 changes: 2 additions & 2 deletions lib/App/AltSQL/Term.pm
Original file line number Diff line number Diff line change
Expand Up @@ -79,9 +79,9 @@ sub _build_term {
sub return_key {
my $self = shift;

## The user has pressed the 'enter' key. If the buffer ends in ';' or '\G', or if they've typed the bare word 'quit' or 'exit', accept the buffer
my $input = join ' ', @{ $self->term->{lines} };
if ($input =~ m{(;|\\G|\\c)\s*$} || $input =~ m{^\s*(quit|exit)\s*$} || $input =~ m{^\s*$}) {
my $ret = $self->app->model->parse_sql_line($input);
if ($ret eq '1') {
$self->term->accept_line();
}
else {
Expand Down
39 changes: 39 additions & 0 deletions t/005_sql_parse_line.t
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
use strict;
use warnings;
use Test::More;
use Test::Deep;
use App::AltSQL::Model;

ok my $model = App::AltSQL::Model->new(app => 1);

is $model->parse_sql_line('test'), 0, 'Incomplete statement';

is $model->parse_sql_line('test;'), 1, 'Semicolon completes statement';

is $model->parse_sql_line('quit'), 1, 'quit statement';

is $model->parse_sql_line('exit'), 1, 'exit statement';

is $model->parse_sql_line(' '), 1, 'blank space statement';

is $model->parse_sql_line('test\G'), 1, '\G statement';

is $model->parse_sql_line('test\c'), 1, '\c statement';

is $model->parse_sql_line('select * from film where title = ";'), '"', 'Semi colon in string';
is $model->parse_sql_line('";', '"'), 1, 'Tail end of statement where we were in a string';

is $model->parse_sql_line('insert into mytab values (";",'), 0, 'Incomplete statement';

is $model->parse_sql_line(q{select * from film where title = '\';}), "'", 'Semi colon in string';

is $model->parse_sql_line(q{select * from film where title = "\";}), '"', 'Semi colon in string';

is $model->parse_sql_line(q{select * from film where title = "\\\\";}), 1, 'Escaped slash, terminated string and end of statement';

is $model->parse_sql_line(q{select * from film where title = /* "\";}), '/*', 'Semi colon in comment';
is $model->parse_sql_line(q{*/ 'test';}, '/*'), 1, 'Statement terminated after comment closes';

is $model->parse_sql_line(q{select 'test'; -- a simple statement}), 1, 'Statement terminated Got a comment after';

done_testing;

0 comments on commit d6082e5

Please sign in to comment.