comparison Lib/IMPL/SQL/Schema/Traits.pm @ 271:56364d0c4b4f

+IMPL::SQL::Schema::MySQL: added basic support for MySQL
author cin
date Mon, 28 Jan 2013 02:43:14 +0400
parents dacfe7c0311a
children 47db27ed5b43
comparison
equal deleted inserted replaced
270:3f59fd828d5f 271:56364d0c4b4f
112 112
113 use base qw(IMPL::SQL::Schema::Traits::Constraint); 113 use base qw(IMPL::SQL::Schema::Traits::Constraint);
114 use fields qw( 114 use fields qw(
115 foreignTable 115 foreignTable
116 foreignColumns 116 foreignColumns
117 onUpdate
118 onDelete
117 ); 119 );
118 120
119 use constant { constraintClass => typeof IMPL::SQL::Schema::Constraint::ForeignKey }; 121 use constant { constraintClass => typeof IMPL::SQL::Schema::Constraint::ForeignKey };
120 122
121 our %CTOR = ( 123 our %CTOR = (
122 'IMPL::SQL::Schema::Traits::Constraint' => sub { @_[0..1] } 124 'IMPL::SQL::Schema::Traits::Constraint' => sub { @_[0..1] }
123 ); 125 );
124 126
125 sub CTOR { 127 sub CTOR {
126 my ($this,$foreignTable,$foreignColumns) = @_[0,3,4]; 128 my ($this,$foreignTable,$foreignColumns,%args) = @_[0,3..$#_];
127 129
128 $this->{foreignTable} = $foreignTable; 130 $this->{foreignTable} = $foreignTable;
129 $this->{foreignColunms} = $foreignColumns; 131 $this->{foreignColumns} = $foreignColumns;
132
133 $this->{onDelete} = $args{onDelete} if $args{onDelete};
134 $this->{onUpdate} = $args{onUpdate} if $args{onUpdate};
130 } 135 }
131 136
132 137
133 ################################################## 138 ##################################################
134 139
135 package IMPL::SQL::Schema::Traits::CreateTable; 140 package IMPL::SQL::Schema::Traits::CreateTable;
136 141
137 use parent qw(-norequire IMPL::SQL::Schema::Traits); 142 use IMPL::Const qw(:prop);
138 use IMPL::Class::Property; 143 use IMPL::declare {
144 require => {
145 Table => '-IMPL::SQL::Schema::Traits::Table',
146 ArgException => '-IMPL::InvalidArgumentException',
147 OpException => '-IMPL::InvalidOperationException'
148 },
149 base => [
150 '-IMPL::SQL::Schema::Traits' => undef
151 ],
152 props => [
153 table => PROP_RO,
154 ]
155 };
139 use IMPL::lang; 156 use IMPL::lang;
140 157
141 BEGIN {
142 public property table => prop_get | owner_set;
143 }
144
145 sub CTOR { 158 sub CTOR {
146 my ($this,$table) = @_; 159 my ($this,$table) = @_;
147 160
148 die new IMPL::InvalidArgumentException("table", "An object of IMPL::SQL::Schema::Traits::Table type is required") 161 die ArgException->new("table", "An object of IMPL::SQL::Schema::Traits::Table type is required")
149 unless is $table, typeof IMPL::SQL::Schema::Traits::Table; 162 unless is($table, Table);
150 163
151 $this->table($table); 164 $this->table($table);
152 } 165 }
153 166
154 sub apply { 167 sub CanApply {
155 my ($this,$schema) = @_; 168 my ($this,$schema) = @_;
156 169
157 return 0 if ( $schema->GetTable( $this->table->{name} ) ); 170 return( $schema->GetTable( $this->table->{name} ) ? 0 : 1 );
158 171 }
159 $schema->AddTable($this->table); 172
160 return 1; 173 sub Apply {
174 my ($this,$schema) = @_;
175
176 my $args = {%{$this->table}};
177
178 my $constraints = delete $args->{constraints} || [];
179
180 my $table = $schema->AddTable($args);
181
182 $table->AddConstraint($_->constraintClass, $_) foreach @{$constraints};
161 } 183 }
162 184
163 ################################################## 185 ##################################################
164 186
165 package IMPL::SQL::Schema::Traits::DropTable; 187 package IMPL::SQL::Schema::Traits::DropTable;
166 use parent qw(-norequire IMPL::SQL::Schema::Traits); 188 use IMPL::Const qw(:prop);
167 use IMPL::Class::Property; 189 use IMPL::declare {
168 190 require => {
169 BEGIN { 191 ArgException => '-IMPL::InvalidArgumentException'
170 public property tableName => prop_get | owner_set; 192 },
171 } 193 base => [
194 '-IMPL::SQL::Schema::Traits' => undef
195 ],
196 props => [
197 tableName => PROP_RO,
198 ]
199 };
172 200
173 sub CTOR { 201 sub CTOR {
174 my ($this,$tableName) = @_; 202 my ($this,$tableName) = @_;
175 203
176 $this->tableName($tableName) or die new IMPL::InvalidArgumentException("tableName is required"); 204 $this->tableName($tableName) or die ArgException->new("tableName is required");
177 } 205 }
178 206
179 sub apply { 207 sub CanApply {
180 my ($this,$schema) = @_; 208 my ($this,$schema) = @_;
181 209
182 return 0 if $schema->GetTable( $this->tableName ); 210 return $schema->GetTable( $this->tableName ) ? 1 : 0;
211 }
212
213 sub Apply {
214 my ($this,$schema) = @_;
183 215
184 $schema->RemoveTable($this->tableName); 216 $schema->RemoveTable($this->tableName);
185
186 return 1;
187 } 217 }
188 218
189 ################################################## 219 ##################################################
190 220
191 package IMPL::SQL::Schema::Traits::RenameTable; 221 package IMPL::SQL::Schema::Traits::RenameTable;
192 use parent qw(-norequire IMPL::SQL::Schema::Traits); 222 use IMPL::Const qw(:prop);
193 use IMPL::Class::Property; 223 use IMPL::declare {
194 224 require => {
195 BEGIN { 225 ArgException => '-IMPL::InvalidArgumentException'
196 public property tableName => prop_get | owner_set; 226 },
197 public property tableNewName => prop_get | owner_set; 227 base => [
198 } 228 '-IMPL::SQL::Schema::Traits' => undef
229 ],
230 props => [
231 tableName => PROP_RO,
232 tableNewName => PROP_RO,
233 ]
234 };
199 235
200 sub CTOR { 236 sub CTOR {
201 my ($this, $oldName, $newName) = @_; 237 my ($this, $oldName, $newName) = @_;
202 238
203 $this->tableName($oldName) or die new IMPL::InvalidArgumentException("A table name is required"); 239 $this->tableName($oldName) or die ArgException->new("A table name is required");
204 $this->tableNewName($newName) or die new IMPL::InvalidArgumentException("A new table name is required"); 240 $this->tableNewName($newName) or die ArgException->new("A new table name is required");
205 } 241 }
206 242
207 sub apply { 243 sub CanApply {
208 my ($this,$schema) = @_; 244 my ($this, $schema) = @_;
209 245
210 return 0 if not $schema->GetTable($this->tableName) or $schema->GetTable($this->tableNewName); 246 return ($schema->GetTable($this->tableName) and not $schema->GetTable($this->tableNewName) ? 1 : 0 );
211 247 }
212 $this->RenameTable($this->tableName, $this->tableNewName); 248
213 249 sub Apply {
214 return 1; 250 my ($this,$schema) = @_;
251
252 $schema->RenameTable($this->tableName, $this->tableNewName);
253
215 } 254 }
216 255
217 ################################################# 256 #################################################
218 257
219 package IMPL::SQL::Schema::Traits::AlterTableAddColumn; 258 package IMPL::SQL::Schema::Traits::AlterTableAddColumn;
220 use parent qw(-norequire IMPL::SQL::Schema::Traits); 259
221 use IMPL::Class::Property; 260 use IMPL::Const qw(:prop);
261 use IMPL::declare {
262 require => {
263 Column => '-IMPL::SQL::Schema::Traits::Column',
264 ArgException => '-IMPL::InvalidArgumentException',
265 OpException => '-IMPL::InvalidOperationException'
266 },
267 base => [
268 '-IMPL::SQL::Schema::Traits' => undef
269 ],
270 props => [
271 tableName => PROP_RO,
272 column => PROP_RO,
273 position => PROP_RO
274 ]
275 };
222 use IMPL::lang; 276 use IMPL::lang;
223 277
224 BEGIN {
225 public property tableName => prop_get | owner_set;
226 public property column => prop_get | owner_set;
227 public property position => prop_get | owner_set;
228 }
229 278
230 sub CTOR { 279 sub CTOR {
231 my ($this,$tableName,$column) = @_; 280 my ($this,$tableName,$column) = @_;
232 281
233 $this->tableName($tableName) or die new IMPL::InvalidArgumentException("A table name is required"); 282 $this->tableName($tableName) or die ArgException->new("A table name is required");
234 283
235 die new IMPL::InvalidArgumentException("A column should be a IMPL::SQL::Schema::Traits::Column object") 284 die ArgException->new("A column should be a IMPL::SQL::Schema::Traits::Column object")
236 unless is $column, typeof IMPL::SQL::Schema::Traits::Column; 285 unless is($column, Column);
237 286
238 $this->column($column); 287 $this->column($column);
239 } 288 }
240 289
241 sub apply { 290 sub CanApply {
242 my ($this,$schema) = @_; 291 my ($this,$schema) = @_;
243 292
244 my $table = $schema->GetTable($this->tableName) or return 0; 293 my $table = $schema->GetTable($this->tableName)
245 294 or return 0;
246 return 0 if $table->GetColumn( $this->column->{name} ); 295
247 296 return $table->GetColumn( $this->column->{name} ) ? 0 : 1;
248 $table->AddColumn($this->column); 297 }
249 298
250 return 1; 299 sub Apply {
300 my ($this,$schema) = @_;
301
302 my $table = $schema->GetTable($this->tableName)
303 or die OpException->new("The specified table doesn't exists", $this->tableName);
304
305 if ($this->position) {
306 $table->AddColumn($this->column);
307 } else {
308 $table->InsertColumn($this->column,$this->position);
309 }
251 } 310 }
252 311
253 ################################################# 312 #################################################
254 313
255 package IMPL::SQL::Schema::Traits::AlterTableDropColumn; 314 package IMPL::SQL::Schema::Traits::AlterTableDropColumn;
256 use parent qw(-norequire IMPL::SQL::Schema::Traits); 315
257 use IMPL::Class::Property; 316 use IMPL::Const qw(:prop);
258 317 use IMPL::declare {
259 BEGIN { 318 require => {
260 public property tableName => prop_get | owner_set; 319 FK => '-IMPL::SQL::Schema::Constraint::ForeignKey',
261 public property columnName => prop_get | owner_set; 320 ArgException => '-IMPL::InvalidArgumentException',
262 } 321 OpException => '-IMPL::InvalidOperationException'
322 },
323 base => [
324 '-IMPL::SQL::Schema::Traits' => undef
325 ],
326 props => [
327 tableName => PROP_RO,
328 columnName => PROP_RO,
329 ]
330 };
331 use IMPL::lang;
332
263 333
264 sub CTOR { 334 sub CTOR {
265 my ($this,$table,$column) = @_; 335 my ($this,$table,$column) = @_;
266 336
267 $this->tableName($table) or die new IMPL::InvalidArgumentException(tableName => "A table name should be specified"); 337 $this->tableName($table) or die ArgException->new(tableName => "A table name should be specified");
268 $this->columnName($column) or die new IMPL::InvalidArgumentException(columnName => "A column name should be specified"); 338 $this->columnName($column) or die ArgException->new(columnName => "A column name should be specified");
269 } 339 }
270 340
271 sub apply { 341 sub CanApply {
272 my ($this,$schema) = @_; 342 my ($this,$schema) = @_;
273 343
274 local $@; 344 my $table = $schema->GetTable($this->tableName)
275 345 or return 0;
276 return eval { 346
277 $schema->GetTable($this->tableName)->RemoveColumn($this->columnName); 347 $table->GetColumn($this->columnName) or
278 return 1; 348 return 0;
279 } || 0; 349
350 # столбец
351 return $table->GetColumnConstraints($this->columnName)
352 ? 0
353 : 1
354 ;
355 }
356
357 sub Apply {
358 my ($this,$schema) = @_;
359
360 my $table = $schema->GetTable($this->tableName)
361 or die OpException->new("The specified table doesn't exists", $this->tableName);
362
363 $table->RemoveColumn($this->columnName);
280 } 364 }
281 365
282 ################################################# 366 #################################################
283 367
284 package IMPL::SQL::Schema::Traits::AlterTableChangeColumn; 368 package IMPL::SQL::Schema::Traits::AlterTableChangeColumn;
285 use parent qw(-norequire IMPL::SQL::Schema::Traits); 369
286 use IMPL::Class::Property; 370 use IMPL::Const qw(:prop);
287 371 use IMPL::declare {
288 BEGIN { 372 require => {
289 public property tableName => prop_get | owner_set; 373 Constraint => '-IMPL::SQL::Schema::Traits::Constraint',
290 public property columnName => prop_get | owner_set; 374 ArgException => '-IMPL::InvalidArgumentException',
291 public property columnType => prop_all; 375 OpException => '-IMPL::InvalidOperationException'
292 public property defaultValue => prop_all; 376 },
293 public property isNullable => prop_all; 377 base => [
294 public property position => prop_all; 378 '-IMPL::SQL::Schema::Traits' => undef
295 public property options => prop_all; # hash diff format, (keys have a prefix '+' - add or update value, '-' remove value) 379 ],
296 } 380 props => [
381 tableName => PROP_RO,
382 columnName => PROP_RO,
383 columnType => PROP_RW,
384 defaultValue => PROP_RW,
385 isNullable => PROP_RW,
386 position => PROP_RW,
387 options => PROP_RW # hash diff format, (keys have a prefix '+' - add or update value, '-' remove value)
388 ]
389 };
390 use IMPL::lang;
297 391
298 sub CTOR { 392 sub CTOR {
299 my ($this, $table,$column,%args) = @_; 393 my ($this, $table,$column,%args) = @_;
300 394
301 $this->tableName($table) or die new IMPL::InvalidArgumentException(tableName => "A table name is required"); 395 $this->tableName($table) or die ArgException->new(tableName => "A table name is required");
302 $this->columnName($column) or die new IMPL::InvalidArgumentException(columnName => "A column name is required"); 396 $this->columnName($column) or die ArgException->new(columnName => "A column name is required");
303 397
304 $this->$_($args{$_}) 398 $this->$_($args{$_})
305 for (grep exists $args{$_}, qw(columnType defaultValue isNullable options)); 399 for (grep exists $args{$_}, qw(columnType defaultValue isNullable options));
306 } 400 }
307 401
308 sub apply { 402 sub CanApply {
309 my ($this,$schema) = @_; 403 my ($this,$schema) = @_;
310 404
311 local $@; 405 my $table = $schema->GetTable($this->tableName)
312 406 or return 0;
313 return eval { 407
314 my $column = $schema->GetTable($this->tableName)->GetColumn($this->columnName); 408 return $table->GetColumn($this->columnName) ? 1 : 0;
315 $column->SetType($this->columnType) if defined $this->columnType; 409 }
316 $column->SetNullable($this->isNullable) if defined $this->isNullable; 410
317 $column->SetDefaultValue($this->defaultValue) if defined $this->defaultValue; 411 sub Apply {
318 $column->SetOptions($this->options) if defined $this->options; 412 my ($this,$schema) = @_;
319 413
320 return 1; 414 my $table = $schema->GetTable($this->tableName)
321 } || 0; 415 or die OpException->new("The specified table doesn't exists", $this->tableName);
416
417 my $column = $table->GetColumn($this->columnName)
418 or die OpException->new("The specified column doesn't exists", $this->tableName, $this->columnName);
419
420 $column->SetType($this->columnType) if defined $this->columnType;
421 $column->SetNullable($this->isNullable) if defined $this->isNullable;
422 $column->SetDefaultValue($this->defaultValue) if defined $this->defaultValue;
423 $column->SetOptions($this->options) if defined $this->options;
424
425 $table->SetColumnPosition($this->position)
426 if ($this->position);
427
322 } 428 }
323 429
324 ################################################# 430 #################################################
325 431
326 package IMPL::SQL::Schema::Traits::AlterTableAddConstraint; 432 package IMPL::SQL::Schema::Traits::AlterTableAddConstraint;
327 use parent qw(-norequire IMPL::SQL::Schema::Traits); 433
328 use IMPL::Class::Property; 434 use IMPL::Const qw(:prop);
435 use IMPL::declare {
436 require => {
437 Constraint => '-IMPL::SQL::Schema::Traits::Constraint',
438 ArgException => '-IMPL::InvalidArgumentException',
439 FK => '-IMPL::SQL::Schema::Traits::ForeignKey'
440 },
441 base => [
442 '-IMPL::SQL::Schema::Traits' => undef
443 ],
444 props => [
445 tableName => PROP_RO,
446 constraint => PROP_RO
447 ]
448 };
329 use IMPL::lang; 449 use IMPL::lang;
330 450
331 BEGIN {
332 public property tableName => prop_get | owner_set;
333 public property constraint => prop_get | owner_set;
334 }
335
336 sub CTOR { 451 sub CTOR {
337 my ($this,$table,$constraint) = @_; 452 my ($this,$table,$constraint) = @_;
338 453
339 $this->tableName($table) or die new IMPL::InvalidArgumentException( tableName => "A table name is required"); 454 $this->tableName($table) or die ArgException->new( tableName => "A table name is required");
340 455
341 die new IMPL::InvalidArgumentException(constaraint => "A valid IMPL::SQL::Schema::Traits::Constarint is required") 456 die ArgException->new(constaraint => "A valid " . Constraint . " is required")
342 unless is $constraint, typeof IMPL::SQL::Schema::Traits::Constraint; 457 unless is($constraint, Constraint);
343 458
344 $this->constraint($constraint); 459 $this->constraint($constraint);
345 } 460 }
346 461
347 sub apply { 462 sub CanApply {
348 my ($this,$schema) = @_; 463 my ($this, $schema) = @_;
349 464
350 local $@; 465 my $table = $schema->GetTable($this->tableName)
351 466 or return 0;
352 return eval { 467
353 $schema->GetTable($this->tableName)->AddConstraint($this->constraint->constraintClass, $this->constraint); 468 my $constraint = $this->constraint;
354 return 1; 469
355 } || 0; 470 my @columns = map $table->GetColumn($_), @{$constraint->{columns} || []};
471
472 # проверяем, что в таблице есть все столбцы для создания ограничения
473 return 0 if grep not($_), @columns;
474
475 if (is($constraint,FK)) {
476 warn "FK";
477 my $foreignTable = $schema->GetTable($constraint->{foreignTable})
478 or return 0;
479
480 warn "Table OK";
481 my @foreignColumns = map $foreignTable->GetColumn($_), @{$constraint->{foreignColumns}||[]};
482
483 # внешняя таблица имеет нужные столбцы
484 return 0
485 if grep not($_), @foreignColumns;
486
487 warn "FK Columns OK";
488
489 return 0
490 if grep not($columns[$_]->type->SameValue($foreignColumns[$_]->type)), (0 .. $#columns);
491
492 warn "FK Columns types OK";
493 }
494
495 return 1;
496 }
497
498 sub Apply {
499 my ($this,$schema) = @_;
500
501 my $table = $schema->GetTable($this->tableName)
502 or die IMPL::InvalidOperationException->new("The specified table doesn't exists", $this->tableName);
503
504 my $constraint = $this->constraint;
505
506 if (is($constraint,FK)) {
507 my $args = { %$constraint };
508 $args->{referencedTable} = $schema->GetTable(delete $args->{foreignTable});
509 $args->{referencedColumns} = delete $args->{foreignColumns};
510 $table->AddConstraint($constraint->constraintClass, $args);
511 } else {
512 $table->AddConstraint($constraint->constraintClass, $constraint);
513 }
356 514
357 } 515 }
358 516
359 ################################################# 517 #################################################
360 518
361 package IMPL::SQL::Schema::Traits::AlterTableDropConstraint; 519 package IMPL::SQL::Schema::Traits::AlterTableDropConstraint;
362 use parent qw(-norequire IMPL::SQL::Schema::Traits); 520 use IMPL::Const qw(:prop);
363 use IMPL::Class::Property; 521 use IMPL::declare {
364 522 require => {
365 BEGIN { 523 PK => '-IMPL::SQL::Schema::Constraint::PrimaryKey'
366 public property tableName => prop_get | owner_set; 524 },
367 public property constraintName => prop_get | owner_set; 525 base => [
368 } 526 '-IMPL::SQL::Schema::Traits' => undef
527 ],
528 props => [
529 tableName => PROP_RO,
530 constraintName => PROP_RO
531 ]
532 };
533 use IMPL::lang qw(is);
369 534
370 sub CTOR { 535 sub CTOR {
371 my ($this,$table,$constraint) = @_; 536 my ($this,$table,$constraint) = @_;
372 537
373 die new IMPL::InvalidArgumentException( tableName => "A table name is required" ) unless $table; 538 die new IMPL::InvalidArgumentException( tableName => "A table name is required" ) unless $table;
375 540
376 $this->tableName($table); 541 $this->tableName($table);
377 $this->constraintName($constraint); 542 $this->constraintName($constraint);
378 } 543 }
379 544
380 sub apply { 545 sub CanApply {
381 my ($this,$schema) = @_; 546 my ($this,$schema) = @_;
382 547
383 my $table = $schema->GetTable($this->tableName) or return 0; 548 my $table = $schema->GetTable($this->tableName);
384 549
385 return 0 unless $table->GetConstraint($this->constraintName); 550 my $constraint = $table->GetConstraint($this->constraintName)
551 or return 0;
552
553 # есть ли внешние ключи на данную таблицу
554 return (
555 is($constraint,PK)
556 && values( %{$constraint->connectedFK || {}} )
557 ? 0
558 : 1
559 );
560 }
561
562 sub Apply {
563 my ($this,$schema) = @_;
564
565 my $table = $schema->GetTable($this->tableName)
566 or die IMPL::InvalidOperationException->new("The specified table doesn't exists", $this->tableName);
386 567
387 $table->RemoveConstraint($this->constraintName); 568 $table->RemoveConstraint($this->constraintName);
388 return 1;
389 } 569 }
390 570
391 571
392 1; 572 1;
393 573
411 591
412 =head2 General 592 =head2 General
413 593
414 Методы обще для всех примитивных операций. 594 Методы обще для всех примитивных операций.
415 595
416 =head3 C<apply($schema)> 596 =head3 C<CanApply($schema)>
417 597
418 Пытается приминить операцию к указанной схеме. 598 Определяет возможность применения операции к указанной схеме.
419 599
420 Возвращаемое значение: 600 Возвращаемое значение:
421 601
422 =over 602 =over
423 603
424 =item C<true> 604 =item C<true>
425 605
426 Операция успешно применена к схеме. 606 Операция приминима к схеме.
427 607
428 =item C<false> 608 =item C<false>
429 609
430 Операция не может быть применена к схеме. 610 Операция не может быть применена к схеме.
431 611
432 =back 612 =back
613
614 =head3 C<Apply($schema)>
615
616 Применяет операцию к указанной схеме.
433 617
434 =head2 Primitive operations 618 =head2 Primitive operations
435 619
436 =head3 C<IMPL::SQL::Schema::Traits::CreateTable> 620 =head3 C<IMPL::SQL::Schema::Traits::CreateTable>
437 621