Repository navigation
A UNION, EXCEPT or INTERSECT is as read-only as a SELECT - #125
Merged
Merged
Conversation
git_sql's read-only guard tested `isinstance(expr, exp.Select)`, and a set operation parses to an exp.SetOperation, so every UNION, EXCEPT and INTERSECT was refused with "this example serves SELECT only -- a git repo is read-only". psql's own `\d` sends one: the publications footer is three SELECTs joined by UNION. The guard is now (exp.Select, exp.SetOperation). Deliberately not exp.Query, which would also admit a bare exp.Subquery -- `(SELECT ...) LIMIT 1`, whose LIMIT the executor drops, answering every row instead of one. TableSession repairs that in _flatten_parenthesized and this example has no such layer, so the shape stays refused rather than silently wrong. Letting set operations through exposed a second shape that has to be refused explicitly. sqlglot's executor answers an ORDER BY over a set operation with the columns stripped -- `[(), (), ()]` -- which is not a wrong value but a `D` message contradicting the RowDescription describe() already sent, so psycopg drops the connection with `unexpected field count in "D" message`. A 0A000 is the honest reply until the executor keeps the columns. Both tests run over the wire, because in-process the stripped rows merely look odd and it is the socket that turns them into a protocol violation. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
CI checks out a detached HEAD, so `git branch` lists nothing and the `branches` table is empty -- `SELECT name FROM branches LIMIT 1` returned no rows and the survives-the-error assertion failed. It also made the UNION comparison vacuous, since two empty sets are equal. `files` is a walk of the working tree rather than anything read out of the log, so it has rows in any checkout. Measured against a detached-HEAD depth-1 clone of this repo: branches 0, commits 1, files 101, commit_files 101. The set operations are now exercised against `files` alone -- UNION and INTERSECT with itself are every path, EXCEPT is none -- which covers all three operators without depending on an overlap between two tables. An assertion that the path set is non-empty keeps the rest from passing vacuously. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
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.
git_sql's read-only guard testedisinstance(expr, exp.Select), and a set operation parses to anexp.SetOperation, so everyUNION,EXCEPTandINTERSECTwas refused as though it were a write:psql's own
\dsends one — the publications footer is threeSELECTs joined byUNION.The guard is now
(exp.Select, exp.SetOperation). Deliberately notexp.Query, which would also admit a bareexp.Subquery—(SELECT ...) LIMIT 1, whoseLIMITthe executor drops, answering every row instead of one.TableSessionrepairs that in_flatten_parenthesized; this example has no such layer, so that shape stays refused rather than becoming silently wrong.The second shape, which has to be refused explicitly
Letting set operations through exposed one that cannot just be allowed. sqlglot's executor answers an
ORDER BYover a set operation with the columns stripped —[(), (), ()]— which is not a wrong value but aDmessage contradicting theRowDescriptiondescribe()already sent:psycopg drops the connection.
_reject_stripped_columnsturns that into a0A000naming the cause. It comes out together with its test when jbylund/sqlglot#34 lands.Tests
Both run over the wire, because in-process the stripped rows merely look odd and it is the socket that turns them into a protocol violation. Both fail without the fix.
_serving()factors out the subprocess-and-connect dance the startup test already did inline.Full suite: 888 passed, 32 xfailed.
ruff checkandruff format --checkclean.Related
While measuring this I found the catalog path has none of
tables.py's workarounds at all, so the same class of bug is live on\d— filed as #124. This PR does not touch that; it only stopsgit_sqlcalling aUNIONa write.