fix(crud): strip view-only join columns from order_by_li to prevent ambiguous account_id in WHERE

Sorting by join-derived columns (e.g. event_presenter_family_name on v_event_file)
caused MariaDB to expand the view's JOIN inline, making the unqualified account_id
clause from sql_and_qry_part ambiguous — resulting in a 400 SQL error. filter_order_by
now accepts raw_table_name and validates ORDER BY columns against the physical table
only; join-only columns are silently stripped. Also switches filter_order_by off the
global db connection to engine.connect() context managers. Updated all four call sites
in api_crud_v3.py and api_crud_v3_nested.py.

Docs: add order_by_li raw-table limitation and direct download link patterns to
GUIDE__AE_API_V3_for_Frontend.md; record fix in TODO__Agents.md.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Scott Idem
2026-06-10 13:33:09 -04:00
parent 22e5a3c3fd
commit 2429a1f731
5 changed files with 90 additions and 19 deletions

View File

@@ -84,6 +84,7 @@ async def get_child_obj_li(
obj_cfg = obj_type_kv_li[obj_name]
table_name = obj_cfg.get(f'tbl_{view}', obj_cfg.get('tbl_default', obj_cfg.get('tbl')))
base_name = obj_cfg.get(f'mdl_{view}', obj_cfg.get('mdl_default', obj_cfg.get('mdl')))
raw_table_name = obj_cfg.get('tbl_update', obj_cfg.get('tbl'))
# Log parent/child resolution details (use INFO so logs appear in production)
log.info("nested.list start parent=%s parent_table=%s parent_id_random=%s child=%s table=%s allowed_parents=%s", parent_obj_type, parent_table, parent_obj_id, obj_name, table_name, obj_cfg.get('parent_types'))
@@ -91,7 +92,7 @@ async def get_child_obj_li(
if not table_name or not base_name:
return mk_resp(data=False, status_code=500, response=response, status_message=f"Configuration error.")
order_by_li = filter_order_by(order_by_li, base_name, table_name)
order_by_li = filter_order_by(order_by_li, base_name, table_name, raw_table_name=raw_table_name)
status_filter = get_supported_filters(base_name, status_filter)
resolved_parent_id = redis_lookup_id_random(record_id_random=parent_obj_id, table_name=parent_table)
@@ -187,11 +188,12 @@ async def search_child_obj_li(
obj_cfg = obj_type_kv_li[obj_name]
table_name = obj_cfg.get(f'tbl_{view}', obj_cfg.get('tbl_default', obj_cfg.get('tbl')))
base_name = obj_cfg.get(f'mdl_{view}', obj_cfg.get('mdl_default', obj_cfg.get('mdl')))
raw_table_name = obj_cfg.get('tbl_update', obj_cfg.get('tbl'))
if not table_name or not base_name:
return mk_resp(data=False, status_code=500, response=response, status_message=f"Configuration error.")
order_by_li = filter_order_by(order_by_li, base_name, table_name)
order_by_li = filter_order_by(order_by_li, base_name, table_name, raw_table_name=raw_table_name)
status_filter = get_supported_filters(base_name, status_filter)
searchable_fields = obj_cfg.get('searchable_fields')