File: SQLServer.pm

package info (click to toggle)
libsql-translator-perl 0.11011-1
  • links: PTS, VCS
  • area: main
  • in suites: wheezy
  • size: 15,380 kB
  • sloc: perl: 251,748; sql: 3,805; xml: 233; makefile: 7
file content (258 lines) | stat: -rw-r--r-- 6,489 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
package SQL::Translator::Generator::DDL::SQLServer;

=head1 NAME

SQL::Translator::Generator::DDL::SQLServer - A Moo based MS SQL Server DDL
generation engine.

=head1 DESCRIPTION

I<documentation volunteers needed>

=cut

use Moo;
use SQL::Translator::Schema::Constants;

with 'SQL::Translator::Generator::Role::Quote';
with 'SQL::Translator::Generator::Role::DDL';

sub quote_chars { [qw([ ])] }
sub name_sep { q(.) }

sub _build_numeric_types {
   +{
      int => 1,
   }
}

sub _build_unquoted_defaults {
   +{
      NULL => 1,
   }
}

sub _build_type_map {
   +{
      date => 'datetime',
      'time' => 'datetime',
   }
}

sub _build_sizeless_types {
   +{ map { $_ => 1 }
         qw( tinyint smallint int integer bigint text bit image datetime ) }
}

sub field {
   my ($self, $field) = @_;

   return join ' ', $self->field_name($field), ($self->field_type($field)||die 'type is required'),
      $self->field_autoinc($field),
      $self->field_nullable($field),
      $self->field_default($field),
}

sub field_autoinc { ( $_[1]->is_auto_increment ? 'IDENTITY' : () ) }

sub primary_key_constraint {
  'CONSTRAINT ' .
    $_[0]->quote($_[1]->name || $_[1]->table->name . '_pk') .
    ' PRIMARY KEY (' .
    join( ', ', map $_[0]->quote($_), $_[1]->fields ) .
    ')'
}

sub index {
  'CREATE INDEX ' .
   $_[0]->quote($_[1]->name || $_[1]->table->name . '_idx') .
   ' ON ' . $_[0]->quote($_[1]->table->name) .
   ' (' . join( ', ', map $_[0]->quote($_), $_[1]->fields ) . ');'
}

sub unique_constraint_single {
  my ($self, $constraint) = @_;

  'CONSTRAINT ' .
   $self->unique_constraint_name($constraint) .
   ' UNIQUE (' . join( ', ', map $self->quote($_), $constraint->fields ) . ')'
}

sub unique_constraint_name {
  my ($self, $constraint) = @_;
  $self->quote($constraint->name || $constraint->table->name . '_uc' )
}

sub unique_constraint_multiple {
  my ($self, $constraint) = @_;

  'CREATE UNIQUE NONCLUSTERED INDEX ' .
   $self->unique_constraint_name($constraint) .
   ' ON ' . $self->quote($constraint->table->name) . ' (' .
   join( ', ', $constraint->fields ) . ')' .
   ' WHERE ' . join( ' AND ',
    map $self->quote($_->name) . ' IS NOT NULL',
    grep { $_->is_nullable } $constraint->fields ) . ';'
}

sub foreign_key_constraint {
  my ($self, $constraint) = @_;

  my $on_delete = uc ($constraint->on_delete || '');
  my $on_update = uc ($constraint->on_update || '');

  # The default implicit constraint action in MSSQL is RESTRICT
  # but you can not specify it explicitly. Go figure :)
  for (map uc $_ || '', $on_delete, $on_update) {
    undef $_ if $_ eq 'RESTRICT'
  }

  'ALTER TABLE ' . $self->quote($constraint->table->name) .
   ' ADD CONSTRAINT ' .
   $self->quote($constraint->name || $constraint->table->name . '_fk') .
   ' FOREIGN KEY' .
   ' (' . join( ', ', map $self->quote($_), $constraint->fields ) . ') REFERENCES '.
   $self->quote($constraint->reference_table) .
   ' (' . join( ', ', map $self->quote($_), $constraint->reference_fields ) . ')'
   . (
     $on_delete && $on_delete ne "NO ACTION"
       ? ' ON DELETE ' . $on_delete
       : ''
   ) . (
     $on_update && $on_update ne "NO ACTION"
       ? ' ON UPDATE ' . $on_update
       : ''
   ) . ';';
}

sub enum_constraint_name {
  my ($self, $field_name) = @_;
  $self->quote($field_name . '_chk' )
}

sub enum_constraint {
  my ( $self, $field_name, $vals ) = @_;

  return (
     'CONSTRAINT ' . $self->enum_constraint_name($field_name) .
       ' CHECK (' . $self->quote($field_name) .
       ' IN (' . join( ',', map qq('$_'), @$vals ) . '))'
  )
}

sub constraints {
  my ($self, $table) = @_;

  (map $self->enum_constraint($_->name, { $_->extra }->{list} || []),
     grep { 'enum' eq lc $_->data_type } $table->get_fields),

  (map $self->primary_key_constraint($_),
     grep { $_->type eq PRIMARY_KEY } $table->get_constraints),

  (map $self->unique_constraint_single($_),
     grep {
       $_->type eq UNIQUE &&
       !grep { $_->is_nullable } $_->fields
     } $table->get_constraints),
}

sub table {
   my ($self, $table) = @_;
   join ( "\n", $self->table_comments($table), '' ) .
   join ( "\n\n",
      'CREATE TABLE ' . $self->quote($table->name) . " (\n".
        join( ",\n",
           map { "  $_" }
           $self->fields($table),
           $self->constraints($table),
        ) .
        "\n);",
        $self->unique_constraints_multiple($table),
        $self->indices($table),
   )
}

sub unique_constraints_multiple {
  my ($self, $table) = @_;
  (map $self->unique_constraint_multiple($_),
     grep {
        $_->type eq UNIQUE &&
        grep { $_->is_nullable } $_->fields
     } $table->get_constraints)
}

sub drop_table {
   my ($self, $table) = @_;
   my $name = $table->name;
   my $q_name = $self->quote($name);
   "IF EXISTS (SELECT name FROM sysobjects WHERE name = '$name' AND type = 'U')" .
      " DROP TABLE $q_name;"
}

sub remove_table_constraints {
   my ($self, $table) = @_;
   my $name = $table->name;
   my $q_name = $self->quote($name);
   "IF EXISTS (SELECT name FROM sysobjects WHERE name = '$name' AND type = 'U')" .
   " ALTER TABLE $q_name NOCHECK CONSTRAINT all;"
}

sub drop_tables {
   my ($self, $schema) = shift;

   if ($self->add_drop_table) {
      my @tables = sort { $b->order <=> $a->order } $schema->get_tables;
      return join "\n", (
         ( $self->add_comments ? (
         '--',
         '-- Turn off constraints',
         '--',
         '',
         ) : () ),
         (map $self->remove_table_constraints($_), @tables),
         ( $self->add_comments ? (
         '--',
         '-- Drop tables',
         '--',
         '',
         ) : () ),
         (map $self->drop_table($_), @tables),
      )
   }
   return '';
}

sub foreign_key_constraints {
   my ($self, $schema) = @_;
   ( map $self->foreign_key_constraint($_),
     grep { $_->type eq FOREIGN_KEY }
     map $_->get_constraints,
     $schema->get_tables )
}

sub schema {
   my ($self, $schema) = @_;

   $self->header_comments .
      $self->drop_tables($schema) .
      join("\n\n", map $self->table($_), grep { $_->name } $schema->get_tables) .
      "\n" . join "\n", $self->foreign_key_constraints($schema)
}

1;

=head1 AUTHORS

See the included AUTHORS file:
L<http://search.cpan.org/dist/SQL-Translator/AUTHORS>

=head1 COPYRIGHT

Copyright (c) 2012 the SQL::Translator L</AUTHORS> as listed above.

=head1 LICENSE

This code is free software and may be distributed under the same terms as Perl
itself.

=cut