ent-framework
Version:
A PostgreSQL graph-database-alike library with microsharding and row-level security
78 lines (59 loc) • 3.53 kB
Markdown
# Query Planner Hints
Another PostgreSQL specific feature (probably the most popular one among the [other custom options](postgresql-specific-features.md)) is giving the PostgreSQL planner some hints on how you prefer the query to be executed.
## GUC (Grand Unified Configuration) Settings
E.g. if your table contains data with very different cardinality of a particular field, and you see that PostgreSQL runs a seqscan sometimes, you may try to lower the seqscan priority:
```typescript
const comments = await EntComment.select(
vc,
{ topic_id: topicID },
100,
[{ created_at: "DESC" }],
{ hints: { enable_seqscan: "off" } },
);
```
The planner settings like [enable\_seqscan](https://www.postgresql.org/docs/current/runtime-config-query.html#GUC-ENABLE-SEQSCAN) do not fully prevent sequential scan, but they greatly reduce its probability, in case you know what you're doing.
If your query is intentionally a sequential scan or just works with lots of rows in the table, does [JOINs uses WITH custom clauses](postgresql-specific-features.md), then you may also want to increase `work_mem` for it, to lower the chance of using temporary files:
```typescript
const comments = await EntComment.select(
vc,
{ topic_id: topicID },
100,
[{ created_at: "DESC" }],
{ hints: { work_mem: "100MB" } },
);
```
Of course, Ent Framework changes the GUC settings during the query execution period only, and guarantees that they are restored after. Internally, `SET LOCAL ... RESET` are used for that (running within the same transaction of the multi-query).
Also, batching is done with respect to the planner hints. Only the SELECT queries having the same set of hints are potentially batched together with `UNION ALL` clause, as explained in [ent-api-select-by-expression.md](../getting-started/ent-api-select-by-expression.md "mention") article.
See [PostgreSQL documentation](https://www.postgresql.org/docs/current/runtime-config.html) for the full list of settings you can customize per query (notice that not all of them allow per-query changing: some require a database restart).
## Using pg\_hint\_plan Extension
There is a special hint with `""` (empty string) name. It allows to raw-prepend an arbitrary string in front of the query that Ent Framework sends to the database.
The raw-prepend hint is useful when e.g. working with PostgreSQL extensions like [pg\_hint\_plan](https://github.com/ossc-db/pg_hint_plan). This extension enables a **way** greater level of planning customization. You can even tell PostgreSQL, which exact index it must use for your query:
```typescript
const comments = await EntComment.select(
vc,
{ topic_id: topicID },
100,
[{ created_at: "DESC" }],
{
hints: {
[""]: "/*+IndexScan(comments comments_created_at_idx)*/",
work_mem: "10MB",
statement_timeout: "20s",
},
},
);
```
The resulting SQL multi-query (one transaction) sent to the server will then look like:
```sql
/*+IndexScan(comments comments_created_at_idx)*/
SET LOCAL search_path TO sh0123;
SET LOCAL work_mem TO 10MB;
SET LOCAL statement_timeout TO 20s;
SELECT ... FROM comments WHERE topic_id=?
ORDER BY created_at DESC LIMIT 100;
RESET statement_timeout;
RESET work_mem;
SELECT pg_last_wal_replay_lsn();
```
Since it's a multi-query, there will be only one round-trip to the server and one transaction.
The use of raw-prepend hint turns off Ent Framework's SELECT query batching. I.e. your query is guaranteed to run alone, not in a `UNION ALL` construction.