# Settings - SET, SHOW ALL, ALTER SYSTEM > One list of the server's settings behind pg_settings, SHOW ALL, SHOW PARAMETERS and GET /api/settings; SET for a session, ALTER SYSTEM and pg_reload_conf() for the server. - Category: Postgres protocol - Keywords: settings, pg\_settings, SHOW ALL, SHOW PARAMETERS, SET, RESET, ALTER SYSTEM, pg\_reload\_conf, current\_setting, work\_mem, max\_parallel\_workers\_per\_gather, enable\_hashjoin, enable\_nestloop, enable\_indexscan, log\_statement, plan\_cache\_mode, plan cache, kdb.conf, kdb\_settings, QuestDB, parameters, txn\_spill\_mb, memory\_limit - See also: [PostgreSQL protocol (port 8832)](postgres-protocol.md), [PostgreSQL-compatible functions](functions-postgres.md), [kdb serve](cli-serve.md), [Settings page (data folder, old log, capacity, program files and users)](console-settings.md), [Limits](limits.md), [Joins](joins.md), [Indexes](indexes.md) KDB keeps **one list** of its settings. The same list is behind: | Where | Shows | |---|---| | `SELECT * FROM pg_settings` | PostgreSQL's view: name, setting, unit, context, source, ... | | `SHOW ALL` | name, value (as `SHOW` writes it: `4MB`, `5s`) and description | | `SHOW PARAMETERS` | QuestDB's view: property path, environment variable, value, where it came from | | `SELECT * FROM kdb_settings` | all of the above in one table, to filter with `WHERE` | | `GET /api/settings` | the list under `"parameters"`, with the server's values | | `SHOW work_mem`, `current_setting('work_mem')` | one setting, for this session | Each setting says what KDB does with it (the `use` column of `kdb_settings`): | use | Meaning | |---|---| | `applied` | KDB acts on it (`statement_timeout`, `work_mem`, `enable_hashjoin`, ...) | | `shown` | kept and shown for clients, but KDB works the same either way (`DateStyle`, `search_path`) | | `ignored` | accepted so PostgreSQL scripts run, with a NOTICE (`shared_buffers` in a session, `enable_seqscan`, `jit`, ...) | ## Change a setting for this session Syntax: ```sql SET name = value SET name TO value SET LOCAL name = value until COMMIT or ROLLBACK RESET name RESET ALL SHOW name SHOW ALL ``` A value can have a unit, as in PostgreSQL: `SET work_mem = '64MB'`, `SET statement_timeout = '5s'`. `SHOW` gives it back the same way. A bad value gives `22023`, an unknown name on `SHOW` gives `42704`. The `context` column says when a setting may change: | context | Changes with | |---|---| | `user` | `SET` in any session | | `superuser` | `SET` by an admin (`log_statement`) | | `sighup` | `ALTER SYSTEM` and `SELECT pg_reload_conf()` | | `postmaster` | a restart: a `kdb serve` flag, `kdb.conf` or an environment variable (`55P02` on `SET`) | | `internal` | never (`server_version`, `block_size`) | Over HTTP, a `SET` lasts for the rest of the same request (and for the whole session when you use one: [sessions](http-sessions-and-cursors.md)). **Example: Set and read a setting** ```sql SET work_mem = '64MB'; SHOW work_mem; SELECT name, setting, unit, source FROM pg_settings WHERE name IN ('work_mem', 'enable_hashjoin'); ``` Result: `SET work_mem = '64MB'` ```text work_mem is 64MB for this session ``` `SHOW work_mem` | work_mem (STRING) | |---| | 64MB | (1 row) `SELECT name, setting, unit, source FROM pg_settings WHERE name IN ('work_mem', 'enable_hashjoin')` | name (STRING) | setting (STRING) | unit (STRING) | source (STRING) | |---|---|---|---| | enable_hashjoin | on | NULL | default | | work_mem | 65536 | kB | session | (2 rows) ## The planner settings KDB applies | Setting | Default | What it does | |---|---|---| | `work_mem` | `0` | how much a `GROUP BY` keeps in memory before its groups go to temporary files. `0` = half of the statement's working memory. Never more than three quarters of it | | `max_parallel_workers_per_gather` | the server's threads | threads one statement's scans and aggregates may use, its own included. `0` or `1` = one thread | | `enable_hashjoin` | `on` | `off`: equality joins compare every pair of rows (a nested loop) instead of building a hash | | `enable_nestloop` | `on` | `off`: a join does not look each row's match up by key or index; it builds a hash | | `enable_indexscan` | `on` | `off`: no secondary index finds rows; the table is scanned. A full-text search keeps its index | | `log_statement` | `none` | `ddl`, `mod` or `all`: statements go to the server's log as `LOG: statement: ...` (admins only) | | `plan_cache_mode` | `auto` | `force_custom_plan`: this session's statements are parsed every time; they neither use nor fill the [plan cache](#plan-cache). `auto` and `force_generic_plan` use it | The switches are there to compare answers and times, as in PostgreSQL. They are read once per statement, never per row. ## Memory settings set at start These change only with a restart (`postmaster`): a `kdb serve` flag or an environment variable. | Setting | Default | Flag / variable | What it does | |---|---|---|---| | `memory_limit` | none | `--memory-limit`, `KDB_MEMORY_LIMIT` | one limit for everything the server holds | | `shared_buffers` | 512 MB | `--memory`, `KDB_MEMORY` | table data kept loaded | | `txn_spill_mb` | `64` | `--txn-spill-mb`, `KDB_TXN_SPILL_MB` | a transaction's log records past this many MB (or 1/16 of `memory_limit`) go to a temporary file until `COMMIT`. `0` = never. See [big transactions](transactions.md#txn-spill) | **Example: Read the memory settings** ```sql SELECT name, setting, unit, context FROM pg_settings WHERE name IN ('memory_limit', 'txn_spill_mb') ORDER BY name; ``` Result: | name (STRING) | setting (STRING) | unit (STRING) | context (STRING) | |---|---|---|---| | memory_limit | 0 | kB | postmaster | | txn_spill_mb | 64 | MB | postmaster | (2 rows) ## The plan cache An app sends the same small statement again and again: a key lookup, a price, a session check. Parsing it can take as long as running it. So the server keeps the parse of a statement it has seen, by its exact text, and the next run of the same text skips the parse. | What | How | |---|---| | Who uses it | every client: `POST /exec`, `GET /exec` and the PostgreSQL port's simple query (prepared statements keep their own parse per connection) | | What is kept | one `SELECT` the first time; one `INSERT ... VALUES`, `UPDATE` or `DELETE` when the same text comes a second time. Not `SELECT ... FOR UPDATE`, a `WITH` that changes rows, or a statement that names another database | | Same statement, other values | an `INSERT ... VALUES`, `UPDATE`, `DELETE` or `CALL` is also kept by its *shape* - the text with its numbers and `'strings'` left out - the first time, per connection (or HTTP worker). The next text of that shape, `INSERT INTO t VALUES (1018, 'open')` after `(1017, 'paid')`, runs the kept parse with its own values put in: the same tree a parse would make, without the parse. Only when every value is a plain value; a `LIMIT`, a cast's precision, a `SIMILAR TO` pattern or a `GROUP BY` keeps the text parsed every time | | Per database | each database has its own entries, even for the same text | | After a schema change | `CREATE`, `ALTER` or `DROP` of a table: the next run parses the text again | | Size | up to 2,048 texts of at most 2 KB each; when full, one not used lately goes | | Planned on each run | which index, which engine and which partitions are worked out again every time, with the values of that run | The answers never change: only the time to parse is saved. `GET /api/stats` shows it under `"plan_cache"`: | Field | Meaning | |---|---| | `hits` | runs that skipped the parse | | `misses` | statements it could keep that had to be parsed: the first time, or after a schema change | | `entries` | parses kept now | | `capacity` | texts it keeps at most | To leave it out for one session, as PostgreSQL does for its own plan cache: **Example: Parse every statement of this session** ```sql SET plan_cache_mode = force_custom_plan; SHOW plan_cache_mode; ``` Result: `SET plan_cache_mode = 'force_custom_plan'` ```text plan_cache_mode is force_custom_plan for this session ``` `SHOW plan_cache_mode` | plan_cache_mode (STRING) | |---| | force_custom_plan | (1 row) ## Change a setting for the server Syntax: ```sql ALTER SYSTEM SET name = value ALTER SYSTEM RESET name ALTER SYSTEM RESET ALL SELECT pg_reload_conf(); ``` `ALTER SYSTEM` saves the value in `kdb.conf`, under `"system"`. It is in use after `SELECT pg_reload_conf()` (or the next start) for every new statement of every session that did not `SET` it itself. Only a server admin may run either. `ALTER SYSTEM` cannot run inside `BEGIN ... COMMIT`. ```text ALTER SYSTEM SET work_mem = '256MB'; ALTER SYSTEM SET log_statement = 'ddl'; SELECT pg_reload_conf(); -- true ``` `kdb.conf` then holds: ```text { "data_dir": "/data", "system": {"log_statement": "ddl", "work_mem": "262144"} } ``` - Settings that only change with a restart (`port`, `shared_buffers`, `max_connections`, ...) give `55P02` and name the flag or `kdb.conf` key to use. - A database folder served by its path (`kdb serve D:\kdb\market`) has no `kdb.conf` of its own: `ALTER SYSTEM` says so. - A value in `kdb.conf` that is no longer valid is skipped with a warning in the server's log; the default is used. ## Also fixed on 2026-09-26 - `KDB_NO_ZONE_MAPS=0` (and every other `KDB_NO_*` switch set to `0`, `off`, `no` or `false`) now means **off**: the speed-up stays on. Some switches used to turn off for any value. - An on/off variable set but empty (`KDB_SCOPED_DDL=` in a compose file) no longer stops the server: it counts as not set. `true`/`false`, `yes`/`no` and `1`/`0` work too. - `kdb serve ` (a database folder by its path) no longer takes `alert_webhook` from the default `kdb.conf`, like every other `kdb.conf` setting. - `KDB_INDEX_MAX_VISITS` must be a whole number of 1 or more; any other value is ignored with a warning, instead of making ordered index reads give up at once. - `kdb help` lists what `kdb.conf` may hold.