Tab/WS crusade
[dbsrgits/SQL-Translator.git] / t / 47postgres-producer.t
CommitLineData
8c4efd11 1#!/usr/bin/perl
2
3use strict;
4use warnings;
5
6use Test::More;
7use Test::Exception;
8use Test::SQL::Translator qw(maybe_plan);
5f31ed66 9use SQL::Translator::Schema::Constants;
8c4efd11 10use Data::Dumper;
11use FindBin qw/$Bin/;
12
13# Testing 1,2,3,4...
14#=============================================================================
15
16BEGIN {
681dc480 17 maybe_plan(51,
8c4efd11 18 'SQL::Translator::Producer::PostgreSQL',
19 'Test::Differences',
20 )
21}
22use Test::Differences;
23use SQL::Translator;
24
bc8e2aa1 25my $PRODUCER = \&SQL::Translator::Producer::PostgreSQL::create_field;
8c4efd11 26
27my $table = SQL::Translator::Schema::Table->new( name => 'mytable');
28
29my $field1 = SQL::Translator::Schema::Field->new( name => 'myfield',
30 table => $table,
31 data_type => 'VARCHAR',
32 size => 10,
33 default_value => undef,
34 is_auto_increment => 0,
35 is_nullable => 1,
36 is_foreign_key => 0,
37 is_unique => 0 );
38
39my $field1_sql = SQL::Translator::Producer::PostgreSQL::create_field($field1);
40
41is($field1_sql, 'myfield character varying(10)', 'Create field works');
42
aacb3187 43my $field_array = SQL::Translator::Schema::Field->new( name => 'myfield',
44 table => $table,
45 data_type => 'character varying[]',
46 size => 10,
47 default_value => undef,
48 is_auto_increment => 0,
49 is_nullable => 1,
50 is_foreign_key => 0,
51 is_unique => 0 );
52
53my $field_array_sql = SQL::Translator::Producer::PostgreSQL::create_field($field_array);
54
55is($field_array_sql, 'myfield character varying(10)[]', 'Create field works');
56
8c4efd11 57my $field2 = SQL::Translator::Schema::Field->new( name => 'myfield',
58 table => $table,
59 data_type => 'VARCHAR',
60 size => 25,
61 default_value => undef,
62 is_auto_increment => 0,
63 is_nullable => 0,
64 is_foreign_key => 0,
65 is_unique => 0 );
66
c50d1a0a 67my $pk_constraint = SQL::Translator::Schema::Constraint->new(
68 table => $table,
69 name => 'foo',
70 fields => [qw(myfield)],
71 type => 'PRIMARY_KEY',
72);
73
74my ($pk_constraint_def_ref, $pk_constraint_fk_ref ) = SQL::Translator::Producer::PostgreSQL::create_constraint($pk_constraint);
75ok(@{$pk_constraint_def_ref} == 1 && @{$pk_constraint_fk_ref} == 0, 'precheck of create_Primary Key constraint');
76is($pk_constraint_def_ref->[0], 'CONSTRAINT foo PRIMARY KEY (myfield)', 'Create Primary Key Constraint works');
77
78my $alter_pk_constraint = SQL::Translator::Producer::PostgreSQL::alter_drop_constraint($pk_constraint);
79is($alter_pk_constraint, 'ALTER TABLE mytable DROP CONSTRAINT foo', 'Alter drop Primary Key constraint works');
80
81my $table2 = SQL::Translator::Schema::Table->new( name => 'mytable2');
82
83my $field1_2 = SQL::Translator::Schema::Field->new( name => 'myfield_2',
84 table => $table,
85 data_type => 'VARCHAR',
86 size => 10,
87 default_value => undef,
88 is_auto_increment => 0,
89 is_nullable => 1,
90 is_foreign_key => 0,
91 is_unique => 0 );
92
681dc480 93# check named, and unnamed foreign keys
94for my $name ( 'foo', undef ) {
95 my $fk_constraint = SQL::Translator::Schema::Constraint->new(
96 table => $table,
97 name => $name,
98 fields => [qw(myfield)],
99 type => 'FOREIGN_KEY',
100 reference_table => $table2,
101 reference_fields => [qw(myfield_2)],
102 );
103 my $fk_constraint_2 = SQL::Translator::Schema::Constraint->new(
104 table => $table,
105 name => $name,
106 fields => [qw(myfield)],
107 type => 'FOREIGN_KEY',
108 reference_table => $table2,
109 reference_fields => [qw(myfield_2)],
110 );
111
112 my ($fk_constraint_def_ref, $fk_constraint_fk_ref ) = SQL::Translator::Producer::PostgreSQL::create_constraint($fk_constraint);
113
114 ok(@{$fk_constraint_def_ref} == 0 && @{$fk_constraint_fk_ref} == 1, 'precheck of create_Foreign Key constraint');
115
116 if ( $name ) {
117 is($fk_constraint_fk_ref->[0], "ALTER TABLE mytable ADD CONSTRAINT $name FOREIGN KEY (myfield)
118 REFERENCES mytable2 (myfield_2) DEFERRABLE", 'Create Foreign Key Constraint works');
119
120 # ToDo: may we should check if the constraint name was valid, or if next
121 # unused_name created has choosen a different one
122 my $alter_fk_constraint = SQL::Translator::Producer::PostgreSQL::alter_drop_constraint($fk_constraint);
123 is($alter_fk_constraint, "ALTER TABLE mytable DROP CONSTRAINT $name", 'Alter drop Foreign Key constraint works');
124 }
125 else {
126 is($fk_constraint_fk_ref->[0], 'ALTER TABLE mytable ADD FOREIGN KEY (myfield)
127 REFERENCES mytable2 (myfield_2) DEFERRABLE', 'Create named Foreign Key Constraint works');
128
129 my $alter_fk_constraint = SQL::Translator::Producer::PostgreSQL::alter_drop_constraint($fk_constraint);
130 is($alter_fk_constraint, 'ALTER TABLE mytable DROP CONSTRAINT mytable_myfield_fkey', 'Alter drop named Foreign Key constraint works');
131 }
132}
c50d1a0a 133
c50d1a0a 134
8c4efd11 135my $alter_field = SQL::Translator::Producer::PostgreSQL::alter_field($field1,
136 $field2);
c50d1a0a 137is($alter_field, qq[ALTER TABLE mytable ALTER COLUMN myfield SET NOT NULL;
3406fd5b 138ALTER TABLE mytable ALTER COLUMN myfield TYPE character varying(25)],
8c4efd11 139 'Alter field works');
140
c50d1a0a 141my $field1_complex = SQL::Translator::Schema::Field->new(
142 name => 'my_complex_field',
143 table => $table,
144 data_type => 'VARCHAR',
145 size => 10,
146 default_value => undef,
147 is_auto_increment => 0,
148 is_nullable => 1,
149 is_foreign_key => 0,
150 is_unique => 0
151);
152
153my $field2_complex = SQL::Translator::Schema::Field->new(
154 name => 'my_altered_field',
155 table => $table,
156 data_type => 'VARCHAR',
157 size => 60,
158 default_value => 'whatever',
159 is_auto_increment => 0,
160 is_nullable => 1,
161 is_foreign_key => 0,
162 is_unique => 0
163);
164
165my $alter_field_complex = SQL::Translator::Producer::PostgreSQL::alter_field($field1_complex, $field2_complex);
166is(
167 $alter_field_complex,
168 q{ALTER TABLE mytable RENAME COLUMN my_complex_field TO my_altered_field;
169ALTER TABLE mytable ALTER COLUMN my_altered_field TYPE character varying(60);
170ALTER TABLE mytable ALTER COLUMN my_altered_field SET DEFAULT 'whatever'},
171 'Complex Alter field works'
172);
173
8c4efd11 174$field1->name('field3');
175my $add_field = SQL::Translator::Producer::PostgreSQL::add_field($field1);
176
3406fd5b 177is($add_field, 'ALTER TABLE mytable ADD COLUMN field3 character varying(10)', 'Add field works');
8c4efd11 178
179my $drop_field = SQL::Translator::Producer::PostgreSQL::drop_field($field2);
3406fd5b 180is($drop_field, 'ALTER TABLE mytable DROP COLUMN myfield', 'Drop field works');
8c4efd11 181
e56dabb7 182my $field3 = SQL::Translator::Schema::Field->new( name => 'time_field',
183 table => $table,
184 data_type => 'TIME',
185 default_value => undef,
186 is_auto_increment => 0,
187 is_nullable => 0,
188 is_foreign_key => 0,
189 is_unique => 0 );
190
191my $field3_sql = SQL::Translator::Producer::PostgreSQL::create_field($field3);
192
193is($field3_sql, 'time_field time NOT NULL', 'Create time field works');
194
621cb859 195my $field3_datetime_with_TZ = SQL::Translator::Schema::Field->new(
196 name => 'datetime_with_TZ',
197 table => $table,
198 data_type => 'timestamp with time zone',
199 size => 7,
200);
201
aee4b66e 202my $field3_datetime_with_TZ_sql =
621cb859 203 SQL::Translator::Producer::PostgreSQL::create_field(
204 $field3_datetime_with_TZ
205 );
206
207is(
aee4b66e 208 $field3_datetime_with_TZ_sql,
209 'datetime_with_TZ timestamp(6) with time zone',
621cb859 210 'Create time field with time zone and size, works'
211);
212
213my $field3_time_without_TZ = SQL::Translator::Schema::Field->new(
214 name => 'time_without_TZ',
215 table => $table,
216 data_type => 'time without time zone',
217 size => 2,
218);
219
aee4b66e 220my $field3_time_without_TZ_sql
621cb859 221 = SQL::Translator::Producer::PostgreSQL::create_field(
222 $field3_time_without_TZ
223 );
224
225is(
aee4b66e 226 $field3_time_without_TZ_sql,
227 'time_without_TZ time(2) without time zone',
621cb859 228 'Create time field without time zone but with size, works'
229);
230
c3bddac9 231my $field_num = SQL::Translator::Schema::Field->new( name => 'num',
232 table => $table,
233 data_type => 'numeric',
234 size => [10,2],
235 );
236my $fieldnum_sql = SQL::Translator::Producer::PostgreSQL::create_field($field_num);
237
238is($fieldnum_sql, 'num numeric(10,2)', 'Create numeric field works');
239
240
e56dabb7 241my $field4 = SQL::Translator::Schema::Field->new( name => 'bytea_field',
242 table => $table,
243 data_type => 'bytea',
244 size => '16777215',
245 default_value => undef,
246 is_auto_increment => 0,
247 is_nullable => 0,
248 is_foreign_key => 0,
249 is_unique => 0 );
250
251my $field4_sql = SQL::Translator::Producer::PostgreSQL::create_field($field4);
8c4efd11 252
e56dabb7 253is($field4_sql, 'bytea_field bytea NOT NULL', 'Create bytea field works');
5342f5c1 254
255my $field5 = SQL::Translator::Schema::Field->new( name => 'enum_field',
256 table => $table,
257 data_type => 'enum',
258 extra => { list => [ 'Foo', 'Bar' ] },
259 is_auto_increment => 0,
260 is_nullable => 0,
261 is_foreign_key => 0,
262 is_unique => 0 );
263
264my $field5_sql = SQL::Translator::Producer::PostgreSQL::create_field($field5,{ postgres_version => 8.3 });
265
266is($field5_sql, 'enum_field mytable_enum_field_type NOT NULL', 'Create real enum field works');
267
90726ffd 268
269
270
271my $field6 = SQL::Translator::Schema::Field->new(
272 name => 'character',
273 table => $table,
274 data_type => 'character',
275 size => '123',
276 default_value => 'foobar',
277 is_auto_increment => 0,
278 is_nullable => 0,
279 is_foreign_key => 0,
280 is_unique => 0);
281
282my $field7 = SQL::Translator::Schema::Field->new(
283 name => 'character',
284 table => $table,
285 data_type => 'character',
286 size => '123',
287 default_value => undef,
288 is_auto_increment => 0,
289 is_nullable => 0,
290 is_foreign_key => 0,
291 is_unique => 0);
292
293$alter_field = SQL::Translator::Producer::PostgreSQL::alter_field($field6,
294 $field7);
295
296is($alter_field, q(ALTER TABLE mytable ALTER COLUMN character DROP DEFAULT), 'DROP DEFAULT');
297
298$field7->default_value(q(foo'bar'));
299
300$alter_field = SQL::Translator::Producer::PostgreSQL::alter_field($field6,
301 $field7);
302
303is($alter_field, q(ALTER TABLE mytable ALTER COLUMN character SET DEFAULT 'foo''bar'''), 'DEFAULT with escaping');
304
305$field7->default_value(\q(foobar));
306
307$alter_field = SQL::Translator::Producer::PostgreSQL::alter_field($field6,
308 $field7);
309
310is($alter_field, q(ALTER TABLE mytable ALTER COLUMN character SET DEFAULT foobar), 'DEFAULT unescaped if scalarref');
311
312$field7->is_nullable(1);
313$field7->default_value(q(foobar));
314
315$alter_field = SQL::Translator::Producer::PostgreSQL::alter_field($field6,
316 $field7);
317
318is($alter_field, q(ALTER TABLE mytable ALTER COLUMN character DROP NOT NULL), 'DROP NOT NULL');
319
ad258776 320my $field8 = SQL::Translator::Schema::Field->new( name => 'ts_field',
321 table => $table,
322 data_type => 'timestamp with time zone',
323 size => 6,
324 is_auto_increment => 0,
325 is_nullable => 0,
326 is_foreign_key => 0,
327 is_unique => 0 );
328
329my $field8_sql = SQL::Translator::Producer::PostgreSQL::create_field($field8,{ postgres_version => 8.3 });
330
331is($field8_sql, 'ts_field timestamp(6) with time zone NOT NULL', 'timestamp with precision');
332
333my $field9 = SQL::Translator::Schema::Field->new( name => 'time_field',
334 table => $table,
335 data_type => 'time with time zone',
336 size => 6,
337 is_auto_increment => 0,
338 is_nullable => 0,
339 is_foreign_key => 0,
340 is_unique => 0 );
341
342my $field9_sql = SQL::Translator::Producer::PostgreSQL::create_field($field9,{ postgres_version => 8.3 });
343
344is($field9_sql, 'time_field time(6) with time zone NOT NULL', 'time with precision');
345
346my $field10 = SQL::Translator::Schema::Field->new( name => 'interval_field',
347 table => $table,
348 data_type => 'interval',
349 size => 6,
350 is_auto_increment => 0,
351 is_nullable => 0,
352 is_foreign_key => 0,
353 is_unique => 0 );
354
355my $field10_sql = SQL::Translator::Producer::PostgreSQL::create_field($field10,{ postgres_version => 8.3 });
356
357is($field10_sql, 'interval_field interval(6) NOT NULL', 'time with precision');
358
359
360my $field11 = SQL::Translator::Schema::Field->new( name => 'time_field',
361 table => $table,
362 data_type => 'time without time zone',
363 size => 6,
364 is_auto_increment => 0,
365 is_nullable => 0,
366 is_foreign_key => 0,
367 is_unique => 0 );
368
369my $field11_sql = SQL::Translator::Producer::PostgreSQL::create_field($field11,{ postgres_version => 8.3 });
370
371is($field11_sql, 'time_field time(6) without time zone NOT NULL', 'time with precision');
372
373
374
375my $field12 = SQL::Translator::Schema::Field->new( name => 'time_field',
376 table => $table,
377 data_type => 'timestamp',
378 is_auto_increment => 0,
379 is_nullable => 0,
380 is_foreign_key => 0,
381 is_unique => 0 );
382
383my $field12_sql = SQL::Translator::Producer::PostgreSQL::create_field($field12,{ postgres_version => 8.3 });
384
385is($field12_sql, 'time_field timestamp NOT NULL', 'time with precision');
386
79f55d7e 387my $field13 = SQL::Translator::Schema::Field->new( name => 'enum_field_with_type_name',
388 table => $table,
389 data_type => 'enum',
390 extra => { list => [ 'Foo', 'Bar' ],
391 custom_type_name => 'real_enum_type' },
392 is_auto_increment => 0,
393 is_nullable => 0,
394 is_foreign_key => 0,
395 is_unique => 0 );
396
397my $field13_sql = SQL::Translator::Producer::PostgreSQL::create_field($field13,{ postgres_version => 8.3 });
398
399is($field13_sql, 'enum_field_with_type_name real_enum_type NOT NULL', 'Create real enum field works');
400
90726ffd 401
bc8e2aa1 402{
403 # let's test default values! -- rjbs, 2008-09-30
404 my %field = (
405 table => $table,
406 data_type => 'VARCHAR',
407 size => 10,
408 is_auto_increment => 0,
409 is_nullable => 1,
410 is_foreign_key => 0,
411 is_unique => 0,
412 );
413
414 {
415 my $simple_default = SQL::Translator::Schema::Field->new(
416 %field,
417 name => 'str_default',
418 default_value => 'foo',
419 );
420
421 is(
422 $PRODUCER->($simple_default),
423 q{str_default character varying(10) DEFAULT 'foo'},
424 'default str',
425 );
426 }
427
428 {
429 my $null_default = SQL::Translator::Schema::Field->new(
430 %field,
431 name => 'null_default',
432 default_value => \'NULL',
433 );
434
435 is(
436 $PRODUCER->($null_default),
437 q{null_default character varying(10) DEFAULT NULL},
438 'default null',
439 );
440 }
441
442 {
443 my $null_default = SQL::Translator::Schema::Field->new(
444 %field,
445 name => 'null_default_2',
446 default_value => 'NULL', # XXX: this should go away
447 );
448
449 is(
450 $PRODUCER->($null_default),
451 q{null_default_2 character varying(10) DEFAULT NULL},
452 'default null from special cased string',
453 );
454 }
455
456 {
457 my $func_default = SQL::Translator::Schema::Field->new(
458 %field,
459 name => 'func_default',
460 default_value => \'func(funky)',
461 );
462
463 is(
464 $PRODUCER->($func_default),
465 q{func_default character varying(10) DEFAULT func(funky)},
466 'unquoted default from scalar ref',
467 );
468 }
469}
470
471
296c2701 472my $view1 = SQL::Translator::Schema::View->new(
473 name => 'view_foo',
474 fields => [qw/id name/],
475 sql => 'SELECT id, name FROM thing',
476);
477my $create_opts = { add_replace_view => 1, no_comments => 1 };
478my $view1_sql1 = SQL::Translator::Producer::PostgreSQL::create_view($view1, $create_opts);
479
f59b2c0e 480my $view_sql_replace = "CREATE VIEW view_foo ( id, name ) AS
296c2701 481 SELECT id, name FROM thing
f59b2c0e 482";
296c2701 483is($view1_sql1, $view_sql_replace, 'correct "CREATE OR REPLACE VIEW" SQL');
484
485my $view2 = SQL::Translator::Schema::View->new(
486 name => 'view_foo2',
487 sql => 'SELECT id, name FROM thing',
488 extra => {
489 'temporary' => '1',
490 'check_option' => 'cascaded',
491 },
492);
493my $create2_opts = { add_replace_view => 1, no_comments => 1 };
494my $view2_sql1 = SQL::Translator::Producer::PostgreSQL::create_view($view2, $create2_opts);
495
f59b2c0e 496my $view2_sql_replace = "CREATE TEMPORARY VIEW view_foo2 AS
296c2701 497 SELECT id, name FROM thing
f59b2c0e 498 WITH CASCADED CHECK OPTION";
296c2701 499is($view2_sql1, $view2_sql_replace, 'correct "CREATE OR REPLACE VIEW" SQL 2');
5f31ed66 500
501{
502 my $table = SQL::Translator::Schema::Table->new( name => 'foobar', fields => [qw( foo bar )] );
503 my $quote = { quote_table_names => '"', quote_field_names => '"' };
504
505 {
506 my $index = $table->add_index(name => 'myindex', fields => ['foo']);
507 my ($def) = SQL::Translator::Producer::PostgreSQL::create_index($index);
508 is($def, "CREATE INDEX myindex on foobar (foo)", 'index created');
509 ($def) = SQL::Translator::Producer::PostgreSQL::create_index($index, $quote);
510 is($def, 'CREATE INDEX "myindex" on "foobar" ("foo")', 'index created w/ quotes');
511 }
512
513 {
514 my $index = $table->add_index(name => 'myindex', fields => ['lower(foo)']);
515 my ($def) = SQL::Translator::Producer::PostgreSQL::create_index($index);
516 is($def, "CREATE INDEX myindex on foobar (lower(foo))", 'index created');
517 ($def) = SQL::Translator::Producer::PostgreSQL::create_index($index, $quote);
518 is($def, 'CREATE INDEX "myindex" on "foobar" (lower(foo))', 'index created w/ quotes');
519 }
520
521 {
522 my $index = $table->add_index(name => 'myindex', fields => ['bar', 'lower(foo)']);
523 my ($def) = SQL::Translator::Producer::PostgreSQL::create_index($index);
524 is($def, "CREATE INDEX myindex on foobar (bar, lower(foo))", 'index created');
525 ($def) = SQL::Translator::Producer::PostgreSQL::create_index($index, $quote);
526 is($def, 'CREATE INDEX "myindex" on "foobar" ("bar", lower(foo))', 'index created w/ quotes');
527 }
528
529 {
530 my $constr = $table->add_constraint(name => 'constr', type => UNIQUE, fields => ['foo']);
531 my ($def) = SQL::Translator::Producer::PostgreSQL::create_constraint($constr);
532 is($def->[0], 'CONSTRAINT constr UNIQUE (foo)', 'constraint created');
533 ($def) = SQL::Translator::Producer::PostgreSQL::create_constraint($constr, $quote);
534 is($def->[0], 'CONSTRAINT "constr" UNIQUE ("foo")', 'constraint created w/ quotes');
535 }
536
537 {
538 my $constr = $table->add_constraint(name => 'constr', type => UNIQUE, fields => ['lower(foo)']);
539 my ($def) = SQL::Translator::Producer::PostgreSQL::create_constraint($constr);
540 is($def->[0], 'CONSTRAINT constr UNIQUE (lower(foo))', 'constraint created');
541 ($def) = SQL::Translator::Producer::PostgreSQL::create_constraint($constr, $quote);
542 is($def->[0], 'CONSTRAINT "constr" UNIQUE (lower(foo))', 'constraint created w/ quotes');
543 }
544
545 {
546 my $constr = $table->add_constraint(name => 'constr', type => UNIQUE, fields => ['bar', 'lower(foo)']);
547 my ($def) = SQL::Translator::Producer::PostgreSQL::create_constraint($constr);
548 is($def->[0], 'CONSTRAINT constr UNIQUE (bar, lower(foo))', 'constraint created');
549 ($def) = SQL::Translator::Producer::PostgreSQL::create_constraint($constr, $quote);
550 is($def->[0], 'CONSTRAINT "constr" UNIQUE ("bar", lower(foo))', 'constraint created w/ quotes');
551 }
552}
25c74c43 553
554my $drop_view_opts1 = { add_drop_view => 1, no_comments => 1, postgres_version => 8.001 };
555my $drop_view_8_1_produced = SQL::Translator::Producer::PostgreSQL::create_view($view1, $drop_view_opts1);
556
557my $drop_view_8_1_expected = "DROP VIEW view_foo;
558CREATE VIEW view_foo ( id, name ) AS
559 SELECT id, name FROM thing
560";
aee4b66e 561
25c74c43 562is($drop_view_8_1_produced, $drop_view_8_1_expected, "My DROP VIEW statement for 8.1 is correct");
563
564my $drop_view_opts2 = { add_drop_view => 1, no_comments => 1, postgres_version => 9.001 };
565my $drop_view_9_1_produced = SQL::Translator::Producer::PostgreSQL::create_view($view1, $drop_view_opts2);
566
567my $drop_view_9_1_expected = "DROP VIEW IF EXISTS view_foo;
568CREATE VIEW view_foo ( id, name ) AS
569 SELECT id, name FROM thing
570";
aee4b66e 571
25c74c43 572is($drop_view_9_1_produced, $drop_view_9_1_expected, "My DROP VIEW statement for 9.1 is correct");