tiny.smg.context.query
Defined in context.
API (1)
Actions
Public operations.
Source
Source: tools/smg/src/context/query.zig
zig
const std = @import("std");const sql = @import("sql");const repo = @import("repo");const smg = @import("../root.zig");const context = @import("root.zig");const model = context.model;const rows = smg.storage.rows;const Visit = struct { name: []const u8, depth: u8 };pub fn run(builder: *model.Builder, reader: *smg.storage.database.Reader) !void { std.debug.assert(builder.target_count != 0); std.debug.assert(builder.target_count <= model.targets_max); var nodes = try reader.catalog.openRelation(reader.allocator, rows.nodes_relation); defer nodes.deinit(); var edges = try reader.catalog.openRelation(reader.allocator, rows.edges_relation); defer edges.deinit(); for (builder.targets[0..builder.target_count]) |*target| { if (!builder.active()) break; if (unsupported(target.input)) { target.state = .unsupported; builder.report.coverage.unsupported += 1; continue; } const node = try resolve(builder, &nodes, target) orelse continue; target.state = .resolved; target.name = node.name; builder.report.coverage.resolved += 1; _ = try append(builder, node, target.input, .target, "target"); try neighborhood(builder, &nodes, &edges, target.input, node.name); } std.mem.sort( model.Entry, builder.entries[0..builder.entry_count], builder.targets[0..builder.target_count], entryLess, );}fn resolve(builder: *model.Builder, nodes: anytype, target: *model.Target) !?smg.Node { std.debug.assert(target.state == .unqueried); std.debug.assert(target.input.len != 0); if (try exact(builder, nodes, target.input)) |node| return node; const translated = try translatedPath(builder, target.input); if (translated) |name| if (try exact(builder, nodes, name)) |node| return node; const query = translated orelse target.input; const slot = indexSlot(nodes, rows.node_name_index) orelse return error.StorageIndexMissing; var scan: sql.IndexScan = undefined; try nodes.relation.indexScan(&scan, builder.allocator, slot, null, null); defer scan.deinit(); var matches: [model.targets_max][]const u8 = undefined; var count: usize = 0; var complete = false; while (builder.row()) { const row = (try scan.next()) orelse { complete = true; break; }; builder.report.work.visited_index_rows += 1; var values: [1]sql.row.Value = undefined; var key_buffer: [sql.page.size]u8 = undefined; const name = try smg.storage.names.indexedName(&values, &key_buffer, row.key); if (!smg.name.matchesSuffix(name, query)) continue; if (contains(matches[0..count], name)) continue; if (count == matches.len) { builder.exhausted(); break; } matches[count] = try builder.allocator.dupe(u8, name); count += 1; } target.candidates = try builder.allocator.dupe([]const u8, matches[0..count]); if (count > 1) { target.state = .ambiguous; builder.report.coverage.ambiguous += 1; } else if (complete and count == 1) { return exact(builder, nodes, matches[0]); } else if (complete) { target.state = .missing; builder.report.coverage.missing += 1; } return null;}fn exact(builder: *model.Builder, nodes: anytype, name: []const u8) !?smg.Node { std.debug.assert(name.len != 0); std.debug.assert(builder.report.work.index_steps <= builder.options.limits.rows); if (!builder.row()) return null; const slot = indexSlot(nodes, rows.node_name_index) orelse return error.StorageIndexMissing; var lookup: sql.IndexScan = undefined; try nodes.relation.lookup(&lookup, builder.allocator, slot, &.{.{ .text = name }}); defer lookup.deinit(); const found = (try lookup.next()) orelse return null; builder.report.work.visited_index_rows += 1; var buffer: [model.row_bytes_max]u8 = undefined; const bytes = (try nodes.relation.getInto(found.rowid, &buffer)) orelse return error.StorageIndexCorrupt; const node = (try rows.decodeNodeRow(builder.allocator, found.rowid, bytes)).node; builder.report.work.loaded_nodes += 1; try validNode(node); return node;}fn neighborhood( builder: *model.Builder, nodes: anytype, edges: anytype, target: []const u8, name: []const u8,) !void { std.debug.assert(name.len != 0); std.debug.assert(builder.options.limits.depth <= 2); var queue: [model.entries_max]Visit = undefined; queue[0] = .{ .name = name, .depth = 0 }; var count: usize = 1; var cursor: usize = 0; while (cursor < count and builder.active()) : (cursor += 1) { const visit = queue[cursor]; builder.report.work.depth_reached = @max(builder.report.work.depth_reached, visit.depth); if (visit.depth == builder.options.limits.depth) { builder.report.omissions.depth = true; builder.exhausted(); continue; } try adjacency(builder, nodes, edges, target, visit, true, &queue, &count); try adjacency(builder, nodes, edges, target, visit, false, &queue, &count); }}fn adjacency( builder: *model.Builder, nodes: anytype, edges: anytype, target: []const u8, visit: Visit, incoming: bool, queue: []Visit, count: *usize,) !void { std.debug.assert(visit.depth < builder.options.limits.depth); std.debug.assert(count.* != 0); std.debug.assert(count.* <= queue.len); const index = if (incoming) rows.edge_target_index else rows.edge_source_index; const slot = indexSlot(edges, index) orelse return error.StorageIndexMissing; var lookup: sql.IndexScan = undefined; try edges.relation.lookup(&lookup, builder.allocator, slot, &.{.{ .text = visit.name }}); defer lookup.deinit(); while (builder.row()) { const found = (try lookup.next()) orelse break; builder.report.work.visited_index_rows += 1; builder.report.work.visited_edges += 1; var buffer: [model.row_bytes_max]u8 = undefined; const bytes = (try edges.relation.getInto(found.rowid, &buffer)) orelse return error.StorageIndexCorrupt; const edge = (try rows.decodeEdgeRow(builder.allocator, found.rowid, bytes)).edge; try validEdge(edge); const name = if (incoming) edge.source else edge.target; if (std.mem.eql(u8, name, queue[0].name)) continue; const node = try exact(builder, nodes, name) orelse continue; const section = classify(node, edge, incoming, visit.depth); if (!try append(builder, node, target, section, edge.rel)) continue; enqueue(builder, queue, count, .{ .name = node.name, .depth = visit.depth + 1 }); }}fn classify(node: smg.Node, edge: smg.Edge, incoming: bool, depth: u8) model.Section { std.debug.assert(node.name.len != 0); std.debug.assert(depth < 2); if (smg.api.isTestDeclaration(node)) return .tests; if (incoming and std.mem.eql(u8, edge.rel, smg.model.RelType.contains)) return .owner; if (incoming) return if (depth == 0) .usages else .impact; return .dependencies;}fn append( builder: *model.Builder, node: smg.Node, target: []const u8, section: model.Section, relation: []const u8,) !bool { std.debug.assert(node.name.len != 0); std.debug.assert(target.len != 0); var entry = model.Entry{ .target = target, .section = section, .relation = relation, .name = node.name, .path = node.file, .line = node.line, }; if (!builder.add(entry)) return false; try context.source.inspect(builder, &entry, node); builder.entries[builder.entry_count - 1] = entry; return true;}fn enqueue(builder: *model.Builder, queue: []Visit, count: *usize, visit: Visit) void { std.debug.assert(count.* <= queue.len); std.debug.assert(visit.depth <= builder.options.limits.depth); for (queue[0..count.*]) |existing| if (std.mem.eql(u8, existing.name, visit.name)) return; if (count.* == queue.len) { builder.exhausted(); return; } queue[count.*] = visit; count.* += 1;}fn indexSlot(handle: anytype, name: []const u8) ?usize { for (handle.index_definitions, 0..) |definition, slot| { if (std.mem.eql(u8, definition.name, name)) return slot; } return null;}fn contains(names: []const []const u8, name: []const u8) bool { std.debug.assert(names.len <= model.targets_max); for (names) |entry| if (std.mem.eql(u8, entry, name)) return true; return false;}fn translatedPath(builder: *model.Builder, input: []const u8) !?[]const u8 { const path = std.mem.indexOfScalar(u8, input, '/') != null or smg.scan.scan_files.supported(input); if (!path) return null; const relative = try repo.work.resolvedPathAlloc(builder.allocator, builder.root, input); const prefixed = try std.fmt.allocPrint(builder.allocator, "./{s}", .{relative}); return smg.name.dottedFromPath(builder.allocator, prefixed);}fn unsupported(input: []const u8) bool { if (std.mem.indexOfScalar(u8, input, '/') == null) return false; if (std.fs.path.extension(input).len == 0) return false; return !smg.scan.scan_files.supported(input);}fn validEdge(edge: smg.Edge) !void { for ([_][]const u8{ edge.source, edge.rel, edge.target }) |value| { if (value.len > 4096 or !std.unicode.utf8ValidateSlice(value)) { return error.InvalidInspectionEdge; } }}fn validNode(node: smg.Node) !void { if (node.name.len > 4096 or !std.unicode.utf8ValidateSlice(node.name)) { return error.InvalidInspectionNode; } if (node.file) |path| if (path.len > 4096 or !std.unicode.utf8ValidateSlice(path)) { return error.InvalidInspectionNode; };}fn entryLess(targets: []model.Target, left: model.Entry, right: model.Entry) bool { std.debug.assert(targets.len != 0); std.debug.assert(targets.len <= model.targets_max); if (!std.mem.eql(u8, left.target, right.target)) { for (targets) |target| { if (std.mem.eql(u8, target.input, left.target)) return true; if (std.mem.eql(u8, target.input, right.target)) return false; } unreachable; } if (left.section != right.section) { return @backingInt(left.section) < @backingInt(right.section); } const name = std.mem.order(u8, left.name, right.name); if (name != .eq) return name == .lt; return std.mem.lessThan(u8, left.relation, right.relation);}Source: tools/smg/src/context/root.zig:7
zig
pub const query = @import("query.zig");Audit
| Definitions | 2 |
|---|---|
| Public names | 2 |
| Members | 0 |
| Version | 26.7.0 |
| Revision | daab053ee433 |