Description
When a role-based @remove(ifRole:) or @skip directive strips the only user-selected field from a sub-select, GraphJin emits an empty column list and the Snowflake emulator rejects the query with:
001007 (-1): query execution failed: query execution error: Parser Error: SELECT clause without selection list
Reproduction
Any query where a directive removes the only field under a sub-select trips this. The existing integration test tests/Example_queryWithAddAndRemoveDirective2 is the canonical repro:
query {
products(limit: 2, order_by: { id: asc }) {
id
name @add(ifRole: \"user\")
}
users(limit: 3, order_by: { id: asc }) {
id @remove(ifRole: \"anon\")
}
}
When run as the anon role, id @remove(ifRole: \"anon\") drops id from the users sub-select, leaving nothing between SELECT and FROM.
cd tests && go test -v -db snowflake -run '^Example_queryWithAddAndRemoveDirective2$'
Expected: {\"products\":[{\"id\":1},{\"id\":2}],\"users\":[{},{},{}]}
Actual: Snowflake parser rejects the SQL.
Scope
- Other dialects (Postgres, MySQL, MariaDB, SQLite, MSSQL, Oracle, MongoDB) handle this correctly — postgres emits something like `SELECT json_agg(json_build_object()) ... FROM users` which is a valid empty JSON object per row.
- Snowflake's inner rendering apparently produces a bare `SELECT FROM users` instead of `SELECT NULL FROM users` or equivalent.
- Reproduces against the ghcr.io snowflake-emulator currently pinned in `tests/dbint_test.go`. Haven't verified against real Snowflake — worth checking whether production Snowflake is more forgiving.
Suggested fix
In the Snowflake dialect's inner-child / JSON-field renderer, when every user field has been filtered out (`SkipRender == SkipTypeDrop` / `SkipTypeBlocked` / `SkipTypeNulled` on all fields), emit a placeholder column so the SELECT is non-empty. The postgres / mssql dialects already have a `RequiresNullOnEmptySelect()` style hook in their inline-child path — mirroring that for Snowflake should cover it.
Not a regression
This reproduces on `master` before any recent PRs — not introduced by the expression-aggregate work (#572) or earlier. Filing separately so it can be triaged on its own scope.
Environment
- GraphJin: master
- Snowflake emulator: `ghcr.io/nnnkkk7/snowflake-emulator` (SHA pinned in `tests/dbint_test.go`)
- Go: 1.25.x
Description
When a role-based
@remove(ifRole:)or@skipdirective strips the only user-selected field from a sub-select, GraphJin emits an empty column list and the Snowflake emulator rejects the query with:Reproduction
Any query where a directive removes the only field under a sub-select trips this. The existing integration test
tests/Example_queryWithAddAndRemoveDirective2is the canonical repro:When run as the
anonrole,id @remove(ifRole: \"anon\")dropsidfrom theuserssub-select, leaving nothing betweenSELECTandFROM.Expected:
{\"products\":[{\"id\":1},{\"id\":2}],\"users\":[{},{},{}]}Actual: Snowflake parser rejects the SQL.
Scope
Suggested fix
In the Snowflake dialect's inner-child / JSON-field renderer, when every user field has been filtered out (`SkipRender == SkipTypeDrop` / `SkipTypeBlocked` / `SkipTypeNulled` on all fields), emit a placeholder column so the SELECT is non-empty. The postgres / mssql dialects already have a `RequiresNullOnEmptySelect()` style hook in their inline-child path — mirroring that for Snowflake should cover it.
Not a regression
This reproduces on `master` before any recent PRs — not introduced by the expression-aggregate work (#572) or earlier. Filing separately so it can be triaged on its own scope.
Environment