From 24705cf36084f46c87946a8a284e85b7dad2b507 Mon Sep 17 00:00:00 2001 From: Sebastian Podjasek Date: Thu, 9 Apr 2015 01:40:35 +0200 Subject: [PATCH 1/8] Fixes Bug #63814: Allow for index creation with USING and WHERE keywords supplied as add_index options --- lib/SQL/Translator/Producer/PostgreSQL.pm | 22 +++++++++++++++++++++- t/47postgres-producer.t | 8 ++++++++ 2 files changed, 29 insertions(+), 1 deletion(-) diff --git a/lib/SQL/Translator/Producer/PostgreSQL.pm b/lib/SQL/Translator/Producer/PostgreSQL.pm index fb2f9d58d..59ec190a6 100644 --- a/lib/SQL/Translator/Producer/PostgreSQL.pm +++ b/lib/SQL/Translator/Producer/PostgreSQL.pm @@ -543,6 +543,23 @@ sub create_index my @fields = $index->fields; return unless @fields; + my $index_using = ''; + my $index_where = ''; + for my $opt ( $index->options ) { + if ( ref $opt eq 'HASH' ) { + foreach my $key (keys %$opt) { + my $value = $opt->{$key}; + next unless defined $value; + if ( $key =~ /using/i ) { + $index_using = "USING $value"; + } + elsif ( $key =~ /where/i ) { + $index_where = "WHERE $value"; + } + } + } + } + my $def_start = 'CONSTRAINT ' . $generator->quote($name) . ' '; my $field_names = '(' . join(", ", (map { $_ =~ /\(.*\)/ ? $_ : ( $generator->quote($_) ) } @fields)) . ')'; if ( $type eq PRIMARY_KEY ) { @@ -553,7 +570,10 @@ sub create_index } elsif ( $type eq NORMAL ) { $index_def = - 'CREATE INDEX ' . $generator->quote($name) . ' on ' . $generator->quote($table_name) . ' ' . $field_names + 'CREATE INDEX ' . $generator->quote($name) . ' on ' . $generator->quote($table_name) . + ($index_using ne '' ? ' ' . $index_using : '') . + ' ' . $field_names . + ($index_where ne '' ? ' ' . $index_where : '') ; } else { diff --git a/t/47postgres-producer.t b/t/47postgres-producer.t index 3e0ac9053..060f71b86 100644 --- a/t/47postgres-producer.t +++ b/t/47postgres-producer.t @@ -627,6 +627,14 @@ is($view2_sql1, $view2_sql_replace, 'correct "CREATE OR REPLACE VIEW" SQL 2'); ($def) = SQL::Translator::Producer::PostgreSQL::create_constraint($constr, $quote); is($def->[0], 'CONSTRAINT "constr" UNIQUE ("bar", lower(foo))', 'constraint created w/ quotes'); } + + { + my $index = $table->add_index(name => 'myindex', options => [{using => 'hash'}, {where => 'predicate'}], fields => ['bar', 'lower(foo)']); + my ($def) = SQL::Translator::Producer::PostgreSQL::create_index($index); + is($def, "CREATE INDEX myindex on foobar USING hash (bar, lower(foo)) WHERE predicate", 'index using & where created'); + ($def) = SQL::Translator::Producer::PostgreSQL::create_index($index, $quote); + is($def, 'CREATE INDEX "myindex" on "foobar" USING hash ("bar", lower(foo)) WHERE predicate', 'index using & where created w/ quotes'); + } } my $drop_view_opts1 = { add_drop_view => 1, no_comments => 1, postgres_version => 8.001 }; From 27441303d2710a28ff9275cc57f087fddb8b29ef Mon Sep 17 00:00:00 2001 From: Sebastian Podjasek Date: Wed, 24 Jun 2015 22:40:06 +0200 Subject: [PATCH 2/8] Remove regexp matching --- lib/SQL/Translator/Producer/PostgreSQL.pm | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/SQL/Translator/Producer/PostgreSQL.pm b/lib/SQL/Translator/Producer/PostgreSQL.pm index 59ec190a6..568015b94 100644 --- a/lib/SQL/Translator/Producer/PostgreSQL.pm +++ b/lib/SQL/Translator/Producer/PostgreSQL.pm @@ -550,10 +550,10 @@ sub create_index foreach my $key (keys %$opt) { my $value = $opt->{$key}; next unless defined $value; - if ( $key =~ /using/i ) { + if ( $key eq 'using' ) { $index_using = "USING $value"; } - elsif ( $key =~ /where/i ) { + elsif ( $key eq 'where' ) { $index_where = "WHERE $value"; } } From c5b8904a2ea32dd974067d4dc3f0254085f9fd8c Mon Sep 17 00:00:00 2001 From: Sebastian Podjasek Date: Wed, 24 Jun 2015 23:36:52 +0200 Subject: [PATCH 3/8] Usage clarification --- t/47postgres-producer.t | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/t/47postgres-producer.t b/t/47postgres-producer.t index 060f71b86..bf57a015c 100644 --- a/t/47postgres-producer.t +++ b/t/47postgres-producer.t @@ -629,11 +629,11 @@ is($view2_sql1, $view2_sql_replace, 'correct "CREATE OR REPLACE VIEW" SQL 2'); } { - my $index = $table->add_index(name => 'myindex', options => [{using => 'hash'}, {where => 'predicate'}], fields => ['bar', 'lower(foo)']); + my $index = $table->add_index(name => 'myindex', options => [{using => 'hash'}, {where => "upper(foo) = 'bar' AND bar = 'foo'"}], fields => ['bar', 'lower(foo)']); my ($def) = SQL::Translator::Producer::PostgreSQL::create_index($index); - is($def, "CREATE INDEX myindex on foobar USING hash (bar, lower(foo)) WHERE predicate", 'index using & where created'); + is($def, "CREATE INDEX myindex on foobar USING hash (bar, lower(foo)) WHERE upper(foo) = 'bar' AND bar = 'foo'", 'index using & where created'); ($def) = SQL::Translator::Producer::PostgreSQL::create_index($index, $quote); - is($def, 'CREATE INDEX "myindex" on "foobar" USING hash ("bar", lower(foo)) WHERE predicate', 'index using & where created w/ quotes'); + is($def, 'CREATE INDEX "myindex" on "foobar" USING hash ("bar", lower(foo)) WHERE upper(foo) = \'bar\' AND bar = \'foo\'', 'index using & where created w/ quotes'); } } From 688d29c88ab2a148e6b2a872f95a37de9a6db4ae Mon Sep 17 00:00:00 2001 From: Sebastian Podjasek Date: Thu, 25 Jun 2015 00:21:02 +0200 Subject: [PATCH 4/8] Remove excessive conditionals --- lib/SQL/Translator/Producer/PostgreSQL.pm | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/lib/SQL/Translator/Producer/PostgreSQL.pm b/lib/SQL/Translator/Producer/PostgreSQL.pm index 568015b94..823f91ca6 100644 --- a/lib/SQL/Translator/Producer/PostgreSQL.pm +++ b/lib/SQL/Translator/Producer/PostgreSQL.pm @@ -571,10 +571,7 @@ sub create_index elsif ( $type eq NORMAL ) { $index_def = 'CREATE INDEX ' . $generator->quote($name) . ' on ' . $generator->quote($table_name) . - ($index_using ne '' ? ' ' . $index_using : '') . - ' ' . $field_names . - ($index_where ne '' ? ' ' . $index_where : '') - ; + ' ' . $index_using . ' ' . $field_names . ' ' . $index_where; } else { warn "Unknown index type ($type) on table $table_name.\n" From 4d7fde245d2a4fda13459aedd286f6f5461b2a98 Mon Sep 17 00:00:00 2001 From: Sebastian Podjasek Date: Thu, 25 Jun 2015 00:24:09 +0200 Subject: [PATCH 5/8] Make case-insensitive --- lib/SQL/Translator/Producer/PostgreSQL.pm | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/SQL/Translator/Producer/PostgreSQL.pm b/lib/SQL/Translator/Producer/PostgreSQL.pm index 823f91ca6..e1e2b3b6f 100644 --- a/lib/SQL/Translator/Producer/PostgreSQL.pm +++ b/lib/SQL/Translator/Producer/PostgreSQL.pm @@ -550,10 +550,10 @@ sub create_index foreach my $key (keys %$opt) { my $value = $opt->{$key}; next unless defined $value; - if ( $key eq 'using' ) { + if ( uc($key) eq 'USING' ) { $index_using = "USING $value"; } - elsif ( $key eq 'where' ) { + elsif ( uc($key) eq 'WHERE' ) { $index_where = "WHERE $value"; } } From ceffd8ac5bbeb52fcafa8d0a6ea1419b3cb21ee7 Mon Sep 17 00:00:00 2001 From: Sebastian Podjasek Date: Thu, 25 Jun 2015 08:47:17 +0200 Subject: [PATCH 6/8] Fix 688d29c to not add spaces where not needed --- lib/SQL/Translator/Producer/PostgreSQL.pm | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/SQL/Translator/Producer/PostgreSQL.pm b/lib/SQL/Translator/Producer/PostgreSQL.pm index e1e2b3b6f..46a5dd37d 100644 --- a/lib/SQL/Translator/Producer/PostgreSQL.pm +++ b/lib/SQL/Translator/Producer/PostgreSQL.pm @@ -543,8 +543,8 @@ sub create_index my @fields = $index->fields; return unless @fields; - my $index_using = ''; - my $index_where = ''; + my $index_using; + my $index_where; for my $opt ( $index->options ) { if ( ref $opt eq 'HASH' ) { foreach my $key (keys %$opt) { @@ -570,8 +570,8 @@ sub create_index } elsif ( $type eq NORMAL ) { $index_def = - 'CREATE INDEX ' . $generator->quote($name) . ' on ' . $generator->quote($table_name) . - ' ' . $index_using . ' ' . $field_names . ' ' . $index_where; + 'CREATE INDEX ' . $generator->quote($name) . ' on ' . $generator->quote($table_name) . ' ' . + join(' ', grep {defined} ($index_using, $field_names, $index_where)); } else { warn "Unknown index type ($type) on table $table_name.\n" From 5615c90ce811ed06ec17845202225d2d6b528cb1 Mon Sep 17 00:00:00 2001 From: Sebastian Podjasek Date: Fri, 26 Jun 2015 15:40:53 +0200 Subject: [PATCH 7/8] Make it nicer... --- lib/SQL/Translator/Producer/PostgreSQL.pm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/SQL/Translator/Producer/PostgreSQL.pm b/lib/SQL/Translator/Producer/PostgreSQL.pm index 46a5dd37d..b344e00a2 100644 --- a/lib/SQL/Translator/Producer/PostgreSQL.pm +++ b/lib/SQL/Translator/Producer/PostgreSQL.pm @@ -571,7 +571,7 @@ sub create_index elsif ( $type eq NORMAL ) { $index_def = 'CREATE INDEX ' . $generator->quote($name) . ' on ' . $generator->quote($table_name) . ' ' . - join(' ', grep {defined} ($index_using, $field_names, $index_where)); + join ' ', grep {defined} ($index_using, $field_names, $index_where); } else { warn "Unknown index type ($type) on table $table_name.\n" From 33c790414771b5f64055936b2d90d113ea03b131 Mon Sep 17 00:00:00 2001 From: Sebastian Podjasek Date: Fri, 26 Jun 2015 15:43:07 +0200 Subject: [PATCH 8/8] Let Parser::PostgreSQL handle CREATE INDEX with USING & WHERE keywords --- lib/SQL/Translator/Parser/PostgreSQL.pm | 13 ++++++++---- t/14postgres-parser.t | 28 +++++++++++++++++++++++++ 2 files changed, 37 insertions(+), 4 deletions(-) diff --git a/lib/SQL/Translator/Parser/PostgreSQL.pm b/lib/SQL/Translator/Parser/PostgreSQL.pm index ce3cfbb05..e8c270193 100644 --- a/lib/SQL/Translator/Parser/PostgreSQL.pm +++ b/lib/SQL/Translator/Parser/PostgreSQL.pm @@ -247,7 +247,8 @@ create : CREATE unique(?) /(index|key)/i index_name /on/i table_id using_method( supertype => $item{'unique'}[0] ? 'constraint' : 'index', type => $item{'unique'}[0] ? 'unique' : 'normal', fields => $item[9], - method => $item{'using_method'}[0], + method => $item{'using_method(?)'}[0], + where => $item{'where_predicate(?)'}[0], } ; } @@ -1075,10 +1076,14 @@ sub parse { } for my $idata ( @{ $tdata->{'indices'} || [] } ) { + my @options = (); + push @options, { using => $idata->{'method'} } if $idata->{method}; + push @options, { where => $idata->{'where'} } if $idata->{where}; my $index = $table->add_index( - name => $idata->{'name'}, - type => uc $idata->{'type'}, - fields => $idata->{'fields'}, + name => $idata->{'name'}, + type => uc $idata->{'type'}, + fields => $idata->{'fields'}, + options => \@options ) or die $table->error . ' ' . $table->name; } diff --git a/t/14postgres-parser.t b/t/14postgres-parser.t index d5a02dded..04f30febb 100644 --- a/t/14postgres-parser.t +++ b/t/14postgres-parser.t @@ -68,6 +68,12 @@ my $sql = q{ FOR EACH ROW EXECUTE PROCEDURE foo(); + CREATE INDEX test_index1 ON t_test1 (f_varchar); + + CREATE INDEX test_index2 ON t_test1 USING hash (f_char, f_bool); + + CREATE INDEX test_index3 ON t_test1 USING hash (f_bigint, f_tz) WHERE f_bigint = '1' AND f_tz IS NULL; + alter table t_test1 add f_fk2 integer; alter table only t_test1 add constraint c_u1 unique (f_varchar); @@ -350,4 +356,26 @@ is( $trigger->perform_action_when, 'before', "Correct time for trigger"); is( $trigger->scope, 'row', "Correct scope for trigger"); is( $trigger->action, 'EXECUTE PROCEDURE foo()', "Correct action for trigger"); +# test index +my @indices = $t1->get_indices; +is(scalar @indices, 3, 'got three indexes'); + +my $t1_i1 = $indices[0]; +is( $t1_i1->name, 'test_index1', 'First index is "test_index1"' ); +is( join(',', $t1_i1->fields), 'f_varchar', 'Index is on field "f_varchar"' ); +is( join(',', map { keys %$_ } $t1_i1->options), '', 'Index is has no options' ); + +my $t1_i2 = $indices[1]; +is( $t1_i2->name, 'test_index2', 'Second index is "test_index2"' ); +is( join(',', $t1_i2->fields), 'f_char,f_bool', 'Index is on fields "f_char, f_bool"' ); +is( join(',', map { keys %$_ } $t1_i2->options), 'using', 'Index is has one option' ); +is( $t1_i2->options->[0]->{using}, 'hash', 'Index is using HASH method' ); + +my $t1_i3 = $indices[2]; +is( $t1_i3->name, 'test_index3', 'Third index is "test_index3"' ); +is( join(',', $t1_i3->fields), 'f_bigint,f_tz', 'Index is on fields "f_bigint, f_tz"' ); +is( join(',', map { keys %$_ } $t1_i3->options), 'using,where', 'Index is has both options' ); +is( $t1_i3->options->[0]->{using}, 'hash', 'Index is using HASH method' ); +is( $t1_i3->options->[1]->{where}, "f_bigint = '1' AND f_tz IS NULL", 'Index predicate is right' ); + done_testing;