# @fjall/clickhouse

Runtime helpers for ClickHouse migration containers. Consumes the user manifest published by `@fjall/components-infrastructure` `ClickHouseDatabase` and provisions workload users in writable `users_local` storage. Also ships a generic SQL-file runner and a connection-retry helper.

## Installation

```bash
npm install @fjall/clickhouse @clickhouse/client
```

`@clickhouse/client` is a peer dependency — the consumer supplies the runtime client.

## Env-var contract

The framework's commitment is the env-var shape, not this package's API. A customer rolling their own consumer reads the same env vars.

| Env var                    | Shape                                   | Producer                                                  |
| -------------------------- | --------------------------------------- | --------------------------------------------------------- |
| `CLICKHOUSE_MANAGED_USERS` | JSON `Array<string>` of user names      | `ClickHouseDatabase.getMigrationContributions()`          |
| `USER_<NAME>_PASSWORD`     | plaintext (one per managed-users entry) | ECS executionRole + Secrets Manager (no runtime SDK call) |

The manifest carries names only — the schema admin (XML-defined) is NOT included, and profile binding is the customer SQL's job (`ALTER USER <name> SETTINGS PROFILE '<profile>'`).

The constant name + env-name helper + schema live in `@fjall/util/migration`:

```typescript
import {
  CLICKHOUSE_MANAGED_USERS_ENV,
  ManagedUserNamesSchema,
  userPasswordEnvName,
} from "@fjall/util/migration";
```

This package re-exports them for convenience so consumers don't need two imports.

## Usage

```typescript
import {
  connectWithRetry,
  createConsoleMigrationLogger,
  provisionUsersFromEnv,
  runSqlMigrations,
} from "@fjall/clickhouse";
import { createClient } from "@clickhouse/client";

const logger = createConsoleMigrationLogger("migration-runner");
const client = createClient({ url, username, password, database });

await connectWithRetry(client, { label: "user-provision", logger });
await provisionUsersFromEnv({ client, logger });
await runSqlMigrations({ client, dir: "./clickhouse-init", logger });
```

`provisionUsersFromEnv` is idempotent — it issues `CREATE USER IF NOT EXISTS` then `ALTER USER` for every manifest entry, so repeated invocations are safe.

## Rolling your own consumer

The env-var contract above is stable. A customer who prefers their own provisioner reads the same env vars; this package's helpers are convenience wrappers. The framework commits to the env shape, not to this package.

If rolling your own, you'll want to replicate:

- single-quote-escape the password literal (`'` → `''`) before string-interpolating into `IDENTIFIED WITH … BY '…'`
- mask passwords in any log output (use `maskSensitiveOutput` from `@fjall/util`)
- order `CREATE USER IF NOT EXISTS` before `ALTER USER` so re-provisioning realigns drifted credentials
- place provisioning BEFORE any migration-hash idempotency gate
- regex-validate user names before string-interpolating into SQL (CH doesn't bind identifier parameters)

## Licence

Proprietary — see [LICENSE](./LICENSE).
