comparison lib/IMPL/SQL/Schema/Table.pm @ 407:c6e90e02dd17 ref20150831

renamed Lib->lib
author cin
date Fri, 04 Sep 2015 19:40:23 +0300
parents
children
comparison
equal deleted inserted replaced
406:f23fcb19d3c1 407:c6e90e02dd17
1 package IMPL::SQL::Schema::Table;
2 use strict;
3
4 use IMPL::lang qw(is);
5 use IMPL::Const qw(:prop);
6 use IMPL::declare {
7 base => [
8 'IMPL::Object' => undef,
9 'IMPL::Object::Disposable' => undef
10 ],
11 props => [
12 name => PROP_RO | PROP_DIRECT,
13 schema => PROP_RO | PROP_DIRECT,
14 columns => PROP_RO | PROP_DIRECT,
15 constraints => PROP_RO | PROP_DIRECT,
16 columnsByName => PROP_RO | PROP_DIRECT,
17 primaryKey => PROP_RO | PROP_DIRECT,
18 tag => PROP_RW | PROP_DIRECT,
19 ]
20 };
21
22 require IMPL::SQL::Schema::Column;
23 require IMPL::SQL::Schema::Constraint;
24 require IMPL::SQL::Schema::Constraint::PrimaryKey;
25 require IMPL::SQL::Schema::Constraint::ForeignKey;
26
27 sub CTOR {
28 my ($this,%args) = @_;
29
30 $this->{$name} = $args{'name'} or die new IMPL::InvalidArgumentException('a table name is required');
31 $this->{$schema} = $args{'schema'} or die new IMPL::InvalidArgumentException('a parent schema is required');
32
33 if ($args{columns}) {
34 die new IMPL::InvalidOperationException('A columns property should be a reference to an array') unless ref $args{columns} eq 'ARRAY';
35
36 $this->InsertColumn($_) foreach @{$args{columns}};
37 }
38 }
39
40 sub InsertColumn {
41 my ($this,$column,$index) = @_;
42
43 $index = ($this->{$columns} ? scalar(@{$this->{$columns}}) : 0) if not defined $index;
44
45 die new IMPL::InvalidArgumentException("The index is out of range") if ($index < 0 || $index > ($this->{$columns} ? scalar(@{$this->{$columns}}) : 0));
46
47 if (UNIVERSAL::isa($column,'IMPL::SQL::Schema::Column')) {
48
49 } elsif (UNIVERSAL::isa($column,'HASH')) {
50 $column = new IMPL::SQL::Schema::Column(%{$column});
51 } else {
52 die new IMPL::InvalidArgumentException("The invalid column parameter");
53 }
54
55 if (exists $this->{$columnsByName}->{$column->name}) {
56 die new IMPL::InvalidOperationException("The column already exists",$column->name);
57 } else {
58 $this->{$columnsByName}->{$column->name} = $column;
59 splice @{$this->{$columns}},$index,0,$column;
60 }
61
62 return $column;
63 }
64
65 sub RemoveColumn {
66 my ($this,$NameOrColumn,$Force) = @_;
67
68 my $ColName;
69 if (UNIVERSAL::isa($NameOrColumn,'IMPL::SQL::Schema::Column')) {
70 $ColName = $NameOrColumn->name;
71 } elsif (not ref $NameOrColumn) {
72 $ColName = $NameOrColumn;
73 }
74
75 if (exists $this->{$columnsByName}->{$ColName}) {
76 my $index = 0;
77 foreach my $column(@{$this->{$columns}}) {
78 last if $column->name eq $ColName;
79 $index++;
80 }
81
82 my $column = $this->{$columns}[$index];
83 if (my @constraints = $this->GetColumnConstraints($column)){
84 $Force or die new IMPL::InvalidOperationException('Can\'t remove column which is used in the constraints',@constraints);
85 $this->RemoveConstraint($_) foreach @constraints;
86 }
87
88 my $removed = splice @{$this->{$columns}},$index,1;
89 delete $this->{$columnsByName}->{$ColName};
90 return $removed;
91 } else {
92 die new IMPL::InvalidOperationException("The column not found",$NameOrColumn->name);
93 }
94 }
95
96 sub GetColumn {
97 my ($this,$name) = @_;
98
99 return $this->{$columnsByName}->{$name};
100 }
101
102 sub GetColumnAt {
103 my ($this,$index) = @_;
104
105 die new IMPL::InvalidArgumentException("The index is out of range")
106 if $index < 0 || $index >= ($this->{$columns} ? scalar(@{$this->{$columns}}) : 0);
107
108 return $this->{$columns}[$index];
109 }
110
111 sub SetColumnPosition {
112 my ($this,$nameOrColumn,$pos) = @_;
113
114 my $colName;
115 if (is($nameOrColumn,'IMPL::SQL::Schema::Column')) {
116 $colName = $nameOrColumn->name;
117 } elsif (not ref $nameOrColumn) {
118 $colName = $nameOrColumn;
119 } else {
120 die IMPL::InvalidArgumentException->new(column => 'The specified column isn\'t found in the table');
121 }
122
123 die IMPL::InvalidArgumentException->new( 'pos' => 'The specified position is invalid')
124 if not defined $pos || $pos < 0 || $pos >= $this->columnsCount;
125
126 my $index = 0;
127 foreach my $column(@{$this->{$columns}}) {
128 last if $column->name eq $colName;
129 $index++;
130 }
131
132 if ($pos != $index) {
133 #position needs to be changed;
134
135 my ($column) = splice @{$this->{$columns}}, $index, 1;
136 splice @{$this->{$columns}}, $pos, 0, $column;
137 }
138
139 return;
140 }
141
142 sub columnsCount {
143 my ($this) = @_;
144
145 return scalar(@{$this->{$columns}});
146 }
147
148 sub ColumnsCount {
149 goto &columnsCount;
150 }
151
152 sub AddConstraint {
153 my $this = shift;
154 if (@_ == 1) {
155 my ($Constraint) = @_;
156
157 die new IMPL::InvalidArgumentException('The invalid parameter') if not is($Constraint,'IMPL::SQL::Schema::Constraint');
158
159 $Constraint->table == $this or die new IMPL::InvalidOperationException('The constaint must belong to the target table');
160
161 if (exists $this->{$constraints}->{$Constraint->name}) {
162 die new IMPL::InvalidOperationException('The table already has the specified constraint',$Constraint->name);
163 } else {
164 if (UNIVERSAL::isa($Constraint,'IMPL::SQL::Schema::Constraint::PrimaryKey')) {
165 not $this->{$primaryKey} or die new IMPL::InvalidOperationException('The table already has a primary key');
166 $this->{$primaryKey} = $Constraint;
167 }
168
169 $this->{$constraints}->{$Constraint->name} = $Constraint;
170 }
171 } elsif( @_ == 2) {
172 my ($type,$params) = @_;
173
174 $type = IMPL::SQL::Schema::Constraint->ResolveAlias($type) or
175 die new IMPL::Exception("Can't resolve a constraint alias",$_[0]);
176
177 $params = {%{$params}};
178
179 $params->{table} = $this;
180
181 $this->AddConstraint($type->new(%$params));
182 } else {
183 die new IMPL::Exception("Wrong arguments number",scalar(@_));
184 }
185 }
186
187 sub RemoveConstraint {
188 my ($this,$Constraint,$Force) = @_;
189
190 my $cn = UNIVERSAL::isa($Constraint,'IMPL::SQL::Schema::Constraint') ? $Constraint->name : $Constraint;
191 $Constraint = $this->{$constraints}->{$cn} or die new IMPL::InvalidOperationException('The specified constraint doesn\'t exists',$cn);
192
193 if (UNIVERSAL::isa($Constraint,'IMPL::SQL::Schema::Constraint::PrimaryKey')) {
194 not scalar keys %{$this->{$primaryKey}->ConnectedFK} or die new IMPL::InvalidOperationException('Can\'t remove Primary Key unless some foreign keys referenses it');
195
196 delete $this->{$primaryKey};
197 }
198 $Constraint->Dispose;
199 delete $this->{$constraints}->{$cn};
200 return $cn;
201 }
202
203 sub GetConstraint {
204 my ($this,$name) = @_;
205
206 return $this->{$constraints}{$name};
207 }
208
209 sub GetConstraints {
210 my ($this) = @_;
211
212 return wantarray ? values %{$this->{$constraints}} : [values %{$this->{$constraints}}];
213 }
214
215 sub GetColumnConstraints {
216 my ($this,@Columns) = @_;
217
218 my @cn = map { UNIVERSAL::isa($_ ,'IMPL::SQL::Schema::Column') ? $_ ->name : $_ } @Columns;
219 exists $this->{$columnsByName}->{$_} or die new IMPL::InvalidOperationException('The specified column isn\'t found',$_) foreach @cn;
220
221 return grep {$_->HasColumn(@cn)} values %{$this->{$constraints}};
222 }
223
224 sub SetPrimaryKey {
225 my ($this,@ColumnList) = @_;
226
227 $this->AddConstraint(new IMPL::SQL::Schema::Constraint::PrimaryKey(name => $this->{$name}.'_PK', table => $this, columns => \@ColumnList));
228 }
229
230 sub LinkTo {
231 my ($this,$table,@ColumnList) = @_;
232 $table->primaryKey or die new IMPL::InvalidOperationException('The referenced table must have a primary key');
233 my $constraintName = $this->{$name}.'_'.$table->name.'_FK_'.join('_',map {ref $_ ? $_->name : $_} @ColumnList);
234 $this->AddConstraint(new IMPL::SQL::Schema::Constraint::ForeignKey(name => $constraintName, table => $this, columns => \@ColumnList, referencedTable => $table, referencedColumns => $table->primaryKey->columns->as_list));
235 }
236
237 sub Dispose {
238 my ($this) = @_;
239
240 $_->Dispose() foreach values %{$this->{$constraints}};
241
242 undef %{$this};
243 $this->SUPER::Dispose();
244 }
245
246 sub SameValue {
247 my ($this,$other) = @_;
248
249 return 0 unless is($other, typeof($this));
250
251 return 0 unless $this->name eq $other->name;
252 return 0 unless $this->ColumnsCount eq $other->ColumnsCount;
253
254 for (my $i = 0; $i < $this->ColumsCount; $i ++) {
255 return 0 unless $this->($i)->SameValue($other->GetColumnAt($i));
256 }
257
258 my %thisConstraints = map { $_->name, $_ } $this->GetConstraints();
259 my %otherConstraints = map { $_->name, $_ } $other->GetConstraints();
260
261 foreach my $name ( keys %thisConstraints ) {
262 return 0 unless $otherConstraints{$name};
263 return 0 unless $thisConstraints{$name}->SameValue(delete $otherConstraints{$name});
264 }
265
266 return 0 if %otherConstraints;
267
268 return 1;
269 }
270
271 1;
272
273