Document new roles, types and utility functions
[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
9593ed04 104has name => (
105 is => 'rw',
106 coerce => quote_sub(q{ defined $_[0] ? $_[0] : '' }),
107 default => quote_sub(q{ '' }),
108);
25868dc9 109
110=head2 options
111
112Get or set the index's options (e.g., "using" or "where" for PG). Returns
113an array or array reference.
114
115 my @options = $index->options;
116
117=cut
118
0fb58589 119with ListAttr options => ();
43b9dc7a 120
121=head2 table
122
123Get or set the index's table object.
124
125 my $table = $index->table;
126
127=cut
128
a5bfeba8 129has table => ( is => 'rw', isa => schema_obj('Table'), weak_ref => 1 );
43b9dc7a 130
45287c81 131around table => \&ex2err;
3c5de62a 132
133=head2 type
134
135Get or set the index's type.
136
137 my $type = $index->type('unique');
138
d06db857 139Get or set the index's type.
19ad0cee 140
141Currently there are only four acceptable types: UNIQUE, NORMAL, FULL_TEXT,
142and SPATIAL. The latter two might be MySQL-specific. While both lowercase
143and uppercase types are acceptable input, this method returns the type in
144uppercase.
145
3c5de62a 146=cut
147
45287c81 148has type => (
149 is => 'rw',
150 isa => sub {
151 my $type = uc $_[0] or return;
152 throw("Invalid index type: $type") unless $VALID_INDEX_TYPE{$type};
153 },
c804300c 154 coerce => quote_sub(q{ uc $_[0] }),
68d75205 155 default => quote_sub(q{ 'NORMAL' }),
45287c81 156);
3c5de62a 157
45287c81 158around type => \&ex2err;
abf315bb 159
160=head2 equals
161
162Determines if this index is the same as another
163
164 my $isIdentical = $index1->equals( $index2 );
165
166=cut
167
2cb3ea55 168around equals => sub {
169 my $orig = shift;
abf315bb 170 my $self = shift;
171 my $other = shift;
172 my $case_insensitive = shift;
d990d84b 173 my $ignore_index_names = shift;
ea93df61 174
2cb3ea55 175 return 0 unless $self->$orig($other);
da5a1bae 176
d990d84b 177 unless ($ignore_index_names) {
da5a1bae 178 unless ((!$self->name && ($other->name eq $other->fields->[0])) ||
179 (!$other->name && ($self->name eq $self->fields->[0]))) {
d990d84b 180 return 0 unless $case_insensitive ? uc($self->name) eq uc($other->name) : $self->name eq $other->name;
da5a1bae 181 }
d990d84b 182 }
82f6b50e 183 #return 0 unless $self->is_valid eq $other->is_valid;
abf315bb 184 return 0 unless $self->type eq $other->type;
ea93df61 185
c243ec2b 186 # Check fields, regardless of order
ea93df61 187 my %otherFields = (); # create a hash of the other fields
c243ec2b 188 foreach my $otherField ($other->fields) {
ea93df61 189 $otherField = uc($otherField) if $case_insensitive;
190 $otherFields{$otherField} = 1;
c243ec2b 191 }
192 foreach my $selfField ($self->fields) { # check for self fields in hash
ea93df61 193 $selfField = uc($selfField) if $case_insensitive;
194 return 0 unless $otherFields{$selfField};
195 delete $otherFields{$selfField};
c243ec2b 196 }
197 # Check all other fields were accounted for
198 return 0 unless keys %otherFields == 0;
199
4598b71c 200 return 0 unless $self->_compare_objects(scalar $self->options, scalar $other->options);
201 return 0 unless $self->_compare_objects(scalar $self->extra, scalar $other->extra);
abf315bb 202 return 1;
2cb3ea55 203};
3c5de62a 204
45287c81 205# Must come after all 'has' declarations
206around new => \&ex2err;
207
3c5de62a 2081;
209
3c5de62a 210=pod
211
212=head1 AUTHOR
213
c3b0b535 214Ken Youens-Clark E<lt>kclark@cpan.orgE<gt>.
3c5de62a 215
216=cut