Doc tweaks
[dbsrgits/SQL-Translator.git] / lib / SQL / Translator / Parser / XML / SQLFairy.pm
CommitLineData
0a1ec87a 1package SQL::Translator::Parser::XML::SQLFairy;
2
3# -------------------------------------------------------------------
87c5565e 4# $Id: SQLFairy.pm,v 1.9 2004-08-19 14:08:59 grommit Exp $
0a1ec87a 5# -------------------------------------------------------------------
6# Copyright (C) 2003 Mark Addison <mark.addison@itn.co.uk>,
7#
8# This program is free software; you can redistribute it and/or
9# modify it under the terms of the GNU General Public License as
10# published by the Free Software Foundation; version 2.
11#
12# This program is distributed in the hope that it will be useful, but
13# WITHOUT ANY WARRANTY; without even the implied warranty of
14# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15# General Public License for more details.
16#
17# You should have received a copy of the GNU General Public License
18# along with this program; if not, write to the Free Software
19# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
20# 02111-1307 USA
21# -------------------------------------------------------------------
22
23=head1 NAME
24
8571d198 25SQL::Translator::Parser::XML::SQLFairy - parser for SQL::Translator's XML.
0a1ec87a 26
27=head1 SYNOPSIS
28
29 use SQL::Translator;
30 use SQL::Translator::Parser::XML::SQLFairy;
31
32 my $translator = SQL::Translator->new(
33 from => 'XML-SQLFairy',
34 to => 'MySQL',
35 filename => 'schema.xml',
36 show_warnings => 1,
37 add_drop_table => 1,
38 );
39
40 print $obj->translate;
41
42=head1 DESCRIPTION
43
44This parser handles the flavor of XML used natively by the SQLFairy
45project (SQL::Translator). The XML must be in the namespace
46"http://sqlfairy.sourceforge.net/sqlfairy.xml."
8571d198 47See L<SQL::Translator::Producer::XML::SQLFairy> for details of this format.
0a1ec87a 48
8571d198 49You do not need to specify every attribute of the Schema objects as any missing
50from the XML will be set to their default values. e.g. A field could be written
51using only;
0a1ec87a 52
8571d198 53 <sqlf:field name="email" data_type="varchar" size="255" />
0a1ec87a 54
8571d198 55Instead of the full;
0a1ec87a 56
8571d198 57 <sqlf:field name="email" data_type="varchar" size="255" is_nullable="1"
58 is_auto_increment="0" is_primary_key="0" is_foreign_key="0" order="4">
59 <sqlf:comments></sqlf:comments>
60 </sqlf:field>
0a1ec87a 61
8571d198 62If you do not explicitly set the order of items using order attributes on the
63tags then the order the tags appear in the XML will be used.
0a1ec87a 64
65=head2 default_value
66
a5e624ac 67Leave the tag out all together to use the default in Schema::Field.
8571d198 68Use empty tags or 'EMPTY_STRING' for a zero lenth string. 'NULL' for an
a5e624ac 69explicit null (currently sets default_value to undef in the
70Schema::Field obj).
0a1ec87a 71
a5e624ac 72 <sqlf:default_value></sqlf:default_value> <!-- Empty string -->
73 <sqlf:default_value>EMPTY_STRING</sqlf:default_value> <!-- Empty string -->
8571d198 74 <sqlf:default_value/> <!-- Empty string -->
a5e624ac 75 <sqlf:default_value>NULL</sqlf:default_value> <!-- NULL -->
0a1ec87a 76
0a1ec87a 77=head2 ARGS
78
79Doesn't take any extra parser args at the moment.
80
4a268a6c 81=head1 LEGACY FORMAT
82
83The previous version of the SQLFairy XML allowed the attributes of the the
84schema objects to be written as either xml attributes or as data elements, in
85any combination. While this allows for lots of flexibility in writing the XML
86the result is a great many possible XML formats, not so good for DTD writing,
87XPathing etc! So we have moved to a fixed version described in
88L<SQL::Translator::Producer::XML::SQLFairy>.
89
90This version of the parser will still parse the old formats and emmit warnings
91when it sees them being used.
92The old format is B<heavily depreciated> and B<will not> be supported in future
93versions.
94
95To convert your old format files simply pass them through the translator;
96
97 sqlt -f XML-SQLFairy -t XML-SQLFairy schema-old.xml > schema-new.xml
98
0a1ec87a 99=cut
100
101# -------------------------------------------------------------------
102
103use strict;
104
105use vars qw[ $DEBUG $VERSION @EXPORT_OK ];
87c5565e 106$VERSION = sprintf "%d.%02d", q$Revision: 1.9 $ =~ /(\d+)\.(\d+)/;
0a1ec87a 107$DEBUG = 0 unless defined $DEBUG;
108
109use Data::Dumper;
110use Exporter;
111use base qw(Exporter);
112@EXPORT_OK = qw(parse);
113
114use base qw/SQL::Translator::Parser/; # Doesnt do anything at the mo!
a5e624ac 115use SQL::Translator::Utils 'debug';
0a1ec87a 116use XML::XPath;
117use XML::XPath::XMLParser;
118
0a1ec87a 119sub parse {
120 my ( $translator, $data ) = @_;
a5e624ac 121 my $schema = $translator->schema;
122 local $DEBUG = $translator->debug;
123 my $xp = XML::XPath->new(xml => $data);
0a1ec87a 124
0a1ec87a 125 $xp->set_namespace("sqlf", "http://sqlfairy.sourceforge.net/sqlfairy.xml");
126
a5e624ac 127 #
0a1ec87a 128 # Work our way through the tables
129 #
87c5565e 130 my @nodes = $xp->findnodes(
131 '/sqlf:schema/sqlf:table|/sqlf:schema/sqlf:tables/sqlf:table'
132 );
0a1ec87a 133 for my $tblnode (
8571d198 134 sort {
59b2ec83 135 "".$xp->findvalue('sqlf:order|@order',$a)
8571d198 136 <=>
137 "".$xp->findvalue('sqlf:order|@order',$b)
a5e624ac 138 } @nodes
0a1ec87a 139 ) {
140 debug "Adding table:".$xp->findvalue('sqlf:name',$tblnode);
a5e624ac 141
0a1ec87a 142 my $table = $schema->add_table(
143 get_tagfields($xp, $tblnode, "sqlf:" => qw/name order/)
144 ) or die $schema->error;
145
a5e624ac 146 #
0a1ec87a 147 # Fields
148 #
149 my @nodes = $xp->findnodes('sqlf:fields/sqlf:field',$tblnode);
150 foreach (
8571d198 151 sort {
a5e624ac 152 ("".$xp->findvalue('sqlf:order',$a) || 0)
8571d198 153 <=>
154 ("".$xp->findvalue('sqlf:order',$b) || 0)
a5e624ac 155 } @nodes
0a1ec87a 156 ) {
157 my %fdata = get_tagfields($xp, $_, "sqlf:",
94ed484b 158 qw/name data_type size default_value is_nullable extra
a5e624ac 159 is_auto_increment is_primary_key is_foreign_key comments/
160 );
161
162 if (
8571d198 163 exists $fdata{'default_value'} and
a5e624ac 164 defined $fdata{'default_value'}
165 ) {
166 if ( $fdata{'default_value'} =~ /^\s*NULL\s*$/ ) {
167 $fdata{'default_value'}= undef;
0a1ec87a 168 }
a5e624ac 169 elsif ( $fdata{'default_value'} =~ /^\s*EMPTY_STRING\s*$/ ) {
170 $fdata{'default_value'} = "";
0a1ec87a 171 }
172 }
a5e624ac 173
19922fbc 174 my $field = $table->add_field( %fdata ) or die $table->error;
a5e624ac 175
176 $table->primary_key( $field->name ) if $fdata{'is_primary_key'};
177
178 #
179 # TODO:
8571d198 180 # - We should be able to make the table obj spot this when
a5e624ac 181 # we use add_field.
a5e624ac 182 #
0a1ec87a 183 }
184
a5e624ac 185 #
0a1ec87a 186 # Constraints
187 #
188 @nodes = $xp->findnodes('sqlf:constraints/sqlf:constraint',$tblnode);
189 foreach (@nodes) {
190 my %data = get_tagfields($xp, $_, "sqlf:",
a5e624ac 191 qw/name type table fields reference_fields reference_table
192 match_type on_delete_do on_update_do/
193 );
19922fbc 194 $table->add_constraint( %data ) or die $table->error;
0a1ec87a 195 }
196
a5e624ac 197 #
0a1ec87a 198 # Indexes
199 #
200 @nodes = $xp->findnodes('sqlf:indices/sqlf:index',$tblnode);
201 foreach (@nodes) {
202 my %data = get_tagfields($xp, $_, "sqlf:",
a5e624ac 203 qw/name type fields options/);
19922fbc 204 $table->add_index( %data ) or die $table->error;
0a1ec87a 205 }
206
207 } # tables loop
208
19922fbc 209 #
210 # Views
211 #
87c5565e 212 @nodes = $xp->findnodes(
213 '/sqlf:schema/sqlf:view|/sqlf:schema/sqlf:views/sqlf:view'
214 );
19922fbc 215 foreach (@nodes) {
216 my %data = get_tagfields($xp, $_, "sqlf:",
217 qw/name sql fields order/
218 );
219 $schema->add_view( %data ) or die $schema->error;
220 }
8571d198 221
19922fbc 222 #
223 # Triggers
224 #
87c5565e 225 @nodes = $xp->findnodes(
226 '/sqlf:schema/sqlf:trigger|/sqlf:schema/sqlf:triggers/sqlf:trigger'
227 );
19922fbc 228 foreach (@nodes) {
229 my %data = get_tagfields($xp, $_, "sqlf:",
230 qw/name perform_action_when database_event fields on_table action order/
231 );
232 $schema->add_trigger( %data ) or die $schema->error;
233 }
8571d198 234
19922fbc 235 #
236 # Procedures
237 #
87c5565e 238 @nodes = $xp->findnodes(
239 '/sqlf:schema/sqlf:procedure|/sqlf:schema/sqlf:procedures/sqlf:procedure'
240 );
19922fbc 241 foreach (@nodes) {
242 my %data = get_tagfields($xp, $_, "sqlf:",
243 qw/name sql parameters owner comments order/
244 );
245 $schema->add_procedure( %data ) or die $schema->error;
246 }
8571d198 247
0a1ec87a 248 return 1;
249}
250
a5e624ac 251# -------------------------------------------------------------------
252sub get_tagfields {
253#
8571d198 254# get_tagfields XP, NODE, NAMESPACE => qw/TAGNAMES/;
0a1ec87a 255# get_tagfields $node, "sqlf:" => qw/name type fields reference/;
256#
8571d198 257# Returns hash of data.
258# TODO - Add handling of an explicit NULL value.
a5e624ac 259#
260
0a1ec87a 261 my ($xp, $node, @names) = @_;
262 my (%data, $ns);
263 foreach (@names) {
264 if ( m/:$/ ) { $ns = $_; next; } # Set def namespace
265 my $thisns = (s/(^.*?:)// ? $1 : $ns);
a5e624ac 266
94ed484b 267 my $is_attrib = m/^(sql|comments|action|extra)$/ ? 0 : 1;
a5e624ac 268
8571d198 269 my $attrib_path = "\@$thisns$_";
270 my $tag_path = "$thisns$_";
271 if ( $xp->exists($attrib_path,$node) ) {
272 $data{$_} = "".$xp->findvalue($attrib_path,$node);
273 warn "Use of '$_' as an attribute is depricated."
274 ." Use a child tag instead."
275 ." To convert your file to the new version see the Docs.\n"
276 unless $is_attrib;
277 debug "Got $_=".( defined $data{ $_ } ? $data{ $_ } : 'UNDEF' );
278 }
279 elsif ( $xp->exists($tag_path,$node) ) {
94ed484b 280 if ($_ eq "extra") {
281 my %extra;
282 my $extra_nodes = $xp->find($tag_path,$node);
283 foreach ( $extra_nodes->pop->getAttributes ) {
284 $extra{$_->getName} = $_->getData;
285 }
286 $data{$_} = \%extra;
287 }
288 else {
289 $data{$_} = "".$xp->findvalue($tag_path,$node);
290 }
8571d198 291 warn "Use of '$_' as a child tag is depricated."
292 ." Use an attribute instead."
293 ." To convert your file to the new version see the Docs.\n"
294 if $is_attrib;
a5e624ac 295 debug "Got $_=".( defined $data{ $_ } ? $data{ $_ } : 'UNDEF' );
0a1ec87a 296 }
297 }
a5e624ac 298
0a1ec87a 299 return wantarray ? %data : \%data;
300}
301
3021;
303
304# -------------------------------------------------------------------
305
306=pod
307
308=head1 BUGS
309
8571d198 310Ignores the order attribute for Constraints, Views, Indices,
311Views, Triggers and Procedures, using the tag order instead. (This is the order
312output by the SQLFairy XML producer).
0a1ec87a 313
314=head1 TODO
315
316=over 4
317
8571d198 318=item *
a5e624ac 319
94ed484b 320Support options attribute.
a5e624ac 321
8571d198 322=item *
0a1ec87a 323
8571d198 324Test foreign keys are parsed ok.
0a1ec87a 325
8571d198 326=item *
0a1ec87a 327
a5e624ac 328Control over defaulting of non-existant tags.
0a1ec87a 329
330=back
331
332=head1 AUTHOR
333
334Mark D. Addison E<lt>mark.addison@itn.co.ukE<gt>.
335
336=head1 SEE ALSO
337
338perl(1), SQL::Translator, SQL::Translator::Producer::XML::SQLFairy,
339SQL::Translator::Schema.
340
341=cut