LCOV - code coverage report
Current view: top level - data/source/storage - data_database.c (source / functions) Hit Total Coverage
Test: crystal-facet-uml_v1.57.0_covts Lines: 232 313 74.1 %
Date: 2024-04-07 11:14:42 Functions: 13 15 86.7 %

          Line data    Source code
       1             : /* File: data_database.c; Copyright and License: see below */
       2             : 
       3             : #include "storage/data_database.h"
       4             : #include "data_id.h"
       5             : #include "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          57 : void data_database_init ( data_database_t *this_ )
     289             : {
     290          57 :     U8_TRACE_BEGIN();
     291          57 :     U8_LOG_EVENT_INT( "compiled against sqlite3:    ", SQLITE_VERSION_NUMBER );
     292          57 :     U8_LOG_EVENT_STR( "linked to sqlite3_libversion:", sqlite3_libversion() );
     293             : 
     294          57 :     (*this_).db_file_name = utf8stringbuf_init( sizeof((*this_).private_db_file_name_buffer), (*this_).private_db_file_name_buffer );
     295          57 :     utf8stringbuf_clear( (*this_).db_file_name );
     296             : 
     297          57 :     g_mutex_init ( &((*this_).lock_on_write) );
     298          57 :     g_mutex_lock ( &((*this_).lock_on_write) );  /* (*this_).locked_on_write may only be changed when having the lock */
     299          57 :     (*this_).locked_on_write = false;
     300          57 :     g_mutex_unlock ( &((*this_).lock_on_write) );  /* this call manages a memory barrier that allows for cache coherence */
     301             : 
     302          57 :     u8_error_t result = data_database_lock_on_write( this_ );
     303             :     {
     304          57 :         (*this_).db_state = DATA_DATABASE_STATE_CLOSED;
     305          57 :         (*this_).transaction_recursion = 0;
     306             :     }
     307          57 :     result |= data_database_unlock_on_write( this_ );
     308          57 :     if( result != U8_ERROR_NONE )
     309             :     {
     310           0 :         assert(false);
     311             :     }
     312             : 
     313          57 :     data_change_notifier_init ( &((*this_).notifier) );
     314          57 :     data_database_private_clear_db_listener_list( this_ );
     315             : 
     316          57 :     U8_TRACE_END();
     317          57 : }
     318             : 
     319          56 : u8_error_t data_database_private_open ( data_database_t *this_, const char* db_file_path, int sqlite3_flags )
     320             : {
     321          56 :     U8_TRACE_BEGIN();
     322          56 :     assert( NULL != db_file_path );
     323             :     /* there should not be pending transactions when calling open */
     324          56 :     assert( (*this_).transaction_recursion == 0 );
     325             :     int sqlite_err;
     326          56 :     u8_error_t result = U8_ERROR_NONE;
     327          56 :     bool notify_listeners = false;
     328             : 
     329          56 :     result |= data_database_lock_on_write( this_ );
     330             : 
     331          56 :     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          56 :         utf8stringbuf_copy_str( (*this_).db_file_name, db_file_path );
     339             : 
     340          56 :         U8_LOG_EVENT_STR( "sqlite3_open_v2:", utf8stringbuf_get_string( (*this_).db_file_name ) );
     341          56 :         sqlite_err = sqlite3_open_v2( utf8stringbuf_get_string( (*this_).db_file_name ),
     342             :                                       &((*this_).db),
     343             :                                       sqlite3_flags,
     344             :                                       NULL
     345             :                                     );
     346          56 :         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          56 :             init_err = data_database_private_initialize_tables( this_ );
     357          56 :             if ( init_err == U8_ERROR_NONE )
     358             :             {
     359          56 :                 init_err = data_database_private_upgrade_tables( this_ );
     360             :             }
     361             : 
     362          56 :             if ( init_err == U8_ERROR_NONE )
     363             :             {
     364             :                 (*this_).db_state
     365         112 :                     = ( (sqlite3_flags & SQLITE_OPEN_MEMORY) == 0 )
     366             :                     ? DATA_DATABASE_STATE_OPEN
     367          56 :                     : DATA_DATABASE_STATE_IN_MEM;
     368          56 :                 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          56 :             result |= init_err;
     382             :         }
     383             :     }
     384             : 
     385          56 :     result |= data_database_unlock_on_write( this_ );
     386             : 
     387          56 :     if ( notify_listeners )
     388             :     {
     389             :         /* do sent notifications is active by default on a newly opened db */
     390          56 :         data_change_notifier_disable_stealth_mode( &((*this_).notifier) );
     391             : 
     392             :         /* inform readers and writers on open */
     393          56 :         result |= data_database_private_notify_db_listeners( this_, DATA_DATABASE_LISTENER_SIGNAL_DB_OPENED );
     394             : 
     395             :         /* inform listeners on changes */
     396          56 :         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          56 :     U8_TRACE_END_ERR( result );
     406          56 :     return result;
     407             : }
     408             : 
     409          56 : u8_error_t data_database_close ( data_database_t *this_ )
     410             : {
     411          56 :     U8_TRACE_BEGIN();
     412             :     /* there should not be pending transactions when calling cloas */
     413          56 :     assert( (*this_).transaction_recursion == 0 );
     414             :     int sqlite_err;
     415          56 :     u8_error_t result = U8_ERROR_NONE;
     416          56 :     bool notify_change_listeners = false;
     417             : 
     418             :     /* do sent notifications before closing: */
     419          56 :     if ( data_database_is_open( this_ ) )
     420             :     {
     421             :         /* do sent notifications when closing: */
     422          56 :         data_change_notifier_disable_stealth_mode( &((*this_).notifier) );
     423             : 
     424             :         /* prepare close */
     425          56 :         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          56 :         result |= data_database_private_notify_db_listeners( this_, DATA_DATABASE_LISTENER_SIGNAL_PREPARE_CLOSE );
     435             :     }
     436             : 
     437          56 :     result |= data_database_lock_on_write( this_ );
     438             : 
     439          56 :     if ( (*this_).db_state != DATA_DATABASE_STATE_CLOSED )
     440             :     {
     441             :         /* perform close */
     442          56 :         U8_LOG_EVENT_STR( "sqlite3_close:", utf8stringbuf_get_string( (*this_).db_file_name ) );
     443          56 :         sqlite_err = sqlite3_close( (*this_).db );
     444          56 :         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          56 :         utf8stringbuf_clear( (*this_).db_file_name );
     451          56 :         (*this_).db_state = DATA_DATABASE_STATE_CLOSED;
     452          56 :         (*this_).transaction_recursion = 0;
     453             : 
     454          56 :         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          56 :     result |= data_database_unlock_on_write( this_ );
     463             : 
     464          56 :     if ( notify_change_listeners )
     465             :     {
     466             :         /* inform listeners on changes */
     467          56 :         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          56 :     U8_TRACE_END_ERR( result );
     477          56 :     return result;
     478             : }
     479             : 
     480          57 : void data_database_destroy ( data_database_t *this_ )
     481             : {
     482          57 :     U8_TRACE_BEGIN();
     483             :     /* there should not be pending transactions when calling destroy */
     484          57 :     assert( (*this_).transaction_recursion == 0 );
     485             : 
     486          57 :     data_database_private_clear_db_listener_list( this_ );
     487          57 :     if ( data_database_is_open( this_ ) )
     488             :     {
     489           2 :         data_database_close( this_ );
     490             :     }
     491          57 :     data_change_notifier_destroy( &((*this_).notifier) );
     492             : 
     493          57 :     u8_error_t result = data_database_lock_on_write( this_ );
     494             :     {
     495          57 :         (*this_).transaction_recursion = 0;
     496             :     }
     497          57 :     result |= data_database_unlock_on_write( this_ );
     498          57 :     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          57 :     U8_TRACE_END();
     506          57 : }
     507             : 
     508          56 : u8_error_t data_database_private_initialize_tables( data_database_t *this_ )
     509             : {
     510          56 :     U8_TRACE_BEGIN();
     511          56 :     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          56 :     result |= data_database_private_exec_sql( this_, DATA_DATABASE_CREATE_HEAD_TABLE, false );
     517          56 :     result |= data_database_private_exec_sql( this_, DATA_DATABASE_CREATE_CLASSIFIER_TABLE, false );
     518          56 :     result |= data_database_private_exec_sql( this_, DATA_DATABASE_CREATE_RELATIONSHIP_TABLE, false );
     519          56 :     result |= data_database_private_exec_sql( this_, DATA_DATABASE_CREATE_FEATURE_TABLE, false );
     520          56 :     result |= data_database_private_exec_sql( this_, DATA_DATABASE_CREATE_DIAGRAM_TABLE, false );
     521          56 :     result |= data_database_private_exec_sql( this_, DATA_DATABASE_CREATE_DIAGRAMELEMENT_TABLE, false );
     522             : 
     523          56 :     U8_TRACE_END_ERR( result );
     524          56 :     return result;
     525             : }
     526             : 
     527          56 : u8_error_t data_database_private_upgrade_tables( data_database_t *this_ )
     528             : {
     529          56 :     U8_TRACE_BEGIN();
     530          56 :     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          56 :     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          56 :     data_database_private_exec_sql( this_, DATA_DATABASE_ALTER_CLASSIFIER_TABLE_UUID, true );
     537          56 :     result |= data_database_private_exec_sql( this_, DATA_DATABASE_UPDATE_CLASSIFIER_UUID, true );
     538          56 :     data_database_private_exec_sql( this_, DATA_DATABASE_ALTER_RELATIONSHIP_TABLE_UUID, true );
     539          56 :     result |= data_database_private_exec_sql( this_, DATA_DATABASE_UPDATE_RELATIONSHIP_UUID, true );
     540          56 :     data_database_private_exec_sql( this_, DATA_DATABASE_ALTER_FEATURE_TABLE_UUID, true );
     541          56 :     result |= data_database_private_exec_sql( this_, DATA_DATABASE_UPDATE_FEATURE_UUID, true );
     542          56 :     data_database_private_exec_sql( this_, DATA_DATABASE_ALTER_DIAGRAM_TABLE_UUID, true );
     543          56 :     result |= data_database_private_exec_sql( this_, DATA_DATABASE_UPDATE_DIAGRAM_UUID, true );
     544          56 :     data_database_private_exec_sql( this_, DATA_DATABASE_ALTER_DIAGRAMELEMENT_TABLE_UUID, true );
     545          56 :     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          56 :     data_database_private_exec_sql( this_, DATA_DATABASE_ALTER_RELATIONSHIP_TABLE_STEREOTYPE, true );
     550          56 :     data_database_private_exec_sql( this_, DATA_DATABASE_ALTER_DIAGRAM_TABLE_STEREOTYPE, true );
     551             : 
     552          56 :     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          56 :     U8_TRACE_END_ERR( result );
     559          56 :     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         813 : u8_error_t data_database_transaction_begin ( data_database_t *this_ )
     630             : {
     631         813 :     U8_TRACE_BEGIN();
     632             :     /* nesting of transactions should not be greater than 2. You may increase this limit if needed. */
     633         813 :     assert( (*this_).transaction_recursion < 2 );
     634         813 :     u8_error_t result = U8_ERROR_NONE;
     635             :     int sqlite_err;
     636         813 :     char *error_msg = NULL;
     637         813 :     sqlite3 *db = data_database_get_database_ptr( this_ );
     638             : 
     639         813 :     if ( data_database_is_open( this_ ) )
     640             :     {
     641         813 :         if ( (*this_).transaction_recursion == 0 )
     642             :         {
     643         805 :             U8_LOG_EVENT_STR( "sqlite3_exec:", DATA_DATABASE_BEGIN_TRANSACTION );
     644         805 :             sqlite_err = sqlite3_exec( db, DATA_DATABASE_BEGIN_TRANSACTION, NULL, NULL, &error_msg );
     645         805 :             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         805 :             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         813 :         (*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         813 :     U8_TRACE_END_ERR( result );
     667         813 :     return result;
     668             : }
     669             : 
     670         813 : u8_error_t data_database_transaction_commit ( data_database_t *this_ )
     671             : {
     672         813 :     U8_TRACE_BEGIN();
     673             :     /* there should be at least 1 pending transaction */
     674         813 :     assert( (*this_).transaction_recursion > 0 );
     675         813 :     u8_error_t result = U8_ERROR_NONE;
     676             :     int sqlite_err;
     677         813 :     char *error_msg = NULL;
     678         813 :     sqlite3 *db = data_database_get_database_ptr( this_ );
     679             : 
     680         813 :     if ( data_database_is_open( this_ ) )
     681             :     {
     682         813 :         if ( (*this_).transaction_recursion == 1 )
     683             :         {
     684         805 :             U8_LOG_EVENT_STR( "sqlite3_exec:", DATA_DATABASE_COMMIT_TRANSACTION );
     685         805 :             sqlite_err = sqlite3_exec( db, DATA_DATABASE_COMMIT_TRANSACTION, NULL, NULL, &error_msg );
     686         805 :             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         805 :             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         813 :         (*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         813 :     U8_TRACE_END_ERR( result );
     708         813 :     return result;
     709             : }
     710             : 
     711         491 : 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         491 :     U8_TRACE_BEGIN();
     714         491 :     assert( NULL != sql_statement );
     715         491 :     u8_error_t result = U8_ERROR_NONE;
     716             :     int sqlite_err;
     717         491 :     char *error_msg = NULL;
     718         491 :     sqlite3 *const db = (*this_).db;
     719             : 
     720         491 :     if ( data_database_is_open( this_ ) )
     721             :     {
     722         491 :         U8_LOG_EVENT( "sqlite3_exec: sql_statement (see trace)" );
     723         491 :         U8_TRACE_INFO_STR( "sqlite3_exec:", sql_statement );
     724         491 :         sqlite_err = sqlite3_exec( db, sql_statement, NULL, NULL, &error_msg );
     725         491 :         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         479 :         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         491 :         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         491 :         if ( NULL != out_new_id )
     747             :         {
     748         491 :             if ( SQLITE_OK == sqlite_err )
     749             :             {
     750             :                 data_row_id_t new_id;
     751         479 :                 new_id = sqlite3_last_insert_rowid(db);
     752         479 :                 U8_LOG_EVENT_INT( "sqlite3_last_insert_rowid():", new_id );
     753         479 :                 *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         491 :     U8_TRACE_END_ERR( result );
     765         491 :     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         222 : u8_error_t data_database_add_db_listener( data_database_t *this_, data_database_listener_t *listener )
     818             : {
     819         222 :     U8_TRACE_BEGIN();
     820         222 :     assert( NULL != listener );
     821         222 :     u8_error_t result = U8_ERROR_NONE;
     822         222 :     bool already_registered = false;
     823             : 
     824         222 :     result |= data_database_lock_on_write( this_ );
     825             : 
     826         222 :     int pos = -1;
     827        4662 :     for( int index = 0; index < DATA_DATABASE_MAX_LISTENERS; index ++ )
     828             :     {
     829        4440 :         if ( NULL == (*this_).listener_list[index] )
     830             :         {
     831        3916 :             pos = index;
     832             :         }
     833        4440 :         if ( listener == (*this_).listener_list[index] )
     834             :         {
     835           1 :             already_registered = true;
     836             :         }
     837             :     }
     838             : 
     839         222 :     if ( already_registered )
     840             :     {
     841           1 :         U8_LOG_ERROR( "Listener already registered." );
     842           1 :         result |= U8_ERROR_INVALID_REQUEST;
     843             :     }
     844         221 :     else if ( -1 != pos )
     845             :     {
     846         220 :         (*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         222 :     result |= data_database_unlock_on_write( this_ );
     855             : 
     856         222 :     U8_TRACE_END_ERR( result );
     857         222 :     return result;
     858             : }
     859             : 
     860         201 : u8_error_t data_database_remove_db_listener( data_database_t *this_, data_database_listener_t *listener )
     861             : {
     862         201 :     U8_TRACE_BEGIN();
     863             : 
     864         201 :     assert( NULL != listener );
     865             : 
     866         201 :     u8_error_t result = U8_ERROR_NONE;
     867         201 :     int count_closed = 0;
     868             : 
     869         201 :     result |= data_database_lock_on_write( this_ );
     870             : 
     871        4221 :     for( int index = 0; index < DATA_DATABASE_MAX_LISTENERS; index ++ )
     872             :     {
     873        4020 :         if ( (*this_).listener_list[index] == listener )
     874             :         {
     875         200 :             (*this_).listener_list[index] = NULL;
     876         200 :             count_closed ++;
     877             :         }
     878             :     }
     879             : 
     880         201 :     result |= data_database_unlock_on_write( this_ );
     881             : 
     882         201 :     if ( count_closed == 0 )
     883             :     {
     884           1 :         U8_LOG_ERROR( "listener not found" );
     885           1 :         result |= U8_ERROR_INVALID_REQUEST;
     886             :     }
     887             : 
     888         201 :     U8_TRACE_END_ERR( result );
     889         201 :     return result;
     890             : }
     891             : 
     892         116 : u8_error_t data_database_private_notify_db_listeners( data_database_t *this_, data_database_listener_signal_t signal_id )
     893             : {
     894         116 :     U8_TRACE_BEGIN();
     895             :     data_database_listener_t *(listener_list_copy[DATA_DATABASE_MAX_LISTENERS]);
     896         116 :     u8_error_t result = U8_ERROR_NONE;
     897             : 
     898         116 :     result |= data_database_lock_on_write( this_ );
     899             : 
     900         116 :     memcpy( listener_list_copy, (*this_).listener_list, sizeof(listener_list_copy) );
     901             : 
     902         116 :     result |= data_database_unlock_on_write( this_ );
     903             : 
     904        2436 :     for( int index = 0; index < DATA_DATABASE_MAX_LISTENERS; index ++ )
     905             :     {
     906        2320 :         if ( NULL != listener_list_copy[index] )
     907             :         {
     908          40 :             data_database_listener_notify( listener_list_copy[index], signal_id );
     909             :         }
     910             :     }
     911             : 
     912         116 :     U8_TRACE_END_ERR( result );
     913         116 :     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             : */

Generated by: LCOV version 1.16