~percona-toolkit-dev/percona-toolkit/fix-change-master-bug-932614

« back to all changes in this revision

Viewing changes to lib/Schema.pm

  • Committer: Daniel Nichter
  • Date: 2011-06-24 17:22:06 UTC
  • Revision ID: daniel@percona.com-20110624172206-c7q4s4ad6r260zz6
Add lib/, t/lib/, and sandbox/.  All modules are updated and passing on MySQL 5.1.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# This program is copyright 2011 Percona Inc.
 
2
# Feedback and improvements are welcome.
 
3
#
 
4
# THIS PROGRAM IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED
 
5
# WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
 
6
# MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
 
7
#
 
8
# This program is free software; you can redistribute it and/or modify it under
 
9
# the terms of the GNU General Public License as published by the Free Software
 
10
# Foundation, version 2; OR the Perl Artistic License.  On UNIX and similar
 
11
# systems, you can issue `man perlgpl' or `man perlartistic' to read these
 
12
# licenses.
 
13
#
 
14
# You should have received a copy of the GNU General Public License along with
 
15
# this program; if not, write to the Free Software Foundation, Inc., 59 Temple
 
16
# Place, Suite 330, Boston, MA  02111-1307  USA.
 
17
# ###########################################################################
 
18
# Schema package $Revision: 7565 $
 
19
# ###########################################################################
 
20
 
 
21
# Package: Schema
 
22
# Schema encapsulates a data structure representing databases and tables.
 
23
# Although in MySQL "schema" is technically equivalent to "databae", we
 
24
# use "schema" loosely to mean a collection of schema objects: databases,
 
25
# tables, and columns.  These objects are organized in a hash keyed on
 
26
# database and table names.  The hash is called schema and looks like,
 
27
# (start code)
 
28
#   db1 => {
 
29
#      tbl1 => {
 
30
#         db         => 'db1',
 
31
#         tbl        => 'tbl1',
 
32
#         tbl_struct => <TableParser::parse()>
 
33
#         ddl        => "CREATE TABLE `tbl` ( ...",
 
34
#      }
 
35
#   }
 
36
# (stop code)
 
37
# Each table has at least a db and tbl key and probably a tbl_struct.
 
38
#
 
39
# The important thing about a Schema object is that it should be the only
 
40
# data structure with this data, and other modules should reference and add
 
41
# data to it rather than creating other similar copies.  <ColumnMap> does
 
42
# this for example.
 
43
#
 
44
# The other important thing about a Schema object is that the data structure
 
45
# is the standard.  Other modules should take db or tbl hashrefs pointing
 
46
# into the data structure.  Tbl hashrefs should always have at least at db
 
47
# and tbl key (which is redundant but necessary so that each tbl hashref
 
48
# includes its own database and table name).
 
49
#
 
50
# Schema objects are usually added by a <SchemaIterator>, but you can add
 
51
# them manually if needed; see <add_schema_object()>.
 
52
{
 
53
package Schema;
 
54
 
 
55
use strict;
 
56
use warnings FATAL => 'all';
 
57
use English qw(-no_match_vars);
 
58
use constant MKDEBUG => $ENV{MKDEBUG} || 0;
 
59
 
 
60
# Sub: new
 
61
#
 
62
# Parameters:
 
63
#   %args - Arguments
 
64
#
 
65
# Returns:
 
66
#  Schema object
 
67
sub new {
 
68
   my ( $class, %args ) = @_;
 
69
   my @required_args = qw();
 
70
   foreach my $arg ( @required_args ) {
 
71
      die "I need a $arg argument" unless $args{$arg};
 
72
   }
 
73
 
 
74
   my $self = {
 
75
      %args,
 
76
      schema  => {},  # keyed on db->tbl
 
77
      # columns => {},  # No tools use is_duplicate_table() or
 
78
      # tables  => {},  # is_duplicate_column() yet...
 
79
   };
 
80
   return bless $self, $class;
 
81
}
 
82
 
 
83
sub get_schema {
 
84
   my ( $self ) = @_;
 
85
   return $self->{schema};
 
86
}
 
87
 
 
88
sub get_table {
 
89
   my ( $self, $db_name, $tbl_name ) = @_;
 
90
   if ( exists $self->{schema}->{$db_name}
 
91
        && exists $self->{schema}->{$db_name}->{$tbl_name} ) {
 
92
      return $self->{schema}->{$db_name}->{$tbl_name};
 
93
   }
 
94
   return;
 
95
}
 
96
 
 
97
#sub is_duplicate_column {
 
98
#   my ( $self, $col ) = @_;
 
99
#   return unless $col;
 
100
#   return ($self->{columns}->{$col} || 0) > 1 ? 1 : 0;
 
101
#}
 
102
 
 
103
#sub is_duplicate_table {
 
104
#   my ( $self, $tbl ) = @_;
 
105
#   return unless $tbl;
 
106
#   return ($self->{tables}->{$tbl} || 0) > 1 ? 1 : 0;
 
107
#}
 
108
 
 
109
# Sub: add_schema_object
 
110
#   Add a schema object.  This sub is called by
 
111
#   <SchemaIterator::next_schema_object()>.
 
112
#
 
113
# Parameters:
 
114
#   $schema_object - Schema object hashref.
 
115
sub add_schema_object {
 
116
   my ( $self, $schema_object ) = @_;
 
117
   die "I need a schema_object argument" unless $schema_object;
 
118
 
 
119
   my ($db, $tbl) = @{$schema_object}{qw(db tbl)};
 
120
   if ( !$db || !$tbl ) {
 
121
      warn "No database or table for schema object";
 
122
      return;
 
123
   }
 
124
 
 
125
   my $tbl_struct = $schema_object->{tbl_struct};
 
126
   if ( !$tbl_struct ) {
 
127
      warn "No table structure for $db.$tbl";
 
128
      return;
 
129
   }
 
130
 
 
131
   # Add/save this schema object.
 
132
   $self->{schema}->{lc $db}->{lc $tbl} = $schema_object;
 
133
 
 
134
   # Get duplicate column and table names.
 
135
   # map { $self->{columns}->{lc $_}++ } @{$tbl_struct->{cols}};
 
136
   # $self->{tables}->{lc $tbl_struct->{name}}++;
 
137
 
 
138
   return;
 
139
}
 
140
 
 
141
sub find_column {
 
142
   my ( $self, %args ) = @_;
 
143
   my $ignore = $args{ignore};
 
144
   my $schema = $self->{schema};
 
145
 
 
146
   my ($col, $tbl, $db);
 
147
   if ( my $col_name = $args{col_name} ) {
 
148
      ($col, $tbl, $db) = reverse map { s/`//g; $_ } split /[.]/, $col_name;
 
149
      MKDEBUG && _d('Column', $col_name, 'has db', $db, 'tbl', $tbl,
 
150
         'col', $col);
 
151
   }
 
152
   else {
 
153
      ($col, $tbl, $db) = @args{qw(col tbl db)};
 
154
   }
 
155
 
 
156
   $db  = lc $db;
 
157
   $tbl = lc $tbl;
 
158
   $col = lc $col;
 
159
 
 
160
   if ( !$col ) {
 
161
      MKDEBUG && _d('No column specified or parsed');
 
162
      return;
 
163
   }
 
164
   MKDEBUG && _d('Finding column', $col, 'in', $db, $tbl);
 
165
 
 
166
   if ( $db && !$schema->{$db} ) {
 
167
      MKDEBUG && _d('Database', $db, 'does not exist');
 
168
      return;
 
169
   }
 
170
 
 
171
   if ( $db && $tbl && !$schema->{$db}->{$tbl} ) {
 
172
      MKDEBUG && _d('Table', $tbl, 'does not exist in database', $db);
 
173
      return;
 
174
   }
 
175
 
 
176
   my @tbls;
 
177
   my @search_dbs = $db ? ($db) : keys %$schema;
 
178
   DATABASE:
 
179
   foreach my $search_db ( @search_dbs ) {
 
180
      my @search_tbls = $tbl ? ($tbl) : keys %{$schema->{$search_db}};
 
181
 
 
182
      TABLE:
 
183
      foreach my $search_tbl ( @search_tbls ) {
 
184
         next DATABASE unless exists $schema->{$search_db}->{$search_tbl};
 
185
 
 
186
         if ( $ignore
 
187
              && grep { $_->{db} eq $search_db && $_->{tbl} eq $search_tbl }
 
188
                 @$ignore ) {
 
189
            MKDEBUG && _d('Ignoring', $search_db, $search_tbl, $col);
 
190
            next TABLE;
 
191
         }
 
192
 
 
193
         my $tbl = $schema->{$search_db}->{$search_tbl};
 
194
         if ( $tbl->{tbl_struct}->{is_col}->{$col} ) {
 
195
            MKDEBUG && _d('Column', $col, 'exists in', $tbl->{db}, $tbl->{tbl});
 
196
            push @tbls, $tbl;
 
197
         }
 
198
      }
 
199
   }
 
200
 
 
201
   return \@tbls;
 
202
}
 
203
 
 
204
sub find_table {
 
205
   my ( $self, %args ) = @_;
 
206
   my $ignore = $args{ignore};
 
207
   my $schema = $self->{schema};
 
208
 
 
209
   my ($tbl, $db);
 
210
   if ( my $tbl_name = $args{tbl_name} ) {
 
211
      ($tbl, $db) = reverse map { s/`//g; $_ } split /[.]/, $tbl_name;
 
212
      MKDEBUG && _d('Table', $tbl_name, 'has db', $db, 'tbl', $tbl);
 
213
   }
 
214
   else {
 
215
      ($tbl, $db) = @args{qw(tbl db)};
 
216
   }
 
217
 
 
218
   $db  = lc $db;
 
219
   $tbl = lc $tbl;
 
220
 
 
221
   if ( !$tbl ) {
 
222
      MKDEBUG && _d('No table specified or parsed');
 
223
      return;
 
224
   }
 
225
   MKDEBUG && _d('Finding table', $tbl, 'in', $db);
 
226
 
 
227
   if ( $db && !$schema->{$db} ) {
 
228
      MKDEBUG && _d('Database', $db, 'does not exist');
 
229
      return;
 
230
   }
 
231
 
 
232
   if ( $db && $tbl && !$schema->{$db}->{$tbl} ) {
 
233
      MKDEBUG && _d('Table', $tbl, 'does not exist in database', $db);
 
234
      return;
 
235
   }
 
236
 
 
237
   my @dbs;
 
238
   my @search_dbs = $db ? ($db) : keys %$schema;
 
239
   DATABASE:
 
240
   foreach my $search_db ( @search_dbs ) {
 
241
      if ( $ignore && grep { $_->{db} eq $search_db } @$ignore ) {
 
242
         MKDEBUG && _d('Ignoring', $search_db);
 
243
         next DATABASE;
 
244
      }
 
245
 
 
246
      if ( exists $schema->{$search_db}->{$tbl} ) {
 
247
         MKDEBUG && _d('Table', $tbl, 'exists in', $search_db);
 
248
         push @dbs, $search_db;
 
249
      }
 
250
   }
 
251
 
 
252
   return \@dbs;
 
253
}
 
254
 
 
255
sub _d {
 
256
   my ($package, undef, $line) = caller 0;
 
257
   @_ = map { (my $temp = $_) =~ s/\n/\n# /g; $temp; }
 
258
        map { defined $_ ? $_ : 'undef' }
 
259
        @_;
 
260
   print STDERR "# $package:$line $PID ", join(' ', @_), "\n";
 
261
}
 
262
 
 
263
1;
 
264
}
 
265
# ###########################################################################
 
266
# End Schema package
 
267
# ###########################################################################