Adding old SqlfXML producer as XML/SQLFairy.
[dbsrgits/SQL-Translator.git] / lib / SQL / Translator / Parser / XML / SQLFairy.pm
CommitLineData
0a1ec87a 1package SQL::Translator::Parser::XML::SQLFairy;
2
3# -------------------------------------------------------------------
4# $Id: SQLFairy.pm,v 1.1 2003-08-22 18:01:50 kycl4rk Exp $
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
25SQL::Translator::Parser::XML::SQLFairy - parser for SQL::Translator's XML
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."
47
48To see an example of the XML translate one of your schema :) e.g.
49
50 $ sql_translator.pl -f MySQL -t XML-SQLFairy schema.sql
51
52=head2 attrib_values
53
54The parser will happily parse XML produced with the attrib_values arg set. If
55it sees a value set as an attribute and a tag, the tag value will override
56that of the attribute.
57
58e.g. For the xml below the table would get the name 'bar'.
59
60 <sqlf:table name="foo">
61 <sqlf:name>foo</name>
62 </sqlf:table>
63
64=head2 default_value
65
66Leave the tag out all together to use the default in Schema::Field. Use empty
67tags or EMPTY_STRING for a zero lenth string. NULL for an explicit null
68(currently sets default_value to undef in the Schema::Field obj).
69
70 <sqlf:default_value></sqlf:default_value> <!-- Empty string -->
71 <sqlf:default_value>EMPTY_STRING</sqlf:default_value> <!-- Empty string -->
72 <sqlf:default_value>NULL</sqlf:default_value> <!-- NULL -->
73
74 <sqlf:default_value/> <!-- Empty string BUT DON'T USE! See BUGS -->
75
76=head2 ARGS
77
78Doesn't take any extra parser args at the moment.
79
80=cut
81
82# -------------------------------------------------------------------
83
84use strict;
85
86use vars qw[ $DEBUG $VERSION @EXPORT_OK ];
87$VERSION = sprintf "%d.%02d", q$Revision: 1.1 $ =~ /(\d+)\.(\d+)/;
88$DEBUG = 0 unless defined $DEBUG;
89
90use Data::Dumper;
91use Exporter;
92use base qw(Exporter);
93@EXPORT_OK = qw(parse);
94
95use base qw/SQL::Translator::Parser/; # Doesnt do anything at the mo!
96use XML::XPath;
97use XML::XPath::XMLParser;
98
99sub debug {
100 warn @_,"\n" if $DEBUG;
101}
102
103sub parse {
104 my ( $translator, $data ) = @_;
105 my $schema = $translator->schema;
106 local $DEBUG = $translator->debug;
107 #local $TRACE = $translator->trace ? 1 : undef;
108 # Nothing with trace option yet!
109
110 my $xp = XML::XPath->new(xml => $data);
111 $xp->set_namespace("sqlf", "http://sqlfairy.sourceforge.net/sqlfairy.xml");
112
113 # Work our way through the tables
114 #
115 my @nodes = $xp->findnodes('/sqlf:schema/sqlf:table');
116 for my $tblnode (
117 sort { "".$xp->findvalue('sqlf:order',$a)
118 <=> "".$xp->findvalue('sqlf:order',$b) } @nodes
119 ) {
120 debug "Adding table:".$xp->findvalue('sqlf:name',$tblnode);
121 my $table = $schema->add_table(
122 get_tagfields($xp, $tblnode, "sqlf:" => qw/name order/)
123 ) or die $schema->error;
124
125 # Fields
126 #
127 my @nodes = $xp->findnodes('sqlf:fields/sqlf:field',$tblnode);
128 foreach (
129 sort { ("".$xp->findvalue('sqlf:order',$a) || 0)
130 <=> ("".$xp->findvalue('sqlf:order',$b) || 0) } @nodes
131 ) {
132 my %fdata = get_tagfields($xp, $_, "sqlf:",
133 qw/name data_type size default_value is_nullable is_auto_increment
134 is_primary_key is_foreign_key comments/);
135 if (exists $fdata{default_value} and defined $fdata{default_value}){
136 if ( $fdata{default_value} =~ /^\s*NULL\s*$/ ) {
137 $fdata{default_value}= undef;
138 }
139 elsif ( $fdata{default_value} =~ /^\s*EMPTY_STRING\s*$/ ) {
140 $fdata{default_value} = "";
141 }
142 }
143 my $field = $table->add_field(%fdata) or die $schema->error;
144 $table->primary_key($field->name) if $fdata{'is_primary_key'};
145 # TODO We should be able to make the table obj spot this when we
146 # use add_field.
147 # TODO Deal with $field->extra
148 }
149
150 # Constraints
151 #
152 @nodes = $xp->findnodes('sqlf:constraints/sqlf:constraint',$tblnode);
153 foreach (@nodes) {
154 my %data = get_tagfields($xp, $_, "sqlf:",
155 qw/name type table fields reference_fields reference_table
156 match_type on_delete_do on_update_do/);
157 $table->add_constraint(%data) or die $schema->error;
158 }
159
160 # Indexes
161 #
162 @nodes = $xp->findnodes('sqlf:indices/sqlf:index',$tblnode);
163 foreach (@nodes) {
164 my %data = get_tagfields($xp, $_, "sqlf:",
165 qw/name type fields options/);
166 $table->add_index(%data) or die $schema->error;
167 }
168
169 } # tables loop
170
171 return 1;
172}
173
174# get_tagfields XPNODE, NAMESPACE => qw/TAGNAMES/;
175# get_tagfields $node, "sqlf:" => qw/name type fields reference/;
176#
177# Returns hash of data. If a tag isn't in the file it is not in this
178# hash.
179# TODO Add handling of and explicit NULL value.
180sub get_tagfields {
181 my ($xp, $node, @names) = @_;
182 my (%data, $ns);
183 foreach (@names) {
184 if ( m/:$/ ) { $ns = $_; next; } # Set def namespace
185 my $thisns = (s/(^.*?:)// ? $1 : $ns);
186 foreach my $path ( "\@$thisns$_","$thisns$_") {
187 $data{$_} = $xp->findvalue($path,$node) if $xp->exists($path,$node);
188 debug "Got $_=".(defined $data{$_} ? $data{$_} : "UNDEF");
189 }
190 }
191 return wantarray ? %data : \%data;
192}
193
1941;
195
196# -------------------------------------------------------------------
197
198=pod
199
200=head1 BUGS
201
202B<Empty Tags> e.g. <sqlf:default_value/> Will be parsed as "" and
203hence also false. This is a bit counter intuative for some tags as
204seeing <sqlf:is_nullable /> you might think that it was set when it
205fact it wouldn't be. So for now it is safest not to use them until
206their handling by the parser is defined.
207
208=head1 TODO
209
210=over 4
211
212=item * Support sqf:options.
213
214=item * Test forign keys are parsed ok.
215
216=item * Sort out sane handling of empty tags <foo/> vs tags with no content
217 <foo></foo> vs it no tag being there.
218
219=item * Control over defaulting of non-existant tags.
220
221=back
222
223=head1 AUTHOR
224
225Mark D. Addison E<lt>mark.addison@itn.co.ukE<gt>.
226
227=head1 SEE ALSO
228
229perl(1), SQL::Translator, SQL::Translator::Producer::XML::SQLFairy,
230SQL::Translator::Schema.
231
232=cut