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