Line data Source code
1 : /* File: data_database_text_search.c; Copyright and License: see below */
2 :
3 : #include "storage/data_database_text_search.h"
4 : #include "u8/u8_trace.h"
5 : #include "u8/u8_log.h"
6 : #include "utf8stringbuf/utf8stringbuf.h"
7 : #include "u8stream/universal_output_stream.h"
8 : #include "u8stream/universal_memory_output_stream.h"
9 : #include "u8stream/universal_escaping_output_stream.h"
10 : #include <sqlite3.h>
11 : #include <assert.h>
12 :
13 : /*!
14 : * \brief translation table to encode strings for usage in LIKE search string literals
15 : */
16 : static const char *const DATA_DATABASE_TEXT_SEARCH_SQL_ENCODE[][2] = {
17 : { "%", "\\%" },
18 : { "_", "\\_" },
19 : { "\\", "\\\\" },
20 : { NULL, NULL }
21 : };
22 : static const char DATA_DATABASE_TEXT_SEARCH_SQL_LIKE_DELIMITER = '%';
23 :
24 0 : u8_error_t data_database_text_search_init( data_database_text_search_t *this_, data_database_t *database )
25 : {
26 0 : U8_TRACE_BEGIN();
27 0 : assert( NULL != database );
28 0 : u8_error_t result = U8_ERROR_NONE;
29 :
30 0 : (*this_).database = database;
31 0 : (*this_).is_open = false;
32 :
33 0 : data_database_listener_init( &((*this_).me_as_listener), this_, (void (*)(void*,data_database_listener_signal_t)) &data_database_text_search_db_change_callback );
34 0 : data_database_add_db_listener( database, &((*this_).me_as_listener) );
35 :
36 0 : if ( data_database_is_open( database ) )
37 : {
38 : /* if the database is open, open also the reader */
39 0 : result |= data_database_text_search_private_open( this_ );
40 : }
41 :
42 0 : data_rules_init ( &((*this_).data_rules) );
43 :
44 0 : U8_TRACE_END_ERR(result);
45 0 : return result;
46 : }
47 :
48 0 : u8_error_t data_database_text_search_destroy( data_database_text_search_t *this_ )
49 : {
50 0 : U8_TRACE_BEGIN();
51 0 : u8_error_t result = U8_ERROR_NONE;
52 :
53 0 : data_rules_destroy ( &((*this_).data_rules) );
54 :
55 0 : if ( (*this_).is_open )
56 : {
57 0 : result |= data_database_text_search_private_close( this_ );
58 : }
59 :
60 0 : data_database_remove_db_listener( (*this_).database, &((*this_).me_as_listener) );
61 :
62 0 : (*this_).database = NULL;
63 :
64 0 : U8_TRACE_END_ERR(result);
65 0 : return result;
66 : }
67 :
68 0 : void data_database_text_search_db_change_callback( data_database_text_search_t *this_, data_database_listener_signal_t signal_id )
69 : {
70 0 : U8_TRACE_BEGIN();
71 0 : u8_error_t result = U8_ERROR_NONE;
72 :
73 0 : switch ( signal_id )
74 : {
75 0 : case DATA_DATABASE_LISTENER_SIGNAL_PREPARE_CLOSE:
76 : {
77 0 : U8_TRACE_INFO( "DATA_DATABASE_LISTENER_SIGNAL_PREPARE_CLOSE" );
78 0 : if ( (*this_).is_open )
79 : {
80 0 : result |= data_database_text_search_private_close( this_ );
81 : }
82 : }
83 0 : break;
84 :
85 0 : case DATA_DATABASE_LISTENER_SIGNAL_DB_OPENED:
86 : {
87 0 : U8_TRACE_INFO( "DATA_DATABASE_LISTENER_SIGNAL_DB_OPENED" );
88 0 : if ( (*this_).is_open )
89 : {
90 0 : result |= data_database_text_search_private_close( this_ );
91 : }
92 0 : result |= data_database_text_search_private_open( this_ );
93 : }
94 0 : break;
95 :
96 0 : default:
97 : {
98 0 : U8_LOG_ERROR( "unexpected data_database_listener_signal_t" );
99 : }
100 : }
101 :
102 0 : U8_TRACE_END();
103 0 : }
104 :
105 0 : u8_error_t data_database_text_search_get_objects_by_textfragment( data_database_text_search_t *this_,
106 : const char *textfragment,
107 : data_search_result_list_t *io_results )
108 : {
109 0 : U8_TRACE_BEGIN();
110 0 : assert( NULL != io_results );
111 0 : assert( NULL != textfragment );
112 0 : const unsigned int text_len = utf8string_get_length( textfragment );
113 0 : u8_error_t result = U8_ERROR_NONE;
114 :
115 : /* escape-encode textfragment */
116 0 : char like_search[64] = "";
117 : universal_memory_output_stream_t mem_out;
118 0 : universal_memory_output_stream_init( &mem_out,
119 : like_search,
120 : sizeof(like_search),
121 : UNIVERSAL_MEMORY_OUTPUT_STREAM_0TERM_UTF8
122 : );
123 0 : const bool search_empty = ( 0 == text_len );
124 0 : if ( search_empty )
125 : {
126 : /* no wildcards and no escaping if search string is empty */
127 : }
128 : else
129 : {
130 0 : result |= universal_memory_output_stream_write( &mem_out,
131 : &DATA_DATABASE_TEXT_SEARCH_SQL_LIKE_DELIMITER,
132 : sizeof(DATA_DATABASE_TEXT_SEARCH_SQL_LIKE_DELIMITER)
133 : );
134 : {
135 0 : universal_output_stream_t* stream_out = universal_memory_output_stream_get_output_stream( &mem_out );
136 : universal_escaping_output_stream_t esc_out;
137 0 : universal_escaping_output_stream_init( &esc_out, &DATA_DATABASE_TEXT_SEARCH_SQL_ENCODE, stream_out );
138 0 : result |= universal_escaping_output_stream_write( &esc_out, textfragment, text_len );
139 0 : result |= universal_escaping_output_stream_flush( &esc_out );
140 0 : result |= universal_escaping_output_stream_destroy( &esc_out );
141 : }
142 0 : result |= universal_memory_output_stream_write( &mem_out,
143 : &DATA_DATABASE_TEXT_SEARCH_SQL_LIKE_DELIMITER,
144 : sizeof(DATA_DATABASE_TEXT_SEARCH_SQL_LIKE_DELIMITER)
145 : );
146 : }
147 0 : result |= universal_memory_output_stream_destroy( &mem_out );
148 :
149 0 : U8_TRACE_INFO_STR( "LIKE SEARCH:", like_search );
150 0 : if ( result != U8_ERROR_NONE )
151 : {
152 0 : U8_LOG_WARNING_STR( "error at escaping the search string", textfragment );
153 : }
154 : else
155 : {
156 : /* search for the prepared pattern. In case of empty, search for a non-existing pattern in the type fields */
157 0 : const char *const search_name = search_empty ? "" : like_search;
158 0 : const char *const search_type = search_empty ? "\n" : like_search;
159 0 : const char *const search_descr = search_empty ? "" : like_search;
160 :
161 0 : result |= data_database_text_search_private_get_diagrams_by_textfragment( this_,
162 : search_name,
163 : search_type,
164 : search_descr,
165 : io_results
166 : );
167 :
168 0 : result |= data_database_text_search_private_get_classifiers_by_textfragment( this_,
169 : search_name,
170 : search_type,
171 : search_descr,
172 : io_results
173 : );
174 :
175 0 : result |= data_database_text_search_private_get_features_by_textfragment( this_,
176 : search_name,
177 : search_type,
178 : search_descr,
179 : io_results
180 : );
181 :
182 0 : result |= data_database_text_search_private_get_relationships_by_textfragment( this_,
183 : search_name,
184 : search_type,
185 : search_descr,
186 : io_results
187 : );
188 : }
189 :
190 0 : U8_TRACE_END_ERR( result );
191 0 : return result;
192 : }
193 :
194 : /* ================================ DIAGRAM ================================ */
195 :
196 : /*!
197 : * \brief predefined search statement to find diagrams by textfragment
198 : *
199 : * note: name is needed for debugging only
200 : */
201 : static const char data_database_text_search_SELECT_DIAGRAM_BY_TEXTFRAGMENT[] =
202 : "SELECT id,diagram_type,name "
203 : "FROM diagrams "
204 : "WHERE name LIKE ? ESCAPE \"\\\" "
205 : "OR stereotype LIKE ? ESCAPE \"\\\" "
206 : "OR description LIKE ? ESCAPE \"\\\";";
207 :
208 : /*!
209 : * \brief the column id of the result where this parameter is stored: id
210 : */
211 : static const int RESULT_DIAGRAM_ID_COLUMN = 0;
212 :
213 : /*!
214 : * \brief the column id of the result where this parameter is stored: diagram_type
215 : */
216 : static const int RESULT_DIAGRAM_TYPE_COLUMN = 1;
217 :
218 : /*!
219 : * \brief the column id of the result where this parameter is stored: name
220 : */
221 : static const int RESULT_DIAGRAM_NAME_COLUMN = 2;
222 :
223 :
224 0 : u8_error_t data_database_text_search_private_get_diagrams_by_textfragment( data_database_text_search_t *this_,
225 : const char *name_fragment,
226 : const char *stereo_fragment,
227 : const char *descr_fragment,
228 : data_search_result_list_t *io_results )
229 : {
230 0 : U8_TRACE_BEGIN();
231 0 : assert( NULL != io_results );
232 0 : assert( NULL != name_fragment );
233 0 : assert( NULL != stereo_fragment );
234 0 : assert( NULL != descr_fragment );
235 0 : u8_error_t result = U8_ERROR_NONE;
236 :
237 : int sqlite_err;
238 : sqlite3_stmt *prepared_statement;
239 :
240 0 : if ( (*this_).is_open )
241 : {
242 0 : prepared_statement = (*this_).statement_diagram_ids_by_textfragment;
243 :
244 0 : result |= data_database_text_search_private_bind_three_texts_to_statement( this_,
245 : prepared_statement,
246 : name_fragment,
247 : stereo_fragment,
248 : descr_fragment
249 : );
250 :
251 0 : sqlite_err = SQLITE_ROW;
252 0 : for ( uint32_t row_index = 0; (SQLITE_ROW == sqlite_err) && (U8_ERROR_NONE == result); row_index ++ )
253 : {
254 0 : U8_TRACE_INFO_INT( "sqlite3_step():", (row_index+1) );
255 0 : sqlite_err = sqlite3_step( prepared_statement );
256 0 : if ( SQLITE_DONE == sqlite_err )
257 : {
258 0 : U8_TRACE_INFO( "sqlite3_step finished: SQLITE_DONE" );
259 : }
260 0 : else if ( SQLITE_ROW != sqlite_err )
261 : {
262 0 : U8_LOG_ERROR_INT( "sqlite3_step failed:", sqlite_err );
263 0 : result |= U8_ERROR_AT_DB;
264 : }
265 : else
266 : {
267 : data_search_result_t current_result;
268 :
269 0 : data_search_result_init_diagram( ¤t_result,
270 0 : sqlite3_column_int64( prepared_statement, RESULT_DIAGRAM_ID_COLUMN ),
271 0 : sqlite3_column_int( prepared_statement, RESULT_DIAGRAM_TYPE_COLUMN ),
272 0 : (const char*) sqlite3_column_text( prepared_statement, RESULT_DIAGRAM_NAME_COLUMN )
273 : );
274 :
275 0 : const u8_error_t err_full = data_search_result_list_add( io_results, ¤t_result );
276 0 : if ( err_full != U8_ERROR_NONE )
277 : {
278 0 : U8_LOG_ANOMALY_INT( "io_results list full:", data_search_result_list_get_length( io_results ) );
279 0 : result |= err_full;
280 : }
281 :
282 0 : data_search_result_trace( ¤t_result );
283 0 : data_search_result_destroy( ¤t_result );
284 : }
285 : }
286 : }
287 : else
288 : {
289 0 : result |= U8_ERROR_NO_DB;
290 0 : U8_TRACE_INFO( "Database not open, cannot request data." );
291 : }
292 :
293 0 : U8_TRACE_END_ERR( result );
294 0 : return result;
295 : }
296 :
297 :
298 : /* ================================ CLASSIFIER ================================ */
299 :
300 : /*!
301 : * \brief predefined search statement to find classifiers by textfragment
302 : *
303 : * note: classifiers.name is needed for debugging only
304 : */
305 : static const char data_database_text_search_SELECT_CLASSIFIER_BY_TEXTFRAGMENT[] =
306 : "SELECT classifiers.id,classifiers.main_type,classifiers.name,diagrams.id "
307 : "FROM classifiers "
308 : "INNER JOIN diagramelements ON diagramelements.classifier_id=classifiers.id "
309 : "INNER JOIN diagrams ON diagramelements.diagram_id=diagrams.id "
310 : "WHERE classifiers.name LIKE ? ESCAPE \"\\\" "
311 : "OR classifiers.stereotype LIKE ? ESCAPE \"\\\" "
312 : "OR classifiers.description LIKE ? ESCAPE \"\\\" "
313 : "GROUP BY classifiers.id,diagrams.id;"; /* no duplicates if a classifier is twice in a diagram */
314 :
315 : /*!
316 : * \brief the column id of the result where this parameter is stored: id
317 : */
318 : static const int RESULT_CLASSIFIER_ID_COLUMN = 0;
319 :
320 : /*!
321 : * \brief the column id of the result where this parameter is stored: main_type
322 : */
323 : static const int RESULT_CLASSIFIER_MAIN_TYPE_COLUMN = 1;
324 :
325 : /*!
326 : * \brief the column id of the result where this parameter is stored: name
327 : */
328 : static const int RESULT_CLASSIFIER_NAME_COLUMN = 2;
329 :
330 : /*!
331 : * \brief the column id of the result where this parameter is stored: diagrams.id
332 : */
333 : static const int RESULT_CLASSIFIER_DIAGRAM_ID_COLUMN = 3;
334 :
335 0 : u8_error_t data_database_text_search_private_get_classifiers_by_textfragment( data_database_text_search_t *this_,
336 : const char *name_fragment,
337 : const char *stereo_fragment,
338 : const char *descr_fragment,
339 : data_search_result_list_t *io_results )
340 : {
341 0 : U8_TRACE_BEGIN();
342 0 : assert( NULL != io_results );
343 0 : assert( NULL != name_fragment );
344 0 : assert( NULL != stereo_fragment );
345 0 : assert( NULL != descr_fragment );
346 0 : u8_error_t result = U8_ERROR_NONE;
347 :
348 : int sqlite_err;
349 : sqlite3_stmt *prepared_statement;
350 :
351 0 : if ( (*this_).is_open )
352 : {
353 0 : prepared_statement = (*this_).statement_classifier_ids_by_textfragment;
354 :
355 0 : result |= data_database_text_search_private_bind_three_texts_to_statement( this_,
356 : prepared_statement,
357 : name_fragment,
358 : stereo_fragment,
359 : descr_fragment
360 : );
361 :
362 0 : sqlite_err = SQLITE_ROW;
363 0 : for ( uint32_t row_index = 0; (SQLITE_ROW == sqlite_err) && (U8_ERROR_NONE == result); row_index ++ )
364 : {
365 0 : U8_TRACE_INFO_INT( "sqlite3_step():", (row_index+1) );
366 0 : sqlite_err = sqlite3_step( prepared_statement );
367 0 : if ( SQLITE_DONE == sqlite_err )
368 : {
369 0 : U8_TRACE_INFO( "sqlite3_step finished: SQLITE_DONE" );
370 : }
371 0 : else if ( SQLITE_ROW != sqlite_err )
372 : {
373 0 : U8_LOG_ERROR_INT( "sqlite3_step failed:", sqlite_err );
374 0 : result |= U8_ERROR_AT_DB;
375 : }
376 : else
377 : {
378 : data_search_result_t current_result;
379 :
380 0 : data_search_result_init_classifier( ¤t_result,
381 0 : sqlite3_column_int64( prepared_statement, RESULT_CLASSIFIER_ID_COLUMN ),
382 0 : sqlite3_column_int( prepared_statement, RESULT_CLASSIFIER_MAIN_TYPE_COLUMN ),
383 0 : (const char*) sqlite3_column_text( prepared_statement, RESULT_CLASSIFIER_NAME_COLUMN ),
384 0 : sqlite3_column_int64( prepared_statement, RESULT_CLASSIFIER_DIAGRAM_ID_COLUMN )
385 : );
386 :
387 0 : const u8_error_t err_full = data_search_result_list_add( io_results, ¤t_result );
388 0 : if ( err_full != U8_ERROR_NONE )
389 : {
390 0 : U8_LOG_ANOMALY_INT( "io_results list full:", data_search_result_list_get_length( io_results ) );
391 0 : result |= err_full;
392 : }
393 :
394 0 : data_search_result_trace( ¤t_result );
395 0 : data_search_result_destroy( ¤t_result );
396 : }
397 : }
398 : }
399 : else
400 : {
401 0 : result |= U8_ERROR_NO_DB;
402 0 : U8_TRACE_INFO( "Database not open, cannot request data." );
403 : }
404 :
405 0 : U8_TRACE_END_ERR( result );
406 0 : return result;
407 : }
408 :
409 : /* ================================ DIAGRAMELEMENT ================================ */
410 :
411 : /* ================================ FEATURE ================================ */
412 :
413 : /*!
414 : * \brief predefined search statement to find features by textfragment
415 : *
416 : * note: features.key is needed for debugging only
417 : */
418 : static const char data_database_text_search_SELECT_FEATURE_BY_TEXTFRAGMENT[] =
419 : "SELECT DISTINCT features.id,features.main_type,features.key,features.classifier_id,"
420 : "classifiers.main_type,diagrams.id,diagrams.diagram_type "
421 : "FROM features "
422 : "INNER JOIN classifiers ON features.classifier_id=classifiers.id "
423 : "INNER JOIN diagramelements ON diagramelements.classifier_id=classifiers.id "
424 : "INNER JOIN diagrams ON diagramelements.diagram_id=diagrams.id "
425 : "WHERE features.key LIKE ? ESCAPE \"\\\" "
426 : "OR features.value LIKE ? ESCAPE \"\\\" "
427 : "OR features.description LIKE ? ESCAPE \"\\\" "
428 : "GROUP BY features.id,diagrams.id;"; /* no duplicates if a classifier is twice in a diagram */
429 :
430 : /*!
431 : * \brief the column id of the result where this parameter is stored: id
432 : */
433 : static const int RESULT_FEATURE_ID_COLUMN = 0;
434 :
435 : /*!
436 : * \brief the column id of the result where this parameter is stored: main_type
437 : */
438 : static const int RESULT_FEATURE_MAIN_TYPE_COLUMN = 1;
439 :
440 : /*!
441 : * \brief the column id of the result where this parameter is stored: key
442 : */
443 : static const int RESULT_FEATURE_KEY_COLUMN = 2;
444 :
445 : /*!
446 : * \brief the column id of the result where this parameter is stored: classifiers.id
447 : */
448 : static const int RESULT_FEATURE_CLASSIFIER_ID_COLUMN = 3;
449 :
450 : /*!
451 : * \brief the column id of the result where this parameter is stored: classifiers.main_type
452 : */
453 : static const int RESULT_FEATURE_CLASSIFIER_MAIN_TYPE_COLUMN = 4;
454 :
455 : /*!
456 : * \brief the column id of the result where this parameter is stored: diagrams.id
457 : */
458 : static const int RESULT_FEATURE_DIAGRAM_ID_COLUMN = 5;
459 :
460 : /*!
461 : * \brief the column id of the result where this parameter is stored: diagrams.diagram_type
462 : */
463 : static const int RESULT_FEATURE_DIAGRAM_TYPE_COLUMN = 6;
464 :
465 0 : u8_error_t data_database_text_search_private_get_features_by_textfragment( data_database_text_search_t *this_,
466 : const char *key_fragment,
467 : const char *value_fragment,
468 : const char *descr_fragment,
469 : data_search_result_list_t *io_results )
470 : {
471 0 : U8_TRACE_BEGIN();
472 0 : assert( NULL != io_results );
473 0 : assert( NULL != key_fragment );
474 0 : assert( NULL != value_fragment );
475 0 : assert( NULL != descr_fragment );
476 0 : u8_error_t result = U8_ERROR_NONE;
477 :
478 : int sqlite_err;
479 : sqlite3_stmt *prepared_statement;
480 :
481 0 : if ( (*this_).is_open )
482 : {
483 0 : prepared_statement = (*this_).statement_feature_ids_by_textfragment;
484 :
485 0 : result |= data_database_text_search_private_bind_three_texts_to_statement( this_,
486 : prepared_statement,
487 : key_fragment,
488 : value_fragment,
489 : descr_fragment
490 : );
491 :
492 0 : sqlite_err = SQLITE_ROW;
493 0 : for ( uint32_t row_index = 0; (SQLITE_ROW == sqlite_err) && (U8_ERROR_NONE == result); row_index ++ )
494 : {
495 0 : U8_TRACE_INFO_INT( "sqlite3_step():", (row_index+1) );
496 0 : sqlite_err = sqlite3_step( prepared_statement );
497 0 : if ( SQLITE_DONE == sqlite_err )
498 : {
499 0 : U8_TRACE_INFO( "sqlite3_step finished: SQLITE_DONE" );
500 : }
501 0 : else if ( SQLITE_ROW != sqlite_err )
502 : {
503 0 : U8_LOG_ERROR_INT( "sqlite3_step failed:", sqlite_err );
504 0 : result |= U8_ERROR_AT_DB;
505 : }
506 : else
507 : {
508 : data_search_result_t current_result;
509 :
510 0 : data_search_result_init_feature( ¤t_result,
511 0 : sqlite3_column_int64( prepared_statement, RESULT_FEATURE_ID_COLUMN ),
512 0 : sqlite3_column_int( prepared_statement, RESULT_FEATURE_MAIN_TYPE_COLUMN ),
513 0 : (const char*) sqlite3_column_text( prepared_statement, RESULT_FEATURE_KEY_COLUMN ),
514 0 : sqlite3_column_int64( prepared_statement, RESULT_FEATURE_CLASSIFIER_ID_COLUMN ),
515 0 : sqlite3_column_int64( prepared_statement, RESULT_FEATURE_DIAGRAM_ID_COLUMN )
516 : );
517 0 : const data_type_t current_type = data_search_result_get_match_type( ¤t_result );
518 0 : const data_feature_type_t f_type = data_type_get_feature_type( ¤t_type );
519 0 : const data_classifier_type_t c_type = sqlite3_column_int( prepared_statement, RESULT_FEATURE_CLASSIFIER_MAIN_TYPE_COLUMN );
520 0 : const data_diagram_type_t d_type = sqlite3_column_int( prepared_statement, RESULT_FEATURE_DIAGRAM_TYPE_COLUMN );
521 0 : U8_TRACE_INFO_INT( "- c_type:", c_type );
522 0 : U8_TRACE_INFO_INT( "- d_type:", d_type );
523 :
524 0 : bool filter = false;
525 0 : const bool is_scenario_feat = data_rules_feature_is_scenario_cond( &((*this_).data_rules), f_type );
526 0 : if ( is_scenario_feat )
527 : {
528 : /* text search never returns lifelines, independant of data_rules_diagram_shows_scenario_features */
529 0 : filter = true;
530 : }
531 : else
532 : {
533 : /* evaluate filter */
534 0 : const bool vis_by_classifier = data_rules_classifier_has_uncond_features ( &((*this_).data_rules), c_type );
535 0 : const bool vis_by_diagram = data_rules_diagram_shows_uncond_features ( &((*this_).data_rules), d_type );
536 0 : filter = !( vis_by_classifier && vis_by_diagram );
537 : }
538 :
539 0 : if ( ! filter )
540 : {
541 0 : const u8_error_t err_full = data_search_result_list_add( io_results, ¤t_result );
542 0 : if ( err_full != U8_ERROR_NONE )
543 : {
544 0 : U8_LOG_ANOMALY_INT( "io_results list full:", data_search_result_list_get_length( io_results ) );
545 0 : result |= err_full;
546 : }
547 : }
548 :
549 0 : data_search_result_trace( ¤t_result );
550 0 : data_search_result_destroy( ¤t_result );
551 : }
552 : }
553 : }
554 : else
555 : {
556 0 : result |= U8_ERROR_NO_DB;
557 0 : U8_TRACE_INFO( "Database not open, cannot request data." );
558 : }
559 :
560 :
561 0 : U8_TRACE_END_ERR( result );
562 0 : return result;
563 : }
564 :
565 :
566 : /* ================================ RELATIONSHIP ================================ */
567 :
568 : /*!
569 : * \brief predefined search statement to find relationships by textfragment
570 : *
571 : * note: relationships.name is needed for debugging only
572 : */
573 : static const char data_database_text_search_SELECT_RELATIONSHIP_BY_TEXTFRAGMENT[] =
574 : "SELECT DISTINCT relationships.id,relationships.main_type,relationships.name,"
575 : "relationships.from_classifier_id,relationships.to_classifier_id,"
576 : "relationships.from_feature_id,relationships.to_feature_id,"
577 : "diagrams.id,diagrams.diagram_type "
578 : "FROM relationships "
579 : "INNER JOIN diagramelements AS source "
580 : "ON source.classifier_id=relationships.from_classifier_id "
581 : "INNER JOIN diagramelements AS dest "
582 : "ON (dest.classifier_id=relationships.to_classifier_id)AND(dest.diagram_id==source.diagram_id) "
583 : "INNER JOIN diagrams ON source.diagram_id=diagrams.id "
584 : "WHERE relationships.name LIKE ? ESCAPE \"\\\" "
585 : "OR relationships.stereotype LIKE ? ESCAPE \"\\\" "
586 : "OR relationships.description LIKE ? ESCAPE \"\\\" "
587 : "GROUP BY relationships.id,diagrams.id;"; /* no duplicates if a classifier is twice in a diagram */
588 :
589 : /*!
590 : * \brief the column id of the result where this parameter is stored: id
591 : */
592 : static const int RESULT_RELATIONSHIP_ID_COLUMN = 0;
593 :
594 : /*!
595 : * \brief the column id of the result where this parameter is stored: main_type
596 : */
597 : static const int RESULT_RELATIONSHIP_MAIN_TYPE_COLUMN = 1;
598 :
599 : /*!
600 : * \brief the column id of the result where this parameter is stored: name
601 : */
602 : static const int RESULT_RELATIONSHIP_NAME_COLUMN = 2;
603 :
604 : /*!
605 : * \brief the column id of the result where this parameter is stored: from_classifier_id
606 : */
607 : static const int RESULT_RELATIONSHIP_FROM_CLASSIFIER_ID_COLUMN = 3;
608 :
609 : /*!
610 : * \brief the column id of the result where this parameter is stored: to_classifier_id
611 : */
612 : static const int RESULT_RELATIONSHIP_TO_CLASSIFIER_ID_COLUMN = 4;
613 :
614 : /*!
615 : * \brief the column id of the result where this parameter is stored: from_feature_id
616 : */
617 : static const int RESULT_RELATIONSHIP_FROM_FEATURE_ID_COLUMN = 5;
618 :
619 : /*!
620 : * \brief the column id of the result where this parameter is stored: to_feature_id
621 : */
622 : static const int RESULT_RELATIONSHIP_TO_FEATURE_ID_COLUMN = 6;
623 :
624 : /*!
625 : * \brief the column id of the result where this parameter is stored: diagrams.id
626 : */
627 : static const int RESULT_RELATIONSHIP_DIAGRAM_ID_COLUMN = 7;
628 :
629 : /*!
630 : * \brief the column id of the result where this parameter is stored: diagrams.diagram_type
631 : */
632 : static const int RESULT_RELATIONSHIP_DIAGRAM_TYPE_COLUMN = 8;
633 :
634 0 : u8_error_t data_database_text_search_private_get_relationships_by_textfragment( data_database_text_search_t *this_,
635 : const char *name_fragment,
636 : const char *stereo_fragment,
637 : const char *descr_fragment,
638 : data_search_result_list_t *io_results )
639 : {
640 0 : U8_TRACE_BEGIN();
641 0 : assert( NULL != io_results );
642 0 : assert( NULL != name_fragment );
643 0 : assert( NULL != stereo_fragment );
644 0 : assert( NULL != descr_fragment );
645 0 : u8_error_t result = U8_ERROR_NONE;
646 :
647 : int sqlite_err;
648 : sqlite3_stmt *prepared_statement;
649 0 : unsigned int dropped_scenario_rel = 0;
650 :
651 0 : if ( (*this_).is_open )
652 : {
653 0 : prepared_statement = (*this_).statement_relationship_ids_by_textfragment;
654 :
655 0 : result |= data_database_text_search_private_bind_three_texts_to_statement( this_,
656 : prepared_statement,
657 : name_fragment,
658 : stereo_fragment,
659 : descr_fragment
660 : );
661 :
662 0 : sqlite_err = SQLITE_ROW;
663 0 : for ( uint32_t row_index = 0; (SQLITE_ROW == sqlite_err) && (U8_ERROR_NONE == result); row_index ++ )
664 : {
665 0 : U8_TRACE_INFO_INT( "sqlite3_step():", (row_index+1) );
666 0 : sqlite_err = sqlite3_step( prepared_statement );
667 0 : if ( SQLITE_DONE == sqlite_err )
668 : {
669 0 : U8_TRACE_INFO( "sqlite3_step finished: SQLITE_DONE" );
670 : }
671 0 : else if ( SQLITE_ROW != sqlite_err )
672 : {
673 0 : U8_LOG_ERROR_INT( "sqlite3_step failed:", sqlite_err );
674 0 : result |= U8_ERROR_AT_DB;
675 : }
676 : else
677 : {
678 : data_search_result_t current_result;
679 :
680 0 : data_search_result_init_relationship( ¤t_result,
681 0 : sqlite3_column_int64( prepared_statement, RESULT_RELATIONSHIP_ID_COLUMN ),
682 0 : sqlite3_column_int( prepared_statement, RESULT_RELATIONSHIP_MAIN_TYPE_COLUMN ),
683 0 : (const char*) sqlite3_column_text( prepared_statement, RESULT_RELATIONSHIP_NAME_COLUMN ),
684 0 : sqlite3_column_int64( prepared_statement, RESULT_RELATIONSHIP_FROM_CLASSIFIER_ID_COLUMN ),
685 0 : sqlite3_column_int64( prepared_statement, RESULT_RELATIONSHIP_TO_CLASSIFIER_ID_COLUMN ),
686 0 : sqlite3_column_int64( prepared_statement, RESULT_RELATIONSHIP_DIAGRAM_ID_COLUMN )
687 : );
688 0 : const data_row_t from_feat = sqlite3_column_int64( prepared_statement, RESULT_RELATIONSHIP_FROM_FEATURE_ID_COLUMN );
689 0 : const data_row_t to_feat = sqlite3_column_int64( prepared_statement, RESULT_RELATIONSHIP_TO_FEATURE_ID_COLUMN );
690 0 : const data_diagram_type_t d_type = sqlite3_column_int( prepared_statement, RESULT_RELATIONSHIP_DIAGRAM_TYPE_COLUMN );
691 0 : U8_TRACE_INFO_INT( "- from_feat:", from_feat );
692 0 : U8_TRACE_INFO_INT( "- to_feat:", to_feat );
693 0 : U8_TRACE_INFO_INT( "- d_type:", d_type );
694 :
695 0 : bool filter = false;
696 0 : const bool is_scenario_diag = data_rules_diagram_is_scenario ( &((*this_).data_rules), d_type );
697 : /*const bool is_scenario_rel = data_rules_relationship_is_scenario_cond( &((*this_).data_rules), from_feature_type, to_feature_type);*/
698 0 : if ( is_scenario_diag )
699 : {
700 : /* there could be valid relationships that are visible and match the search. */
701 : /* but it is quite difficult to determine if the relationship is visible in the current diagram */
702 : /* --> drop the result and write a not to the log */
703 0 : dropped_scenario_rel ++;
704 0 : filter = true;
705 : }
706 : else
707 : {
708 : /* there could be hidden scenario-typed relationships in a non-scenario diagram. */
709 : /* but it is quite difficult to determine if the relationship is scenario-only */
710 : /* --> show the result anyway */
711 0 : const bool vis_by_diagram = data_rules_diagram_shows_uncond_relationships ( &((*this_).data_rules), d_type );
712 0 : filter = ! vis_by_diagram;
713 : }
714 :
715 0 : if ( ! filter )
716 : {
717 0 : const u8_error_t err_full = data_search_result_list_add( io_results, ¤t_result );
718 0 : if ( err_full != U8_ERROR_NONE )
719 : {
720 0 : U8_LOG_ANOMALY_INT( "io_results list full:", data_search_result_list_get_length( io_results ) );
721 0 : result |= err_full;
722 : }
723 : }
724 :
725 0 : data_search_result_trace( ¤t_result );
726 0 : data_search_result_destroy( ¤t_result );
727 : }
728 : }
729 : }
730 : else
731 : {
732 0 : result |= U8_ERROR_NO_DB;
733 0 : U8_TRACE_INFO( "Database not open, cannot request data." );
734 : }
735 :
736 0 : if ( dropped_scenario_rel != 0 )
737 : {
738 0 : U8_LOG_ANOMALY_INT( "Full text search does not work on relationships in scenario based diagrams. Possibly missed relationships:",
739 : dropped_scenario_rel
740 : );
741 : }
742 :
743 0 : U8_TRACE_END_ERR( result );
744 0 : return result;
745 : }
746 :
747 :
748 : /* ================================ private ================================ */
749 :
750 0 : u8_error_t data_database_text_search_private_open( data_database_text_search_t *this_ )
751 : {
752 0 : U8_TRACE_BEGIN();
753 0 : u8_error_t result = U8_ERROR_NONE;
754 :
755 0 : if ( ! (*this_).is_open )
756 : {
757 0 : result |= data_database_prepare_statement( (*this_).database,
758 : data_database_text_search_SELECT_DIAGRAM_BY_TEXTFRAGMENT,
759 : sizeof( data_database_text_search_SELECT_DIAGRAM_BY_TEXTFRAGMENT ),
760 : &((*this_).statement_diagram_ids_by_textfragment)
761 : );
762 0 : result |= data_database_prepare_statement( (*this_).database,
763 : data_database_text_search_SELECT_CLASSIFIER_BY_TEXTFRAGMENT,
764 : sizeof( data_database_text_search_SELECT_CLASSIFIER_BY_TEXTFRAGMENT ),
765 : &((*this_).statement_classifier_ids_by_textfragment)
766 : );
767 0 : result |= data_database_prepare_statement( (*this_).database,
768 : data_database_text_search_SELECT_FEATURE_BY_TEXTFRAGMENT,
769 : sizeof( data_database_text_search_SELECT_FEATURE_BY_TEXTFRAGMENT ),
770 : &((*this_).statement_feature_ids_by_textfragment)
771 : );
772 0 : result |= data_database_prepare_statement( (*this_).database,
773 : data_database_text_search_SELECT_RELATIONSHIP_BY_TEXTFRAGMENT,
774 : sizeof( data_database_text_search_SELECT_RELATIONSHIP_BY_TEXTFRAGMENT ),
775 : &((*this_).statement_relationship_ids_by_textfragment)
776 : );
777 :
778 0 : (*this_).is_open = true;
779 : }
780 : else
781 : {
782 0 : result |= U8_ERROR_INVALID_REQUEST;
783 0 : U8_LOG_WARNING( "Database is already open." );
784 : }
785 :
786 0 : U8_TRACE_END_ERR(result);
787 0 : return result;
788 : }
789 :
790 0 : u8_error_t data_database_text_search_private_close( data_database_text_search_t *this_ )
791 : {
792 0 : U8_TRACE_BEGIN();
793 0 : u8_error_t result = U8_ERROR_NONE;
794 :
795 0 : if ( (*this_).is_open )
796 : {
797 0 : result |= data_database_finalize_statement( (*this_).database, (*this_).statement_relationship_ids_by_textfragment );
798 0 : result |= data_database_finalize_statement( (*this_).database, (*this_).statement_feature_ids_by_textfragment );
799 0 : result |= data_database_finalize_statement( (*this_).database, (*this_).statement_classifier_ids_by_textfragment );
800 0 : result |= data_database_finalize_statement( (*this_).database, (*this_).statement_diagram_ids_by_textfragment );
801 :
802 0 : (*this_).is_open = false;
803 : }
804 : else
805 : {
806 0 : result |= U8_ERROR_INVALID_REQUEST;
807 0 : U8_LOG_WARNING( "Database was not open." );
808 : }
809 :
810 0 : U8_TRACE_END_ERR(result);
811 0 : return result;
812 : }
813 :
814 :
815 : /*
816 : Copyright 2020-2025 Andreas Warnke
817 :
818 : Licensed under the Apache License, Version 2.0 (the "License");
819 : you may not use this file except in compliance with the License.
820 : You may obtain a copy of the License at
821 :
822 : http://www.apache.org/licenses/LICENSE-2.0
823 :
824 : Unless required by applicable law or agreed to in writing, software
825 : distributed under the License is distributed on an "AS IS" BASIS,
826 : WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
827 : See the License for the specific language governing permissions and
828 : limitations under the License.
829 : */
|