with_props=false skips the per-label/per-type JSON property-key discovery: * those json_each() scans walk EVERY row of each label/type (minutes-scale on * multi-million-node graphs) and get_architecture only needs the counts. */
| 3208 | * those json_each() scans walk EVERY row of each label/type (minutes-scale on |
| 3209 | * multi-million-node graphs) and get_architecture only needs the counts. */ |
| 3210 | static int get_schema_impl(cbm_store_t *s, const char *project, cbm_schema_info_t *out, |
| 3211 | bool with_props) { |
| 3212 | memset(out, 0, sizeof(*out)); |
| 3213 | if (!s || !s->db) { |
| 3214 | return CBM_NOT_FOUND; |
| 3215 | } |
| 3216 | |
| 3217 | /* Node labels */ |
| 3218 | { |
| 3219 | const char *sql = "SELECT label, COUNT(*) FROM nodes WHERE project = ?1 GROUP BY label " |
| 3220 | "ORDER BY COUNT(*) DESC;"; |
| 3221 | sqlite3_stmt *stmt = NULL; |
| 3222 | if (sqlite3_prepare_v2(s->db, sql, CBM_NOT_FOUND, &stmt, NULL) != SQLITE_OK || !stmt) { |
| 3223 | if (stmt) { |
| 3224 | sqlite3_finalize(stmt); |
| 3225 | } |
| 3226 | return CBM_NOT_FOUND; |
| 3227 | } |
| 3228 | bind_text(stmt, SKIP_ONE, project); |
| 3229 | |
| 3230 | int cap = ST_INIT_CAP_8; |
| 3231 | int n = 0; |
| 3232 | cbm_label_count_t *arr = malloc(cap * sizeof(cbm_label_count_t)); |
| 3233 | if (!arr) { |
| 3234 | sqlite3_finalize(stmt); |
| 3235 | return CBM_NOT_FOUND; |
| 3236 | } |
| 3237 | while (sqlite3_step(stmt) == SQLITE_ROW) { |
| 3238 | if (n >= cap) { |
| 3239 | int new_cap = cap * ST_GROWTH; |
| 3240 | void *tmp = realloc(arr, new_cap * sizeof(cbm_label_count_t)); |
| 3241 | if (!tmp) { |
| 3242 | for (int i = 0; i < n; i++) { |
| 3243 | safe_str_free(&arr[i].label); |
| 3244 | } |
| 3245 | free(arr); |
| 3246 | sqlite3_finalize(stmt); |
| 3247 | return CBM_NOT_FOUND; |
| 3248 | } |
| 3249 | arr = tmp; |
| 3250 | cap = new_cap; |
| 3251 | } |
| 3252 | arr[n].label = heap_strdup((const char *)sqlite3_column_text(stmt, 0)); |
| 3253 | arr[n].count = sqlite3_column_int(stmt, SKIP_ONE); |
| 3254 | arr[n].properties = NULL; |
| 3255 | arr[n].property_count = 0; |
| 3256 | n++; |
| 3257 | } |
| 3258 | sqlite3_finalize(stmt); |
| 3259 | out->node_labels = arr; |
| 3260 | out->node_label_count = n; |
| 3261 | } |
| 3262 | |
| 3263 | /* Node label property keys: base columns + distinct JSON property keys per label */ |
| 3264 | if (with_props) { |
| 3265 | static const char *node_base_cols[] = {"name", "qualified_name", "file_path", "start_line", |
| 3266 | "end_line"}; |
| 3267 | const char *prop_sql = "SELECT DISTINCT je.key " |
no test coverage detected