| 224 |
hknight |
1 |
#!/usr/bin/perl -w
|
|
|
2 |
|
|
|
3 |
########################################################################
|
|
|
4 |
# Copyright (C) 2008 ERG Limited, All rights reserved
|
|
|
5 |
#
|
|
|
6 |
# Module name : schemadump.pl
|
|
|
7 |
# Module type : Standalone utility
|
|
|
8 |
# Compiler(s) : n/a
|
|
|
9 |
# Environment(s): windows (dos), unix (solaris and linux)
|
|
|
10 |
#
|
|
|
11 |
# Description : Extracts a directory structure from the output of
|
|
|
12 |
# datapump. This is useful for creating files to
|
|
|
13 |
# be put into source control from a database.
|
|
|
14 |
#
|
|
|
15 |
# History : Created by Haydon Knight April 2008
|
|
|
16 |
#
|
|
|
17 |
# Usage : schemadump.pl [options] -src=<srcFile> -dest=<destDir>
|
|
|
18 |
#
|
|
|
19 |
########################################################################
|
|
|
20 |
|
|
|
21 |
#######################################################################
|
|
|
22 |
# Use lines
|
|
|
23 |
#######################################################################
|
|
|
24 |
require 5.6.1;
|
|
|
25 |
use strict;
|
|
|
26 |
use warnings;
|
|
|
27 |
use Pod::Usage; # required for help support
|
|
|
28 |
use Getopt::Long;
|
|
|
29 |
|
|
|
30 |
use commonExports; # see commonExports.pm in this directory
|
|
|
31 |
|
|
|
32 |
#######################################################################
|
|
|
33 |
# Function prototypes
|
|
|
34 |
#######################################################################
|
|
|
35 |
|
|
|
36 |
sub startupChecks();
|
|
|
37 |
sub parseCommandLine();
|
|
|
38 |
sub main();
|
|
|
39 |
|
|
|
40 |
sub getSubSectionLiness(\@);
|
|
|
41 |
sub getFullDirs(\@);
|
|
|
42 |
sub getSubDirs(@);
|
|
|
43 |
sub writeData(\@\@);
|
|
|
44 |
sub getFileLines($\$@);
|
|
|
45 |
sub getFilename($@);
|
|
|
46 |
sub writeDDLHeader(@);
|
|
|
47 |
|
|
|
48 |
sub packageBodySplit(\$@);
|
|
|
49 |
sub splitOnBlank(\$@);
|
|
|
50 |
sub splitOnSlash(\$@);
|
|
|
51 |
sub splitOnSemiColon(\$@);
|
|
|
52 |
sub splitOnCreate(\$@);
|
|
|
53 |
|
|
|
54 |
#######################################################################
|
|
|
55 |
# Constant global variables
|
|
|
56 |
#######################################################################
|
|
|
57 |
|
|
|
58 |
my $VERSION = "1.0.1";
|
|
|
59 |
|
|
|
60 |
#######################################################################
|
|
|
61 |
# Other global variables
|
|
|
62 |
#######################################################################
|
|
|
63 |
|
|
|
64 |
my $srcFile;
|
|
|
65 |
my $destDir;
|
|
|
66 |
|
|
|
67 |
#######################################################################
|
|
|
68 |
# Main code
|
|
|
69 |
#######################################################################
|
|
|
70 |
|
|
|
71 |
parseCommandLine();
|
|
|
72 |
startupChecks();
|
|
|
73 |
main();
|
|
|
74 |
finish();
|
|
|
75 |
|
|
|
76 |
#######################################################################
|
|
|
77 |
# Function definitions
|
|
|
78 |
#######################################################################
|
|
|
79 |
|
|
|
80 |
#-------------------------------------------------------------------------------
|
|
|
81 |
# Function : main
|
|
|
82 |
#
|
|
|
83 |
# Purpose : Main function of this script
|
|
|
84 |
#
|
|
|
85 |
# Arguments : none
|
|
|
86 |
#
|
|
|
87 |
# Returns : none
|
|
|
88 |
#
|
|
|
89 |
# Notes : Does everything but setting up at start and tearing down at end.
|
|
|
90 |
#
|
|
|
91 |
sub main()
|
|
|
92 |
{
|
|
|
93 |
my @lines = readArray( $srcFile ); # all the lines in the datapump dump file
|
|
|
94 |
|
|
|
95 |
mkdir "$destDir/bookKeeping" unless -d "$destDir/bookKeeping";
|
|
|
96 |
|
|
|
97 |
writeDDLHeader( @lines );
|
|
|
98 |
|
|
|
99 |
# getFullDirs() returns an array of strings.
|
|
|
100 |
# Each of these is of the form '<DIRY_1>/<DIRY_2>/.../<DIRY_N>.
|
|
|
101 |
# e.g. one element is 'TABLE/INDEX/STATISTICS/INDEX_STATISTICS'.
|
|
|
102 |
my @fullDirs = getFullDirs( @lines );
|
|
|
103 |
|
|
|
104 |
# getSubDirs() returns an array of strings.
|
|
|
105 |
# Each of these is of the form '<diry_1>/<diry_2>'.
|
|
|
106 |
# e.g. the element formed from 'TABLE/INDEX/STATISTICS/INDEX_STATISTICS' is
|
|
|
107 |
# 'table/index_statistics'.
|
|
|
108 |
my @subDirs = getSubDirs( @fullDirs );
|
|
|
109 |
|
|
|
110 |
# This is a 2-D array - i.e. it is an array of (references to arrays)
|
|
|
111 |
# Each reference to an array corresponds to an element of @subDirs.
|
|
|
112 |
# For each of these there is an array of the lines corresponding to that DDL-data-type.
|
|
|
113 |
my @subSectionLiness = getSubSectionLiness(@lines);
|
|
|
114 |
|
|
|
115 |
die "BUG! Did not get the same number of sub directories as blocks of subsection lines (" .
|
|
|
116 |
scalar(@subDirs) . " vs " . scalar(@subSectionLiness) . ")\n"
|
|
|
117 |
unless scalar(@subDirs) == scalar(@subSectionLiness);
|
|
|
118 |
|
|
|
119 |
writeArray( "$destDir/bookKeeping/objectTypes", @fullDirs);
|
|
|
120 |
createDirectories( $destDir, @subDirs );
|
|
|
121 |
|
|
|
122 |
my $linesWritten = writeData( @subDirs, @subSectionLiness );
|
|
|
123 |
|
|
|
124 |
# Minus one for the very first line: the '-- CONNECT RELEASE_MANAGER' line
|
|
|
125 |
my $linesDesired = scalar(@lines) - scalar(@subDirs) - 1;
|
|
|
126 |
|
|
|
127 |
die "Internal logic error! Desired to write out $linesDesired lines but wrote out " .
|
|
|
128 |
" $linesWritten lines\n"
|
|
|
129 |
unless $linesDesired == $linesWritten;
|
|
|
130 |
}
|
|
|
131 |
|
|
|
132 |
|
|
|
133 |
#-------------------------------------------------------------------------------
|
|
|
134 |
# Function : writeDDLHeader
|
|
|
135 |
#
|
|
|
136 |
# Purpose : Writes the first line of the enormous datapump dump file to a file
|
|
|
137 |
# This line is used by ddlfile.pl to reconstruct the dump file.
|
|
|
138 |
#
|
|
|
139 |
# Arguments : @lines (i) - all the lines from the datapump dump file
|
|
|
140 |
#
|
|
|
141 |
# Returns : nothing
|
|
|
142 |
#
|
|
|
143 |
# Notes :
|
|
|
144 |
#
|
|
|
145 |
sub writeDDLHeader(@)
|
|
|
146 |
{
|
|
|
147 |
my @lines = @_;
|
|
|
148 |
|
|
|
149 |
die "No lines read from input file\n" unless @lines;
|
|
|
150 |
|
|
|
151 |
my $connectLine = $lines[0];
|
|
|
152 |
|
|
|
153 |
die "Could not read the string 'CONNECT' from first line of input file ('$connectLine')\n"
|
|
|
154 |
unless $connectLine =~ m~^-- CONNECT\s+~;
|
|
|
155 |
|
|
|
156 |
writeArray( "$destDir/bookKeeping/DDLHeader", ($connectLine) );
|
|
|
157 |
}
|
|
|
158 |
|
|
|
159 |
|
|
|
160 |
#-------------------------------------------------------------------------------
|
|
|
161 |
# Function : getSubSectionLiness
|
|
|
162 |
#
|
|
|
163 |
# Purpose : Constructs a 2D array of lines in each DDL-type
|
|
|
164 |
# i.e. for each DDL-type (A DDL-type is a sequence or a package or a table etc)
|
|
|
165 |
# there is an array of lines
|
|
|
166 |
#
|
|
|
167 |
# Arguments : $refLines (i) - Reference to @lines - the list of all lines in the datapump dump file
|
|
|
168 |
#
|
|
|
169 |
# Returns : @subSectionLiness - an array of references to arrays
|
|
|
170 |
#
|
|
|
171 |
# Notes : Simply splits on lines containing $subSectionSplitter
|
|
|
172 |
#
|
|
|
173 |
sub getSubSectionLiness(\@)
|
|
|
174 |
{
|
|
|
175 |
my ($refLines) = @_;
|
|
|
176 |
|
|
|
177 |
my @subSectionLiness;
|
|
|
178 |
my @tempList;
|
|
|
179 |
my $isFirstTime = 1;
|
|
|
180 |
|
|
|
181 |
foreach my $line (@$refLines)
|
|
|
182 |
{
|
|
|
183 |
if( $line =~ m~^$subSectionSplitter~ )
|
|
|
184 |
{
|
|
|
185 |
push @subSectionLiness, [ @tempList ] unless $isFirstTime;
|
|
|
186 |
$isFirstTime = 0;
|
|
|
187 |
@tempList = ();
|
|
|
188 |
}
|
|
|
189 |
else
|
|
|
190 |
{
|
|
|
191 |
push @tempList, $line;
|
|
|
192 |
}
|
|
|
193 |
}
|
|
|
194 |
push @subSectionLiness, [ @tempList ];
|
|
|
195 |
|
|
|
196 |
logprint "Got " . scalar(@subSectionLiness) . " sets of subSection Lines";
|
|
|
197 |
|
|
|
198 |
return @subSectionLiness;
|
|
|
199 |
}
|
|
|
200 |
|
|
|
201 |
|
|
|
202 |
#-------------------------------------------------------------------------------
|
|
|
203 |
# Function : getFullDirs
|
|
|
204 |
#
|
|
|
205 |
# Purpose : Constructs an array of directory paths
|
|
|
206 |
#
|
|
|
207 |
# Arguments : $refLines (i) - reference to @lines - all lines in datapump dump file
|
|
|
208 |
#
|
|
|
209 |
# Returns : @fullDirys - array of directory paths
|
|
|
210 |
#
|
|
|
211 |
# Notes : Reads in directory paths from the '--new object' lines in the datapump dump file
|
|
|
212 |
# The directory paths have extra subdirectories than what is actually mkdir'ed
|
|
|
213 |
# Subsequent functionality (getTopBotDirs() for example) strips out these
|
|
|
214 |
# middle subdirectories
|
|
|
215 |
#
|
|
|
216 |
sub getFullDirs(\@)
|
|
|
217 |
{
|
|
|
218 |
my ($refLines) = @_;
|
|
|
219 |
|
|
|
220 |
###############################################################
|
|
|
221 |
# extract out from lines starting with '-- new object'
|
|
|
222 |
# strings of the form 'TABLE/INDEX/STATISTICS/INDEX_STATISTICS'
|
|
|
223 |
my @fullDirys;
|
|
|
224 |
|
|
|
225 |
foreach my $line (@$refLines)
|
|
|
226 |
{
|
|
|
227 |
next unless $line =~ m~^$subSectionSplitter~;
|
|
|
228 |
(my $fullDiry = $line) =~ s~^$subSectionSplitter~~;
|
|
|
229 |
push @fullDirys, $fullDiry;
|
|
|
230 |
|
|
|
231 |
logprint "Got fullDiry '$fullDirys[-1]'";
|
|
|
232 |
}
|
|
|
233 |
|
|
|
234 |
logprint "Got " . scalar(@fullDirys) . " fullDirys contributing to subDirs\n";
|
|
|
235 |
|
|
|
236 |
return @fullDirys;
|
|
|
237 |
}
|
|
|
238 |
|
|
|
239 |
|
|
|
240 |
#-------------------------------------------------------------------------------
|
|
|
241 |
# Function : getSubDirs
|
|
|
242 |
#
|
|
|
243 |
# Purpose : Constructs the list of subdirectories that will be mkdir'ed and filled
|
|
|
244 |
# with .sql files
|
|
|
245 |
#
|
|
|
246 |
# Arguments : @fullDirys (i) - list of directories parsed from datapump dump file
|
|
|
247 |
#
|
|
|
248 |
# Returns : @subDirs - list of directories that will exist if script finishes successfully
|
|
|
249 |
#
|
|
|
250 |
# Notes :
|
|
|
251 |
#
|
|
|
252 |
sub getSubDirs(@)
|
|
|
253 |
{
|
|
|
254 |
my @fullDirys = @_;
|
|
|
255 |
|
|
|
256 |
my @topDirs;
|
|
|
257 |
my @botDirs;
|
|
|
258 |
|
|
|
259 |
foreach my $fullDiry (@fullDirys)
|
|
|
260 |
{
|
|
|
261 |
my ($topDir, $botDir) = getTopBotDirs( $fullDiry );
|
|
|
262 |
|
|
|
263 |
push @topDirs, $topDir;
|
|
|
264 |
push @botDirs, $botDir;
|
|
|
265 |
}
|
|
|
266 |
|
|
|
267 |
###############################################################
|
|
|
268 |
# combine top/bot dirs
|
|
|
269 |
my @subDirs;
|
|
|
270 |
|
|
|
271 |
for( my $i = 0; $i < scalar(@topDirs); $i++)
|
|
|
272 |
{
|
|
|
273 |
push @subDirs, "$topDirs[$i]/$botDirs[$i]";
|
|
|
274 |
logprint "Pushed subDir $i: '$subDirs[-1]'";
|
|
|
275 |
}
|
|
|
276 |
|
|
|
277 |
###############################################################
|
|
|
278 |
# convert to lower case
|
|
|
279 |
foreach my $subDir (@subDirs)
|
|
|
280 |
{
|
|
|
281 |
$subDir =~ tr~A-Z~a-z~;
|
|
|
282 |
}
|
|
|
283 |
|
|
|
284 |
return @subDirs;
|
|
|
285 |
}
|
|
|
286 |
|
|
|
287 |
|
|
|
288 |
#-------------------------------------------------------------------------------
|
|
|
289 |
# Function : parseCommandLine
|
|
|
290 |
#
|
|
|
291 |
# Purpose : Parses command line; invokes help if necessary
|
|
|
292 |
#
|
|
|
293 |
# Arguments : nothing
|
|
|
294 |
#
|
|
|
295 |
# Returns : nothing
|
|
|
296 |
#
|
|
|
297 |
# Notes : Sets up various global variables; these are not checked here - they should be
|
|
|
298 |
# checked in startupChecks()
|
|
|
299 |
#
|
|
|
300 |
sub parseCommandLine()
|
|
|
301 |
{
|
|
|
302 |
my $opt_help = 0;
|
|
|
303 |
my $opt_manual = 0;
|
|
|
304 |
|
|
|
305 |
my $result = GetOptions (
|
|
|
306 |
"help" => \$opt_help, # flag
|
|
|
307 |
"manual" => \$opt_manual, # flag
|
|
|
308 |
"verbose+" => \$verbose, # flag
|
|
|
309 |
"src=s" => \$srcFile, # String
|
|
|
310 |
"dest=s" => \$destDir, # String
|
|
|
311 |
"log=s" => \$logFile, # String
|
|
|
312 |
);
|
|
|
313 |
|
|
|
314 |
pod2usage(-verbose => 2) if( $opt_manual );
|
|
|
315 |
pod2usage(-verbose => 0, -message => "Version: $VERSION") if ($opt_help > 0 || ! $result );
|
|
|
316 |
}
|
|
|
317 |
|
|
|
318 |
|
|
|
319 |
#-------------------------------------------------------------------------------
|
|
|
320 |
# Function : startupChecks
|
|
|
321 |
#
|
|
|
322 |
# Purpose : Checks that important variables are set sanely
|
|
|
323 |
#
|
|
|
324 |
# Arguments : nothing
|
|
|
325 |
#
|
|
|
326 |
# Returns : nothing
|
|
|
327 |
#
|
|
|
328 |
# Notes : Calls die() if things aren't set sanely.
|
|
|
329 |
# This function opens the logfile.
|
|
|
330 |
#
|
|
|
331 |
sub startupChecks()
|
|
|
332 |
{
|
|
|
333 |
die "You need to specify a source file using '-src=<srcFile>'\n" unless $srcFile;
|
|
|
334 |
die "You need to specify an existing destination directory using '-dest=<destDir>'\n"
|
|
|
335 |
unless $destDir;
|
|
|
336 |
|
|
|
337 |
$srcFile =~ s~\\~/~g;
|
|
|
338 |
$destDir =~ s~\\~/~g;
|
|
|
339 |
|
|
|
340 |
die "Source file '$srcFile' does not exist\n" if( ! -e $srcFile || -d $srcFile );
|
|
|
341 |
die "Destination directory '$destDir' does not exist\n" unless -d $destDir;
|
|
|
342 |
|
|
|
343 |
openLog();
|
|
|
344 |
}
|
|
|
345 |
|
|
|
346 |
|
|
|
347 |
#-------------------------------------------------------------------------------
|
|
|
348 |
# Function : getFileLines
|
|
|
349 |
#
|
|
|
350 |
# Purpose : Extracts from a set of lines only those lines for each DDL-data-object.
|
|
|
351 |
# e.g. extracts lines for a package from the lines corresponding to all packages.
|
|
|
352 |
# This function also increments the line counter.
|
|
|
353 |
#
|
|
|
354 |
# Arguments : $subDir (i) - The subdirectory the DDL-data-object is being created in
|
|
|
355 |
# $refIline (io) - Reference to $iline - how far through the current lineset
|
|
|
356 |
# we currently are. (each lineset might correspond to all
|
|
|
357 |
# packages or all tables or all views etc)
|
|
|
358 |
# @lines (i) - The current lineset
|
|
|
359 |
#
|
|
|
360 |
# Returns : $addedExtraLines - boolean value; true indicates that an 'ALTER PACKAGE'
|
|
|
361 |
# DDL-data-object was appended onto the list of lines for this
|
|
|
362 |
# DDL-data-object. This only occurs for some 'PACKAGE_BODY'
|
|
|
363 |
# objects in the release manager database.
|
|
|
364 |
# @fileLines - The lines corresponding to the DDL-data-object
|
|
|
365 |
#
|
|
|
366 |
# Notes : Invokes helper functions for each type of splitting. These functions are
|
|
|
367 |
# tailored to the release manager database and may
|
|
|
368 |
# need to be altered for other databases.
|
|
|
369 |
#
|
|
|
370 |
sub getFileLines($\$@)
|
|
|
371 |
{
|
|
|
372 |
my ($subDir, $refIline, @lines) = @_;
|
|
|
373 |
|
|
|
374 |
# Skip over blank lines
|
|
|
375 |
$$refIline++ while( $$refIline < scalar(@lines) && $lines[$$refIline] =~ m~^\s*$~ );
|
|
|
376 |
|
|
|
377 |
return (0,"end of data") if( $$refIline >= scalar(@lines) );
|
|
|
378 |
|
|
|
379 |
my %fileLinesFunctions = (
|
|
|
380 |
qq(type/type) => \&splitOnSlash,
|
|
|
381 |
qq(function/function) => \&splitOnSlash,
|
|
|
382 |
qq(procedure/procedure) => \&splitOnSlash,
|
|
|
383 |
qq(package/package_body) => \&packageBodySplit,
|
|
|
384 |
qq(package/package_spec) => \&splitOnSlash,
|
|
|
385 |
qq(synonym/synonym) => \&splitOnBlank,
|
|
|
386 |
qq(sequence/sequence) => \&splitOnBlank,
|
|
|
387 |
qq(table/table) => \&splitOnBlank,
|
|
|
388 |
qq(view/view) => \&splitOnSemiColon,
|
|
|
389 |
qq(table/index) => \&splitOnCreate,
|
|
|
390 |
qq(table/se_tbl_fbm_index_index) => \&splitOnCreate);
|
|
|
391 |
|
|
|
392 |
die "Internal coding error: could not find a case for subdirectory" .
|
|
|
393 |
" '$subDir' in the fileLinesFunctions hash table\n"
|
|
|
394 |
unless $fileLinesFunctions{$subDir};
|
|
|
395 |
|
|
|
396 |
return $fileLinesFunctions{$subDir}->( $refIline, @lines );
|
|
|
397 |
}
|
|
|
398 |
|
|
|
399 |
|
|
|
400 |
#-------------------------------------------------------------------------------
|
|
|
401 |
# Function : splitOnBlank
|
|
|
402 |
#
|
|
|
403 |
# Purpose : Extracts out the next DDL object from a set of lines by
|
|
|
404 |
# adding lines until a delineating blank line is found
|
|
|
405 |
#
|
|
|
406 |
# Arguments : $refIline (io) - reference to $iline - line to start searching on
|
|
|
407 |
# @lines (i) - Array to search through
|
|
|
408 |
#
|
|
|
409 |
# Returns : $addedExtraLines - always 0
|
|
|
410 |
# @fileLines - The lines corresponding to the DDL-data-object
|
|
|
411 |
#
|
|
|
412 |
# Notes :
|
|
|
413 |
#
|
|
|
414 |
sub splitOnBlank(\$@)
|
|
|
415 |
{
|
|
|
416 |
my ($refIline, @lines) = @_;
|
|
|
417 |
|
|
|
418 |
my @fileLines;
|
|
|
419 |
|
|
|
420 |
# Keep adding lines until we get a blank line
|
|
|
421 |
while( $$refIline < scalar(@lines) && $lines[$$refIline] !~ m~^\s*$~ )
|
|
|
422 |
{
|
|
|
423 |
push @fileLines, $lines[$$refIline];
|
|
|
424 |
$$refIline++;
|
|
|
425 |
}
|
|
|
426 |
|
|
|
427 |
# Skip past the blank line
|
|
|
428 |
$$refIline++ if $$refIline < scalar(@lines);
|
|
|
429 |
|
|
|
430 |
return (0, @fileLines);
|
|
|
431 |
}
|
|
|
432 |
|
|
|
433 |
|
|
|
434 |
#-------------------------------------------------------------------------------
|
|
|
435 |
# Function : splitOnCreate
|
|
|
436 |
#
|
|
|
437 |
# Purpose : Extracts out the next DDL object from a set of lines by
|
|
|
438 |
# searching for the next 'CREATE' keyword.
|
|
|
439 |
#
|
|
|
440 |
# Arguments : $refIline (io) - reference to $iline - line to start searching on
|
|
|
441 |
# @lines (i) - Array to search through
|
|
|
442 |
#
|
|
|
443 |
# Returns : $addedExtraLines - always 0
|
|
|
444 |
# @fileLines - The lines corresponding to the DDL-data-object
|
|
|
445 |
#
|
|
|
446 |
# Notes :
|
|
|
447 |
#
|
|
|
448 |
sub splitOnCreate(\$@)
|
|
|
449 |
{
|
|
|
450 |
my ($refIline, @lines) = @_;
|
|
|
451 |
|
|
|
452 |
my @fileLines;
|
|
|
453 |
|
|
|
454 |
my $gotFirstCreateLine = 0;
|
|
|
455 |
|
|
|
456 |
# Keep adding lines until we reach the second line starting with 'CREATE':
|
|
|
457 |
while( $$refIline < scalar(@lines) &&
|
|
|
458 |
( !$gotFirstCreateLine || $lines[$$refIline] !~ m~^\s*CREATE~) )
|
|
|
459 |
{
|
|
|
460 |
push @fileLines, $lines[$$refIline];
|
|
|
461 |
$gotFirstCreateLine = 1 if $lines[$$refIline] =~ m~^\s*CREATE~;
|
|
|
462 |
$$refIline++;
|
|
|
463 |
}
|
|
|
464 |
|
|
|
465 |
return (0, @fileLines);
|
|
|
466 |
}
|
|
|
467 |
|
|
|
468 |
|
|
|
469 |
#-------------------------------------------------------------------------------
|
|
|
470 |
# Function : splitOnSemiColon
|
|
|
471 |
#
|
|
|
472 |
# Purpose : Extracts out the next DDL object from a set of lines by
|
|
|
473 |
# adding lines until a terminating semicolon is found
|
|
|
474 |
#
|
|
|
475 |
# Arguments : $refIline (io) - reference to $iline - line to start searching on
|
|
|
476 |
# @lines (i) - Array to search through
|
|
|
477 |
#
|
|
|
478 |
# Returns : $addedExtraLines - always 0
|
|
|
479 |
# @fileLines - The lines corresponding to the DDL-data-object
|
|
|
480 |
#
|
|
|
481 |
# Notes :
|
|
|
482 |
#
|
|
|
483 |
sub splitOnSemiColon(\$@)
|
|
|
484 |
{
|
|
|
485 |
my ($refIline, @lines) = @_;
|
|
|
486 |
|
|
|
487 |
my @fileLines;
|
|
|
488 |
|
|
|
489 |
# Keep adding lines until we get a line ending with a semicolon
|
|
|
490 |
while( $$refIline < scalar(@lines) && $lines[$$refIline] !~ m~;\s*$~ )
|
|
|
491 |
{
|
|
|
492 |
push @fileLines, $lines[$$refIline];
|
|
|
493 |
$$refIline++;
|
|
|
494 |
}
|
|
|
495 |
|
|
|
496 |
# Add the line with the semicolon
|
|
|
497 |
if( $$refIline < scalar(@lines) )
|
|
|
498 |
{
|
|
|
499 |
push @fileLines, $lines[$$refIline];
|
|
|
500 |
$$refIline++;
|
|
|
501 |
}
|
|
|
502 |
|
|
|
503 |
return (0, @fileLines);
|
|
|
504 |
}
|
|
|
505 |
|
|
|
506 |
|
|
|
507 |
#-------------------------------------------------------------------------------
|
|
|
508 |
# Function : splitOnSlash
|
|
|
509 |
#
|
|
|
510 |
# Purpose : Extracts out the next DDL object from a set of lines by
|
|
|
511 |
# adding lines until a terminating slash is found
|
|
|
512 |
#
|
|
|
513 |
# Arguments : $refIline (io) - reference to $iline - line to start searching on
|
|
|
514 |
# @lines (i) - Array to search through
|
|
|
515 |
#
|
|
|
516 |
# Returns : $addedExtraLines - always 0
|
|
|
517 |
# @fileLines - The lines corresponding to the DDL-data-object
|
|
|
518 |
#
|
|
|
519 |
# Notes :
|
|
|
520 |
#
|
|
|
521 |
sub splitOnSlash(\$@)
|
|
|
522 |
{
|
|
|
523 |
my ($refIline, @lines) = @_;
|
|
|
524 |
|
|
|
525 |
my @fileLines;
|
|
|
526 |
|
|
|
527 |
# Keep adding lines until we get a line starting with a slash
|
|
|
528 |
while( $$refIline < scalar(@lines) && $lines[$$refIline] !~ m~^/\s*$~ )
|
|
|
529 |
{
|
|
|
530 |
push @fileLines, $lines[$$refIline];
|
|
|
531 |
$$refIline++;
|
|
|
532 |
}
|
|
|
533 |
|
|
|
534 |
# Add the line with the slash
|
|
|
535 |
if( $$refIline < scalar(@lines) )
|
|
|
536 |
{
|
|
|
537 |
push @fileLines, $lines[$$refIline];
|
|
|
538 |
$$refIline++;
|
|
|
539 |
}
|
|
|
540 |
|
|
|
541 |
return (0, @fileLines);
|
|
|
542 |
}
|
|
|
543 |
|
|
|
544 |
|
|
|
545 |
#-------------------------------------------------------------------------------
|
|
|
546 |
# Function : packageBodySplit
|
|
|
547 |
#
|
|
|
548 |
# Purpose : Extracts out the next DDL object from a set of lines by
|
|
|
549 |
# adding lines until a terminating slash is found. It then
|
|
|
550 |
# adds extra lines from an 'ALTER PACKAGE' directive
|
|
|
551 |
# if such a directive immediately follows the slash.
|
|
|
552 |
#
|
|
|
553 |
# Arguments : $refIline (io) - reference to $iline - line to start searching on
|
|
|
554 |
# @lines (i) - Array to search through
|
|
|
555 |
#
|
|
|
556 |
# Returns : $addedExtraLines - always 0
|
|
|
557 |
# @fileLines - The lines corresponding to the DDL-data-object
|
|
|
558 |
#
|
|
|
559 |
# Notes :
|
|
|
560 |
#
|
|
|
561 |
sub packageBodySplit(\$@)
|
|
|
562 |
{
|
|
|
563 |
my ($refIline, @lines) = @_;
|
|
|
564 |
|
|
|
565 |
my ($addedExtraLines, @fileLines) = splitOnSlash($$refIline, @lines);
|
|
|
566 |
|
|
|
567 |
# Keep looking for lines if there is a 'ALTER PACKAGE' in the next
|
|
|
568 |
# slash-delimited region
|
|
|
569 |
if( @fileLines && $$refIline < scalar(@lines) )
|
|
|
570 |
{
|
|
|
571 |
my $keyWord = $fileLines[0];
|
|
|
572 |
$keyWord =~ s~^\s*CREATE\s+PACKAGE\s+BODY\s+".*?"\."(.*?)".*~$1~;
|
|
|
573 |
|
|
|
574 |
my @extraLines;
|
|
|
575 |
my $jline = $$refIline;
|
|
|
576 |
|
|
|
577 |
# Skip over blank lines
|
|
|
578 |
$jline++ while( $jline < scalar(@lines) && $lines[$jline] =~ m~^\s*$~ );
|
|
|
579 |
|
|
|
580 |
# add lines to @extraLines until we find a slash
|
|
|
581 |
while( $jline < scalar(@lines) && $lines[$jline] !~ m~^/\s*$~ )
|
|
|
582 |
{
|
|
|
583 |
push @extraLines, $lines[$jline];
|
|
|
584 |
$jline++;
|
|
|
585 |
}
|
|
|
586 |
|
|
|
587 |
# add in the line with the slash to @extraLines
|
|
|
588 |
if( $jline < scalar(@lines) )
|
|
|
589 |
{
|
|
|
590 |
push @extraLines, $lines[$jline];
|
|
|
591 |
$jline++;
|
|
|
592 |
}
|
|
|
593 |
|
|
|
594 |
if( @extraLines &&
|
|
|
595 |
$extraLines[0] =~ m~^\s*ALTER\s+PACKAGE\s+".*?"\."${keyWord}"~ )
|
|
|
596 |
{
|
|
|
597 |
# Yes! Add in the 'ALTER PACKAGE' text
|
|
|
598 |
$$refIline = $jline;
|
|
|
599 |
push @fileLines, @extraLines;
|
|
|
600 |
$addedExtraLines = 1;
|
|
|
601 |
}
|
|
|
602 |
}
|
|
|
603 |
|
|
|
604 |
return ($addedExtraLines, @fileLines);
|
|
|
605 |
}
|
|
|
606 |
|
|
|
607 |
|
|
|
608 |
#-------------------------------------------------------------------------------
|
|
|
609 |
# Function : getFilename
|
|
|
610 |
#
|
|
|
611 |
# Purpose : Extracts the name of the .sql file to create from its lines
|
|
|
612 |
#
|
|
|
613 |
# Arguments : $subDir (i) - The subdirectory where the .sql file will be created
|
|
|
614 |
# @fileLines (i) - The lines to be parsed for the filename
|
|
|
615 |
#
|
|
|
616 |
# Returns : $keyWord - the filename to be created (doesn't have any path attached but
|
|
|
617 |
# does have a .sql extension)
|
|
|
618 |
#
|
|
|
619 |
# Notes : This function is tailored specifically for the release manager database, so cannot be
|
|
|
620 |
# guaranteed to work for other databases
|
|
|
621 |
#
|
|
|
622 |
sub getFilename($@)
|
|
|
623 |
{
|
|
|
624 |
my ($subDir, @fileLines) = @_;
|
|
|
625 |
|
|
|
626 |
return "allData.sql" unless defined $wantToExtract{$subDir};
|
|
|
627 |
|
|
|
628 |
die "Bug! No lines scanned for subDir '$subDir'\n" unless scalar(@fileLines);
|
|
|
629 |
|
|
|
630 |
my $keyWord = $fileLines[0];
|
|
|
631 |
|
|
|
632 |
# Extract out the word that is in the second double quotes
|
|
|
633 |
# e.g. for the line:
|
|
|
634 |
#CREATE SYNONYM "RELEASE_MANAGER"."APPLICATIONS" FOR "ACCESS_MANAGER"."APPLICATIONS";
|
|
|
635 |
# extract out 'APPLICATIONS'
|
|
|
636 |
$keyWord =~ s~^.*?".*?"\."(.*?)".*~$1~;
|
|
|
637 |
|
|
|
638 |
if( $subDir eq "type/type" )
|
|
|
639 |
{
|
|
|
640 |
my $firstWord = $fileLines[0];
|
|
|
641 |
# $firstWord literally stores the first word of the line
|
|
|
642 |
# which for 'type/type' could be 'CREATE' or 'ALTER'
|
|
|
643 |
$firstWord =~ s~^\s*(\S+).*~$1~;
|
|
|
644 |
$keyWord .= "_$firstWord";
|
|
|
645 |
}
|
|
|
646 |
|
|
|
647 |
$keyWord .= ".sql";
|
|
|
648 |
$keyWord =~ tr~A-Z~a-z~;
|
|
|
649 |
|
|
|
650 |
logprint "getFilename() returning '$keyWord' for subdir '$subDir'";
|
|
|
651 |
|
|
|
652 |
return $keyWord;
|
|
|
653 |
}
|
|
|
654 |
|
|
|
655 |
|
|
|
656 |
#-------------------------------------------------------------------------------
|
|
|
657 |
# Function : writeData
|
|
|
658 |
#
|
|
|
659 |
# Purpose : Write out files to disk
|
|
|
660 |
#
|
|
|
661 |
# Arguments : $refSubDirs (i) - reference to @subDirs - list of subdirectories having
|
|
|
662 |
# files created.
|
|
|
663 |
# $refSubSectionLiness (i) - reference to @subSectionLiness (2D array of lines
|
|
|
664 |
# per each DDL-data-type)
|
|
|
665 |
#
|
|
|
666 |
# Returns : $linesWritten - a count of the lines written
|
|
|
667 |
#
|
|
|
668 |
# Notes : This is a major function as it calls getFileLines() repeatedly to break
|
|
|
669 |
# down line-list within @subSectionLiness and also performs a lot of loops.
|
|
|
670 |
# So really does a lot more than just writing data.
|
|
|
671 |
#
|
|
|
672 |
# This function also records to file which of the files have had 'extra' bits
|
|
|
673 |
# written to them. (For release manager these are a few 'PACKAGE BODY' functions
|
|
|
674 |
# that have had 'ALTER PACKAGE' added on). This is so that ddlfile.pl can break
|
|
|
675 |
# these back into two DDL-data-objects and re-delimit them with a slash.
|
|
|
676 |
#
|
|
|
677 |
# This function also writes out an index file of all filenames created.
|
|
|
678 |
# This allows the order in which the files were in the original datapump dump
|
|
|
679 |
# file to be recorded.
|
|
|
680 |
#
|
|
|
681 |
sub writeData(\@\@)
|
|
|
682 |
{
|
|
|
683 |
my ($refSubDirs, $refSubSectionLiness) = @_;
|
|
|
684 |
|
|
|
685 |
my $linesWritten = 0;
|
|
|
686 |
|
|
|
687 |
my @filesWithExtras;
|
|
|
688 |
my @filenamesCreated;
|
|
|
689 |
|
|
|
690 |
for( my $isubdir = 0; $isubdir < scalar(@$refSubDirs); $isubdir++)
|
|
|
691 |
{
|
|
|
692 |
# Simplify by copying the array of lines for this subdirectory into '@lines'
|
|
|
693 |
my @lines = @{ $$refSubSectionLiness[$isubdir] };
|
|
|
694 |
|
|
|
695 |
logprint "Working with subdir '$$refSubDirs[$isubdir]'";
|
|
|
696 |
|
|
|
697 |
# Recall that wantToExtract hash indicates whether or not we want to
|
|
|
698 |
# write one or many (i.e. extract them out) files to each subdirectory.
|
|
|
699 |
if( $wantToExtract{$$refSubDirs[$isubdir]} )
|
|
|
700 |
{
|
|
|
701 |
logprint "Going to separate into separate files for subdir '$$refSubDirs[$isubdir]'";
|
|
|
702 |
|
|
|
703 |
my $iline = 0;
|
|
|
704 |
my %filenames;
|
|
|
705 |
|
|
|
706 |
# Keep extracting out lines for files until we run out
|
|
|
707 |
until( $iline >= scalar(@lines) )
|
|
|
708 |
{
|
|
|
709 |
my ($addedExtraLines, @fileLines) =
|
|
|
710 |
getFileLines( $$refSubDirs[$isubdir], $iline, @lines);
|
|
|
711 |
|
|
|
712 |
last if( scalar(@fileLines) == 1 && $fileLines[0] eq "end of data" );
|
|
|
713 |
|
|
|
714 |
my $filename = getFilename( $$refSubDirs[$isubdir], @fileLines );
|
|
|
715 |
my $fullname = "$destDir/$$refSubDirs[$isubdir]/$filename";
|
|
|
716 |
|
|
|
717 |
push @filesWithExtras, "$$refSubDirs[$isubdir]/$filename" if $addedExtraLines;
|
|
|
718 |
|
|
|
719 |
die "Duplicate filename '$fullname'" if $filenames{$filename};
|
|
|
720 |
$filenames{$filename} = 1;
|
|
|
721 |
|
|
|
722 |
writeArray( $fullname, @fileLines);
|
|
|
723 |
push @filenamesCreated, "$$refSubDirs[$isubdir]/$filename";
|
|
|
724 |
}
|
|
|
725 |
}
|
|
|
726 |
else
|
|
|
727 |
{
|
|
|
728 |
logprint "Writing out just one file in subdir '$$refSubDirs[$isubdir]'";
|
|
|
729 |
writeArray( "$destDir/$$refSubDirs[$isubdir]/allData.sql", @lines);
|
|
|
730 |
push @filenamesCreated, "$$refSubDirs[$isubdir]/allData.sql";
|
|
|
731 |
}
|
|
|
732 |
|
|
|
733 |
$linesWritten += scalar( @lines );
|
|
|
734 |
}
|
|
|
735 |
|
|
|
736 |
writeArray( "$destDir/bookKeeping/filesWithExtras", @filesWithExtras);
|
|
|
737 |
writeArray( "$destDir/bookKeeping/orderedFiles", @filenamesCreated );
|
|
|
738 |
|
|
|
739 |
return $linesWritten;
|
|
|
740 |
}
|
|
|
741 |
|
|
|
742 |
|
|
|
743 |
#######################################################################
|
|
|
744 |
# Documentation
|
|
|
745 |
#######################################################################
|
|
|
746 |
|
|
|
747 |
=pod
|
|
|
748 |
|
|
|
749 |
=head1 NAME
|
|
|
750 |
|
|
|
751 |
schemadump.pl - creates clearcase directory structure from datapump output
|
|
|
752 |
|
|
|
753 |
=head1 SYNOPSIS
|
|
|
754 |
|
|
|
755 |
schemadump.pl [options] -src=<srcFile> -dest=<destDir>
|
|
|
756 |
|
|
|
757 |
Options:
|
|
|
758 |
|
|
|
759 |
-help - brief help message
|
|
|
760 |
-man - Full documentation
|
|
|
761 |
-src=srcFile - File that stores the output of datapump (mandatory)
|
|
|
762 |
-dest=destDir - Directory to unpack to (mandatory)
|
|
|
763 |
-log=logFile - Log messages to this file
|
|
|
764 |
|
|
|
765 |
=head1 OPTIONS
|
|
|
766 |
|
|
|
767 |
=over 8
|
|
|
768 |
|
|
|
769 |
=item B<-help>
|
|
|
770 |
|
|
|
771 |
Print a brief help message and exits.
|
|
|
772 |
|
|
|
773 |
=item B<-man>
|
|
|
774 |
|
|
|
775 |
Prints the manual page and exits.
|
|
|
776 |
|
|
|
777 |
=item B<-src=srcFile>
|
|
|
778 |
|
|
|
779 |
Specify what file to extract out the clearcase files from.
|
|
|
780 |
|
|
|
781 |
=item B<-dest=destDir>
|
|
|
782 |
|
|
|
783 |
Specify where to unpack the clearcase files to. This must be an
|
|
|
784 |
existing directory.
|
|
|
785 |
|
|
|
786 |
=item B<-log=logFile>
|
|
|
787 |
|
|
|
788 |
Specify a file to write log messages to. Default is to just write
|
|
|
789 |
to the terminal.
|
|
|
790 |
|
|
|
791 |
=back
|
|
|
792 |
|
|
|
793 |
=head1 DESCRIPTION
|
|
|
794 |
|
|
|
795 |
This script is used to break down the output of datapump. It is
|
|
|
796 |
designed for use with the release manager database.
|
|
|
797 |
|
|
|
798 |
=cut
|