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() */