File Coverage

blib/lib/VSGDR/StaticData.pm
Criterion Covered Total %
statement 35 537 6.5
branch 0 166 0.0
condition 0 15 0.0
subroutine 12 31 38.7
pod 0 18 0.0
total 47 767 6.1


line stmt bran cond sub pod time code
1             package VSGDR::StaticData;
2            
3 1     1   64289 use strict;
  1         3  
  1         28  
4 1     1   6 use warnings;
  1         2  
  1         22  
5            
6 1     1   22 use 5.010;
  1         4  
7            
8 1     1   5 use List::Util qw(max);
  1         2  
  1         106  
9 1     1   528 use List::MoreUtils qw(any);
  1         12125  
  1         7  
10 1     1   1566 use POSIX qw(strftime);
  1         6165  
  1         5  
11 1     1   1421 use Carp;
  1         2  
  1         59  
12 1     1   1523 use DBI;
  1         17432  
  1         74  
13 1     1   759 use Data::Dumper;
  1         6788  
  1         77  
14 1     1   480 use English;
  1         3549  
  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.48
27            
28             =cut
29            
30             our $VERSION = '0.48';
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   938 no warnings;
  1         2  
  1         87  
125 0 0         my $userName = $OSNAME eq 'MSWin32' ? eval('Win32::LoginName') : ${[getpwuid( $< )]}->[6]; $userName =~ s/,.*//;
  0            
  0            
126 1     1   6 use warnings;
  1         2  
  1         5723  
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;
703             #warn Dumper $$ra_data[0];
704             #warn Dumper @cols;
705             #warn Dumper @$ra_metadata;
706            
707 0           my @useColumnValues = ();
708            
709             #look over data and try to find the slices which are empty
710 0 0         if ($use_IgnoreNulls) {
711 0           @useColumnValues = map { 0 } @$ra_metadata ;
  0            
712 0           foreach my $ra_row (@{$ra_data}){
  0            
713 0           for ( my $i = 0; $i < scalar @{$ra_row}; $i++ ) {
  0            
714 0 0         if ( ( defined ($ra_row->[$i]) ) ) {
715 0           $useColumnValues[$i] = 1 ;
716             }
717             }
718             }
719             }
720             else {
721 0           @useColumnValues = map { 1 } @$ra_metadata ;
  0            
722             }
723            
724 0           my $colList = "" ;#do { local $" = "," ; "@cols" } ;
725            
726 0           my $i =0;
727 0           foreach my $c (@cols)
728             {
729 0 0         if ($useColumnValues[$i]) {
730 0 0         if ( ! scalar($colList) ) {
731 0           $colList = "${c}"
732             }
733             else {
734 0           $colList .= ",${c}"
735             }
736             }
737 0           $i++;
738             }
739            
740             #warn Dumper $colList;
741             #exit;
742             #need to overlay tables with columns apart from those which are 'hidden'
743 0           my @valuesTable ;
744 0           my $valuesClause = "values\n\t\t\t";
745            
746 0           my $lno = 1;
747 0           foreach my $ra_row (@{$ra_data}){
  0            
748 0           my @outVals = undef ;
749 0           for ( my $i = 0; $i < scalar @{$ra_row}; $i++ ) {
  0            
750 0 0         if ($useColumnValues[$i]) {
751 0 0         if ( not ( defined ($ra_row->[$i]) ) ) {
752 0           $outVals[$i] = 'null' ;
753             }
754             else {
755 0 0         if (${$ra_metadata}[$i][1] =~ m{\A(?:date|datetime[2]?|smalldatetime)\z}i) {
  0 0          
756 0           $outVals[$i] = "convert(". ${$ra_columns}[$i][1] ."," . $dbh->quote($ra_row->[$i]) . ",120)" ;
  0            
757             }
758 0           elsif ( ${$ra_metadata}[$i][1] =~ m{(?:uniqueidentifier|char|text|date)}i) {
759 0           $outVals[$i] = $dbh->quote($ra_row->[$i]) ;
760             }
761             else {
762 0           $outVals[$i] = $ra_row->[$i] ;
763             }
764             }
765             }
766             }
767 0           push @valuesTable, \@outVals ;
768             #my $line = do{ local $" = ", "; "@outVals" } ;
769 0           $lno++;
770             }
771            
772 0           my @maxWidth;
773             my $maxCol;
774            
775 0 0         if ( scalar @valuesTable ) {
776 0           my @tmp = @{$valuesTable[0]};
  0            
777 0           $maxCol = scalar @tmp -1 ;
778 0           for ( my $i = 0; $i <= $maxCol; $i++ ) {
779 0           push @maxWidth, 0;
780             }
781 0           for ( my $i = 0; $i < scalar @valuesTable; $i++ ) {
782 0           my @tmp = @{$valuesTable[$i]};
  0            
783 0           for ( my $i = 0; $i <= $maxCol; $i++ ) {
784 0 0         if ($useColumnValues[$i]) {
785 0 0         if (length($tmp[$i]) > $maxWidth[$i] ) {
786 0           $maxWidth[$i] = length($tmp[$i]) ;
787             }
788             }
789             }
790             }
791             }
792            
793             #warn Dumper @maxWidth ;
794            
795 0           for ( my $i = 0; $i < scalar @valuesTable; $i++ ) {
796 0           my @tmp = @{$valuesTable[$i]};
  0            
797 0           my $line = "";
798 0           for ( my $j = 0; $j <= $maxCol; $j++ ) {
799 0 0         if ($useColumnValues[$j]) {
800            
801 0           my $val = $tmp[$j];
802 0           my $valWidth = length($val);
803 0           my $PadLength = $maxWidth[$j]-$valWidth;
804 0           my $padding = " "x$PadLength;
805 0           $line .= ", ${padding}${val}";
806             }
807             }
808 0           $line =~ s{ ^,\s}{}msx;
809 0           $valuesClause .= "(\t" . $line . ")" . "\n\t\t,\t" ;
810             }
811            
812 0           $valuesClause =~ s{ \n\t\t,\t \z }{}msx;
813            
814 0 0         if ( ! $use_MinimalForm) {
815 0           return <<"EOF";
816            
817             ${set_IDENTITY_INSERT_ON}
818             ; with src as
819             (
820             select *
821             from ( ${valuesClause}
822             ) AS vtable
823             ( ${colList})
824             )
825             insert into
826             ${quotedCombinedName}
827             ( ${colList}
828             )
829             select ${colList}
830             from src ;
831             ${set_IDENTITY_INSERT_OFF}
832            
833             EOF
834             }
835             else {
836 0           ${valuesClause} =~ s{\A values\n\t\t\t }{\t\t,\t}msx;
837 0           return <<"EOF";
838             ${valuesClause}
839             EOF
840            
841             }
842             }
843            
844             sub describeTestDataForTable {
845            
846 0     0 0   local $_ = undef ;
847            
848 0 0         my $dbh = shift or croak 'no dbh' ;
849 0 0         my $sql = shift or croak 'no sql' ;
850 0 0         my $ra_validColumns = shift or croak 'no valid columns' ;
851            
852 0           ( my $quoted_sql = $sql ) =~ s{'}{''}g;
853            
854 0           my $metadata_sql = "exec sp_describe_first_result_set N'${quoted_sql}'" ;
855            
856 0           my $sth2 = $dbh->prepare($metadata_sql);
857 0           my $rs = $sth2->execute();
858 0           my $res = $sth2->fetchall_arrayref() ;
859            
860             #warn Dumper @$ra_validColumns ;
861             #warn Dumper @$res ;
862            
863 0 0   0     my @filteredRes = grep { my $col = $_; if ( any { $$_[0] eq $$col[2] } @$ra_validColumns) { $col } } @$res;
  0            
  0            
  0            
  0            
864             #warn Dumper @filteredRes ;
865             #exit;
866 0           my @ret_res = map { [($$_[2],$$_[5],$$_[3],$$_[1])] } @filteredRes;
  0            
867            
868             #warn Dumper @ret_res ;
869            
870 0           return \@ret_res ;
871            
872             }
873            
874             sub getTestDataForTable {
875            
876 0     0 0   local $_ = undef ;
877            
878 0 0         my $dbh = shift or croak 'no dbh' ;
879 0 0         my $combinedName = shift or croak 'no table' ;
880 0 0         my $cols = shift or croak 'no cols list' ;
881 0 0         my $sql = shift or croak 'no sql' ;
882            
883             #warn Dumper "SQL = ", $sql;
884             #warn Dumper "COLS = ", $cols;
885 0           my %cols = map {$_ => 1 } @$cols;
  0            
886             #warn Dumper getCurrentTableDataSQL($combinedName,$pkCol,$cols);
887             #warn Dumper %cols;
888 0           my $sth2 = $dbh->prepare($sql);
889 0           my $rs = $sth2->execute();
890             # my $res = $sth2->fetchall_arrayref() ;
891 0           my $res = $sth2->fetchall_arrayref(\%cols) ;
892            
893             #warn Dumper $res;
894 0           my @res2 = ();
895 0           my $li=0;
896 0           foreach my $row (@$res) {
897            
898             #warn Dumper $row;
899 0           my $ci = 0;
900 0           foreach my $c (@$cols) {
901 0           $res2[$li][$ci] = $$row{$c};
902 0           $ci++;
903             }
904 0           $li++;
905             }
906             #warn Dumper \@res2;
907 0           return \@res2 ;
908            
909             }
910            
911             sub getCurrentTableData {
912            
913 0     0 0   local $_ = undef ;
914            
915 0 0         my $dbh = shift or croak 'no dbh' ;
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             #warn Dumper getCurrentTableDataSQL($combinedName,$pkCol,$cols);
921            
922 0           my $sth2 = $dbh->prepare(getCurrentTableDataSQL($combinedName,$pkCol,$cols));
923 0           my $rs = $sth2->execute();
924 0           my $res = $sth2->fetchall_arrayref() ;
925            
926 0           return $res ;
927            
928             }
929            
930             sub getCurrentTableDataSQL {
931            
932 0     0 0   local $_ = undef ;
933            
934 0 0         my $combinedName = shift or croak 'no table' ;
935 0           my $pkCol = shift ; #or croak 'no primary key' ;
936 0           my $cols = shift ; #or croak 'no primary key' ;
937            
938 0           my $orderBy = "" ;
939            
940 0 0         if ( ! $pkCol ) {
941 0           $orderBy = "" ;
942             }
943             else {
944 0           $orderBy = "order by $pkCol" ;
945             }
946            
947 0           return <<"EOF" ;
948            
949             select ${cols}
950             from ${combinedName} so
951             ${orderBy}
952            
953             EOF
954            
955             }
956            
957             sub idCols {
958            
959 0     0 0   local $_ = undef ;
960            
961 0 0         my $dbh = shift or croak 'no dbh' ;
962 0 0         my $schema = shift or croak 'no schema' ;
963 0 0         my $table = shift or croak 'no table' ;
964            
965 0           my $sth2 = $dbh->prepare(idColsSQL());
966 0           my $rs = $sth2->execute($schema,$table);
967 0           my $res = $sth2->fetchall_arrayref() ;
968            
969 0           return $$res[0][0] ;
970            
971             }
972            
973             sub idColsSQL {
974            
975 0     0 0   return <<"EOF" ;
976            
977             select sc.name as ID_COL
978             FROM dbo.sysobjects so
979             join dbo.syscolumns sc
980             on so.id = sc.id
981             and sc.colstat & 1 = 1
982             where schema_name(so.uid) = ?
983             and so.name = ?
984            
985             EOF
986            
987             }
988            
989             sub has_idCols {
990            
991 0     0 0   local $_ = undef ;
992            
993 0 0         my $dbh = shift or croak 'no dbh' ;
994 0 0         my $schema = shift or croak 'no schema' ;
995 0 0         my $table = shift or croak 'no table' ;
996            
997 0           my $sth2 = $dbh->prepare(has_idColsSQL());
998 0           my $rs = $sth2->execute($schema,$table);
999 0           my $res = $sth2->fetchall_arrayref() ;
1000            
1001 0           return $$res[0][0] ;
1002            
1003             }
1004            
1005             sub has_idColsSQL {
1006            
1007 0     0 0   return <<"EOF" ;
1008            
1009             select 1 as ID_COL
1010             FROM dbo.sysobjects so
1011             where schema_name(so.uid) = ?
1012             and so.name = ?
1013             and exists (
1014             select *
1015             from dbo.syscolumns sc
1016             where so.id = sc.id
1017             and sc.colstat & 1 = 1
1018             )
1019             EOF
1020            
1021             }
1022            
1023            
1024             sub pkcolumns {
1025            
1026 0     0 0   local $_ = undef ;
1027            
1028 0 0         my $dbh = shift or croak 'no dbh' ;
1029 0 0         my $qtd_schema = shift or croak 'no schema' ;
1030 0 0         my $qtd_table = shift or croak 'no table' ;
1031            
1032 0           my $schema = $qtd_schema ;
1033 0           my $table = $qtd_table ;
1034            
1035 0           $schema =~ s/\A\[(.*)\]\z/$1/;
1036 0           $table =~ s/\A\[(.*)\]\z/$1/;
1037            
1038 0           my $sth2 = $dbh->prepare( pkcolumnsSQL());
1039 0           my $rs = $sth2->execute($schema,$table,$schema,$table);
1040 0           my $res = $sth2->fetchall_arrayref() ;
1041            
1042 0 0         if ( scalar @{$res} ) { return $res ; } ;
  0            
  0            
1043 0           return [] ;
1044             }
1045            
1046            
1047            
1048             sub pkcolumnsSQL {
1049            
1050 0     0 0   return <<"EOF" ;
1051            
1052             ; with ranking as (
1053             select CONSTRAINT_SCHEMA, CONSTRAINT_NAME
1054             , row_number() over (order by case when tc.CONSTRAINT_TYPE = 'PRIMARY KEY' then 1 else 2 end, CONSTRAINT_NAME ) as rn
1055             from INFORMATION_SCHEMA.TABLE_CONSTRAINTS tc
1056             where tc.CONSTRAINT_TYPE in( 'PRIMARY KEY','UNIQUE' )
1057             and tc.TABLE_SCHEMA = ?
1058             and tc.TABLE_NAME = ?
1059             )
1060             select COLUMN_NAME
1061             from INFORMATION_SCHEMA.TABLE_CONSTRAINTS tc
1062             join INFORMATION_SCHEMA.KEY_COLUMN_USAGE kcu
1063             on tc.TABLE_CATALOG = kcu.TABLE_CATALOG
1064             and tc.TABLE_SCHEMA = kcu.TABLE_SCHEMA
1065             and tc.TABLE_NAME = kcu.TABLE_NAME
1066             and tc.CONSTRAINT_NAME = kcu.CONSTRAINT_NAME
1067             join ranking rk
1068             on tc.CONSTRAINT_SCHEMA = rk.CONSTRAINT_SCHEMA
1069             and tc.CONSTRAINT_NAME = rk.CONSTRAINT_NAME
1070             where tc.CONSTRAINT_TYPE in( 'PRIMARY KEY','UNIQUE' )
1071             and tc.TABLE_SCHEMA = ?
1072             and tc.TABLE_NAME = ?
1073             and rn = 1
1074             order by
1075             ORDINAL_POSITION
1076            
1077             EOF
1078            
1079             }
1080            
1081            
1082             sub columns {
1083            
1084 0     0 0   local $_ = undef ;
1085            
1086 0 0         my $dbh = shift or croak 'no dbh' ;
1087 0 0         my $schema = shift or croak 'no schema' ;
1088 0 0         my $table = shift or croak 'no table' ;
1089            
1090 0           my $sth2 = $dbh->prepare( columnsSQL());
1091 0           my $rs = $sth2->execute($schema,$table,"[${schema}]","[${table}]");
1092 0           my $res = $sth2->fetchall_arrayref() ;
1093            
1094 0 0         if ( scalar @{$res} ) { return $res ; } ;
  0            
  0            
1095 0           return [] ;
1096             }
1097            
1098            
1099            
1100             sub columnsSQL {
1101            
1102 0     0 0   return <<"EOF" ;
1103             select Column_name
1104             , data_type
1105             , 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
1106             as datasize
1107             , isnull(case when lower(Data_type) = 'float'
1108             then '('+cast(Numeric_precision as varchar(10))+')'
1109             when lower(Data_type) not like '%int%' and lower(Data_type) not like '%money%' and Numeric_precision is not null
1110             then '('+cast(Numeric_precision as varchar(10))+','+cast(Numeric_scale as varchar(10))+')'
1111             else ''
1112             end
1113             ,'')
1114             as dataprecision
1115             , case when DATABASEPROPERTYEX(db_name(), 'Collation') != collation_name then 'collate ' + collation_name else '' end
1116             as collation
1117             , case when LOWER(IS_NULLABLE) = 'no' then 'not null' else 'null' end
1118             as datanullabity
1119             from INFORMATION_SCHEMA.COLUMNS
1120             where 1=1
1121             and TABLE_SCHEMA = ?
1122             and TABLE_NAME = ?
1123             and COLUMNPROPERTY(object_id(?+'.'+?) , COLUMN_NAME,'IsComputed') != 1
1124             --order by ORDINAL_POSITION
1125            
1126             EOF
1127            
1128             }
1129            
1130            
1131            
1132            
1133            
1134            
1135             __DATA__