File Coverage

blib/lib/SQL/Translator/Producer/XML/SQLFairy.pm
Criterion Covered Total %
statement 83 84 98.8
branch 19 26 73.0
condition 9 13 69.2
subroutine 9 9 100.0
pod 0 3 0.0
total 120 135 88.8


line stmt bran cond sub pod time code
1             package SQL::Translator::Producer::XML::SQLFairy;
2              
3             =pod
4              
5             =head1 NAME
6              
7             SQL::Translator::Producer::XML::SQLFairy - SQLFairy's default XML format
8              
9             =head1 SYNOPSIS
10              
11             use SQL::Translator;
12              
13             my $t = SQL::Translator->new(
14             from => 'MySQL',
15             to => 'XML-SQLFairy',
16             filename => 'schema.sql',
17             show_warnings => 1,
18             );
19              
20             print $t->translate;
21              
22             =head1 DESCRIPTION
23              
24             Creates XML output of a schema, in the flavor of XML used natively by the
25             SQLFairy project (L). This format is detailed here.
26              
27             The XML lives in the C namespace.
28             With a root element of .
29              
30             Objects in the schema are mapped to tags of the same name as the objects class
31             (all lowercase).
32              
33             The attributes of the objects (e.g. $field->name) are mapped to attributes of
34             the tag, except for sql, comments and action, which get mapped to child data
35             elements.
36              
37             List valued attributes (such as the list of fields in an index)
38             get mapped to comma separated lists of values in the attribute.
39              
40             Child objects, such as a tables fields, get mapped to child tags wrapped in a
41             set of container tags using the plural of their contained classes name.
42              
43             An objects' extra attribute (a hash of arbitrary data) is
44             mapped to a tag called extra, with the hash of data as attributes, sorted into
45             alphabetical order.
46              
47             e.g.
48              
49            
50             xmlns="http://sqlfairy.sourceforge.net/sqlfairy.xml">
51              
52            
53            
54            
55            
56             is_nullable="0" is_auto_increment="1" is_primary_key="1"
57             is_foreign_key="0" order="3">
58            
59            
60            
61            
62             is_nullable="1" is_auto_increment="0" is_primary_key="0"
63             is_foreign_key="0" order="1">
64            
65            
66            
67             ...
68            
69            
70            
71            
72            
73            
74              
75            
76            
77             SELECT email FROM Basic WHERE email IS NOT NULL
78            
79            
80              
81            
82              
83             To see a complete example of the XML translate one of your schema :)
84              
85             $ sqlt -f MySQL -t XML-SQLFairy schema.sql
86              
87             =head1 ARGS
88              
89             =over 4
90              
91             =item add_prefix
92              
93             Set to true to use the default namespace prefix of 'sqlf', instead of using
94             the default namespace for
95             C
96              
97             e.g.
98              
99            
100            
101              
102            
103            
104              
105             =item prefix
106              
107             Set to the namespace prefix you want to use for the
108             C
109              
110             e.g.
111              
112            
113            
114              
115             =item newlines
116              
117             If true (the default) inserts newlines around the XML, otherwise the schema is
118             written on one line.
119              
120             =item indent
121              
122             When using newlines the number of whitespace characters to use as the indent.
123             Default is 2, set to 0 to turn off indenting.
124              
125             =back
126              
127             =head1 LEGACY FORMAT
128              
129             The previous version of the SQLFairy XML allowed the attributes of the
130             schema objects to be written as either xml attributes or as data elements, in
131             any combination. The old producer could produce attribute only or data element
132             only versions. While this allowed for lots of flexibility in writing the XML
133             the result is a great many possible XML formats, not so good for DTD writing,
134             XPathing etc! So we have moved to a fixed version described above.
135              
136             This version of the producer will now only produce the new style XML.
137             To convert your old format files simply pass them through the translator :)
138              
139             $ sqlt -f XML-SQLFairy -t XML-SQLFairy schema-old.xml > schema-new.xml
140              
141             =cut
142              
143 4     4   27 use strict;
  4         10  
  4         114  
144 4     4   21 use warnings;
  4         8  
  4         187  
145             our @EXPORT_OK;
146             our $VERSION = '1.63';
147              
148 4     4   25 use Exporter;
  4         9  
  4         187  
149 4     4   34 use base qw(Exporter);
  4         15  
  4         506  
150             @EXPORT_OK = qw(produce);
151              
152 4     4   526 use SQL::Translator::Utils qw(header_comment debug);
  4         9  
  4         322  
153             BEGIN {
154             # Will someone fix XML::Writer already?
155 4     4   34 local $^W = 0;
156 4         1971 require XML::Writer;
157 4         39184 import XML::Writer;
158             }
159              
160             # Which schema object attributes (methods) to write as xml elements rather than
161             # as attributes. e.g. blah, blah...
162             my @MAP_AS_ELEMENTS = qw/sql comments action extra/;
163              
164              
165              
166             my $Namespace = 'http://sqlfairy.sourceforge.net/sqlfairy.xml';
167             my $Name = 'sqlf';
168             my $PArgs = {};
169             my $no_comments;
170              
171             sub produce {
172 6     6 0 13 my $translator = shift;
173 6         123 my $schema = $translator->schema;
174 6         162 $no_comments = $translator->no_comments;
175 6         155 $PArgs = $translator->producer_args;
176 6 50       174 my $newlines = defined $PArgs->{newlines} ? $PArgs->{newlines} : 1;
177 6 50       17 my $indent = defined $PArgs->{indent} ? $PArgs->{indent} : 2;
178              
179             # Setup the XML::Writer and set the namespace
180 6         11 my $io;
181 6         13 my $prefix = "";
182 6 50       17 $prefix = $Name if $PArgs->{add_prefix};
183 6 50       15 $prefix = $PArgs->{prefix} if $PArgs->{prefix};
184 6         62 my $xml = XML::Writer->new(
185             OUTPUT => \$io,
186             NAMESPACES => 1,
187             PREFIX_MAP => { $Namespace => $prefix },
188             DATA_MODE => $newlines,
189             DATA_INDENT => $indent,
190             );
191              
192             # Start the document
193 6         2112 $xml->xmlDecl('UTF-8');
194              
195 6 50       356 $xml->comment(header_comment('', ''))
196             unless $no_comments;
197              
198 6         382 xml_obj($xml, $schema,
199             tag => "schema", methods => [qw/name database extra/], end_tag => 0 );
200              
201             #
202             # Table
203             #
204 6         30 $xml->startTag( [ $Namespace => "tables" ] );
205 6         791 for my $table ( $schema->get_tables ) {
206 13         736 debug "Table:",$table->name;
207 13         56 xml_obj($xml, $table,
208             tag => "table",
209             methods => [qw/name order extra/],
210             end_tag => 0
211             );
212              
213             #
214             # Fields
215             #
216 13         92 xml_obj_children( $xml, $table,
217             tag => 'field',
218             methods =>[qw/
219             name data_type size is_nullable default_value is_auto_increment
220             is_primary_key is_foreign_key extra comments order
221             /],
222             );
223              
224             #
225             # Indices
226             #
227 13         750 xml_obj_children( $xml, $table,
228             tag => 'index',
229             collection_tag => "indices",
230             methods => [qw/name type fields options extra/],
231             );
232              
233             #
234             # Constraints
235             #
236 13         671 xml_obj_children( $xml, $table,
237             tag => 'constraint',
238             methods => [qw/
239             name type fields reference_table reference_fields
240             on_delete on_update match_type expression options deferrable
241             extra
242             /],
243             );
244              
245             #
246             # Comments
247             #
248 13         690 xml_obj_children( $xml, $table,
249             tag => 'comment',
250             collection_tag => "comments",
251             methods => [qw/
252             comments
253             /],
254             );
255              
256 13         623 $xml->endTag( [ $Namespace => 'table' ] );
257             }
258 6         245 $xml->endTag( [ $Namespace => 'tables' ] );
259              
260             #
261             # Views
262             #
263 6         319 xml_obj_children( $xml, $schema,
264             tag => 'view',
265             methods => [qw/name sql fields order extra/],
266             );
267              
268             #
269             # Tiggers
270             #
271 6         320 xml_obj_children( $xml, $schema,
272             tag => 'trigger',
273             methods => [qw/name database_events action on_table perform_action_when
274             fields order extra scope/],
275             );
276              
277             #
278             # Procedures
279             #
280 6         311 xml_obj_children( $xml, $schema,
281             tag => 'procedure',
282             methods => [qw/name sql parameters owner comments order extra/],
283             );
284              
285 6         286 $xml->endTag([ $Namespace => 'schema' ]);
286 6         319 $xml->end;
287              
288 6         1024 return $io;
289             }
290              
291              
292             #
293             # Takes and XML::Write object, Schema::* parent object, the tag name,
294             # the collection name and a list of methods (of the children) to write as XML.
295             # The collection name defaults to the name with an s on the end and is used to
296             # work out the method to get the children with. eg a name of 'foo' gives a
297             # collection of foos and gets the members using ->get_foos.
298             #
299             sub xml_obj_children {
300 70     70 0 147 my ($xml,$parent) = (shift,shift);
301 70         224 my %args = @_;
302             my ($name,$collection_name,$methods)
303 70         212 = @args{qw/tag collection_tag methods/};
304 70   66     317 $collection_name ||= "${name}s";
305              
306 70         109 my $meth;
307 70 100       183 if ( $collection_name eq 'comments' ) {
308 13         33 $meth = 'comments';
309             } else {
310 57         106 $meth = "get_$collection_name";
311             }
312              
313 70         623 my @kids = $parent->$meth;
314             #@kids || return;
315 70         518 $xml->startTag( [ $Namespace => $collection_name ] );
316              
317 70         9474 for my $obj ( @kids ) {
318 102 50       3990 if ( $collection_name eq 'comments' ){
319 0         0 $xml->dataElement( [ $Namespace => 'comment' ], $obj );
320             } else {
321 102         309 xml_obj($xml, $obj,
322             tag => "$name",
323             end_tag => 1,
324             methods => $methods,
325             );
326             }
327             }
328 70         1955 $xml->endTag( [ $Namespace => $collection_name ] );
329             }
330              
331             #
332             # Takes an XML::Writer, Schema::* object and list of method names
333             # and writes the object out as XML. All methods values are written as attributes
334             # except for the methods listed in @MAP_AS_ELEMENTS which get written as child
335             # data elements.
336             #
337             # The attributes/tags are written in the same order as the method names are
338             # passed.
339             #
340             # TODO
341             # - Should the Namespace be passed in instead of global? Pass in the same
342             # as Writer ie [ NS => TAGNAME ]
343             #
344             my $elements_re = join("|", @MAP_AS_ELEMENTS);
345             $elements_re = qr/^($elements_re)$/;
346             sub xml_obj {
347 121     121 0 443 my ($xml, $obj, %args) = @_;
348 121   50     353 my $tag = $args{'tag'} || '';
349 121   100     311 my $end_tag = $args{'end_tag'} || '';
350 121         205 my @meths = @{ $args{'methods'} };
  121         359  
351 121         227 my $empty_tag = 0;
352              
353             # Use array to ensure consistent (ie not hash) ordering of attribs
354             # The order comes from the meths list passed in.
355 121         210 my @tags;
356             my @attr;
357 121         245 foreach ( grep { defined $obj->$_ } @meths ) {
  1113         29278  
358 1090 100       4416 my $what = m/$elements_re/ ? \@tags : \@attr;
359 1090 100       17866 my $val = $_ eq 'extra'
360             ? { $obj->$_ }
361             : $obj->$_;
362 1090 100       8402 $val = ref $val eq 'ARRAY' ? join(',', @$val) : $val;
363 1090         2739 push @$what, $_ => $val;
364             };
365 121         234 my $child_tags = @tags;
366 121 50 66     882 $end_tag && !$child_tags
367             ? $xml->emptyTag( [ $Namespace => $tag ], @attr )
368             : $xml->startTag( [ $Namespace => $tag ], @attr );
369 121         55086 while ( my ($name,$val) = splice @tags,0,2 ) {
370 201 100       11673 if ( ref $val eq 'HASH' ) {
371             $xml->emptyTag( [ $Namespace => $name ],
372 121         640 map { ($_, $val->{$_}) } sort keys %$val );
  4         20  
373             }
374             else {
375 80         319 $xml->dataElement( [ $Namespace => $name ], $val );
376             }
377             }
378 121 100 66     22038 $xml->endTag( [ $Namespace => $tag ] ) if $child_tags && $end_tag;
379             }
380              
381             1;
382              
383             # -------------------------------------------------------------------
384             # The eyes of fire, the nostrils of air,
385             # The mouth of water, the beard of earth.
386             # William Blake
387             # -------------------------------------------------------------------
388              
389             =pod
390              
391             =head1 AUTHORS
392              
393             Ken Youens-Clark Ekclark@cpan.orgE,
394             Darren Chamberlain Edarren@cpan.orgE,
395             Mark Addison Emark.addison@itn.co.ukE.
396              
397             =head1 SEE ALSO
398              
399             C, L, L,
400             L, L.
401              
402             =cut