49
|
1 use strict;
|
|
2 package IMPL::SQL::Schema::Table;
|
|
3
|
|
4 use IMPL::SQL::Schema::Column;
|
|
5 use IMPL::SQL::Schema::Constraint;
|
|
6 use IMPL::SQL::Schema::Constraint::PrimaryKey;
|
|
7 use IMPL::SQL::Schema::Constraint::ForeignKey;
|
|
8
|
|
9 use base qw(IMPL::Object IMPL::Object::Disposable);
|
|
10 use IMPL::Class::Property;
|
|
11 use IMPL::Class::Property::Direct;
|
|
12
|
|
13 srand time;
|
|
14
|
|
15 BEGIN {
|
|
16 public _direct property Name => prop_get;
|
|
17 public _direct property Schema => prop_get;
|
|
18 public _direct property Columns => prop_get;
|
|
19 public _direct property Constraints => prop_get;
|
|
20 public _direct property ColumnsByName => prop_none;
|
|
21 public _direct property PrimaryKey => prop_get;
|
|
22 public _direct property Tag => prop_all;
|
|
23 }
|
|
24
|
|
25 sub CTOR {
|
|
26 my ($this,%args) = @_;
|
|
27
|
|
28 $this->{$Name} = $args{'Name'} or die new IMPL::InvalidArgumentException('a table name is required');
|
|
29 $this->{$Schema} = $args{'Schema'} or die new IMPL::InvalidArgumentException('a parent schema is required');
|
|
30 }
|
|
31
|
|
32 sub InsertColumn {
|
|
33 my ($this,$column,$index) = @_;
|
|
34
|
|
35 $index = ($this->{$Columns} ? scalar(@{$this->{$Columns}}) : 0) if not defined $index;
|
|
36
|
|
37 die new IMPL::InvalidArgumentException("The index is out of range") if ($index < 0 || $index > ($this->{$Columns} ? scalar(@{$this->{$Columns}}) : 0));
|
|
38
|
|
39 if (UNIVERSAL::isa($column,'IMPL::SQL::Schema::Column')) {
|
|
40
|
|
41 } elsif (UNIVERSAL::isa($column,'HASH')) {
|
|
42 $column = new IMPL::SQL::Schema::Column(%{$column});
|
|
43 } else {
|
|
44 die new IMPL::InvalidArgumentException("The invalid column parameter");
|
|
45 }
|
|
46
|
|
47 if (exists $this->{$ColumnsByName}->{$column->Name}) {
|
|
48 die new IMPL::InvalidOperationException("The column already exists",$column->name);
|
|
49 } else {
|
|
50 $this->{$ColumnsByName}->{$column->Name} = $column;
|
|
51 splice @{$this->{$Columns}},$index,0,$column;
|
|
52 }
|
|
53
|
|
54 return $column;
|
|
55 }
|
|
56
|
|
57 sub RemoveColumn {
|
|
58 my ($this,$NameOrColumn,$Force) = @_;
|
|
59
|
|
60 my $ColName;
|
|
61 if (UNIVERSAL::isa($NameOrColumn,'IMPL::SQL::Schema::Column')) {
|
|
62 $ColName = $NameOrColumn->Name;
|
|
63 } elsif (not ref $NameOrColumn) {
|
|
64 $ColName = $NameOrColumn;
|
|
65 }
|
|
66
|
|
67 if (exists $this->{$ColumnsByName}->{$ColName}) {
|
|
68 my $index = 0;
|
|
69 foreach my $column(@{$this->{$Columns}}) {
|
|
70 last if $column->Name eq $ColName;
|
|
71 $index++;
|
|
72 }
|
|
73
|
|
74 my $column = $this->{$Columns}[$index];
|
|
75 if (my @constraints = $this->GetColumnConstraints($column)){
|
|
76 $Force or die new IMPL::InvalidOperationException('Can\'t remove column which is used in the constraints',@constraints);
|
|
77 $this->RemoveConstraint($_) foreach @constraints;
|
|
78 }
|
|
79
|
|
80 my $removed = splice @{$this->{$Columns}},$index,1;
|
|
81 delete $this->{$ColumnsByName}->{$ColName};
|
|
82 return $removed;
|
|
83 } else {
|
|
84 die new IMPL::InvalidOperationException("The column not found",$NameOrColumn->Name);
|
|
85 }
|
|
86 }
|
|
87
|
|
88 sub Column {
|
|
89 my ($this,$name) = @_;
|
|
90
|
|
91 return $this->{$ColumnsByName}->{$name};
|
|
92 }
|
|
93
|
|
94 sub ColumnAt {
|
|
95 my ($this,$index) = @_;
|
|
96
|
|
97 die new IMPL::InvalidArgumentException("The index is out of range") if $index < 0 || $index >= ($this->{$Columns} ? scalar(@{$this->{$Columns}}) : 0);
|
|
98
|
|
99 return $this->{$Columns}[$index];
|
|
100 }
|
|
101
|
|
102 sub AddConstraint {
|
|
103 my ($this,$Constraint) = @_;
|
|
104
|
|
105 die new IMPL::InvalidArgumentException('The invalid parameter') if not UNIVERSAL::isa($Constraint,'IMPL::SQL::Schema::Constraint');
|
|
106
|
|
107 $Constraint->Table == $this or die new IMPL::InvalidOperationException('The constaint must belong to the target table');
|
|
108
|
|
109 if (exists $this->{$Constraints}->{$Constraint->Name}) {
|
|
110 die new IMPL::InvalidOperationException('The table already has the specified constraint',$Constraint->Name);
|
|
111 } else {
|
|
112 if (UNIVERSAL::isa($Constraint,'IMPL::SQL::Schema::Constraint::PrimaryKey')) {
|
|
113 not $this->{$PrimaryKey} or die new IMPL::InvalidOperationException('The table already has a primary key');
|
|
114 $this->{$PrimaryKey} = $Constraint;
|
|
115 }
|
|
116
|
|
117 $this->{$Constraints}->{$Constraint->Name} = $Constraint;
|
|
118 }
|
|
119 }
|
|
120
|
|
121 sub RemoveConstraint {
|
|
122 my ($this,$Constraint,$Force) = @_;
|
|
123
|
|
124 my $cn = UNIVERSAL::isa($Constraint,'IMPL::SQL::Schema::Constraint') ? $Constraint->Name : $Constraint;
|
|
125 $Constraint = $this->{$Constraints}->{$cn} or die new IMPL::InvalidOperationException('The specified constraint doesn\'t exists',$cn);
|
|
126
|
|
127 if (UNIVERSAL::isa($Constraint,'IMPL::SQL::Schema::Constraint::PrimaryKey')) {
|
|
128 not scalar keys %{$this->{$PrimaryKey}->ConnectedFK} or die new IMPL::InvalidOperationException('Can\'t remove Primary Key unless some foreign keys referenses it');
|
|
129
|
|
130 delete $this->{$PrimaryKey};
|
|
131 }
|
|
132 $Constraint->Dispose;
|
|
133 delete $this->{$Constraints}->{$cn};
|
|
134 return $cn;
|
|
135 }
|
|
136
|
|
137 sub GetColumnConstraints {
|
|
138 my ($this,@Columns) = @_;
|
|
139
|
|
140 my @cn = map { UNIVERSAL::isa($_ ,'IMPL::SQL::Schema::Column') ? $_ ->Name : $_ } @Columns;
|
|
141 exists $this->{$ColumnsByName}->{$_} or die new IMPL::InvalidOperationException('The specified column isn\'t found',$_) foreach @cn;
|
|
142
|
|
143 return grep {$_->HasColumn(@cn)} values %{$this->{$Constraints}};
|
|
144 }
|
|
145
|
|
146 sub SetPrimaryKey {
|
|
147 my ($this,@ColumnList) = @_;
|
|
148
|
|
149 $this->AddConstraint(new IMPL::SQL::Schema::Constraint::PrimaryKey(Name => $this->{$Name}.'_PK', Table => $this,Columns => \@ColumnList));
|
|
150 }
|
|
151
|
|
152 sub LinkTo {
|
|
153 my ($this,$table,@ColumnList) = @_;
|
|
154 $table->PrimaryKey or die new IMPL::InvalidOperationException('The referenced table must have a primary key');
|
|
155 my $constraintName = $this->{$Name}.'_'.$table->Name.'_FK_'.join('_',map {ref $_ ? $_->Name : $_} @ColumnList);
|
|
156 $this->AddConstraint(new IMPL::SQL::Schema::Constraint::ForeignKey(Name => $constraintName, Table => $this,Columns => \@ColumnList, ReferencedTable => $table, ReferencedColumns => $table->PrimaryKey->Columns));
|
|
157 }
|
|
158
|
|
159 sub Dispose {
|
|
160 my ($this) = @_;
|
|
161
|
|
162 $_->Dispose() foreach values %{$this->{$Constraints}};
|
|
163
|
|
164 undef %{$this};
|
|
165 $this->SUPER::Dispose();
|
|
166 }
|
|
167
|
|
168 1;
|