File Coverage

blib/lib/VSGDR/StaticData.pm
Criterion Covered Total %
statement 32 518 6.1
branch 0 162 0.0
condition 0 15 0.0
subroutine 11 29 37.9
pod 0 18 0.0
total 43 742 5.8


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