|
71 | 71 | File, FileContent, \ |
72 | 72 | Report, ReportAnnotations, ReportAnalysisInfo, ReviewStatus, \ |
73 | 73 | Run, RunHistory, RunHistoryAnalysisInfo, RunLock, \ |
74 | | - SourceComponent, FilterPreset |
| 74 | + SourceComponent, SourceComponentFile, FilterPreset |
75 | 75 |
|
76 | 76 | from .common import exc_to_thrift_reqfail |
77 | 77 | from .thrift_enum_helper import detection_status_enum, \ |
@@ -148,41 +148,79 @@ def slugify(text): |
148 | 148 |
|
149 | 149 |
|
150 | 150 | def get_component_values( |
151 | | - session: DBSession, |
152 | | - component_name: str |
| 151 | + component: SourceComponent |
153 | 152 | ) -> Tuple[List[str], List[str]]: |
154 | 153 | """ |
155 | | - Get component values by component names and returns a tuple where the |
156 | | - first item contains a list path which should be skipped and the second |
157 | | - item contains a list of path which should be included. |
| 154 | + Returns a tuple where the first item contains a list paths that should be |
| 155 | + included and the second item contains a list of paths that should be |
| 156 | + skipped. |
158 | 157 | E.g.: |
159 | 158 | +/a/b/x.cpp |
160 | 159 | +/a/b/y.cpp |
161 | 160 | -/a/b |
162 | 161 | On the above component value this function will return the following: |
163 | | - (['/a/b'], ['/a/b/x.cpp', '/a/b/y.cpp']) |
| 162 | + (['/a/b/x.cpp', '/a/b/y.cpp'], ['/a/b']) |
164 | 163 | """ |
165 | | - components = session.query(SourceComponent) \ |
166 | | - .filter(SourceComponent.name.like(component_name)) \ |
167 | | - .all() |
168 | | - |
169 | | - skip = [] |
170 | 164 | include = [] |
| 165 | + skip = [] |
171 | 166 |
|
172 | | - for component in components: |
173 | | - values = component.value.decode('utf-8').split('\n') |
174 | | - for value in values: |
175 | | - value = value.strip() |
176 | | - if not value: |
177 | | - continue |
| 167 | + values = component.value.decode('utf-8').split('\n') |
| 168 | + for value in values: |
| 169 | + value = value.strip() |
| 170 | + if not value: |
| 171 | + continue |
| 172 | + |
| 173 | + v = value[1:] |
| 174 | + if value[0] == '+': |
| 175 | + include.append(v) |
| 176 | + elif value[0] == '-': |
| 177 | + skip.append(v) |
| 178 | + |
| 179 | + return include, skip |
| 180 | + |
| 181 | + |
| 182 | +def update_source_component_files( |
| 183 | + session: DBSession, |
| 184 | + component: Optional[SourceComponent] = None |
| 185 | +): |
| 186 | + """ |
| 187 | + Refreshes the SourceComponentFile table for a specific source component. |
| 188 | + If `component` is None, then all source components are updated. |
| 189 | + """ |
| 190 | + if component is None: |
| 191 | + all_components = session.query(SourceComponent) |
| 192 | + else: |
| 193 | + all_components = [component] |
| 194 | + |
| 195 | + # 1. Delete existing associations for this component |
| 196 | + session.query(SourceComponentFile) \ |
| 197 | + .filter(SourceComponentFile.source_component_name.in_( |
| 198 | + map(lambda component: component.name, all_components))) \ |
| 199 | + .delete(synchronize_session=False) |
| 200 | + |
| 201 | + for comp in all_components: |
| 202 | + # 2. Re-calculate associations |
| 203 | + include, skip = get_component_values(comp) |
| 204 | + |
| 205 | + file_ids_query = None |
| 206 | + if skip and include: |
| 207 | + include_q, skip_q = get_include_skip_queries(include, skip) |
| 208 | + file_ids_query = include_q.except_(skip_q) |
| 209 | + elif include: |
| 210 | + include_q, _ = get_include_skip_queries(include, []) |
| 211 | + file_ids_query = include_q |
| 212 | + elif skip: |
| 213 | + _, skip_q = get_include_skip_queries([], skip) |
| 214 | + file_ids_query = select(File.id).where(File.id.notin_(skip_q)) |
178 | 215 |
|
179 | | - v = value[1:] |
180 | | - if value[0] == '+': |
181 | | - include.append(v) |
182 | | - elif value[0] == '-': |
183 | | - skip.append(v) |
| 216 | + if file_ids_query is not None: |
| 217 | + file_ids = session.execute(file_ids_query).fetchall() |
184 | 218 |
|
185 | | - return skip, include |
| 219 | + if file_ids: |
| 220 | + session.bulk_insert_mappings( |
| 221 | + SourceComponentFile, |
| 222 | + [{'source_component_name': comp.name, |
| 223 | + 'file_id': fid[0]} for fid in file_ids]) |
186 | 224 |
|
187 | 225 |
|
188 | 226 | def process_report_filter( |
@@ -508,18 +546,10 @@ def get_source_component_file_query( |
508 | 546 | component_name: str |
509 | 547 | ): |
510 | 548 | """ Get filter query for a single source component. """ |
511 | | - skip, include = get_component_values(session, component_name) |
512 | | - |
513 | | - if skip and include: |
514 | | - include_q, skip_q = get_include_skip_queries(include, skip) |
515 | | - return File.id.in_(include_q.except_(skip_q)) |
516 | | - |
517 | | - if include: |
518 | | - return or_(*[File.filepath.like(conv(fp)) for fp in include]) |
519 | | - elif skip: |
520 | | - return and_(*[not_(File.filepath.like(conv(fp))) for fp in skip]) |
521 | | - |
522 | | - return None |
| 549 | + return File.id.in_( |
| 550 | + session.query(SourceComponentFile.file_id) |
| 551 | + .filter(SourceComponentFile.source_component_name == component_name) |
| 552 | + ) |
523 | 553 |
|
524 | 554 |
|
525 | 555 | def get_reports_by_bugpath_filter_for_single_origin( |
@@ -637,28 +667,12 @@ def get_other_source_component_file_query(session): |
637 | 667 | (Files NOT IN Component_1) AND (Files NOT IN Component_2) ... AND |
638 | 668 | (Files NOT IN Component_N) |
639 | 669 | """ |
640 | | - component_names = session.query(SourceComponent.name).all() |
641 | | - |
642 | | - # If there are no user defined source components we don't have to filter. |
643 | | - if not component_names: |
| 670 | + # Check if there are any source components |
| 671 | + if not session.query(SourceComponent).count(): |
644 | 672 | return None |
645 | 673 |
|
646 | | - def get_query(component_name: str): |
647 | | - """ Get file filter query for auto generated Other component. """ |
648 | | - skip, include = get_component_values(session, component_name) |
649 | | - |
650 | | - if skip and include: |
651 | | - include_q, skip_q = get_include_skip_queries(include, skip) |
652 | | - return File.id.notin_(include_q.except_(skip_q)) |
653 | | - elif include: |
654 | | - return and_(*[File.filepath.notlike(conv(fp)) for fp in include]) |
655 | | - elif skip: |
656 | | - return or_(*[File.filepath.like(conv(fp)) for fp in skip]) |
657 | | - |
658 | | - return None |
659 | | - |
660 | | - queries = [get_query(n) for (n, ) in component_names] |
661 | | - return and_(*queries) |
| 674 | + files_in_components = session.query(SourceComponentFile.file_id) |
| 675 | + return File.id.notin_(files_in_components) |
662 | 676 |
|
663 | 677 |
|
664 | 678 | def get_open_reports_date_filter_query(tbl=Report, date=RunHistory.time): |
@@ -4179,6 +4193,8 @@ def addSourceComponent(self, name, value, description): |
4179 | 4193 | user) |
4180 | 4194 |
|
4181 | 4195 | session.add(component) |
| 4196 | + update_source_component_files(session, component) |
| 4197 | + |
4182 | 4198 | session.commit() |
4183 | 4199 |
|
4184 | 4200 | return True |
|
0 commit comments