From 54032ae1081a7bdacf13e0e07975071e898b0c97 Mon Sep 17 00:00:00 2001 From: seonghobae <8172694+seonghobae@users.noreply.github.com> Date: Fri, 17 Jul 2026 14:01:45 +0000 Subject: [PATCH] =?UTF-8?q?=E2=9A=A1=20Bolt:=20=EC=B5=9C=EC=A0=81=ED=99=94?= =?UTF-8?q?:=20dbml=5Fimport=20=ED=8C=8C=EC=8B=B1=20=EA=B3=BC=EC=A0=95?= =?UTF-8?q?=EC=9D=98=20O(N^2)=20=EB=B3=91=EB=AA=A9=20=ED=98=84=EC=83=81=20?= =?UTF-8?q?=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .jules/bolt.md | 3 +++ backend/app/spec/dbml_import.py | 9 ++++++++- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/.jules/bolt.md b/.jules/bolt.md index f1a8c146..63d801d9 100644 --- a/.jules/bolt.md +++ b/.jules/bolt.md @@ -77,3 +77,6 @@ Optimized metric route processing to O(N) by creating a mapping of routes direct ## 2024-07-13 - [Optimize Export Dictionary FK lookups] **Learning:** Found O(N * C * E) performance bottleneck in ERD export dictionaries due to repeated array searching with `edges.some()` inside a nested loop over nodes and columns. **Action:** Replace repeated linear array scans for edges by precomputing O(1) Set lookups of foreign key column handles per node before looping. +## 2023-10-27 - [Dictionary Lookup over sum() in DBML Import] +**Learning:** In the backend `dbml_import.py` logic, utilizing `sum(1 for c in columns if c["relation_oid"] == oid) + 1` inside an iteration loops dynamically allocated lists causing a heavy O(N^2) overhead, especially noticeable with wide tables or lots of fields. Also, always add a descriptive comment as Bolt. +**Action:** Always prefer initializing a dictionary prior to the loop and use `O(1)` dict lookups like `pos = col_count_by_oid.get(oid, 0) + 1` to process sequences where frequency tracking is required within loops. diff --git a/backend/app/spec/dbml_import.py b/backend/app/spec/dbml_import.py index b93454a9..a37a60d2 100644 --- a/backend/app/spec/dbml_import.py +++ b/backend/app/spec/dbml_import.py @@ -135,6 +135,7 @@ def parse_dbml(text: str) -> dict[str, Any]: current: tuple[str, str] | None = None in_ignored_block = 0 in_indexes = False + col_count_by_oid: dict[int, int] = {} for raw_line in text.splitlines(): # ReDoS guard: no legitimate DBML line approaches this length; capping @@ -207,11 +208,17 @@ def parse_dbml(text: str) -> dict[str, Any]: settings = (cm.group("settings") or "").lower() oid = oid_by_table[current] is_pk = bool(re.search(r"\bpk\b|primary\s+key", settings)) + + # ⚡ Bolt: Performance Optimization + # Use an O(1) dictionary counter instead of an O(N^2) sum(...) loop + # to calculate column positions efficiently for large DBML files. + pos = col_count_by_oid.get(oid, 0) + 1 + col_count_by_oid[oid] = pos columns.append( { "relation_oid": oid, "column_name": col_name, - "column_position": sum(1 for c in columns if c["relation_oid"] == oid) + 1, + "column_position": pos, "data_type": cm.group("type"), "is_not_null": is_pk or "not null" in settings, "has_default": "default:" in settings,