WIP: command_tag_format — protocol-level command tag negotiation via _pq_#16
Open
WIP: command_tag_format — protocol-level command tag negotiation via _pq_#16
Conversation
NikolayS
commented
Mar 11, 2026
| options => 'client_message_level_options', | ||
| }, | ||
|
|
||
|
|
Owner
Author
There was a problem hiding this comment.
this extra line is not needed
a89112b to
14e3dea
Compare
Adds a new enum GUC command_tag_format (PGC_USERSET) with four modes: - legacy: INSERT 0 N (default, backward compatible) - modern: INSERT N (drops legacy OID field) - verbose: INSERT tablename N (shows relation name) - fqn: INSERT schema.tablename N (fully qualified) Tested output: legacy: INSERT 0 1 modern: INSERT 1 verbose: INSERT users 1 fqn: INSERT myschema.users 1 Supports SET LOCAL for per-transaction control. libpq PQcmdTuples() handles all formats gracefully. Note: verbose/fqn currently only populated for INSERT (UPDATE/DELETE show count only — relname propagation for those pending).
14e3dea to
0e8ea91
Compare
- Server handles _pq_.command_tag_format in startup parameters,
mapping it to the command_tag_format GUC
- Added GUC_REPORT flag so server auto-reports format changes to client
- Client can negotiate format at connection time via:
psql 'options=-ccommand_tag_format=modern'
- Old client + new server: server uses legacy default (safe)
- New client + old server: _pq_ option silently ignored (safe)
- Mid-session SET also works and is reported to client
Tested: three connections with different formats confirmed working.
- Remove 'modern' mode; keep legacy/verbose/fqn - Change GUC context from PGC_USERSET to PGC_INTERNAL: blocks SET, options=-c, ALTER SYSTEM, and postgresql.conf - Store _pq_.command_tag_format in Port->pq_command_tag_format (not guc_options) to isolate from regular GUC path - Apply deferred in process_startup_options() via PGC_S_OVERRIDE - Revert libpq fe-exec.c: old protocol completely untouched Safety guarantees: Old client + new server (no _pq_) → INSERT 0 N (always safe) New client + old server (_pq_ sent) → silently ignored SET command_tag_format → ERROR: cannot be changed options=-ccommand_tag_format=verbose → FATAL: cannot be changed _pq_.command_tag_format=verbose → INSERT tablename N (works) Tested with stock PG17 psql (old client) and raw _pq_ startup packet (Python socket test).
Owner
Author
Test Evidence: Protocol-Only
|
| Scenario | Result | Status |
|---|---|---|
| Old PG17 psql, default | INSERT 0 1 |
✅ |
Old PG17 psql, options=-c verbose |
FATAL: cannot be changed |
✅ |
Old PG17 psql, SET verbose |
ERROR: cannot be changed |
✅ |
_pq_ no param (default) |
INSERT 0 1 |
✅ |
_pq_ verbose |
INSERT proto_test 1 |
✅ |
_pq_ fqn |
INSERT public.proto_test 1 |
✅ |
_pq_ legacy |
INSERT 0 1 |
✅ |
_pq_ modern (removed) |
FATAL: invalid value |
✅ |
All 8 test cases pass. Old protocol untouched (fe-exec.c has zero changes).
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
(live hacking session: https://www.youtube.com/watch?v=VKuxQZlvd8E)
Adds a new protocol-level feature
_pq_.command_tag_formatthat allows clients to negotiate richer command completion tags at connection time.The problem
Every INSERT returns
INSERT 0 N— the0is a vestigial OID field, hardcoded to zero since PG12 dropped table OIDs. It wastes bytes and confuses users, but changing the wire format breaks old clients.Solution: protocol negotiation
New clients send
_pq_.command_tag_formatin the startup packet. Old clients never send it, so they always get legacy format. Zero breakage.Formats
legacyINSERT 0 N(default)INSERT 0 1verboseINSERT tablename NINSERT users 1fqnINSERT schema.tablename NINSERT public.users 1verboseandfqnalso work for UPDATE, DELETE, and MERGE.Safety guarantees
_pq_)INSERT 0 1— always safe_pq_sent)SET command_tag_format = 'verbose'options=-ccommand_tag_format=verboseThe GUC is
PGC_INTERNAL— only the_pq_startup packet path can set it.SET,options=-c,ALTER SYSTEM, andpostgresql.confare all blocked.Implementation (8 files, ~115 insertions)
cmdtag.h/cmdtag.c— format constants, format-awareBuildQueryCompletionStringpquery.c— populates relation name from executor for verbose/fqnbackend_startup.c—_pq_handler, stores inPort->pq_command_tag_formatlibpq-be.h— newPortfield for deferred protocol optionpostinit.c— deferred application viaSetConfigOption(PGC_INTERNAL, PGC_S_OVERRIDE)guc_parameters.dat/guc_tables.c— GUC registration withGUC_REPORTNOT changed:
fe-exec.c(libpq)Old protocol is completely untouched. New-protocol-aware clients handle the new format themselves.
Evolution
command_tag_omit_oid— simple bool GUC (PGC_USERSET) + libpq patchcommand_tag_format— enum with 4 modes,_pq_negotiation,GUC_REPORTPGC_INTERNAL, removedmodernmode, reverted libpq, separatePortfield +PGC_S_OVERRIDEdeferred applicationTest results
See comment with full test evidence — 8/8 scenarios pass with both stock PG17 psql and raw
_pq_startup packet test.