LCOV - code coverage report
Current view: top level - data/source/storage - data_database_head.c (source / functions) Coverage Total Hit
Test: crystal-facet-uml_v1.63.2_covts Lines: 97.5 % 163 159
Test Date: 2025-05-01 10:10:14 Functions: 100.0 % 7 7

            Line data    Source code
       1              : /* File: data_database_head.c; Copyright and License: see below */
       2              : 
       3              : #include "storage/data_database_head.h"
       4              : #include "entity/data_id.h"
       5              : #include "u8/u8_trace.h"
       6              : #include "u8/u8_log.h"
       7              : #include "utf8stringbuf/utf8string.h"
       8              : #include "utf8stringbuf/utf8stringview.h"
       9              : #include <assert.h>
      10              : 
      11              : /*!
      12              :  *  \brief value separator string constant to insert a diagram or classifier or other table-row
      13              :  */
      14              : static const char *const DATA_DATABASE_HEAD_INSERT_VALUE_SEPARATOR = ",";
      15              : 
      16              : /*!
      17              :  *  \brief string start marker string constant to insert/update a diagram
      18              :  */
      19              : static const char *const DATA_DATABASE_HEAD_STRING_VALUE_START = "\'";
      20              : 
      21              : /*!
      22              :  *  \brief string end marker string constant to insert/update a diagram
      23              :  */
      24              : static const char *const DATA_DATABASE_HEAD_STRING_VALUE_END = "\'";
      25              : 
      26              : /*!
      27              :  *  \brief translation table to encode strings for usage in string literals
      28              :  *
      29              :  *  Note: This table is not suitable for searches using the LIKE operator because _ and % are not handled.
      30              :  */
      31              : const char *const (DATA_DATABASE_HEAD_SQL_ENCODE[][2]) = {
      32              :     { "'", "''" },
      33              :     { NULL, NULL }
      34              : };
      35              : 
      36            7 : void data_database_head_init ( data_database_head_t *this_, data_database_t *database )
      37              : {
      38            7 :     U8_TRACE_BEGIN();
      39              : 
      40            7 :     (*this_).database = database;
      41              : 
      42              :     /* initialize a memory output stream */
      43            7 :     universal_memory_output_stream_init( &((*this_).plain_out),
      44            7 :                                          &((*this_).private_sql_buffer),
      45              :                                          sizeof((*this_).private_sql_buffer),
      46              :                                          UNIVERSAL_MEMORY_OUTPUT_STREAM_0TERM_UTF8
      47              :                                        );
      48              :     universal_output_stream_t *const plain_output
      49            7 :         = universal_memory_output_stream_get_output_stream( &((*this_).plain_out) );
      50              : 
      51            7 :     utf8stream_writer_init( &((*this_).plain), plain_output );
      52              : 
      53              :     /* initialize an sql escaped output stream */
      54            7 :     universal_escaping_output_stream_init( &((*this_).escaped_out),
      55              :                                            &DATA_DATABASE_HEAD_SQL_ENCODE,
      56              :                                            plain_output
      57              :                                          );
      58              :     universal_output_stream_t *const escaped_output
      59            7 :         = universal_escaping_output_stream_get_output_stream( &((*this_).escaped_out) );
      60              : 
      61            7 :     utf8stream_writer_init( &((*this_).escaped), escaped_output );
      62              : 
      63            7 :     U8_TRACE_END();
      64            7 : }
      65              : 
      66            7 : void data_database_head_destroy ( data_database_head_t *this_ )
      67              : {
      68            7 :     U8_TRACE_BEGIN();
      69              : 
      70              :     /* de-initialize an sql escaped output stream */
      71            7 :     utf8stream_writer_destroy( &((*this_).escaped) );
      72            7 :     universal_escaping_output_stream_destroy( &((*this_).escaped_out) );
      73              : 
      74              :     /* de-initialize an output stream */
      75            7 :     utf8stream_writer_destroy( &((*this_).plain) );
      76            7 :     universal_memory_output_stream_destroy( &((*this_).plain_out) );
      77              : 
      78            7 :     (*this_).database = NULL;
      79              : 
      80            7 :     U8_TRACE_END();
      81            7 : }
      82              : 
      83              : /*!
      84              :  *  \brief prefix search statement to find a head value by id
      85              :  */
      86              : static const char *const DATA_DATABASE_HEAD_SELECT_HEAD_BY_ID_PREFIX =
      87              :     "SELECT id,key,value FROM head WHERE id=";
      88              : 
      89              : /*!
      90              :  *  \brief postfix search statement to find a head value by id
      91              :  */
      92              : static const char *const DATA_DATABASE_HEAD_SELECT_HEAD_BY_ID_POSTFIX = ";";
      93              : 
      94            5 : u8_error_t data_database_head_read_value_by_id ( data_database_head_t *this_, data_row_t obj_id, data_head_t *out_head )
      95              : {
      96            5 :     U8_TRACE_BEGIN();
      97            5 :     assert( out_head != NULL );
      98            5 :     u8_error_t result = U8_ERROR_NONE;
      99              : 
     100              :     /* create an sql command */
     101              :     {
     102            5 :         result |= universal_memory_output_stream_reset( &((*this_).plain_out) );
     103              : 
     104            5 :         result |= utf8stream_writer_write_str( &((*this_).plain), DATA_DATABASE_HEAD_SELECT_HEAD_BY_ID_PREFIX );
     105            5 :         result |= utf8stream_writer_write_int( &((*this_).plain), obj_id );
     106            5 :         result |= utf8stream_writer_write_str( &((*this_).plain), DATA_DATABASE_HEAD_SELECT_HEAD_BY_ID_POSTFIX );
     107              : 
     108            5 :         result |= utf8stream_writer_flush( &((*this_).plain) );  /* enforces 0-termination on (*this_).plain_out */
     109              :     }
     110              : 
     111            5 :     if ( result == U8_ERROR_NONE )
     112              :     {
     113            5 :         const char *const sql_cmd = &((*this_).private_sql_buffer[0]);
     114              :         sqlite3_stmt *prepared_statement;
     115              :         int sqlite_err;
     116              : 
     117           15 :         result |= data_database_prepare_statement( (*this_).database,
     118              :                                                    sql_cmd,
     119            5 :                                                    utf8string_get_length( sql_cmd ) + sizeof( char ),
     120              :                                                    &prepared_statement
     121              :                                                  );
     122              : 
     123            5 :         U8_TRACE_INFO( "sqlite3_step()" );
     124            5 :         sqlite_err = sqlite3_step( prepared_statement );
     125            5 :         if ( SQLITE_ROW != sqlite_err )
     126              :         {
     127              :             /* Do not log this incident, the caller may not expect to find a row. */
     128            1 :             U8_TRACE_INFO_INT( "sqlite3_step did not find a row for id", obj_id );
     129            1 :             result |= U8_ERROR_NOT_FOUND;
     130              :         }
     131              : 
     132            5 :         if ( SQLITE_ROW == sqlite_err )
     133              :         {
     134            4 :             result |= data_head_init( out_head,
     135            4 :                                       sqlite3_column_int64( prepared_statement, 0 ),
     136            4 :                                       (const char*) sqlite3_column_text( prepared_statement, 1 ),
     137            4 :                                       (const char*) sqlite3_column_text( prepared_statement, 2 )
     138              :                                     );
     139              : 
     140            4 :             data_head_trace( out_head );
     141              : 
     142            4 :             sqlite_err = sqlite3_step( prepared_statement );
     143            4 :             if ( SQLITE_DONE != sqlite_err )
     144              :             {
     145            0 :                 U8_LOG_ERROR_INT( "sqlite3_step not done yet:", sqlite_err );
     146            0 :                 result |= U8_ERROR_DB_STRUCTURE;
     147              :             }
     148              :         }
     149              : 
     150            5 :         result |= data_database_finalize_statement( (*this_).database, prepared_statement );
     151              :     }
     152              : 
     153            5 :     U8_TRACE_END_ERR( result );
     154            5 :     return( result );
     155              : }
     156              : 
     157              : /*!
     158              :  *  \brief prefix search statement to find a head value by key
     159              :  */
     160              : static const char *const DATA_DATABASE_HEAD_SELECT_HEAD_BY_KEY_PREFIX =
     161              :     "SELECT id,key,value FROM head WHERE key=";
     162              : 
     163              : /*!
     164              :  *  \brief postfix search statement to find a head value by key
     165              :  */
     166              : static const char *const DATA_DATABASE_HEAD_SELECT_HEAD_BY_KEY_POSTFIX = ";";
     167              : 
     168            6 : u8_error_t data_database_head_read_value_by_key ( data_database_head_t *this_, const char *key, data_head_t *out_head )
     169              : {
     170            6 :     U8_TRACE_BEGIN();
     171            6 :     assert( key != NULL );
     172            6 :     assert( out_head != NULL );
     173            6 :     u8_error_t result = U8_ERROR_NONE;
     174              : 
     175              :     /* create an sql command */
     176              :     {
     177            6 :         result |= universal_memory_output_stream_reset( &((*this_).plain_out) );
     178              : 
     179            6 :         result |= utf8stream_writer_write_str( &((*this_).plain), DATA_DATABASE_HEAD_SELECT_HEAD_BY_KEY_PREFIX );
     180            6 :         result |= utf8stream_writer_write_str( &((*this_).plain), DATA_DATABASE_HEAD_STRING_VALUE_START );
     181            6 :         result |= utf8stream_writer_flush( &((*this_).plain) );
     182            6 :         result |= utf8stream_writer_write_str( &((*this_).escaped), key );
     183            6 :         result |= utf8stream_writer_flush( &((*this_).escaped) );
     184            6 :         result |= utf8stream_writer_write_str( &((*this_).plain), DATA_DATABASE_HEAD_STRING_VALUE_END );
     185            6 :         result |= utf8stream_writer_write_str( &((*this_).plain), DATA_DATABASE_HEAD_SELECT_HEAD_BY_KEY_POSTFIX );
     186              : 
     187            6 :         result |= utf8stream_writer_flush( &((*this_).plain) );  /* enforces 0-termination on (*this_).plain_out */
     188              :     }
     189              : 
     190            6 :     if ( result == U8_ERROR_NONE )
     191              :     {
     192            6 :         const char *const sql_cmd = &((*this_).private_sql_buffer[0]);
     193              :         sqlite3_stmt *prepared_statement;
     194              :         int sqlite_err;
     195              : 
     196           18 :         result |= data_database_prepare_statement( (*this_).database,
     197              :                                                    sql_cmd,
     198            6 :                                                    utf8string_get_length( sql_cmd ) + sizeof( char ),
     199              :                                                    &prepared_statement
     200              :                                                  );
     201              : 
     202            6 :         U8_TRACE_INFO( "sqlite3_step()" );
     203            6 :         sqlite_err = sqlite3_step( prepared_statement );
     204            6 :         if ( SQLITE_ROW != sqlite_err )
     205              :         {
     206              :             /* Do not log this incident, the caller may not expect to find a row. */
     207            4 :             U8_TRACE_INFO_STR( "sqlite3_step did not find a row for key", key );
     208            4 :             result |= U8_ERROR_NOT_FOUND;
     209              :         }
     210              : 
     211            6 :         if ( SQLITE_ROW == sqlite_err )
     212              :         {
     213            2 :             result |= data_head_init( out_head,
     214            2 :                                       sqlite3_column_int64( prepared_statement, 0 ),
     215            2 :                                       (const char*) sqlite3_column_text( prepared_statement, 1 ),
     216            2 :                                       (const char*) sqlite3_column_text( prepared_statement, 2 )
     217              :                                     );
     218              : 
     219            2 :             data_head_trace( out_head );
     220              : 
     221            2 :             sqlite_err = sqlite3_step( prepared_statement );
     222            2 :             if ( SQLITE_DONE != sqlite_err )
     223              :             {
     224            0 :                 U8_LOG_ERROR_INT( "sqlite3_step not done yet:", sqlite_err );
     225            0 :                 result |= U8_ERROR_DB_STRUCTURE;
     226              :             }
     227              :         }
     228              : 
     229            6 :         result |= data_database_finalize_statement( (*this_).database, prepared_statement );
     230              :     }
     231              : 
     232            6 :     U8_TRACE_END_ERR( result );
     233            6 :     return( result );
     234              : }
     235              : 
     236              : /*!
     237              :  *  \brief prefix string constant to insert a head value
     238              :  */
     239              : static const char *const DATA_DATABASE_HEAD_INSERT_HEAD_PREFIX =
     240              :     "INSERT INTO head (key,value) VALUES (";
     241              : 
     242              : /*!
     243              :  *  \brief postfix string constant to insert a head value
     244              :  */
     245              : static const char *const DATA_DATABASE_HEAD_INSERT_HEAD_POSTFIX = ");";
     246              : 
     247            8 : u8_error_t data_database_head_create_value ( data_database_head_t *this_, const data_head_t *head, data_row_t* out_new_id )
     248              : {
     249            8 :     U8_TRACE_BEGIN();
     250            8 :     assert( head != NULL );
     251            8 :     u8_error_t result = U8_ERROR_NONE;
     252              : 
     253              :     /* create an sql command */
     254              :     {
     255            8 :         result |= universal_memory_output_stream_reset( &((*this_).plain_out) );
     256              : 
     257            8 :         result |= utf8stream_writer_write_str( &((*this_).plain), DATA_DATABASE_HEAD_INSERT_HEAD_PREFIX );
     258            8 :         result |= utf8stream_writer_write_str( &((*this_).plain), DATA_DATABASE_HEAD_STRING_VALUE_START );
     259            8 :         result |= utf8stream_writer_flush( &((*this_).plain) );
     260            8 :         utf8string_t *const key = data_head_get_key_const( head );
     261            8 :         result |= utf8stream_writer_write_str( &((*this_).escaped), key );
     262            8 :         result |= utf8stream_writer_flush( &((*this_).escaped) );
     263            8 :         result |= utf8stream_writer_write_str( &((*this_).plain), DATA_DATABASE_HEAD_STRING_VALUE_END );
     264            8 :         result |= utf8stream_writer_write_str( &((*this_).plain), DATA_DATABASE_HEAD_INSERT_VALUE_SEPARATOR );
     265            8 :         result |= utf8stream_writer_write_str( &((*this_).plain), DATA_DATABASE_HEAD_STRING_VALUE_START );
     266            8 :         result |= utf8stream_writer_flush( &((*this_).plain) );
     267            8 :         utf8string_t *const value = data_head_get_value_const( head );
     268            8 :         result |= utf8stream_writer_write_str( &((*this_).escaped), value );
     269            8 :         result |= utf8stream_writer_flush( &((*this_).escaped) );
     270            8 :         result |= utf8stream_writer_write_str( &((*this_).plain), DATA_DATABASE_HEAD_STRING_VALUE_END );
     271            8 :         result |= utf8stream_writer_write_str( &((*this_).plain), DATA_DATABASE_HEAD_INSERT_HEAD_POSTFIX );
     272              : 
     273            8 :         result |= utf8stream_writer_flush( &((*this_).plain) );  /* enforces 0-termination on (*this_).plain_out */
     274              :     }
     275              : 
     276            8 :     if ( result == U8_ERROR_NONE )
     277              :     {
     278            8 :         const char *const sql_cmd = &((*this_).private_sql_buffer[0]);
     279            8 :         data_row_t new_id = DATA_ROW_VOID;
     280              : 
     281            8 :         result |= data_database_transaction_begin ( (*this_).database );
     282            8 :         result |= data_database_in_transaction_create( (*this_).database, sql_cmd, &new_id );
     283            8 :         result |= data_database_transaction_commit ( (*this_).database );
     284              : 
     285            8 :         U8_LOG_EVENT_INT( "sqlite3_exec: INSERT INTO head ... ->", new_id );  /* do not log confidential information, only id */
     286            8 :         if ( NULL != out_new_id )
     287              :         {
     288            5 :             *out_new_id = new_id;
     289              :         }
     290              :     }
     291              : 
     292            8 :     U8_TRACE_END_ERR( result );
     293            8 :     return( result );
     294              : }
     295              : 
     296              : /*!
     297              :  *  \brief prefix string constant to delete a head value
     298              :  */
     299              : static const char *const ATA_DATABASE_HEAD_DELETE_HEAD_PREFIX =
     300              :     "DELETE FROM head WHERE (id=";
     301              : 
     302              : /*!
     303              :  *  \brief postfix string constant to delete a head value
     304              :  */
     305              : static const char *const DATA_DATABASE_HEAD_DELETE_HEAD_POSTFIX = ");";
     306              : 
     307            3 : u8_error_t data_database_head_delete_value ( data_database_head_t *this_, data_row_t obj_id, data_head_t *out_old_head )
     308              : {
     309            3 :     U8_TRACE_BEGIN();
     310            3 :     u8_error_t result = U8_ERROR_NONE;
     311              : 
     312            3 :     result |= data_database_transaction_begin ( (*this_).database );
     313              :     /* Note: out_old_head is NULL if old data shall not be returned */
     314            3 :     if ( NULL != out_old_head )
     315              :     {
     316            2 :         result |= data_database_head_read_value_by_id ( this_, obj_id, out_old_head );
     317              :     }
     318              : 
     319              :     /* create an sql command AFTER(!) reading the old value, same stringbuffer is used. */
     320              :     {
     321            3 :         result |= universal_memory_output_stream_reset( &((*this_).plain_out) );
     322              : 
     323            3 :         result |= utf8stream_writer_write_str( &((*this_).plain), ATA_DATABASE_HEAD_DELETE_HEAD_PREFIX );
     324            3 :         result |= utf8stream_writer_write_int( &((*this_).plain), obj_id );
     325            3 :         result |= utf8stream_writer_write_str( &((*this_).plain), DATA_DATABASE_HEAD_DELETE_HEAD_POSTFIX );
     326              : 
     327            3 :         result |= utf8stream_writer_flush( &((*this_).plain) );  /* enforces 0-termination on (*this_).plain_out */
     328              :     }
     329              : 
     330            3 :     if ( result == U8_ERROR_NONE )
     331              :     {
     332            2 :         const char *const sql_cmd = &((*this_).private_sql_buffer[0]);
     333              : 
     334            2 :         result |= data_database_in_transaction_execute( (*this_).database, sql_cmd );
     335            2 :         U8_LOG_EVENT_INT( "sqlite3_exec: DELETE FROM head ... ->", obj_id );  /* do not log confidential information, only id */
     336              :     }
     337              : 
     338            3 :     result |= data_database_transaction_commit ( (*this_).database );
     339              : 
     340            3 :     U8_TRACE_END_ERR( result );
     341            3 :     return( result );
     342              : }
     343              : 
     344              : /*!
     345              :  *  \brief prefix string constant to update a head value
     346              :  */
     347              : static const char *DATA_DATABASE_HEAD_UPDATE_HEAD_PREFIX = "UPDATE head SET value=";
     348              : 
     349              : /*!
     350              :  *  \brief infix string constant to update a head value
     351              :  */
     352              : static const char *DATA_DATABASE_HEAD_UPDATE_HEAD_INFIX = " WHERE id=";
     353              : 
     354              : /*!
     355              :  *  \brief postfix string constant to update a head value
     356              :  */
     357              : static const char *DATA_DATABASE_HEAD_UPDATE_HEAD_POSTFIX = ";";
     358              : 
     359            2 : u8_error_t data_database_head_update_value ( data_database_head_t *this_, data_row_t head_id, const char* new_head_value, data_head_t *out_old_head )
     360              : {
     361            2 :     U8_TRACE_BEGIN();
     362            2 :     assert( new_head_value != NULL );
     363            2 :     u8_error_t result = U8_ERROR_NONE;
     364              : 
     365            2 :     result |= data_database_transaction_begin ( (*this_).database );
     366              :     /* Note: out_old_head is NULL if old data shall not be returned */
     367            2 :     if ( NULL != out_old_head )
     368              :     {
     369            1 :         result |= data_database_head_read_value_by_id ( this_, head_id, out_old_head );
     370              :     }
     371              : 
     372              :     /* create an sql command AFTER(!) reading the old value, same stringbuffer is used. */
     373              :     {
     374            2 :         result |= universal_memory_output_stream_reset( &((*this_).plain_out) );
     375              : 
     376            2 :         result |= utf8stream_writer_write_str( &((*this_).plain), DATA_DATABASE_HEAD_UPDATE_HEAD_PREFIX );
     377            2 :         result |= utf8stream_writer_write_str( &((*this_).plain), DATA_DATABASE_HEAD_STRING_VALUE_START );
     378            2 :         result |= utf8stream_writer_flush( &((*this_).plain) );
     379            2 :         result |= utf8stream_writer_write_str( &((*this_).escaped), new_head_value );
     380            2 :         result |= utf8stream_writer_flush( &((*this_).escaped) );
     381            2 :         result |= utf8stream_writer_write_str( &((*this_).plain), DATA_DATABASE_HEAD_STRING_VALUE_END );
     382            2 :         result |= utf8stream_writer_write_str( &((*this_).plain), DATA_DATABASE_HEAD_UPDATE_HEAD_INFIX );
     383            2 :         result |= utf8stream_writer_write_int( &((*this_).plain), head_id );
     384            2 :         result |= utf8stream_writer_write_str( &((*this_).plain), DATA_DATABASE_HEAD_UPDATE_HEAD_POSTFIX );
     385              : 
     386            2 :         result |= utf8stream_writer_flush( &((*this_).plain) );  /* enforces 0-termination on (*this_).plain_out */
     387              :     }
     388              : 
     389            2 :     if ( result == U8_ERROR_NONE )
     390              :     {
     391            2 :         const char *const sql_cmd = &((*this_).private_sql_buffer[0]);
     392              : 
     393            2 :         result |= data_database_in_transaction_execute( (*this_).database, sql_cmd );
     394            2 :         U8_LOG_EVENT_INT( "sqlite3_exec: UPDATE head ... ->", head_id );  /* do not log confidential information, only id */
     395              :     }
     396              : 
     397            2 :     result |= data_database_transaction_commit ( (*this_).database );
     398              : 
     399            2 :     U8_TRACE_END_ERR( result );
     400            2 :     return( result );
     401              : }
     402              : 
     403              : 
     404              : /*
     405              : Copyright 2024-2025 Andreas Warnke
     406              : 
     407              : Licensed under the Apache License, Version 2.0 (the "License");
     408              : you may not use this file except in compliance with the License.
     409              : You may obtain a copy of the License at
     410              : 
     411              :     http://www.apache.org/licenses/LICENSE-2.0
     412              : 
     413              : Unless required by applicable law or agreed to in writing, software
     414              : distributed under the License is distributed on an "AS IS" BASIS,
     415              : WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     416              : See the License for the specific language governing permissions and
     417              : limitations under the License.
     418              : */
        

Generated by: LCOV version 2.0-1