44#include <unordered_map>
45#include <unordered_set>
60 uint64_t h; memcpy(&h, u.data, 8);
return static_cast<size_t>(h);
65 return memcmp(a.data, b.data, 16) == 0;
68using UUIDSet = std::unordered_set<pg_uuid_t, UUIDHash, UUIDEq>;
111static constexpr unsigned long NOTHING =
static_cast<unsigned long>(-1);
138 void open(
const std::string &path,
size_t elem_size) {
140 int fd =
::open(path.c_str(), O_RDONLY);
142 throw std::runtime_error(
"Cannot open " + path +
": " + strerror(errno));
143 sz_ =
static_cast<size_t>(lseek(fd, 0, SEEK_END));
144 base_ = ::mmap(
nullptr,
sz_, PROT_READ, MAP_SHARED, fd, 0);
146 if (
base_ == MAP_FAILED)
147 throw std::runtime_error(
"mmap(" + path +
"): " + strerror(errno));
149 elems_ =
reinterpret_cast<const uint8_t *
>(
hdr_ + 1);
163 void open(
const std::string &path) {
164 int fd =
::open(path.c_str(), O_RDONLY);
166 throw std::runtime_error(
"Cannot open " + path +
": " + strerror(errno));
167 sz_ =
static_cast<size_t>(lseek(fd, 0, SEEK_END));
168 base_ = ::mmap(
nullptr,
sz_, PROT_READ, MAP_SHARED, fd, 0);
170 if (
base_ == MAP_FAILED)
171 throw std::runtime_error(
"mmap(" + path +
"): " + strerror(errno));
180 uint64_t h; memcpy(&h, u.
data, 8);
181 unsigned long k = h %
cap_;
188 std::vector<std::pair<pg_uuid_t, unsigned long>>
allEntries()
const {
189 std::vector<std::pair<pg_uuid_t, unsigned long>> out;
190 if (!
hdr_)
return out;
191 out.reserve(
hdr_->nb_elements);
192 for (
unsigned long i = 0; i <
cap_; ++i)
205 void open(
const std::string &pgdata) {
206 mapping.open(pgdata +
"/provsql_mapping.mmap");
209 extra.open (pgdata +
"/provsql_extra.mmap",
sizeof(
char));
213 unsigned long idx =
mapping.lookup(u);
214 if (idx ==
NOTHING)
return nullptr;
219 std::vector<pg_uuid_t> result;
221 result.push_back(*
reinterpret_cast<const pg_uuid_t *
>(
wires.at(k)));
229 s += *
reinterpret_cast<const char *
>(
extra.at(k));
251 uint64_t(
'P') | uint64_t(
'v') << 8 | uint64_t(
'S') << 16 | uint64_t(
'G') << 24 |
252 uint64_t(
'a') << 32 | uint64_t(
't') << 40 | uint64_t(
'e') << 48 | uint64_t(
's') << 56;
254 uint64_t(
'P') | uint64_t(
'v') << 8 | uint64_t(
'S') << 16 | uint64_t(
'W') << 24 |
255 uint64_t(
'i') << 32 | uint64_t(
'r') << 40 | uint64_t(
'e') << 48 | uint64_t(
's') << 56;
257 uint64_t(
'P') | uint64_t(
'v') << 8 | uint64_t(
'S') << 16 | uint64_t(
'M') << 24 |
258 uint64_t(
'a') << 32 | uint64_t(
'p') << 40 | uint64_t(
'n') << 48 | uint64_t(
'g') << 56;
260 uint64_t(
'P') | uint64_t(
'v') << 8 | uint64_t(
'S') << 16 | uint64_t(
'E') << 24 |
261 uint64_t(
'x') << 32 | uint64_t(
't') << 40 | uint64_t(
'r') << 48 | uint64_t(
'a') << 56;
289 size_t elem_size,
const uint8_t *raw,
unsigned long nb)
291 unsigned long cap = 1UL << 16;
292 while (cap < nb) cap *= 2;
294 size_t fsz =
sizeof(
NewVecHdr) + elem_size * cap;
295 int fd = ::open(path.c_str(), O_RDWR | O_CREAT | O_TRUNC, 0600);
296 if (fd < 0)
throw std::runtime_error(
"Cannot create " + path +
": " + strerror(errno));
297 if (ftruncate(fd,
static_cast<off_t
>(fsz))) {
299 throw std::runtime_error(
"ftruncate(" + path +
"): " + strerror(errno));
301 void *base = ::mmap(
nullptr, fsz, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
303 if (base == MAP_FAILED)
throw std::runtime_error(
"mmap(" + path +
"): " + strerror(errno));
305 auto *hdr =
reinterpret_cast<NewVecHdr *
>(base);
308 hdr->elem_size =
static_cast<uint16_t
>(elem_size);
310 hdr->nb_elements = nb;
312 if (nb > 0) memcpy(hdr + 1, raw, nb * elem_size);
314 msync(base, fsz, MS_SYNC);
319 const std::vector<std::pair<pg_uuid_t, unsigned long>> &entries)
321 unsigned log_size = 16;
322 while ((1UL << log_size) < entries.size() * 2)
324 unsigned long cap = 1UL << log_size;
327 int fd = ::open(path.c_str(), O_RDWR | O_CREAT | O_TRUNC, 0600);
328 if (fd < 0)
throw std::runtime_error(
"Cannot create " + path +
": " + strerror(errno));
329 if (ftruncate(fd,
static_cast<off_t
>(fsz))) {
331 throw std::runtime_error(
"ftruncate(" + path +
"): " + strerror(errno));
333 void *base = ::mmap(
nullptr, fsz, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
335 if (base == MAP_FAILED)
throw std::runtime_error(
"mmap(" + path +
"): " + strerror(errno));
340 hdr->elem_size =
static_cast<uint16_t
>(
sizeof(
NewHashSlot));
342 hdr->log_size = log_size;
343 hdr->nb_elements = entries.size();
344 hdr->next_value = entries.size();
346 auto *slots =
reinterpret_cast<NewHashSlot *
>(hdr + 1);
347 for (
unsigned long i = 0; i < cap; ++i)
350 for (
auto &[u, v] : entries) {
351 uint64_t h; memcpy(&h, u.data, 8);
352 unsigned long k = h % cap;
353 while (slots[k].value !=
NOTHING)
359 msync(base, fsz, MS_SYNC);
367 explicit Conn(
const std::string &cs) :
c(PQconnectdb(cs.c_str())) {
368 if (PQstatus(
c) != CONNECTION_OK)
369 throw std::runtime_error(std::string(
"libpq connect: ") + PQerrorMessage(
c));
372 PGconn *
get()
const {
return c; }
379 bool ok()
const {
return PQresultStatus(
r) == PGRES_TUPLES_OK; }
381 const char *
get(
int row,
int col)
const {
return PQgetvalue(
r, row, col); }
388 auto hex = [](
char c) -> uint8_t {
389 if (c >=
'0' && c <=
'9')
return static_cast<uint8_t
>(c -
'0');
390 if (c >=
'a' && c <=
'f')
return static_cast<uint8_t
>(c -
'a' + 10);
391 return static_cast<uint8_t
>(c -
'A' + 10);
395 while (*s && b < 16) {
396 if (*s ==
'-') { ++s;
continue; }
397 u.
data[b++] =
static_cast<uint8_t
>((hex(s[0]) << 4) | hex(s[1]));
412static std::unordered_map<unsigned long, GateData>
415 std::unordered_map<unsigned long, GateData> result;
416 std::queue<pg_uuid_t> todo;
419 for (
auto &u : roots)
420 if (!visited.count(u)) { visited.insert(u); todo.push(u); }
422 while (!todo.empty()) {
427 if (result.count(idx))
continue;
436 result[idx] = std::move(gd);
438 for (
auto &child : children_copy)
439 if (!visited.count(child)) { visited.insert(child); todo.push(child); }
447 Oid db_oid,
const UUIDSet &roots)
449 std::string dir = pgdata +
"/base/" + std::to_string(db_oid);
450 std::string gates_path = dir +
"/provsql_gates.mmap";
453 if (stat(gates_path.c_str(), &st) == 0) {
454 std::cerr <<
" Skipping db " << db_oid <<
" (provsql_gates.mmap already exists)\n";
459 std::cerr <<
" db " << db_oid <<
": " << reachable.size() <<
" reachable gates\n";
462 std::unordered_map<unsigned long, pg_uuid_t> old_idx_to_uuid;
465 old_idx_to_uuid[v] = u;
470 std::unordered_map<pg_uuid_t, unsigned long, UUIDHash, UUIDEq> uuid_to_new;
471 unsigned long next_new = 0;
473 for (
auto &[old_idx, gd] : reachable) {
474 auto it = old_idx_to_uuid.find(old_idx);
475 if (it != old_idx_to_uuid.end() && !uuid_to_new.count(it->second))
476 uuid_to_new[it->second] = next_new++;
478 for (
auto &[old_idx, gd] : reachable) {
479 for (
auto &child : gd.children)
480 if (!uuid_to_new.count(child))
481 uuid_to_new[child] = next_new++;
484 for (
auto &u : roots)
485 if (!uuid_to_new.count(u))
486 uuid_to_new[u] = next_new++;
488 unsigned long N = next_new;
489 if (N == 0) { std::cerr <<
" No gates to write, skipping.\n";
return; }
492 std::vector<pg_uuid_t> new_idx_to_uuid(N);
493 for (
auto &[u, ni] : uuid_to_new)
494 new_idx_to_uuid[ni] = u;
497 std::vector<GateInformation> new_gates(N);
498 std::vector<pg_uuid_t> new_wires;
499 std::vector<uint8_t> new_extra;
501 for (
unsigned long ni = 0; ni < N; ++ni) {
510 auto rit = reachable.find(old_idx);
511 if (rit == reachable.end()) {
517 unsigned long wires_start = new_wires.size();
522 new_wires.push_back(child);
525 new_gates[ni] = gd.
gi;
527 new_gates[ni].extra_idx = 0;
528 new_gates[ni].extra_len = 0;
531 new_gates[ni].extra_idx = new_extra.size();
532 new_gates[ni].extra_len =
static_cast<unsigned>(gd.
extra_str.size());
534 new_extra.push_back(
static_cast<uint8_t
>(c));
539 std::vector<std::pair<pg_uuid_t, unsigned long>> mapping_entries(
540 uuid_to_new.begin(), uuid_to_new.end());
544 reinterpret_cast<const uint8_t *
>(new_gates.data()), N);
547 reinterpret_cast<const uint8_t *
>(new_wires.data()), new_wires.size());
550 new_extra.data(), new_extra.size());
554 std::cerr <<
" Wrote " << N <<
" gates to " << dir <<
"\n";
561 std::string pgdata, connstr;
563 for (
int i = 1; i < argc; ++i) {
564 if (strcmp(argv[i],
"-D") == 0 && i + 1 < argc) pgdata = argv[++i];
565 else if (strcmp(argv[i],
"-c") == 0 && i + 1 < argc) connstr = argv[++i];
568 if (pgdata.empty() || connstr.empty()) {
569 std::cerr <<
"Usage: provsql_migrate_mmap -D <pgdata> -c <connstr>\n";
574 if (stat((pgdata +
"/provsql_gates.mmap").c_str(), &st) != 0) {
575 std::cerr <<
"No provsql_gates.mmap found in " << pgdata <<
" — nothing to migrate.\n";
579 std::cerr <<
"Opening old circuit files in " << pgdata <<
"...\n";
583 }
catch (
const std::exception &e) {
584 std::cerr <<
"Error: " << e.what() <<
"\n";
588 std::cerr <<
"Connecting to cluster...\n";
592 Res dbs(PQexec(conn.
get(),
593 "SELECT oid, datname FROM pg_database "
594 "WHERE datallowconn AND NOT datistemplate "
597 std::cerr <<
"pg_database query failed: " << PQerrorMessage(conn.
get()) <<
"\n";
601 for (
int i = 0; i < dbs.
ntuples(); ++i) {
602 Oid db_oid =
static_cast<Oid
>(atol(dbs.
get(i, 0)));
603 std::string dbname = dbs.
get(i, 1);
604 std::cerr <<
"Database " << dbname <<
" (oid=" << db_oid <<
")...\n";
607 PGconn *dbc = PQconnectdb((connstr +
" dbname=" + dbname).c_str());
608 if (PQstatus(dbc) != CONNECTION_OK) {
609 std::cerr <<
" Cannot connect: " << PQerrorMessage(dbc) <<
"\n";
616 PGresult *r = PQexec(dbc,
"SELECT 1 FROM pg_extension WHERE extname='provsql'");
617 bool has_provsql = PQresultStatus(r) == PGRES_TUPLES_OK && PQntuples(r) > 0;
619 if (!has_provsql) { PQfinish(dbc);
continue; }
621 std::cerr <<
" provsql is installed\n";
625 PGresult *r = PQexec(dbc,
626 "SELECT attrelid::regclass::text "
628 "JOIN pg_class ON attrelid = pg_class.oid "
629 "JOIN pg_namespace ON relnamespace = pg_namespace.oid "
630 "WHERE attname = 'provsql' AND attisdropped = false "
631 " AND relkind = 'r' AND nspname <> 'provsql'");
633 if (PQresultStatus(r) != PGRES_TUPLES_OK) {
634 std::cerr <<
" Attribute query failed: " << PQerrorMessage(dbc) <<
"\n";
635 PQclear(r); PQfinish(dbc);
continue;
638 std::vector<std::string> tables;
639 for (
int j = 0; j < PQntuples(r); ++j)
640 tables.emplace_back(PQgetvalue(r, j, 0));
644 for (
const auto &tname : tables) {
646 std::string q =
"SELECT provsql FROM " + tname;
647 PGresult *tr = PQexec(dbc, q.c_str());
648 if (PQresultStatus(tr) != PGRES_TUPLES_OK) {
649 std::cerr <<
" Query on " << tname <<
" failed, skipping\n";
650 PQclear(tr);
continue;
652 for (
int k = 0; k < PQntuples(tr); ++k) {
653 const char *s = PQgetvalue(tr, k, 0);
654 if (s && s[0]) roots.insert(
parseUUID(s));
660 std::cerr <<
" " << roots.size() <<
" root UUIDs across "
661 << tables.size() <<
" table(s)\n";
666 }
catch (
const std::exception &e) {
667 std::cerr <<
"Error: " << e.what() <<
"\n";
674 static const char *old_files[] = {
675 "provsql_gates.mmap",
676 "provsql_wires.mmap",
677 "provsql_mapping.mmap",
678 "provsql_extra.mmap",
681 std::cerr <<
"Removing old flat circuit files from " << pgdata <<
":\n";
682 for (
int i = 0; old_files[i]; ++i) {
683 std::string path = pgdata +
"/" + old_files[i];
684 if (unlink(path.c_str()) == 0)
685 std::cerr <<
" Deleted " << path <<
"\n";
686 else if (errno != ENOENT)
687 std::cerr <<
" Warning: could not delete " << path <<
": " << strerror(errno) <<
"\n";
690 std::cerr <<
"Migration complete.\n"
691 "Please restart the PostgreSQL server.\n";
void open(const std::string &path)
unsigned long lookup(pg_uuid_t u) const
std::vector< std::pair< pg_uuid_t, unsigned long > > allEntries() const
const OldHashSlot * slots_
void open(const std::string &path, size_t elem_size)
const void * at(unsigned long k) const
unsigned long size() const
static pg_uuid_t parseUUID(const char *s)
static constexpr uint64_t MAGIC_GATES
std::unordered_set< pg_uuid_t, UUIDHash, UUIDEq > UUIDSet
static constexpr uint64_t MAGIC_WIRES
int main(int argc, char **argv)
static void migrateDatabase(const OldCircuit &old, const std::string &pgdata, Oid db_oid, const UUIDSet &roots)
static constexpr unsigned long NOTHING
static void writeNewVector(const std::string &path, uint64_t magic, size_t elem_size, const uint8_t *raw, unsigned long nb)
static void writeNewHashTable(const std::string &path, const std::vector< std::pair< pg_uuid_t, unsigned long > > &entries)
static std::unordered_map< unsigned long, GateData > collectReachable(const OldCircuit &old, const UUIDSet &roots)
static constexpr uint64_t MAGIC_EXTRA
static constexpr uint64_t MAGIC_MAPPING
Conn(const std::string &cs)
std::vector< pg_uuid_t > children
unsigned long nb_elements
unsigned long nb_elements
void open(const std::string &pgdata)
std::vector< pg_uuid_t > getChildren(const GateInformation *gi) const
std::string getExtra(const GateInformation *gi) const
const GateInformation * getGate(pg_uuid_t u) const
unsigned long nb_elements
unsigned long nb_elements
const char * get(int row, int col) const
bool operator()(pg_uuid_t a, pg_uuid_t b) const noexcept
size_t operator()(pg_uuid_t u) const noexcept