Skip to content

demo: LEFT JOIN validation in Goqu and SQLAlchemy#8

Open
eitamring wants to merge 1 commit intomainfrom
demo/join-builder-left-join
Open

demo: LEFT JOIN validation in Goqu and SQLAlchemy#8
eitamring wants to merge 1 commit intomainfrom
demo/join-builder-left-join

Conversation

@eitamring
Copy link
Copy Markdown
Contributor

What this shows

This PR demonstrates LEFT JOIN analysis in both Goqu and SQLAlchemy on valk-guard@v0.1.3.

It includes:

  • one safe LEFT JOIN in Goqu
  • one broken LEFT JOIN in Goqu
  • one safe LEFT JOIN in SQLAlchemy
  • one broken LEFT JOIN in SQLAlchemy

Expected result

This PR should produce exactly 6 findings:

  • 4 errors
  • 2 warnings

Rule breakdown:

  • VG004 x2: both broken joins are unbounded (SELECT without LIMIT)
  • VG105 x2: both broken joins project orders.ghost_status, which does not exist
  • VG106 x2: both broken joins filter on orders.missing_flag, which does not exist

Why the clean model/migration files are touched

This branch keeps the workflow aligned with the changed-files-only docs example.

Schema-aware rules like VG105 and VG106 need the schema/model context in the scan input, so this PR includes harmless comment-only changes in the clean schema/model files to keep them in the changed-file set without changing baseline behavior on main.

What we gain

This gives a compact demo PR that proves:

  • Valk Guard understands builder-generated LEFT JOINs in both languages
  • safe joins stay quiet
  • broken joins raise schema-aware errors, not just generic warnings
  • the published v0.1.3 release matches local validation and GitHub review comments

Comment on lines +39 to +46
_, _, err := goqulib.From("users").
LeftJoin(
goqulib.T("orders"),
goqulib.On(goqulib.I("orders.user_id").Eq(goqulib.I("users.id"))),
).
Select("users.id", "users.email", "orders.ghost_status").
Where(goqulib.I("orders.missing_flag").Eq("pending")).
ToSQL()
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ [valk-guard] reported by reviewdog 🐶
VG004: SELECT without LIMIT may return unbounded rows; add LIMIT or FETCH FIRST | Origin: goqu query builder | Query: SELECT users.id, users.email, orders.ghost_status FROM users LEFT JOIN orders ON 1=1 WHERE...

Comment on lines +39 to +46
_, _, err := goqulib.From("users").
LeftJoin(
goqulib.T("orders"),
goqulib.On(goqulib.I("orders.user_id").Eq(goqulib.I("users.id"))),
).
Select("users.id", "users.email", "orders.ghost_status").
Where(goqulib.I("orders.missing_flag").Eq("pending")).
ToSQL()
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🚫 [valk-guard] reported by reviewdog 🐶
VG105: projection column "ghost_status" not found in table "orders" schema; check SELECT list and schema/model mappings | Origin: goqu query builder | Query: SELECT users.id, users.email, orders.ghost_status FROM users LEFT JOIN orders ON 1=1 WHERE...

Comment on lines +39 to +46
_, _, err := goqulib.From("users").
LeftJoin(
goqulib.T("orders"),
goqulib.On(goqulib.I("orders.user_id").Eq(goqulib.I("users.id"))),
).
Select("users.id", "users.email", "orders.ghost_status").
Where(goqulib.I("orders.missing_flag").Eq("pending")).
ToSQL()
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🚫 [valk-guard] reported by reviewdog 🐶
VG106: filter predicate column "missing_flag" not found in table "orders" schema; check predicate/group/order columns in schema/model mappings | Origin: goqu query builder | Query: SELECT users.id, users.email, orders.ghost_status FROM users LEFT JOIN orders ON 1=1 WHERE...

Comment on lines +22 to +25
session.query(User.id, User.email, Order.ghost_status)
.outerjoin(Order, Order.user_id == User.id)
.filter(Order.missing_flag == "pending")
.all()
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ [valk-guard] reported by reviewdog 🐶
VG004: SELECT without LIMIT may return unbounded rows; add LIMIT or FETCH FIRST | Origin: SQLAlchemy query builder | Query: SELECT "users"."id", "users"."email", "orders"."ghost_status" FROM "users" LEFT JOIN "orde...

Comment on lines +22 to +25
session.query(User.id, User.email, Order.ghost_status)
.outerjoin(Order, Order.user_id == User.id)
.filter(Order.missing_flag == "pending")
.all()
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🚫 [valk-guard] reported by reviewdog 🐶
VG105: projection column "ghost_status" not found in table "orders" schema; check SELECT list and schema/model mappings | Origin: SQLAlchemy query builder | Query: SELECT "users"."id", "users"."email", "orders"."ghost_status" FROM "users" LEFT JOIN "orde...

Comment on lines +22 to +25
session.query(User.id, User.email, Order.ghost_status)
.outerjoin(Order, Order.user_id == User.id)
.filter(Order.missing_flag == "pending")
.all()
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🚫 [valk-guard] reported by reviewdog 🐶
VG106: filter predicate column "missing_flag" not found in table "orders" schema; check predicate/group/order columns in schema/model mappings | Origin: SQLAlchemy query builder | Query: SELECT "users"."id", "users"."email", "orders"."ghost_status" FROM "users" LEFT JOIN "orde...

@eitamring eitamring force-pushed the demo/join-builder-left-join branch from a2b7622 to 9bac6b0 Compare March 6, 2026 18:05
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant