File Coverage

blib/lib/PeopleSoft.pm
Criterion Covered Total %
statement 21 125 16.8
branch 0 24 0.0
condition 0 36 0.0
subroutine 7 15 46.6
pod 7 8 87.5
total 35 208 16.8


line stmt bran cond sub pod time code
1             # Copyright (c) 2003 William Goedicke. All rights reserved. This
2             # program is free software; you can redistribute it and/or modify it
3             # under the same terms as Perl itself.
4              
5             =head1 NAME
6              
7             PeopleSoft - Procedural interface for working with PeopleSoft
8             applications.
9              
10             =head1 SYNOPSIS
11              
12             use PeopleSoft;
13             my $dbh = get_dbh( $username, $password, $SID );
14             my $tbl_name_aref = get_tbl_names( 'table_name_spec', $dbh);
15             my $tbl_name_aref = where_from( $view_name, $dbh );
16             if ( is_view($name, $dbh) )
17             my $count = get_rec_count( $tbl_name, $dbh );
18             if ( table_exists($tbl_name, $dbh) ) {...}
19             my $metadata_href = get_fld_metadata_href( $tbl_name, $dbh );
20              
21             =cut
22              
23 1     1   44921 use DBI;
  1         21612  
  1         88  
24 1     1   12 use strict;
  1         2  
  1         41  
25 1     1   1361 use Data::Dumper;
  1         26348  
  1         3176  
26              
27             package PeopleSoft;
28 1     1   15 use Exporter;
  1         2  
  1         52  
29 1     1   6 use vars qw(@ISA @EXPORT $VERSION);
  1         1  
  1         1417  
30             $VERSION = '1.05';
31             @ISA = qw(Exporter);
32              
33             @EXPORT = qw(get_tbl_names
34             get_dbh
35             get_rec_count
36             is_view
37             table_exists
38             where_from
39             get_fld_metadata_href
40             make_ins_stmt
41             );
42              
43             =head1 DESCRIPTION
44              
45             This module provides a set of simple table query and manipulation
46             functions.
47              
48             The following functions are provided (and exported) by this module:
49              
50             =cut
51              
52             # --------------------------------- get_dbh()
53              
54             =over 3
55              
56             =item get_dbh($username, $password, $SID)
57              
58             The get_dbh() function will return a database handle (courtesy of
59             DBI/DBD) for use in accessing the database. It returns C if it
60             fails.
61              
62             =back
63              
64             =cut
65              
66             sub get_dbh {
67 0     0 1   my ( $username, $password, $SID ) = @_;
68              
69 0 0         my $dbh = DBI->connect( "dbi:Oracle:${SID}", $username, $password,
70             { PrintError => 1,
71             LongReadLen => 16 * 1024,
72             RaiseError => 0,
73             AutoCommit => 0}
74             ) or die $DBI::errstr;
75 0           return $dbh;
76             }
77             # ---------------------------------
78              
79             =over 3
80              
81             =item get_table_names('table_name_spec', $dbh)
82              
83             Get all the table names which match I from the
84             database tied to $dbh. I should be in a form
85             appropriate for insertion into a SQL where clause (e.g. 'PS_%').
86              
87             Returns an empty list if it fails.
88              
89             =back
90              
91             =cut
92              
93             sub get_tbl_names {
94 0     0 0   my ( $name_spec, $dbh ) = @_;
95 0           my ( @names, @results );
96              
97 0           my $sql_cmd = "select table_name from all_tables where table_name like '$name_spec'";
98 0           my $sth = $dbh->prepare($sql_cmd);
99 0           $sth->execute;
100 0           while ( @results = $sth->fetchrow_array ) {
101 0           push(@names, @results);
102             }
103 0           $sth->finish;
104              
105 0           return \@names;
106             }
107             # ---------------------------------
108              
109             =over 3
110              
111             =item get_rec_count( $tbl_name, $dbh );
112              
113             Returns the number of records in a table.
114              
115             =back
116              
117             =cut
118              
119             sub get_rec_count {
120 0     0 1   my ( $tbl_name, $dbh ) = @_;
121 0           my ( $count, $result );
122              
123 0           my $sth = $dbh->prepare("select count(*) from $tbl_name");
124 0 0         if ( defined $sth ) {
125 0           $sth->execute;
126 0           while ( ( $result ) = $sth->fetchrow_array ) {
127 0           $count = $result;
128             }
129             }
130 0           $sth->finish;
131              
132 0           return $count;
133             }
134              
135             # ---------------------------------
136              
137             =over 3
138              
139             =item tbl_exists( $tbl_name, $dbh )
140              
141             Tests for the existence of I<$tbl_name>. If the table exists 1 is
142             returned otherwise 0 is returned. Typical use would be:
143              
144             if( tbl_exists($tbl_a, $dbh_a) and tbl_exists($tbl_b, $dbh_b) ) {...}
145              
146             =back
147              
148             =cut
149              
150             sub tbl_exists {
151 0     0 1   my ( $tbl_name, $dbh ) = @_;
152 0           my ( @results );
153              
154 0           my $sql_cmd = "select table_name from all_tables where table_name like '$tbl_name'";
155 0           my $sth = $dbh->prepare($sql_cmd);
156 0           $sth->execute;
157 0           while ( @results = $sth->fetchrow_array ) {
158 0 0         if ( length(@results) == 0 ) { return 0; }
  0            
159 0           else { return 1; }
160             }
161             }
162             # --------------------------------- Query the database and return metadata hashref
163              
164             =over 3
165              
166             =item get_fld_metadata_href( $tbl_name, $dbh )
167              
168             This function returns a reference to a hash keyed by field name. For
169             every field in I<$tbl_name> the hash contains attributes for: data
170             type, precision and nullable. Typical use would be:
171              
172             $metadata = get_fld_metadata_href( $tbl, $dbh );
173             foreach my $field ( keys( %{$metadata} ) ) {
174             print "Field $field has data type $$metadata{$field}->{TYPE}\n";
175             print "Field $field has size $$metadata{$field}->{PRECISION}\n";
176             if ( $$metadata{$field}->{NULLABLE} ) { print "Field $field accepts nulls\n";
177             }
178              
179             =back
180              
181             =cut
182              
183             sub get_fld_metadata_href {
184 0     0 1   my ( $tbl_name, $dbh ) = @_;
185 0           my ($nullable, $field, $metadata, %TYPES, $desc);
186 0           my $i = -10;
187              
188 0           foreach $desc ( qw(WLONGVARCHAR WVARCHAR WCHAR BIT TINYINT BIGINT
189             LONGVARBINARY VARBINARY BINARY LONGVARCHAR
190             NA CHAR NUMERIC DECIMAL INTEGER SMALLINT FLOAT
191             REAL DOUBLE DATE TIME TIMESTAMP VARCHAR) ) {
192 0           $TYPES{$i++} = $desc;
193             }
194              
195 0           my $sql_cmd = "select * from $tbl_name where ROWNUM = 1";
196              
197 0           my $sth = $dbh->prepare($sql_cmd);
198 0           $sth->execute();
199              
200 0           for ( my $i=0; $i < $sth->{NUM_OF_FIELDS}; $i++ ) {
201 0 0         if ( $sth->{NULLABLE}->[$i] != 1 ) {
202 0           $nullable = 0;
203             } else {
204 0           $nullable = 1;
205             }
206 0           my $field = $sth->{NAME}->[$i];
207 0           $$metadata{$field}->{TYPE} = $TYPES{$sth->{TYPE}->[$i]};
208 0           $$metadata{$field}->{NULLABLE} = $nullable;
209 0           $$metadata{$field}->{PRECISION} = $sth->{PRECISION}->[$i];
210             }
211 0           $sth->finish();
212 0           return $metadata;
213             }
214             # ---------------------------------------------------- print_insert_statement
215              
216             =over 3
217              
218             =item make_ins_stmt($src_md_href,$dest_md_href,$tbl,$src_data_href)
219              
220             This function returns a SQL insert statement. It is used to migrate
221             data between tables with "slightly" different table structures. This
222             is accomplished by inserting only data for fields that have the same
223             name and data type. It then creates default values for not-nullable
224             fields in the destinition table that otherwise would not get values.
225             This is particularly useful in migrating data to different versions of
226             PeopleSoft applications.
227              
228             Typical usage is:
229              
230              
231             $src_md_href = get_fld_metadata_href( $tbl, $src_dbh );
232             $dest_md_href = get_fld_metadata_href( $tbl, $dest_dbh );
233              
234             while( $data_href = $sth->fetchrow_hashref ) {
235             $insert_sql_script .= make_ins_stmt($src_md_href, $dest_md_href, $tbl, $data_href);
236             $insert_sql_script .= "\n";
237             }
238              
239             =back
240              
241             =cut
242              
243             sub make_ins_stmt {
244 0     0 1   my ( $src_fld_metadata_href, $dest_fld_metadata_href, $dest_tbl_name, $src_data_href ) = @_;
245 0           my ( $name, @fnames, @dvals, $ins_stmt, %seen );
246              
247 0           my %src_metadata = %{$src_fld_metadata_href};
  0            
248 0           my %dest_metadata = %{$dest_fld_metadata_href};
  0            
249 0           my %src_data = %{$src_data_href};
  0            
250              
251 0           my @all_fields = grep { ! $seen{$_} ++ } ( keys(%src_metadata), keys(%dest_metadata) );
  0            
252              
253 0           $ins_stmt = "insert into $dest_tbl_name (";
254 0           foreach $name ( @all_fields ) {
255 0 0         if ( ! defined $dest_metadata{$name}->{TYPE} ) { next; }
  0            
256 0           push @fnames, $name;
257             }
258 0           $ins_stmt .= join ",", @fnames;
259 0           $ins_stmt .= ") values (";
260 0           foreach $name ( @fnames ) {
261 0 0         if ( ! defined $src_metadata{$name}->{TYPE} ) {
262 0 0 0       if ( $dest_metadata{$name}->{TYPE} =~ /DATE/ ) {
    0 0        
    0 0        
      0        
      0        
      0        
263 0           push @dvals, "'01-JAN-00'";
264             }
265             elsif ( $dest_metadata{$name}->{TYPE} =~ /CHAR/ ) {
266 0           push @dvals, "' '";
267             }
268             elsif ( $dest_metadata{$name}->{TYPE} =~ /BIN/ or
269             $dest_metadata{$name}->{TYPE} =~ /INT/ or
270             $dest_metadata{$name}->{TYPE} =~ /FLOAT/ or
271             $dest_metadata{$name}->{TYPE} =~ /NUMERIC/ or
272             $dest_metadata{$name}->{TYPE} =~ /DECIMAL/ or
273             $dest_metadata{$name}->{TYPE} =~ /REAL/ or
274             $dest_metadata{$name}->{TYPE} =~ /DOUBLE/ ) {
275 0           push @dvals, 0;
276             }
277             else {
278 0           print "Uh oh! I didn't recognize the field type $dest_metadata{$name}->{TYPE}.\n";
279 0           print "You'ld best add it to the print_insert_statment function.\n";
280             }
281             }
282             else {
283 0 0 0       if ( $dest_metadata{$name}->{TYPE} =~ /DATE/ ) {
    0 0        
    0 0        
      0        
      0        
      0        
284 0           push @dvals, "'$src_data{$name}'";
285             }
286             elsif ( $dest_metadata{$name}->{TYPE} =~ /CHAR/ ) {
287 0           push @dvals, "'$src_data{$name}'";
288             }
289             elsif ( $dest_metadata{$name}->{TYPE} =~ /BIN/ or
290             $dest_metadata{$name}->{TYPE} =~ /INT/ or
291             $dest_metadata{$name}->{TYPE} =~ /FLOAT/ or
292             $dest_metadata{$name}->{TYPE} =~ /NUMERIC/ or
293             $dest_metadata{$name}->{TYPE} =~ /DECIMAL/ or
294             $dest_metadata{$name}->{TYPE} =~ /REAL/ or
295             $dest_metadata{$name}->{TYPE} =~ /DOUBLE/ ) {
296 0           push @dvals, $src_data{$name};
297             }
298             else {
299 0           print "Uh oh! I didn't recognize the field type of $name.\n";
300 0           print "You'ld best add it to the print_insert_statment function.\n";
301             }
302             }
303             }
304 0           $ins_stmt .= join ",", @dvals;
305 0           $ins_stmt .= ")";
306              
307 0           return $ins_stmt;
308             }
309             #-----------------------------------------------------------------
310              
311             =over 3
312              
313             =item where_from($view_name, $dbh)
314              
315             This function returns a reference to an array of the names of all the
316             tables which are used to create the view. This is useful when
317             deriving table loading sequences.
318              
319             =back
320              
321             =cut
322              
323             sub where_from {
324 1     1   8 use Data::Dumper;
  1         2  
  1         301  
325 0     0 1   my ( $view_name, $dbh ) = @_;
326 0           my ( $sql_text, @results, %utabs, @tabs, @t2, @all_tabs, %ah );
327              
328 0           my $sql_cmd = "select text from all_views where view_name = '$view_name'";
329 0           my $sth = $dbh->prepare($sql_cmd);
330 0           $sth->execute;
331 0           while( @results = $sth->fetchrow_array ) {
332 0           $sql_text .= $results[0];
333             }
334 0           @tabs = $sql_text =~
335             m/# -- required part:
336             \s+FROM\s+
337             \w+ # table name
338             \s* # normal space after the table name
339             # -- optional part:
340             (?:\w+)? # table abbrv
341             # -- and any number of:
342             (?:\s*,\s*\w+ # a comma and table name, with optional space.
343             (?:\s*\w+)?)* # optional space, optional table abbrv.
344             /xg;
345              
346 0           foreach ( @tabs ) {
347 0           @t2 = $_ =~
348             m/
349             \s* (?:FROM|,) \s* (\w+) (?:\s+\w+)*/xg;
350 0           foreach ( @t2 ) {
351 0           $ah{$_} = '';
352             }
353             }
354              
355 0           push @all_tabs, keys(%ah);
356              
357 0           return \@all_tabs;
358             }
359             #-----------------------------------------------------------------
360              
361             =over 3
362              
363             =item is_view($name, $dbh)
364              
365             This function returns true (i.e. 1) if the given name is the name
366             of a view in the $dbh.
367              
368             =back
369              
370             =cut
371              
372             sub is_view {
373 1     1   6 use Data::Dumper;
  1         2  
  1         149  
374 0     0 1   my ( $name, $dbh ) = @_;
375 0           my ( $sql_text, @results, %utabs, @tabs, @t2, @all_tabs, %ah );
376              
377 0           my $sql_cmd = "select view_name from all_views where view_name = '$name'";
378 0           my $sth = $dbh->prepare($sql_cmd);
379 0           $sth->execute;
380 0           while( @results = $sth->fetchrow_array ) {
381 0           return 1;
382             }
383              
384 0           return undef;
385             }