File Coverage

blib/lib/DBIx/Romani/Query/XML/Select.pm
Criterion Covered Total %
statement 1 3 33.3
branch n/a
condition n/a
subroutine 1 1 100.0
pod n/a
total 2 4 50.0


line stmt bran cond sub pod time code
1              
2             package DBIx::Romani::Query::XML::Select;
3              
4 1     1   582 use DBIx::Romani::Query::XML::TTT;
  0            
  0            
5             use DBIx::Romani::Query::XML::SQL;
6             use DBIx::Romani::Query::Select;
7             use DBIx::Romani::Query::SQL::Column;
8             use XML::DOM;
9              
10             use DBIx::Romani::Query::XML::Util qw/
11             $NS_QUERY
12             $NS_QUERY_OPERATOR
13             get_element_text /;
14              
15             use strict;
16              
17             use Data::Dumper;
18              
19             # NOTE: we do our best to ignore tags that aren't in our namespace so
20             # that the possibility to annotate with other tags is available.
21              
22             sub create_result_from_node
23             {
24             my $result_node = shift;
25              
26             my $name = $result_node->getLocalName();
27             my $ns_uri = $result_node->getNamespaceURI();
28              
29             my $alias_name;
30             my $result;
31            
32             if ( $ns_uri eq $NS_QUERY )
33             {
34             if ( $name eq 'column' )
35             {
36             $alias_name = $result_node->getAttribute('as') || undef;
37            
38             my $args = {
39             table => $result_node->getAttribute('table') || undef,
40             name => DBIx::Romani::Query::XML::Util::get_text ( $result_node )
41             };
42             $result = DBIx::Romani::Query::SQL::Column->new( $args );
43             }
44             elsif ( $name eq 'expr' )
45             {
46             # attempt to read all the elements from the node
47             my @elements;
48             my $node = $result_node->getFirstChild();
49             while ( defined $node )
50             {
51             if ( $node->getNodeType() == XML::DOM::ELEMENT_NODE )
52             {
53             push @elements, $node;
54             }
55             $node = $node->getNextSibling();
56             }
57              
58             if ( scalar @elements == 1 )
59             {
60             my $node = $elements[0];
61              
62             $result = DBIx::Romani::Query::XML::SQL::create_value_from_node($node);
63             $alias_name = $result_node->getAttribute('as') || undef;
64             }
65             else
66             {
67             die "Must have exactly one child element in the tag";
68             }
69             }
70             }
71              
72             if ( not defined $result )
73             {
74             die "Unknown result tag: ns:\"$ns_uri\" \"$name\"";
75             }
76              
77             return { value => $result, as => $alias_name };
78             }
79              
80             # TODO: There is a lot of copy-paste ugly DOM blocks used to implement common idioms
81             # in our XML format. Refactor, and move these into functios in Util.pm, so that this
82             # code is more sane, and this can be reused for other types of queries.
83             sub create_select_from_node
84             {
85             my $select_node = shift;
86              
87             # check that we really have a select node.
88             # NOTE: we only really need to do this because it can be used as a root tag
89             # so there is no calling function to check it first...
90             if ( $select_node->getNamespaceURI() ne $NS_QUERY or
91             $select_node->getLocalName() ne 'select' )
92             {
93             die sprintf "Not a valid select node: \"%s:%s\"", $select_node->getNamespaceURI, $select_node->getTagName();
94             }
95              
96             # Let's get this party started ...
97             my $select = DBIx::Romani::Query::Select->new();
98              
99             # do the short form
100             if ( $select_node->getAttributeNode('from') )
101             {
102             $select->add_from( $select_node->getAttribute('from') );
103             }
104              
105             my $section_node = $select_node->getFirstChild();
106             while ( defined $section_node )
107             {
108             if ( $section_node->getNodeType() == XML::DOM::ELEMENT_NODE and
109             $section_node->getNamespaceURI() eq $NS_QUERY )
110             {
111             my $section_name = $section_node->getLocalName();
112              
113             if ( $section_name eq 'from' )
114             {
115             if ( scalar @{$select->get_from()} > 0 )
116             {
117             die "Cannot have multiple sections or use both the long and short form 'from'";
118             }
119              
120             # add all the table froms
121             my $node = $section_node->getFirstChild();
122             while ( defined $node )
123             {
124             if ( $node->getNodeType() == XML::DOM::ELEMENT_NODE and
125             $node->getNamespaceURI() eq $NS_QUERY )
126             {
127             if ( $node->getLocalName() eq 'table' )
128             {
129             $select->add_from( get_element_text($node) );
130             }
131             else
132             {
133             die sprintf "Invalid tag \"%s\" in ", $node->getTagName();
134             }
135             }
136              
137             $node = $node->getNextSibling();
138             }
139             }
140             elsif ( $section_name eq 'result' )
141             {
142             if ( scalar @{$select->get_result()} > 0 )
143             {
144             die "Cannot have multiple sections in a
145             }
146              
147             # add all the result columns
148             my $node = $section_node->getFirstChild();
149             while ( defined $node )
150             {
151             if ( $node->getNodeType() == XML::DOM::ELEMENT_NODE and
152             $node->getNamespaceURI() eq $NS_QUERY )
153             {
154             $select->add_result( create_result_from_node( $node ) );
155             }
156             $node = $node->getNextSibling();
157             }
158             }
159             elsif ( $section_name eq 'where' )
160             {
161             if ( defined $select->get_where() )
162             {
163             die "Cannot have multiple sections in a
164             }
165              
166             my @op_nodes;
167              
168             my $node = $section_node->getFirstChild();
169             while ( defined $node )
170             {
171             if ( $node->getNodeType() == XML::DOM::ELEMENT_NODE and
172             ( $node->getNamespaceURI() eq $NS_QUERY or
173             $node->getNamespaceURI() eq $NS_QUERY_OPERATOR ) )
174             {
175             push @op_nodes, $node;
176             }
177             $node = $node->getNextSibling();
178             }
179              
180             if ( scalar @op_nodes == 1 )
181             {
182             my $where = DBIx::Romani::Query::XML::SQL::create_where_from_node( $op_nodes[0] );
183             $select->set_where( $where );
184             }
185             else
186             {
187             die "The section can only have one child.";
188             }
189             }
190             elsif ( $section_name eq 'join' )
191             {
192             if ( defined $select->get_join() )
193             {
194             die "Cannot have more than one section in a
195             }
196              
197             my @op_nodes;
198              
199             my $node = $section_node->getFirstChild();
200             while ( defined $node )
201             {
202             if ( $node->getNodeType() == XML::DOM::ELEMENT_NODE and
203             ( $node->getNamespaceURI() eq $NS_QUERY or
204             $node->getNamespaceURI() eq $NS_QUERY_OPERATOR ) )
205             {
206             push @op_nodes, $node;
207             }
208             $node = $node->getNextSibling();
209             }
210              
211             if ( scalar @op_nodes == 1 )
212             {
213             # create and set the join object
214             my $args = {
215             type => $section_node->getAttribute( 'type' ),
216             table => $section_node->getAttribute( 'table' ),
217             on => DBIx::Romani::Query::XML::SQL::create_where_from_node( $op_nodes[0] ),
218             };
219             $select->set_join( $args );
220             }
221             else
222             {
223             die "The section must have one and only one child.";
224             }
225             }
226             elsif ( $section_name eq 'group-by' )
227             {
228             if ( scalar @{$select->get_group_by()} > 0 )
229             {
230             die "Cannot have multiple sections in a
231             }
232              
233             my @col_nodes;
234              
235             my $node = $section_node->getFirstChild();
236             while ( defined $node )
237             {
238             if ( $node->getNodeType() == XML::DOM::ELEMENT_NODE and
239             $node->getNamespaceURI() eq $NS_QUERY )
240             {
241             if ( $node->getLocalName() eq 'column' )
242             {
243             push @col_nodes, $node;
244             }
245             else
246             {
247             die sprintf "Invalid tag \"%s\" used in section", $node->getTagName();
248             }
249             }
250              
251             $node = $node->getNextSibling();
252             }
253              
254             if ( $section_node->getAttributeNode('column') and scalar @col_nodes > 0 )
255             {
256             die "Cannot use both the long form and short form tag";
257             }
258              
259             # add the group bys
260             if ( scalar @col_nodes == 0 )
261             {
262             $select->add_group_by( $section_node->getAttribute( 'column' ) );
263             }
264             else
265             {
266             foreach my $node ( @col_nodes )
267             {
268             $select->add_group_by( get_element_text($node) );
269             }
270             }
271             }
272             elsif ( $section_name eq 'order-by' )
273             {
274             if ( scalar @{$select->get_order_by()} > 0 )
275             {
276             die "Cannot have multiple sections in a
277             }
278              
279             my @col_nodes;
280              
281             my $node = $section_node->getFirstChild();
282             while ( defined $node )
283             {
284             if ( $node->getNodeType() == XML::DOM::ELEMENT_NODE and
285             $node->getNamespaceURI() eq $NS_QUERY )
286             {
287             if ( $node->getLocalName() eq 'column' )
288             {
289             push @col_nodes, $node;
290             }
291             else
292             {
293             die sprintf "Invalid tag \"%s\" used in section", $node->getTagName();
294             }
295             }
296              
297             $node = $node->getNextSibling();
298             }
299              
300             if ( $section_node->getAttributeNode('column') and scalar @col_nodes > 0 )
301             {
302             die "Cannot use both the long form and short form tag";
303             }
304              
305             # add the group bys
306             if ( scalar @col_nodes == 0 )
307             {
308             my $args = {
309             column => $section_node->getAttribute( 'column' ),
310             dir => $section_node->getAttribute( 'dir' ) || 'asc'
311             };
312             $select->add_order_by($args);
313             }
314             else
315             {
316             foreach my $node ( @col_nodes )
317             {
318             my $args = {
319             column => get_element_text($node),
320             dir => $node->getAttribute( 'dir' ) || 'asc'
321             };
322             $select->add_order_by($args);
323             }
324             }
325             }
326             else
327             {
328             die "Unknown section \"$section_name\" in
329             }
330             }
331              
332             $section_node = $section_node->getNextSibling();
333             }
334              
335             return $select;
336             }
337              
338             1;
339