ProvSQL C/C++ API
Adding support for provenance and uncertainty management to PostgreSQL databases
Loading...
Searching...
No Matches
ToolRegistry.h
Go to the documentation of this file.
1/**
2 * @file ToolRegistry.h
3 * @brief In-memory catalog of the external tools ProvSQL can invoke.
4 *
5 * ProvSQL shells out to several knowledge compilers / model counters /
6 * visualisers (@c d4, @c d4v2, @c c2d, @c minic2d, @c dsharp, @c ganak,
7 * @c weightmc, @c graph-easy). Historically the set of tools, their
8 * executable names, and which one is preferred for a given operation were
9 * compiled in as string literals scattered across a dozen call sites. This
10 * registry turns that into a single in-memory table of @ref provsql::ToolRecord, so
11 * the dispatchers query metadata instead of testing literals.
12 *
13 * The registry is seeded at first use with exactly the tools ProvSQL has
14 * always known about, with their current invocations and a default
15 * preference order, so the out-of-the-box behaviour is unchanged: a fresh
16 * backend behaves identically with no registration call. An administrator
17 * may then add / repoint / reorder / disable tools at run time through the
18 * SQL surface (@c provsql.register_tool, @c provsql.unregister_tool,
19 * @c provsql.set_tool_enabled, @c provsql.set_tool_preference, and the
20 * read-only @c provsql.tools view).
21 *
22 * @par Lifetime
23 * The catalog is process-local (one copy per PostgreSQL backend), seeded
24 * from compiled-in defaults and mutated in memory. It is therefore
25 * **per-session and transient**: registrations are visible only within the
26 * backend that made them and are reset when the session ends. This is the
27 * deliberate first-stage backing; a future stage may back it with a shared
28 * catalog table without changing this interface.
29 *
30 * @par Standalone tdkc
31 * The standalone @c tdkc tool deliberately uses no external tool, so it does
32 * not link this registry: every reference to it in @c BooleanCircuit.cpp is
33 * guarded by @c \#ifndef @c TDKC. Keep this header free of any PostgreSQL or
34 * external-tool dependency so it stays a self-contained piece of metadata.
35 */
36#ifndef PROVSQL_TOOL_REGISTRY_H
37#define PROVSQL_TOOL_REGISTRY_H
38
39#include <string>
40#include <utility>
41#include <vector>
42
43namespace provsql {
44
45/**
46 * @brief Expand a command template into a runnable shell command line.
47 *
48 * Replaces @c {binary} / @c {in} / @c {out} and any @p extra placeholders
49 * (e.g. @c {tmpdir}, @c {pivotAC}) in @p tpl. When @p tpl contains no
50 * @c {binary} placeholder, a non-empty @p binary is prepended (the common
51 * "<binary> <args>" shape); a template that places the binaries itself (the
52 * @c dpmc pipeline) is returned as-is.
53 *
54 * Header-only and dependency-free on purpose, so the standalone @c tdkc
55 * build can expand a template without linking the registry.
56 */
57inline std::string expandCommandTemplate(
58 const std::string &tpl, const std::string &binary,
59 const std::string &in, const std::string &out,
60 const std::vector<std::pair<std::string, std::string>> &extra = {})
61{
62 auto sub = [](std::string &s, const std::string &key,
63 const std::string &value) {
64 const std::string token = "{" + key + "}";
65 std::string::size_type pos = 0;
66 while ((pos = s.find(token, pos)) != std::string::npos) {
67 s.replace(pos, token.size(), value);
68 pos += value.size();
69 }
70 };
71 std::string cmd = tpl;
72 sub(cmd, "binary", binary);
73 sub(cmd, "in", in);
74 sub(cmd, "out", out);
75 for (const auto &kv : extra)
76 sub(cmd, kv.first, kv.second);
77 if (tpl.find("{binary}") == std::string::npos && !binary.empty())
78 return binary + " " + cmd;
79 return cmd;
80}
81
82/**
83 * @brief One registered external tool.
84 *
85 * @c name is the logical id used by the dispatchers and the
86 * @c provsql.fallback_compiler GUC (e.g. @c "d4"). @c binary is the
87 * executable resolved through @c find_external_tool; it defaults to
88 * @c name but may be repointed (e.g. an absolute path to a specific build)
89 * without changing the logical id. Distinct logical ids may share one
90 * @c binary: the three @c panini-* compiler variants all run @c "panini"
91 * with a different @c --lang. @c operations advertises what the tool can
92 * do: @c "compile" (knowledge compilation to a d-DNNF / NNF the
93 * @c compilation() parser reads), @c "wmc" (weighted model counting), or
94 * @c "render" (DOT visualisation); a record with no operation would be
95 * unselectable, so every record advertises at least one. @c preference
96 * orders candidates within an operation (higher first); @c enabled lets an
97 * admin keep a record but stop the dispatchers from selecting it.
98 *
99 * @c dependencies lists extra executables the tool needs at run time beyond
100 * @c binary: @c sharpsat-td needs @c flow_cutter_pace17, and @c dpmc is a
101 * two-binary pipeline (@c htb @c | @c dmc) with an empty @c binary and
102 * @c dependencies @c = @c {htb, dmc}. A tool is "available" iff @c binary
103 * (when non-empty) and every dependency resolve on PATH.
104 *
105 * The capability triple @c operations / @c input_formats / @c output_format
106 * uses the **KCMCP shared registry names** (see
107 * doc/source/dev/kc-server-protocol.rst), so a CLI record and a future
108 * @c kind="kcmcp" server record are directly comparable: @c operations is
109 * @c "compile" / @c "wmc" (the ProvSQL-local @c "render" has no KCMCP
110 * counterpart); @c input_formats is drawn from @c "dimacs-cnf" /
111 * @c "circuit-bcs12"; @c output_format from @c "ddnnf-nnf" / @c "decimal" /
112 * @c "rational" (with ProvSQL-local @c "panini-dd" / @c "ascii" where KCMCP
113 * has no code). A KCMCP server *discovers* this triple at the handshake; a
114 * CLI tool *declares* it here.
115 *
116 * @c parser is **CLI-only** -- it says how to decode this tool's raw output
117 * into @c output_format, a job a KCMCP server does on the wire and so leaves
118 * empty: the single tolerant @c "nnf" reader yields @c output_format
119 * @c "ddnnf-nnf" (it auto-detects the c2d/d4 magic header, covering both the
120 * d4-family and classic forms); @c "wmc-line" (the @c "c s exact" line) and
121 * @c "weightmc" (mantissa x 2^e) both yield @c "decimal"; @c "panini-dd" and
122 * @c "ascii" are their own.
123 *
124 * @c argtpl is the command the dispatcher runs, with @c {in} / @c {out}
125 * substituted by the input/output temp files and a few tool-specific
126 * placeholders (@c {binary}, @c {tmpdir}, @c {pivotAC}). When @c argtpl
127 * contains no @c {binary}, the resolved @c binary is prepended; otherwise
128 * the template is the whole command (used by the @c dpmc pipeline and the
129 * @c sharpsat-td @c cd-prefix). @c argtpl_circuit is the alternative command
130 * used when the @c "circuit-bcs12" input is selected (the input is then a
131 * BC-S1.2 circuit rather than a Tseytin CNF); only a tool that accepts that
132 * input needs it. Together these make a CLI tool's whole invocation data: a
133 * new tool whose output is a known parser/format can be registered without
134 * recompiling.
135 */
137 std::string name;
138 std::string kind; ///< "cli" (spawn a binary) or "kcmcp"
139 ///< (talk to a socket server at @c endpoint).
140 std::string binary;
141 std::vector<std::string> operations;
142 std::vector<std::string> input_formats;
143 std::string output_format;
144 std::string parser;
145 int preference = 0;
146 bool enabled = true;
147 std::vector<std::string> dependencies;
148 std::string argtpl;
149 std::string argtpl_circuit;
150 std::string endpoint; ///< KCMCP server address for kind
151 ///< "kcmcp": "unix:/path" or "host:port".
152
153 bool hasOperation(const std::string &op) const;
154 bool acceptsInput(const std::string &fmt) const;
155
156 /**
157 * @brief Build the command line for this tool.
158 *
159 * Expands @c {in} / @c {out} (and @p extra placeholders, e.g.
160 * @c {tmpdir}, @c {pivotAC}) in @c argtpl. @p binary_override is the
161 * executable to use (the registry-resolved @c binary, possibly repointed);
162 * when @c argtpl references @c {binary} it is substituted inline, otherwise
163 * a non-empty binary is prepended.
164 */
165 std::string buildCommand(
166 const std::string &in, const std::string &out,
167 const std::string &binary_override,
168 const std::vector<std::pair<std::string, std::string>> &extra = {}) const;
169};
170
171/**
172 * @brief The process-local registry singleton.
173 *
174 * Not thread-safe; PostgreSQL backends are single-threaded, which is the
175 * only context that touches it.
176 */
178public:
179 /// Access the per-process registry, seeding it on first use.
180 static ToolRegistry &instance();
181
182 /// Find a record by logical name, or @c nullptr if none is registered.
183 const ToolRecord *find(const std::string &name) const;
184
185 /// True iff a record named @p name exists, is enabled, and advertises @p op.
186 bool provides(const std::string &name, const std::string &op) const;
187
188 /**
189 * @brief Enabled tools advertising @p op, ordered by descending
190 * preference then name.
191 */
192 std::vector<const ToolRecord *> byOperation(const std::string &op) const;
193
194 /// All records, in registration order (used by the @c provsql.tools view).
195 const std::vector<ToolRecord> &records() const {
196 return records_;
197 }
198
199 /// Register a new tool or replace the record with the same name.
200 void upsert(const ToolRecord &rec);
201
202 /// Remove the record named @p name; returns false if none existed.
203 bool remove(const std::string &name);
204
205 /// Flip the @c enabled flag of @p name; returns false if none existed.
206 bool setEnabled(const std::string &name, bool enabled);
207
208 /// Set the @c preference of @p name; returns false if none existed.
209 bool setPreference(const std::string &name, int preference);
210
211 /// Discard all records and re-seed the compiled-in defaults.
212 void reset();
213
214private:
216 seed();
217 }
218 void seed();
219
220 std::vector<ToolRecord> records_;
221};
222
223/// Shorthand for @c ToolRegistry::instance().
225 return ToolRegistry::instance();
226}
227
228} // namespace provsql
229
230#endif
The process-local registry singleton.
const std::vector< ToolRecord > & records() const
All records, in registration order (used by the provsql.tools view).
std::vector< const ToolRecord * > byOperation(const std::string &op) const
Enabled tools advertising op, ordered by descending preference then name.
bool provides(const std::string &name, const std::string &op) const
True iff a record named name exists, is enabled, and advertises op.
std::vector< ToolRecord > records_
static ToolRegistry & instance()
Access the per-process registry, seeding it on first use.
void upsert(const ToolRecord &rec)
Register a new tool or replace the record with the same name.
bool setPreference(const std::string &name, int preference)
Set the preference of name; returns false if none existed.
void reset()
Discard all records and re-seed the compiled-in defaults.
bool setEnabled(const std::string &name, bool enabled)
Flip the enabled flag of name; returns false if none existed.
bool remove(const std::string &name)
Remove the record named name; returns false if none existed.
const ToolRecord * find(const std::string &name) const
Find a record by logical name, or nullptr if none is registered.
ToolRegistry & tool_registry()
Shorthand for ToolRegistry::instance().
std::string expandCommandTemplate(const std::string &tpl, const std::string &binary, const std::string &in, const std::string &out, const std::vector< std::pair< std::string, std::string > > &extra={})
Expand a command template into a runnable shell command line.
One registered external tool.
std::string output_format
bool acceptsInput(const std::string &fmt) const
std::vector< std::string > dependencies
std::string argtpl_circuit
std::string kind
"cli" (spawn a binary) or "kcmcp" (talk to a socket server at endpoint).
std::string endpoint
KCMCP server address for kind "kcmcp": "unix:/path" or "host:port".
bool hasOperation(const std::string &op) const
std::vector< std::string > input_formats
std::vector< std::string > operations
std::string buildCommand(const std::string &in, const std::string &out, const std::string &binary_override, const std::vector< std::pair< std::string, std::string > > &extra={}) const
Build the command line for this tool.