File Coverage

blib/lib/VSGDR/StaticData.pm
Criterion Covered Total %
statement 35 527 6.6
branch 0 166 0.0
condition 0 15 0.0
subroutine 12 31 38.7
pod 0 18 0.0
total 47 757 6.2


line stmt bran cond sub pod time code
1             package VSGDR::StaticData;
2            
3 1     1   71674 use strict;
  1         3  
  1         30  
4 1     1   5 use warnings;
  1         2  
  1         22  
5            
6 1     1   26 use 5.010;
  1         4  
7            
8 1     1   6 use List::Util qw(max);
  1         2  
  1         156  
9 1     1   633 use List::MoreUtils qw(any);
  1         13333  
  1         6  
10 1     1   1837 use POSIX qw(strftime);
  1         6859  
  1         6  
11 1     1   1535 use Carp;
  1         3  
  1         67  
12 1     1   2314 use DBI;
  1         18477  
  1         62  
13 1     1   738 use Data::Dumper;
  1         7201  
  1         65  
14 1     1   745 use English;
  1         3878  
  1         6  
15             if ($OSNAME eq 'MSWin32') {require Win32}
16            
17             ##TODO 1. Fix multi-column primary/unique keys.
18             ##TODO 2. Check that non-key identity columns are handled correctly when they occur in the final position in the table.
19            
20             =head1 NAME
21            
22             VSGDR::StaticData - Static data script support package for SSDT post-deployment steps, Ded MedVed.
23            
24             =head1 VERSION
25            
26             Version 0.47
27            
28             =cut
29            
30             our $VERSION = '0.47';
31            
32            
33             sub databaseName {
34            
35 0     0 0   local $_ = undef ;
36            
37 0           my $dbh = shift ;
38            
39 0           my $sth2 = $dbh->prepare(databaseNameSQL());
40 0           my $rs = $sth2->execute();
41 0           my $res = $sth2->fetchall_arrayref() ;
42            
43 0           return $$res[0][0] ;
44            
45             }
46            
47             sub databaseNameSQL {
48            
49 0     0 0   return <<"EOF" ;
50            
51             select db_name()
52            
53             EOF
54            
55             }
56            
57             sub dependency {
58            
59 0     0 0   local $_ = undef ;
60            
61 0           my $dbh = shift ;
62            
63 0           my $sth2 = $dbh->prepare( dependencySQL());
64 0           my $rs = $sth2->execute();
65 0           my $res = $sth2->fetchall_arrayref() ;
66            
67 0 0         if ( scalar @{$res} ) { return $res ; } ;
  0            
  0            
68 0           return [] ;
69             }
70            
71            
72            
73             sub dependencySQL {
74            
75 0     0 0   return <<"EOF" ;
76             select distinct
77             tc2.TABLE_CATALOG as to_CATALOG
78             , tc2.TABLE_SCHEMA as to_SCHEMA
79             , tc2.TABLE_NAME as to_NAME
80             , tc1.TABLE_CATALOG as from_CATALOG
81             , tc1.TABLE_SCHEMA as from_SCHEMA
82             , tc1.TABLE_NAME as from_NAME
83             , rc.CONSTRAINT_NAME as to_CONSTRAINT
84             from INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS rc
85             join INFORMATION_SCHEMA.TABLE_CONSTRAINTS tc1
86             on tc1.CONSTRAINT_SCHEMA = rc.CONSTRAINT_SCHEMA
87             and tc1.CONSTRAINT_CATALOG = rc.CONSTRAINT_CATALOG
88             and tc1.CONSTRAINT_NAME = rc.CONSTRAINT_NAME
89             join INFORMATION_SCHEMA.TABLE_CONSTRAINTS tc2
90             on tc2.CONSTRAINT_SCHEMA = rc.CONSTRAINT_SCHEMA
91             and tc2.CONSTRAINT_CATALOG = rc.CONSTRAINT_CATALOG
92             and tc2.CONSTRAINT_NAME = rc.UNIQUE_CONSTRAINT_NAME
93            
94             EOF
95            
96             }
97            
98            
99             sub generateScript {
100            
101 0     0 0   my ${LargeDataSetThreshhold} = 30 ;
102            
103 0           local $_ = undef;
104            
105 0           my $dbh = shift ;
106 0           my $schema = shift ;
107 0           my $table = shift ;
108            
109 0 0         croak "bad arg dbh" unless defined $dbh;
110 0 0         croak "bad arg schema" unless defined $schema;
111 0 0         croak "bad arg table" unless defined $table;
112            
113 0 0         $schema = substr $schema, 1, -1 if $schema =~ m/\A \[ .+ \] \Z /msix;
114 0 0         $table = substr $table, 1, -1 if $table =~ m/\A \[ .+ \] \Z /msix;
115 0           my $combinedName = "${schema}.${table}";
116 0           my $quotedCombinedName = "[${schema}].[${table}]";
117 0           my $tableVarName = "LocalTable_${table}";
118            
119 0           my $quotedSchema = "[${schema}]";
120 0           my $quotedTable = "[${table}]";
121            
122 0           my $database = databaseName($dbh);
123            
124 1     1   931 no warnings;
  1         3  
  1         83  
125 0 0         my $userName = $OSNAME eq 'MSWin32' ? eval('Win32::LoginName') : ${[getpwuid( $< )]}->[6]; $userName =~ s/,.*//;
  0            
  0            
126 1     1   7 use warnings;
  1         2  
  1         6351  
127            
128 0           my $date = strftime "%d/%m/%Y", localtime;
129            
130 0           my $hasId = has_idCols($dbh,$schema,$table) ;
131 0           my $idCol = undef ;
132 0 0         if ($hasId) {
133 0           $idCol = idCols($dbh,$schema,$table) ;
134             }
135             #warn Dumper $idCol ;
136 0           my $set_IDENTITY_INSERT_ON = "";
137 0           my $set_IDENTITY_INSERT_OFF = "";
138 0 0         $set_IDENTITY_INSERT_ON = "set IDENTITY_INSERT ${quotedCombinedName} ON" if $hasId;
139 0 0         $set_IDENTITY_INSERT_OFF = "set IDENTITY_INSERT ${quotedCombinedName} OFF" if $hasId;
140            
141            
142 0           my $ra_columns = columns($dbh,$schema,$table);
143             #warn Dumper $quotedSchema,$quotedTable ;
144 0           my $ra_pkcolumns = pkcolumns($dbh,$quotedSchema,$quotedTable);
145            
146 0 0         croak "${quotedCombinedName} doesn't appear to be a valid table" unless scalar @{$ra_columns};
  0            
147            
148             #warn Dumper $ra_columns ;
149             #exit ;
150            
151             # croak 'No Primary Key defined' unless scalar @{$ra_pkcolumns};
152             # croak 'Unusable Primary Key defined' unless scalar @{$ra_pkcolumns} == 1;
153            
154 0 0         my @IsColumnNumeric = map { $_->[1] =~ m{uniqueidentifier|char|text|date}i ? 0 : 1 ; } @{$ra_columns} ;
  0            
  0            
155             #warn Dumper $ra_columns;
156             #warn Dumper @IsColumnNumeric ;
157             #exit;
158            
159 0           my $primaryKeyCheckClause = "";
160 0           my $pk_column = undef ; #$ra_pkcolumns->[0][0];
161             #my @nonKeyColumns = grep { $_->[0][0] ne $pk_column } @{$ra_columns};
162            
163 0           my @nonKeyColumns = () ;
164            
165            
166 0           my $widest_column_name_len = max ( map { length ($_->[0]); } @{$ra_columns} ) ;
  0            
  0            
167 0           my $widest_column_name_padding = int($widest_column_name_len/4) + 4;
168            
169 0           my $flatcolumnlist = "" ;
170 0           my $flatvariablelist = "" ;
171 0           foreach my $l (@{$ra_columns}) {
  0            
172 0           do { local $" = ""; $flatcolumnlist .= "[$l->[0]]" ; $flatcolumnlist .= ", "} ;
  0            
  0            
  0            
173 0           do { local $" = ""; $flatvariablelist .= "@"."$l->[0]" ; $flatvariablelist .= ","} ;
  0            
  0            
  0            
174             }
175 0           $flatcolumnlist =~ s{ ,\s? \z }{}msx;
176 0           $flatvariablelist =~ s{ ,\s? \z }{}msx;
177            
178            
179             #warn Dumper @{$ra_pkcolumns} ;
180            
181 0           my $reportingPKCols = "" ;
182 0           my $recordExistenceCheckSQL = "" ;
183 0 0         if ( ! scalar @{$ra_pkcolumns} ) {
  0 0          
184 0           my @pk_ColumnsCheck = () ;
185 0           foreach my $l (@{$ra_columns}) {
  0            
186 0           my $varlen = length($l->[0]) ;
187 0           my $colpadding = $widest_column_name_padding - (int(($varlen)/4));
188 0           my $varpadding = $widest_column_name_padding - (int(($varlen+1)/4));
189 0           push @pk_ColumnsCheck , "([$l->[0]]" . "\t"x$varpadding . " = \@$l->[0]" . "\t"x$varpadding . "or ([$l->[0]]". "\t"x$varpadding . " is null and \@$l->[0] ". "\t"x$varpadding . " is null ) ) " ;
190             }
191             #my @pk_ColumnsCheck = map { "( $_->[0]\t\t\t = \@$_->[0] or ( $_->[0]\t\t\t is null and \@$_->[0] is null ) ) " } @{$ra_columns} ;
192 0           $primaryKeyCheckClause = "where\t" . do { local $" = "\n\t\t\t\tand\t\t"; "@pk_ColumnsCheck" };
  0            
  0            
193            
194 0           $recordExistenceCheckSQL = <<"EOF";
195             if exists
196             (
197             select $flatvariablelist
198             except
199             select ${flatcolumnlist}
200             from ${quotedCombinedName}
201             )
202             EOF
203            
204             }
205 0           elsif ( scalar @{$ra_pkcolumns} != 1 ) {
206            
207 0           my @pk_ColumnsCheck = () ;
208            
209 0           foreach my $l (@{$ra_pkcolumns}) {
  0            
210 0           my $varlen = length($l->[0]) ;
211 0           my $colpadding = $widest_column_name_padding - (int(($varlen)/4));
212 0           my $varpadding = $widest_column_name_padding - (int(($varlen+1)/4));
213 0           push @pk_ColumnsCheck , "([$l->[0]]" . "\t"x$varpadding . " = \@$l->[0]" . "\t"x$varpadding . "or ([$l->[0]]". "\t"x$varpadding . " is null and \@$l->[0] ". "\t"x$varpadding . " is null ) ) " ;
214            
215 0           my @reportingPKCols = map { "$_->[0]" } @{$ra_columns} ;
  0            
  0            
216 0           $reportingPKCols = do {local $" = ", "; "@reportingPKCols"} ;
  0            
  0            
217            
218             # my @pk_ColumnsCheck = map { "$_->[0]\t\t\t = \@$_->[0]" } @{$ra_columns} ;
219             }
220 0           $primaryKeyCheckClause = "where\t" . do { local $" = "\n\t\t\t\tand\t\t"; "@pk_ColumnsCheck" } ;
  0            
  0            
221            
222 0           foreach my $col (@{$ra_columns}) {
  0            
223             #warn Dumper @{$ra_columns};
224             #warn Dumper $col;
225 0 0         push @nonKeyColumns, $col unless grep {$_->[0] eq $col->[0] } @{$ra_pkcolumns} ;
  0            
  0            
226             }
227 0           $recordExistenceCheckSQL = <<"EOF";
228             if not exists
229             (
230             select *
231             from ${quotedCombinedName}
232             ${primaryKeyCheckClause}
233             )
234             EOF
235             }
236             else {
237 0           $reportingPKCols = $ra_pkcolumns->[0][0];
238 0           $pk_column = $ra_pkcolumns->[0][0];
239 0           $primaryKeyCheckClause = "where ${pk_column} = \@${pk_column}";
240 0           @nonKeyColumns = grep { $_->[0] ne $pk_column } @{$ra_columns};
  0            
  0            
241            
242 0           $recordExistenceCheckSQL = <<"EOF";
243             if not exists
244             (
245             select ${flatcolumnlist}
246             from ${quotedCombinedName}
247             ${primaryKeyCheckClause}
248             )
249             EOF
250             }
251            
252            
253 0           my $variabledeclaration = "declare\t" ;
254 0           my $tabledeclaration = "(\t\tStaticDataPopulationId\t\tint\tnot null identity(1,1)\n\t,\t\t" ;
255 0           my $selectstatement = "select\t" ;
256 0           my $insertclause = "insert into ${combinedName}\n\t\t\t\t\t\t(";
257 0           my $valuesclause = "values(";
258             # my $flatcolumnlist = "" ;
259 0           my $flatExtractColumnList = "" ;
260             # my $flatvariablelist = "" ;
261 0           my $updateColumns = "set\t";
262 0           my $printStatement = "" ;
263            
264             #warn Dumper $widest_column_name_len;
265             #warn Dumper $widest_column_name_padding;
266             #warn Dumper @{$ra_columns};
267 0           foreach my $l (@{$ra_columns}) {
  0            
268 0           my $varlen = length($l->[0]) ;
269 0           my $colpadding = $widest_column_name_padding - (int(($varlen)/4));
270 0           my $varpadding = $widest_column_name_padding - (int(($varlen+1)/4));
271             #warn Dumper $l->[0];
272             #warn Dumper $varlen;
273             #warn Dumper $padding;
274             #warn Dumper $variabledeclaration;
275             #warn Dumper $varpadding;
276             # do { local $" = "\t"; $variabledeclaration .= "@"."@{$l}[0,1,2,3,5]" ; $variabledeclaration .= "\n\t,\t\t"} ;
277 0           do { local $" = "\t"; $variabledeclaration .= "@"."@{$l}[0]". "\t"x$varpadding . "$$l[1]" ."@{$l}[2,3]" ; $variabledeclaration .= "\n\t,\t\t"} ;
  0            
  0            
  0            
  0            
  0            
278             # do { local $" = "\t"; $tabledeclaration .= "@{$l}" ; $tabledeclaration .= "\n\t\t,\t"} ;
279 0           do { local $" = "\t"; $tabledeclaration .= "[@{$l}[0]]". "\t"x$colpadding . "[$$l[1]]" ."@{$l}[2,3,4,5]" ; $tabledeclaration .= "\n\t,\t\t"} ;
  0            
  0            
  0            
  0            
  0            
280             # do { local $" = ""; $selectstatement .= "@"."$l->[0]\t\t= $l->[0]" ; $selectstatement .= "\n\t\t,\t\t"} ;
281 0           do { local $" = ""; $selectstatement .= "@"."$l->[0]" . "\t"x$varpadding ."= [$l->[0]]" ; $selectstatement .= "\n\t\t,\t\t"} ;
  0            
  0            
  0            
282 0           do { local $" = ""; $insertclause .= "[$l->[0]]" ; $insertclause .= ", "} ;
  0            
  0            
  0            
283 0           do { local $" = ""; $valuesclause .= "[$l->[0]]" ; $valuesclause .= ", "} ;
  0            
  0            
  0            
284             # do { local $" = ""; $flatcolumnlist .= "[$l->[0]]" ; $flatcolumnlist .= ", "} ;
285 0 0         do { local $" = ""; $flatExtractColumnList .= $l->[1] =~ m{\A(?:date|datetime[2]?|smalldatetime)\z}i ? "convert(varchar(30),[$l->[0]],120)" : "[$l->[0]]" ; $flatExtractColumnList .= ", "} ;
  0            
  0            
  0            
286             # do { local $" = ""; $flatvariablelist .= "@"."$l->[0]" ; $flatvariablelist .= ","} ;
287            
288 0           do { local $" = ""; $printStatement .= "' $$l[0]: ' " ; } ;
  0            
  0            
289 0 0         my $printFragment = $$l[1] !~ m{ (?: char ) }ixms
290             ? "cast( \@$$l[0] as varchar(128))"
291             : "\@$$l[0]" ;
292            
293 0           $printFragment = " + case when ${printFragment} is null then 'NULL' else '''' + ${printFragment} + '''' end + " ; ;
294 0           $printStatement .= $printFragment ;
295             }
296 0           foreach my $l (@nonKeyColumns) {
297             # create update statement for each non-identity column.
298 0 0 0       if ( ! $hasId || ( $l->[0] ne $idCol ) ) {
    0          
299             #warn Dumper $l;
300 0           do { local $" = ""; $updateColumns .= "$l->[0]\t\t= "."@"."$l->[0]" ; $updateColumns .= "\n\t\t\t\t\t,\t"} ;
  0            
  0            
  0            
301             }
302             elsif($hasId) {
303 0           do { local $" = ""; $updateColumns .= "/* cannot update this identity column -- $l->[0]\t\t= "."@"."$l->[0]" ; $updateColumns .= "\n\t\t\t\t\t,*/\t"} ;
  0            
  0            
  0            
304             }
305             }
306            
307            
308             # trim off erroneous trailing cruft - better to resign array interpolations above .
309 0           $variabledeclaration =~ s{ \n\t,\t\t \z }{}msx;
310 0           $tabledeclaration =~ s{ \n\t,\t\t \z }{}msx;
311 0           $selectstatement =~ s{ \n\t\t,\t\t \z }{}msx;
312 0           $updateColumns =~ s{ \n\t\t\t,\t \z }{}msx;
313 0           $insertclause =~ s{ ,\s? \z }{}msx;
314 0           $valuesclause =~ s{ ,\s? \z }{}msx;
315             # $flatcolumnlist =~ s{ ,\s? \z }{}msx;
316 0           $flatExtractColumnList =~ s{ ,\s? \z }{}msx;
317             # $flatvariablelist =~ s{ ,\s? \z }{}msx;
318 0           $updateColumns =~ s{ \n\t\t\t\t\t,\t \z }{}msx;
319 0           $printStatement =~ s{ \+\s \z }{}msx;
320            
321            
322 0           $tabledeclaration .= "\n\t)";
323 0           $insertclause .= ")";
324 0           $valuesclause .= ")";
325            
326 0           my $insertingPrintStatement = "print 'Inserting ${combinedName}:' + " . $printStatement ;
327 0           my $updatingPrintStatement = "print 'Updating ${combinedName}: ' + " . $printStatement;
328            
329            
330             # my $ra_data = getCurrentTableData($dbh,$combinedName,$pk_column,$flatcolumnlist);
331             # my $ra_data = getCurrentTableData($dbh,$quotedCombinedName ,$pk_column,$flatcolumnlist);
332 0           my $ra_data = getCurrentTableData($dbh,$quotedCombinedName ,$pk_column,$flatExtractColumnList);
333            
334 0           my @valuesTable ;
335 0           my $valuesClause = "values\n\t\t\t";
336            
337 0           my $lno = 1;
338 0           foreach my $ra_row (@{$ra_data}){
  0            
339             # warn Dumper $ra_row;
340 0           my @outVals = undef ;
341             #warn Dumper @{$ra_row} ;
342             #exit;
343 0           for ( my $i = 0; $i < scalar @{$ra_row}; $i++ ) {
  0            
344             #warn Dumper $ra_row->[$i] ;
345             #warn Dumper $IsColumnNumeric[$i] ;
346             # $ra_row->[$i] = ( defined $ra_row->[$i] ) ? $ra_row->[$i] : "null" ;
347            
348 0 0 0       if ( ( $IsColumnNumeric[$i] == 1 ) and ( not ( defined ($ra_row->[$i]) ) ) ) {
349 0           $outVals[$i] = 'null' ;
350             }
351 0 0 0       if ( ( $IsColumnNumeric[$i] == 0 ) and ( not ( defined ($ra_row->[$i]) ) ) ) {
352 0           $outVals[$i] = 'null' ;
353             }
354 0 0 0       if ( ( $IsColumnNumeric[$i] == 1 ) and ( ( defined ($ra_row->[$i]) ) ) ) {
355 0           $outVals[$i] = $ra_row->[$i] ;
356             }
357 0 0 0       if ( ( $IsColumnNumeric[$i] == 0 ) and ( ( defined ($ra_row->[$i]) ) ) ) {
358 0 0         if (${$ra_columns}[$i][1] =~ m{\A(?:date|datetime[2]?|smalldatetime)\z}i) {
  0            
359 0           $outVals[$i] = "convert(". ${$ra_columns}[$i][1] ."," . $dbh->quote($ra_row->[$i]) . ",120)" ;
  0            
360             }
361             else {
362 0           $outVals[$i] = $dbh->quote($ra_row->[$i]) ;
363             }
364             }
365             }
366 0           push @valuesTable, \@outVals ;
367             #my @outVals = map { $ColumnNumericity{$_} == 1 ? $_ : $dbh->quote($_) } @{$ra_row};
368 0           my $line = do{ local $" = ", "; "@outVals" } ;
  0            
  0            
369             #$valuesClause .= "(\t" . $line . ")" . "\n\t\t,\t" ;
370 0           $lno++;
371             }
372            
373 0           my @maxWidth;
374             my $maxCol;
375            
376 0 0         if ( scalar @valuesTable ) {
377 0           my @tmp = @{$valuesTable[0]};
  0            
378 0           $maxCol = scalar @tmp -1 ;
379 0           for ( my $i = 0; $i <= $maxCol; $i++ ) {
380 0           push @maxWidth, 0;
381             }
382 0           for ( my $i = 0; $i < scalar @valuesTable; $i++ ) {
383 0           my @tmp = @{$valuesTable[$i]};
  0            
384 0           for ( my $i = 0; $i <= $maxCol; $i++ ) {
385 0 0         if (length($tmp[$i]) > $maxWidth[$i] ) {
386 0           $maxWidth[$i] = length($tmp[$i]) ;
387             }
388             }
389             }
390             }
391            
392             #warn Dumper @maxWidth ;
393            
394 0           for ( my $i = 0; $i < scalar @valuesTable; $i++ ) {
395 0           my @tmp = @{$valuesTable[$i]};
  0            
396 0           my $line = "";
397 0           for ( my $j = 0; $j <= $maxCol; $j++ ) {
398 0           my $val = $tmp[$j];
399 0           my $valWidth = length($val);
400 0           my $PadLength = $maxWidth[$j]-$valWidth;
401 0           my $padding = " "x$PadLength;
402 0           $line .= ", ${padding}${val}";
403             }
404 0           $line =~ s{ ^,\s}{}msx;
405 0           $valuesClause .= "(\t" . $line . ")" . "\n\t\t,\t" ;
406             }
407            
408 0           $valuesClause =~ s{ \n\t\t,\t \z }{}msx;
409            
410            
411 0           my $noopPrintStatement = "'Nothing to update. Values are unchanged.'";
412 0           my $printNoOpStatement = "print ${noopPrintStatement}" ;
413 0 0         if ( ${pk_column} ) {
414 0           $noopPrintStatement = "'Nothing to update. ${combinedName}: Values are unchanged for Primary/Unique Key: '";
415 0           $printNoOpStatement = "print ${noopPrintStatement} + cast(\@${reportingPKCols} as varchar(1000)) "
416             }
417            
418 0           my $elsePrintSection = <<"EOF";
419             else begin
420             ${printNoOpStatement}
421             end
422             EOF
423            
424 0 0         if ( scalar @{$ra_data} > ${LargeDataSetThreshhold} ){
  0            
425 0           $insertingPrintStatement = "" ;
426 0           $updatingPrintStatement = "" ;
427 0           $elsePrintSection = "" ;
428             }
429            
430 0           my ${elseBlock} = "";
431            
432 0 0         if ( scalar @nonKeyColumns ) {
433 0           ${elseBlock} = <<"EOF";
434            
435             -- if the static data doesn''t match what is already there then update it.
436             -- 'except' handily handles null as equal. Saves some extensive twisted logic.
437             else begin
438             if exists
439             (
440             select ${flatcolumnlist}
441             from $quotedCombinedName
442             ${primaryKeyCheckClause}
443             except
444             select ${flatvariablelist}
445             ) begin
446             $updatingPrintStatement
447             if \@DeploySwitch = 1 begin
448             update s
449             ${updateColumns}
450             from $quotedCombinedName s
451             ${primaryKeyCheckClause}
452             end
453             set \@ChangedCount += 1 ;
454             end
455             ${elsePrintSection}
456             end
457             EOF
458             }
459            
460            
461 0           my $tmp_sv = substr(${table},0,20) ;
462 0           my $savePointName = "sc_${tmp_sv}_SP";
463            
464 0           my ${printChangedTotalsSection} = "" ;
465             #warn Dumper @nonKeyColumns ;
466            
467 0 0         if ( scalar @{$ra_data} > ${LargeDataSetThreshhold} ){
  0            
468 0           $printChangedTotalsSection = "print 'Total count of inserted records : ' + cast( \@InsertedCount as varchar(10))" ;
469 0           $printChangedTotalsSection .= "\n\tprint 'Total count of altered records : ' + cast( \@ChangedCount as varchar(10))" ;
470             }
471            
472            
473 0           return <<"EOF";
474            
475             /****************************************************************************************
476             * Database: ${database}
477             * Author : ${userName}
478             * Date : ${date}
479             * Purpose : Static data deployment script for ${combinedName}
480             *
481             *
482             * Version History
483             * ---------------
484             * 1.0.0 ${date} ${userName}
485             * Created.
486             ***************************************************************************************/
487            
488             set nocount on
489            
490             declare \@DeployCmd varchar(20)
491             ,\@DeploySwitch bit
492            
493             set \@DeployCmd = '\$(StaticDataDeploy)'
494             set \@DeploySwitch = 0
495             --Check whether a deploy has been stated.
496             if isnull(upper(\@DeployCmd) , '') <> 'DEPLOY'
497             begin
498             set \@DeploySwitch = 0 --FALSE, only run a dummy deploy where no actual data will be modified.
499             print 'Deploy Type: Dummy Deploy (No data will be changed)'
500             end
501             else
502             begin
503             set \@DeploySwitch = 1 --TRUE, run real deploy.
504             print 'Deploy Type: Actual Deploy'
505             end
506            
507            
508            
509             begin try
510            
511             -- Declarations
512             declare \@ct int
513             , \@i int
514             , \@InsertedCount int = 0
515             , \@ChangedCount int = 0
516            
517             declare \@localTransactionStarted bit;
518            
519            
520             begin transaction
521             save transaction ${savePointName} ;
522            
523             set \@localTransactionStarted = 1;
524            
525             declare \@${tableVarName} table
526             ${tabledeclaration}
527            
528             ; with src as
529             (
530             select *
531             from ( ${valuesClause}
532             ) AS vtable
533             ( $flatcolumnlist)
534             )
535             insert into
536             \@${tableVarName}
537             ( ${flatcolumnlist}
538             )
539             select ${flatcolumnlist}
540             from src
541            
542             ${variabledeclaration}
543            
544            
545            
546            
547             -- count how many records need to be inserted
548             select \@ct = count(*) from \@${tableVarName}
549            
550             set \@i = 1
551             -- insert the records into the ${table} table if they don't already exist, otherwise update them
552             while \@i <=\@ct begin
553            
554             ${selectstatement}
555             from \@${tableVarName}
556             where StaticDataPopulationId\t\t= \@i
557            
558             ${recordExistenceCheckSQL}
559             begin
560             $insertingPrintStatement
561             if \@DeploySwitch = 1 begin
562             ${set_IDENTITY_INSERT_ON}
563             ${insertclause}
564             values (${flatvariablelist})
565             ${set_IDENTITY_INSERT_OFF}
566             end
567             set \@InsertedCount += 1 ;
568             end
569             ${elseBlock}
570            
571             set \@i=\@i+1
572             end
573            
574             commit
575            
576            
577             ${printChangedTotalsSection}
578            
579             end try
580             begin catch
581            
582             -- if xact_state() = -1 then the transaction isn't recorded in \@\@trancount so check both
583             if \@\@trancount > 0 or xact_state() = -1 begin
584             if xact_state() = -1
585             rollback; -- we can't do anything else
586             -- Rollback any locally begun transaction to savepoint. Don't fail the whole deployment if it's transactional.
587             -- If our transaction is the only one, then just rollback.
588             if \@\@trancount > 1 begin
589             if \@localTransactionStarted is not null and \@localTransactionStarted = 1 begin
590             rollback transaction ${savePointName};
591             commit ; -- windback our local transaction completely
592             end;
593             --else it's probably nothing to do with us. but we have to rollback anyway
594             else
595             rollback;
596             end
597             else begin
598             -- final check to see if we need to unwind the transaction
599             if \@\@trancount > 0
600             rollback;
601             end;
602             end;
603            
604             print 'Deployment of ${combinedName} static data failed. This script was rolled back.';
605             print error_message();
606            
607             ${set_IDENTITY_INSERT_OFF};
608            
609            
610             end catch
611            
612             go
613            
614            
615             EOF
616            
617             }
618            
619             sub generateTestDataScript {
620            
621 0     0 0   local $_ = undef;
622            
623 0           my $dbh = shift ;
624 0           my $schema = shift ;
625 0           my $table = shift ;
626 0           my $sql = shift ;
627 0           my $use_MinimalForm = shift ;
628 0           my $use_IgnoreNulls = shift ;
629            
630 0 0         croak "bad arg dbh" unless defined $dbh;
631 0 0         croak "bad arg schema" unless defined $schema;
632 0 0         croak "bad arg table" unless defined $table;
633 0 0         croak "bad arg sql" unless defined $sql;
634 0 0         croak "bad arg minimal form" unless defined $use_MinimalForm;
635 0 0         croak "bad arg ignore nulls" unless defined $use_IgnoreNulls;
636            
637 0 0         $schema = substr $schema, 1, -1 if $schema =~ m/\A \[ .+ \] \Z /msix;
638 0 0         $table = substr $table, 1, -1 if $table =~ m/\A \[ .+ \] \Z /msix;
639 0           my $combinedName = "${schema}.${table}";
640 0           my $quotedCombinedName = "[${schema}].[${table}]";
641            
642 0           my $quotedSchema = "[${schema}]";
643 0           my $quotedTable = "[${table}]";
644            
645 0           my $database = databaseName($dbh);
646            
647 0           my $hasId = has_idCols($dbh,$schema,$table) ;
648 0           my $idCol = undef ;
649 0 0         if ($hasId) {
650 0           $idCol = idCols($dbh,$schema,$table) ;
651             }
652             #warn Dumper $idCol ;
653 0           my $set_IDENTITY_INSERT_ON = "";
654 0           my $set_IDENTITY_INSERT_OFF = "";
655 0 0         $set_IDENTITY_INSERT_ON = "set IDENTITY_INSERT ${quotedCombinedName} ON" if $hasId;
656 0 0         $set_IDENTITY_INSERT_OFF = "set IDENTITY_INSERT ${quotedCombinedName} OFF" if $hasId;
657            
658            
659 0           my $ra_columns = columns($dbh,$schema,$table);
660            
661 0 0         croak "${quotedCombinedName} doesn't appear to be a valid table" unless scalar @{$ra_columns};
  0            
662            
663            
664 0 0         my @IsColumnNumeric = map { $_->[1] =~ m{uniqueidentifier|char|text|date}i ? 0 : 1 ; } @{$ra_columns} ;
  0            
  0            
665 0           my @nonKeyColumns = () ;
666            
667 0           my $widest_column_name_len = max ( map { length ($_->[0]); } @{$ra_columns} ) ;
  0            
  0            
668 0           my $widest_column_name_padding = int($widest_column_name_len/4) + 4;
669            
670 0           my $flatvariablelist = "" ;
671 0           foreach my $l (@{$ra_columns}) {
  0            
672 0           do { local $" = ""; $flatvariablelist .= "@"."$l->[0]" ; $flatvariablelist .= ","} ;
  0            
  0            
  0            
673             }
674 0           $flatvariablelist =~ s{ ,\s? \z }{}msx;
675            
676 0           foreach my $l (@{$ra_columns}) {
  0            
677 0           my $varlen = length($l->[0]) ;
678 0           my $colpadding = $widest_column_name_padding - (int(($varlen)/4));
679 0           my $varpadding = $widest_column_name_padding - (int(($varlen+1)/4));
680             }
681            
682 0           my $flatExtractColumnList = "" ;
683            
684 0           foreach my $l (@{$ra_columns}) {
  0            
685 0           my $varlen = length($l->[0]) ;
686 0           my $colpadding = $widest_column_name_padding - (int(($varlen)/4));
687 0           my $varpadding = $widest_column_name_padding - (int(($varlen+1)/4));
688 0 0         do { local $" = ""; $flatExtractColumnList .= $l->[1] =~ m{\A(?:date|datetime[2]?|smalldatetime)\z}i ? "convert(varchar(30),[$l->[0]],120)" : "[$l->[0]]" ; $flatExtractColumnList .= ", "} ;
  0            
  0            
  0            
689            
690             }
691            
692 0           $flatExtractColumnList =~ s{ ,\s? \z }{}msx;
693            
694             #warn Dumper $flatExtractColumnList;
695             #warn Dumper $ra_columns;
696            
697 0           my $ra_metadata = describeTestDataForTable($dbh,$sql,$ra_columns);
698 0           my @cols = map { $$_[0] } @$ra_metadata ;
  0            
699            
700 0           my $ra_data = getTestDataForTable($dbh,$quotedCombinedName,\@cols,$sql);
701            
702             #warn Dumper $$ra_data[0];
703             #warn Dumper @cols;
704             #warn Dumper @$ra_metadata;
705            
706 0           my @useColumnValues = ();
707            
708             #look over data and try to find the slices which are empty
709 0 0         if ($use_IgnoreNulls) {
710 0           @useColumnValues = map { 0 } @$ra_metadata ;
  0            
711 0           foreach my $ra_row (@{$ra_data}){
  0            
712 0           for ( my $i = 0; $i < scalar @{$ra_row}; $i++ ) {
  0            
713 0 0         if ( ( defined ($ra_row->[$i]) ) ) {
714 0           $useColumnValues[$i] = 1 ;
715             }
716             }
717             }
718             }
719             else {
720 0           @useColumnValues = map { 1 } @$ra_metadata ;
  0            
721             }
722            
723 0           my $colList = "" ;#do { local $" = "," ; "@cols" } ;
724            
725 0           my $i =0;
726 0           foreach my $c (@cols)
727             {
728 0 0         if ($useColumnValues[$i]) {
729 0 0         if ( ! scalar($colList) ) {
730 0           $colList = "${c}"
731             }
732             else {
733 0           $colList .= ",${c}"
734             }
735             }
736 0           $i++;
737             }
738            
739             #warn Dumper $colList;
740             #exit;
741             #need to overlay tables with columns apart from those which are 'hidden'
742 0           my @valuesTable ;
743 0           my $valuesClause = "values\n\t\t\t";
744            
745 0           my $lno = 1;
746 0           foreach my $ra_row (@{$ra_data}){
  0            
747 0           my @outVals = undef ;
748 0           for ( my $i = 0; $i < scalar @{$ra_row}; $i++ ) {
  0            
749 0 0         if ($useColumnValues[$i]) {
750 0 0         if ( not ( defined ($ra_row->[$i]) ) ) {
751 0           $outVals[$i] = 'null' ;
752             }
753             else {
754 0 0         if (${$ra_metadata}[$i][1] =~ m{\A(?:date|datetime[2]?|smalldatetime)\z}i) {
  0 0          
755 0           $outVals[$i] = "convert(". ${$ra_columns}[$i][1] ."," . $dbh->quote($ra_row->[$i]) . ",120)" ;
  0            
756             }
757 0           elsif ( ${$ra_metadata}[$i][1] =~ m{(?:uniqueidentifier|char|text|date)}i) {
758 0           $outVals[$i] = $dbh->quote($ra_row->[$i]) ;
759             }
760             else {
761 0           $outVals[$i] = $ra_row->[$i] ;
762             }
763             }
764             }
765             }
766 0           push @valuesTable, \@outVals ;
767             #my $line = do{ local $" = ", "; "@outVals" } ;
768 0           $lno++;
769             }
770            
771 0           my @maxWidth;
772             my $maxCol;
773            
774 0 0         if ( scalar @valuesTable ) {
775 0           my @tmp = @{$valuesTable[0]};
  0            
776 0           $maxCol = scalar @tmp -1 ;
777 0           for ( my $i = 0; $i <= $maxCol; $i++ ) {
778 0           push @maxWidth, 0;
779             }
780 0           for ( my $i = 0; $i < scalar @valuesTable; $i++ ) {
781 0           my @tmp = @{$valuesTable[$i]};
  0            
782 0           for ( my $i = 0; $i <= $maxCol; $i++ ) {
783 0 0         if ($useColumnValues[$i]) {
784 0 0         if (length($tmp[$i]) > $maxWidth[$i] ) {
785 0           $maxWidth[$i] = length($tmp[$i]) ;
786             }
787             }
788             }
789             }
790             }
791            
792             #warn Dumper @maxWidth ;
793            
794 0           for ( my $i = 0; $i < scalar @valuesTable; $i++ ) {
795 0           my @tmp = @{$valuesTable[$i]};
  0            
796 0           my $line = "";
797 0           for ( my $j = 0; $j <= $maxCol; $j++ ) {
798 0 0         if ($useColumnValues[$j]) {
799            
800 0           my $val = $tmp[$j];
801 0           my $valWidth = length($val);
802 0           my $PadLength = $maxWidth[$j]-$valWidth;
803 0           my $padding = " "x$PadLength;
804 0           $line .= ", ${padding}${val}";
805             }
806             }
807 0           $line =~ s{ ^,\s}{}msx;
808 0           $valuesClause .= "(\t" . $line . ")" . "\n\t\t,\t" ;
809             }
810            
811 0           $valuesClause =~ s{ \n\t\t,\t \z }{}msx;
812            
813 0 0         if ( ! $use_MinimalForm) {
814 0           return <<"EOF";
815            
816             ${set_IDENTITY_INSERT_ON}
817             ; with src as
818             (
819             select *
820             from ( ${valuesClause}
821             ) AS vtable
822             ( ${colList})
823             )
824             insert into
825             ${quotedCombinedName}
826             ( ${colList}
827             )
828             select ${colList}
829             from src ;
830             ${set_IDENTITY_INSERT_OFF}
831            
832             EOF
833             }
834             else {
835 0           ${valuesClause} =~ s{\A values\n\t\t\t }{\t\t,\t}msx;
836 0           return <<"EOF";
837             ${valuesClause}
838             EOF
839            
840             }
841             }
842            
843             sub describeTestDataForTable {
844            
845 0     0 0   local $_ = undef ;
846            
847 0 0         my $dbh = shift or croak 'no dbh' ;
848 0 0         my $sql = shift or croak 'no sql' ;
849 0 0         my $ra_validColumns = shift or croak 'no valid columns' ;
850            
851 0           ( my $quoted_sql = $sql ) =~ s{'}{''}g;
852            
853 0           my $metadata_sql = "exec sp_describe_first_result_set N'${quoted_sql}'" ;
854            
855 0           my $sth2 = $dbh->prepare($metadata_sql);
856 0           my $rs = $sth2->execute();
857 0           my $res = $sth2->fetchall_arrayref() ;
858            
859             #warn Dumper @$ra_validColumns ;
860             #warn Dumper @$res ;
861            
862 0 0   0     my @filteredRes = grep { my $col = $_; if ( any { $$_[0] eq $$col[2] } @$ra_validColumns) { $col } } @$res;
  0            
  0            
  0            
  0            
863             #warn Dumper @filteredRes ;
864             #exit;
865 0           my @ret_res = map { [($$_[2],$$_[5],$$_[3],$$_[1])] } @filteredRes;
  0            
866            
867             #warn Dumper @ret_res ;
868            
869 0           return \@ret_res ;
870            
871             }
872            
873             sub getTestDataForTable {
874            
875 0     0 0   local $_ = undef ;
876            
877 0 0         my $dbh = shift or croak 'no dbh' ;
878 0 0         my $combinedName = shift or croak 'no table' ;
879 0 0         my $cols = shift or croak 'no cols list' ;
880 0 0         my $sql = shift or croak 'no sql' ;
881             #warn Dumper "COLS = ", $cols;
882             #my @cols = map {$_ => 1 } @$cols;
883             #warn Dumper getCurrentTableDataSQL($combinedName,$pkCol,$cols);
884            
885 0           my $sth2 = $dbh->prepare($sql);
886 0           my $rs = $sth2->execute();
887 0           my $res = $sth2->fetchall_arrayref($cols) ;
888            
889 0           return $res ;
890            
891             }
892            
893             sub getCurrentTableData {
894            
895 0     0 0   local $_ = undef ;
896            
897 0 0         my $dbh = shift or croak 'no dbh' ;
898 0 0         my $combinedName = shift or croak 'no table' ;
899 0           my $pkCol = shift ; #or croak 'no primary key' ;
900 0           my $cols = shift ; #or croak 'no primary key' ;
901            
902             #warn Dumper getCurrentTableDataSQL($combinedName,$pkCol,$cols);
903            
904 0           my $sth2 = $dbh->prepare(getCurrentTableDataSQL($combinedName,$pkCol,$cols));
905 0           my $rs = $sth2->execute();
906 0           my $res = $sth2->fetchall_arrayref() ;
907            
908 0           return $res ;
909            
910             }
911            
912             sub getCurrentTableDataSQL {
913            
914 0     0 0   local $_ = undef ;
915            
916 0 0         my $combinedName = shift or croak 'no table' ;
917 0           my $pkCol = shift ; #or croak 'no primary key' ;
918 0           my $cols = shift ; #or croak 'no primary key' ;
919            
920 0           my $orderBy = "" ;
921            
922 0 0         if ( ! $pkCol ) {
923 0           $orderBy = "" ;
924             }
925             else {
926 0           $orderBy = "order by $pkCol" ;
927             }
928            
929 0           return <<"EOF" ;
930            
931             select ${cols}
932             from ${combinedName} so
933             ${orderBy}
934            
935             EOF
936            
937             }
938            
939             sub idCols {
940            
941 0     0 0   local $_ = undef ;
942            
943 0 0         my $dbh = shift or croak 'no dbh' ;
944 0 0         my $schema = shift or croak 'no schema' ;
945 0 0         my $table = shift or croak 'no table' ;
946            
947 0           my $sth2 = $dbh->prepare(idColsSQL());
948 0           my $rs = $sth2->execute($schema,$table);
949 0           my $res = $sth2->fetchall_arrayref() ;
950            
951 0           return $$res[0][0] ;
952            
953             }
954            
955             sub idColsSQL {
956            
957 0     0 0   return <<"EOF" ;
958            
959             select sc.name as ID_COL
960             FROM dbo.sysobjects so
961             join dbo.syscolumns sc
962             on so.id = sc.id
963             and sc.colstat & 1 = 1
964             where schema_name(so.uid) = ?
965             and so.name = ?
966            
967             EOF
968            
969             }
970            
971             sub has_idCols {
972            
973 0     0 0   local $_ = undef ;
974            
975 0 0         my $dbh = shift or croak 'no dbh' ;
976 0 0         my $schema = shift or croak 'no schema' ;
977 0 0         my $table = shift or croak 'no table' ;
978            
979 0           my $sth2 = $dbh->prepare(has_idColsSQL());
980 0           my $rs = $sth2->execute($schema,$table);
981 0           my $res = $sth2->fetchall_arrayref() ;
982            
983 0           return $$res[0][0] ;
984            
985             }
986            
987             sub has_idColsSQL {
988            
989 0     0 0   return <<"EOF" ;
990            
991             select 1 as ID_COL
992             FROM dbo.sysobjects so
993             where schema_name(so.uid) = ?
994             and so.name = ?
995             and exists (
996             select *
997             from dbo.syscolumns sc
998             where so.id = sc.id
999             and sc.colstat & 1 = 1
1000             )
1001             EOF
1002            
1003             }
1004            
1005            
1006             sub pkcolumns {
1007            
1008 0     0 0   local $_ = undef ;
1009            
1010 0 0         my $dbh = shift or croak 'no dbh' ;
1011 0 0         my $qtd_schema = shift or croak 'no schema' ;
1012 0 0         my $qtd_table = shift or croak 'no table' ;
1013            
1014 0           my $schema = $qtd_schema ;
1015 0           my $table = $qtd_table ;
1016            
1017 0           $schema =~ s/\A\[(.*)\]\z/$1/;
1018 0           $table =~ s/\A\[(.*)\]\z/$1/;
1019            
1020 0           my $sth2 = $dbh->prepare( pkcolumnsSQL());
1021 0           my $rs = $sth2->execute($schema,$table,$schema,$table);
1022 0           my $res = $sth2->fetchall_arrayref() ;
1023            
1024 0 0         if ( scalar @{$res} ) { return $res ; } ;
  0            
  0            
1025 0           return [] ;
1026             }
1027            
1028            
1029            
1030             sub pkcolumnsSQL {
1031            
1032 0     0 0   return <<"EOF" ;
1033            
1034             ; with ranking as (
1035             select CONSTRAINT_SCHEMA, CONSTRAINT_NAME
1036             , row_number() over (order by case when tc.CONSTRAINT_TYPE = 'PRIMARY KEY' then 1 else 2 end, CONSTRAINT_NAME ) as rn
1037             from INFORMATION_SCHEMA.TABLE_CONSTRAINTS tc
1038             where tc.CONSTRAINT_TYPE in( 'PRIMARY KEY','UNIQUE' )
1039             and tc.TABLE_SCHEMA = ?
1040             and tc.TABLE_NAME = ?
1041             )
1042             select COLUMN_NAME
1043             from INFORMATION_SCHEMA.TABLE_CONSTRAINTS tc
1044             join INFORMATION_SCHEMA.KEY_COLUMN_USAGE kcu
1045             on tc.TABLE_CATALOG = kcu.TABLE_CATALOG
1046             and tc.TABLE_SCHEMA = kcu.TABLE_SCHEMA
1047             and tc.TABLE_NAME = kcu.TABLE_NAME
1048             and tc.CONSTRAINT_NAME = kcu.CONSTRAINT_NAME
1049             join ranking rk
1050             on tc.CONSTRAINT_SCHEMA = rk.CONSTRAINT_SCHEMA
1051             and tc.CONSTRAINT_NAME = rk.CONSTRAINT_NAME
1052             where tc.CONSTRAINT_TYPE in( 'PRIMARY KEY','UNIQUE' )
1053             and tc.TABLE_SCHEMA = ?
1054             and tc.TABLE_NAME = ?
1055             and rn = 1
1056             order by
1057             ORDINAL_POSITION
1058            
1059             EOF
1060            
1061             }
1062            
1063            
1064             sub columns {
1065            
1066 0     0 0   local $_ = undef ;
1067            
1068 0 0         my $dbh = shift or croak 'no dbh' ;
1069 0 0         my $schema = shift or croak 'no schema' ;
1070 0 0         my $table = shift or croak 'no table' ;
1071            
1072 0           my $sth2 = $dbh->prepare( columnsSQL());
1073 0           my $rs = $sth2->execute($schema,$table,"[${schema}]","[${table}]");
1074 0           my $res = $sth2->fetchall_arrayref() ;
1075            
1076 0 0         if ( scalar @{$res} ) { return $res ; } ;
  0            
  0            
1077 0           return [] ;
1078             }
1079            
1080            
1081            
1082             sub columnsSQL {
1083            
1084 0     0 0   return <<"EOF" ;
1085             select Column_name
1086             , data_type
1087             , case when character_maximum_length is not null then '('+ case when character_maximum_length = -1 then 'max' else cast(character_maximum_length as varchar(10)) end+')' else '' end
1088             as datasize
1089             , isnull(case when lower(Data_type) = 'float'
1090             then '('+cast(Numeric_precision as varchar(10))+')'
1091             when lower(Data_type) not like '%int%' and lower(Data_type) not like '%money%' and Numeric_precision is not null
1092             then '('+cast(Numeric_precision as varchar(10))+','+cast(Numeric_scale as varchar(10))+')'
1093             else ''
1094             end
1095             ,'')
1096             as dataprecision
1097             , case when DATABASEPROPERTYEX(db_name(), 'Collation') != collation_name then 'collate ' + collation_name else '' end
1098             as collation
1099             , case when LOWER(IS_NULLABLE) = 'no' then 'not null' else 'null' end
1100             as datanullabity
1101             from INFORMATION_SCHEMA.COLUMNS
1102             where 1=1
1103             and TABLE_SCHEMA = ?
1104             and TABLE_NAME = ?
1105             and COLUMNPROPERTY(object_id(?+'.'+?) , COLUMN_NAME,'IsComputed') != 1
1106             --order by ORDINAL_POSITION
1107            
1108             EOF
1109            
1110             }
1111            
1112            
1113            
1114            
1115            
1116            
1117             __DATA__