Mooify SQL::Translator
[dbsrgits/SQL-Translator.git] / lib / SQL / Translator / Schema / Index.pm
CommitLineData
3c5de62a 1package SQL::Translator::Schema::Index;
2
3c5de62a 3=pod
4
5=head1 NAME
6
7SQL::Translator::Schema::Index - SQL::Translator index object
8
9=head1 SYNOPSIS
10
11 use SQL::Translator::Schema::Index;
12 my $index = SQL::Translator::Schema::Index->new(
13 name => 'foo',
14 fields => [ id ],
15 type => 'unique',
16 );
17
18=head1 DESCRIPTION
19
20C<SQL::Translator::Schema::Index> is the index object.
21
b54199ae 22Primary and unique keys are table constraints, not indices.
3c5de62a 23
24=head1 METHODS
25
26=cut
27
2cb3ea55 28use Moo;
25868dc9 29use SQL::Translator::Schema::Constants;
45287c81 30use SQL::Translator::Utils qw(parse_list_arg ex2err throw);
31use SQL::Translator::Types qw(schema_obj);
2cb3ea55 32use List::MoreUtils qw(uniq);
3c5de62a 33
954ed12e 34extends 'SQL::Translator::Schema::Object';
b6a880d1 35
0c04c5a2 36our $VERSION = '1.59';
3c5de62a 37
11ad2df9 38my %VALID_INDEX_TYPE = (
39 UNIQUE => 1,
40 NORMAL => 1,
41 FULLTEXT => 1, # MySQL only (?)
42 FULL_TEXT => 1, # MySQL only (?)
43 SPATIAL => 1, # MySQL only (?)
c8efc003 44);
3c5de62a 45
3c5de62a 46=head2 new
47
48Object constructor.
49
50 my $schema = SQL::Translator::Schema::Index->new;
51
3c5de62a 52=head2 fields
53
b54199ae 54Gets and set the fields the index is on. Accepts a string, list or
25868dc9 55arrayref; returns an array or array reference. Will unique the field
56names and keep them in order by the first occurrence of a field name.
3c5de62a 57
b54199ae 58 $index->fields('id');
59 $index->fields('id', 'name');
60 $index->fields( 'id, name' );
61 $index->fields( [ 'id', 'name' ] );
62 $index->fields( qw[ id name ] );
25868dc9 63
b54199ae 64 my @fields = $index->fields;
3c5de62a 65
66=cut
67
2cb3ea55 68has fields => (
69 is => 'rw',
70 default => sub { [] },
71 coerce => sub { [uniq @{parse_list_arg($_[0])}] },
72);
73
74around fields => sub {
75 my $orig = shift;
3c5de62a 76 my $self = shift;
25868dc9 77 my $fields = parse_list_arg( @_ );
2cb3ea55 78 $self->$orig($fields) if @$fields;
3c5de62a 79
2cb3ea55 80 return wantarray ? @{ $self->$orig } : $self->$orig;
81};
3c5de62a 82
b54199ae 83sub is_valid {
84
85=pod
86
87=head2 is_valid
88
89Determine whether the index is valid or not.
90
91 my $ok = $index->is_valid;
92
93=cut
94
95 my $self = shift;
96 my $table = $self->table or return $self->error('No table');
97 my @fields = $self->fields or return $self->error('No fields');
98
99 for my $field ( @fields ) {
100 return $self->error(
101 "Field '$field' does not exist in table '", $table->name, "'"
102 ) unless $table->get_field( $field );
103 }
104
105 return 1;
106}
107
3c5de62a 108=head2 name
109
110Get or set the index's name.
111
112 my $name = $index->name('foo');
113
114=cut
115
2cb3ea55 116has name => ( is => 'rw', coerce => sub { defined $_[0] ? $_[0] : '' }, default => sub { '' } );
25868dc9 117
118=head2 options
119
120Get or set the index's options (e.g., "using" or "where" for PG). Returns
121an array or array reference.
122
123 my @options = $index->options;
124
125=cut
126
2cb3ea55 127has options => (
128 is => 'rw',
129 default => sub { [] },
45287c81 130 coerce => \&parse_list_arg,
2cb3ea55 131);
132
133around options => sub {
134 my $orig = shift;
25868dc9 135 my $self = shift;
136 my $options = parse_list_arg( @_ );
137
2cb3ea55 138 push @{ $self->$orig }, @$options;
43b9dc7a 139
2cb3ea55 140 return wantarray ? @{ $self->$orig } : $self->$orig;
141};
43b9dc7a 142
143=head2 table
144
145Get or set the index's table object.
146
147 my $table = $index->table;
148
149=cut
150
a5bfeba8 151has table => ( is => 'rw', isa => schema_obj('Table'), weak_ref => 1 );
43b9dc7a 152
45287c81 153around table => \&ex2err;
3c5de62a 154
155=head2 type
156
157Get or set the index's type.
158
159 my $type = $index->type('unique');
160
d06db857 161Get or set the index's type.
19ad0cee 162
163Currently there are only four acceptable types: UNIQUE, NORMAL, FULL_TEXT,
164and SPATIAL. The latter two might be MySQL-specific. While both lowercase
165and uppercase types are acceptable input, this method returns the type in
166uppercase.
167
3c5de62a 168=cut
169
45287c81 170has type => (
171 is => 'rw',
172 isa => sub {
173 my $type = uc $_[0] or return;
174 throw("Invalid index type: $type") unless $VALID_INDEX_TYPE{$type};
175 },
176 coerce => sub { uc $_[0] },
177 default => sub { 'NORMAL' },
178);
3c5de62a 179
45287c81 180around type => \&ex2err;
abf315bb 181
182=head2 equals
183
184Determines if this index is the same as another
185
186 my $isIdentical = $index1->equals( $index2 );
187
188=cut
189
2cb3ea55 190around equals => sub {
191 my $orig = shift;
abf315bb 192 my $self = shift;
193 my $other = shift;
194 my $case_insensitive = shift;
d990d84b 195 my $ignore_index_names = shift;
ea93df61 196
2cb3ea55 197 return 0 unless $self->$orig($other);
da5a1bae 198
d990d84b 199 unless ($ignore_index_names) {
da5a1bae 200 unless ((!$self->name && ($other->name eq $other->fields->[0])) ||
201 (!$other->name && ($self->name eq $self->fields->[0]))) {
d990d84b 202 return 0 unless $case_insensitive ? uc($self->name) eq uc($other->name) : $self->name eq $other->name;
da5a1bae 203 }
d990d84b 204 }
82f6b50e 205 #return 0 unless $self->is_valid eq $other->is_valid;
abf315bb 206 return 0 unless $self->type eq $other->type;
ea93df61 207
c243ec2b 208 # Check fields, regardless of order
ea93df61 209 my %otherFields = (); # create a hash of the other fields
c243ec2b 210 foreach my $otherField ($other->fields) {
ea93df61 211 $otherField = uc($otherField) if $case_insensitive;
212 $otherFields{$otherField} = 1;
c243ec2b 213 }
214 foreach my $selfField ($self->fields) { # check for self fields in hash
ea93df61 215 $selfField = uc($selfField) if $case_insensitive;
216 return 0 unless $otherFields{$selfField};
217 delete $otherFields{$selfField};
c243ec2b 218 }
219 # Check all other fields were accounted for
220 return 0 unless keys %otherFields == 0;
221
4598b71c 222 return 0 unless $self->_compare_objects(scalar $self->options, scalar $other->options);
223 return 0 unless $self->_compare_objects(scalar $self->extra, scalar $other->extra);
abf315bb 224 return 1;
2cb3ea55 225};
3c5de62a 226
45287c81 227# Must come after all 'has' declarations
228around new => \&ex2err;
229
3c5de62a 2301;
231
3c5de62a 232=pod
233
234=head1 AUTHOR
235
c3b0b535 236Ken Youens-Clark E<lt>kclark@cpan.orgE<gt>.
3c5de62a 237
238=cut