1 | /***************************************
2 | $Revision: 1.13 $
3 |
4 | rollback(), commit(), delete() - rollback, commit update transaction, delete an object
5 |
6 | Status: NOT REVUED, NOT TESTED
7 |
8 | Author(s): Andrei Robachevsky
9 |
10 | ******************/ /******************
11 | Modification History:
12 | andrei (17/01/2000) Created.
13 | ******************/ /******************
14 | Copyright (c) 2000 RIPE NCC
15 |
16 | All Rights Reserved
17 |
18 | Permission to use, copy, modify, and distribute this software and its
19 | documentation for any purpose and without fee is hereby granted,
20 | provided that the above copyright notice appear in all copies and that
21 | both that copyright notice and this permission notice appear in
22 | supporting documentation, and that the name of the author not be
23 | used in advertising or publicity pertaining to distribution of the
24 | software without specific, written prior permission.
25 |
26 | THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
27 | ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS; IN NO EVENT SHALL
28 | AUTHOR BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY
29 | DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
30 | AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
31 | OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
32 | ***************************************/
33 | #include "ud.h"
34 | #include "ud_int.h"
35 | #include "ud_comrol.h"
36 | #include "rp.h"
37 |
38 | #define RIPE_REG 17
39 |
40 | /************************************************************
41 | * int rollback(Transaction_t *tr)
42 | * Rollback the transaction
43 | *
44 | * **********************************************************/
45 | int rollback(Transaction_t *tr) {
46 | GString *query;
47 | long sequence_id;
48 | int i, j;
49 | int sql_err;
50 |
51 | if(ACT_DELETE(tr->action)) return(0);
52 |
53 | if ((query = g_string_sized_new(STR_XXL)) == NULL){
54 | fprintf(stderr, "E: cannot allocate gstring\n");
55 | tr->succeeded=0;
56 | tr->error |= ERROR_U_MEM;
57 | return(ERROR_U_MEM); }
58 |
59 | /* Lock all relevant tables */
60 | g_string_sprintf(query, "LOCK TABLES %s WRITE,", DF_get_class_sql_table(tr->class_type));
61 |
62 | for (i=0; tables[tr->class_type][i] != NULL; i++)
63 | g_string_sprintfa(query, " %s WRITE,", tables[tr->class_type][i]);
64 |
65 | for (i=TAB_START; tables[tr->class_type][i] != NULL; i++)
66 | g_string_sprintfa(query, " %s WRITE,", tables[tr->class_type][i]);
67 |
68 | g_string_sprintfa(query, " last WRITE, history WRITE ");
69 |
70 | sql_err=SQ_execute_query(tr->sql_connection, query->str, NULL);
71 |
72 | /*fprintf(stderr,"%s\n", query->str);*/
73 |
74 |
75 | /* Process AUX and LEAF tables */
76 | for (i=TAB_START; tables[tr->class_type][i] != NULL; i++) {
77 | /* Delete what has been inserted */
78 | g_string_sprintf(query, "DELETE FROM %s WHERE object_id=%ld AND thread_id=%d", tables[tr->class_type][i], tr->object_id, tr->thread_ins);
79 | sql_err=SQ_execute_query(tr->sql_connection, query->str, NULL);
80 |
81 | /* Normalize what has been updated/touched */
82 | g_string_sprintf(query, "UPDATE %s SET thread_id=0 WHERE object_id=%ld AND thread_id=%d", tables[tr->class_type][i], tr->object_id, tr->thread_upd);
83 | sql_err = SQ_execute_query(tr->sql_connection, query->str, (SQ_result_set_t **)NULL);
84 | }
85 |
86 | /* Process MAIN tables */
87 | g_string_sprintf(query, "DELETE FROM %s WHERE object_id=%ld AND thread_id=%d",
88 | DF_get_class_sql_table(tr->class_type), tr->object_id, tr->thread_ins);
89 | sql_err = SQ_execute_query(tr->sql_connection, query->str, (SQ_result_set_t **)NULL);
90 |
91 | /* This is needed only for objects with dummies, as they are updated with TR_UPDATE */
92 | /* We use this tag when commiting the update to set dummy==0 */
93 | /* XXX may be later this should be reconsidered */
94 | g_string_sprintf(query, "UPDATE %s SET thread_id=0 WHERE object_id=%ld AND thread_id=%d",
95 | DF_get_class_sql_table(tr->class_type), tr->object_id, tr->thread_upd);
96 | sql_err = SQ_execute_query(tr->sql_connection, query->str, (SQ_result_set_t **)NULL);
97 |
98 | /* Now tables that might be affected by dummies */
99 | for(j=0; j < tr->ndummy; j++)
100 | for (i=0; tables[tr->class_type][i] != NULL; i++) {
101 | g_string_sprintf(query, "DELETE FROM %s WHERE object_id=%ld ", tables[tr->class_type][i], tr->dummy_id[j]);
102 | sql_err = SQ_execute_query(tr->sql_connection, query->str, (SQ_result_set_t **)NULL);
103 | }
104 |
105 | /* Rollback last and history tables */
106 | if(ACT_UPDATE(tr->action)) { /* so we are updating an object */
107 | g_string_sprintf(query, "DELETE FROM history WHERE object_id=%ld AND sequence_id=%ld", tr->object_id, tr->sequence_id-1);
108 | sql_err = SQ_execute_query(tr->sql_connection, query->str, (SQ_result_set_t **)NULL);
109 | /* we do not need to delete a row in the last for updates */
110 | }
111 | else { /* we failed to create an object */
112 | sequence_id=1; /* sequence start == 1 */
113 | g_string_sprintf(query, "DELETE FROM last WHERE object_id=%ld AND sequence_id=%ld", tr->object_id, sequence_id);
114 | sql_err = SQ_execute_query(tr->sql_connection, query->str, (SQ_result_set_t **)NULL);
115 | }
116 |
117 |
118 | for(j=0; j < tr->ndummy; j++){/* if dummies have been created */
119 | g_string_sprintf(query, "DELETE FROM last WHERE object_id=%ld ", tr->dummy_id[j]);
120 | sql_err = SQ_execute_query(tr->sql_connection, query->str, (SQ_result_set_t **)NULL);
121 | }
122 |
123 | /* Unlock all tables */
124 | g_string_sprintf(query, "UNLOCK TABLES ");
125 | sql_err = SQ_execute_query(tr->sql_connection, query->str, (SQ_result_set_t **)NULL);
126 |
127 |
128 | g_string_free(query, TRUE);
129 | return(0);
130 | } /* rollback() */
131 |
132 | /************************************************************
133 | * int commit(Transaction_t *tr)
134 | * Commit the transaction
135 | *
136 | * **********************************************************/
137 | int commit(Transaction_t *tr) {
138 | GString *query;
139 | int err=0;
140 | int i,j;
141 | A_Type_t attr_type;
142 | int sql_err;
143 |
144 | if(ACT_DELETE(tr->action)) return(0);
145 |
146 | if ((query = g_string_sized_new(STR_XXL)) == NULL){
147 | fprintf(stderr, "E: cannot allocate gstring\n");
148 | tr->succeeded=0;
149 | tr->error|=ERROR_U_MEM;
150 | return(ERROR_U_MEM);
151 | }
152 |
153 | /* Lock all relevant tables */
154 | g_string_sprintf(query, "LOCK TABLES %s WRITE,", DF_get_class_sql_table(tr->class_type));
155 |
156 | for (i=0; tables[tr->class_type][i] != NULL; i++)
157 | g_string_sprintfa(query, " %s WRITE,", tables[tr->class_type][i]);
158 |
159 | for (i=TAB_START; tables[tr->class_type][i] != NULL; i++)
160 | g_string_sprintfa(query, " %s WRITE,", tables[tr->class_type][i]);
161 |
162 | g_string_sprintfa(query, " last WRITE, history WRITE ");
163 |
164 | sql_err = SQ_execute_query(tr->sql_connection, query->str, (SQ_result_set_t **)NULL);
165 |
166 | /* fprintf(stderr,"%s\n", query->str); */
167 |
168 | /* Commit the transaction for AUX and LEAF tables that may be affected (taken from object template) */
169 | for (i=TAB_START; tables[tr->class_type][i] != NULL; i++) {
170 | /* Delete old records from the tables */
171 | g_string_sprintf(query, "DELETE FROM %s WHERE object_id=%ld AND thread_id=0 ", tables[tr->class_type][i], tr->object_id);
172 | sql_err = SQ_execute_query(tr->sql_connection, query->str, (SQ_result_set_t **)NULL);
173 | /* fprintf(stderr, "D: query (del old): %s\n", query->str); */
174 |
175 | /* Set thread_id to 0 to commit the transaction */
176 | g_string_sprintf(query, "UPDATE %s SET thread_id=0 WHERE object_id=%ld", tables[tr->class_type][i], tr->object_id);
177 | sql_err = SQ_execute_query(tr->sql_connection, query->str, (SQ_result_set_t **)NULL);
178 | /* fprintf(stderr, "D: query (com new): %s\n", query->str); */
179 | }
180 |
181 | /* Commit the transaction for the MAIN tables */
182 |
183 | /* Commit the transaction for person_role, mntner, as_set, route_set tables */
184 | /* They require different handling because of dummies */
185 | /* The rule is: Update: dummy->0, Insert: preserve dummy value */
186 | /* These tables do not require deletions since we cannot have such condition (object_id==0 AND thread_id==0) */
187 | if((tr->class_type==C_PN) || (tr->class_type==C_RO) ||
188 | (tr->class_type==C_AS) || (tr->class_type==C_RS) ||
189 | (tr->class_type==C_MT)){
190 |
191 | /* Process the rows updated/touched */
192 | g_string_sprintf(query, "UPDATE %s SET thread_id=0, dummy=0 WHERE object_id=%ld AND thread_id=%d ", DF_get_class_sql_table(tr->class_type), tr->object_id, tr->thread_upd);
193 | sql_err = SQ_execute_query(tr->sql_connection, query->str, (SQ_result_set_t **)NULL);
194 | }
195 |
196 | switch (tr->class_type) {
197 | case C_IR:
198 | case C_IN:
199 | case C_I6:
200 | case C_FS:
201 | if((tr->save)){ /* Some special processing for tables with the second attribute */
202 | /* Update the second field of the table with query like one below */
203 | /* UPDATE %s SET thread_id=%d, local_as='%s' WHERE object_id=%ld */
204 |
205 | switch(tr->class_type) {
206 | /* Local-as for inet-rtr */
207 | case C_IR: attr_type=A_LA;
208 | break;
209 | /* netname for inetnum and inet6num */
210 | case C_IN:
211 | case C_I6: attr_type=A_NA;
212 | break;
213 | /* filter for filter-set */
214 | case C_FS: attr_type=A_FI;
215 | break;
216 | default:
217 | die;
218 | break;
219 | }
220 | g_string_sprintf(query, DF_get_update_query(attr_type), DF_get_class_sql_table(tr->class_type), 0, (char *)tr->save, tr->object_id);
221 | sql_err = SQ_execute_query(tr->sql_connection, query->str, (SQ_result_set_t **)NULL);
222 | }
223 | else die;
224 | break;
225 |
226 | default:
227 | /* Process all other MAIN tables for updates/inserts and person_role, mntner, as_set, route_set tables for rows inserts */
228 | g_string_sprintf(query, "UPDATE %s SET thread_id=0 WHERE object_id=%ld AND thread_id>0", DF_get_class_sql_table(tr->class_type), tr->object_id);
229 | sql_err = SQ_execute_query(tr->sql_connection, query->str, (SQ_result_set_t **)NULL);
230 | break;
231 | }
232 |
233 |
234 | /* for tables that might be affected by dummies */
235 | for(j=0; j < tr->ndummy; j++)/* if dummies have been created */
236 | for (i=0; tables[tr->class_type][i] != NULL; i++) {
237 | g_string_sprintf(query, "UPDATE %s SET thread_id=0 WHERE object_id=%ld ", tables[tr->class_type][i], tr->dummy_id[j]);
238 | sql_err = SQ_execute_query(tr->sql_connection, query->str, (SQ_result_set_t **)NULL);
239 | }
240 |
241 |
242 | for(j=0; j < tr->ndummy; j++){/* if dummies have been created*/
243 | g_string_sprintf(query, "UPDATE last SET thread_id=0 WHERE object_id=%ld ", tr->dummy_id[j]);
244 | sql_err = SQ_execute_query(tr->sql_connection, query->str, (SQ_result_set_t **)NULL);
245 | }
246 |
247 | /* Unlock all tables */
248 | g_string_sprintf(query, "UNLOCK TABLES ");
249 | sql_err = SQ_execute_query(tr->sql_connection, query->str, (SQ_result_set_t **)NULL);
250 |
251 | /* Update radix tree for route, inetnum and inaddr-arpa domain*/
252 | if(tr->standalone==0) { /* only if server*/
253 |
254 | /* Create a radix node for the object */
255 | if( ( (tr->class_type==C_RT)
256 | || (tr->class_type==C_IN)
257 | || (tr->class_type==C_I6)
258 | || (tr->class_type==C_DN))
259 | && (tr->packptr)) {
260 | rp_upd_pack_t *packptr = tr->packptr;
261 |
262 | packptr->key = tr->object_id;
263 |
264 | if( RP_pack_node(RX_OPER_CRE, packptr, RIPE_REG) == RX_OK ) {
265 | err = 0;
266 | } else {
267 | err = (-1) ;
268 | }
269 | }
270 | /* XXX Check for errors */
271 | }
272 |
273 | g_string_free(query, TRUE);
274 | return(err);
275 | } /* commit() */
276 |
277 |
278 |
279 | /******************************************************
280 | *int delete(Transaction_t *tr)
281 | * Delete the object
282 | *
283 | *****************************************************/
284 | int delete(Transaction_t *tr)
285 | {
286 | GString *query;
287 | int err=0;
288 | int i;
289 | int num;
290 | long ref_id;
291 | long num_rec;
292 | long timestamp;
293 |
294 | char sobject_id[STR_M];
295 | char *sql_str;
296 | int sql_err;
297 |
298 |
299 | /* Try to allocate g_string. Return on error */
300 | if ((query = g_string_sized_new(STR_XXL)) == NULL){
301 | fprintf(stderr, "E: cannot allocate gstring\n");
302 | tr->succeeded=0;
303 | tr->error|=ERROR_U_MEM;
304 | return(ERROR_U_MEM);
305 | }
306 |
307 |
308 | /* Check for referential integrity of deletion */
309 |
310 | sprintf(sobject_id, "%ld", tr->object_id);
311 |
312 | switch(tr->class_type){
313 | case C_PN:
314 | case C_RO:
315 |
316 | if(tr->dummy==1)break; /* This is if the override exists */
317 |
318 | /* Check that this person/role object is not referenced */
319 |
320 | for (i=0; t_ipn[i] != NULL; i++) {
321 | /* Calculate number of references */
322 | sql_str= get_field_str(tr->sql_connection, "COUNT(*)", t_ipn[i], "pe_ro_id", sobject_id, NULL);
323 | if(sql_str) {
324 | num_rec = atol(sql_str); free(sql_str);
325 | ref_id=tr->object_id;
326 | /* Check if it is a self reference (for role objects) */
327 | if(num_rec==1) {
328 | sql_str= get_field_str(tr->sql_connection, "object_id", t_ipn[i], "pe_ro_id", sobject_id, NULL);
329 | if(sql_str) {
330 | ref_id = atol(sql_str); free(sql_str);
331 | } else {
332 | tr->succeeded=0; tr->error |= ERROR_U_DBS; break;
333 | }
334 | }
335 | /* If there are references (and not the only self reference) we cannot delete */
336 | if((num_rec>1) || (ref_id!=tr->object_id)) {
337 | g_string_sprintfa(tr->error_script,"E[%d][%ld]:ref integrity: %s\n" ,ERROR_U_OBJ, num_rec, t_ipn[i]);
338 | tr->succeeded=0; tr->error |= ERROR_U_OBJ;
339 | }
340 | } else {
341 | /* SQL error occured */
342 | tr->succeeded=0; tr->error |= ERROR_U_DBS;
343 | g_string_sprintfa(tr->error_script,"E[%d][%s]:%s\n", ERROR_U_DBS, t_ipn[i], SQ_error(tr->sql_connection));
344 | }
345 | }
346 |
347 | /* Check that this person/role object is not referenced by name (legacy stuff) */
348 |
349 | for (i=0; t_ipn[i] != NULL; i++) {
350 | /* Calculate number of references */
351 |
352 | g_string_sprintf(query, "SELECT COUNT(*) FROM %s, person_role "
353 | "WHERE person_role.object_id=%s.pe_ro_id "
354 | "AND person_role.nic_hdl='%s' ", t_ipn[i], t_ipn[i], tr->save);
355 |
356 | sql_str= get_qresult_str(tr->sql_connection, query->str);
357 | if(sql_str) {
358 | num_rec = atol(sql_str); free(sql_str);
359 | /* If there are references (no self reference is possible in this case) we cannot delete */
360 | if(num_rec>0) {
361 | g_string_sprintfa(tr->error_script,"E[%d][%ld]:ref integrity: %s\n" ,ERROR_U_OBJ, num_rec, t_ipn[i]);
362 | tr->succeeded=0; tr->error |= ERROR_U_OBJ;
363 | }
364 | } else {
365 | /* SQL error occured */
366 | tr->succeeded=0; tr->error |= ERROR_U_DBS;
367 | g_string_sprintfa(tr->error_script,"E[%d][%s]:%s\n", ERROR_U_DBS, t_ipn[i], SQ_error(tr->sql_connection));
368 | }
369 | }
370 |
371 | break;
372 |
373 | case C_MT:
374 |
375 | if(tr->dummy==1)break; /* This is if the override exists */
376 |
377 | /* Check that this mntner object is not referenced */
378 |
379 | for (i=0; t_imt[i] != NULL; i++) {
380 | /* Calculate number of references */
381 | sql_str= get_field_str(tr->sql_connection, "COUNT(*)", t_imt[i], "mnt_id", sobject_id, NULL);
382 | if(sql_str) {
383 | num_rec = atol(sql_str); free(sql_str);
384 | ref_id=tr->object_id;
385 | /* Check if it is a self reference */
386 | if(num_rec==1) {
387 | sql_str= get_field_str(tr->sql_connection, "object_id", t_imt[i], "mnt_id", sobject_id, NULL);
388 | if(sql_str) {
389 | ref_id = atol(sql_str); free(sql_str);
390 | } else {
391 | tr->succeeded=0; tr->error |= ERROR_U_DBS; break;
392 | }
393 | }
394 | /* If there are references (and not the only self reference) we cannot delete */
395 | if((num_rec>1) || (ref_id!=tr->object_id)) {
396 | g_string_sprintfa(tr->error_script,"E[%d][%ld]:ref integrity: %s\n" ,ERROR_U_OBJ, num_rec, t_imt[i]);
397 | tr->succeeded=0; tr->error |= ERROR_U_OBJ;
398 | }
399 | } else {
400 | tr->succeeded=0; tr->error |= ERROR_U_DBS;
401 | }
402 | }
403 | break;
404 |
405 | case C_RS:
406 | case C_AS:
407 | /* Check that this set object is not referenced */
408 | /* Calculate number of references */
409 | sql_str= get_field_str(tr->sql_connection, "COUNT(*)", "member_of", "set_id", sobject_id, NULL);
410 | if(sql_str) {
411 | num_rec = atol(sql_str); free(sql_str);
412 | /* XXX though set may contain other sets as memebers, */
413 | /* there is no member-of attribute in these objects. */
414 | /* So no self-reference is possible */
415 | if(num_rec!=0) {
416 | g_string_sprintfa(tr->error_script,"E[%d][%ld]:ref integrity: %s\n" ,ERROR_U_OBJ, num_rec, "member_of");
417 | tr->succeeded=0; tr->error |= ERROR_U_OBJ;
418 | /* XXX Do not refuse the transaction but change the object to dummy */
419 | }
420 | } else {
421 | tr->succeeded=0; tr->error |= ERROR_U_DBS;
422 | }
423 | break;
424 |
425 | default:
426 | break;
427 | }
428 |
429 | /* Check if we have passed referential integrity check */
430 | if(tr->succeeded==0){
431 | return(-1);
432 | }
433 |
434 |
435 | /* Lock all relevant tables */
436 | g_string_sprintf(query, "LOCK TABLES %s WRITE,", DF_get_class_sql_table(tr->class_type));
437 |
438 | for (i=0; tables[tr->class_type][i] != NULL; i++)
439 | g_string_sprintfa(query, " %s WRITE,", tables[tr->class_type][i]);
440 |
441 | for (i=TAB_START; tables[tr->class_type][i] != NULL; i++)
442 | g_string_sprintfa(query, " %s WRITE,", tables[tr->class_type][i]);
443 |
444 | g_string_sprintfa(query, " last WRITE, history WRITE ");
445 |
446 | sql_err = SQ_execute_query(tr->sql_connection, query->str, (SQ_result_set_t **)NULL);
447 |
448 | for (i=TAB_START; tables[tr->class_type][i] != NULL; i++) {
449 | g_string_sprintf(query, "DELETE FROM %s WHERE object_id=%ld ", tables[tr->class_type][i], tr->object_id);
450 | sql_err = SQ_execute_query(tr->sql_connection, query->str, (SQ_result_set_t **)NULL);
451 | /* fprintf(stderr, "D: query (delete): %s\n", query->str);*/
452 | }
453 |
454 | /* Process the MAIN table */
455 | g_string_sprintf(query, "DELETE FROM %s WHERE object_id=%ld ", DF_get_class_sql_table(tr->class_type), tr->object_id);
456 | sql_err = SQ_execute_query(tr->sql_connection, query->str, (SQ_result_set_t **)NULL);
457 |
458 | /* Update the history table */
459 | g_string_sprintf(query, "INSERT history "
460 | "SELECT 0, object_id, sequence_id, timestamp, object_type, object "
461 | "FROM last "
462 | "WHERE object_id=%ld ", tr->object_id);
463 |
464 |
465 | sql_err = SQ_execute_query(tr->sql_connection, query->str, (SQ_result_set_t **)NULL);
466 | if (sql_err) {
467 | fprintf(stderr, "E ERROR!<perform_update>: INSERT history failed:[%d][%s]\n", num, query->str);
468 | tr->succeeded=0;
469 | tr->error |=ERROR_U_DBS;
470 | }
471 |
472 | /* get sequence number */
473 | tr->sequence_id = get_sequence_id(tr);
474 | tr->sequence_id++;
475 |
476 | /* insert new version into the last */
477 | timestamp=time(NULL);
478 |
479 | /* empty the contents, but leave in the table to restrict re-use of object_id */
480 | g_string_sprintf(query, "UPDATE last SET object='', timestamp=%ld WHERE object_id=%ld ", timestamp, tr->object_id);
481 |
482 | sql_err = SQ_execute_query(tr->sql_connection, query->str, (SQ_result_set_t **)NULL);
483 | if (sql_err) {
484 | fprintf(stderr, "E ERROR!<perform_update>: UPDATE last failed: [%d][%s]\n", num, query->str);
485 | tr->succeeded=0;
486 | tr->error |= ERROR_U_DBS;
487 | }
488 |
489 |
490 | /* Do more in the forest
491 | * Update radix tree for route and inetnum
492 | */
493 | if(tr->standalone==0) { /* only if server */
494 | /* Collect some data for radix tree and NH repository update */
495 | g_slist_foreach((tr->object)->attributes, get_rx_data, tr);
496 |
497 | /* Only for these types of objects and only if we have collected data (tr->save != NULL) */
498 | if( ( (tr->class_type==C_RT)
499 | || (tr->class_type==C_IN)
500 | || (tr->class_type==C_I6)
501 | || (tr->class_type==C_DN))
502 | && (tr->packptr)) {
503 | rp_upd_pack_t *packptr = tr->packptr;
504 |
505 | packptr->key = tr->object_id;
506 | if( RP_pack_node(RX_OPER_DEL, packptr, RIPE_REG) == RX_OK ) {
507 | err = 0;
508 | } else {
509 | err = (-1) ;
510 | }
511 | }
512 | }
513 |
514 | /* Unlock all tables */
515 | g_string_sprintf(query, "UNLOCK TABLES ");
516 | sql_err = SQ_execute_query(tr->sql_connection, query->str, (SQ_result_set_t **)NULL);
517 |
518 | g_string_free(query, TRUE);
519 |
520 | return(err);
521 |
522 | } /* delete() */