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