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 : /* ================================ Lifecycle ================================ */
287 :
288 64 : void data_database_init ( data_database_t *this_ )
289 : {
290 64 : U8_TRACE_BEGIN();
291 64 : U8_LOG_EVENT_INT( "compiled against sqlite3: ", SQLITE_VERSION_NUMBER );
292 64 : U8_LOG_EVENT_STR( "linked to sqlite3_libversion:", sqlite3_libversion() );
293 :
294 64 : (*this_).db_file_name = utf8stringbuf_init( sizeof((*this_).private_db_file_name_buffer), (*this_).private_db_file_name_buffer );
295 64 : utf8stringbuf_clear( (*this_).db_file_name );
296 :
297 64 : g_mutex_init ( &((*this_).lock_on_write) );
298 64 : g_mutex_lock ( &((*this_).lock_on_write) ); /* (*this_).locked_on_write may only be changed when having the lock */
299 64 : (*this_).locked_on_write = false;
300 64 : g_mutex_unlock ( &((*this_).lock_on_write) ); /* this call manages a memory barrier that allows for cache coherence */
301 :
302 64 : u8_error_t result = data_database_lock_on_write( this_ );
303 : {
304 64 : (*this_).db_state = DATA_DATABASE_STATE_CLOSED;
305 64 : (*this_).transaction_recursion = 0;
306 : }
307 64 : result |= data_database_unlock_on_write( this_ );
308 64 : if( result != U8_ERROR_NONE )
309 : {
310 0 : assert(false);
311 : }
312 :
313 64 : data_change_notifier_init ( &((*this_).notifier) );
314 64 : data_database_private_clear_db_listener_list( this_ );
315 :
316 64 : U8_TRACE_END();
317 64 : }
318 :
319 63 : u8_error_t data_database_private_open ( data_database_t *this_, const char* db_file_path, int sqlite3_flags )
320 : {
321 63 : U8_TRACE_BEGIN();
322 63 : assert( NULL != db_file_path );
323 : /* there should not be pending transactions when calling open */
324 63 : assert( (*this_).transaction_recursion == 0 );
325 : int sqlite_err;
326 63 : u8_error_t result = U8_ERROR_NONE;
327 63 : bool notify_listeners = false;
328 :
329 63 : result |= data_database_lock_on_write( this_ );
330 :
331 63 : if ( (*this_).db_state != DATA_DATABASE_STATE_CLOSED )
332 : {
333 0 : U8_TRACE_INFO("data_database_open called on database that was not closed.");
334 0 : result |= U8_ERROR_INVALID_REQUEST;
335 : }
336 : else
337 : {
338 63 : utf8stringbuf_copy_str( (*this_).db_file_name, db_file_path );
339 :
340 63 : U8_LOG_EVENT_STR( "sqlite3_open_v2:", utf8stringbuf_get_string( (*this_).db_file_name ) );
341 63 : sqlite_err = sqlite3_open_v2( utf8stringbuf_get_string( (*this_).db_file_name ),
342 : &((*this_).db),
343 : sqlite3_flags,
344 : NULL
345 : );
346 63 : if ( SQLITE_OK != sqlite_err )
347 : {
348 0 : U8_LOG_ERROR_INT( "sqlite3_open_v2() failed:", sqlite_err );
349 0 : U8_LOG_ERROR_STR( "sqlite3_open_v2() failed:", utf8stringbuf_get_string( (*this_).db_file_name ) );
350 0 : (*this_).db_state = DATA_DATABASE_STATE_CLOSED;
351 0 : result |= U8_ERROR_NO_DB; /* no db to use */
352 : }
353 : else
354 : {
355 : u8_error_t init_err;
356 63 : init_err = data_database_private_initialize_tables( this_ );
357 63 : if ( init_err == U8_ERROR_NONE )
358 : {
359 63 : init_err = data_database_private_upgrade_tables( this_ );
360 : }
361 :
362 63 : if ( init_err == U8_ERROR_NONE )
363 : {
364 : (*this_).db_state
365 126 : = ( (sqlite3_flags & SQLITE_OPEN_MEMORY) == 0 )
366 : ? DATA_DATABASE_STATE_OPEN
367 63 : : DATA_DATABASE_STATE_IN_MEM;
368 63 : notify_listeners = true;
369 : }
370 : else
371 : {
372 0 : U8_LOG_EVENT_STR( "sqlite3_close:", utf8stringbuf_get_string( (*this_).db_file_name ) );
373 0 : sqlite_err = sqlite3_close( (*this_).db );
374 0 : if ( SQLITE_OK != sqlite_err )
375 : {
376 0 : U8_LOG_ERROR_INT( "sqlite3_close() failed:", sqlite_err );
377 : }
378 0 : utf8stringbuf_clear( (*this_).db_file_name );
379 0 : (*this_).db_state = DATA_DATABASE_STATE_CLOSED;
380 : }
381 63 : result |= init_err;
382 : }
383 : }
384 :
385 63 : result |= data_database_unlock_on_write( this_ );
386 :
387 63 : if ( notify_listeners )
388 : {
389 : /* do sent notifications is active by default on a newly opened db */
390 63 : data_change_notifier_disable_stealth_mode( &((*this_).notifier) );
391 :
392 : /* inform readers and writers on open */
393 63 : result |= data_database_private_notify_db_listeners( this_, DATA_DATABASE_LISTENER_SIGNAL_DB_OPENED );
394 :
395 : /* inform listeners on changes */
396 63 : data_change_notifier_emit_signal( &((*this_).notifier),
397 : DATA_CHANGE_EVENT_TYPE_DB_OPENED,
398 : DATA_TABLE_VOID,
399 : DATA_ROW_ID_VOID,
400 : DATA_TABLE_VOID,
401 : DATA_ROW_ID_VOID
402 : );
403 : }
404 :
405 63 : U8_TRACE_END_ERR( result );
406 63 : return result;
407 : }
408 :
409 63 : u8_error_t data_database_close ( data_database_t *this_ )
410 : {
411 63 : U8_TRACE_BEGIN();
412 : /* there should not be pending transactions when calling cloas */
413 63 : assert( (*this_).transaction_recursion == 0 );
414 : int sqlite_err;
415 63 : u8_error_t result = U8_ERROR_NONE;
416 63 : bool notify_change_listeners = false;
417 :
418 : /* do sent notifications before closing: */
419 63 : if ( data_database_is_open( this_ ) )
420 : {
421 : /* do sent notifications when closing: */
422 63 : data_change_notifier_disable_stealth_mode( &((*this_).notifier) );
423 :
424 : /* prepare close */
425 63 : data_change_notifier_emit_signal( &((*this_).notifier),
426 : DATA_CHANGE_EVENT_TYPE_DB_PREPARE_CLOSE,
427 : DATA_TABLE_VOID,
428 : DATA_ROW_ID_VOID,
429 : DATA_TABLE_VOID,
430 : DATA_ROW_ID_VOID
431 : );
432 :
433 : /* inform readers and writers on close */
434 63 : result |= data_database_private_notify_db_listeners( this_, DATA_DATABASE_LISTENER_SIGNAL_PREPARE_CLOSE );
435 : }
436 :
437 63 : result |= data_database_lock_on_write( this_ );
438 :
439 63 : if ( (*this_).db_state != DATA_DATABASE_STATE_CLOSED )
440 : {
441 : /* perform close */
442 63 : U8_LOG_EVENT_STR( "sqlite3_close:", utf8stringbuf_get_string( (*this_).db_file_name ) );
443 63 : sqlite_err = sqlite3_close( (*this_).db );
444 63 : if ( SQLITE_OK != sqlite_err )
445 : {
446 0 : U8_LOG_ERROR_INT( "sqlite3_close() failed:", sqlite_err );
447 0 : result |= U8_ERROR_AT_DB;
448 : }
449 :
450 63 : utf8stringbuf_clear( (*this_).db_file_name );
451 63 : (*this_).db_state = DATA_DATABASE_STATE_CLOSED;
452 63 : (*this_).transaction_recursion = 0;
453 :
454 63 : notify_change_listeners = true;
455 : }
456 : else
457 : {
458 0 : U8_TRACE_INFO("data_database_close called on database that was not open.");
459 0 : result |= U8_ERROR_INVALID_REQUEST;
460 : }
461 :
462 63 : result |= data_database_unlock_on_write( this_ );
463 :
464 63 : if ( notify_change_listeners )
465 : {
466 : /* inform listeners on changes */
467 63 : data_change_notifier_emit_signal( &((*this_).notifier),
468 : DATA_CHANGE_EVENT_TYPE_DB_CLOSED,
469 : DATA_TABLE_VOID,
470 : DATA_ROW_ID_VOID,
471 : DATA_TABLE_VOID,
472 : DATA_ROW_ID_VOID
473 : );
474 : }
475 :
476 63 : U8_TRACE_END_ERR( result );
477 63 : return result;
478 : }
479 :
480 64 : void data_database_destroy ( data_database_t *this_ )
481 : {
482 64 : U8_TRACE_BEGIN();
483 : /* there should not be pending transactions when calling destroy */
484 64 : assert( (*this_).transaction_recursion == 0 );
485 :
486 64 : data_database_private_clear_db_listener_list( this_ );
487 64 : if ( data_database_is_open( this_ ) )
488 : {
489 2 : data_database_close( this_ );
490 : }
491 64 : data_change_notifier_destroy( &((*this_).notifier) );
492 :
493 64 : u8_error_t result = data_database_lock_on_write( this_ );
494 : {
495 64 : (*this_).transaction_recursion = 0;
496 : }
497 64 : result |= data_database_unlock_on_write( this_ );
498 64 : if( result != U8_ERROR_NONE )
499 : {
500 0 : assert(false);
501 : }
502 :
503 : /* g_mutex_clear ( &((*this_).lock_on_write) ); -- must not be called because this GMutex is not on the stack */
504 :
505 64 : U8_TRACE_END();
506 64 : }
507 :
508 63 : u8_error_t data_database_private_initialize_tables( data_database_t *this_ )
509 : {
510 63 : U8_TRACE_BEGIN();
511 63 : u8_error_t result = U8_ERROR_NONE;
512 :
513 : /* since the database is locked during data_database_private_initialize_tables */
514 : /* we cannot call data_database_transaction_begin or data_database_in_transaction_execute here */
515 :
516 63 : result |= data_database_private_exec_sql( this_, DATA_DATABASE_CREATE_HEAD_TABLE, false );
517 63 : result |= data_database_private_exec_sql( this_, DATA_DATABASE_CREATE_CLASSIFIER_TABLE, false );
518 63 : result |= data_database_private_exec_sql( this_, DATA_DATABASE_CREATE_RELATIONSHIP_TABLE, false );
519 63 : result |= data_database_private_exec_sql( this_, DATA_DATABASE_CREATE_FEATURE_TABLE, false );
520 63 : result |= data_database_private_exec_sql( this_, DATA_DATABASE_CREATE_DIAGRAM_TABLE, false );
521 63 : result |= data_database_private_exec_sql( this_, DATA_DATABASE_CREATE_DIAGRAMELEMENT_TABLE, false );
522 :
523 63 : U8_TRACE_END_ERR( result );
524 63 : return result;
525 : }
526 :
527 63 : u8_error_t data_database_private_upgrade_tables( data_database_t *this_ )
528 : {
529 63 : U8_TRACE_BEGIN();
530 63 : u8_error_t result = U8_ERROR_NONE;
531 :
532 : /* update table classifiers from version 1.32.1 or earlier to later versions with diagram.display_flags */
533 63 : data_database_private_exec_sql( this_, DATA_DATABASE_ALTER_DIAGRAM_TABLE_1, true );
534 :
535 : /* update all 5 tables from version 1.32.1 (no uuid) to later versions with uuid field */
536 63 : data_database_private_exec_sql( this_, DATA_DATABASE_ALTER_CLASSIFIER_TABLE_UUID, true );
537 63 : result |= data_database_private_exec_sql( this_, DATA_DATABASE_UPDATE_CLASSIFIER_UUID, true );
538 63 : data_database_private_exec_sql( this_, DATA_DATABASE_ALTER_RELATIONSHIP_TABLE_UUID, true );
539 63 : result |= data_database_private_exec_sql( this_, DATA_DATABASE_UPDATE_RELATIONSHIP_UUID, true );
540 63 : data_database_private_exec_sql( this_, DATA_DATABASE_ALTER_FEATURE_TABLE_UUID, true );
541 63 : result |= data_database_private_exec_sql( this_, DATA_DATABASE_UPDATE_FEATURE_UUID, true );
542 63 : data_database_private_exec_sql( this_, DATA_DATABASE_ALTER_DIAGRAM_TABLE_UUID, true );
543 63 : result |= data_database_private_exec_sql( this_, DATA_DATABASE_UPDATE_DIAGRAM_UUID, true );
544 63 : data_database_private_exec_sql( this_, DATA_DATABASE_ALTER_DIAGRAMELEMENT_TABLE_UUID, true );
545 63 : result |= data_database_private_exec_sql( this_, DATA_DATABASE_UPDATE_DIAGRAMELEMENT_UUID, true );
546 :
547 : /* update table diagrams and relationships from version 1.46.0 or earlier to later versions with stereotype */
548 : /* do not care for "already existed" errors: */
549 63 : data_database_private_exec_sql( this_, DATA_DATABASE_ALTER_RELATIONSHIP_TABLE_STEREOTYPE, true );
550 63 : data_database_private_exec_sql( this_, DATA_DATABASE_ALTER_DIAGRAM_TABLE_STEREOTYPE, true );
551 :
552 63 : if ( u8_error_contains( result, U8_ERROR_READ_ONLY_DB ) )
553 : {
554 0 : U8_LOG_EVENT( "sqlite3 database is read only." );
555 0 : result = u8_error_more_than( result, U8_ERROR_READ_ONLY_DB ) ? U8_ERROR_AT_DB : U8_ERROR_NONE;
556 : }
557 :
558 63 : U8_TRACE_END_ERR( result );
559 63 : return result;
560 : }
561 :
562 : /* ================================ Actions on DB ================================ */
563 :
564 0 : u8_error_t data_database_flush_caches ( data_database_t *this_ )
565 : {
566 0 : U8_TRACE_BEGIN();
567 0 : u8_error_t result = U8_ERROR_NONE;
568 :
569 0 : if ( data_database_is_open( this_ ) )
570 : {
571 0 : U8_LOG_EVENT_INT( "sqlite3_libversion_number()", sqlite3_libversion_number() );
572 0 : if ( sqlite3_libversion_number() >= 3010000 )
573 : {
574 : /* available if sqlite newer than 2016-01-06 (3.10.0) */
575 : #if ( SQLITE_VERSION_NUMBER >= 3010000 )
576 : int sqlite_err;
577 0 : U8_LOG_EVENT( "sqlite3_db_cacheflush" );
578 0 : sqlite_err = sqlite3_db_cacheflush( (*this_).db );
579 0 : if ( SQLITE_OK != sqlite_err )
580 : {
581 0 : U8_LOG_ERROR_INT( "sqlite3_db_cacheflush() failed:", sqlite_err );
582 0 : result = U8_ERROR_AT_DB;
583 : }
584 : #else
585 : U8_LOG_WARNING_INT( "The compile-time version of sqlite3 did not provide the sqlite3_db_cacheflush() function.",
586 : SQLITE_VERSION_NUMBER
587 : );
588 : #endif
589 : }
590 : else
591 : {
592 0 : U8_LOG_WARNING_INT( "The runtime-time version of sqlite3 does not provide the sqlite3_db_cacheflush() function.",
593 : sqlite3_libversion_number()
594 : );
595 : }
596 : }
597 : else
598 : {
599 0 : result = U8_ERROR_NO_DB;
600 : }
601 :
602 0 : U8_TRACE_END_ERR( result );
603 0 : return result;
604 : }
605 :
606 0 : u8_error_t data_database_trace_stats ( data_database_t *this_ )
607 : {
608 0 : U8_TRACE_BEGIN();
609 0 : u8_error_t result = U8_ERROR_NONE;
610 :
611 0 : if ( data_database_is_open( this_ ) )
612 : {
613 : sqlite3_int64 use;
614 : sqlite3_int64 max;
615 :
616 0 : use = sqlite3_memory_used();
617 0 : max = sqlite3_memory_highwater(false);
618 0 : U8_TRACE_INFO_INT_INT( "sqlite3_memory_used/highwater():", use, max );
619 : }
620 : else
621 : {
622 0 : U8_TRACE_INFO( "database not open." );
623 : }
624 :
625 0 : U8_TRACE_END_ERR( result );
626 0 : return result;
627 : }
628 :
629 2702 : u8_error_t data_database_transaction_begin ( data_database_t *this_ )
630 : {
631 2702 : U8_TRACE_BEGIN();
632 : /* nesting of transactions should not be greater than 2. You may increase this limit if needed. */
633 2702 : assert( (*this_).transaction_recursion < 2 );
634 2702 : u8_error_t result = U8_ERROR_NONE;
635 : int sqlite_err;
636 2702 : char *error_msg = NULL;
637 2702 : sqlite3 *db = data_database_get_database_ptr( this_ );
638 :
639 2702 : if ( data_database_is_open( this_ ) )
640 : {
641 2702 : if ( (*this_).transaction_recursion == 0 )
642 : {
643 2694 : U8_LOG_EVENT_STR( "sqlite3_exec:", DATA_DATABASE_BEGIN_TRANSACTION );
644 2694 : sqlite_err = sqlite3_exec( db, DATA_DATABASE_BEGIN_TRANSACTION, NULL, NULL, &error_msg );
645 2694 : if ( SQLITE_OK != sqlite_err )
646 : {
647 0 : U8_LOG_ERROR_STR( "sqlite3_exec() failed:", DATA_DATABASE_BEGIN_TRANSACTION );
648 0 : U8_LOG_ERROR_INT( "sqlite3_exec() failed:", sqlite_err );
649 0 : result |= U8_ERROR_AT_DB;
650 : }
651 2694 : if ( error_msg != NULL )
652 : {
653 0 : U8_LOG_ERROR_STR( "sqlite3_exec() failed:", error_msg );
654 0 : sqlite3_free( error_msg );
655 0 : error_msg = NULL;
656 : }
657 : }
658 2702 : (*this_).transaction_recursion ++;
659 : }
660 : else
661 : {
662 0 : U8_LOG_WARNING_STR( "database not open. cannot execute", DATA_DATABASE_BEGIN_TRANSACTION );
663 0 : result = U8_ERROR_NO_DB;
664 : }
665 :
666 2702 : U8_TRACE_END_ERR( result );
667 2702 : return result;
668 : }
669 :
670 2702 : u8_error_t data_database_transaction_commit ( data_database_t *this_ )
671 : {
672 2702 : U8_TRACE_BEGIN();
673 : /* there should be at least 1 pending transaction */
674 2702 : assert( (*this_).transaction_recursion > 0 );
675 2702 : u8_error_t result = U8_ERROR_NONE;
676 : int sqlite_err;
677 2702 : char *error_msg = NULL;
678 2702 : sqlite3 *db = data_database_get_database_ptr( this_ );
679 :
680 2702 : if ( data_database_is_open( this_ ) )
681 : {
682 2702 : if ( (*this_).transaction_recursion == 1 )
683 : {
684 2694 : U8_LOG_EVENT_STR( "sqlite3_exec:", DATA_DATABASE_COMMIT_TRANSACTION );
685 2694 : sqlite_err = sqlite3_exec( db, DATA_DATABASE_COMMIT_TRANSACTION, NULL, NULL, &error_msg );
686 2694 : if ( SQLITE_OK != sqlite_err )
687 : {
688 0 : U8_LOG_ERROR_STR( "sqlite3_exec() failed:", DATA_DATABASE_COMMIT_TRANSACTION );
689 0 : U8_LOG_ERROR_INT( "sqlite3_exec() failed:", sqlite_err );
690 0 : result |= U8_ERROR_AT_DB;
691 : }
692 2694 : if ( error_msg != NULL )
693 : {
694 0 : U8_LOG_ERROR_STR( "sqlite3_exec() failed:", error_msg );
695 0 : sqlite3_free( error_msg );
696 0 : error_msg = NULL;
697 : }
698 : }
699 2702 : (*this_).transaction_recursion --;
700 : }
701 : else
702 : {
703 0 : U8_LOG_WARNING_STR( "database not open. cannot execute", DATA_DATABASE_COMMIT_TRANSACTION );
704 0 : result = U8_ERROR_NO_DB;
705 : }
706 :
707 2702 : U8_TRACE_END_ERR( result );
708 2702 : return result;
709 : }
710 :
711 2380 : u8_error_t data_database_in_transaction_create ( data_database_t *this_, const char* sql_statement, data_row_id_t* out_new_id )
712 : {
713 2380 : U8_TRACE_BEGIN();
714 2380 : assert( NULL != sql_statement );
715 2380 : u8_error_t result = U8_ERROR_NONE;
716 : int sqlite_err;
717 2380 : char *error_msg = NULL;
718 2380 : sqlite3 *const db = (*this_).db;
719 :
720 2380 : if ( data_database_is_open( this_ ) )
721 : {
722 2380 : U8_LOG_EVENT( "sqlite3_exec: sql_statement (see trace)" );
723 2380 : U8_TRACE_INFO_STR( "sqlite3_exec:", sql_statement );
724 2380 : sqlite_err = sqlite3_exec( db, sql_statement, NULL, NULL, &error_msg );
725 2380 : if ( SQLITE_CONSTRAINT == (0xff & sqlite_err) )
726 : {
727 : /* This case happens if id is not unique and/or if a classifier name is not unique*/
728 12 : U8_LOG_ERROR( "sqlite3_exec() failed due to UNIQUE constraint: sql_statement (see trace)" );
729 12 : U8_TRACE_INFO_STR( "sqlite3_exec() failed due to UNIQUE constraint:", sql_statement );
730 12 : result |= U8_ERROR_DUPLICATE;
731 : }
732 2368 : else if ( SQLITE_OK != sqlite_err )
733 : {
734 0 : U8_LOG_ERROR( "sqlite3_exec() failed: sql_statement (see trace)" );
735 0 : U8_LOG_ERROR_INT( "sqlite3_exec() failed:", sqlite_err );
736 0 : U8_TRACE_INFO_STR( "sqlite3_exec:", sql_statement );
737 0 : result |= (sqlite_err == SQLITE_READONLY) ? U8_ERROR_READ_ONLY_DB : U8_ERROR_AT_DB;
738 : }
739 2380 : if ( error_msg != NULL )
740 : {
741 12 : U8_LOG_ERROR_STR( "sqlite3_exec() failed:", error_msg );
742 12 : sqlite3_free( error_msg );
743 12 : error_msg = NULL;
744 : }
745 :
746 2380 : if ( NULL != out_new_id )
747 : {
748 2380 : if ( SQLITE_OK == sqlite_err )
749 : {
750 : data_row_id_t new_id;
751 2368 : new_id = sqlite3_last_insert_rowid(db);
752 2368 : U8_LOG_EVENT_INT( "sqlite3_last_insert_rowid():", new_id );
753 2368 : *out_new_id = new_id;
754 : }
755 : }
756 : }
757 : else
758 : {
759 0 : U8_LOG_WARNING( "database not open. cannot execute sql_statement (see trace)" );
760 0 : U8_TRACE_INFO_STR( "database not open. cannot execute", sql_statement );
761 0 : result = U8_ERROR_NO_DB;
762 : }
763 :
764 2380 : U8_TRACE_END_ERR( result );
765 2380 : return result;
766 : }
767 :
768 316 : u8_error_t data_database_in_transaction_execute ( data_database_t *this_, const char* sql_statement )
769 : {
770 316 : U8_TRACE_BEGIN();
771 316 : assert( NULL != sql_statement );
772 316 : u8_error_t result = U8_ERROR_NONE;
773 : int sqlite_err;
774 316 : char *error_msg = NULL;
775 316 : sqlite3 *db = (*this_).db;
776 :
777 316 : if ( data_database_is_open( this_ ) )
778 : {
779 316 : U8_LOG_EVENT( "sqlite3_exec: sql_statement (see trace)" );
780 316 : U8_TRACE_INFO_STR( "sqlite3_exec:", sql_statement );
781 316 : sqlite_err = sqlite3_exec( db, sql_statement, NULL, NULL, &error_msg );
782 316 : if ( SQLITE_CONSTRAINT == (0xff & sqlite_err) )
783 : {
784 0 : U8_LOG_ERROR( "sqlite3_exec() failed due to UNIQUE constraint: sql_statement (see trace)" );
785 0 : U8_TRACE_INFO_STR( "sqlite3_exec() failed due to UNIQUE constraint:", sql_statement );
786 0 : result |= U8_ERROR_DUPLICATE_NAME;
787 : }
788 316 : else if ( SQLITE_OK != sqlite_err )
789 : {
790 0 : U8_LOG_ERROR( "sqlite3_exec() failed: sql_statement (see trace)" );
791 0 : U8_LOG_ERROR_INT( "sqlite3_exec() failed:", sqlite_err );
792 0 : U8_TRACE_INFO_STR( "sqlite3_exec() failed:", sql_statement );
793 0 : result |= (sqlite_err == SQLITE_READONLY) ? U8_ERROR_READ_ONLY_DB : U8_ERROR_AT_DB;
794 : }
795 316 : if ( error_msg != NULL )
796 : {
797 0 : U8_LOG_ERROR_STR( "sqlite3_exec() failed:", error_msg );
798 0 : sqlite3_free( error_msg );
799 0 : error_msg = NULL;
800 : }
801 : }
802 : else
803 : {
804 0 : U8_LOG_WARNING( "database not open. cannot execute sql_statement (see trace)" );
805 0 : U8_TRACE_INFO_STR( "database not open. cannot execute", sql_statement );
806 0 : result = U8_ERROR_NO_DB;
807 : }
808 :
809 316 : U8_TRACE_END_ERR( result );
810 316 : return result;
811 : }
812 :
813 : /* ================================ Information ================================ */
814 :
815 : /* ================================ Change Listener ================================ */
816 :
817 236 : u8_error_t data_database_add_db_listener( data_database_t *this_, data_database_listener_t *listener )
818 : {
819 236 : U8_TRACE_BEGIN();
820 236 : assert( NULL != listener );
821 236 : u8_error_t result = U8_ERROR_NONE;
822 236 : bool already_registered = false;
823 :
824 236 : result |= data_database_lock_on_write( this_ );
825 :
826 236 : int pos = -1;
827 4956 : for( int index = 0; index < DATA_DATABASE_MAX_LISTENERS; index ++ )
828 : {
829 4720 : if ( NULL == (*this_).listener_list[index] )
830 : {
831 4189 : pos = index;
832 : }
833 4720 : if ( listener == (*this_).listener_list[index] )
834 : {
835 1 : already_registered = true;
836 : }
837 : }
838 :
839 236 : if ( already_registered )
840 : {
841 1 : U8_LOG_ERROR( "Listener already registered." );
842 1 : result |= U8_ERROR_INVALID_REQUEST;
843 : }
844 235 : else if ( -1 != pos )
845 : {
846 234 : (*this_).listener_list[pos] = listener;
847 : }
848 : else
849 : {
850 1 : U8_LOG_ERROR_INT( "Maximum number of listeners reached.", DATA_DATABASE_MAX_LISTENERS );
851 1 : result |= U8_ERROR_ARRAY_BUFFER_EXCEEDED;
852 : }
853 :
854 236 : result |= data_database_unlock_on_write( this_ );
855 :
856 236 : U8_TRACE_END_ERR( result );
857 236 : return result;
858 : }
859 :
860 215 : u8_error_t data_database_remove_db_listener( data_database_t *this_, data_database_listener_t *listener )
861 : {
862 215 : U8_TRACE_BEGIN();
863 :
864 215 : assert( NULL != listener );
865 :
866 215 : u8_error_t result = U8_ERROR_NONE;
867 215 : int count_closed = 0;
868 :
869 215 : result |= data_database_lock_on_write( this_ );
870 :
871 4515 : for( int index = 0; index < DATA_DATABASE_MAX_LISTENERS; index ++ )
872 : {
873 4300 : if ( (*this_).listener_list[index] == listener )
874 : {
875 214 : (*this_).listener_list[index] = NULL;
876 214 : count_closed ++;
877 : }
878 : }
879 :
880 215 : result |= data_database_unlock_on_write( this_ );
881 :
882 215 : if ( count_closed == 0 )
883 : {
884 1 : U8_LOG_ERROR( "listener not found" );
885 1 : result |= U8_ERROR_INVALID_REQUEST;
886 : }
887 :
888 215 : U8_TRACE_END_ERR( result );
889 215 : return result;
890 : }
891 :
892 130 : u8_error_t data_database_private_notify_db_listeners( data_database_t *this_, data_database_listener_signal_t signal_id )
893 : {
894 130 : U8_TRACE_BEGIN();
895 : data_database_listener_t *(listener_list_copy[DATA_DATABASE_MAX_LISTENERS]);
896 130 : u8_error_t result = U8_ERROR_NONE;
897 :
898 130 : result |= data_database_lock_on_write( this_ );
899 :
900 130 : memcpy( listener_list_copy, (*this_).listener_list, sizeof(listener_list_copy) );
901 :
902 130 : result |= data_database_unlock_on_write( this_ );
903 :
904 2730 : for( int index = 0; index < DATA_DATABASE_MAX_LISTENERS; index ++ )
905 : {
906 2600 : if ( NULL != listener_list_copy[index] )
907 : {
908 40 : data_database_listener_notify( listener_list_copy[index], signal_id );
909 : }
910 : }
911 :
912 130 : U8_TRACE_END_ERR( result );
913 130 : return result;
914 : }
915 :
916 : /* ================================ Lifecycle Lock ================================ */
917 :
918 :
919 : /*
920 : Copyright 2016-2024 Andreas Warnke
921 :
922 : Licensed under the Apache License, Version 2.0 (the "License");
923 : you may not use this file except in compliance with the License.
924 : You may obtain a copy of the License at
925 :
926 : http://www.apache.org/licenses/LICENSE-2.0
927 :
928 : Unless required by applicable law or agreed to in writing, software
929 : distributed under the License is distributed on an "AS IS" BASIS,
930 : WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
931 : See the License for the specific language governing permissions and
932 : limitations under the License.
933 : */
|