codegraphcontext-rust 0.5.5__cp310-cp310-win_amd64.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- codegraphcontext/__init__.py +2 -0
- codegraphcontext/__main__.py +5 -0
- codegraphcontext/_cgc_rust.cp310-win_amd64.pyd +0 -0
- codegraphcontext/cli/__init__.py +1 -0
- codegraphcontext/cli/cli_helpers.py +774 -0
- codegraphcontext/cli/config_manager.py +1051 -0
- codegraphcontext/cli/main.py +2386 -0
- codegraphcontext/cli/registry_commands.py +404 -0
- codegraphcontext/cli/setup_macos.py +93 -0
- codegraphcontext/cli/setup_wizard.py +991 -0
- codegraphcontext/cli/visualizer.py +50 -0
- codegraphcontext/core/__init__.py +165 -0
- codegraphcontext/core/bundle_registry.py +181 -0
- codegraphcontext/core/cgc_bundle.py +858 -0
- codegraphcontext/core/cgcignore.py +118 -0
- codegraphcontext/core/database.py +273 -0
- codegraphcontext/core/database_falkordb.py +480 -0
- codegraphcontext/core/database_falkordb_remote.py +199 -0
- codegraphcontext/core/database_kuzu.py +636 -0
- codegraphcontext/core/falkor_worker.py +132 -0
- codegraphcontext/core/jobs.py +132 -0
- codegraphcontext/core/watcher.py +260 -0
- codegraphcontext/prompts.py +125 -0
- codegraphcontext/server.py +471 -0
- codegraphcontext/tool_definitions.py +220 -0
- codegraphcontext/tools/__init__.py +0 -0
- codegraphcontext/tools/advanced_language_query_tool.py +103 -0
- codegraphcontext/tools/code_finder.py +1160 -0
- codegraphcontext/tools/graph_builder.py +337 -0
- codegraphcontext/tools/handlers/analysis_handlers.py +114 -0
- codegraphcontext/tools/handlers/indexing_handlers.py +116 -0
- codegraphcontext/tools/handlers/management_handlers.py +339 -0
- codegraphcontext/tools/handlers/query_handlers.py +83 -0
- codegraphcontext/tools/handlers/watcher_handlers.py +83 -0
- codegraphcontext/tools/indexing/__init__.py +1 -0
- codegraphcontext/tools/indexing/constants.py +25 -0
- codegraphcontext/tools/indexing/discovery.py +64 -0
- codegraphcontext/tools/indexing/engine.py +109 -0
- codegraphcontext/tools/indexing/persistence/__init__.py +3 -0
- codegraphcontext/tools/indexing/persistence/parquet_loader.py +643 -0
- codegraphcontext/tools/indexing/persistence/writer.py +1114 -0
- codegraphcontext/tools/indexing/pipeline.py +171 -0
- codegraphcontext/tools/indexing/pre_scan.py +105 -0
- codegraphcontext/tools/indexing/resolution/__init__.py +8 -0
- codegraphcontext/tools/indexing/resolution/calls.py +253 -0
- codegraphcontext/tools/indexing/resolution/inheritance.py +91 -0
- codegraphcontext/tools/indexing/sanitize.py +41 -0
- codegraphcontext/tools/indexing/schema.py +79 -0
- codegraphcontext/tools/indexing/schema_contract.py +44 -0
- codegraphcontext/tools/indexing/scip_pipeline.py +140 -0
- codegraphcontext/tools/languages/c.py +563 -0
- codegraphcontext/tools/languages/cpp.py +616 -0
- codegraphcontext/tools/languages/csharp.py +551 -0
- codegraphcontext/tools/languages/dart.py +378 -0
- codegraphcontext/tools/languages/elixir.py +460 -0
- codegraphcontext/tools/languages/go.py +499 -0
- codegraphcontext/tools/languages/haskell.py +426 -0
- codegraphcontext/tools/languages/java.py +500 -0
- codegraphcontext/tools/languages/javascript.py +589 -0
- codegraphcontext/tools/languages/kotlin.py +640 -0
- codegraphcontext/tools/languages/perl.py +260 -0
- codegraphcontext/tools/languages/php.py +519 -0
- codegraphcontext/tools/languages/python.py +576 -0
- codegraphcontext/tools/languages/ruby.py +537 -0
- codegraphcontext/tools/languages/rust.py +295 -0
- codegraphcontext/tools/languages/scala.py +519 -0
- codegraphcontext/tools/languages/swift.py +491 -0
- codegraphcontext/tools/languages/typescript.py +575 -0
- codegraphcontext/tools/languages/typescriptjsx.py +151 -0
- codegraphcontext/tools/package_resolver.py +472 -0
- codegraphcontext/tools/query_tool_languages/c_toolkit.py +5 -0
- codegraphcontext/tools/query_tool_languages/cpp_toolkit.py +87 -0
- codegraphcontext/tools/query_tool_languages/csharp_toolkit.py +5 -0
- codegraphcontext/tools/query_tool_languages/dart_toolkit.py +5 -0
- codegraphcontext/tools/query_tool_languages/go_toolkit.py +5 -0
- codegraphcontext/tools/query_tool_languages/haskell_toolkit.py +5 -0
- codegraphcontext/tools/query_tool_languages/java_toolkit.py +5 -0
- codegraphcontext/tools/query_tool_languages/javascript_toolkit.py +5 -0
- codegraphcontext/tools/query_tool_languages/perl_toolkit.py +5 -0
- codegraphcontext/tools/query_tool_languages/python_toolkit.py +5 -0
- codegraphcontext/tools/query_tool_languages/ruby_toolkit.py +5 -0
- codegraphcontext/tools/query_tool_languages/rust_toolkit.py +5 -0
- codegraphcontext/tools/query_tool_languages/scala_toolkit.py +5 -0
- codegraphcontext/tools/query_tool_languages/swift_toolkit.py +5 -0
- codegraphcontext/tools/query_tool_languages/typescript_toolkit.py +5 -0
- codegraphcontext/tools/scip_indexer.py +467 -0
- codegraphcontext/tools/scip_pb2.py +2455 -0
- codegraphcontext/tools/system.py +133 -0
- codegraphcontext/tools/tree_sitter_parser.py +103 -0
- codegraphcontext/utils/debug_log.py +90 -0
- codegraphcontext/utils/path_ignore.py +55 -0
- codegraphcontext/utils/repo_path.py +27 -0
- codegraphcontext/utils/tree_sitter_manager.py +285 -0
- codegraphcontext/utils/visualize_graph.py +0 -0
- codegraphcontext/viz/server.py +282 -0
- codegraphcontext_rust-0.5.5.dist-info/METADATA +537 -0
- codegraphcontext_rust-0.5.5.dist-info/RECORD +101 -0
- codegraphcontext_rust-0.5.5.dist-info/WHEEL +4 -0
- codegraphcontext_rust-0.5.5.dist-info/entry_points.txt +3 -0
- codegraphcontext_rust-0.5.5.dist-info/licenses/LICENSE +21 -0
- codegraphcontext_rust-0.5.5.dist-info/sboms/cgc-python.cyclonedx.json +2333 -0
|
@@ -0,0 +1,643 @@
|
|
|
1
|
+
"""Parquet bulk loader for KuzuDB — uses COPY FROM for 10-50x faster DB writes.
|
|
2
|
+
|
|
3
|
+
Strategy (like GKG/LadybugDB):
|
|
4
|
+
1. Serialize parsed data → Parquet files (type-safe, no escaping issues)
|
|
5
|
+
2. Drop existing tables → Recreate schema
|
|
6
|
+
3. COPY FROM parquet (KuzuDB native bulk import)
|
|
7
|
+
|
|
8
|
+
This replaces the MERGE-based batch writer for initial indexing.
|
|
9
|
+
For incremental updates, the MERGE writer is still available.
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
from __future__ import annotations
|
|
13
|
+
|
|
14
|
+
import os
|
|
15
|
+
import time
|
|
16
|
+
import tempfile
|
|
17
|
+
from pathlib import Path
|
|
18
|
+
from typing import Any, Callable, Dict, List, Optional, Tuple
|
|
19
|
+
from collections import defaultdict
|
|
20
|
+
|
|
21
|
+
from ....utils.debug_log import info_logger, warning_logger
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def _ensure_pyarrow():
|
|
25
|
+
"""Import pyarrow lazily."""
|
|
26
|
+
try:
|
|
27
|
+
import pyarrow as pa
|
|
28
|
+
import pyarrow.parquet as pq
|
|
29
|
+
return pa, pq
|
|
30
|
+
except ImportError:
|
|
31
|
+
raise ImportError(
|
|
32
|
+
"pyarrow is required for Parquet bulk loading. "
|
|
33
|
+
"Install with: pip install pyarrow"
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def bulk_load_parquet(
|
|
38
|
+
conn: Any,
|
|
39
|
+
all_file_data: List[Dict[str, Any]],
|
|
40
|
+
repo_path: str,
|
|
41
|
+
call_groups: Tuple[List, List, List, List, List, List],
|
|
42
|
+
inheritance_links: List[Dict] = None,
|
|
43
|
+
on_progress: Optional[Callable] = None,
|
|
44
|
+
) -> Dict[str, int]:
|
|
45
|
+
"""Bulk load all data via Parquet COPY FROM.
|
|
46
|
+
|
|
47
|
+
Args:
|
|
48
|
+
conn: Raw kuzu.Connection (not the wrapper)
|
|
49
|
+
all_file_data: List of parsed file dicts
|
|
50
|
+
repo_path: Repository root path
|
|
51
|
+
call_groups: Tuple of 6 call edge lists (fn_fn, fn_cls, cls_fn, cls_cls, file_fn, file_cls)
|
|
52
|
+
inheritance_links: Optional list of inheritance dicts
|
|
53
|
+
on_progress: Optional progress callback
|
|
54
|
+
|
|
55
|
+
Returns:
|
|
56
|
+
Dict with counts of loaded entities.
|
|
57
|
+
"""
|
|
58
|
+
pa, pq = _ensure_pyarrow()
|
|
59
|
+
t_start = time.time()
|
|
60
|
+
repo_path_str = str(Path(repo_path).resolve())
|
|
61
|
+
repo_name = Path(repo_path).name
|
|
62
|
+
|
|
63
|
+
pq_dir = tempfile.mkdtemp(prefix="cgc_parquet_")
|
|
64
|
+
counts = {}
|
|
65
|
+
|
|
66
|
+
def progress(msg):
|
|
67
|
+
if on_progress:
|
|
68
|
+
on_progress(0, 0, msg)
|
|
69
|
+
info_logger(f"[ParquetBulk] {msg}")
|
|
70
|
+
|
|
71
|
+
def write_pq(name, table):
|
|
72
|
+
path = os.path.join(pq_dir, f"{name}.parquet")
|
|
73
|
+
pq.write_table(table, path, compression='snappy')
|
|
74
|
+
return path
|
|
75
|
+
|
|
76
|
+
# ── Phase 1: Serialize to Parquet ──
|
|
77
|
+
progress("Serializing to Parquet...")
|
|
78
|
+
t0 = time.time()
|
|
79
|
+
|
|
80
|
+
# Helper: make uid
|
|
81
|
+
def uid(name, path, line):
|
|
82
|
+
return f"{name}|{path}|{line}"
|
|
83
|
+
|
|
84
|
+
# Collect all data
|
|
85
|
+
file_paths, file_names, file_rels, file_deps = [], [], [], []
|
|
86
|
+
dir_paths, dir_names = [], []
|
|
87
|
+
dir_from, dir_to = defaultdict(list), defaultdict(list) # parent_label → lists
|
|
88
|
+
file_parent_from, file_parent_to = defaultdict(list), defaultdict(list)
|
|
89
|
+
|
|
90
|
+
# Symbols per label: {label: {col_name: [values]}}
|
|
91
|
+
sym_data = {}
|
|
92
|
+
sym_contains = {} # {label: (file_paths, symbol_uids)}
|
|
93
|
+
|
|
94
|
+
param_uids, param_names, param_paths, param_flines = [], [], [], []
|
|
95
|
+
param_rel_fn, param_rel_p = [], []
|
|
96
|
+
|
|
97
|
+
class_fn_cls, class_fn_fn = [], []
|
|
98
|
+
nested_outer, nested_inner = [], []
|
|
99
|
+
|
|
100
|
+
mod_names, mod_fulls = [], []
|
|
101
|
+
imp_file, imp_mod, imp_alias, imp_imported, imp_line = [], [], [], [], []
|
|
102
|
+
|
|
103
|
+
seen_dirs = set()
|
|
104
|
+
seen_symbols = set()
|
|
105
|
+
seen_modules = set()
|
|
106
|
+
|
|
107
|
+
for fd in all_file_data:
|
|
108
|
+
fp = str(Path(fd["path"]).resolve())
|
|
109
|
+
fname = Path(fp).name
|
|
110
|
+
try:
|
|
111
|
+
rel = str(Path(fp).relative_to(repo_path_str))
|
|
112
|
+
except ValueError:
|
|
113
|
+
rel = fname
|
|
114
|
+
is_dep = fd.get("is_dependency", False)
|
|
115
|
+
lang = fd.get("lang", "")
|
|
116
|
+
|
|
117
|
+
file_paths.append(fp)
|
|
118
|
+
file_names.append(fname)
|
|
119
|
+
file_rels.append(rel)
|
|
120
|
+
file_deps.append(is_dep)
|
|
121
|
+
|
|
122
|
+
# Directory chain
|
|
123
|
+
try:
|
|
124
|
+
rel_to_file = Path(fp).relative_to(repo_path_str)
|
|
125
|
+
except ValueError:
|
|
126
|
+
rel_to_file = Path(fname)
|
|
127
|
+
|
|
128
|
+
parent_path = repo_path_str
|
|
129
|
+
parent_label = "Repository"
|
|
130
|
+
for part in rel_to_file.parts[:-1]:
|
|
131
|
+
cur = str(Path(parent_path) / part)
|
|
132
|
+
if cur not in seen_dirs:
|
|
133
|
+
seen_dirs.add(cur)
|
|
134
|
+
dir_paths.append(cur)
|
|
135
|
+
dir_names.append(part)
|
|
136
|
+
dir_from[parent_label].append(parent_path)
|
|
137
|
+
dir_to[parent_label].append(cur)
|
|
138
|
+
parent_path = cur
|
|
139
|
+
parent_label = "Directory"
|
|
140
|
+
|
|
141
|
+
file_parent_from[parent_label].append(parent_path)
|
|
142
|
+
file_parent_to[parent_label].append(fp)
|
|
143
|
+
|
|
144
|
+
# Symbols
|
|
145
|
+
label_mappings = [
|
|
146
|
+
("functions", "Function"), ("classes", "Class"), ("variables", "Variable"),
|
|
147
|
+
("traits", "Trait"), ("interfaces", "Interface"), ("macros", "Macro"),
|
|
148
|
+
("structs", "Struct"), ("enums", "Enum"), ("unions", "Union"),
|
|
149
|
+
("records", "Record"), ("properties", "Property"),
|
|
150
|
+
]
|
|
151
|
+
for key, label in label_mappings:
|
|
152
|
+
items = fd.get(key, [])
|
|
153
|
+
if not items:
|
|
154
|
+
continue
|
|
155
|
+
if label not in sym_data:
|
|
156
|
+
sym_data[label] = defaultdict(list)
|
|
157
|
+
sym_contains[label] = ([], [])
|
|
158
|
+
|
|
159
|
+
for item in items:
|
|
160
|
+
name = item.get("name", "")
|
|
161
|
+
line = item.get("line_number", 0)
|
|
162
|
+
u = uid(name, fp, line)
|
|
163
|
+
if u in seen_symbols:
|
|
164
|
+
continue
|
|
165
|
+
seen_symbols.add(u)
|
|
166
|
+
|
|
167
|
+
d = sym_data[label]
|
|
168
|
+
d["uid"].append(u)
|
|
169
|
+
d["name"].append(name)
|
|
170
|
+
d["path"].append(fp)
|
|
171
|
+
d["line_number"].append(line)
|
|
172
|
+
d["end_line"].append(item.get("end_line", 0))
|
|
173
|
+
d["source"].append(item.get("source", ""))
|
|
174
|
+
d["docstring"].append(item.get("docstring", ""))
|
|
175
|
+
d["lang"].append(item.get("lang", lang))
|
|
176
|
+
d["is_dependency"].append(item.get("is_dependency", is_dep))
|
|
177
|
+
|
|
178
|
+
# Function-specific fields
|
|
179
|
+
if label == "Function":
|
|
180
|
+
d["cyclomatic_complexity"].append(item.get("cyclomatic_complexity", 1))
|
|
181
|
+
d["context"].append(item.get("context", ""))
|
|
182
|
+
d["context_type"].append(item.get("context_type", ""))
|
|
183
|
+
d["class_context"].append(item.get("class_context", ""))
|
|
184
|
+
|
|
185
|
+
args = item.get("args", [])
|
|
186
|
+
decorators = item.get("decorators", [])
|
|
187
|
+
d["args"].append(args if args else [])
|
|
188
|
+
d["decorators"].append(decorators if decorators else [])
|
|
189
|
+
|
|
190
|
+
# Parameters
|
|
191
|
+
for arg_name in args:
|
|
192
|
+
if not arg_name:
|
|
193
|
+
continue
|
|
194
|
+
pu = f"{arg_name}|{fp}|{line}"
|
|
195
|
+
param_uids.append(pu)
|
|
196
|
+
param_names.append(arg_name)
|
|
197
|
+
param_paths.append(fp)
|
|
198
|
+
param_flines.append(line)
|
|
199
|
+
param_rel_fn.append(u)
|
|
200
|
+
param_rel_p.append(pu)
|
|
201
|
+
|
|
202
|
+
# Class containment
|
|
203
|
+
cc = item.get("class_context", "")
|
|
204
|
+
if cc:
|
|
205
|
+
class_fn_cls.append(f"{cc}|{fp}")
|
|
206
|
+
class_fn_fn.append(u)
|
|
207
|
+
|
|
208
|
+
# Nested functions
|
|
209
|
+
if item.get("context_type") == "function_definition" and item.get("context"):
|
|
210
|
+
nested_outer.append(f"{item['context']}|{fp}")
|
|
211
|
+
nested_inner.append(u)
|
|
212
|
+
|
|
213
|
+
elif label == "Class":
|
|
214
|
+
d.setdefault("decorators", []).append(item.get("decorators", []) or [])
|
|
215
|
+
|
|
216
|
+
elif label == "Variable":
|
|
217
|
+
d["value"] = d.get("value", [])
|
|
218
|
+
d["value"].append(item.get("value", ""))
|
|
219
|
+
d["context"] = d.get("context", [])
|
|
220
|
+
d["context"].append(item.get("context", ""))
|
|
221
|
+
|
|
222
|
+
sym_contains[label][0].append(fp)
|
|
223
|
+
sym_contains[label][1].append(u)
|
|
224
|
+
|
|
225
|
+
# Imports
|
|
226
|
+
for imp in fd.get("imports", []):
|
|
227
|
+
name = imp.get("name", "")
|
|
228
|
+
alias = imp.get("alias", "")
|
|
229
|
+
source = imp.get("source", "")
|
|
230
|
+
# Bug 1 fix: full_import_name fallback chain
|
|
231
|
+
full = imp.get("full_import_name", "") or source or name
|
|
232
|
+
line_n = imp.get("line_number", 0)
|
|
233
|
+
mod_name = source if source else name
|
|
234
|
+
imported = name if source else ""
|
|
235
|
+
if not mod_name:
|
|
236
|
+
continue
|
|
237
|
+
|
|
238
|
+
if mod_name not in seen_modules:
|
|
239
|
+
seen_modules.add(mod_name)
|
|
240
|
+
mod_names.append(mod_name)
|
|
241
|
+
mod_fulls.append(full)
|
|
242
|
+
|
|
243
|
+
imp_file.append(fp)
|
|
244
|
+
imp_mod.append(mod_name)
|
|
245
|
+
imp_alias.append(alias)
|
|
246
|
+
imp_imported.append(imported)
|
|
247
|
+
imp_line.append(line_n)
|
|
248
|
+
|
|
249
|
+
t_serialize_data = time.time() - t0
|
|
250
|
+
|
|
251
|
+
# ── Write Parquet files ──
|
|
252
|
+
t0 = time.time()
|
|
253
|
+
pq_files = {}
|
|
254
|
+
|
|
255
|
+
# Repository (single row, COPY FROM)
|
|
256
|
+
pq_files["Repository"] = write_pq("repository", pa.table({
|
|
257
|
+
"path": [repo_path_str],
|
|
258
|
+
"name": [repo_name],
|
|
259
|
+
"is_dependency": [False],
|
|
260
|
+
}))
|
|
261
|
+
|
|
262
|
+
# Files
|
|
263
|
+
if file_paths:
|
|
264
|
+
pq_files["File"] = write_pq("files", pa.table({
|
|
265
|
+
"path": file_paths,
|
|
266
|
+
"name": file_names,
|
|
267
|
+
"relative_path": file_rels,
|
|
268
|
+
"is_dependency": file_deps,
|
|
269
|
+
}))
|
|
270
|
+
counts["files"] = len(file_paths)
|
|
271
|
+
|
|
272
|
+
# Directories
|
|
273
|
+
if dir_paths:
|
|
274
|
+
pq_files["Directory"] = write_pq("directories", pa.table({
|
|
275
|
+
"path": dir_paths,
|
|
276
|
+
"name": dir_names,
|
|
277
|
+
}))
|
|
278
|
+
counts["directories"] = len(dir_paths)
|
|
279
|
+
|
|
280
|
+
# Symbols
|
|
281
|
+
for label, d in sym_data.items():
|
|
282
|
+
if label == "Function":
|
|
283
|
+
tbl = pa.table({
|
|
284
|
+
"uid": d["uid"], "name": d["name"], "path": d["path"],
|
|
285
|
+
"line_number": pa.array(d["line_number"], type=pa.int64()),
|
|
286
|
+
"end_line": pa.array(d["end_line"], type=pa.int64()),
|
|
287
|
+
"source": d["source"], "docstring": d["docstring"],
|
|
288
|
+
"lang": d["lang"],
|
|
289
|
+
"cyclomatic_complexity": pa.array(d["cyclomatic_complexity"], type=pa.int64()),
|
|
290
|
+
"context": d["context"], "context_type": d["context_type"],
|
|
291
|
+
"class_context": d["class_context"],
|
|
292
|
+
"is_dependency": d["is_dependency"],
|
|
293
|
+
"decorators": pa.array(d["decorators"], type=pa.list_(pa.string())),
|
|
294
|
+
"args": pa.array(d["args"], type=pa.list_(pa.string())),
|
|
295
|
+
})
|
|
296
|
+
elif label == "Class":
|
|
297
|
+
tbl = pa.table({
|
|
298
|
+
"uid": d["uid"], "name": d["name"], "path": d["path"],
|
|
299
|
+
"line_number": pa.array(d["line_number"], type=pa.int64()),
|
|
300
|
+
"end_line": pa.array(d["end_line"], type=pa.int64()),
|
|
301
|
+
"source": d["source"], "docstring": d["docstring"],
|
|
302
|
+
"lang": d["lang"], "is_dependency": d["is_dependency"],
|
|
303
|
+
"decorators": pa.array(d.get("decorators", [[] for _ in d["uid"]]), type=pa.list_(pa.string())),
|
|
304
|
+
})
|
|
305
|
+
elif label == "Variable":
|
|
306
|
+
tbl = pa.table({
|
|
307
|
+
"uid": d["uid"], "name": d["name"], "path": d["path"],
|
|
308
|
+
"line_number": pa.array(d["line_number"], type=pa.int64()),
|
|
309
|
+
"source": d["source"], "docstring": d["docstring"],
|
|
310
|
+
"lang": d["lang"],
|
|
311
|
+
"value": d.get("value", ["" for _ in d["uid"]]),
|
|
312
|
+
"context": d.get("context", ["" for _ in d["uid"]]),
|
|
313
|
+
"is_dependency": d["is_dependency"],
|
|
314
|
+
})
|
|
315
|
+
elif label == "Parameter":
|
|
316
|
+
continue # handled separately
|
|
317
|
+
else:
|
|
318
|
+
# Generic: Trait, Interface, Macro, Struct, Enum, Union, Annotation, Record, Property
|
|
319
|
+
tbl = pa.table({
|
|
320
|
+
"uid": d["uid"], "name": d["name"], "path": d["path"],
|
|
321
|
+
"line_number": pa.array(d["line_number"], type=pa.int64()),
|
|
322
|
+
"end_line": pa.array(d["end_line"], type=pa.int64()),
|
|
323
|
+
"source": d["source"], "docstring": d["docstring"],
|
|
324
|
+
"lang": d["lang"], "is_dependency": d["is_dependency"],
|
|
325
|
+
})
|
|
326
|
+
|
|
327
|
+
pq_files[label] = write_pq(f"sym_{label.lower()}", tbl)
|
|
328
|
+
counts[f"sym_{label.lower()}"] = len(d["uid"])
|
|
329
|
+
|
|
330
|
+
# Parameters
|
|
331
|
+
if param_uids:
|
|
332
|
+
pq_files["Parameter"] = write_pq("params", pa.table({
|
|
333
|
+
"uid": param_uids,
|
|
334
|
+
"name": param_names,
|
|
335
|
+
"path": param_paths,
|
|
336
|
+
"function_line_number": pa.array(param_flines, type=pa.int64()),
|
|
337
|
+
}))
|
|
338
|
+
counts["params"] = len(param_uids)
|
|
339
|
+
|
|
340
|
+
# Modules
|
|
341
|
+
if mod_names:
|
|
342
|
+
pq_files["Module"] = write_pq("modules", pa.table({
|
|
343
|
+
"name": mod_names,
|
|
344
|
+
"lang": ["" for _ in mod_names],
|
|
345
|
+
"full_import_name": mod_fulls,
|
|
346
|
+
}))
|
|
347
|
+
counts["modules"] = len(mod_names)
|
|
348
|
+
|
|
349
|
+
# ── Relationship Parquet files ──
|
|
350
|
+
rel_files = {}
|
|
351
|
+
|
|
352
|
+
# Directory CONTAINS (Repository→Directory, Directory→Directory)
|
|
353
|
+
for parent_label in ("Repository", "Directory"):
|
|
354
|
+
froms = dir_from.get(parent_label, [])
|
|
355
|
+
tos = dir_to.get(parent_label, [])
|
|
356
|
+
if froms:
|
|
357
|
+
key = f"CONTAINS_{parent_label}_Directory"
|
|
358
|
+
rel_files[key] = {
|
|
359
|
+
"path": write_pq(f"rel_contains_{parent_label.lower()}_dir", pa.table({
|
|
360
|
+
"from": froms, "to": tos,
|
|
361
|
+
})),
|
|
362
|
+
"from": parent_label, "to": "Directory",
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
# File parent CONTAINS (Repository→File, Directory→File)
|
|
366
|
+
for parent_label in ("Repository", "Directory"):
|
|
367
|
+
froms = file_parent_from.get(parent_label, [])
|
|
368
|
+
tos = file_parent_to.get(parent_label, [])
|
|
369
|
+
if froms:
|
|
370
|
+
key = f"CONTAINS_{parent_label}_File"
|
|
371
|
+
rel_files[key] = {
|
|
372
|
+
"path": write_pq(f"rel_contains_{parent_label.lower()}_file", pa.table({
|
|
373
|
+
"from": froms, "to": tos,
|
|
374
|
+
})),
|
|
375
|
+
"from": parent_label, "to": "File",
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
# File CONTAINS Symbol
|
|
379
|
+
for label, (fps, uids) in sym_contains.items():
|
|
380
|
+
if fps:
|
|
381
|
+
escaped = label
|
|
382
|
+
key = f"CONTAINS_File_{label}"
|
|
383
|
+
rel_files[key] = {
|
|
384
|
+
"path": write_pq(f"rel_file_contains_{label.lower()}", pa.table({
|
|
385
|
+
"from": fps, "to": uids,
|
|
386
|
+
})),
|
|
387
|
+
"from": "File", "to": escaped,
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
# HAS_PARAMETER
|
|
391
|
+
if param_rel_fn:
|
|
392
|
+
rel_files["HAS_PARAMETER"] = {
|
|
393
|
+
"path": write_pq("rel_has_parameter", pa.table({
|
|
394
|
+
"from": param_rel_fn, "to": param_rel_p,
|
|
395
|
+
})),
|
|
396
|
+
"from": "Function", "to": "Parameter",
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
# IMPORTS (File→Module)
|
|
400
|
+
if imp_file:
|
|
401
|
+
rel_files["IMPORTS"] = {
|
|
402
|
+
"path": write_pq("rel_imports", pa.table({
|
|
403
|
+
"from": imp_file, "to": imp_mod,
|
|
404
|
+
"alias": imp_alias,
|
|
405
|
+
"full_import_name": imp_imported,
|
|
406
|
+
"imported_name": imp_imported,
|
|
407
|
+
"line_number": pa.array(imp_line, type=pa.int64()),
|
|
408
|
+
})),
|
|
409
|
+
"from": "File", "to": "Module",
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
# CALLS (6 categories)
|
|
413
|
+
# Build (name, path) → uid lookup for resolving called targets
|
|
414
|
+
func_uid_lookup = {} # (name, path) → first uid
|
|
415
|
+
class_uid_lookup = {}
|
|
416
|
+
for label, d in sym_data.items():
|
|
417
|
+
lookup = func_uid_lookup if label == "Function" else class_uid_lookup if label == "Class" else None
|
|
418
|
+
if lookup is None:
|
|
419
|
+
continue
|
|
420
|
+
for i, u in enumerate(d["uid"]):
|
|
421
|
+
key = (d["name"][i], d["path"][i])
|
|
422
|
+
if key not in lookup:
|
|
423
|
+
lookup[key] = u
|
|
424
|
+
|
|
425
|
+
call_labels = [
|
|
426
|
+
("fn_fn", "Function", "Function", func_uid_lookup),
|
|
427
|
+
("fn_cls", "Function", "Class", class_uid_lookup),
|
|
428
|
+
("cls_fn", "Class", "Function", func_uid_lookup),
|
|
429
|
+
("cls_cls", "Class", "Class", class_uid_lookup),
|
|
430
|
+
("file_fn", "File", "Function", func_uid_lookup),
|
|
431
|
+
("file_cls", "File", "Class", class_uid_lookup),
|
|
432
|
+
]
|
|
433
|
+
fn_to_fn, fn_to_cls, cls_to_fn, cls_to_cls, file_to_fn, file_to_cls = call_groups
|
|
434
|
+
call_lists = [fn_to_fn, fn_to_cls, cls_to_fn, cls_to_cls, file_to_fn, file_to_cls]
|
|
435
|
+
|
|
436
|
+
for (name, from_label, to_label, to_lookup), edges in zip(call_labels, call_lists):
|
|
437
|
+
if not edges:
|
|
438
|
+
continue
|
|
439
|
+
froms, tos, lines, args_list, fcns = [], [], [], [], []
|
|
440
|
+
skipped = 0
|
|
441
|
+
for e in edges:
|
|
442
|
+
# Resolve called target uid
|
|
443
|
+
called_key = (e.get("called_name", ""), e.get("called_file_path", ""))
|
|
444
|
+
called_uid = to_lookup.get(called_key)
|
|
445
|
+
if not called_uid:
|
|
446
|
+
skipped += 1
|
|
447
|
+
continue
|
|
448
|
+
|
|
449
|
+
# Resolve caller
|
|
450
|
+
if from_label == "File":
|
|
451
|
+
froms.append(e.get("caller_file_path", ""))
|
|
452
|
+
else:
|
|
453
|
+
caller_label_lookup = func_uid_lookup if from_label == "Function" else class_uid_lookup
|
|
454
|
+
caller_uid = uid(
|
|
455
|
+
e.get("caller_name", ""),
|
|
456
|
+
e.get("caller_file_path", ""),
|
|
457
|
+
e.get("caller_line_number", 0),
|
|
458
|
+
)
|
|
459
|
+
froms.append(caller_uid)
|
|
460
|
+
|
|
461
|
+
tos.append(called_uid)
|
|
462
|
+
lines.append(e.get("line_number", 0))
|
|
463
|
+
args_list.append(e.get("args", []) or [])
|
|
464
|
+
fcns.append(e.get("full_call_name", ""))
|
|
465
|
+
|
|
466
|
+
if froms:
|
|
467
|
+
key = f"CALLS_{name}"
|
|
468
|
+
rel_files[key] = {
|
|
469
|
+
"path": write_pq(f"rel_calls_{name}", pa.table({
|
|
470
|
+
"from": froms,
|
|
471
|
+
"to": tos,
|
|
472
|
+
"line_number": pa.array(lines, type=pa.int64()),
|
|
473
|
+
"args": pa.array(args_list, type=pa.list_(pa.string())),
|
|
474
|
+
"full_call_name": fcns,
|
|
475
|
+
})),
|
|
476
|
+
"from": from_label, "to": to_label,
|
|
477
|
+
"is_calls": True,
|
|
478
|
+
}
|
|
479
|
+
counts[f"calls_{name}"] = len(froms)
|
|
480
|
+
if skipped:
|
|
481
|
+
info_logger(f"[ParquetBulk] CALLS {name}: {len(froms)} written, {skipped} skipped (target not found)")
|
|
482
|
+
|
|
483
|
+
# INHERITS
|
|
484
|
+
if inheritance_links:
|
|
485
|
+
child_uids, parent_uids = [], []
|
|
486
|
+
for link in inheritance_links:
|
|
487
|
+
child_uids.append(uid(
|
|
488
|
+
link.get("child_name", ""),
|
|
489
|
+
link.get("child_path", ""),
|
|
490
|
+
link.get("child_line", 0),
|
|
491
|
+
))
|
|
492
|
+
parent_uids.append(uid(
|
|
493
|
+
link.get("parent_name", ""),
|
|
494
|
+
link.get("parent_path", ""),
|
|
495
|
+
link.get("parent_line", 0),
|
|
496
|
+
))
|
|
497
|
+
rel_files["INHERITS_Class_Class"] = {
|
|
498
|
+
"path": write_pq("rel_inherits", pa.table({
|
|
499
|
+
"from": child_uids, "to": parent_uids,
|
|
500
|
+
})),
|
|
501
|
+
"from": "Class", "to": "Class",
|
|
502
|
+
}
|
|
503
|
+
counts["inheritance"] = len(inheritance_links)
|
|
504
|
+
|
|
505
|
+
t_write_pq = time.time() - t0
|
|
506
|
+
progress(f"Parquet written in {t_write_pq:.1f}s ({len(pq_files)} node files, {len(rel_files)} rel files)")
|
|
507
|
+
|
|
508
|
+
# ── Phase 2: Drop & recreate tables, COPY FROM ──
|
|
509
|
+
progress("Dropping and recreating tables...")
|
|
510
|
+
t0 = time.time()
|
|
511
|
+
|
|
512
|
+
_drop_all_tables(conn)
|
|
513
|
+
_create_schema(conn)
|
|
514
|
+
|
|
515
|
+
t_schema = time.time() - t0
|
|
516
|
+
|
|
517
|
+
# ── Phase 3: COPY FROM ──
|
|
518
|
+
progress("COPY FROM parquet (bulk import)...")
|
|
519
|
+
t0 = time.time()
|
|
520
|
+
|
|
521
|
+
# Node tables first
|
|
522
|
+
node_order = [
|
|
523
|
+
"Repository", "File", "Directory", "Module", "Parameter",
|
|
524
|
+
"Function", "Class", "Variable", "Trait", "Interface",
|
|
525
|
+
"Macro", "Struct", "Enum", "Union", "Annotation", "Record", "Property",
|
|
526
|
+
]
|
|
527
|
+
for table_name in node_order:
|
|
528
|
+
if table_name in pq_files:
|
|
529
|
+
escaped = f"`{table_name}`" if table_name in ("Macro", "Union", "Property") else table_name
|
|
530
|
+
path = pq_files[table_name]
|
|
531
|
+
try:
|
|
532
|
+
conn.execute(f"COPY {escaped} FROM '{path}'")
|
|
533
|
+
except Exception as e:
|
|
534
|
+
warning_logger(f"COPY {table_name} failed: {e}")
|
|
535
|
+
|
|
536
|
+
# Relationship tables
|
|
537
|
+
for key, info in rel_files.items():
|
|
538
|
+
path = info["path"]
|
|
539
|
+
from_t = info["from"]
|
|
540
|
+
to_t = info["to"]
|
|
541
|
+
is_calls = info.get("is_calls", False)
|
|
542
|
+
|
|
543
|
+
# Determine rel table name
|
|
544
|
+
if "CONTAINS" in key:
|
|
545
|
+
rel_name = "CONTAINS"
|
|
546
|
+
elif "HAS_PARAMETER" in key:
|
|
547
|
+
rel_name = "HAS_PARAMETER"
|
|
548
|
+
elif "IMPORTS" in key:
|
|
549
|
+
rel_name = "IMPORTS"
|
|
550
|
+
elif "CALLS" in key:
|
|
551
|
+
rel_name = "CALLS"
|
|
552
|
+
elif "INHERITS" in key:
|
|
553
|
+
rel_name = "INHERITS"
|
|
554
|
+
else:
|
|
555
|
+
continue
|
|
556
|
+
|
|
557
|
+
# Escape reserved words in from/to
|
|
558
|
+
from_escaped = f"`{from_t}`" if from_t in ("Macro", "Union", "Property") else from_t
|
|
559
|
+
to_escaped = f"`{to_t}`" if to_t in ("Macro", "Union", "Property") else to_t
|
|
560
|
+
|
|
561
|
+
try:
|
|
562
|
+
conn.execute(
|
|
563
|
+
f"COPY {rel_name} FROM '{path}' (from='{from_escaped}', to='{to_escaped}')"
|
|
564
|
+
)
|
|
565
|
+
except Exception as e:
|
|
566
|
+
warning_logger(f"COPY {rel_name} ({from_t}→{to_t}) failed: {e}")
|
|
567
|
+
|
|
568
|
+
t_copy = time.time() - t0
|
|
569
|
+
|
|
570
|
+
# Cleanup temp dir
|
|
571
|
+
import shutil
|
|
572
|
+
shutil.rmtree(pq_dir, ignore_errors=True)
|
|
573
|
+
|
|
574
|
+
total = time.time() - t_start
|
|
575
|
+
progress(f"Done in {total:.1f}s (serialize={t_serialize_data:.1f}s, write_pq={t_write_pq:.1f}s, schema={t_schema:.1f}s, copy={t_copy:.1f}s)")
|
|
576
|
+
counts["total_time"] = round(total, 1)
|
|
577
|
+
return counts
|
|
578
|
+
|
|
579
|
+
|
|
580
|
+
def _drop_all_tables(conn):
|
|
581
|
+
"""Drop all node and rel tables."""
|
|
582
|
+
# Drop rel tables first (depend on node tables)
|
|
583
|
+
for rel in ["CALLS", "CONTAINS", "IMPORTS", "INHERITS", "HAS_PARAMETER", "INCLUDES", "IMPLEMENTS"]:
|
|
584
|
+
try:
|
|
585
|
+
conn.execute(f"DROP TABLE {rel}")
|
|
586
|
+
except Exception:
|
|
587
|
+
pass
|
|
588
|
+
|
|
589
|
+
# Drop node tables
|
|
590
|
+
for node in ["Parameter", "Function", "Class", "Variable", "Trait", "Interface",
|
|
591
|
+
"`Macro`", "Struct", "Enum", "`Union`", "Annotation", "Record",
|
|
592
|
+
"`Property`", "Module", "File", "Directory", "Repository"]:
|
|
593
|
+
try:
|
|
594
|
+
conn.execute(f"DROP TABLE {node}")
|
|
595
|
+
except Exception:
|
|
596
|
+
pass
|
|
597
|
+
|
|
598
|
+
|
|
599
|
+
def _create_schema(conn):
|
|
600
|
+
"""Recreate all node and rel tables."""
|
|
601
|
+
node_tables = [
|
|
602
|
+
("Repository", "path STRING, name STRING, is_dependency BOOLEAN, PRIMARY KEY (path)"),
|
|
603
|
+
("File", "path STRING, name STRING, relative_path STRING, is_dependency BOOLEAN, PRIMARY KEY (path)"),
|
|
604
|
+
("Directory", "path STRING, name STRING, PRIMARY KEY (path)"),
|
|
605
|
+
("Module", "name STRING, lang STRING, full_import_name STRING, PRIMARY KEY (name)"),
|
|
606
|
+
("Function", "uid STRING, name STRING, path STRING, line_number INT64, end_line INT64, source STRING, docstring STRING, lang STRING, cyclomatic_complexity INT64, context STRING, context_type STRING, class_context STRING, is_dependency BOOLEAN, decorators STRING[], args STRING[], PRIMARY KEY (uid)"),
|
|
607
|
+
("Class", "uid STRING, name STRING, path STRING, line_number INT64, end_line INT64, source STRING, docstring STRING, lang STRING, is_dependency BOOLEAN, decorators STRING[], PRIMARY KEY (uid)"),
|
|
608
|
+
("Variable", "uid STRING, name STRING, path STRING, line_number INT64, source STRING, docstring STRING, lang STRING, value STRING, context STRING, is_dependency BOOLEAN, PRIMARY KEY (uid)"),
|
|
609
|
+
("Trait", "uid STRING, name STRING, path STRING, line_number INT64, end_line INT64, source STRING, docstring STRING, lang STRING, is_dependency BOOLEAN, PRIMARY KEY (uid)"),
|
|
610
|
+
("Interface", "uid STRING, name STRING, path STRING, line_number INT64, end_line INT64, source STRING, docstring STRING, lang STRING, is_dependency BOOLEAN, PRIMARY KEY (uid)"),
|
|
611
|
+
("Macro", "uid STRING, name STRING, path STRING, line_number INT64, end_line INT64, source STRING, docstring STRING, lang STRING, is_dependency BOOLEAN, PRIMARY KEY (uid)"),
|
|
612
|
+
("Struct", "uid STRING, name STRING, path STRING, line_number INT64, end_line INT64, source STRING, docstring STRING, lang STRING, is_dependency BOOLEAN, PRIMARY KEY (uid)"),
|
|
613
|
+
("Enum", "uid STRING, name STRING, path STRING, line_number INT64, end_line INT64, source STRING, docstring STRING, lang STRING, is_dependency BOOLEAN, PRIMARY KEY (uid)"),
|
|
614
|
+
("Union", "uid STRING, name STRING, path STRING, line_number INT64, end_line INT64, source STRING, docstring STRING, lang STRING, is_dependency BOOLEAN, PRIMARY KEY (uid)"),
|
|
615
|
+
("Annotation", "uid STRING, name STRING, path STRING, line_number INT64, end_line INT64, source STRING, docstring STRING, lang STRING, is_dependency BOOLEAN, PRIMARY KEY (uid)"),
|
|
616
|
+
("Record", "uid STRING, name STRING, path STRING, line_number INT64, end_line INT64, source STRING, docstring STRING, lang STRING, is_dependency BOOLEAN, PRIMARY KEY (uid)"),
|
|
617
|
+
("Property", "uid STRING, name STRING, path STRING, line_number INT64, end_line INT64, source STRING, docstring STRING, lang STRING, is_dependency BOOLEAN, PRIMARY KEY (uid)"),
|
|
618
|
+
("Parameter", "uid STRING, name STRING, path STRING, function_line_number INT64, PRIMARY KEY (uid)"),
|
|
619
|
+
]
|
|
620
|
+
rel_tables = [
|
|
621
|
+
("CONTAINS", "FROM File TO Function, FROM File TO Class, FROM File TO Variable, FROM File TO Trait, FROM File TO Interface, FROM `Macro` TO `Macro`, FROM File TO `Macro`, FROM File TO Struct, FROM File TO Enum, FROM File TO `Union`, FROM File TO Annotation, FROM File TO Record, FROM File TO `Property`, FROM Repository TO Directory, FROM Directory TO Directory, FROM Directory TO File, FROM Repository TO File, FROM Class TO Function, FROM Function TO Function", True),
|
|
622
|
+
("CALLS", "FROM Function TO Function, FROM Function TO Class, FROM File TO Function, FROM File TO Class, FROM Class TO Function, FROM Class TO Class, line_number INT64, args STRING[], full_call_name STRING", True),
|
|
623
|
+
("IMPORTS", "FROM File TO Module, alias STRING, full_import_name STRING, imported_name STRING, line_number INT64", False),
|
|
624
|
+
("INHERITS", "FROM Class TO Class, FROM Record TO Record, FROM Interface TO Interface", True),
|
|
625
|
+
("HAS_PARAMETER", "FROM Function TO Parameter", False),
|
|
626
|
+
("INCLUDES", "FROM Class TO Module", False),
|
|
627
|
+
("IMPLEMENTS", "FROM Class TO Interface, FROM Struct TO Interface, FROM Record TO Interface", True),
|
|
628
|
+
]
|
|
629
|
+
|
|
630
|
+
for table_name, schema in node_tables:
|
|
631
|
+
try:
|
|
632
|
+
conn.execute(f"CREATE NODE TABLE `{table_name}`({schema})")
|
|
633
|
+
except Exception:
|
|
634
|
+
pass
|
|
635
|
+
|
|
636
|
+
for table_name, schema, use_group in rel_tables:
|
|
637
|
+
try:
|
|
638
|
+
if use_group:
|
|
639
|
+
conn.execute(f"CREATE REL TABLE GROUP `{table_name}`({schema})")
|
|
640
|
+
else:
|
|
641
|
+
conn.execute(f"CREATE REL TABLE `{table_name}`({schema})")
|
|
642
|
+
except Exception:
|
|
643
|
+
pass
|