Skip to content

fix(google_ads): honor LIMIT in handler select()#78

Merged
gabrielbressan-tfy merged 1 commit into
mainfrom
fix/google-ads-limit
Jun 18, 2026
Merged

fix(google_ads): honor LIMIT in handler select()#78
gabrielbressan-tfy merged 1 commit into
mainfrom
fix/google-ads-limit

Conversation

@gabrielbressan-tfy

Copy link
Copy Markdown
Collaborator

Problem

SELECT * FROM google_ads_….campaigns LIMIT 3 returns the entire account (3553 campaigns, 192952 keywords, …) instead of 3 rows.

Root cause: for class_type = "api" handlers, plan_api_db_select forwards LIMIT to the handler (query2.limit) and clears it from the outer SubSelectStep (query_planner.py:446). So DuckDB never applies LIMIT — the handler is solely responsible, and the Google Ads handler ignored query.limit entirely.

The report tables (campaign_performance, search_terms) only appeared to honor LIMIT because they returned 0 rows due to the empty default date range, not because LIMIT worked.

Fix

Three helpers added to google_ads_tables.py, wired into all 11 table classes:

  • _limit_value(query) — extracts the LIMIT integer.
  • _all_conditions_pushable(conditions, col_map) — true only when every WHERE condition is pushed to GAQL.
  • _finalize(query, df, columns, can_limit) — selects columns and slices df.head(n) when safe.

Each table now pushes LIMIT n into the GAQL query (so the API returns ≤ n rows instead of the whole account) and slices the DataFrame — but only when the WHERE is fully handled:

  • GAQL tables: all conditions pushed down.
  • Cached languages / geo_targets: no WHERE.
  • Keyword-planner tables: no leftover conditions beyond consumed params.

This gates against under-returning: a complex WHERE (e.g. name LIKE '%a%') that DuckDB re-filters afterward leaves LIMIT off, so we never hand DuckDB fewer rows than it needs.

Validation

Exercised select() with a mocked Google Ads service (remote server has the connector but the local container does not):

case result
limit 3 GAQL gets LIMIT 3, returns 3 ✅
where status='ENABLED' limit 2 pushed, returns 2 ✅
where name like '%a%' limit 2 not pushed/sliced, full set → DuckDB ✅
no limit unchanged ✅

🤖 Generated with Claude Code

For api-class handlers, plan_api_db_select forwards LIMIT to the handler
and clears it from the outer SubSelectStep, so DuckDB never applies it.
The Google Ads handler ignored query.limit, so entity tables (campaigns,
ad_groups, ads, keywords) returned the whole account regardless of LIMIT.

Add _limit_value / _all_conditions_pushable / _finalize helpers and wire
all table classes to push LIMIT into the GAQL query and slice the
DataFrame — but only when the WHERE is fully handled by pushdown (or
absent), so a complex WHERE that DuckDB re-filters afterward cannot
under-return.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@gabrielbressan-tfy gabrielbressan-tfy marked this pull request as ready for review June 18, 2026 18:59
@gabrielbressan-tfy gabrielbressan-tfy merged commit cde0641 into main Jun 18, 2026
3 of 5 checks passed
@gabrielbressan-tfy gabrielbressan-tfy deleted the fix/google-ads-limit branch June 18, 2026 19:27
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.

4 participants