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