1 /*
2  * Copyright (C) 2008 - 2010 Murray Cumming <murrayc@murrayc.com>
3  * Copyright (C) 2008 - 2011 Vivien Malerba <malerba@gnome-db.org>
4  * Copyright (C) 2009 Bas Driessen <bas.driessen@xobas.com>
5  * Copyright (C) 2010 David King <davidk@openismus.com>
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this library; if not, write to the
19  * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
20  * Boston, MA  02110-1301, USA.
21  */
22 
23 #include <libgda/gda-debug-macros.h>
24 #include <libgda/sql-parser/gda-statement-struct.h>
25 #include <libgda/sql-parser/gda-statement-struct-compound.h>
26 #include <libgda/sql-parser/gda-statement-struct-pspec.h>
27 #include <string.h>
28 #include <glib/gi18n-lib.h>
29 
30 static gpointer gda_sql_statement_compound_new (void);
31 static gboolean gda_sql_statement_compound_check_structure (GdaSqlAnyPart *stmt, gpointer data, GError **error);
32 
33 GdaSqlStatementContentsInfo compound_infos = {
34 	GDA_SQL_STATEMENT_COMPOUND,
35 	"COMPOUND",
36 	gda_sql_statement_compound_new,
37 	_gda_sql_statement_compound_free,
38 	_gda_sql_statement_compound_copy,
39 	_gda_sql_statement_compound_serialize,
40 
41 	gda_sql_statement_compound_check_structure,
42 	NULL,
43 	NULL,
44 	NULL,
45 	NULL,
46 	NULL
47 };
48 
49 GdaSqlStatementContentsInfo *
_gda_sql_statement_compound_get_infos(void)50 _gda_sql_statement_compound_get_infos (void)
51 {
52 	return &compound_infos;
53 }
54 
55 static gpointer
gda_sql_statement_compound_new(void)56 gda_sql_statement_compound_new (void)
57 {
58 	GdaSqlStatementCompound *stmt;
59 	stmt = g_new0 (GdaSqlStatementCompound, 1);
60 	stmt->compound_type = -1;
61 	GDA_SQL_ANY_PART (stmt)->type = GDA_SQL_ANY_STMT_COMPOUND;
62 	return stmt;
63 }
64 
65 void
_gda_sql_statement_compound_free(gpointer stmt)66 _gda_sql_statement_compound_free (gpointer stmt)
67 {
68 	GdaSqlStatementCompound *compound = (GdaSqlStatementCompound *) stmt;
69 
70 	if (compound->stmt_list) {
71 		g_slist_foreach (compound->stmt_list, (GFunc) gda_sql_statement_free, NULL);
72 		g_slist_free (compound->stmt_list);
73 	}
74 	g_free (compound);
75 }
76 
77 gpointer
_gda_sql_statement_compound_copy(gpointer src)78 _gda_sql_statement_compound_copy (gpointer src)
79 {
80 	GdaSqlStatementCompound *dest;
81 	GdaSqlStatementCompound *compound = (GdaSqlStatementCompound *) src;
82 	GSList *list;
83 
84 	dest = gda_sql_statement_compound_new ();
85 	dest->compound_type = compound->compound_type;
86 	for (list = compound->stmt_list; list; list = list->next) {
87 		GdaSqlStatement *sqlst;
88 		sqlst = gda_sql_statement_copy ((GdaSqlStatement*) list->data);
89 		gda_sql_any_part_set_parent (sqlst->contents, dest);
90 		dest->stmt_list = g_slist_prepend (dest->stmt_list, sqlst);
91 	}
92 	dest->stmt_list = g_slist_reverse (dest->stmt_list);
93 
94 	return dest;
95 }
96 
97 gchar *
_gda_sql_statement_compound_serialize(gpointer stmt)98 _gda_sql_statement_compound_serialize (gpointer stmt)
99 {
100 	GString *string;
101 	gchar *str;
102 	GSList *list;
103 	GdaSqlStatementCompound *compound = (GdaSqlStatementCompound *) stmt;
104 
105 	g_return_val_if_fail (stmt, NULL);
106 
107 	string = g_string_new ("\"contents\":{");
108 	g_string_append (string, "\"compount_type\":");
109 	switch (compound->compound_type) {
110 	case GDA_SQL_STATEMENT_COMPOUND_UNION:
111 		str = "UNION"; break;
112 	case GDA_SQL_STATEMENT_COMPOUND_UNION_ALL:
113 		str = "AUNION"; break;
114 	case GDA_SQL_STATEMENT_COMPOUND_INTERSECT:
115 		str = "INTERSECT"; break;
116 	case GDA_SQL_STATEMENT_COMPOUND_INTERSECT_ALL:
117 		str = "AINTERSECT"; break;
118 	case GDA_SQL_STATEMENT_COMPOUND_EXCEPT:
119 		str = "EXCEPT"; break;
120 	case GDA_SQL_STATEMENT_COMPOUND_EXCEPT_ALL:
121 		str = "AEXCEPT"; break;
122 	default:
123 		str = NULL;
124 		g_assert_not_reached ();
125 	}
126 	g_string_append_printf (string, "\"%s\"", str);
127 
128 	if (compound->stmt_list) {
129 		g_string_append (string, ",\"select_stmts\":[");
130 		for (list = compound->stmt_list; list; list = list->next) {
131 			if (list != compound->stmt_list)
132 				g_string_append_c (string, ',');
133 			str = gda_sql_statement_serialize ((GdaSqlStatement*) list->data);
134 			g_string_append (string, str);
135 			g_free (str);
136 		}
137 		g_string_append_c (string, ']');
138 	}
139 
140 	g_string_append_c (string, '}');
141 	str = string->str;
142 	g_string_free (string, FALSE);
143 	return str;
144 }
145 
146 /**
147  * gda_sql_statement_compound_take_stmt
148  * @stmt: a #GdaSqlStatement pointer
149  * @s: a #GdaSqlStatement pointer
150  *
151  * Adds the @s sub-statement to the @stmt compound statement. @s's reference is transferred to
152  * @stmt (which means @stmt is then responsible for freeing it when no longer needed).
153  */
154 void
gda_sql_statement_compound_take_stmt(GdaSqlStatement * stmt,GdaSqlStatement * s)155 gda_sql_statement_compound_take_stmt (GdaSqlStatement *stmt, GdaSqlStatement *s)
156 {
157 	GdaSqlStatementCompound *compound = (GdaSqlStatementCompound *) stmt->contents;
158 
159 	if (s->stmt_type == GDA_SQL_STATEMENT_COMPOUND) {
160 		GdaSqlStatementCompound *scompound = (GdaSqlStatementCompound *) s->contents;
161 		if (scompound->stmt_list) {
162 			if (scompound->stmt_list->next) {
163 				compound->stmt_list = g_slist_append (compound->stmt_list, s);
164 				gda_sql_any_part_set_parent (s->contents, stmt);
165 			}
166 			else {
167 				compound->stmt_list = g_slist_append (compound->stmt_list, scompound->stmt_list->data);
168 				gda_sql_any_part_set_parent (((GdaSqlStatement*) scompound->stmt_list->data)->contents, stmt);
169 				g_slist_free (scompound->stmt_list);
170 				scompound->stmt_list = NULL;
171 				gda_sql_statement_free (s);
172 			}
173 		}
174 		else {
175 			/* ignore @s */
176 			gda_sql_statement_free (s);
177 			return;
178 		}
179 	}
180 	else {
181 		compound->stmt_list = g_slist_append (compound->stmt_list, s);
182 		gda_sql_any_part_set_parent (s->contents, stmt);
183 	}
184 }
185 
186 GdaSqlAnyPart *
_gda_sql_statement_compound_reduce(GdaSqlAnyPart * compound_or_select)187 _gda_sql_statement_compound_reduce (GdaSqlAnyPart *compound_or_select)
188 {
189 	GdaSqlAnyPart *part;
190 
191 	part = compound_or_select;
192 	if (part->type == GDA_SQL_ANY_STMT_COMPOUND) {
193 		/* if only one child, then use that child instead */
194 		GdaSqlStatementCompound *compound = (GdaSqlStatementCompound*) part;
195 		if (compound->stmt_list && !compound->stmt_list->next) {
196 			GdaSqlAnyPart *rpart;
197 			GdaSqlStatement *substmt;
198 			substmt = (GdaSqlStatement *) compound->stmt_list->data;
199 
200 			rpart = GDA_SQL_ANY_PART (substmt->contents);
201 			substmt->contents = NULL;
202 			gda_sql_statement_free (substmt);
203 
204 			g_slist_free (compound->stmt_list);
205 			compound->stmt_list = NULL;
206 			_gda_sql_statement_compound_free (compound);
207 			part = _gda_sql_statement_compound_reduce (rpart);
208 		}
209 	}
210 
211 	return part;
212 }
213 
214 
215 /**
216  * gda_sql_statement_compound_set_type
217  * @stmt: a #GdaSqlStatement pointer
218  * @type: a #GdaSqlStatementCompoundType value
219  *
220  * Specifies @stmt's type of compound
221  */
222 void
gda_sql_statement_compound_set_type(GdaSqlStatement * stmt,GdaSqlStatementCompoundType type)223 gda_sql_statement_compound_set_type (GdaSqlStatement *stmt, GdaSqlStatementCompoundType type)
224 {
225 	GdaSqlStatementCompound *compound = (GdaSqlStatementCompound *) stmt->contents;
226 	compound->compound_type = type;
227 }
228 
229 gint
_gda_sql_statement_compound_get_n_cols(GdaSqlStatementCompound * compound,GError ** error)230 _gda_sql_statement_compound_get_n_cols (GdaSqlStatementCompound *compound, GError **error)
231 {
232 	if (!compound || !compound->stmt_list) {
233 		g_set_error (error, GDA_SQL_ERROR, GDA_SQL_STRUCTURE_CONTENTS_ERROR,
234 			      "%s", _("COMPOUND statement contains an undefined COMPOUND statement"));
235 		return -1;
236 	}
237 
238 	/* @compound's children */
239 	GdaSqlStatement *sqlstmt = (GdaSqlStatement*) compound->stmt_list->data;
240 	if (sqlstmt->stmt_type == GDA_SQL_STATEMENT_SELECT) {
241 		if (!sqlstmt->contents) {
242 			g_set_error (error, GDA_SQL_ERROR, GDA_SQL_STRUCTURE_CONTENTS_ERROR,
243 				      "%s", _("COMPOUND statement contains an undefined SELECT statement"));
244 			return -1;
245 		}
246 		return g_slist_length (((GdaSqlStatementSelect*) sqlstmt->contents)->expr_list);
247 	}
248 	else if (sqlstmt->stmt_type == GDA_SQL_STATEMENT_COMPOUND)
249 		return _gda_sql_statement_compound_get_n_cols ((GdaSqlStatementCompound*) sqlstmt->contents, error);
250 	else {
251 		g_set_error (error, GDA_SQL_ERROR, GDA_SQL_STRUCTURE_CONTENTS_ERROR,
252 			      "%s", _("COMPOUND statement contains a non SELECT statement"));
253 		return -1;
254 	}
255 }
256 
257 static gboolean
gda_sql_statement_compound_check_structure(GdaSqlAnyPart * stmt,G_GNUC_UNUSED gpointer data,GError ** error)258 gda_sql_statement_compound_check_structure (GdaSqlAnyPart *stmt, G_GNUC_UNUSED gpointer data, GError **error)
259 {
260 	GdaSqlStatementCompound *compound = (GdaSqlStatementCompound *) stmt;
261 	gint nb_cols = -1;
262 	GSList *list;
263 
264 	if (!compound->stmt_list) {
265 		g_set_error (error, GDA_SQL_ERROR, GDA_SQL_STRUCTURE_CONTENTS_ERROR,
266 			      "%s", _("COMPOUND statement does not contain any SELECT statement"));
267 		return FALSE;
268 	}
269 
270 	if (!compound->stmt_list->next) {
271 		g_set_error (error, GDA_SQL_ERROR, GDA_SQL_STRUCTURE_CONTENTS_ERROR,
272 			      "%s", _("COMPOUND statement only contains one SELECT statement"));
273 		return FALSE;
274 	}
275 
276 	for (list = compound->stmt_list; list; list = list->next) {
277 		GdaSqlStatement *sqlstmt = (GdaSqlStatement*) list->data;
278 		gint nb;
279 		if (sqlstmt->stmt_type == GDA_SQL_STATEMENT_SELECT) {
280 			if (!sqlstmt->contents) {
281 				g_set_error (error, GDA_SQL_ERROR, GDA_SQL_STRUCTURE_CONTENTS_ERROR,
282 					      "%s", _("COMPOUND statement contains an undefined SELECT statement"));
283 				return FALSE;
284 			}
285 			nb = g_slist_length (((GdaSqlStatementSelect*) sqlstmt->contents)->expr_list);
286 		}
287 		else if (sqlstmt->stmt_type == GDA_SQL_STATEMENT_COMPOUND) {
288 			nb = _gda_sql_statement_compound_get_n_cols ((GdaSqlStatementCompound*) sqlstmt->contents, error);
289 			if (nb < 0)
290 				return FALSE;
291 		}
292 		else {
293 			g_set_error (error, GDA_SQL_ERROR, GDA_SQL_STRUCTURE_CONTENTS_ERROR,
294 			      "%s", _("COMPOUND statement contains a non SELECT statement"));
295 			return FALSE;
296 		}
297 
298 		if (nb_cols == -1) {
299 			nb_cols = nb;
300 			if (nb == 0) {
301 				g_set_error (error, GDA_SQL_ERROR, GDA_SQL_STRUCTURE_CONTENTS_ERROR,
302 					      "%s", _("COMPOUND statement contains an empty SELECT statement"));
303 				return FALSE;
304 			}
305 		}
306 		else if (nb != nb_cols) {
307 			g_set_error (error, GDA_SQL_ERROR, GDA_SQL_STRUCTURE_CONTENTS_ERROR,
308 				      "%s", _("All statements in a COMPOUND must have the same number of columns"));
309 			return FALSE;
310 		}
311 	}
312 
313 	return TRUE;
314 }
315