Line data Source code
1 : /*
2 : * SPDX-License-Identifier: MPL-2.0
3 : *
4 : * This Source Code Form is subject to the terms of the Mozilla Public
5 : * License, v. 2.0. If a copy of the MPL was not distributed with this
6 : * file, You can obtain one at http://mozilla.org/MPL/2.0/.
7 : *
8 : * Copyright 2024 MonetDB Foundation;
9 : * Copyright August 2008 - 2023 MonetDB B.V.;
10 : * Copyright 1997 - July 2008 CWI.
11 : */
12 :
13 : #include "monetdb_config.h"
14 : #include "rel_xml.h"
15 : #include "rel_exp.h"
16 : #include "rel_select.h"
17 : #include "sql_semantic.h"
18 : #include "sql_parser.h"
19 :
20 : static sql_exp *
21 14 : rel_xmlelement(sql_query *query, sql_rel **rel, symbol *sym, int f, exp_kind knd)
22 : {
23 14 : mvc *sql = query->sql;
24 14 : dnode *d = sym->data.lval->h;
25 14 : const char *tag = d->data.sval;
26 14 : dlist *ns_attrs_elms = d->next->data.lval;
27 14 : sql_exp *ns_st = NULL, *attr_st = NULL, *res = NULL;
28 14 : sql_type *t = NULL;
29 14 : sql_subtype xml_type;
30 :
31 14 : if ((t = mvc_bind_type(sql, "xml")) == NULL)
32 0 : return sql_error(sql, 02, SQLSTATE(42000) "XML: xml type missing, probably the xml module wasn't added");
33 14 : sql_init_subtype(&xml_type, t, 0, 0);
34 14 : if (ns_attrs_elms) {
35 14 : symbol *ns = ns_attrs_elms->h->data.sym;
36 14 : symbol *attr = ns_attrs_elms->h->next->data.sym;
37 14 : dlist *content = ns_attrs_elms->h->next->next->data.lval;
38 :
39 : /* loop over the content, create recursive element */
40 14 : if (content) {
41 13 : dnode *n;
42 13 : dlist *cl = content->h->data.lval;
43 :
44 29 : for (n=cl->h; n; n = n->next) {
45 16 : symbol *c = n->data.sym;
46 16 : sql_subtype *st;
47 16 : sql_exp *c_st = rel_value_exp(query, rel, c, f, knd);
48 :
49 16 : if (!c_st)
50 : return NULL;
51 :
52 16 : st = exp_subtype(c_st);
53 16 : assert(st);
54 16 : if (type_cmp(st->type, xml_type.type) != 0) {
55 4 : sql_subtype str_type;
56 :
57 4 : sql_find_subtype(&str_type, "varchar", 0, 0);
58 : /* convert to string first */
59 4 : c_st = exp_check_type(sql, &str_type, rel ? *rel : NULL, c_st, type_equal);
60 : /* then to xml */
61 4 : if (!c_st || (c_st = exp_check_type(sql, &xml_type, rel ? *rel : NULL, c_st, type_equal)) == NULL)
62 0 : return NULL;
63 : }
64 :
65 : /* lets glue the xml content together */
66 16 : if (res) {
67 3 : res = rel_binop_(sql, rel ? *rel : NULL, res, c_st, NULL, "concat", card_value, true);
68 : } else {
69 : res = c_st;
70 : }
71 : }
72 : }
73 14 : if (ns) {
74 0 : ns_st = rel_value_exp(query, rel, ns, f, knd);
75 0 : if (!ns_st)
76 : return NULL;
77 : }
78 14 : if (attr) {
79 5 : attr_st = rel_value_exp(query, rel, attr, f, knd);
80 5 : if (!attr_st)
81 : return NULL;
82 : }
83 : }
84 :
85 14 : if (!ns_st)
86 14 : ns_st = exp_atom(sql->sa, atom_general(sql->sa, &xml_type, NULL, 0));
87 14 : if (!attr_st)
88 9 : attr_st = exp_atom(sql->sa, atom_general(sql->sa, &xml_type, NULL, 0));
89 14 : if (!res)
90 1 : res = exp_atom(sql->sa, atom_general(sql->sa, &xml_type, NULL, 0));
91 :
92 14 : if (!ns_st || !attr_st || !res)
93 : return NULL;
94 14 : return rel_nop_(query->sql, rel ? *rel : NULL, exp_atom_clob(sql->sa, tag), ns_st, attr_st, res, NULL, "element",
95 : card_value);
96 : }
97 :
98 : static sql_exp *
99 5 : rel_xmlforest(sql_query *query, sql_rel **rel, symbol *sym, int f, exp_kind knd)
100 : {
101 5 : mvc *sql = query->sql;
102 5 : dnode *d = sym->data.lval->h;
103 5 : symbol *ns = d->data.sym;
104 5 : dlist *elms = d->next->data.lval;
105 5 : sql_exp *ns_st, *attr_st, *res = NULL;
106 5 : sql_type *t = NULL;
107 5 : sql_subtype xml_type;
108 :
109 5 : if ((t = mvc_bind_type(sql, "xml")) == NULL)
110 0 : return sql_error(sql, 02, SQLSTATE(42000) "XML: xml type missing, probably the xml module wasn't added");
111 5 : sql_init_subtype(&xml_type, t, 0, 0);
112 5 : if (ns) {
113 0 : ns_st = rel_value_exp(query, rel, ns, f, knd);
114 : } else {
115 5 : ns_st = exp_atom(sql->sa, atom_general(sql->sa, &xml_type, NULL, 0));
116 : }
117 5 : if (!ns_st)
118 : return NULL;
119 5 : attr_st = exp_atom(sql->sa, atom_general(sql->sa, &xml_type, NULL, 0));
120 5 : if (elms) {
121 5 : dnode *e;
122 :
123 19 : for (e = elms->h; e; e = e->next) {
124 14 : dnode *cc = e->data.lval->h;
125 14 : symbol *c = cc->data.sym;
126 14 : const char *tag = cc->next->data.sval;
127 :
128 14 : sql_exp *c_st = rel_value_exp(query, rel, c, f, knd);
129 14 : sql_subtype *st;
130 14 : if (!c_st)
131 : return NULL;
132 :
133 14 : st = exp_subtype(c_st);
134 14 : assert(st);
135 14 : if (type_cmp(st->type, xml_type.type) != 0) {
136 14 : sql_subtype str_type;
137 :
138 14 : sql_find_subtype(&str_type, "varchar", 0, 0);
139 : /* convert to string first */
140 14 : c_st = exp_check_type(sql, &str_type, rel ? *rel : NULL, c_st, type_equal);
141 : /* then to xml */
142 14 : if (!c_st || (c_st = exp_check_type(sql, &xml_type, rel ? *rel : NULL, c_st, type_equal)) == NULL)
143 0 : return NULL;
144 : }
145 :
146 14 : if (!tag) {
147 6 : tag = exp_name(c_st);
148 6 : if (!tag)
149 0 : tag = "single_value";
150 : }
151 14 : c_st = rel_nop_(sql, rel ? *rel : NULL, exp_atom_clob(sql->sa, tag), ns_st, attr_st, c_st, NULL,
152 : "element", card_value);
153 : /* lets glue the xml content together */
154 14 : if (res) {
155 9 : res = rel_binop_(sql, rel ? *rel : NULL, res, c_st, NULL, "concat", card_value, true);
156 : } else {
157 : res = c_st;
158 : }
159 : }
160 : }
161 : return res;
162 : }
163 :
164 : static sql_exp *
165 1 : rel_xmlcomment(sql_query *query, sql_rel **rel, symbol *sym, int f, exp_kind knd)
166 : {
167 1 : dnode *d = sym->data.lval->h;
168 1 : symbol *comment = d->data.sym;
169 1 : sql_exp *comment_st;
170 :
171 1 : comment_st = rel_value_exp(query, rel, comment, f, knd);
172 1 : if (!comment_st)
173 : return NULL;
174 1 : return rel_unop_(query->sql, rel ? *rel : NULL, comment_st, NULL, "comment", card_value);
175 : }
176 :
177 : static sql_exp *
178 5 : rel_xmlattribute(sql_query *query, sql_rel **rel, symbol *sym, int f, exp_kind knd)
179 : {
180 5 : dnode *d = sym->data.lval->h;
181 5 : const char *attr_name = d->data.sval;
182 5 : symbol *attr = d->next->data.sym;
183 5 : sql_exp *attr_st, *attr_name_st = NULL;
184 :
185 5 : attr_st = rel_value_exp(query, rel, attr, f, knd);
186 5 : if (!attr_st)
187 : return NULL;
188 5 : if (!attr_name) {
189 : /*TODO:convert simple column names into valid attribute names */
190 1 : attr_name = exp_name(attr_st);
191 1 : if (!attr_name)
192 1 : attr_name = "single_value";
193 : }
194 5 : sql_subtype str_type;
195 :
196 5 : sql_find_subtype(&str_type, "varchar", 0, 0);
197 5 : attr_name_st = exp_atom_str(query->sql->sa, attr_name, &str_type);
198 5 : return rel_binop_(query->sql, rel ? *rel : NULL, attr_name_st, attr_st, NULL, "attribute", card_value, false);
199 : }
200 :
201 : static sql_exp *
202 0 : rel_xmlconcat(sql_query *query, sql_rel **rel, symbol *sym, int f, exp_kind knd)
203 : {
204 0 : dnode *d = sym->data.lval->h;
205 0 : dnode *en = d->data.lval->h;
206 0 : sql_exp *concat_st, *res = NULL;
207 :
208 0 : for (; en; en = en->next) {
209 0 : symbol *c = en->data.sym;
210 0 : concat_st = rel_value_exp(query, rel, c, f, knd);
211 0 : if (!concat_st)
212 : return NULL;
213 0 : if (res)
214 0 : res = rel_binop_(query->sql, rel ? *rel : NULL, res, concat_st, NULL, "concat", card_value, false);
215 : else
216 : res = concat_st;
217 : }
218 : return res;
219 : }
220 :
221 : static sql_exp *
222 0 : rel_xmldocument(sql_query *query, sql_rel **rel, symbol *sym, int f, exp_kind knd)
223 : {
224 0 : dnode *d = sym->data.lval->h;
225 0 : symbol *val = d->data.sym;
226 0 : sql_exp *val_st;
227 :
228 0 : val_st = rel_value_exp(query, rel, val, f, knd);
229 0 : if (!val_st)
230 : return NULL;
231 0 : return rel_unop_(query->sql, rel ? *rel : NULL, val_st, NULL, "document", card_value);
232 : }
233 :
234 : static sql_exp *
235 0 : rel_xmlpi(sql_query *query, sql_rel **rel, symbol *sym, int f, exp_kind knd)
236 : {
237 0 : dnode *d = sym->data.lval->h;
238 0 : char *target = d->data.sval;
239 0 : symbol *val = d->next->data.sym;
240 0 : sql_exp *target_st, *val_st;
241 0 : sql_subtype str_type;
242 :
243 0 : sql_find_subtype(&str_type, "varchar", 0, 0);
244 0 : target_st = exp_atom_str(query->sql->sa, target, &str_type);
245 0 : if (!val)
246 0 : val_st = rel_value_exp(query, rel, val, f, knd);
247 : else
248 0 : val_st = exp_atom(query->sql->sa, atom_general(query->sql->sa, &str_type, NULL, 0));
249 0 : if (!val_st)
250 : return NULL;
251 0 : return rel_binop_(query->sql, rel ? *rel : NULL, target_st, val_st, NULL, "pi", card_value, false);
252 : }
253 :
254 : /* cast string to xml */
255 : static sql_exp *
256 0 : rel_xmltext(sql_query *query, sql_rel **rel, symbol *sym, int f, exp_kind knd)
257 : {
258 0 : dnode *d = sym->data.lval->h;
259 0 : symbol *text = d->data.sym;
260 0 : sql_exp *text_st;
261 0 : sql_type *t = NULL;
262 0 : sql_subtype xml_type;
263 :
264 0 : if ((t = mvc_bind_type(query->sql, "xml")) == NULL)
265 0 : return sql_error(query->sql, 02, SQLSTATE(42000) "XML: xml type missing, probably the xml module wasn't added");
266 0 : sql_init_subtype(&xml_type, t, 0, 0);
267 0 : text_st = rel_value_exp(query, rel, text, f, knd);
268 0 : if (!text_st || (text_st = exp_check_type(query->sql, &xml_type, rel ? *rel : NULL, text_st, type_equal)) == NULL)
269 0 : return NULL;
270 : return text_st;
271 : }
272 :
273 : sql_exp *
274 25 : rel_xml(sql_query *query, sql_rel **rel, symbol *s, int f, exp_kind knd)
275 : {
276 25 : mvc *sql = query->sql;
277 25 : sql_exp *ret = NULL;
278 :
279 25 : switch (s->token) {
280 14 : case SQL_XMLELEMENT:
281 14 : ret = rel_xmlelement(query, rel, s, f, knd);
282 14 : break;
283 5 : case SQL_XMLFOREST:
284 5 : ret = rel_xmlforest(query, rel, s, f, knd);
285 5 : break;
286 1 : case SQL_XMLCOMMENT:
287 1 : ret = rel_xmlcomment(query, rel, s, f, knd);
288 1 : break;
289 5 : case SQL_XMLATTRIBUTE:
290 5 : ret = rel_xmlattribute(query, rel, s, f, knd);
291 5 : break;
292 0 : case SQL_XMLCONCAT:
293 0 : ret = rel_xmlconcat(query, rel, s, f, knd);
294 0 : break;
295 0 : case SQL_XMLDOCUMENT:
296 0 : ret = rel_xmldocument(query, rel, s, f, knd);
297 0 : break;
298 0 : case SQL_XMLPI:
299 0 : ret = rel_xmlpi(query, rel, s, f, knd);
300 0 : break;
301 0 : case SQL_XMLTEXT:
302 0 : ret = rel_xmltext(query, rel, s, f, knd);
303 0 : break;
304 0 : default:
305 0 : return sql_error(sql, 01, SQLSTATE(42000) "XML statement unknown symbol(%p)->token = %s", s, token2string(s->token));
306 : }
307 : return ret;
308 : }
|