Line data Source code
1 : //
2 : // dbutils.c
3 : // cloudsync
4 : //
5 : // Created by Marco Bambini on 23/09/24.
6 : //
7 :
8 : #include <stdlib.h>
9 : #include <inttypes.h>
10 :
11 : #include "sql.h"
12 : #include "utils.h"
13 : #include "dbutils.h"
14 : #include "cloudsync.h"
15 :
16 : #if CLOUDSYNC_UNITTEST
17 : char *OUT_OF_MEMORY_BUFFER = "OUT_OF_MEMORY_BUFFER";
18 : #ifndef SQLITE_MAX_ALLOCATION_SIZE
19 : #define SQLITE_MAX_ALLOCATION_SIZE 2147483391
20 : #endif
21 : #endif
22 :
23 : // MARK: - Others -
24 :
25 : // compares two SQLite values and returns an integer indicating the comparison result
26 31469 : int dbutils_value_compare (dbvalue_t *lvalue, dbvalue_t *rvalue) {
27 31469 : if (lvalue == rvalue) return 0;
28 31469 : if (!lvalue) return -1;
29 31469 : if (!rvalue) return 1;
30 :
31 31467 : int l_type = (lvalue) ? database_value_type(lvalue) : DBTYPE_NULL;
32 31467 : int r_type = database_value_type(rvalue);
33 :
34 : // early exit if types differ, null is less than all types
35 31467 : if (l_type != r_type) return (r_type - l_type);
36 :
37 : // at this point lvalue and rvalue are of the same type
38 31460 : switch (l_type) {
39 : case DBTYPE_INTEGER: {
40 10562 : int64_t l_int = database_value_int(lvalue);
41 10562 : int64_t r_int = database_value_int(rvalue);
42 10562 : return (l_int < r_int) ? -1 : (l_int > r_int);
43 : } break;
44 :
45 : case DBTYPE_FLOAT: {
46 4 : double l_double = database_value_double(lvalue);
47 4 : double r_double = database_value_double(rvalue);
48 4 : return (l_double < r_double) ? -1 : (l_double > r_double);
49 : } break;
50 :
51 : case DBTYPE_NULL:
52 634 : break;
53 :
54 : case DBTYPE_TEXT: {
55 20220 : const char *l_text = database_value_text(lvalue);
56 20220 : const char *r_text = database_value_text(rvalue);
57 20220 : if (l_text == NULL && r_text == NULL) return 0;
58 20220 : if (l_text == NULL && r_text != NULL) return -1;
59 20220 : if (l_text != NULL && r_text == NULL) return 1;
60 20220 : return strcmp((const char *)l_text, (const char *)r_text);
61 : } break;
62 :
63 : case DBTYPE_BLOB: {
64 40 : const void *l_blob = database_value_blob(lvalue);
65 40 : const void *r_blob = database_value_blob(rvalue);
66 40 : if (l_blob == NULL && r_blob == NULL) return 0;
67 40 : if (l_blob == NULL && r_blob != NULL) return -1;
68 40 : if (l_blob != NULL && r_blob == NULL) return 1;
69 40 : int l_size = database_value_bytes(lvalue);
70 40 : int r_size = database_value_bytes(rvalue);
71 40 : int cmp = memcmp(l_blob, r_blob, (l_size < r_size) ? l_size : r_size);
72 40 : return (cmp != 0) ? cmp : (l_size - r_size);
73 : } break;
74 : }
75 :
76 634 : return 0;
77 31469 : }
78 :
79 28 : void dbutils_debug_value (dbvalue_t *value) {
80 28 : switch (database_value_type(value)) {
81 : case DBTYPE_INTEGER:
82 7 : printf("\t\tINTEGER: %" PRId64 "\n", database_value_int(value));
83 7 : break;
84 : case DBTYPE_FLOAT:
85 7 : printf("\t\tFLOAT: %f\n", database_value_double(value));
86 7 : break;
87 : case DBTYPE_TEXT:
88 6 : printf("\t\tTEXT: %s (%d)\n", database_value_text(value), database_value_bytes(value));
89 6 : break;
90 : case DBTYPE_BLOB:
91 6 : printf("\t\tBLOB: %p (%d)\n", (char *)database_value_blob(value), database_value_bytes(value));
92 6 : break;
93 : case DBTYPE_NULL:
94 2 : printf("\t\tNULL\n");
95 2 : break;
96 : }
97 28 : }
98 :
99 14 : void dbutils_debug_values (dbvalue_t **argv, int argc) {
100 42 : for (int i = 0; i < argc; i++) {
101 28 : dbutils_debug_value(argv[i]);
102 28 : }
103 14 : }
104 :
105 : // MARK: - Settings -
106 :
107 352 : int dbutils_binary_comparison (int x, int y) {
108 352 : return (x == y) ? 0 : (x > y ? 1 : -1);
109 : }
110 :
111 2594 : int dbutils_settings_get_value (cloudsync_context *data, const char *key, char *buffer, size_t *blen, int64_t *intvalue) {
112 : DEBUG_SETTINGS("dbutils_settings_get_value key: %s", key);
113 :
114 : // if intvalue requested: buffer/blen optional
115 2594 : size_t buffer_len = 0;
116 2594 : if (intvalue) {
117 2257 : *intvalue = 0;
118 2257 : } else {
119 337 : if (!buffer || !blen || *blen == 0) return DBRES_MISUSE;
120 337 : buffer[0] = 0;
121 337 : buffer_len = *blen;
122 337 : *blen = 0;
123 : }
124 :
125 2594 : dbvm_t *vm = NULL;
126 2594 : int rc = databasevm_prepare(data, SQL_SETTINGS_GET_VALUE, (void **)&vm, 0);
127 2594 : if (rc != DBRES_OK) goto finalize_get_value;
128 :
129 2594 : rc = databasevm_bind_text(vm, 1, key, -1);
130 2594 : if (rc != DBRES_OK) goto finalize_get_value;
131 :
132 2594 : rc = databasevm_step(vm);
133 2594 : if (rc == DBRES_DONE) rc = DBRES_OK;
134 1503 : else if (rc != DBRES_ROW) goto finalize_get_value;
135 :
136 : // SQLITE_ROW case
137 2931 : if (rc == DBRES_ROW) {
138 1503 : rc = DBRES_OK;
139 :
140 : // NULL case
141 1503 : if (database_column_type(vm, 0) == DBTYPE_NULL) {
142 0 : goto finalize_get_value;
143 : }
144 :
145 : // INT case
146 1503 : if (intvalue) {
147 1166 : int type = database_column_type(vm, 0);
148 1166 : if (type == DBTYPE_TEXT) {
149 1166 : const char *value = database_column_text(vm, 0);
150 1166 : *intvalue = value ? strtoll(value, NULL, 10) : 0;
151 1166 : } else {
152 0 : *intvalue = database_column_int(vm, 0);
153 : }
154 1166 : goto finalize_get_value;
155 : }
156 :
157 : // buffer case
158 337 : const char *value = database_column_text(vm, 0);
159 337 : size_t size = (size_t)database_column_bytes(vm, 0);
160 337 : if (!value || size == 0) goto finalize_get_value;
161 337 : if (size + 1 > buffer_len) {
162 0 : rc = DBRES_NOMEM;
163 0 : } else {
164 337 : memcpy(buffer, value, size);
165 337 : buffer[size] = '\0';
166 337 : *blen = size;
167 : }
168 337 : }
169 :
170 : finalize_get_value:
171 2594 : if (rc != DBRES_OK) {
172 0 : DEBUG_ALWAYS("dbutils_settings_get_value error %s", database_errmsg(data));
173 0 : }
174 :
175 2594 : if (vm) databasevm_finalize(vm);
176 2594 : return rc;
177 2594 : }
178 :
179 851 : int dbutils_settings_set_key_value (cloudsync_context *data, const char *key, const char *value) {
180 851 : if (!key) return DBRES_MISUSE;
181 : DEBUG_SETTINGS("dbutils_settings_set_key_value key: %s value: %s", key, value);
182 :
183 851 : int rc = DBRES_OK;
184 851 : if (value) {
185 851 : const char *values[] = {key, value};
186 851 : DBTYPE types[] = {DBTYPE_TEXT, DBTYPE_TEXT};
187 851 : int lens[] = {-1, -1};
188 851 : rc = database_write(data, SQL_SETTINGS_SET_KEY_VALUE_REPLACE, values, types, lens, 2);
189 851 : } else {
190 0 : const char *values[] = {key};
191 0 : DBTYPE types[] = {DBTYPE_TEXT};
192 0 : int lens[] = {-1};
193 0 : rc = database_write(data, SQL_SETTINGS_SET_KEY_VALUE_DELETE, values, types, lens, 1);
194 : }
195 :
196 851 : if (rc == DBRES_OK && data) cloudsync_sync_key(data, key, value);
197 851 : return rc;
198 851 : }
199 :
200 0 : int dbutils_settings_get_int_value (cloudsync_context *data, const char *key) {
201 : DEBUG_SETTINGS("dbutils_settings_get_int_value key: %s", key);
202 0 : int64_t value = 0;
203 0 : if (dbutils_settings_get_value(data, key, NULL, NULL, &value) != DBRES_OK) return -1;
204 :
205 0 : return (int)value;
206 0 : }
207 :
208 2257 : int64_t dbutils_settings_get_int64_value (cloudsync_context *data, const char *key) {
209 : DEBUG_SETTINGS("dbutils_settings_get_int_value key: %s", key);
210 2257 : int64_t value = 0;
211 2257 : if (dbutils_settings_get_value(data, key, NULL, NULL, &value) != DBRES_OK) return -1;
212 :
213 2257 : return value;
214 2257 : }
215 :
216 337 : int dbutils_settings_check_version (cloudsync_context *data, const char *version) {
217 : DEBUG_SETTINGS("dbutils_settings_check_version");
218 : char buffer[256];
219 337 : size_t len = sizeof(buffer);
220 337 : if (dbutils_settings_get_value(data, CLOUDSYNC_KEY_LIBVERSION, buffer, &len, NULL) != DBRES_OK) return -666;
221 :
222 : int major1, minor1, patch1;
223 : int major2, minor2, patch2;
224 337 : int count1 = sscanf(buffer, "%d.%d.%d", &major1, &minor1, &patch1);
225 337 : int count2 = sscanf((version == NULL ? CLOUDSYNC_VERSION : version), "%d.%d.%d", &major2, &minor2, &patch2);
226 :
227 337 : if (count1 != 3 || count2 != 3) return -666;
228 :
229 337 : int res = 0;
230 337 : if ((res = dbutils_binary_comparison(major1, major2)) == 0) {
231 5 : if ((res = dbutils_binary_comparison(minor1, minor2)) == 0) {
232 5 : return dbutils_binary_comparison(patch1, patch2);
233 : }
234 0 : }
235 :
236 : DEBUG_SETTINGS(" %s %s (%d)", buffer, CLOUDSYNC_VERSION, res);
237 332 : return res;
238 337 : }
239 :
240 984 : int dbutils_table_settings_get_value (cloudsync_context *data, const char *table, const char *column_name, const char *key, char *buffer, size_t blen) {
241 : DEBUG_SETTINGS("dbutils_table_settings_get_value table: %s column: %s key: %s", table, column_name, key);
242 :
243 984 : if (!buffer || blen == 0) return DBRES_MISUSE;
244 984 : buffer[0] = 0;
245 :
246 984 : dbvm_t *vm = NULL;
247 984 : int rc = databasevm_prepare(data, SQL_TABLE_SETTINGS_GET_VALUE, (void **)&vm, 0);
248 984 : if (rc != DBRES_OK) goto finalize_get_value;
249 :
250 984 : rc = databasevm_bind_text(vm, 1, table, -1);
251 984 : if (rc != DBRES_OK) goto finalize_get_value;
252 :
253 984 : rc = databasevm_bind_text(vm, 2, (column_name) ? column_name : "*", -1);
254 984 : if (rc != DBRES_OK) goto finalize_get_value;
255 :
256 984 : rc = databasevm_bind_text(vm, 3, key, -1);
257 984 : if (rc != DBRES_OK) goto finalize_get_value;
258 :
259 984 : rc = databasevm_step(vm);
260 984 : if (rc == DBRES_DONE) rc = DBRES_OK;
261 96 : else if (rc != DBRES_ROW) goto finalize_get_value;
262 :
263 : // SQLITE_ROW case
264 1080 : if (rc == DBRES_ROW) {
265 96 : rc = DBRES_OK;
266 :
267 : // NULL case
268 96 : if (database_column_type(vm, 0) == DBTYPE_NULL) {
269 0 : goto finalize_get_value;
270 : }
271 :
272 96 : const char *value = database_column_text(vm, 0);
273 96 : size_t size = (size_t)database_column_bytes(vm, 0);
274 96 : if (size + 1 > blen) {
275 0 : rc = DBRES_NOMEM;
276 0 : } else {
277 96 : memcpy(buffer, value, size);
278 96 : buffer[size] = '\0';
279 : }
280 96 : }
281 :
282 : finalize_get_value:
283 984 : if (rc != DBRES_OK) {
284 0 : DEBUG_ALWAYS("cloudsync_table_settings error %s", database_errmsg(data));
285 0 : }
286 984 : if (vm) databasevm_finalize(vm);
287 984 : return rc;
288 984 : }
289 :
290 382 : int dbutils_table_settings_set_key_value (cloudsync_context *data, const char *table_name, const char *column_name, const char *key, const char *value) {
291 : DEBUG_SETTINGS("dbutils_table_settings_set_key_value table: %s column: %s key: %s", table_name, column_name, key);
292 :
293 382 : int rc = DBRES_OK;
294 :
295 : // sanity check tbl_name
296 382 : if (table_name == NULL) {
297 0 : return cloudsync_set_error(data, "cloudsync_set_table/set_column requires a non-null table parameter", DBRES_ERROR);
298 : }
299 :
300 : // sanity check column name
301 382 : if (column_name == NULL) column_name = "*";
302 :
303 : // remove all table_name entries
304 382 : if (key == NULL) {
305 4 : const char *values[] = {table_name};
306 4 : DBTYPE types[] = {DBTYPE_TEXT};
307 4 : int lens[] = {-1};
308 4 : rc = database_write(data, SQL_TABLE_SETTINGS_DELETE_ALL_FOR_TABLE, values, types, lens, 1);
309 4 : return rc;
310 : }
311 :
312 378 : if (key && value) {
313 374 : const char *values[] = {table_name, column_name, key, value};
314 374 : DBTYPE types[] = {DBTYPE_TEXT, DBTYPE_TEXT, DBTYPE_TEXT, DBTYPE_TEXT};
315 374 : int lens[] = {-1, -1, -1, -1};
316 374 : rc = database_write(data, SQL_TABLE_SETTINGS_REPLACE, values, types, lens, 4);
317 374 : }
318 :
319 378 : if (value == NULL) {
320 4 : const char *values[] = {table_name, column_name, key};
321 4 : DBTYPE types[] = {DBTYPE_TEXT, DBTYPE_TEXT, DBTYPE_TEXT};
322 4 : int lens[] = {-1, -1, -1};
323 4 : rc = database_write(data, SQL_TABLE_SETTINGS_DELETE_ONE, values, types, lens, 3);
324 4 : }
325 :
326 : // unused in this version
327 : // cloudsync_context *data = (context) ? (cloudsync_context *)sqlite3_user_data(context) : NULL;
328 : // if (rc == DBRES_OK && data) cloudsync_sync_table_key(data, table, column, key, value);
329 378 : return rc;
330 382 : }
331 :
332 951 : int64_t dbutils_table_settings_count_tables (cloudsync_context *data) {
333 : DEBUG_SETTINGS("dbutils_table_settings_count_tables");
334 :
335 951 : int64_t count = 0;
336 951 : int rc = database_select_int(data, SQL_TABLE_SETTINGS_COUNT_TABLES, &count);
337 951 : return (rc == DBRES_OK) ? count : 0;
338 : }
339 :
340 349 : table_algo dbutils_table_settings_get_algo (cloudsync_context *data, const char *table_name) {
341 : DEBUG_SETTINGS("dbutils_table_settings_get_algo %s", table_name);
342 :
343 : char buffer[512];
344 349 : int rc = dbutils_table_settings_get_value(data, table_name, "*", "algo", buffer, sizeof(buffer));
345 349 : return (rc == DBRES_OK) ? cloudsync_algo_from_name(buffer) : table_algo_none;
346 : }
347 :
348 500 : int dbutils_settings_load_callback (void *xdata, int ncols, char **values, char **names) {
349 500 : cloudsync_context *data = (cloudsync_context *)xdata;
350 :
351 1000 : for (int i=0; i+1<ncols; i+=2) {
352 500 : const char *key = values[i];
353 500 : const char *value = values[i+1];
354 500 : cloudsync_sync_key(data, key, value);
355 : DEBUG_SETTINGS("key: %s value: %s", key, value);
356 500 : }
357 :
358 500 : return 0;
359 : }
360 :
361 10 : int dbutils_settings_table_load_callback (void *xdata, int ncols, char **values, char **names) {
362 10 : cloudsync_context *data = (cloudsync_context *)xdata;
363 :
364 20 : for (int i=0; i+3<ncols; i+=4) {
365 10 : const char *table_name = values[i];
366 10 : const char *col_name = values[i+1];
367 10 : const char *key = values[i+2];
368 10 : const char *value = values[i+3];
369 :
370 : // Table-level algo setting (col_name == "*")
371 10 : if (strcmp(key, "algo") == 0 && col_name && strcmp(col_name, "*") == 0) {
372 : // Skip stale settings for tables that no longer exist
373 8 : if (!database_table_exists(data, table_name, cloudsync_schema(data))) {
374 : DEBUG_SETTINGS("skipping stale settings for dropped table: %s", table_name);
375 2 : continue;
376 : }
377 6 : table_algo algo = cloudsync_algo_from_name(value);
378 : char fbuf[2048];
379 6 : int frc = dbutils_table_settings_get_value(data, table_name, "*", "filter", fbuf, sizeof(fbuf));
380 6 : const char *filt = (frc == DBRES_OK && fbuf[0]) ? fbuf : NULL;
381 6 : if (database_create_triggers(data, table_name, algo, filt) != DBRES_OK) return DBRES_MISUSE;
382 6 : if (table_add_to_context(data, algo, table_name) == false) return DBRES_MISUSE;
383 : DEBUG_SETTINGS("load tbl_name: %s value: %s", key, value);
384 6 : continue;
385 : }
386 :
387 : // Column-level algo=block setting (col_name != "*")
388 3 : if (strcmp(key, "algo") == 0 && value && strcmp(value, "block") == 0 &&
389 1 : col_name && strcmp(col_name, "*") != 0) {
390 : // Read optional delimiter
391 : char dbuf[256];
392 1 : int drc = dbutils_table_settings_get_value(data, table_name, col_name, "delimiter", dbuf, sizeof(dbuf));
393 1 : const char *delim = (drc == DBRES_OK && dbuf[0]) ? dbuf : NULL;
394 1 : cloudsync_setup_block_column(data, table_name, col_name, delim, false);
395 : DEBUG_SETTINGS("load block column: %s.%s delimiter: %s", table_name, col_name, delim ? delim : "(default)");
396 1 : continue;
397 : }
398 1 : }
399 :
400 10 : return 0;
401 10 : }
402 :
403 0 : bool dbutils_settings_migrate (cloudsync_context *data) {
404 : // dbutils_settings_check_version comparison failed
405 : // so check for logic migration here (if necessary)
406 0 : return true;
407 : }
408 :
409 249 : int dbutils_settings_load (cloudsync_context *data) {
410 : DEBUG_SETTINGS("dbutils_settings_load %p", data);
411 :
412 : // load global settings
413 249 : const char *sql = SQL_SETTINGS_LOAD_GLOBAL;
414 249 : int rc = database_exec_callback(data, sql, dbutils_settings_load_callback, data);
415 249 : if (rc != DBRES_OK) DEBUG_ALWAYS("cloudsync_load_settings error: %s", database_errmsg(data));
416 :
417 : // load table-specific settings
418 249 : sql = SQL_SETTINGS_LOAD_TABLE;
419 249 : rc = database_exec_callback(data, sql, dbutils_settings_table_load_callback, data);
420 249 : if (rc != DBRES_OK) DEBUG_ALWAYS("cloudsync_load_settings error: %s", database_errmsg(data));
421 :
422 249 : return DBRES_OK;
423 : }
424 :
425 249 : int dbutils_settings_init (cloudsync_context *data) {
426 : DEBUG_SETTINGS("dbutils_settings_init %p", data);
427 :
428 : // check if cloudsync_settings table exists
429 249 : int rc = DBRES_OK;
430 249 : bool settings_exists = database_internal_table_exists(data, CLOUDSYNC_SETTINGS_NAME);
431 249 : if (settings_exists == false) {
432 : DEBUG_SETTINGS("cloudsync_settings does not exist (creating a new one)");
433 :
434 : // create table and fill-in initial data
435 241 : rc = database_exec(data, SQL_CREATE_SETTINGS_TABLE);
436 241 : if (rc != DBRES_OK) return rc;
437 :
438 : // library version
439 241 : char *sql = cloudsync_memory_mprintf(SQL_INSERT_SETTINGS_STR_FORMAT, CLOUDSYNC_KEY_LIBVERSION, CLOUDSYNC_VERSION);
440 241 : if (!sql) return DBRES_NOMEM;
441 241 : rc = database_exec(data, sql);
442 241 : cloudsync_memory_free(sql);
443 241 : if (rc != DBRES_OK) return rc;
444 :
445 : // schema version
446 : char sql_int[1024];
447 241 : snprintf(sql_int, sizeof(sql_int), SQL_INSERT_SETTINGS_INT_FORMAT, CLOUDSYNC_KEY_SCHEMAVERSION, (long long)database_schema_version(data));
448 241 : rc = database_exec(data, sql_int);
449 241 : if (rc != DBRES_OK) return rc;
450 241 : }
451 :
452 249 : if (database_internal_table_exists(data, CLOUDSYNC_SITEID_NAME) == false) {
453 : DEBUG_SETTINGS("cloudsync_site_id does not exist (creating a new one)");
454 :
455 : // create table and fill-in initial data
456 : // site_id is implicitly indexed
457 : // the rowid column is the primary key
458 241 : rc = database_exec(data, SQL_CREATE_SITE_ID_TABLE);
459 241 : if (rc != DBRES_OK) return rc;
460 :
461 : // siteid (to uniquely identify this local copy of the database)
462 : uint8_t site_id[UUID_LEN];
463 241 : if (cloudsync_uuid_v7(site_id) == -1) return DBRES_ERROR;
464 :
465 : // rowid 0 means local site_id
466 241 : const char *values[] = {"0", (const char *)&site_id};
467 241 : DBTYPE types[] = {DBTYPE_INTEGER, DBTYPE_BLOB};
468 241 : int lens[] = {-1, UUID_LEN};
469 241 : rc = database_write(data, SQL_INSERT_SITE_ID_ROWID, values, types, lens, 2);
470 241 : if (rc != DBRES_OK) return rc;
471 241 : }
472 :
473 : // check if cloudsync_table_settings table exists
474 249 : if (database_internal_table_exists(data, CLOUDSYNC_TABLE_SETTINGS_NAME) == false) {
475 : DEBUG_SETTINGS("cloudsync_table_settings does not exist (creating a new one)");
476 :
477 241 : rc = database_exec(data, SQL_CREATE_TABLE_SETTINGS_TABLE);
478 241 : if (rc != DBRES_OK) return rc;
479 241 : }
480 :
481 : // check if cloudsync_settings table exists
482 249 : bool schema_versions_exists = database_internal_table_exists(data, CLOUDSYNC_SCHEMA_VERSIONS_NAME);
483 249 : if (schema_versions_exists == false) {
484 : DEBUG_SETTINGS("cloudsync_schema_versions does not exist (creating a new one)");
485 :
486 : // create table
487 241 : rc = database_exec(data, SQL_CREATE_SCHEMA_VERSIONS_TABLE);
488 241 : if (rc != DBRES_OK) return rc;
489 241 : }
490 :
491 : // check if cloudsync_payload_fragments table exists
492 : // created at init time because the apply path runs under sync-only
493 : // credentials that lack DDL rights on server nodes
494 249 : if (database_internal_table_exists(data, CLOUDSYNC_PAYLOAD_FRAGMENTS_NAME) == false) {
495 : DEBUG_SETTINGS("cloudsync_payload_fragments does not exist (creating a new one)");
496 :
497 241 : rc = database_exec(data, SQL_PAYLOAD_FRAGMENTS_CREATE_TABLE);
498 241 : if (rc != DBRES_OK) return rc;
499 241 : }
500 :
501 : // cloudsync_settings table exists so load it
502 249 : dbutils_settings_load(data);
503 :
504 : // check if some process changed schema outside of the lib
505 : /*
506 : if ((settings_exists == true) && (data->schema_version != database_schema_version(data))) {
507 : // SOMEONE CHANGED SCHEMAs SO WE NEED TO RECHECK AUGMENTED TABLES and RELATED TRIGGERS
508 : assert(0);
509 : }
510 : */
511 :
512 249 : return DBRES_OK;
513 249 : }
514 :
515 2 : int dbutils_settings_cleanup (cloudsync_context *data) {
516 2 : return database_exec(data, SQL_SETTINGS_CLEANUP_DROP_ALL);
517 : }
|