Line data Source code
1 : /* File: data_database.c; Copyright and License: see below */
2 :
3 : #include "storage/data_database.h"
4 : #include "entity/data_id.h"
5 : #include "entity/data_table.h"
6 : #include "u8/u8_trace.h"
7 : #include "u8/u8_log.h"
8 : #include <assert.h>
9 :
10 : const char DATA_DATABASE_SQLITE3_MAGIC[16]
11 : = {0x53, 0x51, 0x4c, 0x69, 0x74, 0x65, 0x20, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x20, 0x33, 0x00 };
12 :
13 : /*!
14 : * \brief string constant to create an sql database table
15 : *
16 : * This table contains head values
17 : */
18 : static const char *DATA_DATABASE_CREATE_HEAD_TABLE =
19 : "CREATE TABLE IF NOT EXISTS head ( "
20 : "id INTEGER NOT NULL PRIMARY KEY ASC, "
21 : "key TEXT NOT NULL UNIQUE, "
22 : "value TEXT NOT NULL"
23 : ");";
24 :
25 : /*!
26 : * \brief string constant to create an sql database table
27 : *
28 : * This table contains instances of classes, states, activities, interfaces (which are classifiers)
29 : * and also packages.
30 : * It does not contain relationships (even if they are classifiers) like generalizations, associations.
31 : * \see http://www.omg.org/spec/UML/
32 : */
33 : static const char *DATA_DATABASE_CREATE_CLASSIFIER_TABLE =
34 : "CREATE TABLE IF NOT EXISTS classifiers ( "
35 : "id INTEGER NOT NULL PRIMARY KEY ASC, "
36 : "main_type INTEGER NOT NULL, "
37 : "stereotype TEXT, "
38 : "name TEXT UNIQUE, "
39 : "description TEXT, "
40 : "x_order INTEGER, "
41 : "y_order INTEGER, "
42 : "list_order INTEGER, "
43 : "uuid TEXT NOT NULL DEFAULT \'\'" /* DEFAULT needed in case a new DB is modified by an old program version */
44 : ");";
45 :
46 : /*!
47 : * \brief string constant to update an sql database table
48 : *
49 : * The DEFAULT clause is needed to convert the existing records to the new format
50 : * and in case a new database is modified by an old program version.
51 : *
52 : * This command extends classifiers by a uuid field.
53 : * \see http://sqlite.org/lang_altertable.html
54 : */
55 : static const char *DATA_DATABASE_ALTER_CLASSIFIER_TABLE_UUID =
56 : "ALTER TABLE classifiers "
57 : "ADD COLUMN uuid TEXT NOT NULL DEFAULT \'\';";
58 :
59 : #define DATA_DATABASE_CREATE_UUID \
60 : "lower(hex( randomblob(4)) || '-' || hex( randomblob(2))"\
61 : " || '-' || '4' || substr( hex( randomblob(2)), 2) || '-' || substr('89AB', 1 + (abs(random()) % 4) , 1) ||"\
62 : " substr(hex(randomblob(2)), 2) || '-' || hex(randomblob(6)))"
63 :
64 : /*!
65 : * \brief string constant to update an sql database table
66 : *
67 : * A uuid is generated and updated wherever missing.
68 : * The subselect-WHERE clause is needed to generate for each row a different uuid.
69 : */
70 : static const char *DATA_DATABASE_UPDATE_CLASSIFIER_UUID =
71 : "UPDATE classifiers SET uuid=(SELECT " DATA_DATABASE_CREATE_UUID " WHERE classifiers.id!=-1) WHERE uuid=\'\';";
72 :
73 : /*!
74 : * \brief string constant to create an sql database table
75 : *
76 : * This table contains instances of generalizations, associations (which are relationships)
77 : * \see http://www.omg.org/spec/UML/
78 : */
79 : static const char *DATA_DATABASE_CREATE_RELATIONSHIP_TABLE =
80 : "CREATE TABLE IF NOT EXISTS relationships ( "
81 : "id INTEGER NOT NULL PRIMARY KEY ASC, "
82 : "main_type INTEGER NOT NULL, "
83 : "from_classifier_id INTEGER NOT NULL, "
84 : "to_classifier_id INTEGER NOT NULL, "
85 : "stereotype TEXT DEFAULT \'\', " /* since 1.47.0, DEFAULT needed in case a new DB is modified by an old program version */
86 : "name TEXT, "
87 : "description TEXT, "
88 : "list_order INTEGER, "
89 : "from_feature_id INTEGER DEFAULT NULL, " /* DEFAULT needed in case a new DB is modified by an old program version */
90 : "to_feature_id INTEGER DEFAULT NULL, " /* DEFAULT needed in case a new DB is modified by an old program version */
91 : "uuid TEXT NOT NULL DEFAULT \'\', " /* DEFAULT needed in case a new DB is modified by an old program version */
92 : "FOREIGN KEY(from_classifier_id) REFERENCES classifiers(id), "
93 : "FOREIGN KEY(to_classifier_id) REFERENCES classifiers(id), "
94 : "FOREIGN KEY(from_feature_id) REFERENCES features(id), "
95 : "FOREIGN KEY(to_feature_id) REFERENCES features(id) "
96 : ");";
97 :
98 : /*!
99 : * \brief string constant to update an sql database table
100 : *
101 : * The DEFAULT clause is needed to convert the existing records to the new format
102 : * and in case a new database is modified by an old program version.
103 : *
104 : * This command extends relationships by a uuid field.
105 : */
106 : static const char *DATA_DATABASE_ALTER_RELATIONSHIP_TABLE_UUID =
107 : "ALTER TABLE relationships "
108 : "ADD COLUMN uuid TEXT NOT NULL DEFAULT \'\';";
109 :
110 : /*!
111 : * \brief string constant to update an sql database table
112 : *
113 : * A uuid is generated and updated wherever missing.
114 : * The subselect-WHERE clause is needed to generate for each row a different uuid.
115 : */
116 : static const char *DATA_DATABASE_UPDATE_RELATIONSHIP_UUID =
117 : "UPDATE relationships SET uuid=(SELECT " DATA_DATABASE_CREATE_UUID " WHERE relationships.id!=-1) WHERE uuid=\'\';";
118 :
119 : /*!
120 : * \brief string constant to update an sql database table
121 : *
122 : * The DEFAULT clause is needed to convert the existing records to the new format
123 : * and in case a new database is modified by an old program version.
124 : *
125 : * This command extends relationships by a stereotype field.
126 : */
127 : static const char *DATA_DATABASE_ALTER_RELATIONSHIP_TABLE_STEREOTYPE =
128 : "ALTER TABLE relationships "
129 : "ADD COLUMN stereotype TEXT DEFAULT \'\';";
130 :
131 : /*!
132 : * \brief string constant to create an sql database table
133 : *
134 : * This table contains instances of attributes (which are properties which are features).
135 : * \see http://www.omg.org/spec/UML/
136 : */
137 : static const char *DATA_DATABASE_CREATE_FEATURE_TABLE =
138 : "CREATE TABLE IF NOT EXISTS features ( "
139 : "id INTEGER NOT NULL PRIMARY KEY ASC, "
140 : "main_type INTEGER NOT NULL, "
141 : "classifier_id INTEGER NOT NULL, "
142 : "key TEXT, "
143 : "value TEXT, "
144 : "description TEXT, "
145 : "list_order INTEGER, "
146 : "uuid TEXT NOT NULL DEFAULT \'\', " /* DEFAULT needed in case a new DB is modified by an old program version */
147 : "FOREIGN KEY(classifier_id) REFERENCES classifiers(id) "
148 : ");";
149 :
150 : /*!
151 : * \brief string constant to update an sql database table
152 : *
153 : * The DEFAULT clause is needed to convert the existing records to the new format
154 : * and in case a new database is modified by an old program version.
155 : *
156 : * This command extends featues by a uuid field.
157 : */
158 : static const char *DATA_DATABASE_ALTER_FEATURE_TABLE_UUID =
159 : "ALTER TABLE features "
160 : "ADD COLUMN uuid TEXT NOT NULL DEFAULT \'\';";
161 :
162 : /*!
163 : * \brief string constant to update an sql database table
164 : *
165 : * A uuid is generated and updated wherever missing.
166 : * The subselect-WHERE clause is needed to generate for each row a different uuid.
167 : */
168 : static const char *DATA_DATABASE_UPDATE_FEATURE_UUID =
169 : "UPDATE features SET uuid=(SELECT " DATA_DATABASE_CREATE_UUID " WHERE features.id!=-1) WHERE uuid=\'\';";
170 :
171 : /*!
172 : * \brief string constant to create an sql database table
173 : */
174 : static const char *DATA_DATABASE_CREATE_DIAGRAM_TABLE =
175 : "CREATE TABLE IF NOT EXISTS diagrams ( "
176 : "id INTEGER NOT NULL PRIMARY KEY ASC, "
177 : "parent_id INTEGER, " /* is NULL for the root diagram */
178 : "diagram_type INTEGER NOT NULL, "
179 : "stereotype TEXT DEFAULT \'\', " /* since 1.47.0, DEFAULT needed in case a new DB is modified by an old program version */
180 : "name TEXT, "
181 : "description TEXT, "
182 : "list_order INTEGER, "
183 : "display_flags INTEGER NOT NULL DEFAULT 0, " /* DEFAULT needed in case a new DB is modified by an old program version */
184 : "uuid TEXT NOT NULL DEFAULT \'\', " /* DEFAULT needed in case a new DB is modified by an old program version */
185 : "FOREIGN KEY(parent_id) REFERENCES diagrams(id) "
186 : ");";
187 :
188 : /*!
189 : * \brief string constant to update an sql database table
190 : *
191 : * The DEFAULT clause is needed to convert the existing records to the new format
192 : * and in case a new database is modified by an old program version.
193 : *
194 : * This command extends diagrams by display_flags field.
195 : */
196 : static const char *DATA_DATABASE_ALTER_DIAGRAM_TABLE_1 =
197 : "ALTER TABLE diagrams "
198 : "ADD COLUMN display_flags INTEGER NOT NULL DEFAULT 0;";
199 :
200 : /*!
201 : * \brief string constant to update an sql database table
202 : *
203 : * The DEFAULT clause is needed to convert the existing records to the new format
204 : * and in case a new database is modified by an old program version.
205 : *
206 : * This command extends diagrams by a uuid field.
207 : */
208 : static const char *DATA_DATABASE_ALTER_DIAGRAM_TABLE_UUID =
209 : "ALTER TABLE diagrams "
210 : "ADD COLUMN uuid TEXT NOT NULL DEFAULT \'\';";
211 :
212 : /*!
213 : * \brief string constant to update an sql database table
214 : *
215 : * A uuid is generated and updated wherever missing.
216 : * The subselect-WHERE clause is needed to generate for each row a different uuid.
217 : */
218 : static const char *DATA_DATABASE_UPDATE_DIAGRAM_UUID =
219 : "UPDATE diagrams SET uuid=(SELECT " DATA_DATABASE_CREATE_UUID " WHERE diagrams.id!=-1) WHERE uuid=\'\';";
220 :
221 : /*!
222 : * \brief string constant to update an sql database table
223 : *
224 : * The DEFAULT clause is needed to convert the existing records to the new format
225 : * and in case a new database is modified by an old program version.
226 : *
227 : * This command extends diagrams by a stereotype field.
228 : */
229 : static const char *DATA_DATABASE_ALTER_DIAGRAM_TABLE_STEREOTYPE =
230 : "ALTER TABLE diagrams "
231 : "ADD COLUMN stereotype TEXT DEFAULT \'\';";
232 :
233 : /*!
234 : * \brief string constant to create an sql database table
235 : */
236 : static const char *DATA_DATABASE_CREATE_DIAGRAMELEMENT_TABLE =
237 : "CREATE TABLE IF NOT EXISTS diagramelements ( "
238 : "id INTEGER NOT NULL PRIMARY KEY ASC, "
239 : "diagram_id INTEGER NOT NULL, "
240 : "classifier_id INTEGER NOT NULL, "
241 : "display_flags INTEGER NOT NULL, "
242 : "focused_feature_id INTEGER DEFAULT NULL, " /* DEFAULT needed in case a new DB is modified by an old program version */
243 : "uuid TEXT NOT NULL DEFAULT \'\', " /* DEFAULT needed in case a new DB is modified by an old program version */
244 : "FOREIGN KEY(diagram_id) REFERENCES diagrams(id), "
245 : "FOREIGN KEY(classifier_id) REFERENCES classifiers(id), "
246 : "FOREIGN KEY(focused_feature_id) REFERENCES features(id) "
247 : ");";
248 :
249 : /*!
250 : * \brief string constant to update an sql database table
251 : *
252 : * The DEFAULT clause is needed to convert the existing records to the new format.
253 : *
254 : * This command extends diagramelements by a uuid field.
255 : * \see http://sqlite.org/lang_altertable.html
256 : */
257 : static const char *DATA_DATABASE_ALTER_DIAGRAMELEMENT_TABLE_UUID =
258 : "ALTER TABLE diagramelements "
259 : "ADD COLUMN uuid TEXT NOT NULL DEFAULT \'\';";
260 :
261 : /*!
262 : * \brief string constant to update an sql database table
263 : *
264 : * A uuid is generated and updated wherever missing.
265 : * The subselect-WHERE clause is needed to generate for each row a different uuid.
266 : */
267 : static const char *DATA_DATABASE_UPDATE_DIAGRAMELEMENT_UUID =
268 : "UPDATE diagramelements SET uuid=(SELECT " DATA_DATABASE_CREATE_UUID " WHERE diagramelements.id!=-1) WHERE uuid=\'\';";
269 :
270 : /*!
271 : * \brief string constant to start a transaction
272 : *
273 : * \see http://sqlite.org/lang.html
274 : */
275 : static const char *DATA_DATABASE_BEGIN_TRANSACTION =
276 : "BEGIN TRANSACTION;";
277 :
278 : /*!
279 : * \brief string constant to commit a transaction
280 : *
281 : * \see http://sqlite.org/lang.html
282 : */
283 : static const char *DATA_DATABASE_COMMIT_TRANSACTION =
284 : "COMMIT TRANSACTION;";
285 :
286 : /*!
287 : * \brief keep track of the next unused revision identifier
288 : *
289 : * because data_database_set_revision() may set an already used identifier
290 : */
291 : static uint32_t data_database_unused_revision = 0;
292 :
293 : /* ================================ Lifecycle ================================ */
294 :
295 62 : void data_database_init ( data_database_t *this_ )
296 : {
297 62 : U8_TRACE_BEGIN();
298 62 : U8_LOG_EVENT_INT( "compiled against sqlite3: ", SQLITE_VERSION_NUMBER );
299 62 : U8_LOG_EVENT_STR( "linked to sqlite3_libversion:", sqlite3_libversion() );
300 :
301 62 : (*this_).db_file_name = utf8stringbuf_new( (*this_).private_db_file_name_buffer, sizeof((*this_).private_db_file_name_buffer) );
302 62 : utf8stringbuf_clear( &((*this_).db_file_name) );
303 :
304 62 : g_mutex_init ( &((*this_).lock_on_write) );
305 62 : g_mutex_lock ( &((*this_).lock_on_write) ); /* (*this_).locked_on_write may only be changed when having the lock */
306 62 : (*this_).locked_on_write = false;
307 62 : g_mutex_unlock ( &((*this_).lock_on_write) ); /* this call manages a memory barrier that allows for cache coherence */
308 :
309 62 : u8_error_t result = data_database_lock_on_write( this_ );
310 : {
311 62 : (*this_).db_state = DATA_DATABASE_STATE_CLOSED;
312 62 : (*this_).transaction_recursion = 0;
313 62 : (*this_).revision = ( data_database_unused_revision++ );
314 : }
315 62 : result |= data_database_unlock_on_write( this_ );
316 62 : if( result != U8_ERROR_NONE )
317 : {
318 0 : assert(false);
319 : }
320 :
321 62 : data_change_notifier_init ( &((*this_).notifier) );
322 62 : data_database_private_clear_db_listener_list( this_ );
323 :
324 62 : U8_TRACE_END();
325 62 : }
326 :
327 62 : u8_error_t data_database_private_open ( data_database_t *this_, const char* db_file_path, int sqlite3_flags )
328 : {
329 62 : U8_TRACE_BEGIN();
330 62 : assert( NULL != db_file_path );
331 : /* there should not be pending transactions when calling open */
332 62 : assert( (*this_).transaction_recursion == 0 );
333 : int sqlite_err;
334 62 : u8_error_t result = U8_ERROR_NONE;
335 62 : bool notify_listeners = false;
336 :
337 62 : result |= data_database_lock_on_write( this_ );
338 :
339 62 : if ( (*this_).db_state != DATA_DATABASE_STATE_CLOSED )
340 : {
341 0 : U8_TRACE_INFO("data_database_open called on database that was not closed.");
342 0 : result |= U8_ERROR_INVALID_REQUEST;
343 : }
344 : else
345 : {
346 62 : utf8stringbuf_copy_str( &((*this_).db_file_name), db_file_path );
347 :
348 62 : U8_LOG_EVENT_STR( "sqlite3_open_v2:", utf8stringbuf_get_string( &((*this_).db_file_name) ) );
349 62 : sqlite_err = sqlite3_open_v2( utf8stringbuf_get_string( &((*this_).db_file_name) ),
350 : &((*this_).db),
351 : sqlite3_flags,
352 : NULL
353 : );
354 62 : if ( SQLITE_OK != sqlite_err )
355 : {
356 0 : U8_LOG_ERROR_INT( "sqlite3_open_v2() failed:", sqlite_err );
357 0 : U8_LOG_ERROR_STR( "sqlite3_open_v2() failed:", utf8stringbuf_get_string( &((*this_).db_file_name) ) );
358 0 : (*this_).db_state = DATA_DATABASE_STATE_CLOSED;
359 0 : result |= U8_ERROR_NO_DB; /* no db to use */
360 : }
361 : else
362 : {
363 : u8_error_t init_err;
364 62 : init_err = data_database_private_initialize_tables( this_ );
365 62 : if ( init_err == U8_ERROR_NONE )
366 : {
367 62 : init_err = data_database_private_upgrade_tables( this_ );
368 : }
369 :
370 62 : if ( init_err == U8_ERROR_NONE )
371 : {
372 : (*this_).db_state
373 124 : = ( (sqlite3_flags & SQLITE_OPEN_MEMORY) == 0 )
374 : ? DATA_DATABASE_STATE_OPEN
375 62 : : DATA_DATABASE_STATE_IN_MEM;
376 62 : notify_listeners = true;
377 : }
378 : else
379 : {
380 0 : U8_LOG_EVENT_STR( "sqlite3_close:", utf8stringbuf_get_string( &((*this_).db_file_name) ) );
381 0 : sqlite_err = sqlite3_close( (*this_).db );
382 0 : if ( SQLITE_OK != sqlite_err )
383 : {
384 0 : U8_LOG_ERROR_INT( "sqlite3_close() failed:", sqlite_err );
385 : }
386 0 : utf8stringbuf_clear( &((*this_).db_file_name) );
387 0 : (*this_).db_state = DATA_DATABASE_STATE_CLOSED;
388 : }
389 62 : result |= init_err;
390 : }
391 : }
392 :
393 62 : result |= data_database_unlock_on_write( this_ );
394 :
395 62 : if ( notify_listeners )
396 : {
397 : /* do sent notifications is active by default on a newly opened db */
398 62 : data_change_notifier_disable_stealth_mode( &((*this_).notifier) );
399 :
400 : /* inform readers and writers on open */
401 62 : result |= data_database_private_notify_db_listeners( this_, DATA_DATABASE_LISTENER_SIGNAL_DB_OPENED );
402 :
403 : /* inform listeners on changes */
404 62 : data_change_notifier_emit_signal( &((*this_).notifier),
405 : DATA_CHANGE_EVENT_TYPE_DB_OPENED,
406 : DATA_TABLE_VOID,
407 : DATA_ROW_VOID,
408 : DATA_TABLE_VOID,
409 : DATA_ROW_VOID
410 : );
411 : }
412 :
413 62 : U8_TRACE_END_ERR( result );
414 62 : return result;
415 : }
416 :
417 62 : u8_error_t data_database_close ( data_database_t *this_ )
418 : {
419 62 : U8_TRACE_BEGIN();
420 : /* there should not be pending transactions when calling cloas */
421 62 : assert( (*this_).transaction_recursion == 0 );
422 : int sqlite_err;
423 62 : u8_error_t result = U8_ERROR_NONE;
424 62 : bool notify_change_listeners = false;
425 :
426 : /* do sent notifications before closing: */
427 62 : if ( data_database_is_open( this_ ) )
428 : {
429 : /* do sent notifications when closing: */
430 62 : data_change_notifier_disable_stealth_mode( &((*this_).notifier) );
431 :
432 : /* prepare close */
433 62 : data_change_notifier_emit_signal( &((*this_).notifier),
434 : DATA_CHANGE_EVENT_TYPE_DB_PREPARE_CLOSE,
435 : DATA_TABLE_VOID,
436 : DATA_ROW_VOID,
437 : DATA_TABLE_VOID,
438 : DATA_ROW_VOID
439 : );
440 :
441 : /* inform readers and writers on close */
442 62 : result |= data_database_private_notify_db_listeners( this_, DATA_DATABASE_LISTENER_SIGNAL_PREPARE_CLOSE );
443 : }
444 :
445 62 : result |= data_database_lock_on_write( this_ );
446 :
447 62 : if ( (*this_).db_state != DATA_DATABASE_STATE_CLOSED )
448 : {
449 : /* perform close */
450 62 : U8_LOG_EVENT_STR( "sqlite3_close:", utf8stringbuf_get_string( &((*this_).db_file_name) ) );
451 62 : sqlite_err = sqlite3_close( (*this_).db );
452 62 : if ( SQLITE_OK != sqlite_err )
453 : {
454 0 : U8_LOG_ERROR_INT( "sqlite3_close() failed:", sqlite_err );
455 0 : result |= U8_ERROR_AT_DB;
456 : }
457 :
458 62 : utf8stringbuf_clear( &((*this_).db_file_name) );
459 62 : (*this_).db_state = DATA_DATABASE_STATE_CLOSED;
460 62 : (*this_).transaction_recursion = 0;
461 :
462 62 : notify_change_listeners = true;
463 : }
464 : else
465 : {
466 0 : U8_TRACE_INFO("data_database_close called on database that was not open.");
467 0 : result |= U8_ERROR_INVALID_REQUEST;
468 : }
469 :
470 62 : result |= data_database_unlock_on_write( this_ );
471 :
472 62 : if ( notify_change_listeners )
473 : {
474 : /* inform listeners on changes */
475 62 : data_change_notifier_emit_signal( &((*this_).notifier),
476 : DATA_CHANGE_EVENT_TYPE_DB_CLOSED,
477 : DATA_TABLE_VOID,
478 : DATA_ROW_VOID,
479 : DATA_TABLE_VOID,
480 : DATA_ROW_VOID
481 : );
482 : }
483 :
484 62 : U8_TRACE_END_ERR( result );
485 62 : return result;
486 : }
487 :
488 62 : void data_database_destroy ( data_database_t *this_ )
489 : {
490 62 : U8_TRACE_BEGIN();
491 : /* there should not be pending transactions when calling destroy */
492 62 : assert( (*this_).transaction_recursion == 0 );
493 :
494 62 : data_database_private_clear_db_listener_list( this_ );
495 62 : if ( data_database_is_open( this_ ) )
496 : {
497 1 : data_database_close( this_ );
498 : }
499 62 : data_change_notifier_destroy( &((*this_).notifier) );
500 :
501 62 : u8_error_t result = data_database_lock_on_write( this_ );
502 : {
503 62 : (*this_).transaction_recursion = 0;
504 : }
505 62 : result |= data_database_unlock_on_write( this_ );
506 62 : if( result != U8_ERROR_NONE )
507 : {
508 0 : assert(false);
509 : }
510 :
511 : /* g_mutex_clear ( &((*this_).lock_on_write) ); -- must not be called because this GMutex is not on the stack */
512 :
513 62 : U8_TRACE_END();
514 62 : }
515 :
516 62 : u8_error_t data_database_private_initialize_tables( data_database_t *this_ )
517 : {
518 62 : U8_TRACE_BEGIN();
519 62 : u8_error_t result = U8_ERROR_NONE;
520 :
521 : /* since the database is locked during data_database_private_initialize_tables */
522 : /* we cannot call data_database_transaction_begin or data_database_in_transaction_execute here */
523 :
524 62 : result |= data_database_private_exec_sql( this_, DATA_DATABASE_CREATE_HEAD_TABLE, false );
525 62 : result |= data_database_private_exec_sql( this_, DATA_DATABASE_CREATE_CLASSIFIER_TABLE, false );
526 62 : result |= data_database_private_exec_sql( this_, DATA_DATABASE_CREATE_RELATIONSHIP_TABLE, false );
527 62 : result |= data_database_private_exec_sql( this_, DATA_DATABASE_CREATE_FEATURE_TABLE, false );
528 62 : result |= data_database_private_exec_sql( this_, DATA_DATABASE_CREATE_DIAGRAM_TABLE, false );
529 62 : result |= data_database_private_exec_sql( this_, DATA_DATABASE_CREATE_DIAGRAMELEMENT_TABLE, false );
530 :
531 62 : U8_TRACE_END_ERR( result );
532 62 : return result;
533 : }
534 :
535 62 : u8_error_t data_database_private_upgrade_tables( data_database_t *this_ )
536 : {
537 62 : U8_TRACE_BEGIN();
538 62 : u8_error_t result = U8_ERROR_NONE;
539 :
540 : /* update table classifiers from version 1.32.1 or earlier to later versions with diagram.display_flags */
541 62 : data_database_private_exec_sql( this_, DATA_DATABASE_ALTER_DIAGRAM_TABLE_1, true );
542 :
543 : /* update all 5 tables from version 1.32.1 (no uuid) to later versions with uuid field */
544 62 : data_database_private_exec_sql( this_, DATA_DATABASE_ALTER_CLASSIFIER_TABLE_UUID, true );
545 62 : result |= data_database_private_exec_sql( this_, DATA_DATABASE_UPDATE_CLASSIFIER_UUID, true );
546 62 : data_database_private_exec_sql( this_, DATA_DATABASE_ALTER_RELATIONSHIP_TABLE_UUID, true );
547 62 : result |= data_database_private_exec_sql( this_, DATA_DATABASE_UPDATE_RELATIONSHIP_UUID, true );
548 62 : data_database_private_exec_sql( this_, DATA_DATABASE_ALTER_FEATURE_TABLE_UUID, true );
549 62 : result |= data_database_private_exec_sql( this_, DATA_DATABASE_UPDATE_FEATURE_UUID, true );
550 62 : data_database_private_exec_sql( this_, DATA_DATABASE_ALTER_DIAGRAM_TABLE_UUID, true );
551 62 : result |= data_database_private_exec_sql( this_, DATA_DATABASE_UPDATE_DIAGRAM_UUID, true );
552 62 : data_database_private_exec_sql( this_, DATA_DATABASE_ALTER_DIAGRAMELEMENT_TABLE_UUID, true );
553 62 : result |= data_database_private_exec_sql( this_, DATA_DATABASE_UPDATE_DIAGRAMELEMENT_UUID, true );
554 :
555 : /* update table diagrams and relationships from version 1.46.0 or earlier to later versions with stereotype */
556 : /* do not care for "already existed" errors: */
557 62 : data_database_private_exec_sql( this_, DATA_DATABASE_ALTER_RELATIONSHIP_TABLE_STEREOTYPE, true );
558 62 : data_database_private_exec_sql( this_, DATA_DATABASE_ALTER_DIAGRAM_TABLE_STEREOTYPE, true );
559 :
560 62 : if ( u8_error_contains( result, U8_ERROR_READ_ONLY_DB ) )
561 : {
562 0 : U8_LOG_EVENT( "sqlite3 database is read only." );
563 0 : result = u8_error_more_than( result, U8_ERROR_READ_ONLY_DB ) ? U8_ERROR_AT_DB : U8_ERROR_NONE;
564 : }
565 :
566 62 : U8_TRACE_END_ERR( result );
567 62 : return result;
568 : }
569 :
570 : /* ================================ Actions on DB ================================ */
571 :
572 0 : u8_error_t data_database_flush_caches ( data_database_t *this_ )
573 : {
574 0 : U8_TRACE_BEGIN();
575 0 : u8_error_t result = U8_ERROR_NONE;
576 :
577 0 : if ( data_database_is_open( this_ ) )
578 : {
579 0 : U8_LOG_EVENT_INT( "sqlite3_libversion_number()", sqlite3_libversion_number() );
580 0 : if ( sqlite3_libversion_number() >= 3010000 )
581 : {
582 : /* available if sqlite newer than 2016-01-06 (3.10.0) */
583 : #if ( SQLITE_VERSION_NUMBER >= 3010000 )
584 : int sqlite_err;
585 0 : U8_LOG_EVENT( "sqlite3_db_cacheflush" );
586 0 : sqlite_err = sqlite3_db_cacheflush( (*this_).db );
587 0 : if ( SQLITE_OK != sqlite_err )
588 : {
589 0 : U8_LOG_ERROR_INT( "sqlite3_db_cacheflush() failed:", sqlite_err );
590 0 : result = U8_ERROR_AT_DB;
591 : }
592 : #else
593 : U8_LOG_WARNING_INT( "The compile-time version of sqlite3 did not provide the sqlite3_db_cacheflush() function.",
594 : SQLITE_VERSION_NUMBER
595 : );
596 : #endif
597 : }
598 : else
599 : {
600 0 : U8_LOG_WARNING_INT( "The runtime-time version of sqlite3 does not provide the sqlite3_db_cacheflush() function.",
601 : sqlite3_libversion_number()
602 : );
603 : }
604 : }
605 : else
606 : {
607 0 : result = U8_ERROR_NO_DB;
608 : }
609 :
610 0 : U8_TRACE_END_ERR( result );
611 0 : return result;
612 : }
613 :
614 0 : u8_error_t data_database_trace_stats ( data_database_t *this_ )
615 : {
616 0 : U8_TRACE_BEGIN();
617 0 : u8_error_t result = U8_ERROR_NONE;
618 :
619 0 : if ( data_database_is_open( this_ ) )
620 : {
621 : sqlite3_int64 use;
622 : sqlite3_int64 max;
623 :
624 0 : use = sqlite3_memory_used();
625 0 : max = sqlite3_memory_highwater(false);
626 0 : U8_TRACE_INFO_INT_INT( "sqlite3_memory_used/highwater():", use, max );
627 : }
628 : else
629 : {
630 0 : U8_TRACE_INFO( "database not open." );
631 : }
632 :
633 0 : U8_TRACE_END_ERR( result );
634 0 : return result;
635 : }
636 :
637 1799 : u8_error_t data_database_transaction_begin ( data_database_t *this_ )
638 : {
639 1799 : U8_TRACE_BEGIN();
640 : /* nesting of transactions should not be greater than 2. You may increase this limit if needed. */
641 1799 : assert( (*this_).transaction_recursion < 2 );
642 1799 : u8_error_t result = U8_ERROR_NONE;
643 : int sqlite_err;
644 1799 : char *error_msg = NULL;
645 1799 : sqlite3 *db = data_database_get_database_ptr( this_ );
646 :
647 1799 : if ( data_database_is_open( this_ ) )
648 : {
649 1799 : if ( (*this_).transaction_recursion == 0 )
650 : {
651 1791 : U8_LOG_EVENT_STR( "sqlite3_exec:", DATA_DATABASE_BEGIN_TRANSACTION );
652 1791 : sqlite_err = sqlite3_exec( db, DATA_DATABASE_BEGIN_TRANSACTION, NULL, NULL, &error_msg );
653 1791 : if ( SQLITE_OK != sqlite_err )
654 : {
655 0 : U8_LOG_ERROR_STR( "sqlite3_exec() failed:", DATA_DATABASE_BEGIN_TRANSACTION );
656 0 : U8_LOG_ERROR_INT( "sqlite3_exec() failed:", sqlite_err );
657 0 : result |= U8_ERROR_AT_DB;
658 : }
659 1791 : if ( error_msg != NULL )
660 : {
661 0 : U8_LOG_ERROR_STR( "sqlite3_exec() failed:", error_msg );
662 0 : sqlite3_free( error_msg );
663 0 : error_msg = NULL;
664 : }
665 : }
666 1799 : (*this_).transaction_recursion ++;
667 : }
668 : else
669 : {
670 0 : U8_LOG_WARNING_STR( "database not open. cannot execute", DATA_DATABASE_BEGIN_TRANSACTION );
671 0 : result = U8_ERROR_NO_DB;
672 : }
673 :
674 1799 : U8_TRACE_END_ERR( result );
675 1799 : return result;
676 : }
677 :
678 1799 : u8_error_t data_database_transaction_commit ( data_database_t *this_ )
679 : {
680 1799 : U8_TRACE_BEGIN();
681 : /* there should be at least 1 pending transaction */
682 1799 : assert( (*this_).transaction_recursion > 0 );
683 1799 : u8_error_t result = U8_ERROR_NONE;
684 : int sqlite_err;
685 1799 : char *error_msg = NULL;
686 1799 : sqlite3 *db = data_database_get_database_ptr( this_ );
687 :
688 1799 : if ( data_database_is_open( this_ ) )
689 : {
690 1799 : if ( (*this_).transaction_recursion == 1 )
691 : {
692 1791 : U8_LOG_EVENT_STR( "sqlite3_exec:", DATA_DATABASE_COMMIT_TRANSACTION );
693 1791 : sqlite_err = sqlite3_exec( db, DATA_DATABASE_COMMIT_TRANSACTION, NULL, NULL, &error_msg );
694 1791 : if ( SQLITE_OK != sqlite_err )
695 : {
696 0 : U8_LOG_ERROR_STR( "sqlite3_exec() failed:", DATA_DATABASE_COMMIT_TRANSACTION );
697 0 : U8_LOG_ERROR_INT( "sqlite3_exec() failed:", sqlite_err );
698 0 : result |= U8_ERROR_AT_DB;
699 : }
700 1791 : if ( error_msg != NULL )
701 : {
702 0 : U8_LOG_ERROR_STR( "sqlite3_exec() failed:", error_msg );
703 0 : sqlite3_free( error_msg );
704 0 : error_msg = NULL;
705 : }
706 :
707 : /* increase the revision id */
708 : u8_error_t locking_error;
709 1791 : locking_error = data_database_lock_on_write( this_ );
710 1791 : (*this_).revision = ( data_database_unused_revision++ );
711 1791 : locking_error |= data_database_unlock_on_write( this_ );
712 1791 : assert( locking_error == U8_ERROR_NONE );
713 : (void) locking_error; /* this should not happen in RELEASE mode */
714 : }
715 1799 : (*this_).transaction_recursion --;
716 : }
717 : else
718 : {
719 0 : U8_LOG_WARNING_STR( "database not open. cannot execute", DATA_DATABASE_COMMIT_TRANSACTION );
720 0 : result = U8_ERROR_NO_DB;
721 : }
722 :
723 1799 : U8_TRACE_END_ERR( result );
724 1799 : return result;
725 : }
726 :
727 1476 : u8_error_t data_database_in_transaction_create ( data_database_t *this_, const char* sql_statement, data_row_t* out_new_id )
728 : {
729 1476 : U8_TRACE_BEGIN();
730 1476 : assert( NULL != sql_statement );
731 1476 : u8_error_t result = U8_ERROR_NONE;
732 : int sqlite_err;
733 1476 : char *error_msg = NULL;
734 1476 : sqlite3 *const db = (*this_).db;
735 :
736 1476 : if ( data_database_is_open( this_ ) )
737 : {
738 1476 : U8_LOG_EVENT( "sqlite3_exec: sql_statement (see trace)" );
739 1476 : U8_TRACE_INFO_STR( "sqlite3_exec:", sql_statement );
740 1476 : sqlite_err = sqlite3_exec( db, sql_statement, NULL, NULL, &error_msg );
741 1476 : if ( SQLITE_CONSTRAINT == (0xff & sqlite_err) )
742 : {
743 : /* This case happens if id is not unique and/or if a classifier name is not unique*/
744 12 : U8_LOG_ERROR( "sqlite3_exec() failed due to UNIQUE constraint: sql_statement (see trace)" );
745 12 : U8_TRACE_INFO_STR( "sqlite3_exec() failed due to UNIQUE constraint:", sql_statement );
746 12 : result |= U8_ERROR_DUPLICATE;
747 : }
748 1464 : else if ( SQLITE_OK != sqlite_err )
749 : {
750 0 : U8_LOG_ERROR( "sqlite3_exec() failed: sql_statement (see trace)" );
751 0 : U8_LOG_ERROR_INT( "sqlite3_exec() failed:", sqlite_err );
752 0 : U8_TRACE_INFO_STR( "sqlite3_exec:", sql_statement );
753 0 : result |= (sqlite_err == SQLITE_READONLY) ? U8_ERROR_READ_ONLY_DB : U8_ERROR_AT_DB;
754 : }
755 1476 : if ( error_msg != NULL )
756 : {
757 12 : U8_LOG_ERROR_STR( "sqlite3_exec() failed:", error_msg );
758 12 : sqlite3_free( error_msg );
759 12 : error_msg = NULL;
760 : }
761 :
762 1476 : if ( NULL != out_new_id )
763 : {
764 1476 : if ( SQLITE_OK == sqlite_err )
765 : {
766 : data_row_t new_id;
767 1464 : new_id = sqlite3_last_insert_rowid(db);
768 1464 : U8_LOG_EVENT_INT( "sqlite3_last_insert_rowid():", new_id );
769 1464 : *out_new_id = new_id;
770 : }
771 : }
772 : }
773 : else
774 : {
775 0 : U8_LOG_WARNING( "database not open. cannot execute sql_statement (see trace)" );
776 0 : U8_TRACE_INFO_STR( "database not open. cannot execute", sql_statement );
777 0 : result = U8_ERROR_NO_DB;
778 : }
779 :
780 1476 : U8_TRACE_END_ERR( result );
781 1476 : return result;
782 : }
783 :
784 316 : u8_error_t data_database_in_transaction_execute ( data_database_t *this_, const char* sql_statement )
785 : {
786 316 : U8_TRACE_BEGIN();
787 316 : assert( NULL != sql_statement );
788 316 : u8_error_t result = U8_ERROR_NONE;
789 : int sqlite_err;
790 316 : char *error_msg = NULL;
791 316 : sqlite3 *db = (*this_).db;
792 :
793 316 : if ( data_database_is_open( this_ ) )
794 : {
795 316 : U8_LOG_EVENT( "sqlite3_exec: sql_statement (see trace)" );
796 316 : U8_TRACE_INFO_STR( "sqlite3_exec:", sql_statement );
797 316 : sqlite_err = sqlite3_exec( db, sql_statement, NULL, NULL, &error_msg );
798 316 : if ( SQLITE_CONSTRAINT == (0xff & sqlite_err) )
799 : {
800 0 : U8_LOG_ERROR( "sqlite3_exec() failed due to UNIQUE constraint: sql_statement (see trace)" );
801 0 : U8_TRACE_INFO_STR( "sqlite3_exec() failed due to UNIQUE constraint:", sql_statement );
802 0 : result |= U8_ERROR_DUPLICATE_NAME;
803 : }
804 316 : else if ( SQLITE_OK != sqlite_err )
805 : {
806 0 : U8_LOG_ERROR( "sqlite3_exec() failed: sql_statement (see trace)" );
807 0 : U8_LOG_ERROR_INT( "sqlite3_exec() failed:", sqlite_err );
808 0 : U8_TRACE_INFO_STR( "sqlite3_exec() failed:", sql_statement );
809 0 : result |= (sqlite_err == SQLITE_READONLY) ? U8_ERROR_READ_ONLY_DB : U8_ERROR_AT_DB;
810 : }
811 316 : if ( error_msg != NULL )
812 : {
813 0 : U8_LOG_ERROR_STR( "sqlite3_exec() failed:", error_msg );
814 0 : sqlite3_free( error_msg );
815 0 : error_msg = NULL;
816 : }
817 : }
818 : else
819 : {
820 0 : U8_LOG_WARNING( "database not open. cannot execute sql_statement (see trace)" );
821 0 : U8_TRACE_INFO_STR( "database not open. cannot execute", sql_statement );
822 0 : result = U8_ERROR_NO_DB;
823 : }
824 :
825 316 : U8_TRACE_END_ERR( result );
826 316 : return result;
827 : }
828 :
829 : /* ================================ Information ================================ */
830 :
831 : /* ================================ Change Listener ================================ */
832 :
833 235 : u8_error_t data_database_add_db_listener( data_database_t *this_, data_database_listener_t *listener )
834 : {
835 235 : U8_TRACE_BEGIN();
836 235 : assert( NULL != listener );
837 235 : u8_error_t result = U8_ERROR_NONE;
838 235 : bool already_registered = false;
839 :
840 235 : result |= data_database_lock_on_write( this_ );
841 :
842 235 : int pos = -1;
843 4935 : for( int index = 0; index < DATA_DATABASE_MAX_LISTENERS; index ++ )
844 : {
845 4700 : if ( NULL == (*this_).listener_list[index] )
846 : {
847 4162 : pos = index;
848 : }
849 4700 : if ( listener == (*this_).listener_list[index] )
850 : {
851 1 : already_registered = true;
852 : }
853 : }
854 :
855 235 : if ( already_registered )
856 : {
857 1 : U8_LOG_ERROR( "Listener already registered." );
858 1 : result |= U8_ERROR_INVALID_REQUEST;
859 : }
860 234 : else if ( -1 != pos )
861 : {
862 233 : (*this_).listener_list[pos] = listener;
863 : }
864 : else
865 : {
866 1 : U8_LOG_ERROR_INT( "Maximum number of listeners reached.", DATA_DATABASE_MAX_LISTENERS );
867 1 : result |= U8_ERROR_ARRAY_BUFFER_EXCEEDED;
868 : }
869 :
870 235 : result |= data_database_unlock_on_write( this_ );
871 :
872 235 : U8_TRACE_END_ERR( result );
873 235 : return result;
874 : }
875 :
876 214 : u8_error_t data_database_remove_db_listener( data_database_t *this_, data_database_listener_t *listener )
877 : {
878 214 : U8_TRACE_BEGIN();
879 :
880 214 : assert( NULL != listener );
881 :
882 214 : u8_error_t result = U8_ERROR_NONE;
883 214 : int count_closed = 0;
884 :
885 214 : result |= data_database_lock_on_write( this_ );
886 :
887 4494 : for( int index = 0; index < DATA_DATABASE_MAX_LISTENERS; index ++ )
888 : {
889 4280 : if ( (*this_).listener_list[index] == listener )
890 : {
891 213 : (*this_).listener_list[index] = NULL;
892 213 : count_closed ++;
893 : }
894 : }
895 :
896 214 : result |= data_database_unlock_on_write( this_ );
897 :
898 214 : if ( count_closed == 0 )
899 : {
900 1 : U8_LOG_ERROR( "listener not found" );
901 1 : result |= U8_ERROR_INVALID_REQUEST;
902 : }
903 :
904 214 : U8_TRACE_END_ERR( result );
905 214 : return result;
906 : }
907 :
908 128 : u8_error_t data_database_private_notify_db_listeners( data_database_t *this_, data_database_listener_signal_t signal_id )
909 : {
910 128 : U8_TRACE_BEGIN();
911 : data_database_listener_t *(listener_list_copy[DATA_DATABASE_MAX_LISTENERS]);
912 128 : u8_error_t result = U8_ERROR_NONE;
913 :
914 128 : result |= data_database_lock_on_write( this_ );
915 :
916 128 : memcpy( listener_list_copy, (*this_).listener_list, sizeof(listener_list_copy) );
917 :
918 128 : result |= data_database_unlock_on_write( this_ );
919 :
920 2688 : for( int index = 0; index < DATA_DATABASE_MAX_LISTENERS; index ++ )
921 : {
922 2560 : if ( NULL != listener_list_copy[index] )
923 : {
924 49 : data_database_listener_notify( listener_list_copy[index], signal_id );
925 : }
926 : }
927 :
928 128 : U8_TRACE_END_ERR( result );
929 128 : return result;
930 : }
931 :
932 : /* ================================ Lifecycle Lock ================================ */
933 :
934 :
935 : /*
936 : Copyright 2016-2025 Andreas Warnke
937 :
938 : Licensed under the Apache License, Version 2.0 (the "License");
939 : you may not use this file except in compliance with the License.
940 : You may obtain a copy of the License at
941 :
942 : http://www.apache.org/licenses/LICENSE-2.0
943 :
944 : Unless required by applicable law or agreed to in writing, software
945 : distributed under the License is distributed on an "AS IS" BASIS,
946 : WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
947 : See the License for the specific language governing permissions and
948 : limitations under the License.
949 : */
|