import { ident } from "pg-format";
import "../../../../../shared/src/bigint-json.js";
import "js-xxhash";
import { cvrSchema } from "../../../types/shards.js";
import { versionFromString, versionString } from "./types.js";
function schema(shard) {
  return ident(cvrSchema(shard));
}
function createSchema(shard) {
  return `CREATE SCHEMA IF NOT EXISTS ${schema(shard)};`;
}
function createInstancesTable(shard) {
  return (
    /*sql*/
    `
CREATE TABLE ${schema(shard)}.instances (
  "clientGroupID"  TEXT PRIMARY KEY,
  "version"        TEXT NOT NULL,                       -- Sortable representation of CVRVersion, e.g. "5nbqa2w:09"
  "lastActive"     TIMESTAMPTZ NOT NULL,                -- For garbage collection
  "ttlClock"       DOUBLE PRECISION NOT NULL DEFAULT 0, -- The ttl clock gets "paused" when disconnected.
  "replicaVersion" TEXT,                                -- Identifies the replica (i.e. initial-sync point) from which the CVR data comes.
  "owner"          TEXT,                                -- The ID of the task / server that has been granted ownership of the CVR.
  "grantedAt"      TIMESTAMPTZ,                         -- The time at which the current owner was last granted ownership (most recent connection time).
  "clientSchema"   JSONB,                               -- ClientSchema of the client group
  "profileID"      TEXT,                                -- Stable profile id ("p..."), falling back to the clientGroupID ("cg{clientGroupID}") for old clients
  "deleted"        BOOL DEFAULT FALSE                   -- Tombstone column for deleted CVRs; instances rows are kept longer for usage stats
);

-- For garbage collection.
CREATE INDEX instances_last_active
  ON ${schema(shard)}.instances ("lastActive") WHERE NOT "deleted";
CREATE INDEX tombstones_last_active
  ON ${schema(shard)}.instances ("lastActive") WHERE "deleted";

-- For usage stats; the composite index allows a 
-- SELECT COUNT(DISTINCT("profileID")) query to be answered by
-- an index scan without additional table lookups.
CREATE INDEX profile_ids_last_active ON ${schema(shard)}.instances ("lastActive", "profileID")
  WHERE "profileID" IS NOT NULL;
`
  );
}
function createClientsTable(shard) {
  return `
CREATE TABLE ${schema(shard)}.clients (
  "clientGroupID"      TEXT,
  "clientID"           TEXT,

  PRIMARY KEY ("clientGroupID", "clientID"),

  CONSTRAINT fk_clients_client_group
    FOREIGN KEY("clientGroupID")
    REFERENCES ${schema(shard)}.instances("clientGroupID")
    ON DELETE CASCADE
);

`;
}
function createQueriesTable(shard) {
  return `
CREATE TABLE ${schema(shard)}.queries (
  "clientGroupID"         TEXT,
  "queryHash"             TEXT, -- this is the hash of the client query AST
  "clientAST"             JSONB, -- this is nullable as custom queries will not persist an AST
  "queryName"             TEXT, -- the name of the query if it is a custom query
  "queryArgs"             JSON, -- the arguments of the query if it is a custom query
  "patchVersion"          TEXT,  -- NULL if only desired but not yet "got"
  "transformationHash"    TEXT,
  "transformationVersion" TEXT,
  "internal"              BOOL,  -- If true, no need to track / send patches
  "deleted"               BOOL,  -- put vs del "got" query

  PRIMARY KEY ("clientGroupID", "queryHash"),

  CONSTRAINT fk_queries_client_group
    FOREIGN KEY("clientGroupID")
    REFERENCES ${schema(shard)}.instances("clientGroupID")
    ON DELETE CASCADE
);

-- For catchup patches.
CREATE INDEX queries_patch_version 
  ON ${schema(shard)}.queries ("patchVersion" NULLS FIRST);
`;
}
function createDesiresTable(shard) {
  return `
CREATE TABLE ${schema(shard)}.desires (
  "clientGroupID"      TEXT,
  "clientID"           TEXT,
  "queryHash"          TEXT,
  "patchVersion"       TEXT NOT NULL,
  "deleted"            BOOL,  -- put vs del "desired" query
  "ttl"                INTERVAL,  -- DEPRECATED: Use ttlMs instead. Time to live for this client
  "ttlMs"              DOUBLE PRECISION,  -- Time to live in milliseconds
  "inactivatedAt"      TIMESTAMPTZ,  -- DEPRECATED: Use inactivatedAtMs instead. Time at which this row was inactivated
  "inactivatedAtMs"    DOUBLE PRECISION,  -- Time at which this row was inactivated (milliseconds since client group start)

  PRIMARY KEY ("clientGroupID", "clientID", "queryHash"),

  CONSTRAINT fk_desires_query
    FOREIGN KEY("clientGroupID", "queryHash")
    REFERENCES ${ident(cvrSchema(shard))}.queries("clientGroupID", "queryHash")
    ON DELETE CASCADE
);

-- For catchup patches.
CREATE INDEX desires_patch_version
  ON ${schema(shard)}.desires ("patchVersion");

CREATE INDEX desires_inactivated_at
  ON ${schema(shard)}.desires ("inactivatedAt");
`;
}
function rowsRowToRowID(rowsRow) {
  return {
    schema: rowsRow.schema,
    table: rowsRow.table,
    rowKey: rowsRow.rowKey
  };
}
function rowsRowToRowRecord(rowsRow) {
  return {
    id: rowsRowToRowID(rowsRow),
    rowVersion: rowsRow.rowVersion,
    patchVersion: versionFromString(rowsRow.patchVersion),
    refCounts: rowsRow.refCounts
  };
}
function rowRecordToRowsRow(clientGroupID, rowRecord) {
  return {
    clientGroupID,
    schema: rowRecord.id.schema,
    table: rowRecord.id.table,
    rowKey: rowRecord.id.rowKey,
    rowVersion: rowRecord.rowVersion,
    patchVersion: versionString(rowRecord.patchVersion),
    refCounts: rowRecord.refCounts
  };
}
function createRowsVersionTable(shard) {
  return `
CREATE TABLE ${schema(shard)}."rowsVersion" (
  "clientGroupID" TEXT PRIMARY KEY,
  "version"       TEXT NOT NULL
);
`;
}
function createRowsTable(shard) {
  return `
CREATE TABLE ${schema(shard)}.rows (
  "clientGroupID"    TEXT,
  "schema"           TEXT,
  "table"            TEXT,
  "rowKey"           JSONB,
  "rowVersion"       TEXT NOT NULL,
  "patchVersion"     TEXT NOT NULL,
  "refCounts"        JSONB,  -- {[queryHash: string]: number}, NULL for tombstone

  PRIMARY KEY ("clientGroupID", "schema", "table", "rowKey"),

  CONSTRAINT fk_rows_client_group
    FOREIGN KEY("clientGroupID")
    REFERENCES ${schema(shard)}."rowsVersion" ("clientGroupID")
    ON DELETE CASCADE
);

-- For catchup patches.
CREATE INDEX row_patch_version 
  ON ${schema(shard)}.rows ("patchVersion");

-- For listing rows returned by one or more query hashes. e.g.
-- SELECT * FROM cvr_shard.rows WHERE "refCounts" ?| array[...queryHashes...];
CREATE INDEX row_ref_counts ON ${schema(shard)}.rows 
  USING GIN ("refCounts");
`;
}
function createTables(shard) {
  return createSchema(shard) + createInstancesTable(shard) + createClientsTable(shard) + createQueriesTable(shard) + createDesiresTable(shard) + createRowsVersionTable(shard) + createRowsTable(shard);
}
async function setupCVRTables(lc, db, shard) {
  lc.info?.(`Setting up CVR tables`);
  await db.unsafe(createTables(shard));
}
export {
  createRowsVersionTable,
  rowRecordToRowsRow,
  rowsRowToRowID,
  rowsRowToRowRecord,
  setupCVRTables
};
//# sourceMappingURL=cvr.js.map
