| From: | wyuebei(at)gmail(dot)com |
|---|---|
| To: | <pgsql-bugs(at)lists(dot)postgresql(dot)org> |
| Subject: | pg ctl start spawns visible cmd.exe console window on Windows — CreateProcessAsUser missing CREATE NO WINDOW |
| Date: | 2026-06-24 06:15:40 |
| Message-ID: | 18bbefccd530e218.a6bdbcf84d30dc08.260dd5df653405e8@NBK11-64-T5J |
| Views: | Whole Thread | Raw Message | Download mbox | Resend email |
| Thread: | |
| Lists: | pgsql-bugs |
Dear PostgreSQL developers,
We discovered this bug while debugging a cascading failure across three
layers of software that ultimately traced back to pg_ctl.exe on Windows.
## The discovery chain
We use a tool called pg0 (vectorize-io/pg0) which bundles PostgreSQL via
the theseus-rs/postgresql-embedded Rust crate. On every PostgreSQL start,
a cmd.exe console window flashed briefly. We traced the process tree:
parent (CREATE_NO_WINDOW) → pg0.exe → postgresql_embedded → pg_ctl start
→ cmd.exe /C "postgres.exe -D ... < nul >> start.log 2>&1" ← console window!
→ postgres.exe
The theseus-rs crate already applies CREATE_NO_WINDOW (0x08000000) to its
CreateProcess call when spawning pg_ctl.exe, so the parent→pg_ctl edge
was clean. The flash came from pg_ctl.exe itself spawning cmd.exe.
## Root cause
File: src/bin/pg_ctl/pg_ctl.c
Lines 510-511 explain the design choice:
"As with the Unix case, it's easiest to use the shell (CMD.EXE) to
handle redirection etc."
The command is constructed at lines 557-561:
cmd = psprintf("\"%s\" /C \"\"%s\" %s%s < \"%s\" >> \"%s\" 2>&1\"",
comspec, exec_path, pgdata_opt, post_opts,
DEVNULL, log_file);
It is launched via CreateRestrictedProcess() at line 1846:
r = CreateProcessAsUser(restrictedToken, NULL, cmd, NULL, NULL, TRUE,
CREATE_SUSPENDED, // <-- no CREATE_NO_WINDOW
NULL, NULL, &si, processInfo);
Since cmd.exe is a CUI binary (subsystem 3), Windows allocates a new
console for it on every CreateProcess call. The parent's own
CREATE_NO_WINDOW flag does not propagate to grandchildren.
## Proposed fix
Option A (minimal, one flag):
Add CREATE_NO_WINDOW (0x08000000) to dwCreationFlags:
r = CreateProcessAsUser(restrictedToken, NULL, cmd, NULL, NULL, TRUE,
CREATE_SUSPENDED | CREATE_NO_WINDOW,
NULL, NULL, &si, processInfo);
Option B (eliminate cmd.exe dependency):
Use native CreateProcess handle inheritance (hStdInput/hStdOutput/hStdError
in STARTUPINFO) for I/O redirection, removing the need for cmd.exe /C
entirely. This would also eliminate the dependency on COMSPEC being set.
## Impact
Any application that launches PostgreSQL via pg_ctl from a headless or
background context sees an unexpected console flash on every start.
This affects embedded databases, CI/CD pipelines, IDE-integrated
PostgreSQL instances, and tools using libraries like pg0,
theseus-rs/postgresql-embedded, and zonkyio/embedded-postgres.
## System details
- OS: Windows 11 (22H2+, ConPTY enabled)
- PostgreSQL version tested: 18 (bundled via pg0 v0.14.2)
- The code path in pg_ctl.c is substantially unchanged across versions
We are happy to test a patch if one is drafted.
Thank you for your work on this excellent project.
Best regards,
Yuebei Wang
| From | Date | Subject | |
|---|---|---|---|
| Next Message | PG Bug reporting form | 2026-06-24 07:20:28 | BUG #19531: Inconsistent Error Messages for the Same SQL Query |
| Previous Message | Hüseyin Demir | 2026-06-24 06:14:53 | Re: BUG #19483: pg_upgrade fails with orphan records in pg_init_priv catalog table |