35#include "executor/spi.h"
36#include "catalog/pg_type.h"
37#include "utils/array.h"
38#include "utils/builtins.h"
39#if PG_VERSION_NUM >= 160000
64std::string text_to_string(text *t)
66 return std::string(VARDATA_ANY(t), VARSIZE_ANY_EXHDR(t));
70Datum string_vector_to_text_array(
const std::vector<std::string> &v)
73 return PointerGetDatum(construct_empty_array(TEXTOID));
75 std::vector<Datum> elems;
76 elems.reserve(v.size());
77 for (
const auto &s : v)
78 elems.push_back(PointerGetDatum(cstring_to_text_with_len(s.data(),
81 ArrayType *arr = construct_array(elems.data(),
82 static_cast<int>(elems.size()),
84 return PointerGetDatum(arr);
89std::vector<std::string> text_array_to_string_vector(ArrayType *arr)
91 std::vector<std::string> out;
97 for (
int i = 0; i < n; ++i) {
100 out.push_back(text_to_string(DatumGetTextPP(elems[i])));
106void require_superuser(
const char *fn)
109 provsql_error(
"%s: must be superuser (a tool record can run arbitrary "
110 "commands as the PostgreSQL OS user)", fn);
116std::vector<std::string> spi_text_array(HeapTuple t, TupleDesc td,
int col)
119 Datum d = SPI_getbinval(t, td, col, &isnull);
122 return text_array_to_string_vector(DatumGetArrayTypeP(d));
126std::string spi_text(HeapTuple t, TupleDesc td,
int col)
128 char *s = SPI_getvalue(t, td, col);
129 return s ? std::string(s) :
std::string();
134bool overrides_table_exists()
136 if (SPI_execute(
"SELECT to_regclass('provsql.tool_overrides') IS NOT NULL",
137 true, 1) != SPI_OK_SELECT || SPI_processed != 1)
140 Datum d = SPI_getbinval(SPI_tuptable->vals[0], SPI_tuptable->tupdesc,
142 return !isnull && DatumGetBool(d);
148 Oid types[13] = {TEXTOID, TEXTOID, TEXTOID, TEXTARRAYOID, TEXTARRAYOID,
149 TEXTOID, TEXTOID, INT4OID, BOOLOID, TEXTARRAYOID,
150 TEXTOID, TEXTOID, TEXTOID};
152 CStringGetTextDatum(rec.
name.c_str()),
153 CStringGetTextDatum(rec.
kind.c_str()),
154 CStringGetTextDatum(rec.
binary.c_str()),
158 CStringGetTextDatum(rec.
parser.c_str()),
162 CStringGetTextDatum(rec.
argtpl.c_str()),
164 CStringGetTextDatum(rec.
endpoint.c_str()),
166 SPI_execute_with_args(
167 "INSERT INTO provsql.tool_overrides "
168 "(name, removed, kind, executable, operations, input_formats, "
169 " output_format, parser, preference, enabled, dependencies, argtpl, "
170 " argtpl_circuit, endpoint) "
171 "VALUES ($1, false, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13) "
172 "ON CONFLICT (name) DO UPDATE SET "
173 " removed=false, kind=$2, executable=$3, operations=$4, "
174 " input_formats=$5, output_format=$6, parser=$7, preference=$8, "
175 " enabled=$9, dependencies=$10, argtpl=$11, argtpl_circuit=$12, "
177 13, types, vals, NULL,
false, 0);
181void tombstone_override(
const std::string &name)
183 Oid types[1] = {TEXTOID};
184 Datum vals[1] = {CStringGetTextDatum(name.c_str())};
185 SPI_execute_with_args(
186 "INSERT INTO provsql.tool_overrides (name, removed) VALUES ($1, true) "
187 "ON CONFLICT (name) DO UPDATE SET removed=true, kind=NULL, "
188 " executable=NULL, operations=NULL, input_formats=NULL, "
189 " output_format=NULL, parser=NULL, preference=NULL, enabled=NULL, "
190 " dependencies=NULL, argtpl=NULL, argtpl_circuit=NULL, endpoint=NULL",
191 1, types, vals, NULL,
false, 0);
205 if (SPI_connect() != SPI_OK_CONNECT)
207 if (overrides_table_exists()) {
209 "SELECT name, removed, kind, executable, operations, input_formats, "
210 " output_format, parser, preference, enabled, dependencies, argtpl, "
211 " argtpl_circuit, endpoint FROM provsql.tool_overrides",
true, 0)
213 TupleDesc td = SPI_tuptable->tupdesc;
214 for (uint64 i = 0; i < SPI_processed; ++i) {
215 HeapTuple t = SPI_tuptable->vals[i];
216 std::string name = spi_text(t, td, 1);
218 Datum rd = SPI_getbinval(t, td, 2, &isnull);
219 if (!isnull && DatumGetBool(rd)) {
225 rec.
kind = spi_text(t, td, 3);
226 rec.
binary = spi_text(t, td, 4);
230 rec.
parser = spi_text(t, td, 8);
231 Datum pd = SPI_getbinval(t, td, 9, &isnull);
232 rec.
preference = isnull ? 0 : DatumGetInt32(pd);
233 Datum ed = SPI_getbinval(t, td, 10, &isnull);
234 rec.
enabled = isnull ? true : DatumGetBool(ed);
236 rec.
argtpl = spi_text(t, td, 12);
264 ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
266 MemoryContext per_query_ctx = rsinfo->econtext->ecxt_per_query_memory;
267 MemoryContext oldcontext = MemoryContextSwitchTo(per_query_ctx);
270 if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE) {
271 MemoryContextSwitchTo(oldcontext);
272 provsql_error(
"tool_registry_list: function must return a row type");
274 tupdesc = BlessTupleDesc(tupdesc);
276 Tuplestorestate *tupstore = tuplestore_begin_heap(
277 rsinfo->allowedModes & SFRM_Materialize_Random,
false, work_mem);
278 rsinfo->returnMode = SFRM_Materialize;
279 rsinfo->setResult = tupstore;
280 rsinfo->setDesc = tupdesc;
285 bool nulls[13] = {
false,
false,
false,
false,
false,
false,
false,
286 false,
false,
false,
false,
false,
false};
288 values[0] = PointerGetDatum(cstring_to_text_with_len(rec.name.data(),
290 values[1] = PointerGetDatum(cstring_to_text_with_len(rec.kind.data(),
292 values[2] = PointerGetDatum(cstring_to_text_with_len(rec.binary.data(),
294 values[3] = string_vector_to_text_array(rec.operations);
295 values[4] = string_vector_to_text_array(rec.input_formats);
296 values[5] = PointerGetDatum(cstring_to_text_with_len(
297 rec.output_format.data(), rec.output_format.size()));
298 values[6] = PointerGetDatum(cstring_to_text_with_len(rec.parser.data(),
300 values[7] = Int32GetDatum(rec.preference);
301 values[8] = BoolGetDatum(rec.enabled);
302 values[9] = PointerGetDatum(cstring_to_text_with_len(rec.argtpl.data(),
304 values[10] = PointerGetDatum(cstring_to_text_with_len(
305 rec.argtpl_circuit.data(), rec.argtpl_circuit.size()));
306 values[11] = PointerGetDatum(cstring_to_text_with_len(
307 rec.endpoint.data(), rec.endpoint.size()));
310 tuplestore_putvalues(tupstore, tupdesc, values, nulls);
312 }
catch (
const std::exception &e) {
313 MemoryContextSwitchTo(oldcontext);
316 MemoryContextSwitchTo(oldcontext);
320 MemoryContextSwitchTo(oldcontext);
338 require_superuser(
"register_tool");
345 rec.
name = text_to_string(PG_GETARG_TEXT_PP(0));
347 : text_to_string(PG_GETARG_TEXT_PP(1));
348 rec.
kind = PG_ARGISNULL(2) ? std::string(
"cli")
349 : text_to_string(PG_GETARG_TEXT_PP(2));
350 if (!PG_ARGISNULL(3))
351 rec.
operations = text_array_to_string_vector(PG_GETARG_ARRAYTYPE_P(3));
352 if (!PG_ARGISNULL(4))
353 rec.
input_formats = text_array_to_string_vector(PG_GETARG_ARRAYTYPE_P(4));
354 if (!PG_ARGISNULL(5))
356 if (!PG_ARGISNULL(6))
357 rec.
parser = text_to_string(PG_GETARG_TEXT_PP(6));
358 if (!PG_ARGISNULL(7))
359 rec.
argtpl = text_to_string(PG_GETARG_TEXT_PP(7));
360 if (!PG_ARGISNULL(8))
362 rec.
preference = PG_ARGISNULL(9) ? 0 : PG_GETARG_INT32(9);
363 rec.
enabled = PG_ARGISNULL(10) ? true : PG_GETARG_BOOL(10);
364 if (!PG_ARGISNULL(11))
365 rec.
endpoint = text_to_string(PG_GETARG_TEXT_PP(11));
367 if (rec.
name.empty())
371 if (SPI_connect() != SPI_OK_CONNECT)
373 upsert_override(rec);
375 }
catch (
const std::exception &e) {
390 require_superuser(
"unregister_tool");
391 std::string name = text_to_string(PG_GETARG_TEXT_PP(0));
395 provsql_error(
"unregister_tool: no tool named '%s' is registered",
398 if (SPI_connect() != SPI_OK_CONNECT)
400 tombstone_override(name);
414 provsql_error(
"%s: no tool named '%s' is registered", fn, name.c_str());
417 if (SPI_connect() != SPI_OK_CONNECT)
419 upsert_override(rec);
427 require_superuser(
"set_tool_enabled");
428 std::string name = text_to_string(PG_GETARG_TEXT_PP(0));
429 bool enabled = PG_GETARG_BOOL(1);
439 require_superuser(
"set_tool_preference");
440 std::string name = text_to_string(PG_GETARG_TEXT_PP(0));
441 int preference = PG_GETARG_INT32(1);
PostgreSQL cross-version compatibility shims for ProvSQL.
#define TYPALIGN_INT
int alignment code for the array routines (construct_array / deconstruct_array).
ToolRegistry & tool_registry()
Shorthand for ToolRegistry::instance().
Uniform error-reporting macros for ProvSQL.
#define provsql_error(fmt,...)
Report a fatal ProvSQL error and abort the current transaction.