Skip to content

Commit

Permalink
finish support + test EXCLUDE on PG
Browse files Browse the repository at this point in the history
  • Loading branch information
rabbiveesh committed Dec 22, 2023
1 parent adc0035 commit 6554d97
Show file tree
Hide file tree
Showing 2 changed files with 64 additions and 32 deletions.
80 changes: 48 additions & 32 deletions lib/SQL/Translator/Producer/PostgreSQL.pm
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,7 @@ and table_constraint is:
{ UNIQUE ( column_name [, ... ] ) |
PRIMARY KEY ( column_name [, ... ] ) |
CHECK ( expression ) |
EXCLUDE [USING acc_method] (expression) [INCLUDE (column [, ...])] [WHERE (predicate)]
FOREIGN KEY ( column_name [, ... ] ) REFERENCES reftable [ ( refcolumn [, ... ] ) ]
[ MATCH FULL | MATCH PARTIAL ] [ ON DELETE action ] [ ON UPDATE action ] }
[ DEFERRABLE | NOT DEFERRABLE ] [ INITIALLY DEFERRED | INITIALLY IMMEDIATE ]
Expand Down Expand Up @@ -615,6 +616,19 @@ sub create_geometry_constraints {
return @constraints;
}

sub _extract_extras_from_options {
my ($options_haver, $dispatcher) = @_;
for my $opt ($options_haver->options) {
if (ref $opt eq 'HASH') {
for my $key (keys %$opt) {
my $val = $opt->{$key};
next unless defined $val;
$dispatcher->{lc $key}->($val);
}
}
}
}

{
my %index_name;
sub create_index
Expand All @@ -636,26 +650,17 @@ sub create_geometry_constraints {
return unless @fields;

my %index_extras;
for my $opt ( $index->options ) {
if ( ref $opt eq 'HASH' ) {
foreach my $key (keys %$opt) {
my $value = $opt->{$key};
next unless defined $value;
if ( uc($key) eq 'USING' ) {
$index_extras{using} = "USING $value";
}
elsif ( uc($key) eq 'WHERE' ) {
$index_extras{where} = "WHERE $value";
}
elsif ( uc($key) eq 'INCLUDE' ) {
next unless $postgres_version >= 11;
die 'Include list must be an arrayref' unless ref $value eq 'ARRAY';
my $value_list = join ', ', @$value;
$index_extras{include} = "INCLUDE ($value_list)"
}
}
_extract_extras_from_options($index, {
using => sub { $index_extras{using} = "USING $_[0]" },
where => sub { $index_extras{where} = "WHERE $_[0]" },
include => sub {
my ($value) = @_;
return unless $postgres_version >= 11;
die 'Include list must be an arrayref' unless ref $value eq 'ARRAY';
my $value_list = join ', ', @$value;
$index_extras{include} = "INCLUDE ($value_list)"
}
}
});

my $def_start = 'CONSTRAINT ' . $generator->quote($name) . ' ';
my $field_names = '(' . join(", ", (map { $_ =~ /\(.*\)/ ? $_ : ( $generator->quote($_) ) } @fields)) . ')';
Expand Down Expand Up @@ -684,31 +689,45 @@ sub create_constraint
my ($c, $options) = @_;

my $generator = _generator($options);
my $postgres_version = $options->{postgres_version} || 0;
my $table_name = $c->table->name;
my (@constraint_defs, @fks);
my %constraint_extras;
_extract_extras_from_options($c, {
using => sub { $constraint_extras{using} = "USING $_[0]" },
where => sub { $constraint_extras{where} = "WHERE ( $_[0] )" },
include => sub {
my ($value) = @_;
return unless $postgres_version >= 11;
die 'Include list must be an arrayref' unless ref $value eq 'ARRAY';
my $value_list = join ', ', @$value;
$constraint_extras{include} = "INCLUDE ( $value_list )"
},
});

my $name = $c->name || '';

my @fields = grep { defined } $c->fields;

my @rfields = grep { defined } $c->reference_fields;

next if !@fields && $c->type ne CHECK_C;
my $def_start = $name ? 'CONSTRAINT ' . $generator->quote($name) . ' ' : '';
return if !@fields && ($c->type ne CHECK_C && $c->type ne EXCLUDE);
my $def_start = $name ? 'CONSTRAINT ' . $generator->quote($name) : '';
my $field_names = '(' . join(", ", (map { $_ =~ /\(.*\)/ ? $_ : ( $generator->quote($_) ) } @fields)) . ')';
my $include = $constraint_extras{include} || '';
if ( $c->type eq PRIMARY_KEY ) {
push @constraint_defs, "${def_start}PRIMARY KEY ".$field_names;
push @constraint_defs, join ' ', grep $_, $def_start, "PRIMARY KEY", $field_names, $include;
}
elsif ( $c->type eq UNIQUE ) {
push @constraint_defs, "${def_start}UNIQUE " .$field_names;
push @constraint_defs, join ' ', grep $_, $def_start, "UNIQUE", $field_names, $include;
}
elsif ( $c->type eq CHECK_C ) {
my $expression = $c->expression;
push @constraint_defs, "${def_start}CHECK ($expression)";
push @constraint_defs, join ' ', grep $_, $def_start, "CHECK ($expression)";
}
elsif ( $c->type eq FOREIGN_KEY ) {
my $def .= "ALTER TABLE " . $generator->quote($table_name) . " ADD ${def_start}FOREIGN KEY $field_names"
. "\n REFERENCES " . $generator->quote($c->reference_table);
my $def .= join ' ', grep $_, "ALTER TABLE", $generator->quote($table_name), 'ADD', $def_start, "FOREIGN KEY $field_names";
$def .= "\n REFERENCES " . $generator->quote($c->reference_table);

if ( @rfields ) {
$def .= ' (' . join( ', ', map { $generator->quote($_) } @rfields ) . ')';
Expand All @@ -734,15 +753,12 @@ sub create_constraint
push @fks, "$def";
}
elsif( $c->type eq EXCLUDE ) {
my $using = $c->options->[0]{ using };
$using = $using ? "USING $using" : '';
my $using = $constraint_extras{using} || '';
my $expression = $c->expression;
my $where = $c->options->[0]{ where };
$where = $where ? "WHERE $where" : '';
push @constraint_defs, "$def_start EXCLUDE $using ( $expression ) $where";
my $where = $constraint_extras{where} || '';
push @constraint_defs, join ' ', grep $_, $def_start, 'EXCLUDE', $using, "( $expression )", $include, $where;
}


return \@constraint_defs, \@fks;
}

Expand Down
16 changes: 16 additions & 0 deletions t/47postgres-producer.t
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,22 @@ for my $name ( 'foo', undef ) {
}
}

subtest 'exclude constraints' => sub {
my $exclude_constraint = SQL::Translator::Schema::Constraint->new(
table => $table,
name => 'game',
expression => 'dates WITH &&, char with =',
options => {
using => 'gist',
where => 'fun IS TRUE',
},
type => 'EXCLUDE',
);
my ($def, undef) = SQL::Translator::Producer::PostgreSQL::create_constraint($exclude_constraint);
is $def->[0], 'CONSTRAINT game EXCLUDE USING gist ( dates WITH &&, char with = ) WHERE ( fun IS TRUE )', 'exclude constraint';

};

my $alter_field = SQL::Translator::Producer::PostgreSQL::alter_field($field1,
$field2);
is($alter_field, qq[ALTER TABLE mytable ALTER COLUMN myfield SET NOT NULL;
Expand Down

0 comments on commit 6554d97

Please sign in to comment.