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