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